From c3fb25a808077c19337158773d68e6eef4a7663e Mon Sep 17 00:00:00 2001 From: n0099 Date: Fri, 7 Jun 2024 02:59:43 +0800 Subject: [PATCH] * add option for rule `@typescript-eslint/restrict-template-expressions` * fix `Parse errors in imported module '': parserPath or languageOptions.parser is required!`: https://github.com/un-ts/eslint-plugin-import-x/pull/85#issuecomment-2153376351 @ eslint.config.js * re-plug eslint: https://github.com/pzmosquito/eslint-import-resolver-vite/issues/12#issuecomment-2153237283 @ package.json * fix some violations of eslint rules @ fe --- fe/eslint.config.js | 36 +++++++++++-------- fe/package.json | 10 ++---- .../components/Post/queryForm/useQueryForm.ts | 11 +++--- .../Post/renderers/list/RendererList.vue | 2 +- fe/src/main.ts | 4 +-- fe/src/router/index.ts | 5 ++- fe/src/views/BilibiliVote.vue | 2 +- fe/yarn.lock | 3 -- 8 files changed, 38 insertions(+), 35 deletions(-) diff --git a/fe/eslint.config.js b/fe/eslint.config.js index 235a84cd..edae50d8 100644 --- a/fe/eslint.config.js +++ b/fe/eslint.config.js @@ -382,6 +382,7 @@ const rules = [{ // as of eslint-plugin-unicorn@50.0.1 '@typescript-eslint/no-unsafe-enum-comparison': 'error', '@typescript-eslint/no-unsafe-unary-minus': 'error', '@typescript-eslint/parameter-properties': ['error', { prefer: 'parameter-property' }], + '@typescript-eslint/restrict-template-expressions': ['error', { allowNumber: true }], }, }, { // as of eslint-plugin-vue@9.19.2 optout: { @@ -520,20 +521,25 @@ const rules = [{ // as of eslint-plugin-unicorn@50.0.1 }, }]; -import viteConfig from './vite.config.ts'; -import pluginStylistic from '@stylistic/eslint-plugin'; -import pluginImportX from 'eslint-plugin-import-x'; -import pluginUnicorn from 'eslint-plugin-unicorn'; -import * as typescriptESLintParserForExtraFiles from 'typescript-eslint-parser-for-extra-files'; import * as vueESLintParser from 'vue-eslint-parser'; +// eslint-disable-next-line import-x/extensions import vueESLintConfigTypescriptRecommendedExtends from '@vue/eslint-config-typescript/recommended.js'; import pluginVue from 'eslint-plugin-vue'; import { fixupConfigRules } from '@eslint/compat'; import { FlatCompat } from '@eslint/eslintrc'; import eslintJs from '@eslint/js'; +import pluginStylistic from '@stylistic/eslint-plugin'; import stylisticMigrate from '@stylistic/eslint-plugin-migrate'; +import pluginImportX from 'eslint-plugin-import-x'; +import pluginUnicorn from 'eslint-plugin-unicorn'; +// eslint-disable-next-line import-x/extensions +import { tsImport } from 'tsx/esm/api'; +import * as typescriptESLintParserForExtraFiles from 'typescript-eslint-parser-for-extra-files'; import * as _ from 'lodash-es'; +// https://github.com/pzmosquito/eslint-import-resolver-vite/issues/12#issuecomment-2151349705 +const viteConfig = await tsImport('./vite.config.ts', import.meta.url); + // https://github.com/eslint/eslint/issues/18093 // https://github.com/eslint/eslint/issues/18391 const compat = new FlatCompat(); @@ -546,8 +552,11 @@ export default [ 'plugin:@typescript-eslint/strict-type-checked', 'plugin:@typescript-eslint/stylistic-type-checked', ), - ...compat.config(pluginImportX.configs.recommended), // https://github.com/un-ts/eslint-plugin-import-x/issues/29#issuecomment-2148843214 - ...compat.config(pluginImportX.configs.typescript), + { // https://github.com/un-ts/eslint-plugin-import-x/pull/85#issuecomment-2153376351 + plugins: { 'import-x': { rules: pluginImportX.rules } }, + rules: pluginImportX.configs.recommended.rules, + }, // https://github.com/import-js/eslint-plugin-import/issues/2556#issuecomment-2119520339 + pluginImportX.configs.typescript, ...fixupConfigRules(...compat.extends( 'plugin:@tanstack/eslint-plugin-query/recommended', // https://github.com/TanStack/query/pull/7253 )), @@ -562,6 +571,12 @@ export default [ tsconfigRootDir: import.meta.dirname, }, }, + settings: { + 'import-x/resolver': { + typescript: true, + vite: { viteConfig }, // https://github.com/pzmosquito/eslint-import-resolver-vite/issues/12#issuecomment-2148676875 + }, + }, plugins: { '@stylistic': pluginStylistic }, // https://stackoverflow.com/questions/30221286/how-to-convert-an-array-of-objects-to-an-object-in-lodash/36692117#36692117 @@ -576,7 +591,6 @@ export default [ { files: ['**/*.ts'], languageOptions: { parser: typescriptESLintParserForExtraFiles }, - settings: { 'import-x/resolver': { typescript: true } }, }, { files: ['**/*.vue'], @@ -588,12 +602,6 @@ export default [ tsconfigRootDir: import.meta.dirname, }, }, - settings: { - 'import-x/resolver': { - typescript: true, - vite: { viteConfig }, // https://github.com/pzmosquito/eslint-import-resolver-vite/issues/12#issuecomment-2148676875 - }, - }, }, { files: ['eslint.config.js'], diff --git a/fe/package.json b/fe/package.json index 52074c26..fd8cfc89 100644 --- a/fe/package.json +++ b/fe/package.json @@ -6,8 +6,7 @@ "scripts": { "dev": "vite", "build": "vue-tsc && vite build", - "preview": "vite preview", - "eslint": "tsx .yarn/unplugged/eslint-npm-*/node_modules/eslint/bin/eslint.js" + "preview": "vite preview" }, "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.5.2", @@ -81,10 +80,5 @@ "not ie 11", "not dead" ], - "packageManager": "yarn@4.2.2", - "dependenciesMeta": { - "eslint@9.4.0": { - "unplugged": true - } - } + "packageManager": "yarn@4.2.2" } diff --git a/fe/src/components/Post/queryForm/useQueryForm.ts b/fe/src/components/Post/queryForm/useQueryForm.ts index 3b1fe506..776c5c57 100644 --- a/fe/src/components/Post/queryForm/useQueryForm.ts +++ b/fe/src/components/Post/queryForm/useQueryForm.ts @@ -8,7 +8,7 @@ import * as _ from 'lodash-es'; export interface UnknownParam { name: string, value: unknown, subParam: ObjUnknown & { not?: boolean } } export interface NamelessUnknownParam { value?: unknown, subParam?: ObjUnknown } export type ParamPreprocessorOrWatcher = (p: UnknownParam) => void; -export default < +const useQueryForm = < UniqueParams extends Record = Record, Params extends Record = Record >( @@ -29,7 +29,7 @@ export default < const fillParamDefaultValue = (param: Partial & { name: string }, resetToDefault = false): T => { // prevent defaultsDeep mutate origin paramsDefaultValue - const defaultParam = _.cloneDeep(deps.paramsDefaultValue[param.name]); + const defaultParam = structuredClone(deps.paramsDefaultValue[param.name]); if (defaultParam === undefined) throw new Error(`Param ${param.name} not found in paramsDefaultValue`); defaultParam.subParam ??= {}; @@ -39,7 +39,7 @@ export default < if (resetToDefault) return _.defaultsDeep(defaultParam, param) as T; - return _.defaultsDeep(_.cloneDeep(param), defaultParam) as T; + return _.defaultsDeep(structuredClone(param), defaultParam) as T; }; const addParam = (name: string) => { params.value.push(fillParamDefaultValue({ name })); @@ -57,14 +57,14 @@ export default < params.value.splice(paramIndex, 1); }; const clearParamDefaultValue = (param: UnknownParam): Partial | null => { - const defaultParam = _.cloneDeep(deps.paramsDefaultValue[param.name]); + const defaultParam = structuredClone(deps.paramsDefaultValue[param.name]); if (defaultParam === undefined) throw new Error(`Param ${param.name} not found in paramsDefaultValue`); /** remove subParam.not: false, which previously added by {@link fillParamDefaultValue()} */ if (defaultParam.subParam !== undefined) defaultParam.subParam.not ??= false; - const newParam: Partial = _.cloneDeep(param); // prevent mutating origin param + const newParam: Partial = structuredClone(param); // prevent mutating origin param /** number will consider as empty in {@link _.isEmpty()} */ // to prevent this we use complex short circuit evaluate expression if (!(_.isNumber(newParam.value) || !_.isEmpty(newParam.value)) @@ -223,3 +223,4 @@ export default < generateParamRoute }; }; +export default useQueryForm; diff --git a/fe/src/components/Post/renderers/list/RendererList.vue b/fe/src/components/Post/renderers/list/RendererList.vue index 4b4d30d8..338648ed 100644 --- a/fe/src/components/Post/renderers/list/RendererList.vue +++ b/fe/src/components/Post/renderers/list/RendererList.vue @@ -37,7 +37,7 @@ export type ThreadWithGroupedSubReplies }> }; const posts = computed(() => { // https://github.com/microsoft/TypeScript/issues/33591 - const newPosts = _.cloneDeep(props.initialPosts) as + const newPosts = structuredClone(props.initialPosts) as Modify> }>; newPosts.threads = newPosts.threads.map(thread => { thread.replies = thread.replies.map(reply => { diff --git a/fe/src/main.ts b/fe/src/main.ts index 6f71f797..e9dfa183 100644 --- a/fe/src/main.ts +++ b/fe/src/main.ts @@ -1,12 +1,12 @@ import App from '@/App.vue'; import router from '@/router'; import '@/styles/style.css'; -import 'bootstrap/dist/css/bootstrap.min.css'; import { createApp } from 'vue'; import { createPinia } from 'pinia'; import { VueQueryPlugin } from '@tanstack/vue-query'; import { createHead } from '@unhead/vue'; import 'bootstrap'; +import 'bootstrap/dist/css/bootstrap.min.css'; import 'noty/lib/noty.css'; import 'noty/lib/themes/mint.css'; import nprogress from 'nprogress'; @@ -15,7 +15,7 @@ import 'nprogress/nprogress.css'; nprogress.configure({ trickleSpeed: 200 }); if (import.meta.env.DEV) { - // @ts-expect-error no .d.ts + // @ts-expect-error too small to write a .d.ts for it await import('@/stats'); await import('@/checkCSS'); } diff --git a/fe/src/router/index.ts b/fe/src/router/index.ts index af697884..c271af6d 100644 --- a/fe/src/router/index.ts +++ b/fe/src/router/index.ts @@ -42,7 +42,10 @@ const lazyLoadRouteView = async (lazyComponent: Promise) => { routeLazyComponent.isLoading = true; return lazyComponent - .catch((e: Error) => { notyShow('error', `${e.name}
${e.message}`) }) + .catch((e: unknown) => { + if (e instanceof Error) + notyShow('error', `${e.name}
${e.message}`); + }) .finally(() => { routeLazyComponent.isLoading = false }); }; diff --git a/fe/src/views/BilibiliVote.vue b/fe/src/views/BilibiliVote.vue index c90890f7..dc00a313 100644 --- a/fe/src/views/BilibiliVote.vue +++ b/fe/src/views/BilibiliVote.vue @@ -576,7 +576,7 @@ const loadCharts = { }); // clone last timeline option then transform it to official votes count option - const originalTimelineOptions = _.cloneDeep(options.at(-1)); + const originalTimelineOptions = structuredClone(options.at(-1)); if (originalTimelineOptions === undefined || !_.isArray(originalTimelineOptions.series)) return; _.remove(originalTimelineOptions.series, { id: 'totalVotesValidation' }); diff --git a/fe/yarn.lock b/fe/yarn.lock index 4718fcd3..4eb3ed15 100644 --- a/fe/yarn.lock +++ b/fe/yarn.lock @@ -5685,9 +5685,6 @@ __metadata: vue-eslint-parser: "npm:^9.4.3" vue-router: "npm:^4.3.2" vue-tsc: "npm:^2.0.19" - dependenciesMeta: - eslint@9.4.0: - unplugged: true languageName: unknown linkType: soft