diff --git a/scripts/traverse-rules.ts b/scripts/traverse-rules.ts index a5476c5..5ecffaa 100644 --- a/scripts/traverse-rules.ts +++ b/scripts/traverse-rules.ts @@ -10,6 +10,7 @@ import { import { reactHookRulesInsideReactScope, typescriptRulesExtendEslintRules, + viteTestCompatibleRules, } from '../src/constants.js'; // Recursive function to read files in a directory, this currently assumes that the directory @@ -160,6 +161,7 @@ async function processFile( category: keywordMatch[1], }); + // special case for eslint and typescript alias rules if (scope === 'eslint') { const ruleName = effectiveRuleName.replace(/^.*\//, ''); @@ -170,6 +172,18 @@ async function processFile( category: keywordMatch[1], }); } + + // special case for jest and vitest alias rules + } else if (scope === 'jest') { + const ruleName = effectiveRuleName.replace(/^.*\//, ''); + + if (viteTestCompatibleRules.includes(ruleName)) { + successResultArray.push({ + value: `vitest/${ruleName}`, + scope: 'vitest', + category: keywordMatch[1], + }); + } } } diff --git a/src/__snapshots__/configs.spec.ts.snap b/src/__snapshots__/configs.spec.ts.snap index 44f740f..b681586 100644 --- a/src/__snapshots__/configs.spec.ts.snap +++ b/src/__snapshots__/configs.spec.ts.snap @@ -1424,15 +1424,51 @@ exports[`contains all the oxlint rules 1`] = ` "requireStringLiterals": false, }, ], + "vitest/consistent-test-it": [ + 0, + ], + "vitest/expect-expect": [ + 0, + ], + "vitest/no-alias-methods": [ + 0, + ], + "vitest/no-commented-out-tests": [ + 0, + ], + "vitest/no-conditional-expect": [ + 0, + ], + "vitest/no-conditional-in-test": [ + 0, + ], "vitest/no-conditional-tests": [ 0, ], + "vitest/no-disabled-tests": [ + 0, + ], + "vitest/no-focused-tests": [ + 0, + ], + "vitest/no-identical-title": [ + 0, + ], "vitest/no-import-node-test": [ 0, ], + "vitest/no-restricted-jest-methods": [ + 0, + ], + "vitest/no-test-prefixes": [ + 0, + ], "vitest/prefer-each": [ 0, ], + "vitest/prefer-hooks-in-order": [ + 0, + ], "vitest/prefer-to-be-falsy": [ 0, ], @@ -1445,6 +1481,12 @@ exports[`contains all the oxlint rules 1`] = ` "vitest/require-local-test-context-for-concurrent-snapshots": [ 0, ], + "vitest/valid-describe-callback": [ + 0, + ], + "vitest/valid-expect": [ + 0, + ], "yoda": [ 0, "never", diff --git a/src/build-from-oxlint-config.spec.ts b/src/build-from-oxlint-config.spec.ts index 1429f7b..63d764b 100644 --- a/src/build-from-oxlint-config.spec.ts +++ b/src/build-from-oxlint-config.spec.ts @@ -6,7 +6,10 @@ import { import fs from 'node:fs'; import { execSync } from 'node:child_process'; import type { Linter } from 'eslint'; -import { typescriptRulesExtendEslintRules } from './constants.js'; +import { + typescriptRulesExtendEslintRules, + viteTestCompatibleRules, +} from './constants.js'; describe('buildFromOxlintConfig', () => { describe('rule values', () => { @@ -185,6 +188,22 @@ describe('buildFromOxlintConfig', () => { expect('@next/next/no-img-element' in configs[0].rules!).toBe(false); }); + for (const alias of viteTestCompatibleRules) { + it(`disables vitest jest alias rules for ${alias}`, () => { + for (const rule of [`jest/${alias}`, `vitest/${alias}`]) { + const configs = buildFromOxlintConfig({ + rules: { + [rule]: 'warn', + }, + }); + + expect(configs.length).toBe(1); + expect(configs[0].rules).not.toBeUndefined(); + expect(rule in configs[0].rules!).toBe(true); + } + }); + } + describe('ignorePattern Property', () => { it('should append ignorePatterns to eslint v9 ignore property', () => { const configs = buildFromOxlintConfig({ @@ -406,6 +425,13 @@ describe('integration test with oxlint', () => { ).length; } + // special case for vitest / jest alias rules + if (config.plugins?.includes('vitest')) { + expectedCount += viteTestCompatibleRules.filter( + (aliasRule) => `vitest/${aliasRule}` in configs[0].rules! + ).length; + } + expect(Object.keys(configs[0].rules!).length).toBe(expectedCount); }); } diff --git a/src/constants.ts b/src/constants.ts index d96d059..b11f720 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -15,7 +15,6 @@ export const aliasPluginNames: Record = { }; // Some typescript-eslint rules are re-implemented version of eslint rules. -// e.g. no-array-constructor, max-params, etc... // Since oxlint supports these rules under eslint/* and it also supports TS, // we should override these to make implementation status up-to-date. export const typescriptRulesExtendEslintRules = [ @@ -58,6 +57,25 @@ export const typescriptRulesExtendEslintRules = [ 'space-infix-ops', ]; +// Some vitest rules are re-implemented version of jest rules. +// Since oxlint supports these rules under jest/*, we need to remap them. +export const viteTestCompatibleRules = [ + 'consistent-test-it', + 'expect-expect', + 'no-alias-methods', + 'no-conditional-expect', + 'no-conditional-in-test', + 'no-commented-out-tests', + 'no-disabled-tests', + 'no-focused-tests', + 'no-identical-title', + 'no-restricted-jest-methods', + 'no-test-prefixes', + 'prefer-hooks-in-order', + 'valid-describe-callback', + 'valid-expect', +]; + // All rules from `eslint-plugin-react-hooks` // Since oxlint supports these rules under react/*, we need to remap them. export const reactHookRulesInsideReactScope = [ diff --git a/src/generated/rules-by-category.ts b/src/generated/rules-by-category.ts index 544c625..cb482ca 100644 --- a/src/generated/rules-by-category.ts +++ b/src/generated/rules-by-category.ts @@ -78,6 +78,7 @@ const pedanticRules = { 'unicorn/prefer-string-slice': 'off', 'unicorn/prefer-type-error': 'off', 'unicorn/require-number-to-fixed-digits-argument': 'off', + 'vitest/no-conditional-in-test': 'off', } as const; const restrictionRules = { @@ -254,8 +255,14 @@ const styleRules = { 'unicorn/switch-case-braces': 'off', 'unicorn/text-encoding-identifier-case': 'off', 'unicorn/throw-new-error': 'off', + 'vitest/consistent-test-it': 'off', + 'vitest/no-alias-methods': 'off', + 'vitest/no-identical-title': 'off', 'vitest/no-import-node-test': 'off', + 'vitest/no-restricted-jest-methods': 'off', + 'vitest/no-test-prefixes': 'off', 'vitest/prefer-each': 'off', + 'vitest/prefer-hooks-in-order': 'off', 'vitest/prefer-to-be-falsy': 'off', 'vitest/prefer-to-be-object': 'off', 'vitest/prefer-to-be-truthy': 'off', @@ -421,8 +428,14 @@ const correctnessRules = { 'unicorn/no-useless-spread': 'off', 'unicorn/prefer-set-size': 'off', 'unicorn/prefer-string-starts-ends-with': 'off', + 'vitest/expect-expect': 'off', + 'vitest/no-conditional-expect': 'off', 'vitest/no-conditional-tests': 'off', + 'vitest/no-disabled-tests': 'off', + 'vitest/no-focused-tests': 'off', 'vitest/require-local-test-context-for-concurrent-snapshots': 'off', + 'vitest/valid-describe-callback': 'off', + 'vitest/valid-expect': 'off', } as const; const perfRules = { @@ -458,6 +471,7 @@ const suspiciousRules = { '@typescript-eslint/no-useless-constructor': 'off', 'unicorn/consistent-function-scoping': 'off', 'unicorn/prefer-add-event-listener': 'off', + 'vitest/no-commented-out-tests': 'off', } as const; export { diff --git a/src/generated/rules-by-scope.ts b/src/generated/rules-by-scope.ts index feb0977..3e3d201 100644 --- a/src/generated/rules-by-scope.ts +++ b/src/generated/rules-by-scope.ts @@ -475,13 +475,27 @@ const unicornRules = { } as const; const vitestRules = { + 'vitest/consistent-test-it': 'off', + 'vitest/expect-expect': 'off', + 'vitest/no-alias-methods': 'off', + 'vitest/no-commented-out-tests': 'off', + 'vitest/no-conditional-expect': 'off', + 'vitest/no-conditional-in-test': 'off', 'vitest/no-conditional-tests': 'off', + 'vitest/no-disabled-tests': 'off', + 'vitest/no-focused-tests': 'off', + 'vitest/no-identical-title': 'off', 'vitest/no-import-node-test': 'off', + 'vitest/no-restricted-jest-methods': 'off', + 'vitest/no-test-prefixes': 'off', 'vitest/prefer-each': 'off', + 'vitest/prefer-hooks-in-order': 'off', 'vitest/prefer-to-be-falsy': 'off', 'vitest/prefer-to-be-object': 'off', 'vitest/prefer-to-be-truthy': 'off', 'vitest/require-local-test-context-for-concurrent-snapshots': 'off', + 'vitest/valid-describe-callback': 'off', + 'vitest/valid-expect': 'off', } as const; export {