diff --git a/README.md b/README.md index e649c70..c3e18c6 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,6 @@ And then you can add the following script to your `package.json`: // turn eslint rules off by oxlint category 'flat/pedantic': { rules: [Object] }, - 'flat/nursery': { rules: [Object] }, 'flat/style': { rules: [Object] }, 'flat/correctness': { rules: [Object] }, 'flat/restriction': { rules: [Object] }, diff --git a/scripts/constants.ts b/scripts/constants.ts index edd3ea8..8dd0572 100644 --- a/scripts/constants.ts +++ b/scripts/constants.ts @@ -10,6 +10,9 @@ export const SPARSE_CLONE_DIRECTORY = 'crates/oxc_linter/src'; // these are the rules that don't have a direct equivalent in the eslint rules export const ignoreScope = new Set(['oxc', 'deepscan', 'security']); +// these are the rules that are not fully implemented in oxc +export const ignoreCategories = new Set(['nursery']); + export function convertScope(scope: string) { return Reflect.has(aliasPluginNames, scope) ? aliasPluginNames[scope as 'eslint'] diff --git a/scripts/traverse-rules.ts b/scripts/traverse-rules.ts index d9ee7cb..39d5b49 100644 --- a/scripts/traverse-rules.ts +++ b/scripts/traverse-rules.ts @@ -1,6 +1,7 @@ import { promises } from 'node:fs'; import path from 'node:path'; import { + ignoreCategories, ignoreScope, prefixScope, SPARSE_CLONE_DIRECTORY, @@ -70,13 +71,6 @@ async function processFile( ): Promise { const content = await promises.readFile(filePath, 'utf8'); - // find the correct macro block where `);` or `}` is the end of the block - // ensure that the `);` or `}` is on its own line, with no characters before it - const blockRegex = - /declare_oxc_lint!\s*(\(([\S\s]*?)^\s*\)\s*;?|\s*{([\S\s]*?)^\s*}\s)/gm; - - let match = blockRegex.exec(content); - // 'ok' way to get the scope, depends on the directory structure let scope = getFolderNameUnderRules(filePath); const shouldIgnoreRule = ignoreScope.has(scope); @@ -111,6 +105,13 @@ async function processFile( return; } + // find the correct macro block where `);` or `}` is the end of the block + // ensure that the `);` or `}` is on its own line, with no characters before it + const blockRegex = + /declare_oxc_lint!\s*(\(([\S\s]*?)^\s*\)\s*;?|\s*{([\S\s]*?)^\s*}\s)/gm; + + let match = blockRegex.exec(content); + if (match === null) { failureResultArray.push({ value: effectiveRuleName, @@ -118,9 +119,10 @@ async function processFile( category: 'unknown', error: 'No match block for `declare_oxc_lint`', }); + return; } - while (match !== null) { + do { const block = match[2] ?? match[3]; // Remove comments to prevent them from affecting the regex @@ -134,35 +136,43 @@ async function processFile( const keywordRegex = /,\s*(\w+)\s*,?\s*(?:(\w+)\s*,?\s*)?$/; const keywordMatch = keywordRegex.exec(cleanBlock); - if (keywordMatch) { - successResultArray.push({ - value: effectiveRuleName, - scope: scope, - category: keywordMatch[1], - }); - - if (scope === 'eslint') { - const ruleName = effectiveRuleName.replace(/^.*\//, ''); - - if (typescriptRulesExtendEslintRules.includes(ruleName)) { - successResultArray.push({ - value: `@typescript-eslint/${ruleName}`, - scope: 'typescript', - category: keywordMatch[1], - }); - } - } - } else { + if (!keywordMatch) { failureResultArray.push({ value: effectiveRuleName, scope: `unknown: ${scope}`, category: 'unknown', error: 'Could not extract keyword from macro block', }); + continue; } - match = blockRegex.exec(content); // Update match for the next iteration - } + if (ignoreCategories.has(keywordMatch[1])) { + skippedResultArray.push({ + value: effectiveRuleName, + scope: scope, + category: keywordMatch[1], + }); + continue; + } + + successResultArray.push({ + value: effectiveRuleName, + scope: scope, + category: keywordMatch[1], + }); + + if (scope === 'eslint') { + const ruleName = effectiveRuleName.replace(/^.*\//, ''); + + if (typescriptRulesExtendEslintRules.includes(ruleName)) { + successResultArray.push({ + value: `@typescript-eslint/${ruleName}`, + scope: 'typescript', + category: keywordMatch[1], + }); + } + } + } while ((match = blockRegex.exec(content))); } export function getFolderNameUnderRules(filePath: string) { diff --git a/src/__snapshots__/build-from-oxlint-config.spec.ts.snap b/src/__snapshots__/build-from-oxlint-config.spec.ts.snap index 8d79c9c..9dca402 100644 --- a/src/__snapshots__/build-from-oxlint-config.spec.ts.snap +++ b/src/__snapshots__/build-from-oxlint-config.spec.ts.snap @@ -5,13 +5,15 @@ exports[`buildFromOxlintConfig > custom plugins, custom categories > customPlugi { "name": "oxlint/from-oxlint-config", "rules": { - "constructor-super": "off", - "getter-return": "off", - "import/export": "off", - "import/no-deprecated": "off", - "import/no-unused-modules": "off", - "no-undef": "off", - "no-unreachable": "off", + "import/no-duplicates": "off", + "import/no-named-as-default": "off", + "import/no-named-as-default-member": "off", + "import/no-self-import": "off", + "no-extend-native": "off", + "no-new": "off", + "no-unexpected-multiline": "off", + "no-useless-concat": "off", + "no-useless-constructor": "off", }, }, ] diff --git a/src/__snapshots__/configs.spec.ts.snap b/src/__snapshots__/configs.spec.ts.snap index 719af67..77f3840 100644 --- a/src/__snapshots__/configs.spec.ts.snap +++ b/src/__snapshots__/configs.spec.ts.snap @@ -83,9 +83,6 @@ exports[`contains all the oxlint rules 1`] = ` "@typescript-eslint/consistent-type-definitions": [ 0, ], - "@typescript-eslint/consistent-type-imports": [ - 0, - ], "@typescript-eslint/default-param-last": [ 0, ], @@ -217,9 +214,6 @@ exports[`contains all the oxlint rules 1`] = ` "checkForEach": false, }, ], - "constructor-super": [ - 0, - ], "default-case": [ 0, {}, @@ -241,19 +235,12 @@ exports[`contains all the oxlint rules 1`] = ` "always", {}, ], - "getter-return": [ - 0, - {}, - ], "guard-for-in": [ 0, ], "import/default": [ 0, ], - "import/export": [ - 0, - ], "import/first": [ 0, ], @@ -281,9 +268,6 @@ exports[`contains all the oxlint rules 1`] = ` "import/no-default-export": [ 0, ], - "import/no-deprecated": [ - 0, - ], "import/no-duplicates": [ 0, ], @@ -299,9 +283,6 @@ exports[`contains all the oxlint rules 1`] = ` "import/no-self-import": [ 0, ], - "import/no-unused-modules": [ - 0, - ], "import/no-webpack-loader-syntax": [ 0, ], @@ -853,19 +834,12 @@ exports[`contains all the oxlint rules 1`] = ` "no-throw-literal": [ 0, ], - "no-undef": [ - 0, - {}, - ], "no-undefined": [ 0, ], "no-unexpected-multiline": [ 0, ], - "no-unreachable": [ - 0, - ], "no-unsafe-finally": [ 0, ], @@ -939,9 +913,6 @@ exports[`contains all the oxlint rules 1`] = ` "promise/no-new-statics": [ 0, ], - "promise/no-return-in-finally": [ - 0, - ], "promise/param-names": [ 0, ], @@ -961,12 +932,6 @@ exports[`contains all the oxlint rules 1`] = ` 0, "always", ], - "react-hooks/exhaustive-deps": [ - 0, - ], - "react-hooks/rules-of-hooks": [ - 0, - ], "react-perf/jsx-no-jsx-as-prop": [ 0, ], @@ -1057,9 +1022,6 @@ exports[`contains all the oxlint rules 1`] = ` "react/react-in-jsx-scope": [ 0, ], - "react/require-render-return": [ - 0, - ], "react/self-closing-comp": [ 0, ], diff --git a/src/build-from-oxlint-config.spec.ts b/src/build-from-oxlint-config.spec.ts index d07cdbb..3d46221 100644 --- a/src/build-from-oxlint-config.spec.ts +++ b/src/build-from-oxlint-config.spec.ts @@ -101,7 +101,7 @@ describe('buildFromOxlintConfig', () => { buildFromOxlintConfig({ plugins: ['import'], categories: { - nursery: 'warn', + suspicious: 'warn', correctness: 'off', }, }) @@ -112,17 +112,17 @@ describe('buildFromOxlintConfig', () => { const configs = buildFromOxlintConfig({ plugins: ['import'], categories: { - nursery: 'warn', + suspicious: 'warn', correctness: 'off', }, rules: { - 'import/no-unused-modules': 'off', + 'import/no-self-import': 'off', }, }); expect(configs.length).toBe(1); expect(configs[0].rules).not.toBeUndefined(); - expect('import/no-unused-modules' in configs[0].rules!).toBe(false); + expect('import/no-self-import' in configs[0].rules!).toBe(false); }); // look here: @@ -134,7 +134,7 @@ describe('buildFromOxlintConfig', () => { 'react_perf/jsx-no-new-array-as-prop': 'warn', 'nextjs/no-img-element': 'warn', 'jsx_a11y/alt-text': 'warn', - 'react/rules-of-hooks': 'warn', + // 'react/rules-of-hooks': 'warn', -- rules are currently in nursery // 'deepscan/xxx': 'warn', }, }); @@ -148,7 +148,7 @@ describe('buildFromOxlintConfig', () => { ); expect('@next/next/no-img-element' in configs[0].rules!).toBe(true); expect('jsx-a11y/alt-text' in configs[0].rules!).toBe(true); - expect('react-hooks/rules-of-hooks' in configs[0].rules!).toBe(true); + // expect('react-hooks/rules-of-hooks' in configs[0].rules!).toBe(true); -- rules are currently in nursery }); it('detects rules without plugin name', () => { @@ -307,7 +307,7 @@ describe('integration test with oxlint', () => { { plugins: ['vite'], rules: { eqeqeq: 'off' } }, // categories change - { categories: { correctness: 'off', nusery: 'warn' } }, + { categories: { correctness: 'off', suspicious: 'warn' } }, // combination plugin + categires + rules { plugins: ['vite'], @@ -317,8 +317,8 @@ describe('integration test with oxlint', () => { // all categories enabled { categories: { + nursery: 'off', // we not support this category correctness: 'warn', - nursery: 'warn', pedantic: 'warn', perf: 'warn', restriction: 'warn', @@ -360,8 +360,8 @@ describe('integration test with oxlint', () => { 'vitest', ], categories: { + nursery: 'off', // we not support this category correctness: 'warn', - nursery: 'off', // ToDo: something with the import plugin pedantic: 'warn', perf: 'warn', restriction: 'warn', diff --git a/src/generated/configs-by-category.ts b/src/generated/configs-by-category.ts index 749eda9..aa6e90d 100644 --- a/src/generated/configs-by-category.ts +++ b/src/generated/configs-by-category.ts @@ -7,11 +7,6 @@ const pedanticConfig = { rules: rules.pedanticRules, }; -const nurseryConfig = { - name: 'oxlint/nursery', - rules: rules.nurseryRules, -}; - const restrictionConfig = { name: 'oxlint/restriction', rules: rules.restrictionRules, @@ -39,7 +34,6 @@ const suspiciousConfig = { const configByCategory = { 'flat/pedantic': pedanticConfig, - 'flat/nursery': nurseryConfig, 'flat/restriction': restrictionConfig, 'flat/style': styleConfig, 'flat/correctness': correctnessConfig, diff --git a/src/generated/configs-by-scope.ts b/src/generated/configs-by-scope.ts index 5e51f64..2c3c63a 100644 --- a/src/generated/configs-by-scope.ts +++ b/src/generated/configs-by-scope.ts @@ -47,11 +47,6 @@ const reactConfig = { rules: rules.reactRules, }; -const reactHooksConfig = { - name: 'oxlint/react-hooks', - rules: rules.reactHooksRules, -}; - const reactPerfConfig = { name: 'oxlint/react-perf', rules: rules.reactPerfRules, @@ -82,7 +77,6 @@ const configByScope = { 'flat/node': nodeConfig, 'flat/promise': promiseConfig, 'flat/react': reactConfig, - 'flat/react-hooks': reactHooksConfig, 'flat/react-perf': reactPerfConfig, 'flat/typescript': typescriptConfig, 'flat/unicorn': unicornConfig, diff --git a/src/generated/rules-by-category.ts b/src/generated/rules-by-category.ts index e9a3f92..6f9451d 100644 --- a/src/generated/rules-by-category.ts +++ b/src/generated/rules-by-category.ts @@ -78,21 +78,6 @@ const pedanticRules = { 'unicorn/require-number-to-fixed-digits-argument': 'off', } as const; -const nurseryRules = { - 'constructor-super': 'off', - 'getter-return': 'off', - 'no-undef': 'off', - 'no-unreachable': 'off', - 'import/export': 'off', - 'import/no-deprecated': 'off', - 'import/no-unused-modules': 'off', - 'promise/no-return-in-finally': 'off', - 'react/require-render-return': 'off', - 'react-hooks/exhaustive-deps': 'off', - 'react-hooks/rules-of-hooks': 'off', - '@typescript-eslint/consistent-type-imports': 'off', -} as const; - const restrictionRules = { 'default-case': 'off', 'no-alert': 'off', @@ -464,7 +449,6 @@ const suspiciousRules = { export { pedanticRules, - nurseryRules, restrictionRules, styleRules, correctnessRules, diff --git a/src/generated/rules-by-scope.ts b/src/generated/rules-by-scope.ts index 0456f10..db83094 100644 --- a/src/generated/rules-by-scope.ts +++ b/src/generated/rules-by-scope.ts @@ -2,14 +2,12 @@ const eslintRules = { 'array-callback-return': 'off', - 'constructor-super': 'off', 'default-case': 'off', 'default-case-last': 'off', 'default-param-last': 'off', eqeqeq: 'off', 'for-direction': 'off', 'func-names': 'off', - 'getter-return': 'off', 'guard-for-in': 'off', 'max-classes-per-file': 'off', 'max-lines': 'off', @@ -84,10 +82,8 @@ const eslintRules = { 'no-ternary': 'off', 'no-this-before-super': 'off', 'no-throw-literal': 'off', - 'no-undef': 'off', 'no-undefined': 'off', 'no-unexpected-multiline': 'off', - 'no-unreachable': 'off', 'no-unsafe-finally': 'off', 'no-unsafe-negation': 'off', 'no-unsafe-optional-chaining': 'off', @@ -119,7 +115,6 @@ const eslintRules = { const importRules = { 'import/default': 'off', - 'import/export': 'off', 'import/first': 'off', 'import/import-no-namespace': 'off', 'import/max-dependencies': 'off', @@ -129,13 +124,11 @@ const importRules = { 'import/no-commonjs': 'off', 'import/no-cycle': 'off', 'import/no-default-export': 'off', - 'import/no-deprecated': 'off', 'import/no-duplicates': 'off', 'import/no-dynamic-require': 'off', 'import/no-named-as-default': 'off', 'import/no-named-as-default-member': 'off', 'import/no-self-import': 'off', - 'import/no-unused-modules': 'off', 'import/no-webpack-loader-syntax': 'off', 'import/unambiguous': 'off', } as const; @@ -275,7 +268,6 @@ const promiseRules = { 'promise/catch-or-return': 'off', 'promise/no-callback-in-promise': 'off', 'promise/no-new-statics': 'off', - 'promise/no-return-in-finally': 'off', 'promise/param-names': 'off', 'promise/prefer-await-to-callbacks': 'off', 'promise/prefer-await-to-then': 'off', @@ -310,17 +302,11 @@ const reactRules = { 'react/no-unknown-property': 'off', 'react/prefer-es6-class': 'off', 'react/react-in-jsx-scope': 'off', - 'react/require-render-return': 'off', 'react/self-closing-comp': 'off', 'react/style-prop-object': 'off', 'react/void-dom-elements-no-children': 'off', } as const; -const reactHooksRules = { - 'react-hooks/exhaustive-deps': 'off', - 'react-hooks/rules-of-hooks': 'off', -} as const; - const reactPerfRules = { 'react-perf/jsx-no-jsx-as-prop': 'off', 'react-perf/jsx-no-new-array-as-prop': 'off', @@ -336,7 +322,6 @@ const typescriptRules = { '@typescript-eslint/ban-types': 'off', '@typescript-eslint/consistent-indexed-object-style': 'off', '@typescript-eslint/consistent-type-definitions': 'off', - '@typescript-eslint/consistent-type-imports': 'off', '@typescript-eslint/default-param-last': 'off', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/max-params': 'off', @@ -493,7 +478,6 @@ export { nodeRules, promiseRules, reactRules, - reactHooksRules, reactPerfRules, typescriptRules, unicornRules,