├── .node-version ├── .husky └── pre-commit ├── .mise.toml ├── .prettierignore ├── .oxfmtrc.json ├── .vscode ├── extensions.json └── settings.json ├── test └── helpers.ts ├── src ├── index.ts ├── build-from-oxlint-config │ ├── ignore-patterns.spec.ts │ ├── plugins.ts │ ├── ignore-patterns.ts │ ├── types.ts │ ├── utilities.ts │ ├── overrides.ts │ ├── overrides.spec.ts │ ├── extends.ts │ ├── categories.ts │ ├── categories.spec.ts │ ├── index.ts │ ├── rules.ts │ ├── extends.spec.ts │ ├── rules.spec.ts │ └── __snapshots__ │ │ └── categories.spec.ts.snap ├── generated │ ├── configs-by-category.ts │ ├── configs-by-scope.ts │ ├── rules-by-scope.ts │ └── rules-by-category.ts ├── configs.ts ├── configs.spec.ts ├── config-helper.ts ├── config-helper.spec.ts ├── constants.ts └── build-from-oxlint-config.spec.ts ├── .oxlintrc.json ├── eslint.config.ts ├── .github ├── renovate.json └── workflows │ ├── build.yml │ ├── test.yml │ ├── generate.yml │ ├── ci_security.yml │ ├── release.yml │ └── bump_oxlint.yml ├── vite.config.ts ├── scripts ├── __snapshots__ │ └── rules-generator.test.ts.snap ├── generate.ts ├── rules-generator.test.ts ├── constants.ts ├── config-generator.ts ├── traverse-rules.ts └── rules-generator.ts ├── LICENSE ├── .gitignore ├── package.json ├── README.md └── tsconfig.json /.node-version: -------------------------------------------------------------------------------- 1 | 22.14.0 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx lint-staged 2 | -------------------------------------------------------------------------------- /.mise.toml: -------------------------------------------------------------------------------- 1 | [tools] 2 | pnpm = "10.25.0" 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | pnpm-lock.yaml 2 | dist/ 3 | ~ 4 | -------------------------------------------------------------------------------- /.oxfmtrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/oxfmt/configuration_schema.json", 3 | "trailingComma": "es5", 4 | "tabWidth": 2, 5 | "semi": true, 6 | "singleQuote": true 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "esbenp.prettier-vscode", 5 | "oxc.oxc-vscode", 6 | "vitest.explorer" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /test/helpers.ts: -------------------------------------------------------------------------------- 1 | import type { ESLint } from 'eslint'; 2 | import oxlint from '../src/index.js'; 3 | 4 | export const ESLintTestConfig: ESLint.Options = { 5 | baseConfig: oxlint.configs['flat/all'], 6 | overrideConfigFile: true, 7 | }; 8 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import configs from './configs.js'; 2 | import { 3 | buildFromOxlintConfig, 4 | buildFromOxlintConfigFile, 5 | } from './build-from-oxlint-config/index.js'; 6 | 7 | export default { 8 | configs, 9 | buildFromOxlintConfig, 10 | buildFromOxlintConfigFile, 11 | }; 12 | -------------------------------------------------------------------------------- /.oxlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["unicorn", "typescript", "oxc"], 3 | "categories": { 4 | "correctness": "error", 5 | "suspicious": "error" 6 | }, 7 | "rules": { 8 | "@typescript-eslint/no-unsafe-type-assertion": "off" // FIX: ignore them inline or fix them 9 | }, 10 | "ignorePatterns": ["dist/"] 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Editor values 3 | "editor.formatOnSave": true, 4 | // Eslint Configuration 5 | "eslint.workingDirectories": ["."], 6 | "editor.codeActionsOnSave": { 7 | "source.fixAll.eslint": "explicit", 8 | "source.organizeImports": "never" 9 | }, 10 | "editor.defaultFormatter": "oxc.oxc-vscode", 11 | "eslint.validate": ["javascript", "typescript"] 12 | } 13 | -------------------------------------------------------------------------------- /eslint.config.ts: -------------------------------------------------------------------------------- 1 | import oxlint from './src/index.js'; 2 | import unicorn from 'eslint-plugin-unicorn'; 3 | import eslint from '@eslint/js'; 4 | import tseslint from 'typescript-eslint'; 5 | 6 | export default [ 7 | eslint.configs.recommended, 8 | unicorn.configs['flat/recommended'], 9 | ...tseslint.configs.recommended, 10 | ...oxlint.buildFromOxlintConfigFile('.oxlintrc.json', { 11 | typeAware: true, 12 | }), 13 | ]; 14 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/ignore-patterns.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { handleIgnorePatternsScope } from './ignore-patterns.js'; 3 | 4 | describe('ignorePattern handleIgnorePatternsScope', () => { 5 | it('should append ignorePatterns to eslint v9 ignore property', () => { 6 | const config = {}; 7 | handleIgnorePatternsScope(['./tests/.*ts'], config); 8 | 9 | // @ts-expect-error -- ignores does not exists 10 | expect(config.ignores).toStrictEqual(['./tests/.*ts']); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["github>Boshen/renovate", "helpers:pinGitHubActionDigestsToSemver"], 4 | "packageRules": [ 5 | { 6 | "groupName": "npm packages", 7 | "matchManagers": ["npm"], 8 | "ignoreDeps": ["oxlint", "oxlint-tsgolint"] 9 | }, 10 | { 11 | "groupName": "oxlint", 12 | "matchManagers": ["npm"], 13 | "matchPackageNames": ["oxlint", "oxlint-tsgolint"], 14 | "schedule": ["at any time"] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize] 6 | push: 7 | branches: 8 | - main 9 | 10 | permissions: {} 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 18 | with: 19 | persist-credentials: false 20 | 21 | - uses: oxc-project/setup-node@141eb77546de6702f92d320926403fe3f9f6a6f2 # v1.0.5 22 | 23 | - name: Build 24 | run: pnpm run build 25 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/plugins.ts: -------------------------------------------------------------------------------- 1 | import { OxlintConfig, OxlintConfigOverride, OxlintConfigPlugins } from './types.js'; 2 | 3 | // default plugins, see 4 | export const defaultPlugins: OxlintConfigPlugins = ['unicorn', 'typescript']; 5 | 6 | /** 7 | * tries to return the "plugins" section from the config. 8 | * it returns `undefined` when not found or invalid. 9 | */ 10 | export const readPluginsFromConfig = ( 11 | config: OxlintConfig | OxlintConfigOverride 12 | ): OxlintConfigPlugins | undefined => { 13 | return 'plugins' in config && Array.isArray(config.plugins) ? config.plugins : undefined; 14 | }; 15 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/ignore-patterns.ts: -------------------------------------------------------------------------------- 1 | import { EslintPluginOxlintConfig, OxlintConfig, OxlintConfigIgnorePatterns } from './types.js'; 2 | 3 | /** 4 | * adds the ignore patterns to the config 5 | */ 6 | export const handleIgnorePatternsScope = ( 7 | ignorePatterns: OxlintConfigIgnorePatterns, 8 | config: EslintPluginOxlintConfig 9 | ): void => { 10 | config.ignores = ignorePatterns; 11 | }; 12 | /** 13 | * tries to return the "ignorePattern" section from the config. 14 | * it returns `undefined` when not found or invalid. 15 | */ 16 | export const readIgnorePatternsFromConfig = ( 17 | config: OxlintConfig 18 | ): OxlintConfigIgnorePatterns | undefined => { 19 | return 'ignorePatterns' in config && Array.isArray(config.ignorePatterns) 20 | ? config.ignorePatterns 21 | : undefined; 22 | }; 23 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize] 6 | push: 7 | branches: 8 | - main 9 | 10 | permissions: {} 11 | 12 | jobs: 13 | test: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 18 | with: 19 | persist-credentials: false 20 | 21 | - uses: oxc-project/setup-node@141eb77546de6702f92d320926403fe3f9f6a6f2 # v1.0.5 22 | 23 | - name: Run oxlint 24 | run: npx oxlint --tsconfig=tsconfig.json 25 | 26 | - name: Run eslint 27 | run: npx eslint 28 | 29 | - name: Run Format (oxfmt) 30 | run: npx oxfmt . --check 31 | 32 | - name: Type Check 33 | run: npx tsc --noEmit 34 | 35 | - name: Run tests 36 | run: pnpm run test --coverage 37 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import dts from 'vite-plugin-dts'; 3 | import { defineConfig } from 'vitest/config'; 4 | 5 | export default defineConfig({ 6 | test: { 7 | coverage: { 8 | include: ['src', 'scripts'], 9 | }, 10 | }, 11 | build: { 12 | lib: { 13 | entry: [path.resolve(import.meta.dirname, 'src/index.ts')], 14 | fileName: (format, entryName) => { 15 | return `${entryName}.${format === 'es' ? 'mjs' : 'cjs'}`; 16 | }, 17 | name: 'eslint-plugin-oxlint', 18 | formats: ['cjs', 'es'], 19 | }, 20 | rollupOptions: { 21 | external: (id: string) => !id.startsWith('.') && !path.isAbsolute(id), 22 | output: { 23 | preserveModules: true, 24 | }, 25 | }, 26 | minify: false, 27 | }, 28 | plugins: [ 29 | dts({ 30 | include: 'src/**', 31 | exclude: 'src/**/*.spec.ts', 32 | }), 33 | ], 34 | }); 35 | -------------------------------------------------------------------------------- /scripts/__snapshots__/rules-generator.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`RulesGenerator > RulesGenerator generates rules correctly > byCategory 1`] = ` 4 | "// These rules are automatically generated by scripts/generate-rules.ts 5 | 6 | const styleRules: Record = { 7 | 'rulename-with-mod': "off" 8 | }; 9 | 10 | const correctnessRules: Record = { 11 | '@typescript-eslint/rulename-without-mod': "off" 12 | }; 13 | 14 | export { 15 | styleRules, 16 | correctnessRules 17 | }" 18 | `; 19 | 20 | exports[`RulesGenerator > RulesGenerator generates rules correctly > byScope 1`] = ` 21 | "// These rules are automatically generated by scripts/generate-rules.ts 22 | 23 | const eslintRules: Record = { 24 | 'rulename-with-mod': "off" 25 | }; 26 | 27 | const typescriptRules: Record = { 28 | '@typescript-eslint/rulename-without-mod': "off" 29 | }; 30 | 31 | export { 32 | eslintRules, 33 | typescriptRules 34 | }" 35 | `; 36 | -------------------------------------------------------------------------------- /scripts/generate.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import path from 'node:path'; 3 | import { RulesGenerator, RulesGrouping } from './rules-generator.js'; 4 | import { ConfigGenerator } from './config-generator.js'; 5 | import { traverseRules } from './traverse-rules.js'; 6 | 7 | const __dirname = new URL('.', import.meta.url).pathname; 8 | 9 | const successResultArray = traverseRules(); 10 | 11 | const rulesGenerator = new RulesGenerator(successResultArray); 12 | const configGenerator = new ConfigGenerator(successResultArray); 13 | 14 | const generateFolder = path.resolve(__dirname, '..', `src/generated`); 15 | 16 | if (!fs.existsSync(generateFolder)) { 17 | fs.mkdirSync(generateFolder); 18 | } 19 | 20 | const promises = [rulesGenerator, configGenerator].map(async (generator) => { 21 | generator.setRulesGrouping(RulesGrouping.SCOPE); 22 | await generator.generateRules(generateFolder); 23 | generator.setRulesGrouping(RulesGrouping.CATEGORY); 24 | await generator.generateRules(generateFolder); 25 | }); 26 | 27 | await Promise.all(promises); 28 | -------------------------------------------------------------------------------- /scripts/rules-generator.test.ts: -------------------------------------------------------------------------------- 1 | import { test, suite, expect } from 'vitest'; 2 | import { RulesGenerator, RulesGrouping } from './rules-generator.js'; 3 | import type { Rule } from './traverse-rules.js'; 4 | 5 | suite('RulesGenerator', () => { 6 | test('RulesGenerator generates rules correctly', async () => { 7 | const successResultArray: Rule[] = [ 8 | { 9 | category: 'style', 10 | scope: 'eslint', 11 | value: 'rulename-with-mod', 12 | }, 13 | { 14 | category: 'correctness', 15 | scope: 'typescript', 16 | value: '@typescript-eslint/rulename-without-mod', 17 | }, 18 | ]; 19 | 20 | // Create an instance of RulesGenerator 21 | const generator = new RulesGenerator(successResultArray, RulesGrouping.SCOPE); 22 | 23 | // Call the generateRules method 24 | expect(generator.generateRulesCode()).toMatchSnapshot('byScope'); 25 | 26 | generator.setRulesGrouping(RulesGrouping.CATEGORY); 27 | // Call the generateRules method 28 | expect(generator.generateRulesCode()).toMatchSnapshot('byCategory'); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/types.ts: -------------------------------------------------------------------------------- 1 | import type { Linter } from 'eslint'; 2 | 3 | export type BuildFromOxlintConfigOptions = { 4 | withNursery?: boolean; 5 | typeAware?: boolean; 6 | }; 7 | 8 | export type OxlintConfigExtends = string[]; 9 | 10 | export type OxlintConfigPlugins = string[]; 11 | 12 | export type OxlintConfigCategories = Record; 13 | 14 | export type OxlintConfigRules = Record; 15 | 16 | export type OxlintConfigIgnorePatterns = string[]; 17 | 18 | export type EslintPluginOxlintConfig = Linter.Config>; 19 | 20 | export type OxlintConfigOverride = { 21 | files: string[]; 22 | plugins?: OxlintConfigPlugins; 23 | rules?: OxlintConfigRules; 24 | }; 25 | 26 | export type OxlintConfig = { 27 | [key: string]: unknown; 28 | extends?: OxlintConfigExtends; 29 | plugins?: OxlintConfigPlugins; 30 | categories?: OxlintConfigCategories; 31 | rules?: OxlintConfigRules; 32 | ignorePatterns?: OxlintConfigIgnorePatterns; 33 | 34 | // extra properties only used by `eslint-plugin-oxlint` 35 | __misc?: { 36 | // absolute path to the config file 37 | filePath: string; 38 | }; 39 | }; 40 | -------------------------------------------------------------------------------- /.github/workflows/generate.yml: -------------------------------------------------------------------------------- 1 | name: Code generation 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | types: [opened, synchronize] 7 | paths: 8 | - 'pnpm-lock.yaml' 9 | - 'scripts/**' 10 | - 'src/constants.ts' 11 | - '.github/workflows/generate.yml' 12 | push: 13 | branches: 14 | - main 15 | paths: 16 | - 'pnpm-lock.yaml' 17 | - 'scripts/**' 18 | - 'src/constants.ts' 19 | - '.github/workflows/generate.yml' 20 | 21 | permissions: {} 22 | 23 | jobs: 24 | generate: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 29 | with: 30 | persist-credentials: false 31 | 32 | - uses: oxc-project/setup-node@141eb77546de6702f92d320926403fe3f9f6a6f2 # v1.0.5 33 | 34 | - name: Remove current generated code 35 | run: rm -r ./src/generated/ 36 | 37 | - name: Generate from source code 38 | run: pnpm run generate 39 | 40 | - name: Format generated code 41 | run: pnpm run format 42 | 43 | - name: Check for git diff 44 | run: git diff --exit-code 45 | -------------------------------------------------------------------------------- /.github/workflows/ci_security.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Actions Security Analysis 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | types: [opened, synchronize] 7 | paths: 8 | - '.github/workflows/**' 9 | push: 10 | branches: 11 | - main 12 | paths: 13 | - '.github/workflows/**' 14 | 15 | permissions: {} 16 | 17 | jobs: 18 | zizmor: 19 | name: zizmor 20 | runs-on: ubuntu-latest 21 | permissions: 22 | security-events: write 23 | steps: 24 | - name: Checkout repository 25 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 26 | with: 27 | persist-credentials: false 28 | 29 | - uses: taiki-e/install-action@61e5998d108b2b55a81b9b386c18bd46e4237e4f # v2.63.1 30 | with: 31 | tool: zizmor 32 | 33 | - name: Run zizmor 34 | run: zizmor --format sarif . > results.sarif 35 | env: 36 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 37 | 38 | - name: Upload SARIF file 39 | uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8 40 | with: 41 | sarif_file: results.sarif 42 | category: zizmor 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025-present VoidZero Inc. & Contributors 4 | Copyright (c) 2023-2025 Dunqing 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /src/generated/configs-by-category.ts: -------------------------------------------------------------------------------- 1 | // These rules are automatically generated by scripts/generate-rules.ts 2 | 3 | import * as rules from './rules-by-category.js'; 4 | 5 | const pedanticConfig = { 6 | name: 'oxlint/pedantic', 7 | rules: rules.pedanticRules, 8 | } as const; 9 | 10 | const styleConfig = { 11 | name: 'oxlint/style', 12 | rules: rules.styleRules, 13 | } as const; 14 | 15 | const suspiciousConfig = { 16 | name: 'oxlint/suspicious', 17 | rules: rules.suspiciousRules, 18 | } as const; 19 | 20 | const restrictionConfig = { 21 | name: 'oxlint/restriction', 22 | rules: rules.restrictionRules, 23 | } as const; 24 | 25 | const correctnessConfig = { 26 | name: 'oxlint/correctness', 27 | rules: rules.correctnessRules, 28 | } as const; 29 | 30 | const nurseryConfig = { 31 | name: 'oxlint/nursery', 32 | rules: rules.nurseryRules, 33 | } as const; 34 | 35 | const perfConfig = { 36 | name: 'oxlint/perf', 37 | rules: rules.perfRules, 38 | } as const; 39 | 40 | const configByCategory = { 41 | 'flat/pedantic': pedanticConfig, 42 | 'flat/style': styleConfig, 43 | 'flat/suspicious': suspiciousConfig, 44 | 'flat/restriction': restrictionConfig, 45 | 'flat/correctness': correctnessConfig, 46 | 'flat/nursery': nurseryConfig, 47 | 'flat/perf': perfConfig, 48 | }; 49 | 50 | export default configByCategory; 51 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/utilities.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import JSONCParser from 'jsonc-parser'; 3 | import { OxlintConfig } from './types.js'; 4 | 5 | /** 6 | * Detects it the value is an object 7 | */ 8 | export const isObject = (value: unknown): boolean => 9 | typeof value === 'object' && value !== null && !Array.isArray(value); 10 | 11 | /** 12 | * tries to read the oxlint config file and returning its JSON content. 13 | * if the file is not found or could not be parsed, undefined is returned. 14 | * And an error message will be emitted to `console.error` 15 | */ 16 | export const getConfigContent = (oxlintConfigFile: string): OxlintConfig | undefined => { 17 | try { 18 | const content = fs.readFileSync(oxlintConfigFile, 'utf8'); 19 | 20 | try { 21 | const configContent = JSONCParser.parse(content); 22 | 23 | if (!isObject(configContent)) { 24 | throw new TypeError('not an valid config file'); 25 | } 26 | 27 | return configContent; 28 | } catch { 29 | console.error( 30 | `eslint-plugin-oxlint: could not parse oxlint config file: ${oxlintConfigFile}` 31 | ); 32 | return undefined; 33 | } 34 | } catch { 35 | console.error(`eslint-plugin-oxlint: could not find oxlint config file: ${oxlintConfigFile}`); 36 | return undefined; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | permissions: {} 8 | 9 | jobs: 10 | release: 11 | if: startsWith(github.event.head_commit.message, 'release') 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: write 15 | id-token: write # for `npm publish --provenance` 16 | steps: 17 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 18 | with: 19 | fetch-depth: 0 20 | persist-credentials: true 21 | 22 | - uses: oxc-project/setup-node@141eb77546de6702f92d320926403fe3f9f6a6f2 # v1.0.5 23 | 24 | - name: Build 25 | run: pnpm run build 26 | 27 | - name: Extract version from commit message 28 | env: 29 | COMMIT_MESSAGE: ${{ github.event.head_commit.message }} 30 | run: | 31 | VERSION=$(echo "${COMMIT_MESSAGE}" | grep -oP 'release: \Kv[0-9]+\.[0-9]+\.[0-9]+') 32 | echo "VERSION=$VERSION" >> $GITHUB_ENV 33 | 34 | - name: Create and push tag 35 | run: | 36 | git tag ${VERSION} 37 | git push origin ${VERSION} 38 | 39 | - run: npx changelogithub 40 | continue-on-error: true 41 | env: 42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 43 | 44 | - run: npm install -g npm@latest # For trusted publishing support 45 | 46 | - name: Publish to NPM 47 | run: npm publish --tag latest --provenance --access public 48 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/overrides.ts: -------------------------------------------------------------------------------- 1 | import { handleCategoriesScope } from './categories.js'; 2 | import { readPluginsFromConfig } from './plugins.js'; 3 | import { handleRulesScope, readRulesFromConfig } from './rules.js'; 4 | import { 5 | BuildFromOxlintConfigOptions, 6 | EslintPluginOxlintConfig, 7 | OxlintConfig, 8 | OxlintConfigCategories, 9 | OxlintConfigOverride, 10 | } from './types.js'; 11 | 12 | export const handleOverridesScope = ( 13 | overrides: OxlintConfigOverride[], 14 | configs: EslintPluginOxlintConfig[], 15 | baseCategories?: OxlintConfigCategories, 16 | options: BuildFromOxlintConfigOptions = {} 17 | ): void => { 18 | for (const [overrideIndex, override] of overrides.entries()) { 19 | const eslintRules: Record = {}; 20 | const eslintConfig: EslintPluginOxlintConfig = { 21 | name: `oxlint/from-oxlint-config-override-${overrideIndex}`, 22 | // expect that oxlint `files` syntax is the same as eslint 23 | files: override.files, 24 | }; 25 | 26 | const plugins = readPluginsFromConfig(override); 27 | if (baseCategories !== undefined && plugins !== undefined) { 28 | handleCategoriesScope(plugins, baseCategories, eslintRules, options); 29 | } 30 | 31 | const rules = readRulesFromConfig(override); 32 | if (rules !== undefined) { 33 | handleRulesScope(rules, eslintRules, options); 34 | } 35 | 36 | eslintConfig.rules = eslintRules; 37 | configs.push(eslintConfig); 38 | } 39 | }; 40 | 41 | export const readOverridesFromConfig = ( 42 | config: OxlintConfig 43 | ): OxlintConfigOverride[] | undefined => { 44 | return 'overrides' in config && Array.isArray(config.overrides) 45 | ? (config.overrides as OxlintConfigOverride[]) 46 | : undefined; 47 | }; 48 | -------------------------------------------------------------------------------- /src/configs.ts: -------------------------------------------------------------------------------- 1 | import * as ruleMapsByScope from './generated/rules-by-scope.js'; 2 | import * as ruleMapsByCategory from './generated/rules-by-category.js'; 3 | import configByScope from './generated/configs-by-scope.js'; 4 | import configByCategory from './generated/configs-by-category.js'; 5 | import { 6 | overrideDisabledRulesForVueAndSvelteFiles, 7 | splitDisabledRulesForVueAndSvelteFiles, 8 | splitDisabledRulesForVueAndSvelteFilesDeep, 9 | } from './config-helper.js'; 10 | 11 | type UnionToIntersection = (U extends unknown ? (x: U) => void : never) extends ( 12 | x: infer I 13 | ) => void 14 | ? I 15 | : never; 16 | 17 | type RulesGroups = keyof typeof ruleMapsByScope; 18 | type AllRules = (typeof ruleMapsByScope)[RulesGroups]; 19 | 20 | const allRulesIncludingNursery: UnionToIntersection = Object.assign( 21 | {}, 22 | ...Object.values(ruleMapsByScope) 23 | ); 24 | 25 | // Exclude nursery rules from the default 'all' config 26 | const allRules = Object.fromEntries( 27 | Object.entries(allRulesIncludingNursery).filter( 28 | ([ruleName]) => !(ruleName in ruleMapsByCategory.nurseryRules) 29 | ) 30 | ) as UnionToIntersection; 31 | 32 | export default { 33 | recommended: overrideDisabledRulesForVueAndSvelteFiles({ 34 | plugins: ['oxlint'], 35 | rules: ruleMapsByCategory.correctnessRules, 36 | }), 37 | all: overrideDisabledRulesForVueAndSvelteFiles({ 38 | plugins: ['oxlint'], 39 | rules: allRules, 40 | }), 41 | 'flat/all': splitDisabledRulesForVueAndSvelteFiles({ 42 | name: 'oxlint/all', 43 | rules: allRules, 44 | }), 45 | 'flat/recommended': splitDisabledRulesForVueAndSvelteFiles({ 46 | name: 'oxlint/recommended', 47 | rules: ruleMapsByCategory.correctnessRules, 48 | }), 49 | ...splitDisabledRulesForVueAndSvelteFilesDeep(configByScope), 50 | ...splitDisabledRulesForVueAndSvelteFilesDeep(configByCategory), 51 | } as const; 52 | -------------------------------------------------------------------------------- /src/configs.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, it, describe } from 'vitest'; 2 | import { ESLint } from 'eslint'; 3 | import { ESLintTestConfig } from '../test/helpers.js'; 4 | import configs from './configs.js'; 5 | import { nurseryRules } from './generated/rules-by-category.js'; 6 | import configByScope from './generated/configs-by-scope.js'; 7 | 8 | it('contains all the oxlint rules', async () => { 9 | const eslint = new ESLint(ESLintTestConfig); 10 | const config = await eslint.calculateConfigForFile('index.js'); 11 | expect(config.rules).toMatchSnapshot(); 12 | }); 13 | 14 | describe('nursery rules in configs', () => { 15 | it('should not include nursery rules in "all" config', () => { 16 | const allConfig = configs.all; 17 | expect(allConfig.rules).toBeDefined(); 18 | 19 | // Check that none of the nursery rules are in the "all" config 20 | for (const nurseryRule of Object.keys(nurseryRules)) { 21 | expect(nurseryRule in allConfig.rules!).toBe(false); 22 | } 23 | }); 24 | 25 | it('should not include nursery rules in "flat/all" config', () => { 26 | const flatAllConfigs = configs['flat/all']; 27 | 28 | // flat/all returns an array of configs 29 | for (const config of flatAllConfigs) { 30 | if (config.rules) { 31 | // Check that none of the nursery rules are in the config 32 | for (const nurseryRule of Object.keys(nurseryRules)) { 33 | expect(nurseryRule in config.rules).toBe(false); 34 | } 35 | } 36 | } 37 | }); 38 | 39 | it('should not include nursery rules in scope-based configs', () => { 40 | // Check all scope-based configs (flat/eslint, flat/react, etc.) 41 | for (const [_configName, config] of Object.entries(configByScope)) { 42 | expect(config.rules).toBeDefined(); 43 | 44 | // Check that none of the nursery rules are in any scope config 45 | for (const nurseryRule of Object.keys(nurseryRules)) { 46 | expect(nurseryRule in config.rules).toBe(false); 47 | } 48 | } 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /.github/workflows/bump_oxlint.yml: -------------------------------------------------------------------------------- 1 | name: Bump oxlint 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | required: true 8 | type: string 9 | 10 | env: 11 | OXLINT_PACKAGE_NAME: oxlint 12 | 13 | permissions: {} 14 | 15 | jobs: 16 | bump: 17 | runs-on: ubuntu-latest 18 | permissions: 19 | pull-requests: write 20 | steps: 21 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 22 | with: 23 | persist-credentials: false # should be fine, we give another token for PR creation 24 | 25 | - uses: oxc-project/setup-node@141eb77546de6702f92d320926403fe3f9f6a6f2 # v1.0.5 26 | 27 | - name: Generate version ${{ inputs.version }} 28 | env: 29 | OXLINT_VERSION: ${{ inputs.version }} 30 | run: | 31 | pnpm install oxlint@${OXLINT_VERSION} 32 | pnpm run generate # Generate rules 33 | pnpm run format # run prettier over it 34 | 35 | - name: Test and update snapshot 36 | continue-on-error: true # we check in PR why it fails 37 | run: pnpm run test -u # Update test snapshots 38 | 39 | - name: Bump oxlint rules 40 | env: 41 | OXLINT_VERSION: ${{ inputs.version }} 42 | run: npm version ${OXLINT_VERSION} --no-git-tag-version 43 | 44 | - uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0 45 | with: 46 | # bot account with PAT required for triggering workflow runs 47 | # See https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#triggering-further-workflow-runs 48 | token: ${{ secrets.OXC_BOT_PAT }} 49 | commit-message: 'release: v${{ inputs.version }}' 50 | committer: Boshen 51 | author: Boshen 52 | branch: release 53 | branch-suffix: timestamp 54 | title: 'release: v${{ inputs.version }}' 55 | assignees: camc314, Sysix 56 | base: main 57 | -------------------------------------------------------------------------------- /src/generated/configs-by-scope.ts: -------------------------------------------------------------------------------- 1 | // These rules are automatically generated by scripts/generate-rules.ts 2 | 3 | import * as rules from './rules-by-scope.js'; 4 | 5 | const eslintConfig = { 6 | name: 'oxlint/eslint', 7 | rules: rules.eslintRules, 8 | } as const; 9 | 10 | const importConfig = { 11 | name: 'oxlint/import', 12 | rules: rules.importRules, 13 | } as const; 14 | 15 | const jestConfig = { 16 | name: 'oxlint/jest', 17 | rules: rules.jestRules, 18 | } as const; 19 | 20 | const jsdocConfig = { 21 | name: 'oxlint/jsdoc', 22 | rules: rules.jsdocRules, 23 | } as const; 24 | 25 | const jsxA11yConfig = { 26 | name: 'oxlint/jsx-a11y', 27 | rules: rules.jsxA11yRules, 28 | } as const; 29 | 30 | const nextjsConfig = { 31 | name: 'oxlint/nextjs', 32 | rules: rules.nextjsRules, 33 | } as const; 34 | 35 | const nodeConfig = { 36 | name: 'oxlint/node', 37 | rules: rules.nodeRules, 38 | } as const; 39 | 40 | const promiseConfig = { 41 | name: 'oxlint/promise', 42 | rules: rules.promiseRules, 43 | } as const; 44 | 45 | const reactConfig = { 46 | name: 'oxlint/react', 47 | rules: rules.reactRules, 48 | } as const; 49 | 50 | const reactHooksConfig = { 51 | name: 'oxlint/react-hooks', 52 | rules: rules.reactHooksRules, 53 | } as const; 54 | 55 | const reactPerfConfig = { 56 | name: 'oxlint/react-perf', 57 | rules: rules.reactPerfRules, 58 | } as const; 59 | 60 | const typescriptConfig = { 61 | name: 'oxlint/typescript', 62 | rules: rules.typescriptRules, 63 | } as const; 64 | 65 | const unicornConfig = { 66 | name: 'oxlint/unicorn', 67 | rules: rules.unicornRules, 68 | } as const; 69 | 70 | const vitestConfig = { 71 | name: 'oxlint/vitest', 72 | rules: rules.vitestRules, 73 | } as const; 74 | 75 | const vueConfig = { 76 | name: 'oxlint/vue', 77 | rules: rules.vueRules, 78 | } as const; 79 | 80 | const configByScope = { 81 | 'flat/eslint': eslintConfig, 82 | 'flat/import': importConfig, 83 | 'flat/jest': jestConfig, 84 | 'flat/jsdoc': jsdocConfig, 85 | 'flat/jsx-a11y': jsxA11yConfig, 86 | 'flat/nextjs': nextjsConfig, 87 | 'flat/node': nodeConfig, 88 | 'flat/promise': promiseConfig, 89 | 'flat/react': reactConfig, 90 | 'flat/react-hooks': reactHooksConfig, 91 | 'flat/react-perf': reactPerfConfig, 92 | 'flat/typescript': typescriptConfig, 93 | 'flat/unicorn': unicornConfig, 94 | 'flat/vitest': vitestConfig, 95 | 'flat/vue': vueConfig, 96 | }; 97 | 98 | export default configByScope; 99 | -------------------------------------------------------------------------------- /src/config-helper.ts: -------------------------------------------------------------------------------- 1 | import { rulesDisabledForVueAndSvelteFiles } from './constants.js'; 2 | 3 | // Some type helpers for better type inference 4 | type LegacyConfig = { 5 | rules?: Record; 6 | overrides?: { 7 | files: string[]; 8 | excludedFiles?: string[]; 9 | rules?: Record; 10 | }[]; 11 | }; 12 | 13 | type FlatConfig = { 14 | name?: string; 15 | rules?: Record; 16 | ignores?: string[]; 17 | }; 18 | 19 | // for eslint legacy configuration 20 | export const overrideDisabledRulesForVueAndSvelteFiles = (config: C): C => { 21 | const foundRules = Object.keys(config.rules!).filter((rule) => 22 | rulesDisabledForVueAndSvelteFiles.includes(rule) 23 | ); 24 | 25 | if (foundRules.length === 0) { 26 | return config; 27 | } 28 | 29 | const newConfig = structuredClone(config); 30 | 31 | newConfig.overrides = [ 32 | { 33 | // classic configs use glob syntax 34 | files: ['*.*'], 35 | excludedFiles: ['*.vue', '*.svelte'], 36 | rules: {}, 37 | }, 38 | ]; 39 | 40 | for (const rule of foundRules) { 41 | delete newConfig.rules![rule]; 42 | newConfig.overrides[0].rules![rule] = 'off'; 43 | } 44 | 45 | return newConfig; 46 | }; 47 | 48 | export type SplittedFlatConfig = [C] | [C, FlatConfig]; 49 | 50 | // for eslint flat configuration 51 | export const splitDisabledRulesForVueAndSvelteFiles = ( 52 | config: C 53 | ): SplittedFlatConfig => { 54 | const foundRules = Object.keys(config.rules!).filter((rule) => 55 | rulesDisabledForVueAndSvelteFiles.includes(rule) 56 | ); 57 | 58 | if (foundRules.length === 0) { 59 | return [config]; 60 | } 61 | 62 | const oldConfig = structuredClone(config); 63 | 64 | const newConfig: FlatConfig = { 65 | // flat configs use minimatch syntax 66 | name: 'oxlint/vue-svelte-exceptions', 67 | ignores: ['**/*.vue', '**/*.svelte'], 68 | rules: {}, 69 | }; 70 | 71 | for (const rule of foundRules) { 72 | delete oldConfig.rules![rule]; 73 | newConfig.rules![rule] = 'off'; 74 | } 75 | 76 | return [oldConfig, newConfig]; 77 | }; 78 | 79 | export const splitDisabledRulesForVueAndSvelteFilesDeep = >( 80 | config: T 81 | ): { [K in keyof T]: SplittedFlatConfig } => { 82 | const result = {} as { [K in keyof T]: SplittedFlatConfig }; 83 | 84 | for (const name in config) { 85 | result[name] = splitDisabledRulesForVueAndSvelteFiles(config[name]); 86 | } 87 | 88 | return result; 89 | }; 90 | -------------------------------------------------------------------------------- /src/config-helper.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { 3 | overrideDisabledRulesForVueAndSvelteFiles, 4 | splitDisabledRulesForVueAndSvelteFiles, 5 | } from './config-helper.js'; 6 | 7 | describe('overrideDisabledRulesForVueAndSvelteFiles', () => { 8 | it('does not create an override when no matching rule detected', () => { 9 | const config = { 10 | rules: { 11 | 'no-magic-numbers': 'off', 12 | }, 13 | } as const; 14 | 15 | const newConfig = overrideDisabledRulesForVueAndSvelteFiles(config); 16 | 17 | expect(newConfig).toStrictEqual({ 18 | rules: { 19 | 'no-magic-numbers': 'off', 20 | }, 21 | }); 22 | }); 23 | 24 | it('creates override when matching rule detected', () => { 25 | const config = { 26 | rules: { 27 | 'no-magic-numbers': 'off', 28 | 'no-unused-vars': 'off', 29 | }, 30 | } as const; 31 | 32 | const newConfig = overrideDisabledRulesForVueAndSvelteFiles(config); 33 | 34 | expect(newConfig).toStrictEqual({ 35 | rules: { 36 | 'no-magic-numbers': 'off', 37 | }, 38 | overrides: [ 39 | { 40 | files: ['*.*'], 41 | excludedFiles: ['*.vue', '*.svelte'], 42 | rules: { 43 | 'no-unused-vars': 'off', 44 | }, 45 | }, 46 | ], 47 | }); 48 | }); 49 | }); 50 | 51 | describe('splitDisabledRulesForVueAndSvelteFiles', () => { 52 | it('does not create a second config when no matching rule detected', () => { 53 | const config = { 54 | rules: { 55 | 'no-magic-numbers': 'off', 56 | }, 57 | } as const; 58 | 59 | const newConfigs = splitDisabledRulesForVueAndSvelteFiles(config); 60 | 61 | expect(newConfigs).toStrictEqual([ 62 | { 63 | rules: { 64 | 'no-magic-numbers': 'off', 65 | }, 66 | }, 67 | ]); 68 | }); 69 | 70 | it('creates a second config when no matching rule detected', () => { 71 | const config = { 72 | rules: { 73 | 'no-magic-numbers': 'off', 74 | 'no-unused-vars': 'off', 75 | }, 76 | } as const; 77 | 78 | const newConfigs = splitDisabledRulesForVueAndSvelteFiles(config); 79 | 80 | expect(newConfigs).toStrictEqual([ 81 | { 82 | rules: { 83 | 'no-magic-numbers': 'off', 84 | }, 85 | }, 86 | { 87 | name: 'oxlint/vue-svelte-exceptions', 88 | ignores: ['**/*.vue', '**/*.svelte'], 89 | rules: { 90 | 'no-unused-vars': 'off', 91 | }, 92 | }, 93 | ]); 94 | }); 95 | }); 96 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/overrides.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { handleOverridesScope } from './overrides.js'; 3 | 4 | describe('handleOverridesScope', () => { 5 | it('supports simple files + rules overrides', () => { 6 | const configs = [ 7 | { 8 | rules: { 9 | eqeqeq: 'off', 10 | } as const, 11 | }, 12 | ]; 13 | handleOverridesScope( 14 | [ 15 | { 16 | files: ['./*.ts'], 17 | rules: { 18 | 'no-alert': 'error', 19 | }, 20 | }, 21 | ], 22 | configs 23 | ); 24 | 25 | expect(configs).toStrictEqual([ 26 | { 27 | rules: { 28 | eqeqeq: 'off', 29 | }, 30 | }, 31 | { 32 | name: 'oxlint/from-oxlint-config-override-0', 33 | files: ['./*.ts'], 34 | rules: { 35 | 'no-alert': 'off', 36 | }, 37 | }, 38 | ]); 39 | }); 40 | 41 | it('supports simple files + plugins overrides', () => { 42 | const configs = [ 43 | { 44 | rules: { 45 | eqeqeq: 'off', 46 | } as const, 47 | }, 48 | ]; 49 | handleOverridesScope( 50 | [ 51 | { 52 | files: ['./*.test.ts'], 53 | plugins: ['vitest'], 54 | }, 55 | ], 56 | configs, 57 | { 58 | correctness: 'warn', 59 | } 60 | ); 61 | 62 | expect(configs.length).toBe(2); 63 | 64 | expect(configs[0]).toStrictEqual({ 65 | rules: { 66 | eqeqeq: 'off', 67 | }, 68 | }); 69 | 70 | expect(configs[1].rules.eqeqeq).toBe(undefined); 71 | // @ts-expect-error -- because we are using const no other rule then eqeqeq is allowed 72 | expect(configs[1].rules['vitest/no-conditional-tests']).toBe('off'); 73 | }); 74 | 75 | it('rule in overrides', () => { 76 | const configs = [ 77 | { 78 | rules: { 79 | 'no-debugger': 'off', 80 | } as const, 81 | }, 82 | ]; 83 | handleOverridesScope( 84 | [ 85 | { 86 | files: ['./*.test.ts'], 87 | rules: { 88 | 'no-debugger': 'off', 89 | }, 90 | }, 91 | ], 92 | configs 93 | ); 94 | 95 | expect(configs).toStrictEqual([ 96 | { 97 | rules: { 98 | 'no-debugger': 'off', 99 | }, 100 | }, 101 | { 102 | name: 'oxlint/from-oxlint-config-override-0', 103 | files: ['./*.test.ts'], 104 | rules: {}, 105 | }, 106 | ]); 107 | }); 108 | }); 109 | -------------------------------------------------------------------------------- /scripts/constants.ts: -------------------------------------------------------------------------------- 1 | // these are the rules that don't have a direct equivalent in the eslint rules 2 | export const ignoreScope = new Set(['oxc', 'deepscan', 'security']); 3 | 4 | // TypeScript type-aware rules that require type information 5 | // These are excluded from pre-built configs by default but can be enabled 6 | // via the `typeAware` option in buildFromOxlintConfig 7 | // List copied from: 8 | // https://github.com/typescript-eslint/typescript-eslint/blob/7319bad3a5022be2adfbcb331451cfd85d1d786a/packages/eslint-plugin/src/configs/flat/disable-type-checked.ts 9 | export const typescriptTypeAwareRules = [ 10 | 'await-thenable', 11 | 'consistent-return', 12 | 'consistent-type-exports', 13 | 'dot-notation', 14 | 'naming-convention', 15 | 'no-array-delete', 16 | 'no-base-to-string', 17 | 'no-confusing-void-expression', 18 | 'no-deprecated', 19 | 'no-duplicate-type-constituents', 20 | 'no-floating-promises', 21 | 'no-for-in-array', 22 | 'no-implied-eval', 23 | 'no-meaningless-void-operator', 24 | 'no-misused-promises', 25 | 'no-misused-spread', 26 | 'no-mixed-enums', 27 | 'no-redundant-type-constituents', 28 | 'no-unnecessary-boolean-literal-compare', 29 | 'no-unnecessary-condition', 30 | 'no-unnecessary-qualifier', 31 | 'no-unnecessary-template-expression', 32 | 'no-unnecessary-type-arguments', 33 | 'no-unnecessary-type-assertion', 34 | 'no-unnecessary-type-conversion', 35 | 'no-unnecessary-type-parameters', 36 | 'no-unsafe-argument', 37 | 'no-unsafe-assignment', 38 | 'no-unsafe-call', 39 | 'no-unsafe-enum-comparison', 40 | 'no-unsafe-member-access', 41 | 'no-unsafe-return', 42 | 'no-unsafe-type-assertion', 43 | 'no-unsafe-unary-minus', 44 | 'non-nullable-type-assertion-style', 45 | 'only-throw-error', 46 | 'prefer-destructuring', 47 | 'prefer-find', 48 | 'prefer-includes', 49 | 'prefer-nullish-coalescing', 50 | 'prefer-optional-chain', 51 | 'prefer-promise-reject-errors', 52 | 'prefer-readonly', 53 | 'prefer-readonly-parameter-types', 54 | 'prefer-reduce-type-parameter', 55 | 'prefer-regexp-exec', 56 | 'prefer-return-this-type', 57 | 'prefer-string-starts-ends-with', 58 | 'promise-function-async', 59 | 'related-getter-setter-pairs', 60 | 'require-array-sort-compare', 61 | 'require-await', 62 | 'restrict-plus-operands', 63 | 'restrict-template-expressions', 64 | 'return-await', 65 | 'strict-boolean-expressions', 66 | 'switch-exhaustiveness-check', 67 | 'unbound-method', 68 | 'use-unknown-in-catch-callback-variable', 69 | ]; 70 | 71 | // Set of type-aware rules with full names for efficient O(1) lookup 72 | export const typeAwareRulesSet = new Set( 73 | typescriptTypeAwareRules.map((rule) => `@typescript-eslint/${rule}`) 74 | ); 75 | -------------------------------------------------------------------------------- /scripts/config-generator.ts: -------------------------------------------------------------------------------- 1 | import { writeFile } from 'node:fs/promises'; 2 | import path from 'node:path'; 3 | import type { Rule } from './traverse-rules.js'; 4 | import { camelCase, kebabCase, pascalCase } from 'scule'; 5 | 6 | export enum RulesGrouping { 7 | CATEGORY = 'category', 8 | SCOPE = 'scope', 9 | } 10 | 11 | export type ResultMap = Map; 12 | 13 | export class ConfigGenerator { 14 | private rulesGrouping: RulesGrouping; 15 | private rulesArray: Rule[]; 16 | constructor(rulesArray: Rule[] = [], rulesGrouping: RulesGrouping = RulesGrouping.SCOPE) { 17 | this.rulesArray = rulesArray; 18 | this.rulesGrouping = rulesGrouping; 19 | } 20 | 21 | public setRulesGrouping(rulesGrouping: RulesGrouping) { 22 | this.rulesGrouping = rulesGrouping; 23 | } 24 | 25 | private groupItemsBy(rules: Rule[], rulesGrouping: RulesGrouping): Set { 26 | const set = new Set(); 27 | for (const item of rules) { 28 | set.add(item[rulesGrouping]); 29 | } 30 | 31 | return set; 32 | } 33 | 34 | public generateRulesCode() { 35 | console.log(`Generating config, grouped by ${this.rulesGrouping}`); 36 | 37 | const rulesGrouping = this.rulesGrouping; 38 | // Filter out nursery rules when grouping by scope 39 | const rulesArray = 40 | this.rulesGrouping === RulesGrouping.SCOPE 41 | ? this.rulesArray.filter((rule) => rule.category !== 'nursery') 42 | : this.rulesArray; 43 | 44 | const rulesMap = this.groupItemsBy(rulesArray, rulesGrouping); 45 | const exportName = pascalCase(this.rulesGrouping); 46 | 47 | const exportGrouping: string[] = []; 48 | let code = '// These rules are automatically generated by scripts/generate-rules.ts\n\n'; 49 | 50 | code += `import * as rules from "./rules-by-${this.rulesGrouping}.js";\n\n`; 51 | 52 | for (const grouping of rulesMap) { 53 | exportGrouping.push(grouping); 54 | 55 | code += `const ${camelCase(grouping)}Config = {\n`; 56 | 57 | code += ` name: 'oxlint/${kebabCase(grouping)}',\n`; 58 | code += ` rules: rules.${camelCase(grouping)}Rules,`; 59 | code += '\n} as const;\n\n'; 60 | } 61 | 62 | code += `const configBy${exportName} = {\n`; 63 | code += exportGrouping 64 | .map((grouping) => { 65 | return ` 'flat/${kebabCase(grouping)}': ${camelCase(grouping)}Config`; 66 | }) 67 | .join(',\n'); 68 | code += '\n}\n\n'; 69 | 70 | code += `export default configBy${exportName}`; 71 | 72 | return code; 73 | } 74 | 75 | public async generateRules(folderPath: string): Promise { 76 | const output = this.generateRulesCode(); 77 | 78 | return writeFile(path.resolve(folderPath, `configs-by-${this.rulesGrouping}.ts`), output); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # Jetbrains 126 | .idea 127 | 128 | # yarn v2 129 | .yarn/cache 130 | .yarn/unplugged 131 | .yarn/build-state.yml 132 | .yarn/install-state.gz 133 | .pnp.* 134 | 135 | .oxc_sparse -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-oxlint", 3 | "version": "1.34.0", 4 | "description": "Turn off all rules already supported by oxlint", 5 | "type": "module", 6 | "types": "./dist/index.d.ts", 7 | "packageManager": "pnpm@10.25.0", 8 | "exports": { 9 | ".": { 10 | "types": "./dist/index.d.ts", 11 | "import": "./dist/index.mjs", 12 | "require": "./dist/index.cjs", 13 | "default": "./dist/index.mjs" 14 | }, 15 | "./rules-by-category": { 16 | "types": "./dist/generated/rules-by-category.d.ts", 17 | "import": "./dist/generated/rules-by-category.mjs", 18 | "require": "./dist/generated/rules-by-category.cjs", 19 | "default": "./dist/generated/rules-by-category.mjs" 20 | }, 21 | "./rules-by-scope": { 22 | "types": "./dist/generated/rules-by-scope.d.ts", 23 | "import": "./dist/generated/rules-by-scope.mjs", 24 | "require": "./dist/generated/rules-by-scope.cjs", 25 | "default": "./dist/generated/rules-by-scope.mjs" 26 | }, 27 | "./package.json": "./package.json" 28 | }, 29 | "files": [ 30 | "dist", 31 | "src", 32 | "!**/*.spec.ts", 33 | "!**/*.snap" 34 | ], 35 | "author": "Dunqing ", 36 | "homepage": "https://github.com/oxc-project/eslint-plugin-oxlint", 37 | "repository": { 38 | "type": "git", 39 | "url": "git@github.com:oxc-project/eslint-plugin-oxlint.git" 40 | }, 41 | "license": "MIT", 42 | "scripts": { 43 | "prepare": "husky", 44 | "generate": "node --import @oxc-node/core/register ./scripts/generate.ts", 45 | "build": "vite build", 46 | "lint": "npx oxlint --tsconfig=tsconfig.json && npx eslint", 47 | "format": "npx oxfmt", 48 | "type-check": "tsc --noEmit", 49 | "test": "vitest --reporter=verbose" 50 | }, 51 | "keywords": [ 52 | "oxc", 53 | "oxlint", 54 | "eslint", 55 | "rules" 56 | ], 57 | "devDependencies": { 58 | "@eslint/js": "^9.13.0", 59 | "@oxc-node/core": "^0.0.35", 60 | "@types/node": "^25.0.0", 61 | "@types/shelljs": "^0.8.15", 62 | "@vitest/coverage-v8": "^4.0.0", 63 | "dedent": "^1.5.3", 64 | "eslint": "^9.13.0", 65 | "eslint-plugin-unicorn": "^62.0.0", 66 | "husky": "^9.1.6", 67 | "jiti": "^2.4.2", 68 | "lint-staged": "^16.0.0", 69 | "memfs": "^4.14.0", 70 | "oxfmt": "^0.17.0", 71 | "oxlint": "^1.34.0", 72 | "oxlint-tsgolint": "^0.1.5", 73 | "scule": "^1.3.0", 74 | "shelljs": "^0.10.0", 75 | "typescript": "^5.6.3", 76 | "typescript-eslint": "^8.10.0", 77 | "vite": "^7.0.0", 78 | "vite-plugin-dts": "^4.2.4", 79 | "vitest": "^4.0.0" 80 | }, 81 | "lint-staged": { 82 | "*.{js,cjs,ts}": "eslint", 83 | "*": "oxfmt ." 84 | }, 85 | "volta": { 86 | "node": "20.14.0" 87 | }, 88 | "dependencies": { 89 | "jsonc-parser": "^3.3.1" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /scripts/traverse-rules.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from 'node:child_process'; 2 | import { ignoreScope } from './constants.js'; 3 | import { 4 | aliasPluginNames, 5 | reactHookRulesInsideReactScope, 6 | typescriptRulesExtendEslintRules, 7 | unicornRulesExtendEslintRules, 8 | viteTestCompatibleRules, 9 | } from '../src/constants.js'; 10 | 11 | export type Rule = { 12 | value: string; 13 | scope: string; 14 | category: string; 15 | }; 16 | 17 | /** 18 | * Read the rules from oxlint command and returns an array of Rule-Objects 19 | */ 20 | function readRulesFromCommand(): Rule[] { 21 | // do not handle the exception 22 | const oxlintOutput = execSync(`npx oxlint --rules --format=json`, { 23 | encoding: 'utf8', 24 | stdio: 'pipe', 25 | }); 26 | 27 | // do not handle the exception 28 | return JSON.parse(oxlintOutput); 29 | } 30 | 31 | /** 32 | * Some rules are in a different scope then in eslint 33 | */ 34 | function fixScopeOfRule(rule: Rule): void { 35 | if (rule.scope === 'react' && reactHookRulesInsideReactScope.includes(rule.value)) { 36 | rule.scope = 'react_hooks'; 37 | } 38 | } 39 | 40 | /** 41 | * oxlint returns the value without a scope name 42 | */ 43 | function fixValueOfRule(rule: Rule): void { 44 | if (rule.scope === 'eslint') { 45 | return; 46 | } 47 | 48 | const scope = rule.scope in aliasPluginNames ? aliasPluginNames[rule.scope] : rule.scope; 49 | 50 | rule.value = `${scope}/${rule.value}`; 51 | } 52 | 53 | /** 54 | * some rules are reimplemented in another scope 55 | * remap them so we can disable all the reimplemented too 56 | */ 57 | function getAliasRules(rule: Rule): Rule | undefined { 58 | if (rule.scope === 'eslint' && typescriptRulesExtendEslintRules.includes(rule.value)) { 59 | return { 60 | value: `@typescript-eslint/${rule.value}`, 61 | scope: 'typescript', 62 | category: rule.category, 63 | }; 64 | } 65 | 66 | if (rule.scope === 'jest' && viteTestCompatibleRules.includes(rule.value)) { 67 | return { 68 | value: `vitest/${rule.value}`, 69 | scope: 'vitest', 70 | category: rule.category, 71 | }; 72 | } 73 | 74 | if (rule.scope === 'eslint' && unicornRulesExtendEslintRules.includes(rule.value)) { 75 | return { 76 | value: `unicorn/${rule.value}`, 77 | scope: 'unicorn', 78 | category: rule.category, 79 | }; 80 | } 81 | } 82 | 83 | export function traverseRules(): Rule[] { 84 | // get all rules and filter the ignored one 85 | const rules = readRulesFromCommand().filter((rule) => !ignoreScope.has(rule.scope)); 86 | 87 | const aliasRules: Rule[] = []; 88 | 89 | for (const rule of rules) { 90 | const aliasRule = getAliasRules(rule); 91 | if (aliasRule) { 92 | aliasRules.push(aliasRule); 93 | } 94 | 95 | fixScopeOfRule(rule); 96 | fixValueOfRule(rule); 97 | } 98 | 99 | return [...rules, ...aliasRules]; 100 | } 101 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/extends.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import type { 3 | OxlintConfig, 4 | OxlintConfigOverride, 5 | OxlintConfigPlugins, 6 | OxlintConfigRules, 7 | OxlintConfigExtends, 8 | } from './types.js'; 9 | import { getConfigContent } from './utilities.js'; 10 | import { defaultPlugins, readPluginsFromConfig } from './plugins.js'; 11 | import { readRulesFromConfig } from './rules.js'; 12 | import { readOverridesFromConfig } from './overrides.js'; 13 | 14 | /** 15 | * tries to return the "extends" section from the config. 16 | * it returns `undefined` when not found or invalid. 17 | */ 18 | const readExtendsFromConfig = (config: OxlintConfig): OxlintConfigExtends | undefined => { 19 | return 'extends' in config && Array.isArray(config.extends) ? config.extends : undefined; 20 | }; 21 | 22 | /** 23 | * Resolves the paths of the "extends" section relative to the given config file. 24 | */ 25 | export const resolveRelativeExtendsPaths = (config: OxlintConfig) => { 26 | if (!config.__misc?.filePath) { 27 | return; 28 | } 29 | 30 | const extendsFiles = readExtendsFromConfig(config); 31 | if (!extendsFiles?.length) return; 32 | const configFileDirectory = path.dirname(config.__misc.filePath); 33 | config.extends = extendsFiles.map((extendFile) => path.resolve(configFileDirectory, extendFile)); 34 | }; 35 | 36 | /** 37 | * Appends plugins, rules and overrides from the extends configs files to the given config. 38 | */ 39 | export const handleExtendsScope = (extendsConfigs: OxlintConfig[], config: OxlintConfig) => { 40 | let rules: OxlintConfigRules = readRulesFromConfig(config) ?? {}; 41 | const plugins: OxlintConfigPlugins = readPluginsFromConfig(config) ?? []; 42 | const overrides: OxlintConfigOverride[] = readOverridesFromConfig(config) ?? []; 43 | for (const extendConfig of extendsConfigs) { 44 | plugins.unshift(...(readPluginsFromConfig(extendConfig) ?? defaultPlugins)); 45 | rules = { ...readRulesFromConfig(extendConfig), ...rules }; 46 | overrides.unshift(...(readOverridesFromConfig(extendConfig) ?? [])); 47 | } 48 | if (plugins.length > 0) config.plugins = [...new Set(plugins)]; 49 | if (Object.keys(rules).length > 0) config.rules = rules; 50 | if (overrides.length > 0) config.overrides = overrides; 51 | }; 52 | 53 | /** 54 | * tries to return the content of the chain "extends" section from the config. 55 | */ 56 | export const readExtendsConfigsFromConfig = (config: OxlintConfig): OxlintConfig[] => { 57 | const extendsFiles = readExtendsFromConfig(config); 58 | if (!extendsFiles?.length) return []; 59 | 60 | const extendsConfigs: OxlintConfig[] = []; 61 | for (const file of extendsFiles) { 62 | const extendConfig = getConfigContent(file); 63 | if (!extendConfig) continue; 64 | 65 | extendConfig.__misc = { 66 | filePath: file, 67 | }; 68 | 69 | resolveRelativeExtendsPaths(extendConfig); 70 | 71 | extendsConfigs.push(extendConfig, ...readExtendsConfigsFromConfig(extendConfig)); 72 | } 73 | return extendsConfigs; 74 | }; 75 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/categories.ts: -------------------------------------------------------------------------------- 1 | import { aliasPluginNames } from '../constants.js'; 2 | import * as allRulesObjects from '../generated/rules-by-category.js'; 3 | import { 4 | BuildFromOxlintConfigOptions, 5 | OxlintConfig, 6 | OxlintConfigCategories, 7 | OxlintConfigPlugins, 8 | } from './types.js'; 9 | import { isObject } from './utilities.js'; 10 | 11 | // default categories, see 12 | export const defaultCategories: OxlintConfigCategories = { 13 | correctness: 'warn', 14 | }; 15 | 16 | /** 17 | * appends all rules which are enabled by a plugin and falls into a specific category 18 | */ 19 | export const handleCategoriesScope = ( 20 | plugins: OxlintConfigPlugins, 21 | categories: OxlintConfigCategories, 22 | rules: Record, 23 | options: BuildFromOxlintConfigOptions = {} 24 | ): void => { 25 | for (const category in categories) { 26 | const configName = `${category}Rules`; 27 | 28 | // Skip nursery category unless explicitly enabled 29 | if (category === 'nursery' && !options.withNursery) { 30 | continue; 31 | } 32 | 33 | // category is not enabled or not in found categories 34 | if (categories[category] === 'off' || !(configName in allRulesObjects)) { 35 | continue; 36 | } 37 | 38 | const possibleRules: string[] = []; 39 | // Correct lookup for type-aware rules export: e.g., correctnessTypeAwareRules 40 | const typeAwareConfigName = `${category}TypeAwareRules`; 41 | if (options.typeAware && typeAwareConfigName in allRulesObjects) { 42 | // @ts-expect-error -- come on TS, we are checking if the configName exists in the allRulesObjects 43 | possibleRules.push(...Object.keys(allRulesObjects[typeAwareConfigName])); 44 | } 45 | // @ts-expect-error -- come on TS, we are checking if the configName exists in the allRulesObjects 46 | possibleRules.push(...Object.keys(allRulesObjects[configName])); 47 | 48 | // iterate to each rule to check if the rule can be appended, because the plugin is activated 49 | for (const rule of possibleRules) { 50 | for (const plugin of plugins) { 51 | const pluginPrefix = plugin in aliasPluginNames ? aliasPluginNames[plugin] : plugin; 52 | 53 | // the rule has no prefix, so it is a eslint one 54 | if (pluginPrefix === '' && !rule.includes('/')) { 55 | rules[rule] = 'off'; 56 | // other rules with a prefix like @typescript-eslint/ 57 | } else if (rule.startsWith(`${pluginPrefix}/`)) { 58 | rules[rule] = 'off'; 59 | } 60 | } 61 | } 62 | } 63 | }; 64 | 65 | /** 66 | * tries to return the "categories" section from the config. 67 | * it returns `undefined` when not found or invalid. 68 | */ 69 | export const readCategoriesFromConfig = ( 70 | config: OxlintConfig 71 | ): OxlintConfigCategories | undefined => { 72 | return 'categories' in config && isObject(config.categories) 73 | ? (config.categories as OxlintConfigCategories) 74 | : undefined; 75 | }; 76 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/categories.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { handleCategoriesScope } from './categories.js'; 3 | 4 | describe('handleCategoriesScope', () => { 5 | it('default plugins (react, unicorn, typescript), default categories', () => { 6 | const rules = {}; 7 | handleCategoriesScope( 8 | ['eslint', 'unicorn', 'react', 'typescript'], 9 | { 10 | correctness: 'warn', 11 | }, 12 | rules 13 | ); 14 | 15 | // snapshot because it can change with the next release 16 | expect(rules).toMatchSnapshot('defaultPluginDefaultCategories'); 17 | }); 18 | 19 | it('skip deactivate categories', () => { 20 | const rules = {}; 21 | handleCategoriesScope(['unicorn', 'react', 'typescript'], {}, rules); 22 | 23 | expect(rules).toStrictEqual({}); 24 | }); 25 | 26 | it('custom plugins, default categories', () => { 27 | const rules = {}; 28 | handleCategoriesScope( 29 | ['eslint', 'unicorn'], 30 | { 31 | correctness: 'warn', 32 | }, 33 | rules 34 | ); 35 | // snapshot because it can change with the next release 36 | expect(rules).toMatchSnapshot('customPluginDefaultCategories'); 37 | }); 38 | 39 | it('custom plugins, custom categories', () => { 40 | const rules = {}; 41 | handleCategoriesScope( 42 | ['eslint', 'import'], 43 | { 44 | suspicious: 'warn', 45 | correctness: 'off', 46 | }, 47 | rules 48 | ); 49 | // snapshot because it can change with the next release 50 | expect(rules).toMatchSnapshot('customPluginCustomCategories'); 51 | }); 52 | 53 | it('skip deactivate rules, for custom enable category', () => { 54 | const rules = { 55 | 'import/no-self-import': 'off', 56 | } as const; 57 | handleCategoriesScope( 58 | ['eslint', 'import'], 59 | { 60 | suspicious: 'warn', 61 | correctness: 'off', 62 | }, 63 | rules 64 | ); 65 | 66 | expect(rules['import/no-self-import']).toBe('off'); 67 | }); 68 | 69 | it('includes type-aware rules when typeAware=true', () => { 70 | const rules: Record = {}; 71 | handleCategoriesScope( 72 | ['eslint', 'typescript'], 73 | { 74 | correctness: 'warn', 75 | }, 76 | rules, 77 | { typeAware: true } 78 | ); 79 | 80 | // Comes from correctnessTypeAwareRules and requires typescript plugin 81 | expect(rules['@typescript-eslint/no-floating-promises']).toBe('off'); 82 | // Base correctness rule should still be included 83 | expect(rules['@typescript-eslint/no-unused-vars']).toBe('off'); 84 | }); 85 | 86 | it('excludes type-aware rules when typeAware=false (default)', () => { 87 | const rules: Record = {}; 88 | handleCategoriesScope( 89 | ['eslint', 'typescript'], 90 | { 91 | correctness: 'warn', 92 | }, 93 | rules, 94 | { typeAware: false } 95 | ); 96 | 97 | // Type-aware rule should not be present 98 | expect(rules['@typescript-eslint/no-floating-promises']).toBeUndefined(); 99 | // Base correctness rule should be present 100 | expect(rules['@typescript-eslint/no-unused-vars']).toBe('off'); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /scripts/rules-generator.ts: -------------------------------------------------------------------------------- 1 | import { writeFile } from 'node:fs/promises'; 2 | import path from 'node:path'; 3 | import type { Rule } from './traverse-rules.js'; 4 | import { camelCase } from 'scule'; 5 | import { typeAwareRulesSet } from './constants.js'; 6 | 7 | export enum RulesGrouping { 8 | CATEGORY = 'category', 9 | SCOPE = 'scope', 10 | } 11 | 12 | export type ResultMap = Map; 13 | 14 | export class RulesGenerator { 15 | private rulesGrouping: RulesGrouping; 16 | private rulesArray: Rule[]; 17 | constructor(rulesArray: Rule[] = [], rulesGrouping: RulesGrouping = RulesGrouping.SCOPE) { 18 | this.rulesArray = rulesArray; 19 | this.rulesGrouping = rulesGrouping; 20 | } 21 | 22 | public setRulesGrouping(rulesGrouping: RulesGrouping) { 23 | this.rulesGrouping = rulesGrouping; 24 | } 25 | 26 | private groupItemsBy(rules: Rule[], rulesGrouping: RulesGrouping): Map { 27 | const map = new Map(); 28 | for (const item of rules) { 29 | const key = item[rulesGrouping]; 30 | const group = map.get(key) || []; 31 | group.push(item.value); 32 | map.set(key, group); 33 | } 34 | 35 | return map; 36 | } 37 | 38 | private splitIntoTypeAwareAndNonTypeAwareRules(rules: Rule[]): { 39 | typeAwareRules: Rule[]; 40 | nonTypeAwareRules: Rule[]; 41 | } { 42 | const typeAwareRules: Rule[] = []; 43 | const nonTypeAwareRules: Rule[] = []; 44 | 45 | for (const rule of rules) { 46 | if (rule.scope === 'typescript' && typeAwareRulesSet.has(rule.value)) { 47 | typeAwareRules.push(rule); 48 | } else { 49 | nonTypeAwareRules.push(rule); 50 | } 51 | } 52 | 53 | return { typeAwareRules, nonTypeAwareRules }; 54 | } 55 | 56 | public generateRulesCode() { 57 | console.log(`Generating rules, grouped by ${this.rulesGrouping}`); 58 | 59 | const rulesGrouping = this.rulesGrouping; 60 | // Filter out nursery rules when grouping by scope 61 | const rulesArray = 62 | this.rulesGrouping === RulesGrouping.SCOPE 63 | ? this.rulesArray.filter((rule) => rule.category !== 'nursery') 64 | : this.rulesArray; 65 | 66 | // Split into type-aware and non-type-aware rules 67 | const { typeAwareRules, nonTypeAwareRules } = 68 | this.splitIntoTypeAwareAndNonTypeAwareRules(rulesArray); 69 | 70 | let code = '// These rules are automatically generated by scripts/generate-rules.ts\n\n'; 71 | const exports: string[] = []; 72 | 73 | for (const { rules, suffix } of [ 74 | { rules: nonTypeAwareRules, suffix: '' }, 75 | { rules: typeAwareRules, suffix: 'TypeAware' }, 76 | ]) { 77 | const rulesMap = this.groupItemsBy(rules, rulesGrouping); 78 | 79 | for (const [grouping, groupRules] of rulesMap.entries()) { 80 | const jsVariableName = camelCase(grouping) + suffix + 'Rules'; 81 | 82 | exports.push(jsVariableName); 83 | code += `const ${jsVariableName}: Record = {\n`; 84 | 85 | code += groupRules 86 | .map((rule) => { 87 | return ` '${rule.replaceAll('_', '-')}': "off"`; 88 | }) 89 | .join(',\n'); 90 | code += '\n};\n\n'; 91 | } 92 | } 93 | 94 | code += 'export {\n'; 95 | code += exports 96 | .map((grouping) => { 97 | return ` ${grouping}`; 98 | }) 99 | .join(',\n'); 100 | code += '\n}'; 101 | 102 | return code; 103 | } 104 | 105 | public generateRules(folderPath: string): Promise { 106 | const output = this.generateRulesCode(); 107 | 108 | return writeFile(path.resolve(folderPath, `rules-by-${this.rulesGrouping}.ts`), output); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/index.ts: -------------------------------------------------------------------------------- 1 | import { BuildFromOxlintConfigOptions, EslintPluginOxlintConfig, OxlintConfig } from './types.js'; 2 | import { handleRulesScope, readRulesFromConfig } from './rules.js'; 3 | import { 4 | defaultCategories, 5 | handleCategoriesScope, 6 | readCategoriesFromConfig, 7 | } from './categories.js'; 8 | import { defaultPlugins, readPluginsFromConfig } from './plugins.js'; 9 | import { handleIgnorePatternsScope, readIgnorePatternsFromConfig } from './ignore-patterns.js'; 10 | import { handleOverridesScope, readOverridesFromConfig } from './overrides.js'; 11 | import { splitDisabledRulesForVueAndSvelteFiles } from '../config-helper.js'; 12 | import { 13 | handleExtendsScope, 14 | readExtendsConfigsFromConfig, 15 | resolveRelativeExtendsPaths, 16 | } from './extends.js'; 17 | import { getConfigContent } from './utilities.js'; 18 | import path from 'node:path'; 19 | 20 | /** 21 | * builds turned off rules, which are supported by oxlint. 22 | * It accepts an object similar to the .oxlintrc.json file. 23 | */ 24 | export const buildFromOxlintConfig = ( 25 | config: OxlintConfig, 26 | options: BuildFromOxlintConfigOptions = {} 27 | ): EslintPluginOxlintConfig[] => { 28 | resolveRelativeExtendsPaths(config); 29 | 30 | const extendConfigs = readExtendsConfigsFromConfig(config); 31 | if (extendConfigs.length > 0) { 32 | handleExtendsScope(extendConfigs, config); 33 | } 34 | 35 | const rules: Record = {}; 36 | const plugins = readPluginsFromConfig(config) ?? defaultPlugins; 37 | const categories = readCategoriesFromConfig(config) ?? defaultCategories; 38 | 39 | // it is not a plugin but it is activated by default 40 | plugins.push('eslint'); 41 | 42 | // oxc handles "react-hooks" rules inside "react" plugin 43 | // our generator split them into own plugins 44 | if (plugins.includes('react')) { 45 | plugins.push('react-hooks'); 46 | } 47 | 48 | handleCategoriesScope(plugins, categories, rules, options); 49 | 50 | const configRules = readRulesFromConfig(config); 51 | 52 | if (configRules !== undefined) { 53 | handleRulesScope(configRules, rules, options); 54 | } 55 | 56 | const baseConfig = { 57 | name: 'oxlint/from-oxlint-config', 58 | rules, 59 | }; 60 | 61 | const overrides = readOverridesFromConfig(config); 62 | const configs = splitDisabledRulesForVueAndSvelteFiles(baseConfig) as EslintPluginOxlintConfig[]; 63 | 64 | if (overrides !== undefined) { 65 | handleOverridesScope(overrides, configs, categories, options); 66 | } 67 | 68 | const ignorePatterns = readIgnorePatternsFromConfig(config); 69 | if (ignorePatterns === undefined) { 70 | return configs; 71 | } else { 72 | const ignoreConfig: EslintPluginOxlintConfig = { 73 | name: 'oxlint/oxlint-config-ignore-patterns', 74 | }; 75 | handleIgnorePatternsScope(ignorePatterns, ignoreConfig); 76 | return [ignoreConfig, ...configs]; 77 | } 78 | }; 79 | 80 | /** 81 | * builds turned off rules, which are supported by oxlint. 82 | * It accepts an filepath to the .oxlintrc.json file. 83 | * 84 | * It the .oxlintrc.json file could not be found or parsed, 85 | * no rules will be deactivated and an error to `console.error` will be emitted 86 | */ 87 | export const buildFromOxlintConfigFile = ( 88 | oxlintConfigFile: string, 89 | options: BuildFromOxlintConfigOptions = {} 90 | ): EslintPluginOxlintConfig[] => { 91 | const config = getConfigContent(oxlintConfigFile); 92 | 93 | // we could not parse form the file, do not build with default values 94 | // we can not be sure if the setup is right 95 | if (config === undefined) { 96 | return []; 97 | } 98 | 99 | config.__misc = { 100 | filePath: path.resolve(oxlintConfigFile), 101 | }; 102 | 103 | return buildFromOxlintConfig(config, options); 104 | }; 105 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | // these are the mappings from the scope in the rules.rs to the eslint scope 2 | // only used for the scopes where the directory structure doesn't reflect the eslint scope 3 | // such as `typescript` vs `@typescript-eslint` or others. Eslint as a scope is an exception, 4 | // as eslint doesn't have a scope. 5 | // look here: 6 | export const aliasPluginNames: Record = { 7 | // for scripts/generate and src/build-from-oxlint-config 8 | eslint: '', 9 | typescript: '@typescript-eslint', 10 | nextjs: '@next/next', 11 | 12 | // only for src/build-from-oxlint-config 13 | react_perf: 'react-perf', 14 | jsx_a11y: 'jsx-a11y', 15 | 'import-x': 'import', 16 | }; 17 | 18 | // Some typescript-eslint rules are re-implemented version of eslint rules. 19 | // Since oxlint supports these rules under eslint/* and it also supports TS, 20 | // we should override these to make implementation status up-to-date. 21 | // remapping in source-code: 22 | export const typescriptRulesExtendEslintRules = [ 23 | 'class-methods-use-this', 24 | 'default-param-last', 25 | 'init-declarations', 26 | 'max-params', 27 | 'no-array-constructor', 28 | 'no-dupe-class-members', 29 | 'no-empty-function', 30 | 'no-invalid-this', 31 | 'no-loop-func', 32 | 'no-loss-of-precision', 33 | 'no-magic-numbers', 34 | 'no-redeclare', 35 | 'no-restricted-imports', 36 | 'no-shadow', 37 | 'no-unused-expressions', 38 | 'no-unused-vars', 39 | 'no-use-before-define', 40 | 'no-useless-constructor', 41 | ]; 42 | 43 | // Some vitest rules are re-implemented version of jest rules. 44 | // Since oxlint supports these rules under jest/*, we need to remap them. 45 | // remapping in source-code: 46 | export const viteTestCompatibleRules = [ 47 | 'consistent-test-it', 48 | 'expect-expect', 49 | 'max-expects', 50 | 'max-nested-describe', 51 | 'no-alias-methods', 52 | 'no-commented-out-tests', 53 | 'no-conditional-expect', 54 | 'no-conditional-in-test', 55 | 'no-disabled-tests', 56 | 'no-duplicate-hooks', 57 | 'no-focused-tests', 58 | 'no-hooks', 59 | 'no-identical-title', 60 | 'no-interpolation-in-snapshots', 61 | 'no-large-snapshots', 62 | 'no-mocks-import', 63 | 'no-restricted-jest-methods', 64 | 'no-restricted-matchers', 65 | 'no-standalone-expect', 66 | 'no-test-prefixes', 67 | 'no-test-return-statement', 68 | 'prefer-called-with', 69 | 'prefer-comparison-matcher', 70 | 'prefer-each', 71 | 'prefer-equality-matcher', 72 | 'prefer-expect-resolves', 73 | 'prefer-hooks-in-order', 74 | 'prefer-hooks-on-top', 75 | 'prefer-lowercase-title', 76 | 'prefer-mock-promise-shorthand', 77 | 'prefer-spy-on', 78 | 'prefer-strict-equal', 79 | 'prefer-to-be', 80 | 'prefer-to-contain', 81 | 'prefer-to-have-length', 82 | 'prefer-todo', 83 | 'require-hook', 84 | 'require-to-throw-message', 85 | 'require-top-level-describe', 86 | 'valid-describe-callback', 87 | 'valid-expect', 88 | ]; 89 | 90 | export const unicornRulesExtendEslintRules = ['no-negated-condition']; 91 | 92 | // All rules from `eslint-plugin-react-hooks` 93 | // Since oxlint supports these rules under react/*, we need to remap them. 94 | export const reactHookRulesInsideReactScope = ['rules-of-hooks', 'exhaustive-deps']; 95 | 96 | // These rules are disabled for vue and svelte files 97 | // because oxlint can not parse currently the HTML 98 | export const rulesDisabledForVueAndSvelteFiles = [ 99 | 'no-unused-vars', 100 | '@typescript-eslint/no-unused-vars', 101 | 'react-hooks/rules-of-hooks', // disabled because its react 102 | ]; 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eslint-plugin-oxlint 2 | 3 | ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/oxc-project/eslint-plugin-oxlint/.github%2Fworkflows%2Ftest.yml?branch=main) 4 | ![NPM Version](https://img.shields.io/npm/v/eslint-plugin-oxlint) ![NPM Downloads](https://img.shields.io/npm/dm/eslint-plugin-oxlint) 5 | 6 | Turn off all rules already supported by `oxlint`. The rules are extracted from [here](https://github.com/oxc-project/oxc/blob/main/crates/oxc_linter/src/rules.rs). 7 | 8 | ## What is oxlint? 9 | 10 | See https://oxc.rs/blog/2023-12-12-announcing-oxlint.html 11 | 12 | ## Installation 13 | 14 | ```shell 15 | pnpm add eslint-plugin-oxlint -D 16 | ``` 17 | 18 | ## Usage 19 | 20 | ### Run oxlint before eslint 21 | 22 | Add the following script to your `package.json`: 23 | 24 | ```json 25 | { 26 | "scripts": { 27 | "lint": "npx oxlint && npx eslint" 28 | } 29 | } 30 | ``` 31 | 32 | ### Flat config 33 | 34 | This plugin is optimized for flat config usage (eslint >= 9.0). See [here](https://eslint.org/docs/latest/use/configure/configuration-files-new) for more details. 35 | 36 | Example: 37 | 38 | ```js 39 | // eslint.config.js 40 | import oxlint from 'eslint-plugin-oxlint'; 41 | export default [ 42 | ...// other plugins 43 | ...oxlint.configs['flat/recommended'], // oxlint should be the last one 44 | ]; 45 | ``` 46 | 47 | ### Legacy config 48 | 49 | If you are using legacy configuration (eslint < 9.0), you can use the following config: 50 | 51 | ```js 52 | // .eslintrc.js 53 | module.exports = { 54 | ... // other config 55 | extends: [ 56 | ... // other presets 57 | "plugin:oxlint/recommended", 58 | ], 59 | } 60 | ``` 61 | 62 | ### Detect rules from `.oxlintrc.json` 63 | 64 | If you are using flat configuration (eslint >= 9.0), you can use the following config: 65 | 66 | ```js 67 | // eslint.config.js 68 | import oxlint from 'eslint-plugin-oxlint'; 69 | export default [ 70 | ..., // other plugins 71 | ...oxlint.buildFromOxlintConfigFile('./.oxlintrc.json'), 72 | ]; 73 | ``` 74 | 75 | Or build it by an `.oxlintrc.json`-like object: 76 | 77 | ```js 78 | // eslint.config.js 79 | import oxlint from 'eslint-plugin-oxlint'; 80 | export default [ 81 | ..., // other plugins 82 | ...oxlint.buildFromOxlintConfig({ 83 | categories: { 84 | correctness: 'warn' 85 | }, 86 | rules: { 87 | eqeqeq: 'warn' 88 | } 89 | }), 90 | ]; 91 | ``` 92 | 93 | `buildFromOxlintConfigFile` is not supported for legacy configuration (eslint < 9.0). 94 | 95 | ## All Configs 96 | 97 | ```js 98 | configs: { 99 | // recommended only contains the `correctness` category 100 | recommended: { plugins: [Array], rules: [Object] }, 101 | 'flat/recommended': { rules: [Object] }, 102 | 103 | // all rules available 104 | all: { plugins: [Array], rules: [Object] }, 105 | 'flat/all': { rules: [Object] }, 106 | 107 | // turn eslint rules off by plugin 108 | 'flat/eslint': { rules: [Object] }, 109 | 'flat/import': { rules: [Object] }, 110 | 'flat/jest': { rules: [Object] }, 111 | 'flat/jsdoc': { rules: [Object] }, 112 | 'flat/jsx-a11y': { rules: [Object] }, 113 | 'flat/nextjs': { rules: [Object] }, 114 | 'flat/react': { rules: [Object] }, 115 | 'flat/react-perf': { rules: [Object] }, 116 | 'flat/tree-shaking': { rules: [Object] }, 117 | 'flat/typescript': { rules: [Object] }, 118 | 'flat/unicorn': { rules: [Object] }, 119 | 120 | // turn eslint rules off by oxlint category 121 | 'flat/pedantic': { rules: [Object] }, 122 | 'flat/style': { rules: [Object] }, 123 | 'flat/correctness': { rules: [Object] }, 124 | 'flat/restriction': { rules: [Object] }, 125 | 'flat/suspicious': { rules: [Object] } 126 | } 127 | ``` 128 | 129 | ## VSCode Support 130 | 131 | You need to install both the [oxc](https://marketplace.visualstudio.com/items?itemName=oxc.oxc-vscode) and [eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) extensions 132 | 133 | ## Contributing 134 | 135 | ### Generate rules 136 | 137 | Generates the rules from installed oxlint version 138 | 139 | ```shell 140 | pnpm generate 141 | pnpm format 142 | ``` 143 | 144 | ### Test 145 | 146 | Tests the source code 147 | 148 | ```shell 149 | pnpm test 150 | ``` 151 | 152 | ## License 153 | 154 | [MIT](https://github.com/Dunqing/eslint-plugin-oxlint/blob/main/LICENSE) 155 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/rules.ts: -------------------------------------------------------------------------------- 1 | import { aliasPluginNames, reactHookRulesInsideReactScope } from '../constants.js'; 2 | import { 3 | BuildFromOxlintConfigOptions, 4 | OxlintConfig, 5 | OxlintConfigOverride, 6 | OxlintConfigRules, 7 | } from './types.js'; 8 | import * as allRulesObjects from '../generated/rules-by-category.js'; 9 | import { isObject } from './utilities.js'; 10 | 11 | const allRules: string[] = Object.values(allRulesObjects).flatMap((rulesObject) => 12 | Object.keys(rulesObject) 13 | ); 14 | 15 | const typeAwareRules = new Set( 16 | Object.entries(allRulesObjects) 17 | .filter(([key]) => key.endsWith('TypeAwareRules')) 18 | .flatMap(([, rulesObject]) => Object.keys(rulesObject)) 19 | ); 20 | 21 | const getEsLintRuleName = ( 22 | rule: string, 23 | options: BuildFromOxlintConfigOptions = {} 24 | ): string | undefined => { 25 | // there is no plugin prefix, it can be all plugin 26 | if (!rule.includes('/')) { 27 | const found = allRules.find((search) => search.endsWith(`/${rule}`) || search === rule); 28 | 29 | if (!found) { 30 | return undefined; 31 | } 32 | 33 | // Filter out nursery rules unless explicitly enabled 34 | if (!options.withNursery && found in allRulesObjects.nurseryRules) { 35 | return undefined; 36 | } 37 | 38 | // Check for type-aware rules when enabled 39 | if (!options.typeAware && typeAwareRules.has(found)) { 40 | return undefined; 41 | } 42 | 43 | return found; 44 | } 45 | 46 | // greedy works with `@next/next/no-img-element` as an example 47 | const match = rule.match(/(^.*)\/(.*)/); 48 | 49 | if (match === null) { 50 | return undefined; 51 | } 52 | 53 | const pluginName = match[1]; 54 | const ruleName = match[2]; 55 | 56 | // map to the right eslint plugin 57 | let esPluginName = pluginName in aliasPluginNames ? aliasPluginNames[pluginName] : pluginName; 58 | 59 | // special case for eslint-plugin-react-hooks 60 | if (esPluginName === 'react' && reactHookRulesInsideReactScope.includes(ruleName)) { 61 | esPluginName = 'react-hooks'; 62 | } 63 | 64 | // extra check for eslint 65 | const expectedRule = esPluginName === '' ? ruleName : `${esPluginName}/${ruleName}`; 66 | 67 | const found = allRules.find((rule) => rule === expectedRule); 68 | 69 | if (!found) { 70 | return undefined; 71 | } 72 | // Filter out nursery rules unless explicitly enabled 73 | if (!options.withNursery && found in allRulesObjects.nurseryRules) { 74 | return undefined; 75 | } 76 | 77 | // Check for type-aware rules when enabled 78 | if (!options.typeAware && typeAwareRules.has(found)) { 79 | return undefined; 80 | } 81 | 82 | return found; 83 | }; 84 | 85 | /** 86 | * checks if value is validSet, or if validSet is an array, check if value is first value of it 87 | */ 88 | const isValueInSet = (value: unknown, validSet: unknown[]) => 89 | validSet.includes(value) || (Array.isArray(value) && validSet.includes(value[0])); 90 | 91 | /** 92 | * check if the value is "off", 0, ["off", ...], or [0, ...] 93 | */ 94 | const isDeactivateValue = (value: unknown) => isValueInSet(value, ['off', 0]); 95 | 96 | /** 97 | * check if the value is "error", "warn", 1, 2, ["error", ...], ["warn", ...], [1, ...], or [2, ...] 98 | */ 99 | const isActiveValue = (value: unknown) => isValueInSet(value, ['error', 'warn', 1, 2]); 100 | 101 | /** 102 | * checks if the oxlint rule is activated/deactivated and append/remove it. 103 | */ 104 | export const handleRulesScope = ( 105 | oxlintRules: OxlintConfigRules, 106 | rules: Record, 107 | options: BuildFromOxlintConfigOptions = {} 108 | ): void => { 109 | for (const rule in oxlintRules) { 110 | const eslintName = getEsLintRuleName(rule, options); 111 | 112 | if (eslintName === undefined) { 113 | continue; 114 | } 115 | 116 | // is this rules not turned off 117 | if (isActiveValue(oxlintRules[rule])) { 118 | rules[eslintName] = 'off'; 119 | } else if (rule in rules && isDeactivateValue(oxlintRules[rule])) { 120 | // rules extended by categories or plugins can be disabled manually 121 | delete rules[eslintName]; 122 | } 123 | } 124 | }; 125 | 126 | /** 127 | * tries to return the "rules" section from the config. 128 | * it returns `undefined` when not found or invalid. 129 | */ 130 | export const readRulesFromConfig = ( 131 | config: OxlintConfig | OxlintConfigOverride 132 | ): OxlintConfigRules | undefined => { 133 | return 'rules' in config && isObject(config.rules) 134 | ? (config.rules as OxlintConfigRules) 135 | : undefined; 136 | }; 137 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/extends.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { handleExtendsScope, resolveRelativeExtendsPaths } from './extends.js'; 3 | import { OxlintConfig } from './types.js'; 4 | 5 | describe('handleExtendsScope', () => { 6 | it('should handle empty extends configs', () => { 7 | const extendsConfigs: OxlintConfig[] = []; 8 | const config: OxlintConfig = { 9 | plugins: ['react'], 10 | categories: { correctness: 'warn' }, 11 | rules: { eqeqeq: 'error' }, 12 | }; 13 | handleExtendsScope(extendsConfigs, config); 14 | expect(config).toEqual(config); 15 | }); 16 | 17 | it('should merge extends configs', () => { 18 | const extendsConfigs: OxlintConfig[] = [ 19 | { 20 | plugins: ['react', 'unicorn'], 21 | categories: { correctness: 'error' }, 22 | rules: { rule1: 'error' }, 23 | }, 24 | ]; 25 | const config: OxlintConfig = { 26 | categories: { correctness: 'warn' }, 27 | rules: { rule3: 'warn' }, 28 | }; 29 | handleExtendsScope(extendsConfigs, config); 30 | expect(config).toEqual({ 31 | plugins: ['react', 'unicorn'], 32 | categories: config.categories, 33 | rules: { rule1: 'error', rule3: 'warn' }, 34 | }); 35 | }); 36 | 37 | it('should merge extends and de duplicate plugins and rules', () => { 38 | const extendsConfigs: OxlintConfig[] = [ 39 | { 40 | plugins: ['react', 'typescript'], 41 | categories: { correctness: 'error' }, 42 | rules: { rule1: 'error', rule2: 'error' }, 43 | }, 44 | ]; 45 | const config: OxlintConfig = { 46 | plugins: ['react', 'unicorn'], 47 | categories: { correctness: 'warn' }, 48 | rules: { rule1: 'warn' }, 49 | }; 50 | handleExtendsScope(extendsConfigs, config); 51 | expect(config).toEqual({ 52 | plugins: ['react', 'typescript', 'unicorn'], 53 | categories: config.categories, 54 | rules: { rule1: 'warn', rule2: 'error' }, 55 | }); 56 | }); 57 | 58 | it('should merge multiple extends configs', () => { 59 | const extendsConfigs: OxlintConfig[] = [ 60 | { 61 | plugins: ['react', 'unicorn'], 62 | categories: { correctness: 'error' }, 63 | rules: { rule1: 'error', rule2: 'error' }, 64 | }, 65 | { 66 | plugins: ['typescript'], 67 | overrides: [{ files: ['*.ts'], rules: { rule3: 'error' } }], 68 | }, 69 | ]; 70 | const config: OxlintConfig = { 71 | plugins: ['react', 'vitest'], 72 | categories: { correctness: 'warn' }, 73 | rules: { rule1: 'warn' }, 74 | }; 75 | handleExtendsScope(extendsConfigs, config); 76 | expect(config).toEqual({ 77 | plugins: ['typescript', 'react', 'unicorn', 'vitest'], 78 | categories: config.categories, 79 | rules: { rule1: 'warn', rule2: 'error' }, 80 | overrides: [{ files: ['*.ts'], rules: { rule3: 'error' } }], 81 | }); 82 | }); 83 | 84 | it('should merge multiple extends configs with multiple overrides', () => { 85 | const extendsConfigs: OxlintConfig[] = [ 86 | { 87 | plugins: ['react', 'unicorn'], 88 | categories: { correctness: 'error' }, 89 | rules: { rule1: 'error', rule2: 'error' }, 90 | }, 91 | { 92 | plugins: ['typescript'], 93 | overrides: [{ files: ['*.ts'], rules: { rule3: 'error' } }], 94 | }, 95 | ]; 96 | const config: OxlintConfig = { 97 | plugins: ['react', 'vitest'], 98 | categories: { correctness: 'warn' }, 99 | rules: { rule1: 'warn' }, 100 | overrides: [{ files: ['*.spec.ts'], rules: { rule4: 'error' } }], 101 | }; 102 | handleExtendsScope(extendsConfigs, config); 103 | expect(config).toEqual({ 104 | plugins: ['typescript', 'react', 'unicorn', 'vitest'], 105 | categories: config.categories, 106 | rules: { rule1: 'warn', rule2: 'error' }, 107 | overrides: [ 108 | { files: ['*.ts'], rules: { rule3: 'error' } }, 109 | { files: ['*.spec.ts'], rules: { rule4: 'error' } }, 110 | ], 111 | }); 112 | }); 113 | }); 114 | 115 | describe('resolveRelativeExtendsPaths', () => { 116 | it('should resolve relative paths', () => { 117 | const config: OxlintConfig = { 118 | extends: ['./extends1.json', './folder/extends2.json', '../parent/extends3.json'], 119 | __misc: { 120 | filePath: '/root/of/the/file/test-config.json', 121 | }, 122 | }; 123 | resolveRelativeExtendsPaths(config); 124 | 125 | expect(config.extends).toEqual([ 126 | '/root/of/the/file/extends1.json', 127 | '/root/of/the/file/folder/extends2.json', 128 | '/root/of/the/parent/extends3.json', 129 | ]); 130 | }); 131 | }); 132 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/rules.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { handleRulesScope } from './rules.js'; 3 | import { unicornRulesExtendEslintRules, viteTestCompatibleRules } from '../constants.js'; 4 | 5 | describe('handleRulesScope', () => { 6 | for (const ruleSetting of ['error', ['error'], 'warn', ['warn'], 1, [1], 2, [2]]) { 7 | it(`detect active rule ${JSON.stringify(ruleSetting)} inside "rules" scope`, () => { 8 | const rules = {}; 9 | handleRulesScope( 10 | { 11 | eqeqeq: ruleSetting, 12 | }, 13 | rules 14 | ); 15 | 16 | expect(rules).toStrictEqual({ 17 | eqeqeq: 'off', 18 | }); 19 | }); 20 | } 21 | 22 | for (const ruleSetting of ['off', ['off'], 0, [0]]) { 23 | it(`skip deactive rule ${JSON.stringify(ruleSetting)} inside "rules" scope`, () => { 24 | const rules = {}; 25 | handleRulesScope( 26 | { 27 | eqeqeq: ruleSetting, 28 | }, 29 | rules 30 | ); 31 | 32 | expect(rules).toStrictEqual({}); 33 | }); 34 | } 35 | 36 | for (const ruleSetting of ['on', ['on'], 3, [3]]) { 37 | it(`skip invalid ${JSON.stringify(ruleSetting)} inside "rules" scope`, () => { 38 | const rules = {}; 39 | handleRulesScope( 40 | { 41 | eqeqeq: ruleSetting, 42 | }, 43 | rules 44 | ); 45 | 46 | expect(rules).toStrictEqual({}); 47 | }); 48 | } 49 | 50 | // look here: 51 | it('detects oxlint rules with plugin alias inside rules block', () => { 52 | const rules = {}; 53 | handleRulesScope( 54 | { 55 | 'eslint/eqeqeq': 'warn', 56 | 'typescript/no-unused-vars': 'warn', 57 | 'react_perf/jsx-no-new-array-as-prop': 'warn', 58 | 'nextjs/no-img-element': 'warn', 59 | 'jsx_a11y/alt-text': 'warn', 60 | 'react/rules-of-hooks': 'warn', 61 | 'import-x/namespace': 'warn', 62 | // 'deepscan/xxx': 'warn', 63 | }, 64 | rules 65 | ); 66 | 67 | expect(rules).toStrictEqual({ 68 | eqeqeq: 'off', 69 | '@typescript-eslint/no-unused-vars': 'off', 70 | 'react-perf/jsx-no-new-array-as-prop': 'off', 71 | '@next/next/no-img-element': 'off', 72 | 'jsx-a11y/alt-text': 'off', 73 | 'react-hooks/rules-of-hooks': 'off', 74 | 'import/namespace': 'off', 75 | }); 76 | }); 77 | 78 | it('detects rules without plugin name', () => { 79 | const rules = {}; 80 | handleRulesScope( 81 | { 82 | 'no-unused-vars': 'warn', 83 | 'jsx-no-new-array-as-prop': 'warn', 84 | 'no-img-element': 'warn', 85 | 'no-array-reduce': 'warn', 86 | }, 87 | rules 88 | ); 89 | 90 | expect(rules).toStrictEqual({ 91 | 'no-unused-vars': 'off', // ToDo: should be @typescript-eslint/ 92 | 'react-perf/jsx-no-new-array-as-prop': 'off', 93 | '@next/next/no-img-element': 'off', 94 | 'unicorn/no-array-reduce': 'off', 95 | }); 96 | }); 97 | 98 | it('skips unknown oxlint rules', () => { 99 | const rules = {}; 100 | handleRulesScope( 101 | { 102 | unknown: 'warn', 103 | 'typescript/no-img-element': 'warn', // valid rule, but wrong plugin-name 104 | }, 105 | rules 106 | ); 107 | 108 | expect(rules).toStrictEqual({}); 109 | }); 110 | 111 | for (const alias of viteTestCompatibleRules) { 112 | it(`disables vitest jest alias rules for ${alias}`, () => { 113 | for (const rule of [`jest/${alias}`, `vitest/${alias}`]) { 114 | const rules = {}; 115 | handleRulesScope( 116 | { 117 | [rule]: 'warn', 118 | }, 119 | rules 120 | ); 121 | 122 | expect(rules).toStrictEqual({ 123 | [rule]: 'off', 124 | }); 125 | } 126 | }); 127 | } 128 | 129 | for (const alias of unicornRulesExtendEslintRules) { 130 | it(`disables unicorn eslint alias rules for ${alias}`, () => { 131 | for (const rule of [`unicorn/${alias}`, alias]) { 132 | const rules = {}; 133 | handleRulesScope( 134 | { 135 | [rule]: 'warn', 136 | }, 137 | rules 138 | ); 139 | 140 | expect(rules).toStrictEqual({ 141 | [rule]: 'off', 142 | }); 143 | } 144 | }); 145 | } 146 | 147 | describe('type-aware rules', () => { 148 | it('should filter out type-aware rules by default', () => { 149 | const rules = {}; 150 | handleRulesScope( 151 | { 152 | '@typescript-eslint/await-thenable': 'error', 153 | '@typescript-eslint/no-unused-vars': 'error', 154 | }, 155 | rules 156 | ); 157 | 158 | expect(rules).toStrictEqual({ 159 | '@typescript-eslint/no-unused-vars': 'off', 160 | }); 161 | }); 162 | 163 | it('should include type-aware rules when typeAware is true', () => { 164 | const rules = {}; 165 | handleRulesScope( 166 | { 167 | '@typescript-eslint/await-thenable': 'error', 168 | '@typescript-eslint/no-unused-vars': 'error', 169 | }, 170 | rules, 171 | { typeAware: true } 172 | ); 173 | 174 | expect(rules).toStrictEqual({ 175 | '@typescript-eslint/await-thenable': 'off', 176 | '@typescript-eslint/no-unused-vars': 'off', 177 | }); 178 | }); 179 | 180 | it('should filter multiple type-aware rules', () => { 181 | const rules = {}; 182 | handleRulesScope( 183 | { 184 | '@typescript-eslint/no-unsafe-call': 'error', 185 | '@typescript-eslint/no-floating-promises': 'warn', 186 | eqeqeq: 'error', 187 | }, 188 | rules 189 | ); 190 | 191 | expect(rules).toStrictEqual({ 192 | eqeqeq: 'off', 193 | }); 194 | }); 195 | }); 196 | }); 197 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/__snapshots__/categories.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`handleCategoriesScope > custom plugins, custom categories > customPluginCustomCategories 1`] = ` 4 | { 5 | "block-scoped-var": "off", 6 | "import/no-absolute-path": "off", 7 | "import/no-empty-named-blocks": "off", 8 | "import/no-named-as-default": "off", 9 | "import/no-named-as-default-member": "off", 10 | "import/no-self-import": "off", 11 | "import/no-unassigned-import": "off", 12 | "no-extend-native": "off", 13 | "no-extra-bind": "off", 14 | "no-new": "off", 15 | "no-unexpected-multiline": "off", 16 | "no-unneeded-ternary": "off", 17 | "no-useless-concat": "off", 18 | "no-useless-constructor": "off", 19 | "preserve-caught-error": "off", 20 | } 21 | `; 22 | 23 | exports[`handleCategoriesScope > custom plugins, default categories > customPluginDefaultCategories 1`] = ` 24 | { 25 | "constructor-super": "off", 26 | "for-direction": "off", 27 | "no-async-promise-executor": "off", 28 | "no-caller": "off", 29 | "no-class-assign": "off", 30 | "no-compare-neg-zero": "off", 31 | "no-cond-assign": "off", 32 | "no-const-assign": "off", 33 | "no-constant-binary-expression": "off", 34 | "no-constant-condition": "off", 35 | "no-control-regex": "off", 36 | "no-debugger": "off", 37 | "no-delete-var": "off", 38 | "no-dupe-class-members": "off", 39 | "no-dupe-else-if": "off", 40 | "no-dupe-keys": "off", 41 | "no-duplicate-case": "off", 42 | "no-empty-character-class": "off", 43 | "no-empty-pattern": "off", 44 | "no-empty-static-block": "off", 45 | "no-eval": "off", 46 | "no-ex-assign": "off", 47 | "no-extra-boolean-cast": "off", 48 | "no-func-assign": "off", 49 | "no-global-assign": "off", 50 | "no-import-assign": "off", 51 | "no-invalid-regexp": "off", 52 | "no-irregular-whitespace": "off", 53 | "no-loss-of-precision": "off", 54 | "no-new-native-nonconstructor": "off", 55 | "no-nonoctal-decimal-escape": "off", 56 | "no-obj-calls": "off", 57 | "no-self-assign": "off", 58 | "no-setter-return": "off", 59 | "no-shadow-restricted-names": "off", 60 | "no-sparse-arrays": "off", 61 | "no-this-before-super": "off", 62 | "no-unassigned-vars": "off", 63 | "no-unsafe-finally": "off", 64 | "no-unsafe-negation": "off", 65 | "no-unsafe-optional-chaining": "off", 66 | "no-unused-expressions": "off", 67 | "no-unused-labels": "off", 68 | "no-unused-private-class-members": "off", 69 | "no-unused-vars": "off", 70 | "no-useless-backreference": "off", 71 | "no-useless-catch": "off", 72 | "no-useless-escape": "off", 73 | "no-useless-rename": "off", 74 | "no-with": "off", 75 | "require-yield": "off", 76 | "unicorn/no-await-in-promise-methods": "off", 77 | "unicorn/no-empty-file": "off", 78 | "unicorn/no-invalid-fetch-options": "off", 79 | "unicorn/no-invalid-remove-event-listener": "off", 80 | "unicorn/no-new-array": "off", 81 | "unicorn/no-single-promise-in-promise-methods": "off", 82 | "unicorn/no-thenable": "off", 83 | "unicorn/no-unnecessary-await": "off", 84 | "unicorn/no-useless-fallback-in-spread": "off", 85 | "unicorn/no-useless-length-check": "off", 86 | "unicorn/no-useless-spread": "off", 87 | "unicorn/prefer-set-size": "off", 88 | "unicorn/prefer-string-starts-ends-with": "off", 89 | "use-isnan": "off", 90 | "valid-typeof": "off", 91 | } 92 | `; 93 | 94 | exports[`handleCategoriesScope > default plugins (react, unicorn, typescript), default categories > defaultPluginDefaultCategories 1`] = ` 95 | { 96 | "@typescript-eslint/no-dupe-class-members": "off", 97 | "@typescript-eslint/no-duplicate-enum-values": "off", 98 | "@typescript-eslint/no-extra-non-null-assertion": "off", 99 | "@typescript-eslint/no-loss-of-precision": "off", 100 | "@typescript-eslint/no-misused-new": "off", 101 | "@typescript-eslint/no-non-null-asserted-optional-chain": "off", 102 | "@typescript-eslint/no-this-alias": "off", 103 | "@typescript-eslint/no-unnecessary-parameter-property-assignment": "off", 104 | "@typescript-eslint/no-unsafe-declaration-merging": "off", 105 | "@typescript-eslint/no-unused-expressions": "off", 106 | "@typescript-eslint/no-unused-vars": "off", 107 | "@typescript-eslint/no-useless-empty-export": "off", 108 | "@typescript-eslint/no-wrapper-object-types": "off", 109 | "@typescript-eslint/prefer-as-const": "off", 110 | "@typescript-eslint/triple-slash-reference": "off", 111 | "constructor-super": "off", 112 | "for-direction": "off", 113 | "no-async-promise-executor": "off", 114 | "no-caller": "off", 115 | "no-class-assign": "off", 116 | "no-compare-neg-zero": "off", 117 | "no-cond-assign": "off", 118 | "no-const-assign": "off", 119 | "no-constant-binary-expression": "off", 120 | "no-constant-condition": "off", 121 | "no-control-regex": "off", 122 | "no-debugger": "off", 123 | "no-delete-var": "off", 124 | "no-dupe-class-members": "off", 125 | "no-dupe-else-if": "off", 126 | "no-dupe-keys": "off", 127 | "no-duplicate-case": "off", 128 | "no-empty-character-class": "off", 129 | "no-empty-pattern": "off", 130 | "no-empty-static-block": "off", 131 | "no-eval": "off", 132 | "no-ex-assign": "off", 133 | "no-extra-boolean-cast": "off", 134 | "no-func-assign": "off", 135 | "no-global-assign": "off", 136 | "no-import-assign": "off", 137 | "no-invalid-regexp": "off", 138 | "no-irregular-whitespace": "off", 139 | "no-loss-of-precision": "off", 140 | "no-new-native-nonconstructor": "off", 141 | "no-nonoctal-decimal-escape": "off", 142 | "no-obj-calls": "off", 143 | "no-self-assign": "off", 144 | "no-setter-return": "off", 145 | "no-shadow-restricted-names": "off", 146 | "no-sparse-arrays": "off", 147 | "no-this-before-super": "off", 148 | "no-unassigned-vars": "off", 149 | "no-unsafe-finally": "off", 150 | "no-unsafe-negation": "off", 151 | "no-unsafe-optional-chaining": "off", 152 | "no-unused-expressions": "off", 153 | "no-unused-labels": "off", 154 | "no-unused-private-class-members": "off", 155 | "no-unused-vars": "off", 156 | "no-useless-backreference": "off", 157 | "no-useless-catch": "off", 158 | "no-useless-escape": "off", 159 | "no-useless-rename": "off", 160 | "no-with": "off", 161 | "react/forward-ref-uses-ref": "off", 162 | "react/jsx-key": "off", 163 | "react/jsx-no-duplicate-props": "off", 164 | "react/jsx-no-undef": "off", 165 | "react/jsx-props-no-spread-multi": "off", 166 | "react/no-children-prop": "off", 167 | "react/no-danger-with-children": "off", 168 | "react/no-direct-mutation-state": "off", 169 | "react/no-find-dom-node": "off", 170 | "react/no-is-mounted": "off", 171 | "react/no-render-return-value": "off", 172 | "react/no-string-refs": "off", 173 | "react/void-dom-elements-no-children": "off", 174 | "require-yield": "off", 175 | "unicorn/no-await-in-promise-methods": "off", 176 | "unicorn/no-empty-file": "off", 177 | "unicorn/no-invalid-fetch-options": "off", 178 | "unicorn/no-invalid-remove-event-listener": "off", 179 | "unicorn/no-new-array": "off", 180 | "unicorn/no-single-promise-in-promise-methods": "off", 181 | "unicorn/no-thenable": "off", 182 | "unicorn/no-unnecessary-await": "off", 183 | "unicorn/no-useless-fallback-in-spread": "off", 184 | "unicorn/no-useless-length-check": "off", 185 | "unicorn/no-useless-spread": "off", 186 | "unicorn/prefer-set-size": "off", 187 | "unicorn/prefer-string-starts-ends-with": "off", 188 | "use-isnan": "off", 189 | "valid-typeof": "off", 190 | } 191 | `; 192 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { 3 | buildFromOxlintConfig, 4 | buildFromOxlintConfigFile, 5 | } from './build-from-oxlint-config/index.js'; 6 | import fs from 'node:fs'; 7 | import { execSync } from 'node:child_process'; 8 | import type { Linter } from 'eslint'; 9 | import { 10 | typescriptRulesExtendEslintRules, 11 | unicornRulesExtendEslintRules, 12 | viteTestCompatibleRules, 13 | } from './constants.js'; 14 | 15 | describe('buildFromOxlintConfigFile', () => { 16 | it('successfully parse oxlint json config', () => { 17 | const configs = createConfigFileAndBuildFromIt( 18 | 'success-config.json', 19 | `{ 20 | "rules": { 21 | // hello world 22 | "no-await-in-loop": "error", 23 | }, 24 | }` 25 | ); 26 | 27 | expect(configs.length).toBeGreaterThanOrEqual(1); 28 | expect(configs[0].rules).not.toBeUndefined(); 29 | expect('no-await-in-loop' in configs[0].rules!).toBe(true); 30 | }); 31 | 32 | it('fails to find oxlint config', () => { 33 | const configs = buildFromOxlintConfigFile('not-found.json'); 34 | 35 | expect(configs).toStrictEqual([]); 36 | }); 37 | 38 | it('fails to parse invalid json', () => { 39 | const configs = createConfigFileAndBuildFromIt( 40 | 'invalid-json.json', 41 | '["this", is an invalid json format]' 42 | ); 43 | 44 | expect(configs).toStrictEqual([]); 45 | }); 46 | 47 | it('fails to parse invalid oxlint config', () => { 48 | const configs = createConfigFileAndBuildFromIt( 49 | 'invalid-config.json', 50 | JSON.stringify(['this is valid json but not an object']) 51 | ); 52 | 53 | expect(configs).toStrictEqual([]); 54 | }); 55 | }); 56 | 57 | describe('integration test with oxlint', () => { 58 | for (const [index, config] of [ 59 | // default 60 | {}, 61 | // no plugins 62 | { plugins: [] }, 63 | // simple plugin override 64 | { plugins: ['typescript'] }, 65 | // custom rule off 66 | { 67 | rules: { eqeqeq: 'off' }, 68 | }, 69 | // combination plugin + rule 70 | { plugins: ['nextjs'], rules: { eqeqeq: 'off' } }, 71 | 72 | // categories change 73 | { categories: { correctness: 'off', suspicious: 'warn' } }, 74 | // combination plugin + categires + rules 75 | { 76 | plugins: ['nextjs'], 77 | categories: { correctness: 'off', style: 'warn' }, 78 | rules: { eqeqeq: 'off' }, 79 | }, 80 | // all categories enabled 81 | { 82 | categories: { 83 | nursery: 'off', // we not support this category 84 | correctness: 'warn', 85 | pedantic: 'warn', 86 | perf: 'warn', 87 | restriction: 'warn', 88 | style: 'warn', 89 | suspicious: 'warn', 90 | }, 91 | }, 92 | // all plugins enabled 93 | { 94 | plugins: [ 95 | 'typescript', 96 | 'unicorn', 97 | 'react', 98 | 'react-perf', 99 | 'nextjs', 100 | 'import', 101 | 'jsdoc', 102 | 'jsx-a11y', 103 | 'node', 104 | 'promise', 105 | 'jest', 106 | 'vitest', 107 | 'vue', 108 | ], 109 | }, 110 | // everything on 111 | { 112 | plugins: [ 113 | 'typescript', 114 | 'unicorn', 115 | 'react', 116 | 'react-perf', 117 | 'nextjs', 118 | 'import', 119 | 'jsdoc', 120 | 'jsx-a11y', 121 | 'node', 122 | 'promise', 123 | 'jest', 124 | 'vitest', 125 | 'vue', 126 | ], 127 | categories: { 128 | nursery: 'off', // we not support this category 129 | correctness: 'warn', 130 | pedantic: 'warn', 131 | perf: 'warn', 132 | restriction: 'warn', 133 | style: 'warn', 134 | suspicious: 'warn', 135 | }, 136 | }, 137 | ].entries()) { 138 | const fileContent = JSON.stringify(config); 139 | 140 | it(`should output same rule count for: ${fileContent}`, () => { 141 | const oxlintRulesCount = executeOxlintWithConfiguration( 142 | `integration-test-${index}-oxlint.json`, 143 | config 144 | ); 145 | 146 | const configs = buildFromOxlintConfig(config); 147 | 148 | expect(configs.length).toBeGreaterThanOrEqual(1); 149 | expect(configs[0].rules).not.toBeUndefined(); 150 | 151 | let expectedCount = oxlintRulesCount ?? 0; 152 | let receivedCount = 0; 153 | 154 | for (const buildConfig of configs) { 155 | receivedCount += Object.keys(buildConfig.rules!).length; 156 | 157 | // special mapping for ts alias rules 158 | if (config.plugins === undefined || config.plugins.includes('typescript')) { 159 | expectedCount += typescriptRulesExtendEslintRules.filter( 160 | (aliasRule) => aliasRule in buildConfig.rules! 161 | ).length; 162 | } 163 | 164 | // special case for vitest / jest alias rules 165 | if (config.plugins?.includes('vitest')) { 166 | expectedCount += viteTestCompatibleRules.filter( 167 | (aliasRule) => `vitest/${aliasRule}` in buildConfig.rules! 168 | ).length; 169 | } 170 | 171 | // special mapping for unicorn alias rules 172 | if (config.plugins === undefined || config.plugins.includes('unicorn')) { 173 | expectedCount += unicornRulesExtendEslintRules.filter( 174 | (aliasRule) => `unicorn/${aliasRule}` in buildConfig.rules! 175 | ).length; 176 | } 177 | } 178 | 179 | expect(receivedCount).toBe(expectedCount); 180 | }); 181 | } 182 | }); 183 | 184 | describe('nursery rules', () => { 185 | it('should not output nursery rules by default', () => { 186 | const config = { 187 | rules: { 188 | 'import/named': 'error', 189 | 'no-undef': 'error', 190 | }, 191 | }; 192 | 193 | const configs = buildFromOxlintConfig(config); 194 | 195 | expect(configs.length).toBeGreaterThanOrEqual(1); 196 | expect(configs[0].rules).not.toBeUndefined(); 197 | 198 | // nursery rules should NOT be present 199 | expect('import/named' in configs[0].rules!).toBe(false); 200 | expect('no-undef' in configs[0].rules!).toBe(false); 201 | }); 202 | 203 | it('should output nursery rules when withNursery option is true', () => { 204 | const config = { 205 | rules: { 206 | 'import/named': 'error', 207 | 'no-undef': 'error', 208 | }, 209 | }; 210 | 211 | const configs = buildFromOxlintConfig(config, { withNursery: true }); 212 | 213 | expect(configs.length).toBeGreaterThanOrEqual(1); 214 | expect(configs[0].rules).not.toBeUndefined(); 215 | 216 | // nursery rules SHOULD be present when withNursery is true 217 | expect('import/named' in configs[0].rules!).toBe(true); 218 | expect('no-undef' in configs[0].rules!).toBe(true); 219 | expect(configs[0].rules!['import/named']).toBe('off'); 220 | expect(configs[0].rules!['no-undef']).toBe('off'); 221 | }); 222 | }); 223 | 224 | describe('type-aware rules', () => { 225 | it('should not output type-aware rules by default', () => { 226 | const config = { 227 | rules: { 228 | '@typescript-eslint/await-thenable': 'error', 229 | '@typescript-eslint/no-floating-promises': 'warn', 230 | }, 231 | }; 232 | 233 | const configs = buildFromOxlintConfig(config); 234 | 235 | expect(configs.length).toBeGreaterThanOrEqual(1); 236 | expect(configs[0].rules).not.toBeUndefined(); 237 | 238 | // type-aware rules should NOT be present by default 239 | expect('@typescript-eslint/await-thenable' in configs[0].rules!).toBe(false); 240 | expect('@typescript-eslint/no-floating-promises' in configs[0].rules!).toBe(false); 241 | }); 242 | 243 | it('should output type-aware rules when typeAware option is true', () => { 244 | const config = { 245 | rules: { 246 | '@typescript-eslint/await-thenable': 'error', 247 | '@typescript-eslint/no-floating-promises': 'warn', 248 | }, 249 | }; 250 | 251 | const configs = buildFromOxlintConfig(config, { typeAware: true }); 252 | 253 | expect(configs.length).toBeGreaterThanOrEqual(1); 254 | expect(configs[0].rules).not.toBeUndefined(); 255 | 256 | expect('@typescript-eslint/await-thenable' in configs[0].rules!).toBe(true); 257 | expect('@typescript-eslint/no-floating-promises' in configs[0].rules!).toBe(true); 258 | }); 259 | }); 260 | 261 | describe('ignorePatterns handling', () => { 262 | it('should create ignore config as eslint global ignores', () => { 263 | const config = { 264 | ignorePatterns: ['**/*.test.ts', '**/*.spec.ts'], 265 | rules: { eqeqeq: 'off' }, 266 | }; 267 | const configs = buildFromOxlintConfig(config); 268 | 269 | expect(configs.length).toBeGreaterThanOrEqual(2); 270 | expect(configs[0].rules).toBeUndefined(); 271 | expect(configs[0].ignores).toStrictEqual(['**/*.test.ts', '**/*.spec.ts']); 272 | }); 273 | }); 274 | 275 | const createConfigFileAndBuildFromIt = ( 276 | filename: string, 277 | content: string 278 | ): Linter.Config>[] => { 279 | fs.writeFileSync(filename, content); 280 | 281 | const rules = buildFromOxlintConfigFile(filename); 282 | 283 | fs.unlinkSync(filename); 284 | 285 | return rules; 286 | }; 287 | 288 | const executeOxlintWithConfiguration = ( 289 | filename: string, 290 | config: { 291 | [key: string]: unknown; 292 | plugins?: string[]; 293 | categories?: Record; 294 | rules?: Record; 295 | } 296 | ) => { 297 | fs.writeFileSync(filename, JSON.stringify(config)); 298 | let oxlintOutput: string; 299 | 300 | const cliArguments = [`--config=${filename}`, '--disable-oxc-plugin', '--silent']; 301 | 302 | try { 303 | oxlintOutput = execSync(`npx oxlint ${cliArguments.join(' ')}`, { 304 | encoding: 'utf8', 305 | stdio: 'pipe', 306 | }); 307 | } catch { 308 | oxlintOutput = ''; 309 | } 310 | 311 | fs.unlinkSync(filename); 312 | 313 | const result = /with\s(\d+)\srules/.exec(oxlintOutput); 314 | 315 | if (result === null) { 316 | return; 317 | } 318 | 319 | return Number.parseInt(result[1], 10) ?? undefined; 320 | }; 321 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | /* Projects */ 5 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 6 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 7 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 8 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 9 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 10 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 11 | /* Language and Environment */ 12 | "target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, 13 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 14 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 15 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ 16 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 17 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 18 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 19 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 20 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 21 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 22 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 23 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 24 | /* Modules */ 25 | "module": "NodeNext" /* Specify what module code is generated. */, 26 | // "rootDir": "./", /* Specify the root folder within your source files. */ 27 | "moduleResolution": "NodeNext" /* Specify how TypeScript looks up a file from a given module specifier. */, 28 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 29 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 30 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 31 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 32 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 33 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 34 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 35 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ 36 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ 37 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ 38 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ 39 | "resolveJsonModule": true /* Enable importing .json files. */, 40 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ 41 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 42 | /* JavaScript Support */ 43 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 44 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 45 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 46 | /* Emit */ 47 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 48 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 49 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 50 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 51 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 52 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 53 | // "outDir": "./", /* Specify an output folder for all emitted files. */ 54 | // "removeComments": true, /* Disable emitting comments. */ 55 | // "noEmit": true, /* Disable emitting files from a compilation. */ 56 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 57 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 58 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 59 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 60 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 61 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 62 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 63 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 64 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 65 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 66 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 67 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 68 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 69 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 70 | /* Interop Constraints */ 71 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 72 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ 73 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 74 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, 75 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 76 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, 77 | /* Type Checking */ 78 | "strict": true /* Enable all strict type-checking options. */, 79 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 80 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 81 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 82 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 83 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 84 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 85 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 86 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 87 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 88 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 89 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 90 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 91 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 92 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 93 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 94 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 95 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 96 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 97 | /* Completeness */ 98 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 99 | "skipLibCheck": true /* Skip type checking all .d.ts files. */, 100 | }, 101 | "exclude": ["dist"], 102 | } 103 | -------------------------------------------------------------------------------- /src/generated/rules-by-scope.ts: -------------------------------------------------------------------------------- 1 | // These rules are automatically generated by scripts/generate-rules.ts 2 | 3 | const eslintRules: Record = { 4 | 'accessor-pairs': 'off', 5 | 'array-callback-return': 'off', 6 | 'arrow-body-style': 'off', 7 | 'block-scoped-var': 'off', 8 | 'capitalized-comments': 'off', 9 | 'class-methods-use-this': 'off', 10 | 'constructor-super': 'off', 11 | curly: 'off', 12 | 'default-case': 'off', 13 | 'default-case-last': 'off', 14 | 'default-param-last': 'off', 15 | eqeqeq: 'off', 16 | 'for-direction': 'off', 17 | 'func-style': 'off', 18 | 'func-names': 'off', 19 | 'grouped-accessor-pairs': 'off', 20 | 'guard-for-in': 'off', 21 | 'id-length': 'off', 22 | 'init-declarations': 'off', 23 | 'max-classes-per-file': 'off', 24 | 'max-depth': 'off', 25 | 'max-lines-per-function': 'off', 26 | 'max-lines': 'off', 27 | 'max-nested-callbacks': 'off', 28 | 'max-params': 'off', 29 | 'new-cap': 'off', 30 | 'no-implicit-coercion': 'off', 31 | 'no-inline-comments': 'off', 32 | 'no-loop-func': 'off', 33 | 'no-useless-computed-key': 'off', 34 | 'no-unassigned-vars': 'off', 35 | 'no-extra-bind': 'off', 36 | 'no-alert': 'off', 37 | 'no-array-constructor': 'off', 38 | 'no-async-promise-executor': 'off', 39 | 'no-await-in-loop': 'off', 40 | 'no-bitwise': 'off', 41 | 'no-caller': 'off', 42 | 'no-case-declarations': 'off', 43 | 'no-class-assign': 'off', 44 | 'no-duplicate-imports': 'off', 45 | 'no-extra-label': 'off', 46 | 'no-labels': 'off', 47 | 'no-lone-blocks': 'off', 48 | 'no-lonely-if': 'off', 49 | 'no-multi-assign': 'off', 50 | 'no-nested-ternary': 'off', 51 | 'no-object-constructor': 'off', 52 | 'no-param-reassign': 'off', 53 | 'no-restricted-imports': 'off', 54 | 'no-unneeded-ternary': 'off', 55 | 'no-useless-backreference': 'off', 56 | 'no-useless-call': 'off', 57 | 'no-compare-neg-zero': 'off', 58 | 'no-cond-assign': 'off', 59 | 'no-console': 'off', 60 | 'no-const-assign': 'off', 61 | 'no-constant-binary-expression': 'off', 62 | 'no-constant-condition': 'off', 63 | 'no-constructor-return': 'off', 64 | 'no-continue': 'off', 65 | 'no-control-regex': 'off', 66 | 'no-debugger': 'off', 67 | 'no-delete-var': 'off', 68 | 'no-div-regex': 'off', 69 | 'no-dupe-class-members': 'off', 70 | 'no-dupe-else-if': 'off', 71 | 'no-dupe-keys': 'off', 72 | 'no-duplicate-case': 'off', 73 | 'no-else-return': 'off', 74 | 'no-empty-character-class': 'off', 75 | 'no-empty-function': 'off', 76 | 'no-empty-pattern': 'off', 77 | 'no-empty-static-block': 'off', 78 | 'no-empty': 'off', 79 | 'no-eq-null': 'off', 80 | 'no-eval': 'off', 81 | 'no-ex-assign': 'off', 82 | 'no-extend-native': 'off', 83 | 'no-extra-boolean-cast': 'off', 84 | 'no-fallthrough': 'off', 85 | 'no-func-assign': 'off', 86 | 'no-global-assign': 'off', 87 | 'no-import-assign': 'off', 88 | 'no-inner-declarations': 'off', 89 | 'no-invalid-regexp': 'off', 90 | 'no-irregular-whitespace': 'off', 91 | 'no-iterator': 'off', 92 | 'no-label-var': 'off', 93 | 'no-loss-of-precision': 'off', 94 | 'no-magic-numbers': 'off', 95 | 'no-negated-condition': 'off', 96 | 'no-multi-str': 'off', 97 | 'no-new-func': 'off', 98 | 'no-new-native-nonconstructor': 'off', 99 | 'no-new-wrappers': 'off', 100 | 'no-new': 'off', 101 | 'no-nonoctal-decimal-escape': 'off', 102 | 'no-obj-calls': 'off', 103 | 'no-plusplus': 'off', 104 | 'no-promise-executor-return': 'off', 105 | 'no-proto': 'off', 106 | 'no-prototype-builtins': 'off', 107 | 'no-redeclare': 'off', 108 | 'no-regex-spaces': 'off', 109 | 'no-restricted-globals': 'off', 110 | 'no-return-assign': 'off', 111 | 'no-script-url': 'off', 112 | 'no-self-assign': 'off', 113 | 'no-self-compare': 'off', 114 | 'no-sequences': 'off', 115 | 'no-setter-return': 'off', 116 | 'no-shadow-restricted-names': 'off', 117 | 'no-sparse-arrays': 'off', 118 | 'no-template-curly-in-string': 'off', 119 | 'no-ternary': 'off', 120 | 'no-this-before-super': 'off', 121 | 'no-throw-literal': 'off', 122 | 'no-undefined': 'off', 123 | 'no-unexpected-multiline': 'off', 124 | 'no-unsafe-finally': 'off', 125 | 'no-unsafe-negation': 'off', 126 | 'no-unsafe-optional-chaining': 'off', 127 | 'no-unused-expressions': 'off', 128 | 'no-unused-labels': 'off', 129 | 'no-unused-private-class-members': 'off', 130 | 'no-unused-vars': 'off', 131 | 'no-useless-catch': 'off', 132 | 'no-useless-concat': 'off', 133 | 'no-useless-constructor': 'off', 134 | 'no-useless-escape': 'off', 135 | 'no-useless-rename': 'off', 136 | 'no-useless-return': 'off', 137 | 'no-var': 'off', 138 | 'no-void': 'off', 139 | 'no-warning-comments': 'off', 140 | 'no-with': 'off', 141 | 'operator-assignment': 'off', 142 | 'prefer-template': 'off', 143 | 'prefer-destructuring': 'off', 144 | 'prefer-promise-reject-errors': 'off', 145 | 'prefer-exponentiation-operator': 'off', 146 | 'prefer-numeric-literals': 'off', 147 | 'prefer-object-has-own': 'off', 148 | 'prefer-object-spread': 'off', 149 | 'prefer-rest-params': 'off', 150 | 'prefer-spread': 'off', 151 | 'preserve-caught-error': 'off', 152 | radix: 'off', 153 | 'require-await': 'off', 154 | 'require-yield': 'off', 155 | 'sort-imports': 'off', 156 | 'sort-keys': 'off', 157 | 'sort-vars': 'off', 158 | 'symbol-description': 'off', 159 | 'unicode-bom': 'off', 160 | 'use-isnan': 'off', 161 | 'valid-typeof': 'off', 162 | 'vars-on-top': 'off', 163 | yoda: 'off', 164 | }; 165 | 166 | const importRules: Record = { 167 | 'import/consistent-type-specifier-style': 'off', 168 | 'import/default': 'off', 169 | 'import/exports-last': 'off', 170 | 'import/extensions': 'off', 171 | 'import/first': 'off', 172 | 'import/group-exports': 'off', 173 | 'import/no-named-export': 'off', 174 | 'import/no-unassigned-import': 'off', 175 | 'import/no-empty-named-blocks': 'off', 176 | 'import/no-anonymous-default-export': 'off', 177 | 'import/no-absolute-path': 'off', 178 | 'import/no-mutable-exports': 'off', 179 | 'import/no-named-default': 'off', 180 | 'import/no-namespace': 'off', 181 | 'import/max-dependencies': 'off', 182 | 'import/namespace': 'off', 183 | 'import/no-amd': 'off', 184 | 'import/no-commonjs': 'off', 185 | 'import/no-cycle': 'off', 186 | 'import/no-default-export': 'off', 187 | 'import/no-duplicates': 'off', 188 | 'import/no-dynamic-require': 'off', 189 | 'import/no-named-as-default': 'off', 190 | 'import/no-named-as-default-member': 'off', 191 | 'import/no-self-import': 'off', 192 | 'import/no-webpack-loader-syntax': 'off', 193 | 'import/prefer-default-export': 'off', 194 | 'import/unambiguous': 'off', 195 | }; 196 | 197 | const jestRules: Record = { 198 | 'jest/consistent-test-it': 'off', 199 | 'jest/expect-expect': 'off', 200 | 'jest/max-expects': 'off', 201 | 'jest/max-nested-describe': 'off', 202 | 'jest/no-alias-methods': 'off', 203 | 'jest/no-commented-out-tests': 'off', 204 | 'jest/no-conditional-expect': 'off', 205 | 'jest/no-conditional-in-test': 'off', 206 | 'jest/no-confusing-set-timeout': 'off', 207 | 'jest/no-deprecated-functions': 'off', 208 | 'jest/no-disabled-tests': 'off', 209 | 'jest/no-done-callback': 'off', 210 | 'jest/no-duplicate-hooks': 'off', 211 | 'jest/no-export': 'off', 212 | 'jest/no-focused-tests': 'off', 213 | 'jest/no-hooks': 'off', 214 | 'jest/no-identical-title': 'off', 215 | 'jest/no-interpolation-in-snapshots': 'off', 216 | 'jest/no-jasmine-globals': 'off', 217 | 'jest/no-large-snapshots': 'off', 218 | 'jest/no-mocks-import': 'off', 219 | 'jest/no-restricted-jest-methods': 'off', 220 | 'jest/no-restricted-matchers': 'off', 221 | 'jest/no-standalone-expect': 'off', 222 | 'jest/no-test-prefixes': 'off', 223 | 'jest/no-test-return-statement': 'off', 224 | 'jest/no-untyped-mock-factory': 'off', 225 | 'jest/padding-around-test-blocks': 'off', 226 | 'jest/prefer-each': 'off', 227 | 'jest/prefer-called-with': 'off', 228 | 'jest/prefer-comparison-matcher': 'off', 229 | 'jest/prefer-equality-matcher': 'off', 230 | 'jest/prefer-expect-resolves': 'off', 231 | 'jest/prefer-hooks-in-order': 'off', 232 | 'jest/prefer-hooks-on-top': 'off', 233 | 'jest/prefer-jest-mocked': 'off', 234 | 'jest/prefer-lowercase-title': 'off', 235 | 'jest/prefer-mock-promise-shorthand': 'off', 236 | 'jest/prefer-spy-on': 'off', 237 | 'jest/prefer-strict-equal': 'off', 238 | 'jest/prefer-to-be': 'off', 239 | 'jest/prefer-to-contain': 'off', 240 | 'jest/prefer-to-have-been-called': 'off', 241 | 'jest/prefer-to-have-been-called-times': 'off', 242 | 'jest/prefer-to-have-length': 'off', 243 | 'jest/prefer-todo': 'off', 244 | 'jest/require-hook': 'off', 245 | 'jest/require-to-throw-message': 'off', 246 | 'jest/require-top-level-describe': 'off', 247 | 'jest/valid-describe-callback': 'off', 248 | 'jest/valid-expect': 'off', 249 | 'jest/valid-title': 'off', 250 | }; 251 | 252 | const jsdocRules: Record = { 253 | 'jsdoc/check-access': 'off', 254 | 'jsdoc/check-property-names': 'off', 255 | 'jsdoc/check-tag-names': 'off', 256 | 'jsdoc/empty-tags': 'off', 257 | 'jsdoc/implements-on-classes': 'off', 258 | 'jsdoc/no-defaults': 'off', 259 | 'jsdoc/require-param': 'off', 260 | 'jsdoc/require-param-description': 'off', 261 | 'jsdoc/require-param-name': 'off', 262 | 'jsdoc/require-param-type': 'off', 263 | 'jsdoc/require-property': 'off', 264 | 'jsdoc/require-property-description': 'off', 265 | 'jsdoc/require-property-name': 'off', 266 | 'jsdoc/require-property-type': 'off', 267 | 'jsdoc/require-returns': 'off', 268 | 'jsdoc/require-returns-description': 'off', 269 | 'jsdoc/require-returns-type': 'off', 270 | 'jsdoc/require-yields': 'off', 271 | }; 272 | 273 | const jsxA11yRules: Record = { 274 | 'jsx-a11y/alt-text': 'off', 275 | 'jsx-a11y/anchor-has-content': 'off', 276 | 'jsx-a11y/anchor-is-valid': 'off', 277 | 'jsx-a11y/aria-activedescendant-has-tabindex': 'off', 278 | 'jsx-a11y/aria-props': 'off', 279 | 'jsx-a11y/aria-role': 'off', 280 | 'jsx-a11y/aria-unsupported-elements': 'off', 281 | 'jsx-a11y/autocomplete-valid': 'off', 282 | 'jsx-a11y/click-events-have-key-events': 'off', 283 | 'jsx-a11y/heading-has-content': 'off', 284 | 'jsx-a11y/html-has-lang': 'off', 285 | 'jsx-a11y/iframe-has-title': 'off', 286 | 'jsx-a11y/img-redundant-alt': 'off', 287 | 'jsx-a11y/label-has-associated-control': 'off', 288 | 'jsx-a11y/lang': 'off', 289 | 'jsx-a11y/media-has-caption': 'off', 290 | 'jsx-a11y/mouse-events-have-key-events': 'off', 291 | 'jsx-a11y/no-noninteractive-tabindex': 'off', 292 | 'jsx-a11y/no-access-key': 'off', 293 | 'jsx-a11y/no-aria-hidden-on-focusable': 'off', 294 | 'jsx-a11y/no-autofocus': 'off', 295 | 'jsx-a11y/no-distracting-elements': 'off', 296 | 'jsx-a11y/no-redundant-roles': 'off', 297 | 'jsx-a11y/prefer-tag-over-role': 'off', 298 | 'jsx-a11y/role-has-required-aria-props': 'off', 299 | 'jsx-a11y/role-supports-aria-props': 'off', 300 | 'jsx-a11y/scope': 'off', 301 | 'jsx-a11y/tabindex-no-positive': 'off', 302 | 'jsx-a11y/anchor-ambiguous-text': 'off', 303 | }; 304 | 305 | const nextjsRules: Record = { 306 | '@next/next/google-font-display': 'off', 307 | '@next/next/google-font-preconnect': 'off', 308 | '@next/next/inline-script-id': 'off', 309 | '@next/next/next-script-for-ga': 'off', 310 | '@next/next/no-assign-module-variable': 'off', 311 | '@next/next/no-async-client-component': 'off', 312 | '@next/next/no-before-interactive-script-outside-document': 'off', 313 | '@next/next/no-css-tags': 'off', 314 | '@next/next/no-document-import-in-page': 'off', 315 | '@next/next/no-duplicate-head': 'off', 316 | '@next/next/no-head-element': 'off', 317 | '@next/next/no-head-import-in-document': 'off', 318 | '@next/next/no-img-element': 'off', 319 | '@next/next/no-page-custom-font': 'off', 320 | '@next/next/no-script-component-in-head': 'off', 321 | '@next/next/no-styled-jsx-in-document': 'off', 322 | '@next/next/no-sync-scripts': 'off', 323 | '@next/next/no-title-in-document-head': 'off', 324 | '@next/next/no-typos': 'off', 325 | '@next/next/no-unwanted-polyfillio': 'off', 326 | '@next/next/no-html-link-for-pages': 'off', 327 | }; 328 | 329 | const nodeRules: Record = { 330 | 'node/no-process-env': 'off', 331 | 'node/no-exports-assign': 'off', 332 | 'node/no-new-require': 'off', 333 | }; 334 | 335 | const promiseRules: Record = { 336 | 'promise/always-return': 'off', 337 | 'promise/avoid-new': 'off', 338 | 'promise/catch-or-return': 'off', 339 | 'promise/no-return-wrap': 'off', 340 | 'promise/no-nesting': 'off', 341 | 'promise/no-promise-in-callback': 'off', 342 | 'promise/no-callback-in-promise': 'off', 343 | 'promise/no-multiple-resolved': 'off', 344 | 'promise/no-new-statics': 'off', 345 | 'promise/param-names': 'off', 346 | 'promise/prefer-catch': 'off', 347 | 'promise/prefer-await-to-callbacks': 'off', 348 | 'promise/prefer-await-to-then': 'off', 349 | 'promise/spec-only': 'off', 350 | 'promise/valid-params': 'off', 351 | }; 352 | 353 | const reactRules: Record = { 354 | 'react/button-has-type': 'off', 355 | 'react/checked-requires-onchange-or-readonly': 'off', 356 | 'react/forbid-dom-props': 'off', 357 | 'react/forbid-elements': 'off', 358 | 'react/forward-ref-uses-ref': 'off', 359 | 'react/iframe-missing-sandbox': 'off', 360 | 'react/jsx-pascal-case': 'off', 361 | 'react/jsx-fragments': 'off', 362 | 'react/jsx-filename-extension': 'off', 363 | 'react/jsx-boolean-value': 'off', 364 | 'react/jsx-curly-brace-presence': 'off', 365 | 'react/jsx-handler-names': 'off', 366 | 'react/jsx-key': 'off', 367 | 'react/jsx-no-comment-textnodes': 'off', 368 | 'react/jsx-no-duplicate-props': 'off', 369 | 'react/jsx-no-script-url': 'off', 370 | 'react/jsx-no-target-blank': 'off', 371 | 'react/jsx-no-undef': 'off', 372 | 'react/jsx-no-useless-fragment': 'off', 373 | 'react/jsx-props-no-spread-multi': 'off', 374 | 'react/jsx-props-no-spreading': 'off', 375 | 'react/no-namespace': 'off', 376 | 'react/no-array-index-key': 'off', 377 | 'react/no-children-prop': 'off', 378 | 'react/no-danger-with-children': 'off', 379 | 'react/no-danger': 'off', 380 | 'react/no-direct-mutation-state': 'off', 381 | 'react/no-find-dom-node': 'off', 382 | 'react/no-is-mounted': 'off', 383 | 'react/no-redundant-should-component-update': 'off', 384 | 'react/no-render-return-value': 'off', 385 | 'react/no-set-state': 'off', 386 | 'react/no-string-refs': 'off', 387 | 'react/no-unescaped-entities': 'off', 388 | 'react/no-unknown-property': 'off', 389 | 'react/only-export-components': 'off', 390 | 'react/prefer-es6-class': 'off', 391 | 'react/react-in-jsx-scope': 'off', 392 | 'react/self-closing-comp': 'off', 393 | 'react/state-in-constructor': 'off', 394 | 'react/style-prop-object': 'off', 395 | 'react/void-dom-elements-no-children': 'off', 396 | }; 397 | 398 | const reactHooksRules: Record = { 399 | 'react-hooks/exhaustive-deps': 'off', 400 | 'react-hooks/rules-of-hooks': 'off', 401 | }; 402 | 403 | const reactPerfRules: Record = { 404 | 'react-perf/jsx-no-jsx-as-prop': 'off', 405 | 'react-perf/jsx-no-new-array-as-prop': 'off', 406 | 'react-perf/jsx-no-new-function-as-prop': 'off', 407 | 'react-perf/jsx-no-new-object-as-prop': 'off', 408 | }; 409 | 410 | const typescriptRules: Record = { 411 | '@typescript-eslint/adjacent-overload-signatures': 'off', 412 | '@typescript-eslint/array-type': 'off', 413 | '@typescript-eslint/ban-ts-comment': 'off', 414 | '@typescript-eslint/ban-tslint-comment': 'off', 415 | '@typescript-eslint/ban-types': 'off', 416 | '@typescript-eslint/consistent-generic-constructors': 'off', 417 | '@typescript-eslint/consistent-indexed-object-style': 'off', 418 | '@typescript-eslint/consistent-type-definitions': 'off', 419 | '@typescript-eslint/consistent-type-imports': 'off', 420 | '@typescript-eslint/explicit-module-boundary-types': 'off', 421 | '@typescript-eslint/explicit-function-return-type': 'off', 422 | '@typescript-eslint/no-inferrable-types': 'off', 423 | '@typescript-eslint/no-confusing-non-null-assertion': 'off', 424 | '@typescript-eslint/no-duplicate-enum-values': 'off', 425 | '@typescript-eslint/no-dynamic-delete': 'off', 426 | '@typescript-eslint/no-empty-interface': 'off', 427 | '@typescript-eslint/no-empty-object-type': 'off', 428 | '@typescript-eslint/no-explicit-any': 'off', 429 | '@typescript-eslint/no-extra-non-null-assertion': 'off', 430 | '@typescript-eslint/no-extraneous-class': 'off', 431 | '@typescript-eslint/no-import-type-side-effects': 'off', 432 | '@typescript-eslint/no-misused-new': 'off', 433 | '@typescript-eslint/no-namespace': 'off', 434 | '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'off', 435 | '@typescript-eslint/no-non-null-asserted-optional-chain': 'off', 436 | '@typescript-eslint/no-non-null-assertion': 'off', 437 | '@typescript-eslint/no-require-imports': 'off', 438 | '@typescript-eslint/no-restricted-types': 'off', 439 | '@typescript-eslint/no-this-alias': 'off', 440 | '@typescript-eslint/no-unnecessary-parameter-property-assignment': 'off', 441 | '@typescript-eslint/no-unnecessary-type-constraint': 'off', 442 | '@typescript-eslint/no-unsafe-declaration-merging': 'off', 443 | '@typescript-eslint/no-unsafe-function-type': 'off', 444 | '@typescript-eslint/no-useless-empty-export': 'off', 445 | '@typescript-eslint/no-var-requires': 'off', 446 | '@typescript-eslint/no-wrapper-object-types': 'off', 447 | '@typescript-eslint/prefer-as-const': 'off', 448 | '@typescript-eslint/prefer-enum-initializers': 'off', 449 | '@typescript-eslint/prefer-for-of': 'off', 450 | '@typescript-eslint/prefer-function-type': 'off', 451 | '@typescript-eslint/prefer-literal-enum-member': 'off', 452 | '@typescript-eslint/prefer-namespace-keyword': 'off', 453 | '@typescript-eslint/prefer-ts-expect-error': 'off', 454 | '@typescript-eslint/triple-slash-reference': 'off', 455 | '@typescript-eslint/class-methods-use-this': 'off', 456 | '@typescript-eslint/default-param-last': 'off', 457 | '@typescript-eslint/init-declarations': 'off', 458 | '@typescript-eslint/max-params': 'off', 459 | '@typescript-eslint/no-loop-func': 'off', 460 | '@typescript-eslint/no-array-constructor': 'off', 461 | '@typescript-eslint/no-restricted-imports': 'off', 462 | '@typescript-eslint/no-dupe-class-members': 'off', 463 | '@typescript-eslint/no-empty-function': 'off', 464 | '@typescript-eslint/no-loss-of-precision': 'off', 465 | '@typescript-eslint/no-magic-numbers': 'off', 466 | '@typescript-eslint/no-redeclare': 'off', 467 | '@typescript-eslint/no-unused-expressions': 'off', 468 | '@typescript-eslint/no-unused-vars': 'off', 469 | '@typescript-eslint/no-useless-constructor': 'off', 470 | }; 471 | 472 | const unicornRules: Record = { 473 | 'unicorn/catch-error-name': 'off', 474 | 'unicorn/consistent-assert': 'off', 475 | 'unicorn/consistent-date-clone': 'off', 476 | 'unicorn/consistent-empty-array-spread': 'off', 477 | 'unicorn/consistent-existence-index-check': 'off', 478 | 'unicorn/consistent-function-scoping': 'off', 479 | 'unicorn/empty-brace-spaces': 'off', 480 | 'unicorn/error-message': 'off', 481 | 'unicorn/escape-case': 'off', 482 | 'unicorn/explicit-length-check': 'off', 483 | 'unicorn/filename-case': 'off', 484 | 'unicorn/new-for-builtins': 'off', 485 | 'unicorn/no-unnecessary-array-splice-count': 'off', 486 | 'unicorn/no-array-callback-reference': 'off', 487 | 'unicorn/no-useless-collection-argument': 'off', 488 | 'unicorn/no-useless-error-capture-stack-trace': 'off', 489 | 'unicorn/no-array-sort': 'off', 490 | 'unicorn/no-array-reverse': 'off', 491 | 'unicorn/no-instanceof-builtins': 'off', 492 | 'unicorn/no-array-method-this-argument': 'off', 493 | 'unicorn/no-unnecessary-array-flat-depth': 'off', 494 | 'unicorn/no-unnecessary-slice-end': 'off', 495 | 'unicorn/no-accessor-recursion': 'off', 496 | 'unicorn/no-invalid-fetch-options': 'off', 497 | 'unicorn/no-abusive-eslint-disable': 'off', 498 | 'unicorn/no-anonymous-default-export': 'off', 499 | 'unicorn/no-array-for-each': 'off', 500 | 'unicorn/no-array-reduce': 'off', 501 | 'unicorn/no-await-expression-member': 'off', 502 | 'unicorn/no-await-in-promise-methods': 'off', 503 | 'unicorn/no-console-spaces': 'off', 504 | 'unicorn/no-document-cookie': 'off', 505 | 'unicorn/no-empty-file': 'off', 506 | 'unicorn/no-hex-escape': 'off', 507 | 'unicorn/no-instanceof-array': 'off', 508 | 'unicorn/no-invalid-remove-event-listener': 'off', 509 | 'unicorn/no-length-as-slice-end': 'off', 510 | 'unicorn/no-lonely-if': 'off', 511 | 'unicorn/no-magic-array-flat-depth': 'off', 512 | 'unicorn/no-negation-in-equality-check': 'off', 513 | 'unicorn/no-nested-ternary': 'off', 514 | 'unicorn/no-new-array': 'off', 515 | 'unicorn/no-new-buffer': 'off', 516 | 'unicorn/no-null': 'off', 517 | 'unicorn/no-object-as-default-parameter': 'off', 518 | 'unicorn/no-process-exit': 'off', 519 | 'unicorn/no-single-promise-in-promise-methods': 'off', 520 | 'unicorn/no-static-only-class': 'off', 521 | 'unicorn/no-thenable': 'off', 522 | 'unicorn/no-this-assignment': 'off', 523 | 'unicorn/no-typeof-undefined': 'off', 524 | 'unicorn/no-unnecessary-await': 'off', 525 | 'unicorn/no-unreadable-array-destructuring': 'off', 526 | 'unicorn/no-unreadable-iife': 'off', 527 | 'unicorn/no-useless-fallback-in-spread': 'off', 528 | 'unicorn/no-useless-length-check': 'off', 529 | 'unicorn/no-useless-promise-resolve-reject': 'off', 530 | 'unicorn/no-useless-spread': 'off', 531 | 'unicorn/no-useless-switch-case': 'off', 532 | 'unicorn/no-useless-undefined': 'off', 533 | 'unicorn/no-zero-fractions': 'off', 534 | 'unicorn/number-literal-case': 'off', 535 | 'unicorn/numeric-separators-style': 'off', 536 | 'unicorn/prefer-classlist-toggle': 'off', 537 | 'unicorn/prefer-class-fields': 'off', 538 | 'unicorn/prefer-bigint-literals': 'off', 539 | 'unicorn/prefer-default-parameters': 'off', 540 | 'unicorn/prefer-response-static-json': 'off', 541 | 'unicorn/prefer-top-level-await': 'off', 542 | 'unicorn/prefer-at': 'off', 543 | 'unicorn/prefer-global-this': 'off', 544 | 'unicorn/prefer-keyboard-event-key': 'off', 545 | 'unicorn/prefer-object-from-entries': 'off', 546 | 'unicorn/prefer-array-find': 'off', 547 | 'unicorn/prefer-array-index-of': 'off', 548 | 'unicorn/prefer-spread': 'off', 549 | 'unicorn/prefer-add-event-listener': 'off', 550 | 'unicorn/prefer-array-flat': 'off', 551 | 'unicorn/prefer-array-flat-map': 'off', 552 | 'unicorn/prefer-array-some': 'off', 553 | 'unicorn/prefer-blob-reading-methods': 'off', 554 | 'unicorn/prefer-code-point': 'off', 555 | 'unicorn/prefer-date-now': 'off', 556 | 'unicorn/prefer-dom-node-append': 'off', 557 | 'unicorn/prefer-dom-node-dataset': 'off', 558 | 'unicorn/prefer-dom-node-remove': 'off', 559 | 'unicorn/prefer-dom-node-text-content': 'off', 560 | 'unicorn/prefer-event-target': 'off', 561 | 'unicorn/prefer-includes': 'off', 562 | 'unicorn/prefer-logical-operator-over-ternary': 'off', 563 | 'unicorn/prefer-math-min-max': 'off', 564 | 'unicorn/prefer-math-trunc': 'off', 565 | 'unicorn/prefer-modern-dom-apis': 'off', 566 | 'unicorn/prefer-modern-math-apis': 'off', 567 | 'unicorn/prefer-native-coercion-functions': 'off', 568 | 'unicorn/prefer-negative-index': 'off', 569 | 'unicorn/prefer-node-protocol': 'off', 570 | 'unicorn/prefer-number-properties': 'off', 571 | 'unicorn/prefer-optional-catch-binding': 'off', 572 | 'unicorn/prefer-prototype-methods': 'off', 573 | 'unicorn/prefer-query-selector': 'off', 574 | 'unicorn/prefer-reflect-apply': 'off', 575 | 'unicorn/prefer-regexp-test': 'off', 576 | 'unicorn/prefer-set-has': 'off', 577 | 'unicorn/prefer-set-size': 'off', 578 | 'unicorn/prefer-string-raw': 'off', 579 | 'unicorn/prefer-string-replace-all': 'off', 580 | 'unicorn/prefer-string-slice': 'off', 581 | 'unicorn/prefer-string-starts-ends-with': 'off', 582 | 'unicorn/prefer-string-trim-start-end': 'off', 583 | 'unicorn/prefer-structured-clone': 'off', 584 | 'unicorn/prefer-type-error': 'off', 585 | 'unicorn/require-module-specifiers': 'off', 586 | 'unicorn/require-post-message-target-origin': 'off', 587 | 'unicorn/require-array-join-separator': 'off', 588 | 'unicorn/require-number-to-fixed-digits-argument': 'off', 589 | 'unicorn/switch-case-braces': 'off', 590 | 'unicorn/text-encoding-identifier-case': 'off', 591 | 'unicorn/throw-new-error': 'off', 592 | 'unicorn/no-negated-condition': 'off', 593 | }; 594 | 595 | const vitestRules: Record = { 596 | 'vitest/no-conditional-tests': 'off', 597 | 'vitest/no-import-node-test': 'off', 598 | 'vitest/prefer-to-be-falsy': 'off', 599 | 'vitest/prefer-to-be-object': 'off', 600 | 'vitest/prefer-to-be-truthy': 'off', 601 | 'vitest/require-local-test-context-for-concurrent-snapshots': 'off', 602 | 'vitest/consistent-test-it': 'off', 603 | 'vitest/expect-expect': 'off', 604 | 'vitest/max-expects': 'off', 605 | 'vitest/max-nested-describe': 'off', 606 | 'vitest/no-alias-methods': 'off', 607 | 'vitest/no-commented-out-tests': 'off', 608 | 'vitest/no-conditional-expect': 'off', 609 | 'vitest/no-conditional-in-test': 'off', 610 | 'vitest/no-disabled-tests': 'off', 611 | 'vitest/no-duplicate-hooks': 'off', 612 | 'vitest/no-focused-tests': 'off', 613 | 'vitest/no-hooks': 'off', 614 | 'vitest/no-identical-title': 'off', 615 | 'vitest/no-interpolation-in-snapshots': 'off', 616 | 'vitest/no-large-snapshots': 'off', 617 | 'vitest/no-mocks-import': 'off', 618 | 'vitest/no-restricted-jest-methods': 'off', 619 | 'vitest/no-restricted-matchers': 'off', 620 | 'vitest/no-standalone-expect': 'off', 621 | 'vitest/no-test-prefixes': 'off', 622 | 'vitest/no-test-return-statement': 'off', 623 | 'vitest/prefer-each': 'off', 624 | 'vitest/prefer-called-with': 'off', 625 | 'vitest/prefer-comparison-matcher': 'off', 626 | 'vitest/prefer-equality-matcher': 'off', 627 | 'vitest/prefer-expect-resolves': 'off', 628 | 'vitest/prefer-hooks-in-order': 'off', 629 | 'vitest/prefer-hooks-on-top': 'off', 630 | 'vitest/prefer-lowercase-title': 'off', 631 | 'vitest/prefer-mock-promise-shorthand': 'off', 632 | 'vitest/prefer-spy-on': 'off', 633 | 'vitest/prefer-strict-equal': 'off', 634 | 'vitest/prefer-to-be': 'off', 635 | 'vitest/prefer-to-contain': 'off', 636 | 'vitest/prefer-to-have-length': 'off', 637 | 'vitest/prefer-todo': 'off', 638 | 'vitest/require-hook': 'off', 639 | 'vitest/require-to-throw-message': 'off', 640 | 'vitest/require-top-level-describe': 'off', 641 | 'vitest/valid-describe-callback': 'off', 642 | 'vitest/valid-expect': 'off', 643 | }; 644 | 645 | const vueRules: Record = { 646 | 'vue/define-emits-declaration': 'off', 647 | 'vue/define-props-declaration': 'off', 648 | 'vue/define-props-destructuring': 'off', 649 | 'vue/max-props': 'off', 650 | 'vue/no-export-in-script-setup': 'off', 651 | 'vue/no-import-compiler-macros': 'off', 652 | 'vue/no-multiple-slot-args': 'off', 653 | 'vue/no-required-prop-with-default': 'off', 654 | 'vue/prefer-import-from-vue': 'off', 655 | 'vue/require-default-export': 'off', 656 | 'vue/require-typed-ref': 'off', 657 | 'vue/valid-define-emits': 'off', 658 | 'vue/valid-define-props': 'off', 659 | }; 660 | 661 | const typescriptTypeAwareRules: Record = { 662 | '@typescript-eslint/await-thenable': 'off', 663 | '@typescript-eslint/no-misused-promises': 'off', 664 | '@typescript-eslint/no-floating-promises': 'off', 665 | '@typescript-eslint/no-array-delete': 'off', 666 | '@typescript-eslint/no-base-to-string': 'off', 667 | '@typescript-eslint/no-confusing-void-expression': 'off', 668 | '@typescript-eslint/no-deprecated': 'off', 669 | '@typescript-eslint/no-duplicate-type-constituents': 'off', 670 | '@typescript-eslint/no-for-in-array': 'off', 671 | '@typescript-eslint/no-implied-eval': 'off', 672 | '@typescript-eslint/no-meaningless-void-operator': 'off', 673 | '@typescript-eslint/no-misused-spread': 'off', 674 | '@typescript-eslint/no-mixed-enums': 'off', 675 | '@typescript-eslint/no-redundant-type-constituents': 'off', 676 | '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off', 677 | '@typescript-eslint/no-unnecessary-template-expression': 'off', 678 | '@typescript-eslint/no-unnecessary-type-arguments': 'off', 679 | '@typescript-eslint/no-unnecessary-type-assertion': 'off', 680 | '@typescript-eslint/no-unsafe-argument': 'off', 681 | '@typescript-eslint/no-unsafe-assignment': 'off', 682 | '@typescript-eslint/no-unsafe-call': 'off', 683 | '@typescript-eslint/no-unsafe-enum-comparison': 'off', 684 | '@typescript-eslint/no-unsafe-member-access': 'off', 685 | '@typescript-eslint/no-unsafe-return': 'off', 686 | '@typescript-eslint/no-unsafe-type-assertion': 'off', 687 | '@typescript-eslint/no-unsafe-unary-minus': 'off', 688 | '@typescript-eslint/non-nullable-type-assertion-style': 'off', 689 | '@typescript-eslint/only-throw-error': 'off', 690 | '@typescript-eslint/prefer-includes': 'off', 691 | '@typescript-eslint/prefer-nullish-coalescing': 'off', 692 | '@typescript-eslint/prefer-promise-reject-errors': 'off', 693 | '@typescript-eslint/prefer-reduce-type-parameter': 'off', 694 | '@typescript-eslint/prefer-return-this-type': 'off', 695 | '@typescript-eslint/promise-function-async': 'off', 696 | '@typescript-eslint/related-getter-setter-pairs': 'off', 697 | '@typescript-eslint/require-array-sort-compare': 'off', 698 | '@typescript-eslint/require-await': 'off', 699 | '@typescript-eslint/restrict-plus-operands': 'off', 700 | '@typescript-eslint/restrict-template-expressions': 'off', 701 | '@typescript-eslint/return-await': 'off', 702 | '@typescript-eslint/strict-boolean-expressions': 'off', 703 | '@typescript-eslint/switch-exhaustiveness-check': 'off', 704 | '@typescript-eslint/unbound-method': 'off', 705 | '@typescript-eslint/use-unknown-in-catch-callback-variable': 'off', 706 | }; 707 | 708 | export { 709 | eslintRules, 710 | importRules, 711 | jestRules, 712 | jsdocRules, 713 | jsxA11yRules, 714 | nextjsRules, 715 | nodeRules, 716 | promiseRules, 717 | reactRules, 718 | reactHooksRules, 719 | reactPerfRules, 720 | typescriptRules, 721 | unicornRules, 722 | vitestRules, 723 | vueRules, 724 | typescriptTypeAwareRules, 725 | }; 726 | -------------------------------------------------------------------------------- /src/generated/rules-by-category.ts: -------------------------------------------------------------------------------- 1 | // These rules are automatically generated by scripts/generate-rules.ts 2 | 3 | const pedanticRules: Record = { 4 | 'accessor-pairs': 'off', 5 | 'array-callback-return': 'off', 6 | eqeqeq: 'off', 7 | 'max-classes-per-file': 'off', 8 | 'max-depth': 'off', 9 | 'max-lines-per-function': 'off', 10 | 'max-lines': 'off', 11 | 'max-nested-callbacks': 'off', 12 | 'no-inline-comments': 'off', 13 | 'no-loop-func': 'off', 14 | 'no-array-constructor': 'off', 15 | 'no-case-declarations': 'off', 16 | 'no-lonely-if': 'off', 17 | 'no-object-constructor': 'off', 18 | 'no-constructor-return': 'off', 19 | 'no-else-return': 'off', 20 | 'no-fallthrough': 'off', 21 | 'no-inner-declarations': 'off', 22 | 'no-negated-condition': 'off', 23 | 'no-new-wrappers': 'off', 24 | 'no-promise-executor-return': 'off', 25 | 'no-prototype-builtins': 'off', 26 | 'no-redeclare': 'off', 27 | 'no-self-compare': 'off', 28 | 'no-throw-literal': 'off', 29 | 'no-useless-return': 'off', 30 | 'no-warning-comments': 'off', 31 | radix: 'off', 32 | 'require-await': 'off', 33 | 'sort-vars': 'off', 34 | 'symbol-description': 'off', 35 | 'import/max-dependencies': 'off', 36 | 'jest/no-conditional-in-test': 'off', 37 | 'jsdoc/require-param': 'off', 38 | 'jsdoc/require-param-description': 'off', 39 | 'jsdoc/require-param-name': 'off', 40 | 'jsdoc/require-param-type': 'off', 41 | 'jsdoc/require-returns': 'off', 42 | 'jsdoc/require-returns-description': 'off', 43 | 'jsdoc/require-returns-type': 'off', 44 | 'react/checked-requires-onchange-or-readonly': 'off', 45 | 'react/jsx-no-target-blank': 'off', 46 | 'react/jsx-no-useless-fragment': 'off', 47 | 'react/no-unescaped-entities': 'off', 48 | 'react-hooks/rules-of-hooks': 'off', 49 | '@typescript-eslint/ban-ts-comment': 'off', 50 | '@typescript-eslint/ban-types': 'off', 51 | '@typescript-eslint/no-unsafe-function-type': 'off', 52 | '@typescript-eslint/prefer-enum-initializers': 'off', 53 | '@typescript-eslint/prefer-ts-expect-error': 'off', 54 | 'unicorn/consistent-assert': 'off', 55 | 'unicorn/consistent-empty-array-spread': 'off', 56 | 'unicorn/escape-case': 'off', 57 | 'unicorn/explicit-length-check': 'off', 58 | 'unicorn/new-for-builtins': 'off', 59 | 'unicorn/no-unnecessary-array-splice-count': 'off', 60 | 'unicorn/no-array-callback-reference': 'off', 61 | 'unicorn/no-unnecessary-array-flat-depth': 'off', 62 | 'unicorn/no-unnecessary-slice-end': 'off', 63 | 'unicorn/no-hex-escape': 'off', 64 | 'unicorn/no-instanceof-array': 'off', 65 | 'unicorn/no-lonely-if': 'off', 66 | 'unicorn/no-negation-in-equality-check': 'off', 67 | 'unicorn/no-new-buffer': 'off', 68 | 'unicorn/no-object-as-default-parameter': 'off', 69 | 'unicorn/no-static-only-class': 'off', 70 | 'unicorn/no-this-assignment': 'off', 71 | 'unicorn/no-typeof-undefined': 'off', 72 | 'unicorn/no-unreadable-iife': 'off', 73 | 'unicorn/no-useless-promise-resolve-reject': 'off', 74 | 'unicorn/no-useless-switch-case': 'off', 75 | 'unicorn/no-useless-undefined': 'off', 76 | 'unicorn/prefer-top-level-await': 'off', 77 | 'unicorn/prefer-at': 'off', 78 | 'unicorn/prefer-array-flat': 'off', 79 | 'unicorn/prefer-array-some': 'off', 80 | 'unicorn/prefer-blob-reading-methods': 'off', 81 | 'unicorn/prefer-code-point': 'off', 82 | 'unicorn/prefer-date-now': 'off', 83 | 'unicorn/prefer-dom-node-append': 'off', 84 | 'unicorn/prefer-dom-node-dataset': 'off', 85 | 'unicorn/prefer-dom-node-remove': 'off', 86 | 'unicorn/prefer-event-target': 'off', 87 | 'unicorn/prefer-math-min-max': 'off', 88 | 'unicorn/prefer-math-trunc': 'off', 89 | 'unicorn/prefer-native-coercion-functions': 'off', 90 | 'unicorn/prefer-prototype-methods': 'off', 91 | 'unicorn/prefer-query-selector': 'off', 92 | 'unicorn/prefer-regexp-test': 'off', 93 | 'unicorn/prefer-string-replace-all': 'off', 94 | 'unicorn/prefer-string-slice': 'off', 95 | 'unicorn/prefer-type-error': 'off', 96 | 'unicorn/require-number-to-fixed-digits-argument': 'off', 97 | '@typescript-eslint/no-loop-func': 'off', 98 | '@typescript-eslint/no-array-constructor': 'off', 99 | 'unicorn/no-negated-condition': 'off', 100 | '@typescript-eslint/no-redeclare': 'off', 101 | 'vitest/no-conditional-in-test': 'off', 102 | }; 103 | 104 | const styleRules: Record = { 105 | 'arrow-body-style': 'off', 106 | 'capitalized-comments': 'off', 107 | curly: 'off', 108 | 'default-case-last': 'off', 109 | 'default-param-last': 'off', 110 | 'func-style': 'off', 111 | 'func-names': 'off', 112 | 'grouped-accessor-pairs': 'off', 113 | 'guard-for-in': 'off', 114 | 'id-length': 'off', 115 | 'init-declarations': 'off', 116 | 'max-params': 'off', 117 | 'new-cap': 'off', 118 | 'no-implicit-coercion': 'off', 119 | 'no-useless-computed-key': 'off', 120 | 'no-duplicate-imports': 'off', 121 | 'no-extra-label': 'off', 122 | 'no-labels': 'off', 123 | 'no-lone-blocks': 'off', 124 | 'no-multi-assign': 'off', 125 | 'no-nested-ternary': 'off', 126 | 'no-continue': 'off', 127 | 'no-label-var': 'off', 128 | 'no-magic-numbers': 'off', 129 | 'no-multi-str': 'off', 130 | 'no-new-func': 'off', 131 | 'no-return-assign': 'off', 132 | 'no-script-url': 'off', 133 | 'no-template-curly-in-string': 'off', 134 | 'no-ternary': 'off', 135 | 'operator-assignment': 'off', 136 | 'prefer-template': 'off', 137 | 'prefer-destructuring': 'off', 138 | 'prefer-promise-reject-errors': 'off', 139 | 'prefer-exponentiation-operator': 'off', 140 | 'prefer-numeric-literals': 'off', 141 | 'prefer-object-has-own': 'off', 142 | 'prefer-object-spread': 'off', 143 | 'prefer-rest-params': 'off', 144 | 'prefer-spread': 'off', 145 | 'sort-imports': 'off', 146 | 'sort-keys': 'off', 147 | 'vars-on-top': 'off', 148 | yoda: 'off', 149 | 'import/consistent-type-specifier-style': 'off', 150 | 'import/exports-last': 'off', 151 | 'import/first': 'off', 152 | 'import/group-exports': 'off', 153 | 'import/no-named-export': 'off', 154 | 'import/no-anonymous-default-export': 'off', 155 | 'import/no-mutable-exports': 'off', 156 | 'import/no-named-default': 'off', 157 | 'import/no-namespace': 'off', 158 | 'import/no-duplicates': 'off', 159 | 'import/prefer-default-export': 'off', 160 | 'jest/consistent-test-it': 'off', 161 | 'jest/max-expects': 'off', 162 | 'jest/max-nested-describe': 'off', 163 | 'jest/no-alias-methods': 'off', 164 | 'jest/no-confusing-set-timeout': 'off', 165 | 'jest/no-deprecated-functions': 'off', 166 | 'jest/no-done-callback': 'off', 167 | 'jest/no-duplicate-hooks': 'off', 168 | 'jest/no-hooks': 'off', 169 | 'jest/no-identical-title': 'off', 170 | 'jest/no-interpolation-in-snapshots': 'off', 171 | 'jest/no-jasmine-globals': 'off', 172 | 'jest/no-large-snapshots': 'off', 173 | 'jest/no-mocks-import': 'off', 174 | 'jest/no-restricted-jest-methods': 'off', 175 | 'jest/no-restricted-matchers': 'off', 176 | 'jest/no-test-prefixes': 'off', 177 | 'jest/no-test-return-statement': 'off', 178 | 'jest/no-untyped-mock-factory': 'off', 179 | 'jest/padding-around-test-blocks': 'off', 180 | 'jest/prefer-each': 'off', 181 | 'jest/prefer-called-with': 'off', 182 | 'jest/prefer-comparison-matcher': 'off', 183 | 'jest/prefer-equality-matcher': 'off', 184 | 'jest/prefer-expect-resolves': 'off', 185 | 'jest/prefer-hooks-in-order': 'off', 186 | 'jest/prefer-hooks-on-top': 'off', 187 | 'jest/prefer-jest-mocked': 'off', 188 | 'jest/prefer-lowercase-title': 'off', 189 | 'jest/prefer-mock-promise-shorthand': 'off', 190 | 'jest/prefer-spy-on': 'off', 191 | 'jest/prefer-strict-equal': 'off', 192 | 'jest/prefer-to-be': 'off', 193 | 'jest/prefer-to-contain': 'off', 194 | 'jest/prefer-to-have-been-called': 'off', 195 | 'jest/prefer-to-have-been-called-times': 'off', 196 | 'jest/prefer-to-have-length': 'off', 197 | 'jest/prefer-todo': 'off', 198 | 'jest/require-hook': 'off', 199 | 'jest/require-top-level-describe': 'off', 200 | 'node/no-exports-assign': 'off', 201 | 'promise/avoid-new': 'off', 202 | 'promise/no-return-wrap': 'off', 203 | 'promise/no-nesting': 'off', 204 | 'promise/param-names': 'off', 205 | 'promise/prefer-catch': 'off', 206 | 'promise/prefer-await-to-callbacks': 'off', 207 | 'promise/prefer-await-to-then': 'off', 208 | 'react/jsx-pascal-case': 'off', 209 | 'react/jsx-fragments': 'off', 210 | 'react/jsx-boolean-value': 'off', 211 | 'react/jsx-curly-brace-presence': 'off', 212 | 'react/jsx-handler-names': 'off', 213 | 'react/jsx-props-no-spreading': 'off', 214 | 'react/no-redundant-should-component-update': 'off', 215 | 'react/no-set-state': 'off', 216 | 'react/prefer-es6-class': 'off', 217 | 'react/self-closing-comp': 'off', 218 | 'react/state-in-constructor': 'off', 219 | '@typescript-eslint/adjacent-overload-signatures': 'off', 220 | '@typescript-eslint/array-type': 'off', 221 | '@typescript-eslint/ban-tslint-comment': 'off', 222 | '@typescript-eslint/consistent-generic-constructors': 'off', 223 | '@typescript-eslint/consistent-indexed-object-style': 'off', 224 | '@typescript-eslint/consistent-type-definitions': 'off', 225 | '@typescript-eslint/consistent-type-imports': 'off', 226 | '@typescript-eslint/no-inferrable-types': 'off', 227 | '@typescript-eslint/no-empty-interface': 'off', 228 | '@typescript-eslint/prefer-for-of': 'off', 229 | '@typescript-eslint/prefer-function-type': 'off', 230 | '@typescript-eslint/prefer-namespace-keyword': 'off', 231 | 'unicorn/catch-error-name': 'off', 232 | 'unicorn/consistent-date-clone': 'off', 233 | 'unicorn/consistent-existence-index-check': 'off', 234 | 'unicorn/empty-brace-spaces': 'off', 235 | 'unicorn/error-message': 'off', 236 | 'unicorn/filename-case': 'off', 237 | 'unicorn/no-useless-collection-argument': 'off', 238 | 'unicorn/no-array-method-this-argument': 'off', 239 | 'unicorn/no-await-expression-member': 'off', 240 | 'unicorn/no-console-spaces': 'off', 241 | 'unicorn/no-nested-ternary': 'off', 242 | 'unicorn/no-null': 'off', 243 | 'unicorn/no-unreadable-array-destructuring': 'off', 244 | 'unicorn/no-zero-fractions': 'off', 245 | 'unicorn/number-literal-case': 'off', 246 | 'unicorn/numeric-separators-style': 'off', 247 | 'unicorn/prefer-classlist-toggle': 'off', 248 | 'unicorn/prefer-class-fields': 'off', 249 | 'unicorn/prefer-bigint-literals': 'off', 250 | 'unicorn/prefer-default-parameters': 'off', 251 | 'unicorn/prefer-response-static-json': 'off', 252 | 'unicorn/prefer-global-this': 'off', 253 | 'unicorn/prefer-keyboard-event-key': 'off', 254 | 'unicorn/prefer-object-from-entries': 'off', 255 | 'unicorn/prefer-array-index-of': 'off', 256 | 'unicorn/prefer-spread': 'off', 257 | 'unicorn/prefer-dom-node-text-content': 'off', 258 | 'unicorn/prefer-includes': 'off', 259 | 'unicorn/prefer-logical-operator-over-ternary': 'off', 260 | 'unicorn/prefer-modern-dom-apis': 'off', 261 | 'unicorn/prefer-negative-index': 'off', 262 | 'unicorn/prefer-optional-catch-binding': 'off', 263 | 'unicorn/prefer-reflect-apply': 'off', 264 | 'unicorn/prefer-string-raw': 'off', 265 | 'unicorn/prefer-string-trim-start-end': 'off', 266 | 'unicorn/prefer-structured-clone': 'off', 267 | 'unicorn/require-array-join-separator': 'off', 268 | 'unicorn/switch-case-braces': 'off', 269 | 'unicorn/text-encoding-identifier-case': 'off', 270 | 'unicorn/throw-new-error': 'off', 271 | 'vitest/no-import-node-test': 'off', 272 | 'vitest/prefer-to-be-falsy': 'off', 273 | 'vitest/prefer-to-be-object': 'off', 274 | 'vitest/prefer-to-be-truthy': 'off', 275 | 'vue/define-emits-declaration': 'off', 276 | 'vue/define-props-declaration': 'off', 277 | 'vue/define-props-destructuring': 'off', 278 | 'vue/require-typed-ref': 'off', 279 | '@typescript-eslint/default-param-last': 'off', 280 | '@typescript-eslint/init-declarations': 'off', 281 | '@typescript-eslint/max-params': 'off', 282 | '@typescript-eslint/no-magic-numbers': 'off', 283 | 'vitest/consistent-test-it': 'off', 284 | 'vitest/max-expects': 'off', 285 | 'vitest/max-nested-describe': 'off', 286 | 'vitest/no-alias-methods': 'off', 287 | 'vitest/no-duplicate-hooks': 'off', 288 | 'vitest/no-hooks': 'off', 289 | 'vitest/no-identical-title': 'off', 290 | 'vitest/no-interpolation-in-snapshots': 'off', 291 | 'vitest/no-large-snapshots': 'off', 292 | 'vitest/no-mocks-import': 'off', 293 | 'vitest/no-restricted-jest-methods': 'off', 294 | 'vitest/no-restricted-matchers': 'off', 295 | 'vitest/no-test-prefixes': 'off', 296 | 'vitest/no-test-return-statement': 'off', 297 | 'vitest/prefer-each': 'off', 298 | 'vitest/prefer-called-with': 'off', 299 | 'vitest/prefer-comparison-matcher': 'off', 300 | 'vitest/prefer-equality-matcher': 'off', 301 | 'vitest/prefer-expect-resolves': 'off', 302 | 'vitest/prefer-hooks-in-order': 'off', 303 | 'vitest/prefer-hooks-on-top': 'off', 304 | 'vitest/prefer-lowercase-title': 'off', 305 | 'vitest/prefer-mock-promise-shorthand': 'off', 306 | 'vitest/prefer-spy-on': 'off', 307 | 'vitest/prefer-strict-equal': 'off', 308 | 'vitest/prefer-to-be': 'off', 309 | 'vitest/prefer-to-contain': 'off', 310 | 'vitest/prefer-to-have-length': 'off', 311 | 'vitest/prefer-todo': 'off', 312 | 'vitest/require-hook': 'off', 313 | 'vitest/require-top-level-describe': 'off', 314 | }; 315 | 316 | const suspiciousRules: Record = { 317 | 'block-scoped-var': 'off', 318 | 'no-extra-bind': 'off', 319 | 'no-unneeded-ternary': 'off', 320 | 'no-extend-native': 'off', 321 | 'no-new': 'off', 322 | 'no-unexpected-multiline': 'off', 323 | 'no-useless-concat': 'off', 324 | 'no-useless-constructor': 'off', 325 | 'preserve-caught-error': 'off', 326 | 'import/no-unassigned-import': 'off', 327 | 'import/no-empty-named-blocks': 'off', 328 | 'import/no-absolute-path': 'off', 329 | 'import/no-named-as-default': 'off', 330 | 'import/no-named-as-default-member': 'off', 331 | 'import/no-self-import': 'off', 332 | 'jest/no-commented-out-tests': 'off', 333 | 'promise/always-return': 'off', 334 | 'promise/no-promise-in-callback': 'off', 335 | 'promise/no-multiple-resolved': 'off', 336 | 'react/iframe-missing-sandbox': 'off', 337 | 'react/jsx-no-comment-textnodes': 'off', 338 | 'react/jsx-no-script-url': 'off', 339 | 'react/no-namespace': 'off', 340 | 'react/react-in-jsx-scope': 'off', 341 | 'react/style-prop-object': 'off', 342 | '@typescript-eslint/no-confusing-non-null-assertion': 'off', 343 | '@typescript-eslint/no-extraneous-class': 'off', 344 | '@typescript-eslint/no-unnecessary-type-constraint': 'off', 345 | 'unicorn/consistent-function-scoping': 'off', 346 | 'unicorn/no-array-sort': 'off', 347 | 'unicorn/no-array-reverse': 'off', 348 | 'unicorn/no-instanceof-builtins': 'off', 349 | 'unicorn/no-accessor-recursion': 'off', 350 | 'unicorn/prefer-add-event-listener': 'off', 351 | 'unicorn/require-module-specifiers': 'off', 352 | 'unicorn/require-post-message-target-origin': 'off', 353 | 'vue/no-required-prop-with-default': 'off', 354 | 'vue/require-default-export': 'off', 355 | '@typescript-eslint/no-useless-constructor': 'off', 356 | 'vitest/no-commented-out-tests': 'off', 357 | }; 358 | 359 | const restrictionRules: Record = { 360 | 'class-methods-use-this': 'off', 361 | 'default-case': 'off', 362 | 'no-alert': 'off', 363 | 'no-bitwise': 'off', 364 | 'no-param-reassign': 'off', 365 | 'no-restricted-imports': 'off', 366 | 'no-console': 'off', 367 | 'no-div-regex': 'off', 368 | 'no-empty-function': 'off', 369 | 'no-empty': 'off', 370 | 'no-eq-null': 'off', 371 | 'no-iterator': 'off', 372 | 'no-plusplus': 'off', 373 | 'no-proto': 'off', 374 | 'no-regex-spaces': 'off', 375 | 'no-restricted-globals': 'off', 376 | 'no-sequences': 'off', 377 | 'no-undefined': 'off', 378 | 'no-var': 'off', 379 | 'no-void': 'off', 380 | 'unicode-bom': 'off', 381 | 'import/extensions': 'off', 382 | 'import/no-amd': 'off', 383 | 'import/no-commonjs': 'off', 384 | 'import/no-cycle': 'off', 385 | 'import/no-default-export': 'off', 386 | 'import/no-dynamic-require': 'off', 387 | 'import/no-webpack-loader-syntax': 'off', 388 | 'import/unambiguous': 'off', 389 | 'jsdoc/check-access': 'off', 390 | 'jsdoc/empty-tags': 'off', 391 | 'jsx-a11y/anchor-ambiguous-text': 'off', 392 | 'node/no-process-env': 'off', 393 | 'node/no-new-require': 'off', 394 | 'promise/catch-or-return': 'off', 395 | 'promise/spec-only': 'off', 396 | 'react/button-has-type': 'off', 397 | 'react/forbid-dom-props': 'off', 398 | 'react/forbid-elements': 'off', 399 | 'react/jsx-filename-extension': 'off', 400 | 'react/no-danger': 'off', 401 | 'react/no-unknown-property': 'off', 402 | 'react/only-export-components': 'off', 403 | '@typescript-eslint/explicit-module-boundary-types': 'off', 404 | '@typescript-eslint/explicit-function-return-type': 'off', 405 | '@typescript-eslint/no-dynamic-delete': 'off', 406 | '@typescript-eslint/no-empty-object-type': 'off', 407 | '@typescript-eslint/no-explicit-any': 'off', 408 | '@typescript-eslint/no-import-type-side-effects': 'off', 409 | '@typescript-eslint/no-namespace': 'off', 410 | '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'off', 411 | '@typescript-eslint/no-non-null-assertion': 'off', 412 | '@typescript-eslint/no-require-imports': 'off', 413 | '@typescript-eslint/no-restricted-types': 'off', 414 | '@typescript-eslint/no-var-requires': 'off', 415 | '@typescript-eslint/prefer-literal-enum-member': 'off', 416 | 'unicorn/no-useless-error-capture-stack-trace': 'off', 417 | 'unicorn/no-abusive-eslint-disable': 'off', 418 | 'unicorn/no-anonymous-default-export': 'off', 419 | 'unicorn/no-array-for-each': 'off', 420 | 'unicorn/no-array-reduce': 'off', 421 | 'unicorn/no-document-cookie': 'off', 422 | 'unicorn/no-length-as-slice-end': 'off', 423 | 'unicorn/no-magic-array-flat-depth': 'off', 424 | 'unicorn/no-process-exit': 'off', 425 | 'unicorn/prefer-modern-math-apis': 'off', 426 | 'unicorn/prefer-node-protocol': 'off', 427 | 'unicorn/prefer-number-properties': 'off', 428 | 'vue/max-props': 'off', 429 | 'vue/no-import-compiler-macros': 'off', 430 | 'vue/no-multiple-slot-args': 'off', 431 | '@typescript-eslint/class-methods-use-this': 'off', 432 | '@typescript-eslint/no-restricted-imports': 'off', 433 | '@typescript-eslint/no-empty-function': 'off', 434 | }; 435 | 436 | const correctnessRules: Record = { 437 | 'constructor-super': 'off', 438 | 'for-direction': 'off', 439 | 'no-unassigned-vars': 'off', 440 | 'no-async-promise-executor': 'off', 441 | 'no-caller': 'off', 442 | 'no-class-assign': 'off', 443 | 'no-useless-backreference': 'off', 444 | 'no-compare-neg-zero': 'off', 445 | 'no-cond-assign': 'off', 446 | 'no-const-assign': 'off', 447 | 'no-constant-binary-expression': 'off', 448 | 'no-constant-condition': 'off', 449 | 'no-control-regex': 'off', 450 | 'no-debugger': 'off', 451 | 'no-delete-var': 'off', 452 | 'no-dupe-class-members': 'off', 453 | 'no-dupe-else-if': 'off', 454 | 'no-dupe-keys': 'off', 455 | 'no-duplicate-case': 'off', 456 | 'no-empty-character-class': 'off', 457 | 'no-empty-pattern': 'off', 458 | 'no-empty-static-block': 'off', 459 | 'no-eval': 'off', 460 | 'no-ex-assign': 'off', 461 | 'no-extra-boolean-cast': 'off', 462 | 'no-func-assign': 'off', 463 | 'no-global-assign': 'off', 464 | 'no-import-assign': 'off', 465 | 'no-invalid-regexp': 'off', 466 | 'no-irregular-whitespace': 'off', 467 | 'no-loss-of-precision': 'off', 468 | 'no-new-native-nonconstructor': 'off', 469 | 'no-nonoctal-decimal-escape': 'off', 470 | 'no-obj-calls': 'off', 471 | 'no-self-assign': 'off', 472 | 'no-setter-return': 'off', 473 | 'no-shadow-restricted-names': 'off', 474 | 'no-sparse-arrays': 'off', 475 | 'no-this-before-super': 'off', 476 | 'no-unsafe-finally': 'off', 477 | 'no-unsafe-negation': 'off', 478 | 'no-unsafe-optional-chaining': 'off', 479 | 'no-unused-expressions': 'off', 480 | 'no-unused-labels': 'off', 481 | 'no-unused-private-class-members': 'off', 482 | 'no-unused-vars': 'off', 483 | 'no-useless-catch': 'off', 484 | 'no-useless-escape': 'off', 485 | 'no-useless-rename': 'off', 486 | 'no-with': 'off', 487 | 'require-yield': 'off', 488 | 'use-isnan': 'off', 489 | 'valid-typeof': 'off', 490 | 'import/default': 'off', 491 | 'import/namespace': 'off', 492 | 'jest/expect-expect': 'off', 493 | 'jest/no-conditional-expect': 'off', 494 | 'jest/no-disabled-tests': 'off', 495 | 'jest/no-export': 'off', 496 | 'jest/no-focused-tests': 'off', 497 | 'jest/no-standalone-expect': 'off', 498 | 'jest/require-to-throw-message': 'off', 499 | 'jest/valid-describe-callback': 'off', 500 | 'jest/valid-expect': 'off', 501 | 'jest/valid-title': 'off', 502 | 'jsdoc/check-property-names': 'off', 503 | 'jsdoc/check-tag-names': 'off', 504 | 'jsdoc/implements-on-classes': 'off', 505 | 'jsdoc/no-defaults': 'off', 506 | 'jsdoc/require-property': 'off', 507 | 'jsdoc/require-property-description': 'off', 508 | 'jsdoc/require-property-name': 'off', 509 | 'jsdoc/require-property-type': 'off', 510 | 'jsdoc/require-yields': 'off', 511 | 'jsx-a11y/alt-text': 'off', 512 | 'jsx-a11y/anchor-has-content': 'off', 513 | 'jsx-a11y/anchor-is-valid': 'off', 514 | 'jsx-a11y/aria-activedescendant-has-tabindex': 'off', 515 | 'jsx-a11y/aria-props': 'off', 516 | 'jsx-a11y/aria-role': 'off', 517 | 'jsx-a11y/aria-unsupported-elements': 'off', 518 | 'jsx-a11y/autocomplete-valid': 'off', 519 | 'jsx-a11y/click-events-have-key-events': 'off', 520 | 'jsx-a11y/heading-has-content': 'off', 521 | 'jsx-a11y/html-has-lang': 'off', 522 | 'jsx-a11y/iframe-has-title': 'off', 523 | 'jsx-a11y/img-redundant-alt': 'off', 524 | 'jsx-a11y/label-has-associated-control': 'off', 525 | 'jsx-a11y/lang': 'off', 526 | 'jsx-a11y/media-has-caption': 'off', 527 | 'jsx-a11y/mouse-events-have-key-events': 'off', 528 | 'jsx-a11y/no-noninteractive-tabindex': 'off', 529 | 'jsx-a11y/no-access-key': 'off', 530 | 'jsx-a11y/no-aria-hidden-on-focusable': 'off', 531 | 'jsx-a11y/no-autofocus': 'off', 532 | 'jsx-a11y/no-distracting-elements': 'off', 533 | 'jsx-a11y/no-redundant-roles': 'off', 534 | 'jsx-a11y/prefer-tag-over-role': 'off', 535 | 'jsx-a11y/role-has-required-aria-props': 'off', 536 | 'jsx-a11y/role-supports-aria-props': 'off', 537 | 'jsx-a11y/scope': 'off', 538 | 'jsx-a11y/tabindex-no-positive': 'off', 539 | '@next/next/google-font-display': 'off', 540 | '@next/next/google-font-preconnect': 'off', 541 | '@next/next/inline-script-id': 'off', 542 | '@next/next/next-script-for-ga': 'off', 543 | '@next/next/no-assign-module-variable': 'off', 544 | '@next/next/no-async-client-component': 'off', 545 | '@next/next/no-before-interactive-script-outside-document': 'off', 546 | '@next/next/no-css-tags': 'off', 547 | '@next/next/no-document-import-in-page': 'off', 548 | '@next/next/no-duplicate-head': 'off', 549 | '@next/next/no-head-element': 'off', 550 | '@next/next/no-head-import-in-document': 'off', 551 | '@next/next/no-img-element': 'off', 552 | '@next/next/no-page-custom-font': 'off', 553 | '@next/next/no-script-component-in-head': 'off', 554 | '@next/next/no-styled-jsx-in-document': 'off', 555 | '@next/next/no-sync-scripts': 'off', 556 | '@next/next/no-title-in-document-head': 'off', 557 | '@next/next/no-typos': 'off', 558 | '@next/next/no-unwanted-polyfillio': 'off', 559 | '@next/next/no-html-link-for-pages': 'off', 560 | 'promise/no-callback-in-promise': 'off', 561 | 'promise/no-new-statics': 'off', 562 | 'promise/valid-params': 'off', 563 | 'react-hooks/exhaustive-deps': 'off', 564 | 'react/forward-ref-uses-ref': 'off', 565 | 'react/jsx-key': 'off', 566 | 'react/jsx-no-duplicate-props': 'off', 567 | 'react/jsx-no-undef': 'off', 568 | 'react/jsx-props-no-spread-multi': 'off', 569 | 'react/no-children-prop': 'off', 570 | 'react/no-danger-with-children': 'off', 571 | 'react/no-direct-mutation-state': 'off', 572 | 'react/no-find-dom-node': 'off', 573 | 'react/no-is-mounted': 'off', 574 | 'react/no-render-return-value': 'off', 575 | 'react/no-string-refs': 'off', 576 | 'react/void-dom-elements-no-children': 'off', 577 | '@typescript-eslint/no-duplicate-enum-values': 'off', 578 | '@typescript-eslint/no-extra-non-null-assertion': 'off', 579 | '@typescript-eslint/no-misused-new': 'off', 580 | '@typescript-eslint/no-non-null-asserted-optional-chain': 'off', 581 | '@typescript-eslint/no-this-alias': 'off', 582 | '@typescript-eslint/no-unnecessary-parameter-property-assignment': 'off', 583 | '@typescript-eslint/no-unsafe-declaration-merging': 'off', 584 | '@typescript-eslint/no-useless-empty-export': 'off', 585 | '@typescript-eslint/no-wrapper-object-types': 'off', 586 | '@typescript-eslint/prefer-as-const': 'off', 587 | '@typescript-eslint/triple-slash-reference': 'off', 588 | 'unicorn/no-invalid-fetch-options': 'off', 589 | 'unicorn/no-await-in-promise-methods': 'off', 590 | 'unicorn/no-empty-file': 'off', 591 | 'unicorn/no-invalid-remove-event-listener': 'off', 592 | 'unicorn/no-new-array': 'off', 593 | 'unicorn/no-single-promise-in-promise-methods': 'off', 594 | 'unicorn/no-thenable': 'off', 595 | 'unicorn/no-unnecessary-await': 'off', 596 | 'unicorn/no-useless-fallback-in-spread': 'off', 597 | 'unicorn/no-useless-length-check': 'off', 598 | 'unicorn/no-useless-spread': 'off', 599 | 'unicorn/prefer-set-size': 'off', 600 | 'unicorn/prefer-string-starts-ends-with': 'off', 601 | 'vitest/no-conditional-tests': 'off', 602 | 'vitest/require-local-test-context-for-concurrent-snapshots': 'off', 603 | 'vue/no-export-in-script-setup': 'off', 604 | 'vue/prefer-import-from-vue': 'off', 605 | 'vue/valid-define-emits': 'off', 606 | 'vue/valid-define-props': 'off', 607 | '@typescript-eslint/no-dupe-class-members': 'off', 608 | '@typescript-eslint/no-loss-of-precision': 'off', 609 | '@typescript-eslint/no-unused-expressions': 'off', 610 | '@typescript-eslint/no-unused-vars': 'off', 611 | 'vitest/expect-expect': 'off', 612 | 'vitest/no-conditional-expect': 'off', 613 | 'vitest/no-disabled-tests': 'off', 614 | 'vitest/no-focused-tests': 'off', 615 | 'vitest/no-standalone-expect': 'off', 616 | 'vitest/require-to-throw-message': 'off', 617 | 'vitest/valid-describe-callback': 'off', 618 | 'vitest/valid-expect': 'off', 619 | }; 620 | 621 | const nurseryRules: Record = { 622 | 'getter-return': 'off', 623 | 'no-misleading-character-class': 'off', 624 | 'no-undef': 'off', 625 | 'no-unreachable': 'off', 626 | 'import/export': 'off', 627 | 'import/named': 'off', 628 | 'promise/no-return-in-finally': 'off', 629 | 'react/require-render-return': 'off', 630 | }; 631 | 632 | const perfRules: Record = { 633 | 'no-await-in-loop': 'off', 634 | 'no-useless-call': 'off', 635 | 'react/no-array-index-key': 'off', 636 | 'react-perf/jsx-no-jsx-as-prop': 'off', 637 | 'react-perf/jsx-no-new-array-as-prop': 'off', 638 | 'react-perf/jsx-no-new-function-as-prop': 'off', 639 | 'react-perf/jsx-no-new-object-as-prop': 'off', 640 | 'unicorn/prefer-array-find': 'off', 641 | 'unicorn/prefer-array-flat-map': 'off', 642 | 'unicorn/prefer-set-has': 'off', 643 | }; 644 | 645 | const correctnessTypeAwareRules: Record = { 646 | '@typescript-eslint/await-thenable': 'off', 647 | '@typescript-eslint/no-floating-promises': 'off', 648 | '@typescript-eslint/no-array-delete': 'off', 649 | '@typescript-eslint/no-base-to-string': 'off', 650 | '@typescript-eslint/no-duplicate-type-constituents': 'off', 651 | '@typescript-eslint/no-for-in-array': 'off', 652 | '@typescript-eslint/no-implied-eval': 'off', 653 | '@typescript-eslint/no-meaningless-void-operator': 'off', 654 | '@typescript-eslint/no-misused-spread': 'off', 655 | '@typescript-eslint/no-redundant-type-constituents': 'off', 656 | '@typescript-eslint/no-unsafe-unary-minus': 'off', 657 | '@typescript-eslint/require-array-sort-compare': 'off', 658 | '@typescript-eslint/restrict-template-expressions': 'off', 659 | '@typescript-eslint/unbound-method': 'off', 660 | }; 661 | 662 | const pedanticTypeAwareRules: Record = { 663 | '@typescript-eslint/no-misused-promises': 'off', 664 | '@typescript-eslint/no-confusing-void-expression': 'off', 665 | '@typescript-eslint/no-deprecated': 'off', 666 | '@typescript-eslint/no-mixed-enums': 'off', 667 | '@typescript-eslint/no-unsafe-argument': 'off', 668 | '@typescript-eslint/no-unsafe-assignment': 'off', 669 | '@typescript-eslint/no-unsafe-call': 'off', 670 | '@typescript-eslint/no-unsafe-member-access': 'off', 671 | '@typescript-eslint/no-unsafe-return': 'off', 672 | '@typescript-eslint/only-throw-error': 'off', 673 | '@typescript-eslint/prefer-includes': 'off', 674 | '@typescript-eslint/prefer-nullish-coalescing': 'off', 675 | '@typescript-eslint/prefer-promise-reject-errors': 'off', 676 | '@typescript-eslint/related-getter-setter-pairs': 'off', 677 | '@typescript-eslint/require-await': 'off', 678 | '@typescript-eslint/restrict-plus-operands': 'off', 679 | '@typescript-eslint/return-await': 'off', 680 | '@typescript-eslint/strict-boolean-expressions': 'off', 681 | '@typescript-eslint/switch-exhaustiveness-check': 'off', 682 | }; 683 | 684 | const suspiciousTypeAwareRules: Record = { 685 | '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off', 686 | '@typescript-eslint/no-unnecessary-template-expression': 'off', 687 | '@typescript-eslint/no-unnecessary-type-arguments': 'off', 688 | '@typescript-eslint/no-unnecessary-type-assertion': 'off', 689 | '@typescript-eslint/no-unsafe-enum-comparison': 'off', 690 | '@typescript-eslint/no-unsafe-type-assertion': 'off', 691 | }; 692 | 693 | const restrictionTypeAwareRules: Record = { 694 | '@typescript-eslint/non-nullable-type-assertion-style': 'off', 695 | '@typescript-eslint/promise-function-async': 'off', 696 | '@typescript-eslint/use-unknown-in-catch-callback-variable': 'off', 697 | }; 698 | 699 | const styleTypeAwareRules: Record = { 700 | '@typescript-eslint/prefer-reduce-type-parameter': 'off', 701 | '@typescript-eslint/prefer-return-this-type': 'off', 702 | }; 703 | 704 | export { 705 | pedanticRules, 706 | styleRules, 707 | suspiciousRules, 708 | restrictionRules, 709 | correctnessRules, 710 | nurseryRules, 711 | perfRules, 712 | correctnessTypeAwareRules, 713 | pedanticTypeAwareRules, 714 | suspiciousTypeAwareRules, 715 | restrictionTypeAwareRules, 716 | styleTypeAwareRules, 717 | }; 718 | --------------------------------------------------------------------------------