From db1f9e02ac6943aaeeaddc261fe98569b5141a12 Mon Sep 17 00:00:00 2001 From: Vida Xie Date: Thu, 20 Nov 2025 19:59:36 +0800 Subject: [PATCH 1/8] ci: setup `typescript-eslint` --- docs/.vitepress/config.mts | 10 ++++---- docs/.vitepress/theme/index.ts | 17 +++++-------- docs/.vitepress/vite-plugin.mts | 30 ++++++++++------------- eslint.config.mjs | 42 +++++++++++++++++++++++++++++++-- package.json | 1 + vitest.config.mts | 20 +++++----------- 6 files changed, 70 insertions(+), 50 deletions(-) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 7d198d588..d64579d1b 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -87,12 +87,10 @@ export default async () => { ) return !exists }) - .map(({ ruleId, name }) => { - return { - text: ruleId, - link: `/rules/${name}` - } - }) + .map(({ ruleId, name }) => ({ + text: ruleId, + link: `/rules/${name}` + })) if (children.length === 0) { continue diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index 757e63cab..e1f17198c 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -1,23 +1,18 @@ // @ts-expect-error -- Browser -if (typeof window !== 'undefined') { - if (typeof require === 'undefined') { - // @ts-expect-error -- Browser - ;(window as any).require = () => { - const e = new Error('require is not defined') - ;(e as any).code = 'MODULE_NOT_FOUND' - throw e - } +if (typeof window !== 'undefined' && typeof require === 'undefined') { + // @ts-expect-error -- Browser + ;(window as any).require = () => { + const e = new Error('require is not defined') + ;(e as any).code = 'MODULE_NOT_FOUND' + throw e } } // @ts-expect-error -- Cannot change `module` option import type { Theme } from 'vitepress' // @ts-expect-error -- Cannot change `module` option import DefaultTheme from 'vitepress/theme' -// @ts-expect-error -- ignore import Layout from './Layout.vue' -// @ts-expect-error -- ignore import ESLintCodeBlock from './components/eslint-code-block.vue' -// @ts-expect-error -- ignore import RulesTable from './components/rules-table.vue' const theme: Theme = { diff --git a/docs/.vitepress/vite-plugin.mts b/docs/.vitepress/vite-plugin.mts index cd6811cb6..f0ef5ffad 100644 --- a/docs/.vitepress/vite-plugin.mts +++ b/docs/.vitepress/vite-plugin.mts @@ -34,8 +34,8 @@ export function viteCommonjs(): Plugin { format: 'esm' }) return transformed.code - } catch (e) { - console.error('Transform error. base code:\n' + base, e) + } catch (error) { + console.error(`Transform error. base code:\n${base}`, error) } return undefined } @@ -57,27 +57,23 @@ function transformRequire(code: string) { return match } - let id = - '__' + - moduleString.replace(/[^a-zA-Z0-9_$]+/gu, '_') + - Math.random().toString(32).substring(2) + let id = `__${moduleString.replace( + /[^a-zA-Z0-9_$]+/gu, + '_' + )}${Math.random().toString(32).slice(2)}` while (code.includes(id) || modules.has(id)) { - id += Math.random().toString(32).substring(2) + id += Math.random().toString(32).slice(2) } modules.set(id, moduleString) - return id + '()' + return `${id}()` } ) - return ( - [...modules] - .map(([id, moduleString]) => { - return `import * as __temp_${id} from ${moduleString}; + return `${[...modules] + .map( + ([id, moduleString]) => `import * as __temp_${id} from ${moduleString}; const ${id} = () => __temp_${id}.default || __temp_${id}; ` - }) - .join('') + - ';\n' + - replaced - ) + ) + .join('')};\n${replaced}` } diff --git a/eslint.config.mjs b/eslint.config.mjs index 0fe41f792..52818eae0 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -7,6 +7,8 @@ import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended' import eslintPluginUnicorn from 'eslint-plugin-unicorn' import eslintMarkdown from '@eslint/markdown' import eslintPluginMarkdownPreferences from 'eslint-plugin-markdown-preferences' +import eslintPluginTs from '@typescript-eslint/eslint-plugin' +import tsEslintParser from '@typescript-eslint/parser' import vueEslintParser from 'vue-eslint-parser' import noInvalidMeta from './eslint-internal-rules/no-invalid-meta.js' import noInvalidMetaDocsCategories from './eslint-internal-rules/no-invalid-meta-docs-categories.js' @@ -46,10 +48,13 @@ const MD_LINKS_FOR_DOCS = { ...MD_BASE_LINKS } +const GLOB_TS = '**/*.?([cm])ts' + export default typegen([ { ignores: [ '.nyc_output', + 'eslint-typegen.d.ts', 'coverage', 'node_modules', '.changeset/**/*.md', @@ -78,7 +83,7 @@ export default typegen([ } }, ...defineConfig({ - files: ['**/*.js'], + files: ['**/*.?([cm])[jt]s'], extends: [ eslintPluginEslintPlugin, eslintPluginUnicorn.configs['flat/recommended'] @@ -105,7 +110,21 @@ export default typegen([ }), { - files: ['**/*.js'], + name: 'typescript/setup', + files: [GLOB_TS], + languageOptions: { + parser: tsEslintParser, + parserOptions: { + sourceType: 'module' + } + }, + plugins: { + ts: eslintPluginTs + } + }, + + { + files: ['**/*.?([cm])[jt]s'], languageOptions: { ecmaVersion: 'latest', sourceType: 'commonjs', @@ -249,6 +268,25 @@ export default typegen([ 'internal/require-eslint-community': ['error'] } }, + + { + files: ['**/*.mjs'], + languageOptions: { + sourceType: 'module' + } + }, + + { + files: [GLOB_TS], + rules: { + 'no-undef': 'off', // https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors + 'no-unused-vars': 'off', + 'ts/no-unused-vars': 'error', + 'no-redeclare': 'off', + 'ts/no-redeclare': 'error' + } + }, + { files: ['./**/*.vue'], languageOptions: { diff --git a/package.json b/package.json index 78a103cdf..4eccdd8e9 100644 --- a/package.json +++ b/package.json @@ -90,6 +90,7 @@ "@types/node": "^24.0.8", "@types/semver": "^7.5.8", "@types/xml-name-validator": "^4.0.3", + "@typescript-eslint/eslint-plugin": "^8.35.1", "@typescript-eslint/parser": "^8.35.1", "@typescript-eslint/types": "^8.35.1", "@vitest/coverage-v8": "^3.2.4", diff --git a/vitest.config.mts b/vitest.config.mts index eeef751d6..be1e63448 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -1,11 +1,8 @@ +import { defineConfig } from 'vitest/config' -import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { - include: [ - 'tests/lib/**/*.js', - 'tests/integrations/**/*.js' - ], + include: ['tests/lib/**/*.js', 'tests/integrations/**/*.js'], exclude: [ '**/node_modules/**', '**/dist/**', @@ -18,15 +15,10 @@ export default defineConfig({ coverage: { provider: 'v8', include: ['lib/**/*.js'], - exclude: [ - 'tests/**', - 'dist/**', - 'tools/**', - 'node_modules/**' - ], + exclude: ['tests/**', 'dist/**', 'tools/**', 'node_modules/**'], reporter: ['text', 'lcov', 'json-summary', 'html'], all: true, reportsDirectory: './coverage' - }, - }, -}); + } + } +}) From fbd5b4454ce3e56e0caa51d13faca6fad32a7e51 Mon Sep 17 00:00:00 2001 From: Vida Xie Date: Thu, 20 Nov 2025 22:11:00 +0800 Subject: [PATCH 2/8] chore: fix --- docs/.vitepress/config.mts | 1 + docs/.vitepress/vite-plugin.mts | 3 ++- eslint.config.mjs | 14 +++++++++----- typings/eslint-plugin-vue/util-types/ast/es-ast.ts | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index d64579d1b..20ac9bff5 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -10,6 +10,7 @@ import './build-system/build.mjs' const dirname = path.dirname(fileURLToPath(import.meta.url)) +// eslint-disable-next-line unicorn/no-anonymous-default-export export default async () => { const rulesPath = '../../tools/lib/rules.js' // Avoid bundle const rules: typeof import('../../tools/lib/rules.js') = await import( diff --git a/docs/.vitepress/vite-plugin.mts b/docs/.vitepress/vite-plugin.mts index f0ef5ffad..6901866bc 100644 --- a/docs/.vitepress/vite-plugin.mts +++ b/docs/.vitepress/vite-plugin.mts @@ -11,7 +11,7 @@ const libRoot = path.join(fileURLToPath(import.meta.url), '../../../lib') export function vitePluginRequireResolve(): Plugin { return { name: 'vite-plugin-require.resolve', - transform(code, id, _options) { + transform(code, id) { if (id.startsWith(libRoot)) { return code.replace(/require\.resolve/gu, '(function(){return 0})') } @@ -35,6 +35,7 @@ export function viteCommonjs(): Plugin { }) return transformed.code } catch (error) { + // eslint-disable-next-line no-console console.error(`Transform error. base code:\n${base}`, error) } return undefined diff --git a/eslint.config.mjs b/eslint.config.mjs index 52818eae0..97a6b1d63 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -119,7 +119,7 @@ export default typegen([ } }, plugins: { - ts: eslintPluginTs + '@typescript-eslint': eslintPluginTs } }, @@ -279,11 +279,15 @@ export default typegen([ { files: [GLOB_TS], rules: { - 'no-undef': 'off', // https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors - 'no-unused-vars': 'off', - 'ts/no-unused-vars': 'error', + ...eslintPluginTs.configs.strict.rules, + ...eslintPluginTs.configs['flat/eslint-recommended'].rules, 'no-redeclare': 'off', - 'ts/no-redeclare': 'error' + '@typescript-eslint/no-redeclare': 'error', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-empty-object-type': 'off', + '@typescript-eslint/no-namespace': 'off', + '@typescript-eslint/triple-slash-reference': 'off', + '@typescript-eslint/unified-signatures': 'off' } }, diff --git a/typings/eslint-plugin-vue/util-types/ast/es-ast.ts b/typings/eslint-plugin-vue/util-types/ast/es-ast.ts index 3205dc74e..4297d3d56 100644 --- a/typings/eslint-plugin-vue/util-types/ast/es-ast.ts +++ b/typings/eslint-plugin-vue/util-types/ast/es-ast.ts @@ -351,7 +351,7 @@ export interface PrivateIdentifier extends HasParentNode { } export interface Literal extends HasParentNode { type: 'Literal' - value: string | boolean | null | number | RegExp | BigInt + value: string | boolean | null | number | RegExp | bigint raw: string regex?: { pattern: string From 3fc14310e526f187b66b1862205e1d496096a0e5 Mon Sep 17 00:00:00 2001 From: Vida Xie Date: Thu, 20 Nov 2025 23:19:01 +0800 Subject: [PATCH 3/8] chore: update --- docs/.vitepress/vite-plugin.mts | 25 ++++++++++++------- eslint.config.mjs | 10 +++----- .../util-types/ast/es-ast.ts | 3 ++- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/docs/.vitepress/vite-plugin.mts b/docs/.vitepress/vite-plugin.mts index 6901866bc..588a90cc1 100644 --- a/docs/.vitepress/vite-plugin.mts +++ b/docs/.vitepress/vite-plugin.mts @@ -58,10 +58,11 @@ function transformRequire(code: string) { return match } - let id = `__${moduleString.replace( - /[^a-zA-Z0-9_$]+/gu, - '_' - )}${Math.random().toString(32).slice(2)}` + let id = + // eslint-disable-next-line prefer-template + '__' + + moduleString.replace(/[^a-zA-Z0-9_$]+/gu, '_') + + Math.random().toString(32).slice(2) while (code.includes(id) || modules.has(id)) { id += Math.random().toString(32).slice(2) } @@ -70,11 +71,17 @@ function transformRequire(code: string) { } ) - return `${[...modules] - .map( - ([id, moduleString]) => `import * as __temp_${id} from ${moduleString}; + return ( + // eslint-disable-next-line prefer-template + [...modules] + // eslint-disable-next-line arrow-body-style + .map(([id, moduleString]) => { + return `import * as __temp_${id} from ${moduleString}; const ${id} = () => __temp_${id}.default || __temp_${id}; ` - ) - .join('')};\n${replaced}` + }) + .join('') + + ';\n' + + replaced + ) } diff --git a/eslint.config.mjs b/eslint.config.mjs index 97a6b1d63..dddb5d6f9 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -48,8 +48,6 @@ const MD_LINKS_FOR_DOCS = { ...MD_BASE_LINKS } -const GLOB_TS = '**/*.?([cm])ts' - export default typegen([ { ignores: [ @@ -83,7 +81,7 @@ export default typegen([ } }, ...defineConfig({ - files: ['**/*.?([cm])[jt]s'], + files: ['**/*.{js,mjs,ts,mts}'], extends: [ eslintPluginEslintPlugin, eslintPluginUnicorn.configs['flat/recommended'] @@ -111,7 +109,7 @@ export default typegen([ { name: 'typescript/setup', - files: [GLOB_TS], + files: ['**/*.{ts,mts}'], languageOptions: { parser: tsEslintParser, parserOptions: { @@ -124,7 +122,7 @@ export default typegen([ }, { - files: ['**/*.?([cm])[jt]s'], + files: ['**/*.{js,mjs,ts,mts}'], languageOptions: { ecmaVersion: 'latest', sourceType: 'commonjs', @@ -277,7 +275,7 @@ export default typegen([ }, { - files: [GLOB_TS], + files: ['**/*.{ts,mts}'], rules: { ...eslintPluginTs.configs.strict.rules, ...eslintPluginTs.configs['flat/eslint-recommended'].rules, diff --git a/typings/eslint-plugin-vue/util-types/ast/es-ast.ts b/typings/eslint-plugin-vue/util-types/ast/es-ast.ts index 4297d3d56..4ce1f98c9 100644 --- a/typings/eslint-plugin-vue/util-types/ast/es-ast.ts +++ b/typings/eslint-plugin-vue/util-types/ast/es-ast.ts @@ -351,7 +351,8 @@ export interface PrivateIdentifier extends HasParentNode { } export interface Literal extends HasParentNode { type: 'Literal' - value: string | boolean | null | number | RegExp | bigint + // eslint-disable-next-line @typescript-eslint/no-wrapper-object-types + value: string | boolean | null | number | RegExp | BigInt raw: string regex?: { pattern: string From 5ef89e10c9a23a44c284d3e58cef05967896754a Mon Sep 17 00:00:00 2001 From: Vida Xie Date: Thu, 20 Nov 2025 23:26:32 +0800 Subject: [PATCH 4/8] chore: loose `ban-ts-comment` --- docs/.vitepress/theme/index.ts | 3 +++ eslint.config.mjs | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index e1f17198c..34f6ee9b0 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -11,8 +11,11 @@ if (typeof window !== 'undefined' && typeof require === 'undefined') { import type { Theme } from 'vitepress' // @ts-expect-error -- Cannot change `module` option import DefaultTheme from 'vitepress/theme' +// @ts-expect-error -- ignore import Layout from './Layout.vue' +// @ts-expect-error -- ignore import ESLintCodeBlock from './components/eslint-code-block.vue' +// @ts-expect-error -- ignore import RulesTable from './components/rules-table.vue' const theme: Theme = { diff --git a/eslint.config.mjs b/eslint.config.mjs index dddb5d6f9..985b15d82 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -285,7 +285,13 @@ export default typegen([ '@typescript-eslint/no-empty-object-type': 'off', '@typescript-eslint/no-namespace': 'off', '@typescript-eslint/triple-slash-reference': 'off', - '@typescript-eslint/unified-signatures': 'off' + '@typescript-eslint/unified-signatures': 'off', + '@typescript-eslint/ban-ts-comment': [ + 'error', + { + minimumDescriptionLength: 3 + } + ] } }, From 34e23928f109cfbafdd1e78a6bf43afc22833af3 Mon Sep 17 00:00:00 2001 From: Vida Xie Date: Thu, 20 Nov 2025 23:53:18 +0800 Subject: [PATCH 5/8] chore: update `Literal` value type --- typings/eslint-plugin-vue/util-types/ast/es-ast.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/typings/eslint-plugin-vue/util-types/ast/es-ast.ts b/typings/eslint-plugin-vue/util-types/ast/es-ast.ts index 4ce1f98c9..4297d3d56 100644 --- a/typings/eslint-plugin-vue/util-types/ast/es-ast.ts +++ b/typings/eslint-plugin-vue/util-types/ast/es-ast.ts @@ -351,8 +351,7 @@ export interface PrivateIdentifier extends HasParentNode { } export interface Literal extends HasParentNode { type: 'Literal' - // eslint-disable-next-line @typescript-eslint/no-wrapper-object-types - value: string | boolean | null | number | RegExp | BigInt + value: string | boolean | null | number | RegExp | bigint raw: string regex?: { pattern: string From d7fcd78e089ed579e9853daad46945578178a54b Mon Sep 17 00:00:00 2001 From: Vida Xie Date: Thu, 20 Nov 2025 23:56:02 +0800 Subject: [PATCH 6/8] chore: update --- docs/.eslintrc.js | 7 ------- eslint.config.mjs | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 docs/.eslintrc.js diff --git a/docs/.eslintrc.js b/docs/.eslintrc.js deleted file mode 100644 index 1ec3b7eae..000000000 --- a/docs/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict' - -module.exports = { - parserOptions: { - sourceType: 'module' - } -} diff --git a/eslint.config.mjs b/eslint.config.mjs index 985b15d82..67713ac7b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -268,7 +268,7 @@ export default typegen([ }, { - files: ['**/*.mjs'], + files: ['**/*.{mjs,mts}'], languageOptions: { sourceType: 'module' } From caeefb2c07bdbad1ccf8a4fe6366958a2281253b Mon Sep 17 00:00:00 2001 From: Vida Xie Date: Thu, 20 Nov 2025 23:57:13 +0800 Subject: [PATCH 7/8] chore: update --- docs/.vitepress/vite-plugin.mts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/.vitepress/vite-plugin.mts b/docs/.vitepress/vite-plugin.mts index 588a90cc1..bd9c9090b 100644 --- a/docs/.vitepress/vite-plugin.mts +++ b/docs/.vitepress/vite-plugin.mts @@ -74,12 +74,11 @@ function transformRequire(code: string) { return ( // eslint-disable-next-line prefer-template [...modules] - // eslint-disable-next-line arrow-body-style - .map(([id, moduleString]) => { - return `import * as __temp_${id} from ${moduleString}; + .map( + ([id, moduleString]) => `import * as __temp_${id} from ${moduleString}; const ${id} = () => __temp_${id}.default || __temp_${id}; ` - }) + ) .join('') + ';\n' + replaced From 39642369d2d8b0b08214deb7012693ba74bddca2 Mon Sep 17 00:00:00 2001 From: Vida Xie Date: Thu, 20 Nov 2025 23:59:20 +0800 Subject: [PATCH 8/8] chore: update --- eslint.config.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 67713ac7b..044005f23 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -268,7 +268,7 @@ export default typegen([ }, { - files: ['**/*.{mjs,mts}'], + files: ['**/*.{mjs,ts,mts}'], languageOptions: { sourceType: 'module' }