├── .github ├── renovate.json └── workflows │ ├── build.yml │ ├── bump_oxlint.yml │ ├── ci_security.yml │ ├── format.yml │ ├── generate.yml │ ├── lint.yml │ ├── release.yml │ ├── test.yml │ └── type-check.yml ├── .gitignore ├── .husky └── pre-commit ├── .mise.toml ├── .node-version ├── .oxlintrc.json ├── .prettierignore ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── eslint.config.ts ├── package.json ├── pnpm-lock.yaml ├── prettier.config.js ├── scripts ├── __snapshots__ │ └── rules-generator.test.ts.snap ├── config-generator.ts ├── constants.ts ├── generate.ts ├── rules-generator.test.ts ├── rules-generator.ts └── traverse-rules.ts ├── src ├── __snapshots__ │ └── configs.spec.ts.snap ├── build-from-oxlint-config.spec.ts ├── build-from-oxlint-config │ ├── __snapshots__ │ │ └── categories.spec.ts.snap │ ├── categories.spec.ts │ ├── categories.ts │ ├── extends.spec.ts │ ├── extends.ts │ ├── ignore-patterns.spec.ts │ ├── ignore-patterns.ts │ ├── index.ts │ ├── overrides.spec.ts │ ├── overrides.ts │ ├── plugins.ts │ ├── rules.spec.ts │ ├── rules.ts │ ├── types.ts │ └── utilities.ts ├── config-helper.spec.ts ├── config-helper.ts ├── configs.spec.ts ├── configs.ts ├── constants.ts ├── generated │ ├── configs-by-category.ts │ ├── configs-by-scope.ts │ ├── rules-by-category.ts │ └── rules-by-scope.ts └── index.ts ├── test └── helpers.ts ├── tsconfig.json └── vite.config.ts /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "github>Boshen/renovate", 5 | "helpers:pinGitHubActionDigestsToSemver" 6 | ], 7 | "packageRules": [ 8 | { 9 | "groupName": "npm packages", 10 | "matchManagers": ["npm"], 11 | "ignoreDeps": ["oxlint"] 12 | }, 13 | { 14 | "groupName": "oxlint", 15 | "matchManagers": ["npm"], 16 | "matchPackageNames": ["oxlint"], 17 | "schedule": ["at any time"] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 18 | with: 19 | persist-credentials: false 20 | 21 | - uses: oxc-project/setup-node@f42e3bda950c7454575e78ee4eaac880a077700c # v1.0.0 22 | 23 | - name: Build 24 | run: pnpm run build 25 | -------------------------------------------------------------------------------- /.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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 22 | with: 23 | persist-credentials: false # should be fine, we give another token for PR creation 24 | 25 | - uses: oxc-project/setup-node@f42e3bda950c7454575e78ee4eaac880a077700c # v1.0.0 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@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 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 | -------------------------------------------------------------------------------- /.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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | with: 27 | persist-credentials: false 28 | 29 | - uses: taiki-e/install-action@92f69c195229fe62d58b4d697ab4bc75def98e76 # v2.52.7 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@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19 40 | with: 41 | sarif_file: results.sarif 42 | category: zizmor 43 | -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | name: Format (prettier) 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize] 6 | push: 7 | branches: 8 | - main 9 | 10 | permissions: {} 11 | 12 | jobs: 13 | format: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 18 | with: 19 | persist-credentials: false 20 | 21 | - name: Run Format (prettier) 22 | run: npx prettier . --check 23 | -------------------------------------------------------------------------------- /.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 | - '.github/workflows/generate.yml' 11 | push: 12 | branches: 13 | - main 14 | paths: 15 | - 'pnpm-lock.yaml' 16 | - 'scripts/**' 17 | - '.github/workflows/generate.yml' 18 | 19 | permissions: {} 20 | 21 | jobs: 22 | generate: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - name: Checkout repository 26 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 27 | with: 28 | persist-credentials: false 29 | 30 | - uses: oxc-project/setup-node@f42e3bda950c7454575e78ee4eaac880a077700c # v1.0.0 31 | 32 | - name: Remove current generated code 33 | run: rm -r ./src/generated/ 34 | 35 | - name: Generate from source code 36 | run: pnpm run generate 37 | 38 | - name: Format generated code 39 | run: pnpm run format 40 | 41 | - name: Check for git diff 42 | run: git diff --exit-code 43 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize] 6 | push: 7 | branches: 8 | - main 9 | 10 | permissions: {} 11 | 12 | jobs: 13 | lint: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 18 | with: 19 | persist-credentials: false 20 | 21 | - uses: oxc-project/setup-node@f42e3bda950c7454575e78ee4eaac880a077700c # v1.0.0 22 | 23 | - name: Run oxlint 24 | run: npx oxlint --tsconfig=tsconfig.json 25 | 26 | - name: Run eslint 27 | run: npx eslint 28 | -------------------------------------------------------------------------------- /.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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 18 | with: 19 | fetch-depth: 0 20 | persist-credentials: true 21 | 22 | - uses: oxc-project/setup-node@f42e3bda950c7454575e78ee4eaac880a077700c # v1.0.0 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 | - name: Publish to NPM 45 | run: npm publish --tag latest --provenance --access public 46 | env: 47 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 48 | -------------------------------------------------------------------------------- /.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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 18 | with: 19 | persist-credentials: false 20 | 21 | - uses: oxc-project/setup-node@f42e3bda950c7454575e78ee4eaac880a077700c # v1.0.0 22 | 23 | - name: Run tests 24 | run: pnpm run test --coverage 25 | -------------------------------------------------------------------------------- /.github/workflows/type-check.yml: -------------------------------------------------------------------------------- 1 | name: Type Check 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize] 6 | push: 7 | branches: 8 | - main 9 | 10 | permissions: {} 11 | 12 | jobs: 13 | type-check: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 18 | with: 19 | persist-credentials: false 20 | 21 | - uses: oxc-project/setup-node@f42e3bda950c7454575e78ee4eaac880a077700c # v1.0.0 22 | 23 | - name: Type Check 24 | run: npx tsc --noEmit 25 | -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx lint-staged 2 | -------------------------------------------------------------------------------- /.mise.toml: -------------------------------------------------------------------------------- 1 | [tools] 2 | pnpm = "10.12.1" 3 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 20.14.0 2 | -------------------------------------------------------------------------------- /.oxlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["unicorn", "typescript", "oxc"], 3 | "categories": { 4 | "correctness": "error", 5 | "suspicious": "error" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | pnpm-lock.yaml 2 | dist/ 3 | ~ 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "esbenp.prettier-vscode", 5 | "oxc.oxc-vscode", 6 | "vitest.explorer" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.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": "esbenp.prettier-vscode", 11 | "eslint.validate": ["javascript", "typescript"] 12 | } 13 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 eslintConfigPrettier from 'eslint-config-prettier'; 5 | import tseslint from 'typescript-eslint'; 6 | 7 | export default [ 8 | { 9 | ignores: ['dist/'], 10 | }, 11 | eslint.configs.recommended, 12 | unicorn.configs['flat/recommended'], 13 | ...tseslint.configs.recommended, 14 | eslintConfigPrettier, 15 | ...oxlint.buildFromOxlintConfigFile('.oxlintrc.json'), 16 | ]; 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-oxlint", 3 | "version": "1.0.0", 4 | "description": "Turn off all rules already supported by oxlint", 5 | "type": "module", 6 | "types": "./dist/index.d.ts", 7 | "packageManager": "pnpm@10.12.1", 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 | ], 33 | "author": "Dunqing ", 34 | "homepage": "https://github.com/oxc-project/eslint-plugin-oxlint", 35 | "repository": { 36 | "type": "git", 37 | "url": "git@github.com:oxc-project/eslint-plugin-oxlint.git" 38 | }, 39 | "license": "MIT", 40 | "scripts": { 41 | "prepare": "husky", 42 | "generate": "node --import @oxc-node/core/register ./scripts/generate.ts", 43 | "build": "vite build", 44 | "lint": "npx oxlint --tsconfig=tsconfig.json && npx eslint", 45 | "format": "npx prettier --write .", 46 | "type-check": "tsc --noEmit", 47 | "test": "vitest --reporter=verbose" 48 | }, 49 | "keywords": [ 50 | "oxc", 51 | "oxlint", 52 | "eslint", 53 | "rules" 54 | ], 55 | "devDependencies": { 56 | "@eslint/js": "^9.13.0", 57 | "@oxc-node/core": "^0.0.27", 58 | "@types/eslint-config-prettier": "^6.11.3", 59 | "@types/node": "^22.7.7", 60 | "@types/shelljs": "^0.8.15", 61 | "@vitest/coverage-v8": "^3.0.0", 62 | "dedent": "^1.5.3", 63 | "eslint": "^9.13.0", 64 | "eslint-config-prettier": "^10.0.0", 65 | "eslint-plugin-unicorn": "^59.0.0", 66 | "husky": "^9.1.6", 67 | "jiti": "^2.4.2", 68 | "lint-staged": "^16.0.0", 69 | "memfs": "^4.14.0", 70 | "oxlint": "^1.0.0", 71 | "prettier": "^3.3.3", 72 | "scule": "^1.3.0", 73 | "shelljs": "^0.10.0", 74 | "typescript": "^5.6.3", 75 | "typescript-eslint": "^8.10.0", 76 | "vite": "^6.0.0", 77 | "vite-plugin-dts": "^4.2.4", 78 | "vitest": "^3.0.0" 79 | }, 80 | "lint-staged": { 81 | "*.{js,cjs,ts}": "eslint", 82 | "*": "prettier --ignore-unknown --write" 83 | }, 84 | "volta": { 85 | "node": "20.14.0" 86 | }, 87 | "dependencies": { 88 | "jsonc-parser": "^3.3.1" 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import("prettier").Config} */ 2 | const config = { 3 | trailingComma: 'es5', 4 | tabWidth: 2, 5 | semi: true, 6 | singleQuote: true, 7 | }; 8 | 9 | export default config; 10 | -------------------------------------------------------------------------------- /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/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( 17 | rulesArray: Rule[] = [], 18 | rulesGrouping: RulesGrouping = RulesGrouping.SCOPE 19 | ) { 20 | this.rulesArray = rulesArray; 21 | this.rulesGrouping = rulesGrouping; 22 | } 23 | 24 | public setRulesGrouping(rulesGrouping: RulesGrouping) { 25 | this.rulesGrouping = rulesGrouping; 26 | } 27 | 28 | private groupItemsBy( 29 | rules: Rule[], 30 | rulesGrouping: RulesGrouping 31 | ): Set { 32 | const set = new Set(); 33 | for (const item of rules) { 34 | set.add(item[rulesGrouping]); 35 | } 36 | 37 | return set; 38 | } 39 | 40 | public generateRulesCode() { 41 | console.log(`Generating config, grouped by ${this.rulesGrouping}`); 42 | 43 | const rulesGrouping = this.rulesGrouping; 44 | const rulesArray = this.rulesArray; 45 | 46 | const rulesMap = this.groupItemsBy(rulesArray, rulesGrouping); 47 | const exportName = pascalCase(this.rulesGrouping); 48 | 49 | const exportGrouping: string[] = []; 50 | let code = 51 | '// These rules are automatically generated by scripts/generate-rules.ts\n\n'; 52 | 53 | code += `import * as rules from "./rules-by-${this.rulesGrouping}.js";\n\n`; 54 | 55 | for (const grouping of rulesMap) { 56 | exportGrouping.push(grouping); 57 | 58 | code += `const ${camelCase(grouping)}Config = {\n`; 59 | 60 | code += ` name: 'oxlint/${kebabCase(grouping)}',\n`; 61 | code += ` rules: rules.${camelCase(grouping)}Rules,`; 62 | code += '\n};\n\n'; 63 | } 64 | 65 | code += `const configBy${exportName} = {\n`; 66 | code += exportGrouping 67 | .map((grouping) => { 68 | return ` 'flat/${kebabCase(grouping)}': ${camelCase(grouping)}Config`; 69 | }) 70 | .join(',\n'); 71 | code += '\n}\n\n'; 72 | 73 | code += `export default configBy${exportName}`; 74 | 75 | return code; 76 | } 77 | 78 | public async generateRules(folderPath: string): Promise { 79 | const output = this.generateRulesCode(); 80 | 81 | return writeFile( 82 | path.resolve(folderPath, `configs-by-${this.rulesGrouping}.ts`), 83 | output 84 | ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /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 | // these are the rules that are not fully implemented in oxc 5 | export const ignoreCategories = new Set(['nursery']); 6 | -------------------------------------------------------------------------------- /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( 22 | successResultArray, 23 | RulesGrouping.SCOPE 24 | ); 25 | 26 | // Call the generateRules method 27 | expect(await generator.generateRulesCode()).toMatchSnapshot('byScope'); 28 | 29 | generator.setRulesGrouping(RulesGrouping.CATEGORY); 30 | // Call the generateRules method 31 | expect(await generator.generateRulesCode()).toMatchSnapshot('byCategory'); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /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 | 6 | export enum RulesGrouping { 7 | CATEGORY = 'category', 8 | SCOPE = 'scope', 9 | } 10 | 11 | export type ResultMap = Map; 12 | 13 | export class RulesGenerator { 14 | private rulesGrouping: RulesGrouping; 15 | private rulesArray: Rule[]; 16 | constructor( 17 | rulesArray: Rule[] = [], 18 | rulesGrouping: RulesGrouping = RulesGrouping.SCOPE 19 | ) { 20 | this.rulesArray = rulesArray; 21 | this.rulesGrouping = rulesGrouping; 22 | } 23 | 24 | public setRulesGrouping(rulesGrouping: RulesGrouping) { 25 | this.rulesGrouping = rulesGrouping; 26 | } 27 | 28 | private groupItemsBy( 29 | rules: Rule[], 30 | rulesGrouping: RulesGrouping 31 | ): Map { 32 | const map = new Map(); 33 | for (const item of rules) { 34 | const key = item[rulesGrouping]; 35 | const group = map.get(key) || []; 36 | group.push(item.value); 37 | map.set(key, group); 38 | } 39 | 40 | return map; 41 | } 42 | 43 | public generateRulesCode() { 44 | console.log(`Generating rules, grouped by ${this.rulesGrouping}`); 45 | 46 | const rulesGrouping = this.rulesGrouping; 47 | const rulesArray = this.rulesArray; 48 | 49 | const rulesMap = this.groupItemsBy(rulesArray, rulesGrouping); 50 | 51 | const exportGrouping: string[] = []; 52 | let code = 53 | '// These rules are automatically generated by scripts/generate-rules.ts\n\n'; 54 | 55 | for (const grouping of rulesMap.keys()) { 56 | exportGrouping.push(grouping); 57 | const rules = rulesMap.get(grouping); 58 | 59 | code += `const ${camelCase(grouping)}Rules: Record = {\n`; 60 | 61 | code += rules 62 | ?.map((rule) => { 63 | return ` '${rule.replaceAll('_', '-')}': "off"`; 64 | }) 65 | .join(',\n'); 66 | code += '\n};\n\n'; 67 | } 68 | 69 | code += 'export {\n'; 70 | code += exportGrouping 71 | .map((grouping) => { 72 | return ` ${grouping.replaceAll(/_(\w)/g, (_, c) => c.toUpperCase())}Rules`; 73 | }) 74 | .join(',\n'); 75 | code += '\n}'; 76 | 77 | return code; 78 | } 79 | 80 | public generateRules(folderPath: string): Promise { 81 | const output = this.generateRulesCode(); 82 | 83 | return writeFile( 84 | path.resolve(folderPath, `rules-by-${this.rulesGrouping}.ts`), 85 | output 86 | ); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /scripts/traverse-rules.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from 'node:child_process'; 2 | import { ignoreCategories, 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 ( 36 | rule.scope === 'react' && 37 | reactHookRulesInsideReactScope.includes(rule.value) 38 | ) { 39 | rule.scope = 'react_hooks'; 40 | } 41 | } 42 | 43 | /** 44 | * oxlint returns the value without a scope name 45 | */ 46 | function fixValueOfRule(rule: Rule): void { 47 | if (rule.scope === 'eslint') { 48 | return; 49 | } 50 | 51 | const scope = 52 | rule.scope in aliasPluginNames ? aliasPluginNames[rule.scope] : rule.scope; 53 | 54 | rule.value = `${scope}/${rule.value}`; 55 | } 56 | 57 | /** 58 | * some rules are reimplemented in another scope 59 | * remap them so we can disable all the reimplemented too 60 | */ 61 | function getAliasRules(rule: Rule): Rule | undefined { 62 | if ( 63 | rule.scope === 'eslint' && 64 | typescriptRulesExtendEslintRules.includes(rule.value) 65 | ) { 66 | return { 67 | value: `@typescript-eslint/${rule.value}`, 68 | scope: 'typescript', 69 | category: rule.category, 70 | }; 71 | } 72 | 73 | if (rule.scope === 'jest' && viteTestCompatibleRules.includes(rule.value)) { 74 | return { 75 | value: `vitest/${rule.value}`, 76 | scope: 'vitest', 77 | category: rule.category, 78 | }; 79 | } 80 | 81 | if ( 82 | rule.scope === 'eslint' && 83 | unicornRulesExtendEslintRules.includes(rule.value) 84 | ) { 85 | return { 86 | value: `unicorn/${rule.value}`, 87 | scope: 'unicorn', 88 | category: rule.category, 89 | }; 90 | } 91 | } 92 | 93 | export function traverseRules(): Rule[] { 94 | // get all rules and filter the ignored one 95 | const rules = readRulesFromCommand().filter( 96 | (rule) => 97 | !ignoreCategories.has(rule.category) && !ignoreScope.has(rule.scope) 98 | ); 99 | 100 | const aliasRules: Rule[] = []; 101 | 102 | for (const rule of rules) { 103 | const aliasRule = getAliasRules(rule); 104 | if (aliasRule) { 105 | aliasRules.push(aliasRule); 106 | } 107 | 108 | fixScopeOfRule(rule); 109 | fixValueOfRule(rule); 110 | } 111 | 112 | return [...rules, ...aliasRules]; 113 | } 114 | -------------------------------------------------------------------------------- /src/__snapshots__/configs.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`contains all the oxlint rules 1`] = ` 4 | { 5 | "@next/next/google-font-display": [ 6 | 0, 7 | ], 8 | "@next/next/google-font-preconnect": [ 9 | 0, 10 | ], 11 | "@next/next/inline-script-id": [ 12 | 0, 13 | ], 14 | "@next/next/next-script-for-ga": [ 15 | 0, 16 | ], 17 | "@next/next/no-assign-module-variable": [ 18 | 0, 19 | ], 20 | "@next/next/no-async-client-component": [ 21 | 0, 22 | ], 23 | "@next/next/no-before-interactive-script-outside-document": [ 24 | 0, 25 | ], 26 | "@next/next/no-css-tags": [ 27 | 0, 28 | ], 29 | "@next/next/no-document-import-in-page": [ 30 | 0, 31 | ], 32 | "@next/next/no-duplicate-head": [ 33 | 0, 34 | ], 35 | "@next/next/no-head-element": [ 36 | 0, 37 | ], 38 | "@next/next/no-head-import-in-document": [ 39 | 0, 40 | ], 41 | "@next/next/no-img-element": [ 42 | 0, 43 | ], 44 | "@next/next/no-page-custom-font": [ 45 | 0, 46 | ], 47 | "@next/next/no-script-component-in-head": [ 48 | 0, 49 | ], 50 | "@next/next/no-styled-jsx-in-document": [ 51 | 0, 52 | ], 53 | "@next/next/no-sync-scripts": [ 54 | 0, 55 | ], 56 | "@next/next/no-title-in-document-head": [ 57 | 0, 58 | ], 59 | "@next/next/no-typos": [ 60 | 0, 61 | ], 62 | "@next/next/no-unwanted-polyfillio": [ 63 | 0, 64 | ], 65 | "@typescript-eslint/adjacent-overload-signatures": [ 66 | 0, 67 | ], 68 | "@typescript-eslint/array-type": [ 69 | 0, 70 | ], 71 | "@typescript-eslint/ban-ts-comment": [ 72 | 0, 73 | ], 74 | "@typescript-eslint/ban-tslint-comment": [ 75 | 0, 76 | ], 77 | "@typescript-eslint/ban-types": [ 78 | 0, 79 | ], 80 | "@typescript-eslint/consistent-generic-constructors": [ 81 | 0, 82 | ], 83 | "@typescript-eslint/consistent-indexed-object-style": [ 84 | 0, 85 | ], 86 | "@typescript-eslint/consistent-type-definitions": [ 87 | 0, 88 | ], 89 | "@typescript-eslint/consistent-type-imports": [ 90 | 0, 91 | ], 92 | "@typescript-eslint/default-param-last": [ 93 | 0, 94 | ], 95 | "@typescript-eslint/explicit-function-return-type": [ 96 | 0, 97 | ], 98 | "@typescript-eslint/init-declarations": [ 99 | 0, 100 | ], 101 | "@typescript-eslint/max-params": [ 102 | 0, 103 | ], 104 | "@typescript-eslint/no-array-constructor": [ 105 | 0, 106 | ], 107 | "@typescript-eslint/no-confusing-non-null-assertion": [ 108 | 0, 109 | ], 110 | "@typescript-eslint/no-dupe-class-members": [ 111 | 0, 112 | ], 113 | "@typescript-eslint/no-duplicate-enum-values": [ 114 | 0, 115 | ], 116 | "@typescript-eslint/no-dynamic-delete": [ 117 | 0, 118 | ], 119 | "@typescript-eslint/no-empty-function": [ 120 | 0, 121 | ], 122 | "@typescript-eslint/no-empty-interface": [ 123 | 0, 124 | ], 125 | "@typescript-eslint/no-empty-object-type": [ 126 | 0, 127 | ], 128 | "@typescript-eslint/no-explicit-any": [ 129 | 0, 130 | ], 131 | "@typescript-eslint/no-extra-non-null-assertion": [ 132 | 0, 133 | ], 134 | "@typescript-eslint/no-extraneous-class": [ 135 | 0, 136 | ], 137 | "@typescript-eslint/no-import-type-side-effects": [ 138 | 0, 139 | ], 140 | "@typescript-eslint/no-inferrable-types": [ 141 | 0, 142 | ], 143 | "@typescript-eslint/no-loss-of-precision": [ 144 | 0, 145 | ], 146 | "@typescript-eslint/no-magic-numbers": [ 147 | 0, 148 | ], 149 | "@typescript-eslint/no-misused-new": [ 150 | 0, 151 | ], 152 | "@typescript-eslint/no-namespace": [ 153 | 0, 154 | ], 155 | "@typescript-eslint/no-non-null-asserted-nullish-coalescing": [ 156 | 0, 157 | ], 158 | "@typescript-eslint/no-non-null-asserted-optional-chain": [ 159 | 0, 160 | ], 161 | "@typescript-eslint/no-non-null-assertion": [ 162 | 0, 163 | ], 164 | "@typescript-eslint/no-redeclare": [ 165 | 0, 166 | ], 167 | "@typescript-eslint/no-require-imports": [ 168 | 0, 169 | ], 170 | "@typescript-eslint/no-restricted-imports": [ 171 | 0, 172 | ], 173 | "@typescript-eslint/no-this-alias": [ 174 | 0, 175 | ], 176 | "@typescript-eslint/no-unnecessary-parameter-property-assignment": [ 177 | 0, 178 | ], 179 | "@typescript-eslint/no-unnecessary-type-constraint": [ 180 | 0, 181 | ], 182 | "@typescript-eslint/no-unsafe-declaration-merging": [ 183 | 0, 184 | ], 185 | "@typescript-eslint/no-unsafe-function-type": [ 186 | 0, 187 | ], 188 | "@typescript-eslint/no-unused-expressions": [ 189 | 0, 190 | ], 191 | "@typescript-eslint/no-unused-vars": [ 192 | 0, 193 | ], 194 | "@typescript-eslint/no-useless-constructor": [ 195 | 0, 196 | ], 197 | "@typescript-eslint/no-useless-empty-export": [ 198 | 0, 199 | ], 200 | "@typescript-eslint/no-var-requires": [ 201 | 0, 202 | ], 203 | "@typescript-eslint/no-wrapper-object-types": [ 204 | 0, 205 | ], 206 | "@typescript-eslint/prefer-as-const": [ 207 | 0, 208 | ], 209 | "@typescript-eslint/prefer-enum-initializers": [ 210 | 0, 211 | ], 212 | "@typescript-eslint/prefer-for-of": [ 213 | 0, 214 | ], 215 | "@typescript-eslint/prefer-function-type": [ 216 | 0, 217 | ], 218 | "@typescript-eslint/prefer-literal-enum-member": [ 219 | 0, 220 | ], 221 | "@typescript-eslint/prefer-namespace-keyword": [ 222 | 0, 223 | ], 224 | "@typescript-eslint/prefer-ts-expect-error": [ 225 | 0, 226 | ], 227 | "@typescript-eslint/triple-slash-reference": [ 228 | 0, 229 | ], 230 | "array-callback-return": [ 231 | 0, 232 | { 233 | "allowImplicit": false, 234 | "allowVoid": false, 235 | "checkForEach": false, 236 | }, 237 | ], 238 | "block-scoped-var": [ 239 | 0, 240 | ], 241 | "curly": [ 242 | 0, 243 | "all", 244 | ], 245 | "default-case": [ 246 | 0, 247 | {}, 248 | ], 249 | "default-case-last": [ 250 | 0, 251 | ], 252 | "default-param-last": [ 253 | 0, 254 | ], 255 | "eqeqeq": [ 256 | 0, 257 | ], 258 | "for-direction": [ 259 | 0, 260 | ], 261 | "func-names": [ 262 | 0, 263 | "always", 264 | {}, 265 | ], 266 | "func-style": [ 267 | 0, 268 | "expression", 269 | { 270 | "allowArrowFunctions": false, 271 | "allowTypeAnnotation": false, 272 | "overrides": {}, 273 | }, 274 | ], 275 | "grouped-accessor-pairs": [ 276 | 0, 277 | "anyOrder", 278 | ], 279 | "guard-for-in": [ 280 | 0, 281 | ], 282 | "import/consistent-type-specifier-style": [ 283 | 0, 284 | ], 285 | "import/default": [ 286 | 0, 287 | ], 288 | "import/exports-last": [ 289 | 0, 290 | ], 291 | "import/first": [ 292 | 0, 293 | ], 294 | "import/group-exports": [ 295 | 0, 296 | ], 297 | "import/max-dependencies": [ 298 | 0, 299 | ], 300 | "import/namespace": [ 301 | 0, 302 | ], 303 | "import/no-absolute-path": [ 304 | 0, 305 | ], 306 | "import/no-amd": [ 307 | 0, 308 | ], 309 | "import/no-anonymous-default-export": [ 310 | 0, 311 | ], 312 | "import/no-commonjs": [ 313 | 0, 314 | ], 315 | "import/no-cycle": [ 316 | 0, 317 | ], 318 | "import/no-default-export": [ 319 | 0, 320 | ], 321 | "import/no-duplicates": [ 322 | 0, 323 | ], 324 | "import/no-dynamic-require": [ 325 | 0, 326 | ], 327 | "import/no-empty-named-blocks": [ 328 | 0, 329 | ], 330 | "import/no-mutable-exports": [ 331 | 0, 332 | ], 333 | "import/no-named-as-default": [ 334 | 0, 335 | ], 336 | "import/no-named-as-default-member": [ 337 | 0, 338 | ], 339 | "import/no-named-default": [ 340 | 0, 341 | ], 342 | "import/no-namespace": [ 343 | 0, 344 | ], 345 | "import/no-self-import": [ 346 | 0, 347 | ], 348 | "import/no-unassigned-import": [ 349 | 0, 350 | ], 351 | "import/no-webpack-loader-syntax": [ 352 | 0, 353 | ], 354 | "import/unambiguous": [ 355 | 0, 356 | ], 357 | "init-declarations": [ 358 | 0, 359 | ], 360 | "jest/consistent-test-it": [ 361 | 0, 362 | ], 363 | "jest/expect-expect": [ 364 | 0, 365 | ], 366 | "jest/max-expects": [ 367 | 0, 368 | ], 369 | "jest/max-nested-describe": [ 370 | 0, 371 | ], 372 | "jest/no-alias-methods": [ 373 | 0, 374 | ], 375 | "jest/no-commented-out-tests": [ 376 | 0, 377 | ], 378 | "jest/no-conditional-expect": [ 379 | 0, 380 | ], 381 | "jest/no-conditional-in-test": [ 382 | 0, 383 | ], 384 | "jest/no-confusing-set-timeout": [ 385 | 0, 386 | ], 387 | "jest/no-deprecated-functions": [ 388 | 0, 389 | ], 390 | "jest/no-disabled-tests": [ 391 | 0, 392 | ], 393 | "jest/no-done-callback": [ 394 | 0, 395 | ], 396 | "jest/no-duplicate-hooks": [ 397 | 0, 398 | ], 399 | "jest/no-export": [ 400 | 0, 401 | ], 402 | "jest/no-focused-tests": [ 403 | 0, 404 | ], 405 | "jest/no-hooks": [ 406 | 0, 407 | ], 408 | "jest/no-identical-title": [ 409 | 0, 410 | ], 411 | "jest/no-interpolation-in-snapshots": [ 412 | 0, 413 | ], 414 | "jest/no-jasmine-globals": [ 415 | 0, 416 | ], 417 | "jest/no-large-snapshots": [ 418 | 0, 419 | ], 420 | "jest/no-mocks-import": [ 421 | 0, 422 | ], 423 | "jest/no-restricted-jest-methods": [ 424 | 0, 425 | ], 426 | "jest/no-restricted-matchers": [ 427 | 0, 428 | ], 429 | "jest/no-standalone-expect": [ 430 | 0, 431 | ], 432 | "jest/no-test-prefixes": [ 433 | 0, 434 | ], 435 | "jest/no-test-return-statement": [ 436 | 0, 437 | ], 438 | "jest/no-untyped-mock-factory": [ 439 | 0, 440 | ], 441 | "jest/prefer-called-with": [ 442 | 0, 443 | ], 444 | "jest/prefer-comparison-matcher": [ 445 | 0, 446 | ], 447 | "jest/prefer-each": [ 448 | 0, 449 | ], 450 | "jest/prefer-equality-matcher": [ 451 | 0, 452 | ], 453 | "jest/prefer-expect-resolves": [ 454 | 0, 455 | ], 456 | "jest/prefer-hooks-in-order": [ 457 | 0, 458 | ], 459 | "jest/prefer-hooks-on-top": [ 460 | 0, 461 | ], 462 | "jest/prefer-jest-mocked": [ 463 | 0, 464 | ], 465 | "jest/prefer-lowercase-title": [ 466 | 0, 467 | ], 468 | "jest/prefer-mock-promise-shorthand": [ 469 | 0, 470 | ], 471 | "jest/prefer-spy-on": [ 472 | 0, 473 | ], 474 | "jest/prefer-strict-equal": [ 475 | 0, 476 | ], 477 | "jest/prefer-to-be": [ 478 | 0, 479 | ], 480 | "jest/prefer-to-contain": [ 481 | 0, 482 | ], 483 | "jest/prefer-to-have-length": [ 484 | 0, 485 | ], 486 | "jest/prefer-todo": [ 487 | 0, 488 | ], 489 | "jest/require-hook": [ 490 | 0, 491 | ], 492 | "jest/require-to-throw-message": [ 493 | 0, 494 | ], 495 | "jest/require-top-level-describe": [ 496 | 0, 497 | ], 498 | "jest/valid-describe-callback": [ 499 | 0, 500 | ], 501 | "jest/valid-expect": [ 502 | 0, 503 | ], 504 | "jest/valid-title": [ 505 | 0, 506 | ], 507 | "jsdoc/check-access": [ 508 | 0, 509 | ], 510 | "jsdoc/check-property-names": [ 511 | 0, 512 | ], 513 | "jsdoc/check-tag-names": [ 514 | 0, 515 | ], 516 | "jsdoc/empty-tags": [ 517 | 0, 518 | ], 519 | "jsdoc/implements-on-classes": [ 520 | 0, 521 | ], 522 | "jsdoc/no-defaults": [ 523 | 0, 524 | ], 525 | "jsdoc/require-param": [ 526 | 0, 527 | ], 528 | "jsdoc/require-param-description": [ 529 | 0, 530 | ], 531 | "jsdoc/require-param-name": [ 532 | 0, 533 | ], 534 | "jsdoc/require-param-type": [ 535 | 0, 536 | ], 537 | "jsdoc/require-property": [ 538 | 0, 539 | ], 540 | "jsdoc/require-property-description": [ 541 | 0, 542 | ], 543 | "jsdoc/require-property-name": [ 544 | 0, 545 | ], 546 | "jsdoc/require-property-type": [ 547 | 0, 548 | ], 549 | "jsdoc/require-returns": [ 550 | 0, 551 | ], 552 | "jsdoc/require-returns-description": [ 553 | 0, 554 | ], 555 | "jsdoc/require-returns-type": [ 556 | 0, 557 | ], 558 | "jsdoc/require-yields": [ 559 | 0, 560 | ], 561 | "jsx-a11y/alt-text": [ 562 | 0, 563 | ], 564 | "jsx-a11y/anchor-ambiguous-text": [ 565 | 0, 566 | ], 567 | "jsx-a11y/anchor-has-content": [ 568 | 0, 569 | ], 570 | "jsx-a11y/anchor-is-valid": [ 571 | 0, 572 | ], 573 | "jsx-a11y/aria-activedescendant-has-tabindex": [ 574 | 0, 575 | ], 576 | "jsx-a11y/aria-props": [ 577 | 0, 578 | ], 579 | "jsx-a11y/aria-role": [ 580 | 0, 581 | ], 582 | "jsx-a11y/aria-unsupported-elements": [ 583 | 0, 584 | ], 585 | "jsx-a11y/autocomplete-valid": [ 586 | 0, 587 | ], 588 | "jsx-a11y/click-events-have-key-events": [ 589 | 0, 590 | ], 591 | "jsx-a11y/heading-has-content": [ 592 | 0, 593 | ], 594 | "jsx-a11y/html-has-lang": [ 595 | 0, 596 | ], 597 | "jsx-a11y/iframe-has-title": [ 598 | 0, 599 | ], 600 | "jsx-a11y/img-redundant-alt": [ 601 | 0, 602 | ], 603 | "jsx-a11y/label-has-associated-control": [ 604 | 0, 605 | ], 606 | "jsx-a11y/lang": [ 607 | 0, 608 | ], 609 | "jsx-a11y/media-has-caption": [ 610 | 0, 611 | ], 612 | "jsx-a11y/mouse-events-have-key-events": [ 613 | 0, 614 | ], 615 | "jsx-a11y/no-access-key": [ 616 | 0, 617 | ], 618 | "jsx-a11y/no-aria-hidden-on-focusable": [ 619 | 0, 620 | ], 621 | "jsx-a11y/no-autofocus": [ 622 | 0, 623 | ], 624 | "jsx-a11y/no-distracting-elements": [ 625 | 0, 626 | ], 627 | "jsx-a11y/no-noninteractive-tabindex": [ 628 | 0, 629 | ], 630 | "jsx-a11y/no-redundant-roles": [ 631 | 0, 632 | ], 633 | "jsx-a11y/prefer-tag-over-role": [ 634 | 0, 635 | ], 636 | "jsx-a11y/role-has-required-aria-props": [ 637 | 0, 638 | ], 639 | "jsx-a11y/role-supports-aria-props": [ 640 | 0, 641 | ], 642 | "jsx-a11y/scope": [ 643 | 0, 644 | ], 645 | "jsx-a11y/tabindex-no-positive": [ 646 | 0, 647 | ], 648 | "max-classes-per-file": [ 649 | 0, 650 | ], 651 | "max-depth": [ 652 | 0, 653 | ], 654 | "max-lines": [ 655 | 0, 656 | ], 657 | "max-lines-per-function": [ 658 | 0, 659 | ], 660 | "max-nested-callbacks": [ 661 | 0, 662 | ], 663 | "max-params": [ 664 | 0, 665 | ], 666 | "new-cap": [ 667 | 0, 668 | { 669 | "capIsNew": true, 670 | "capIsNewExceptions": [ 671 | "Array", 672 | "Boolean", 673 | "Date", 674 | "Error", 675 | "Function", 676 | "Number", 677 | "Object", 678 | "RegExp", 679 | "String", 680 | "Symbol", 681 | "BigInt", 682 | ], 683 | "newIsCap": true, 684 | "newIsCapExceptions": [], 685 | "properties": true, 686 | }, 687 | ], 688 | "no-alert": [ 689 | 0, 690 | ], 691 | "no-array-constructor": [ 692 | 0, 693 | ], 694 | "no-async-promise-executor": [ 695 | 0, 696 | ], 697 | "no-await-in-loop": [ 698 | 0, 699 | ], 700 | "no-bitwise": [ 701 | 0, 702 | { 703 | "allow": [], 704 | "int32Hint": false, 705 | }, 706 | ], 707 | "no-caller": [ 708 | 0, 709 | ], 710 | "no-case-declarations": [ 711 | 0, 712 | ], 713 | "no-class-assign": [ 714 | 0, 715 | ], 716 | "no-compare-neg-zero": [ 717 | 0, 718 | ], 719 | "no-cond-assign": [ 720 | 0, 721 | "except-parens", 722 | ], 723 | "no-console": [ 724 | 0, 725 | {}, 726 | ], 727 | "no-const-assign": [ 728 | 0, 729 | ], 730 | "no-constant-binary-expression": [ 731 | 0, 732 | ], 733 | "no-constant-condition": [ 734 | 0, 735 | { 736 | "checkLoops": "allExceptWhileTrue", 737 | }, 738 | ], 739 | "no-constructor-return": [ 740 | 0, 741 | ], 742 | "no-continue": [ 743 | 0, 744 | ], 745 | "no-control-regex": [ 746 | 0, 747 | ], 748 | "no-debugger": [ 749 | 0, 750 | ], 751 | "no-delete-var": [ 752 | 0, 753 | ], 754 | "no-div-regex": [ 755 | 0, 756 | ], 757 | "no-dupe-class-members": [ 758 | 0, 759 | ], 760 | "no-dupe-else-if": [ 761 | 0, 762 | ], 763 | "no-dupe-keys": [ 764 | 0, 765 | ], 766 | "no-duplicate-case": [ 767 | 0, 768 | ], 769 | "no-duplicate-imports": [ 770 | 0, 771 | { 772 | "includeExports": false, 773 | }, 774 | ], 775 | "no-else-return": [ 776 | 0, 777 | { 778 | "allowElseIf": true, 779 | }, 780 | ], 781 | "no-empty": [ 782 | 0, 783 | { 784 | "allowEmptyCatch": false, 785 | }, 786 | ], 787 | "no-empty-character-class": [ 788 | 0, 789 | ], 790 | "no-empty-function": [ 791 | 0, 792 | { 793 | "allow": [], 794 | }, 795 | ], 796 | "no-empty-pattern": [ 797 | 0, 798 | { 799 | "allowObjectPatternsAsParameters": false, 800 | }, 801 | ], 802 | "no-empty-static-block": [ 803 | 0, 804 | ], 805 | "no-eq-null": [ 806 | 0, 807 | ], 808 | "no-eval": [ 809 | 0, 810 | { 811 | "allowIndirect": false, 812 | }, 813 | ], 814 | "no-ex-assign": [ 815 | 0, 816 | ], 817 | "no-extend-native": [ 818 | 0, 819 | { 820 | "exceptions": [], 821 | }, 822 | ], 823 | "no-extra-boolean-cast": [ 824 | 0, 825 | {}, 826 | ], 827 | "no-extra-label": [ 828 | 0, 829 | ], 830 | "no-fallthrough": [ 831 | 0, 832 | { 833 | "allowEmptyCase": false, 834 | "reportUnusedFallthroughComment": false, 835 | }, 836 | ], 837 | "no-func-assign": [ 838 | 0, 839 | ], 840 | "no-global-assign": [ 841 | 0, 842 | { 843 | "exceptions": [], 844 | }, 845 | ], 846 | "no-import-assign": [ 847 | 0, 848 | ], 849 | "no-inner-declarations": [ 850 | 0, 851 | "functions", 852 | { 853 | "blockScopedFunctions": "allow", 854 | }, 855 | ], 856 | "no-invalid-regexp": [ 857 | 0, 858 | {}, 859 | ], 860 | "no-irregular-whitespace": [ 861 | 0, 862 | { 863 | "skipComments": false, 864 | "skipJSXText": false, 865 | "skipRegExps": false, 866 | "skipStrings": true, 867 | "skipTemplates": false, 868 | }, 869 | ], 870 | "no-iterator": [ 871 | 0, 872 | ], 873 | "no-label-var": [ 874 | 0, 875 | ], 876 | "no-labels": [ 877 | 0, 878 | { 879 | "allowLoop": false, 880 | "allowSwitch": false, 881 | }, 882 | ], 883 | "no-lone-blocks": [ 884 | 0, 885 | ], 886 | "no-lonely-if": [ 887 | 0, 888 | ], 889 | "no-loss-of-precision": [ 890 | 0, 891 | ], 892 | "no-magic-numbers": [ 893 | 0, 894 | ], 895 | "no-multi-assign": [ 896 | 0, 897 | { 898 | "ignoreNonDeclaration": false, 899 | }, 900 | ], 901 | "no-multi-str": [ 902 | 0, 903 | ], 904 | "no-negated-condition": [ 905 | 0, 906 | ], 907 | "no-nested-ternary": [ 908 | 0, 909 | ], 910 | "no-new": [ 911 | 0, 912 | ], 913 | "no-new-func": [ 914 | 0, 915 | ], 916 | "no-new-native-nonconstructor": [ 917 | 0, 918 | ], 919 | "no-new-wrappers": [ 920 | 0, 921 | ], 922 | "no-nonoctal-decimal-escape": [ 923 | 0, 924 | ], 925 | "no-obj-calls": [ 926 | 0, 927 | ], 928 | "no-object-constructor": [ 929 | 0, 930 | ], 931 | "no-plusplus": [ 932 | 0, 933 | { 934 | "allowForLoopAfterthoughts": false, 935 | }, 936 | ], 937 | "no-proto": [ 938 | 0, 939 | ], 940 | "no-prototype-builtins": [ 941 | 0, 942 | ], 943 | "no-redeclare": [ 944 | 0, 945 | { 946 | "builtinGlobals": true, 947 | }, 948 | ], 949 | "no-regex-spaces": [ 950 | 0, 951 | ], 952 | "no-restricted-globals": [ 953 | 0, 954 | ], 955 | "no-restricted-imports": [ 956 | 0, 957 | ], 958 | "no-return-assign": [ 959 | 0, 960 | "except-parens", 961 | ], 962 | "no-script-url": [ 963 | 0, 964 | ], 965 | "no-self-assign": [ 966 | 0, 967 | { 968 | "props": true, 969 | }, 970 | ], 971 | "no-self-compare": [ 972 | 0, 973 | ], 974 | "no-setter-return": [ 975 | 0, 976 | ], 977 | "no-shadow-restricted-names": [ 978 | 0, 979 | { 980 | "reportGlobalThis": false, 981 | }, 982 | ], 983 | "no-sparse-arrays": [ 984 | 0, 985 | ], 986 | "no-template-curly-in-string": [ 987 | 0, 988 | ], 989 | "no-ternary": [ 990 | 0, 991 | ], 992 | "no-this-before-super": [ 993 | 0, 994 | ], 995 | "no-throw-literal": [ 996 | 0, 997 | ], 998 | "no-undefined": [ 999 | 0, 1000 | ], 1001 | "no-unexpected-multiline": [ 1002 | 0, 1003 | ], 1004 | "no-unneeded-ternary": [ 1005 | 0, 1006 | { 1007 | "defaultAssignment": true, 1008 | }, 1009 | ], 1010 | "no-unsafe-finally": [ 1011 | 0, 1012 | ], 1013 | "no-unsafe-negation": [ 1014 | 0, 1015 | { 1016 | "enforceForOrderingRelations": false, 1017 | }, 1018 | ], 1019 | "no-unsafe-optional-chaining": [ 1020 | 0, 1021 | { 1022 | "disallowArithmeticOperators": false, 1023 | }, 1024 | ], 1025 | "no-unused-expressions": [ 1026 | 0, 1027 | { 1028 | "allowShortCircuit": false, 1029 | "allowTaggedTemplates": false, 1030 | "allowTernary": false, 1031 | "enforceForJSX": false, 1032 | "ignoreDirectives": false, 1033 | }, 1034 | ], 1035 | "no-unused-labels": [ 1036 | 0, 1037 | ], 1038 | "no-unused-private-class-members": [ 1039 | 0, 1040 | ], 1041 | "no-unused-vars": [ 1042 | 0, 1043 | ], 1044 | "no-useless-backreference": [ 1045 | 0, 1046 | ], 1047 | "no-useless-call": [ 1048 | 0, 1049 | ], 1050 | "no-useless-catch": [ 1051 | 0, 1052 | ], 1053 | "no-useless-concat": [ 1054 | 0, 1055 | ], 1056 | "no-useless-constructor": [ 1057 | 0, 1058 | ], 1059 | "no-useless-escape": [ 1060 | 0, 1061 | { 1062 | "allowRegexCharacters": [], 1063 | }, 1064 | ], 1065 | "no-useless-rename": [ 1066 | 0, 1067 | { 1068 | "ignoreDestructuring": false, 1069 | "ignoreExport": false, 1070 | "ignoreImport": false, 1071 | }, 1072 | ], 1073 | "no-var": [ 1074 | 0, 1075 | ], 1076 | "no-void": [ 1077 | 0, 1078 | { 1079 | "allowAsStatement": false, 1080 | }, 1081 | ], 1082 | "no-with": [ 1083 | 0, 1084 | ], 1085 | "node/no-exports-assign": [ 1086 | 0, 1087 | ], 1088 | "node/no-new-require": [ 1089 | 0, 1090 | ], 1091 | "operator-assignment": [ 1092 | 0, 1093 | "always", 1094 | ], 1095 | "prefer-exponentiation-operator": [ 1096 | 0, 1097 | ], 1098 | "prefer-numeric-literals": [ 1099 | 0, 1100 | ], 1101 | "prefer-object-has-own": [ 1102 | 0, 1103 | ], 1104 | "prefer-object-spread": [ 1105 | 0, 1106 | ], 1107 | "prefer-promise-reject-errors": [ 1108 | 0, 1109 | { 1110 | "allowEmptyReject": false, 1111 | }, 1112 | ], 1113 | "prefer-rest-params": [ 1114 | 0, 1115 | ], 1116 | "prefer-spread": [ 1117 | 0, 1118 | ], 1119 | "promise/avoid-new": [ 1120 | 0, 1121 | ], 1122 | "promise/catch-or-return": [ 1123 | 0, 1124 | ], 1125 | "promise/no-callback-in-promise": [ 1126 | 0, 1127 | ], 1128 | "promise/no-nesting": [ 1129 | 0, 1130 | ], 1131 | "promise/no-new-statics": [ 1132 | 0, 1133 | ], 1134 | "promise/no-promise-in-callback": [ 1135 | 0, 1136 | ], 1137 | "promise/no-return-wrap": [ 1138 | 0, 1139 | ], 1140 | "promise/param-names": [ 1141 | 0, 1142 | ], 1143 | "promise/prefer-await-to-callbacks": [ 1144 | 0, 1145 | ], 1146 | "promise/prefer-await-to-then": [ 1147 | 0, 1148 | ], 1149 | "promise/prefer-catch": [ 1150 | 0, 1151 | ], 1152 | "promise/spec-only": [ 1153 | 0, 1154 | ], 1155 | "promise/valid-params": [ 1156 | 0, 1157 | ], 1158 | "radix": [ 1159 | 0, 1160 | "always", 1161 | ], 1162 | "react-hooks/exhaustive-deps": [ 1163 | 0, 1164 | ], 1165 | "react-hooks/rules-of-hooks": [ 1166 | 0, 1167 | ], 1168 | "react-perf/jsx-no-jsx-as-prop": [ 1169 | 0, 1170 | ], 1171 | "react-perf/jsx-no-new-array-as-prop": [ 1172 | 0, 1173 | ], 1174 | "react-perf/jsx-no-new-function-as-prop": [ 1175 | 0, 1176 | ], 1177 | "react-perf/jsx-no-new-object-as-prop": [ 1178 | 0, 1179 | ], 1180 | "react/button-has-type": [ 1181 | 0, 1182 | ], 1183 | "react/checked-requires-onchange-or-readonly": [ 1184 | 0, 1185 | ], 1186 | "react/forbid-elements": [ 1187 | 0, 1188 | ], 1189 | "react/forward-ref-uses-ref": [ 1190 | 0, 1191 | ], 1192 | "react/iframe-missing-sandbox": [ 1193 | 0, 1194 | ], 1195 | "react/jsx-boolean-value": [ 1196 | 0, 1197 | ], 1198 | "react/jsx-curly-brace-presence": [ 1199 | 0, 1200 | ], 1201 | "react/jsx-filename-extension": [ 1202 | 0, 1203 | ], 1204 | "react/jsx-key": [ 1205 | 0, 1206 | ], 1207 | "react/jsx-no-comment-textnodes": [ 1208 | 0, 1209 | ], 1210 | "react/jsx-no-duplicate-props": [ 1211 | 0, 1212 | ], 1213 | "react/jsx-no-script-url": [ 1214 | 0, 1215 | ], 1216 | "react/jsx-no-target-blank": [ 1217 | 0, 1218 | ], 1219 | "react/jsx-no-undef": [ 1220 | 0, 1221 | ], 1222 | "react/jsx-no-useless-fragment": [ 1223 | 0, 1224 | ], 1225 | "react/jsx-props-no-spread-multi": [ 1226 | 0, 1227 | ], 1228 | "react/no-array-index-key": [ 1229 | 0, 1230 | ], 1231 | "react/no-children-prop": [ 1232 | 0, 1233 | ], 1234 | "react/no-danger": [ 1235 | 0, 1236 | ], 1237 | "react/no-danger-with-children": [ 1238 | 0, 1239 | ], 1240 | "react/no-direct-mutation-state": [ 1241 | 0, 1242 | ], 1243 | "react/no-find-dom-node": [ 1244 | 0, 1245 | ], 1246 | "react/no-is-mounted": [ 1247 | 0, 1248 | ], 1249 | "react/no-namespace": [ 1250 | 0, 1251 | ], 1252 | "react/no-render-return-value": [ 1253 | 0, 1254 | ], 1255 | "react/no-set-state": [ 1256 | 0, 1257 | ], 1258 | "react/no-string-refs": [ 1259 | 0, 1260 | ], 1261 | "react/no-unescaped-entities": [ 1262 | 0, 1263 | ], 1264 | "react/no-unknown-property": [ 1265 | 0, 1266 | ], 1267 | "react/prefer-es6-class": [ 1268 | 0, 1269 | ], 1270 | "react/react-in-jsx-scope": [ 1271 | 0, 1272 | ], 1273 | "react/self-closing-comp": [ 1274 | 0, 1275 | ], 1276 | "react/style-prop-object": [ 1277 | 0, 1278 | ], 1279 | "react/void-dom-elements-no-children": [ 1280 | 0, 1281 | ], 1282 | "require-await": [ 1283 | 0, 1284 | ], 1285 | "require-yield": [ 1286 | 0, 1287 | ], 1288 | "sort-imports": [ 1289 | 0, 1290 | { 1291 | "allowSeparatedGroups": false, 1292 | "ignoreCase": false, 1293 | "ignoreDeclarationSort": false, 1294 | "ignoreMemberSort": false, 1295 | "memberSyntaxSortOrder": [ 1296 | "none", 1297 | "all", 1298 | "multiple", 1299 | "single", 1300 | ], 1301 | }, 1302 | ], 1303 | "sort-keys": [ 1304 | 0, 1305 | "asc", 1306 | { 1307 | "allowLineSeparatedGroups": false, 1308 | "caseSensitive": true, 1309 | "ignoreComputedKeys": false, 1310 | "minKeys": 2, 1311 | "natural": false, 1312 | }, 1313 | ], 1314 | "sort-vars": [ 1315 | 0, 1316 | { 1317 | "ignoreCase": false, 1318 | }, 1319 | ], 1320 | "symbol-description": [ 1321 | 0, 1322 | ], 1323 | "unicode-bom": [ 1324 | 0, 1325 | "never", 1326 | ], 1327 | "unicorn/catch-error-name": [ 1328 | 0, 1329 | ], 1330 | "unicorn/consistent-assert": [ 1331 | 0, 1332 | ], 1333 | "unicorn/consistent-date-clone": [ 1334 | 0, 1335 | ], 1336 | "unicorn/consistent-empty-array-spread": [ 1337 | 0, 1338 | ], 1339 | "unicorn/consistent-existence-index-check": [ 1340 | 0, 1341 | ], 1342 | "unicorn/consistent-function-scoping": [ 1343 | 0, 1344 | ], 1345 | "unicorn/empty-brace-spaces": [ 1346 | 0, 1347 | ], 1348 | "unicorn/error-message": [ 1349 | 0, 1350 | ], 1351 | "unicorn/escape-case": [ 1352 | 0, 1353 | ], 1354 | "unicorn/explicit-length-check": [ 1355 | 0, 1356 | ], 1357 | "unicorn/filename-case": [ 1358 | 0, 1359 | ], 1360 | "unicorn/new-for-builtins": [ 1361 | 0, 1362 | ], 1363 | "unicorn/no-abusive-eslint-disable": [ 1364 | 0, 1365 | ], 1366 | "unicorn/no-accessor-recursion": [ 1367 | 0, 1368 | ], 1369 | "unicorn/no-anonymous-default-export": [ 1370 | 0, 1371 | ], 1372 | "unicorn/no-array-for-each": [ 1373 | 0, 1374 | ], 1375 | "unicorn/no-array-method-this-argument": [ 1376 | 0, 1377 | ], 1378 | "unicorn/no-array-reduce": [ 1379 | 0, 1380 | ], 1381 | "unicorn/no-await-expression-member": [ 1382 | 0, 1383 | ], 1384 | "unicorn/no-await-in-promise-methods": [ 1385 | 0, 1386 | ], 1387 | "unicorn/no-console-spaces": [ 1388 | 0, 1389 | ], 1390 | "unicorn/no-document-cookie": [ 1391 | 0, 1392 | ], 1393 | "unicorn/no-empty-file": [ 1394 | 0, 1395 | ], 1396 | "unicorn/no-hex-escape": [ 1397 | 0, 1398 | ], 1399 | "unicorn/no-instanceof-array": [ 1400 | 0, 1401 | ], 1402 | "unicorn/no-instanceof-builtins": [ 1403 | 0, 1404 | ], 1405 | "unicorn/no-invalid-fetch-options": [ 1406 | 0, 1407 | ], 1408 | "unicorn/no-invalid-remove-event-listener": [ 1409 | 0, 1410 | ], 1411 | "unicorn/no-length-as-slice-end": [ 1412 | 0, 1413 | ], 1414 | "unicorn/no-lonely-if": [ 1415 | 0, 1416 | ], 1417 | "unicorn/no-magic-array-flat-depth": [ 1418 | 0, 1419 | ], 1420 | "unicorn/no-negated-condition": [ 1421 | 0, 1422 | ], 1423 | "unicorn/no-negation-in-equality-check": [ 1424 | 0, 1425 | ], 1426 | "unicorn/no-nested-ternary": [ 1427 | 0, 1428 | ], 1429 | "unicorn/no-new-array": [ 1430 | 0, 1431 | ], 1432 | "unicorn/no-new-buffer": [ 1433 | 0, 1434 | ], 1435 | "unicorn/no-null": [ 1436 | 0, 1437 | ], 1438 | "unicorn/no-object-as-default-parameter": [ 1439 | 0, 1440 | ], 1441 | "unicorn/no-process-exit": [ 1442 | 0, 1443 | ], 1444 | "unicorn/no-single-promise-in-promise-methods": [ 1445 | 0, 1446 | ], 1447 | "unicorn/no-static-only-class": [ 1448 | 0, 1449 | ], 1450 | "unicorn/no-thenable": [ 1451 | 0, 1452 | ], 1453 | "unicorn/no-this-assignment": [ 1454 | 0, 1455 | ], 1456 | "unicorn/no-typeof-undefined": [ 1457 | 0, 1458 | ], 1459 | "unicorn/no-unnecessary-array-flat-depth": [ 1460 | 0, 1461 | ], 1462 | "unicorn/no-unnecessary-await": [ 1463 | 0, 1464 | ], 1465 | "unicorn/no-unnecessary-slice-end": [ 1466 | 0, 1467 | ], 1468 | "unicorn/no-unreadable-array-destructuring": [ 1469 | 0, 1470 | ], 1471 | "unicorn/no-unreadable-iife": [ 1472 | 0, 1473 | ], 1474 | "unicorn/no-useless-fallback-in-spread": [ 1475 | 0, 1476 | ], 1477 | "unicorn/no-useless-length-check": [ 1478 | 0, 1479 | ], 1480 | "unicorn/no-useless-promise-resolve-reject": [ 1481 | 0, 1482 | ], 1483 | "unicorn/no-useless-spread": [ 1484 | 0, 1485 | ], 1486 | "unicorn/no-useless-switch-case": [ 1487 | 0, 1488 | ], 1489 | "unicorn/no-useless-undefined": [ 1490 | 0, 1491 | ], 1492 | "unicorn/no-zero-fractions": [ 1493 | 0, 1494 | ], 1495 | "unicorn/number-literal-case": [ 1496 | 0, 1497 | ], 1498 | "unicorn/numeric-separators-style": [ 1499 | 0, 1500 | ], 1501 | "unicorn/prefer-add-event-listener": [ 1502 | 0, 1503 | ], 1504 | "unicorn/prefer-array-find": [ 1505 | 0, 1506 | ], 1507 | "unicorn/prefer-array-flat": [ 1508 | 0, 1509 | ], 1510 | "unicorn/prefer-array-flat-map": [ 1511 | 0, 1512 | ], 1513 | "unicorn/prefer-array-index-of": [ 1514 | 0, 1515 | ], 1516 | "unicorn/prefer-array-some": [ 1517 | 0, 1518 | ], 1519 | "unicorn/prefer-blob-reading-methods": [ 1520 | 0, 1521 | ], 1522 | "unicorn/prefer-code-point": [ 1523 | 0, 1524 | ], 1525 | "unicorn/prefer-date-now": [ 1526 | 0, 1527 | ], 1528 | "unicorn/prefer-dom-node-append": [ 1529 | 0, 1530 | ], 1531 | "unicorn/prefer-dom-node-dataset": [ 1532 | 0, 1533 | ], 1534 | "unicorn/prefer-dom-node-remove": [ 1535 | 0, 1536 | ], 1537 | "unicorn/prefer-dom-node-text-content": [ 1538 | 0, 1539 | ], 1540 | "unicorn/prefer-event-target": [ 1541 | 0, 1542 | ], 1543 | "unicorn/prefer-global-this": [ 1544 | 0, 1545 | ], 1546 | "unicorn/prefer-includes": [ 1547 | 0, 1548 | ], 1549 | "unicorn/prefer-logical-operator-over-ternary": [ 1550 | 0, 1551 | ], 1552 | "unicorn/prefer-math-min-max": [ 1553 | 0, 1554 | ], 1555 | "unicorn/prefer-math-trunc": [ 1556 | 0, 1557 | ], 1558 | "unicorn/prefer-modern-dom-apis": [ 1559 | 0, 1560 | ], 1561 | "unicorn/prefer-modern-math-apis": [ 1562 | 0, 1563 | ], 1564 | "unicorn/prefer-native-coercion-functions": [ 1565 | 0, 1566 | ], 1567 | "unicorn/prefer-negative-index": [ 1568 | 0, 1569 | ], 1570 | "unicorn/prefer-node-protocol": [ 1571 | 0, 1572 | ], 1573 | "unicorn/prefer-number-properties": [ 1574 | 0, 1575 | ], 1576 | "unicorn/prefer-object-from-entries": [ 1577 | 0, 1578 | ], 1579 | "unicorn/prefer-optional-catch-binding": [ 1580 | 0, 1581 | ], 1582 | "unicorn/prefer-prototype-methods": [ 1583 | 0, 1584 | ], 1585 | "unicorn/prefer-query-selector": [ 1586 | 0, 1587 | ], 1588 | "unicorn/prefer-reflect-apply": [ 1589 | 0, 1590 | ], 1591 | "unicorn/prefer-regexp-test": [ 1592 | 0, 1593 | ], 1594 | "unicorn/prefer-set-has": [ 1595 | 0, 1596 | ], 1597 | "unicorn/prefer-set-size": [ 1598 | 0, 1599 | ], 1600 | "unicorn/prefer-spread": [ 1601 | 0, 1602 | ], 1603 | "unicorn/prefer-string-raw": [ 1604 | 0, 1605 | ], 1606 | "unicorn/prefer-string-replace-all": [ 1607 | 0, 1608 | ], 1609 | "unicorn/prefer-string-slice": [ 1610 | 0, 1611 | ], 1612 | "unicorn/prefer-string-starts-ends-with": [ 1613 | 0, 1614 | ], 1615 | "unicorn/prefer-string-trim-start-end": [ 1616 | 0, 1617 | ], 1618 | "unicorn/prefer-structured-clone": [ 1619 | 0, 1620 | ], 1621 | "unicorn/prefer-type-error": [ 1622 | 0, 1623 | ], 1624 | "unicorn/require-array-join-separator": [ 1625 | 0, 1626 | ], 1627 | "unicorn/require-number-to-fixed-digits-argument": [ 1628 | 0, 1629 | ], 1630 | "unicorn/require-post-message-target-origin": [ 1631 | 0, 1632 | ], 1633 | "unicorn/switch-case-braces": [ 1634 | 0, 1635 | ], 1636 | "unicorn/text-encoding-identifier-case": [ 1637 | 0, 1638 | ], 1639 | "unicorn/throw-new-error": [ 1640 | 0, 1641 | ], 1642 | "use-isnan": [ 1643 | 0, 1644 | { 1645 | "enforceForIndexOf": false, 1646 | "enforceForSwitchCase": true, 1647 | }, 1648 | ], 1649 | "valid-typeof": [ 1650 | 0, 1651 | { 1652 | "requireStringLiterals": false, 1653 | }, 1654 | ], 1655 | "vars-on-top": [ 1656 | 0, 1657 | ], 1658 | "vitest/consistent-test-it": [ 1659 | 0, 1660 | ], 1661 | "vitest/expect-expect": [ 1662 | 0, 1663 | ], 1664 | "vitest/max-expects": [ 1665 | 0, 1666 | ], 1667 | "vitest/max-nested-describe": [ 1668 | 0, 1669 | ], 1670 | "vitest/no-alias-methods": [ 1671 | 0, 1672 | ], 1673 | "vitest/no-commented-out-tests": [ 1674 | 0, 1675 | ], 1676 | "vitest/no-conditional-expect": [ 1677 | 0, 1678 | ], 1679 | "vitest/no-conditional-in-test": [ 1680 | 0, 1681 | ], 1682 | "vitest/no-conditional-tests": [ 1683 | 0, 1684 | ], 1685 | "vitest/no-disabled-tests": [ 1686 | 0, 1687 | ], 1688 | "vitest/no-duplicate-hooks": [ 1689 | 0, 1690 | ], 1691 | "vitest/no-focused-tests": [ 1692 | 0, 1693 | ], 1694 | "vitest/no-hooks": [ 1695 | 0, 1696 | ], 1697 | "vitest/no-identical-title": [ 1698 | 0, 1699 | ], 1700 | "vitest/no-import-node-test": [ 1701 | 0, 1702 | ], 1703 | "vitest/no-interpolation-in-snapshots": [ 1704 | 0, 1705 | ], 1706 | "vitest/no-restricted-jest-methods": [ 1707 | 0, 1708 | ], 1709 | "vitest/no-restricted-matchers": [ 1710 | 0, 1711 | ], 1712 | "vitest/no-standalone-expect": [ 1713 | 0, 1714 | ], 1715 | "vitest/no-test-prefixes": [ 1716 | 0, 1717 | ], 1718 | "vitest/no-test-return-statement": [ 1719 | 0, 1720 | ], 1721 | "vitest/prefer-comparison-matcher": [ 1722 | 0, 1723 | ], 1724 | "vitest/prefer-each": [ 1725 | 0, 1726 | ], 1727 | "vitest/prefer-equality-matcher": [ 1728 | 0, 1729 | ], 1730 | "vitest/prefer-expect-resolves": [ 1731 | 0, 1732 | ], 1733 | "vitest/prefer-hooks-in-order": [ 1734 | 0, 1735 | ], 1736 | "vitest/prefer-hooks-on-top": [ 1737 | 0, 1738 | ], 1739 | "vitest/prefer-lowercase-title": [ 1740 | 0, 1741 | ], 1742 | "vitest/prefer-mock-promise-shorthand": [ 1743 | 0, 1744 | ], 1745 | "vitest/prefer-strict-equal": [ 1746 | 0, 1747 | ], 1748 | "vitest/prefer-to-be-falsy": [ 1749 | 0, 1750 | ], 1751 | "vitest/prefer-to-be-object": [ 1752 | 0, 1753 | ], 1754 | "vitest/prefer-to-be-truthy": [ 1755 | 0, 1756 | ], 1757 | "vitest/prefer-to-have-length": [ 1758 | 0, 1759 | ], 1760 | "vitest/prefer-todo": [ 1761 | 0, 1762 | ], 1763 | "vitest/require-local-test-context-for-concurrent-snapshots": [ 1764 | 0, 1765 | ], 1766 | "vitest/require-to-throw-message": [ 1767 | 0, 1768 | ], 1769 | "vitest/require-top-level-describe": [ 1770 | 0, 1771 | ], 1772 | "vitest/valid-describe-callback": [ 1773 | 0, 1774 | ], 1775 | "vitest/valid-expect": [ 1776 | 0, 1777 | ], 1778 | "yoda": [ 1779 | 0, 1780 | "never", 1781 | { 1782 | "exceptRange": false, 1783 | "onlyEquality": false, 1784 | }, 1785 | ], 1786 | } 1787 | `; 1788 | -------------------------------------------------------------------------------- /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: ['vite'], rules: { eqeqeq: 'off' } }, 71 | 72 | // categories change 73 | { categories: { correctness: 'off', suspicious: 'warn' } }, 74 | // combination plugin + categires + rules 75 | { 76 | plugins: ['vite'], 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 | 'n', 104 | 'promise', 105 | 'jest', 106 | 'vitest', 107 | ], 108 | }, 109 | // everything on 110 | { 111 | plugins: [ 112 | 'typescript', 113 | 'unicorn', 114 | 'react', 115 | 'react-perf', 116 | 'nextjs', 117 | 'import', 118 | 'jsdoc', 119 | 'jsx-a11y', 120 | 'n', 121 | 'promise', 122 | 'jest', 123 | 'vitest', 124 | ], 125 | categories: { 126 | nursery: 'off', // we not support this category 127 | correctness: 'warn', 128 | pedantic: 'warn', 129 | perf: 'warn', 130 | restriction: 'warn', 131 | style: 'warn', 132 | suspicious: 'warn', 133 | }, 134 | }, 135 | ].entries()) { 136 | const fileContent = JSON.stringify(config); 137 | 138 | it(`should output same rule count for: ${fileContent}`, () => { 139 | const oxlintRulesCount = executeOxlintWithConfiguration( 140 | `integration-test-${index}-oxlint.json`, 141 | config 142 | ); 143 | 144 | const configs = buildFromOxlintConfig(config); 145 | 146 | expect(configs.length).toBeGreaterThanOrEqual(1); 147 | expect(configs[0].rules).not.toBeUndefined(); 148 | 149 | let expectedCount = oxlintRulesCount ?? 0; 150 | let receivedCount = 0; 151 | 152 | for (const buildConfig of configs) { 153 | receivedCount += Object.keys(buildConfig.rules!).length; 154 | 155 | // special mapping for ts alias rules 156 | if ( 157 | config.plugins === undefined || 158 | config.plugins.includes('typescript') 159 | ) { 160 | expectedCount += typescriptRulesExtendEslintRules.filter( 161 | (aliasRule) => aliasRule in buildConfig.rules! 162 | ).length; 163 | } 164 | 165 | // special case for vitest / jest alias rules 166 | if (config.plugins?.includes('vitest')) { 167 | expectedCount += viteTestCompatibleRules.filter( 168 | (aliasRule) => `vitest/${aliasRule}` in buildConfig.rules! 169 | ).length; 170 | } 171 | 172 | // special mapping for unicorn alias rules 173 | if ( 174 | config.plugins === undefined || 175 | config.plugins.includes('unicorn') 176 | ) { 177 | expectedCount += unicornRulesExtendEslintRules.filter( 178 | (aliasRule) => `unicorn/${aliasRule}` in buildConfig.rules! 179 | ).length; 180 | } 181 | } 182 | 183 | expect(receivedCount).toBe(expectedCount); 184 | }); 185 | } 186 | }); 187 | 188 | const createConfigFileAndBuildFromIt = ( 189 | filename: string, 190 | content: string 191 | ): Linter.Config>[] => { 192 | fs.writeFileSync(filename, content); 193 | 194 | const rules = buildFromOxlintConfigFile(filename); 195 | 196 | fs.unlinkSync(filename); 197 | 198 | return rules; 199 | }; 200 | 201 | const executeOxlintWithConfiguration = ( 202 | filename: string, 203 | config: { 204 | [key: string]: unknown; 205 | plugins?: string[]; 206 | categories?: Record; 207 | rules?: Record; 208 | } 209 | ) => { 210 | fs.writeFileSync(filename, JSON.stringify(config)); 211 | let oxlintOutput: string; 212 | 213 | const cliArguments = [ 214 | `--config=${filename}`, 215 | '--disable-oxc-plugin', 216 | '--silent', 217 | ]; 218 | 219 | try { 220 | oxlintOutput = execSync(`npx oxlint ${cliArguments.join(' ')}`, { 221 | encoding: 'utf8', 222 | stdio: 'pipe', 223 | }); 224 | } catch { 225 | oxlintOutput = ''; 226 | } 227 | 228 | fs.unlinkSync(filename); 229 | 230 | const result = /with\s(\d+)\srules/.exec(oxlintOutput); 231 | 232 | if (result === null) { 233 | return; 234 | } 235 | 236 | return Number.parseInt(result[1], 10) ?? undefined; 237 | }; 238 | -------------------------------------------------------------------------------- /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-duplicates": "off", 8 | "import/no-empty-named-blocks": "off", 9 | "import/no-named-as-default": "off", 10 | "import/no-named-as-default-member": "off", 11 | "import/no-self-import": "off", 12 | "import/no-unassigned-import": "off", 13 | "no-extend-native": "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 | } 20 | `; 21 | 22 | exports[`handleCategoriesScope > custom plugins, default categories > customPluginDefaultCategories 1`] = ` 23 | { 24 | "for-direction": "off", 25 | "no-async-promise-executor": "off", 26 | "no-caller": "off", 27 | "no-class-assign": "off", 28 | "no-compare-neg-zero": "off", 29 | "no-cond-assign": "off", 30 | "no-const-assign": "off", 31 | "no-constant-binary-expression": "off", 32 | "no-constant-condition": "off", 33 | "no-control-regex": "off", 34 | "no-debugger": "off", 35 | "no-delete-var": "off", 36 | "no-dupe-class-members": "off", 37 | "no-dupe-else-if": "off", 38 | "no-dupe-keys": "off", 39 | "no-duplicate-case": "off", 40 | "no-empty-character-class": "off", 41 | "no-empty-pattern": "off", 42 | "no-empty-static-block": "off", 43 | "no-eval": "off", 44 | "no-ex-assign": "off", 45 | "no-extra-boolean-cast": "off", 46 | "no-func-assign": "off", 47 | "no-global-assign": "off", 48 | "no-import-assign": "off", 49 | "no-invalid-regexp": "off", 50 | "no-irregular-whitespace": "off", 51 | "no-loss-of-precision": "off", 52 | "no-new-native-nonconstructor": "off", 53 | "no-nonoctal-decimal-escape": "off", 54 | "no-obj-calls": "off", 55 | "no-self-assign": "off", 56 | "no-setter-return": "off", 57 | "no-shadow-restricted-names": "off", 58 | "no-sparse-arrays": "off", 59 | "no-this-before-super": "off", 60 | "no-unsafe-finally": "off", 61 | "no-unsafe-negation": "off", 62 | "no-unsafe-optional-chaining": "off", 63 | "no-unused-labels": "off", 64 | "no-unused-private-class-members": "off", 65 | "no-unused-vars": "off", 66 | "no-useless-backreference": "off", 67 | "no-useless-catch": "off", 68 | "no-useless-escape": "off", 69 | "no-useless-rename": "off", 70 | "no-with": "off", 71 | "require-yield": "off", 72 | "unicorn/no-await-in-promise-methods": "off", 73 | "unicorn/no-empty-file": "off", 74 | "unicorn/no-invalid-fetch-options": "off", 75 | "unicorn/no-invalid-remove-event-listener": "off", 76 | "unicorn/no-new-array": "off", 77 | "unicorn/no-single-promise-in-promise-methods": "off", 78 | "unicorn/no-thenable": "off", 79 | "unicorn/no-unnecessary-await": "off", 80 | "unicorn/no-useless-fallback-in-spread": "off", 81 | "unicorn/no-useless-length-check": "off", 82 | "unicorn/no-useless-spread": "off", 83 | "unicorn/prefer-set-size": "off", 84 | "unicorn/prefer-string-starts-ends-with": "off", 85 | "use-isnan": "off", 86 | "valid-typeof": "off", 87 | } 88 | `; 89 | 90 | exports[`handleCategoriesScope > default plugins (react, unicorn, typescript), default categories > defaultPluginDefaultCategories 1`] = ` 91 | { 92 | "@typescript-eslint/no-dupe-class-members": "off", 93 | "@typescript-eslint/no-duplicate-enum-values": "off", 94 | "@typescript-eslint/no-extra-non-null-assertion": "off", 95 | "@typescript-eslint/no-loss-of-precision": "off", 96 | "@typescript-eslint/no-misused-new": "off", 97 | "@typescript-eslint/no-non-null-asserted-optional-chain": "off", 98 | "@typescript-eslint/no-this-alias": "off", 99 | "@typescript-eslint/no-unnecessary-parameter-property-assignment": "off", 100 | "@typescript-eslint/no-unsafe-declaration-merging": "off", 101 | "@typescript-eslint/no-unused-vars": "off", 102 | "@typescript-eslint/no-useless-empty-export": "off", 103 | "@typescript-eslint/no-wrapper-object-types": "off", 104 | "@typescript-eslint/prefer-as-const": "off", 105 | "@typescript-eslint/triple-slash-reference": "off", 106 | "for-direction": "off", 107 | "no-async-promise-executor": "off", 108 | "no-caller": "off", 109 | "no-class-assign": "off", 110 | "no-compare-neg-zero": "off", 111 | "no-cond-assign": "off", 112 | "no-const-assign": "off", 113 | "no-constant-binary-expression": "off", 114 | "no-constant-condition": "off", 115 | "no-control-regex": "off", 116 | "no-debugger": "off", 117 | "no-delete-var": "off", 118 | "no-dupe-class-members": "off", 119 | "no-dupe-else-if": "off", 120 | "no-dupe-keys": "off", 121 | "no-duplicate-case": "off", 122 | "no-empty-character-class": "off", 123 | "no-empty-pattern": "off", 124 | "no-empty-static-block": "off", 125 | "no-eval": "off", 126 | "no-ex-assign": "off", 127 | "no-extra-boolean-cast": "off", 128 | "no-func-assign": "off", 129 | "no-global-assign": "off", 130 | "no-import-assign": "off", 131 | "no-invalid-regexp": "off", 132 | "no-irregular-whitespace": "off", 133 | "no-loss-of-precision": "off", 134 | "no-new-native-nonconstructor": "off", 135 | "no-nonoctal-decimal-escape": "off", 136 | "no-obj-calls": "off", 137 | "no-self-assign": "off", 138 | "no-setter-return": "off", 139 | "no-shadow-restricted-names": "off", 140 | "no-sparse-arrays": "off", 141 | "no-this-before-super": "off", 142 | "no-unsafe-finally": "off", 143 | "no-unsafe-negation": "off", 144 | "no-unsafe-optional-chaining": "off", 145 | "no-unused-labels": "off", 146 | "no-unused-private-class-members": "off", 147 | "no-unused-vars": "off", 148 | "no-useless-backreference": "off", 149 | "no-useless-catch": "off", 150 | "no-useless-escape": "off", 151 | "no-useless-rename": "off", 152 | "no-with": "off", 153 | "react/forward-ref-uses-ref": "off", 154 | "react/jsx-key": "off", 155 | "react/jsx-no-duplicate-props": "off", 156 | "react/jsx-no-target-blank": "off", 157 | "react/jsx-no-undef": "off", 158 | "react/jsx-props-no-spread-multi": "off", 159 | "react/no-children-prop": "off", 160 | "react/no-danger-with-children": "off", 161 | "react/no-direct-mutation-state": "off", 162 | "react/no-find-dom-node": "off", 163 | "react/no-is-mounted": "off", 164 | "react/no-render-return-value": "off", 165 | "react/no-string-refs": "off", 166 | "react/void-dom-elements-no-children": "off", 167 | "require-yield": "off", 168 | "unicorn/no-await-in-promise-methods": "off", 169 | "unicorn/no-empty-file": "off", 170 | "unicorn/no-invalid-fetch-options": "off", 171 | "unicorn/no-invalid-remove-event-listener": "off", 172 | "unicorn/no-new-array": "off", 173 | "unicorn/no-single-promise-in-promise-methods": "off", 174 | "unicorn/no-thenable": "off", 175 | "unicorn/no-unnecessary-await": "off", 176 | "unicorn/no-useless-fallback-in-spread": "off", 177 | "unicorn/no-useless-length-check": "off", 178 | "unicorn/no-useless-spread": "off", 179 | "unicorn/prefer-set-size": "off", 180 | "unicorn/prefer-string-starts-ends-with": "off", 181 | "use-isnan": "off", 182 | "valid-typeof": "off", 183 | } 184 | `; 185 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/categories.ts: -------------------------------------------------------------------------------- 1 | import { aliasPluginNames } from '../constants.js'; 2 | import configByCategory from '../generated/configs-by-category.js'; 3 | import { 4 | OxlintConfig, 5 | OxlintConfigCategories, 6 | OxlintConfigPlugins, 7 | } from './types.js'; 8 | import { isObject } from './utilities.js'; 9 | 10 | // default categories, see 11 | export const defaultCategories: OxlintConfigCategories = { 12 | correctness: 'warn', 13 | }; 14 | 15 | /** 16 | * appends all rules which are enabled by a plugin and falls into a specific category 17 | */ 18 | export const handleCategoriesScope = ( 19 | plugins: OxlintConfigPlugins, 20 | categories: OxlintConfigCategories, 21 | rules: Record 22 | ): void => { 23 | for (const category in categories) { 24 | const configName = `flat/${category}`; 25 | 26 | // category is not enabled or not in found categories 27 | if (categories[category] === 'off' || !(configName in configByCategory)) { 28 | continue; 29 | } 30 | 31 | // @ts-expect-error -- come on TS, we are checking if the configName exists in the configByCategory 32 | const possibleRules = configByCategory[configName].rules; 33 | 34 | // iterate to each rule to check if the rule can be appended, because the plugin is activated 35 | for (const rule of Object.keys(possibleRules)) { 36 | for (const plugin of plugins) { 37 | const pluginPrefix = 38 | plugin in aliasPluginNames ? aliasPluginNames[plugin] : plugin; 39 | 40 | // the rule has no prefix, so it is a eslint one 41 | if (pluginPrefix === '' && !rule.includes('/')) { 42 | rules[rule] = 'off'; 43 | // other rules with a prefix like @typescript-eslint/ 44 | } else if (rule.startsWith(`${pluginPrefix}/`)) { 45 | rules[rule] = 'off'; 46 | } 47 | } 48 | } 49 | } 50 | }; 51 | 52 | /** 53 | * tries to return the "categories" section from the config. 54 | * it returns `undefined` when not found or invalid. 55 | */ 56 | export const readCategoriesFromConfig = ( 57 | config: OxlintConfig 58 | ): OxlintConfigCategories | undefined => { 59 | return 'categories' in config && isObject(config.categories) 60 | ? (config.categories as OxlintConfigCategories) 61 | : undefined; 62 | }; 63 | -------------------------------------------------------------------------------- /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: [ 119 | './extends1.json', 120 | './folder/extends2.json', 121 | '../parent/extends3.json', 122 | ], 123 | __misc: { 124 | filePath: '/root/of/the/file/test-config.json', 125 | }, 126 | }; 127 | resolveRelativeExtendsPaths(config); 128 | 129 | expect(config.extends).toEqual([ 130 | '/root/of/the/file/extends1.json', 131 | '/root/of/the/file/folder/extends2.json', 132 | '/root/of/the/parent/extends3.json', 133 | ]); 134 | }); 135 | }); 136 | -------------------------------------------------------------------------------- /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 = ( 19 | config: OxlintConfig 20 | ): OxlintConfigExtends | undefined => { 21 | return 'extends' in config && Array.isArray(config.extends) 22 | ? (config.extends as OxlintConfigExtends) 23 | : undefined; 24 | }; 25 | 26 | /** 27 | * Resolves the paths of the "extends" section relative to the given config file. 28 | */ 29 | export const resolveRelativeExtendsPaths = (config: OxlintConfig) => { 30 | if (!config.__misc?.filePath) { 31 | return; 32 | } 33 | 34 | const extendsFiles = readExtendsFromConfig(config); 35 | if (!extendsFiles?.length) return; 36 | const configFileDirectory = path.dirname(config.__misc.filePath); 37 | config.extends = extendsFiles.map((extendFile) => 38 | path.resolve(configFileDirectory, extendFile) 39 | ); 40 | }; 41 | 42 | /** 43 | * Appends plugins, rules and overrides from the extends configs files to the given config. 44 | */ 45 | export const handleExtendsScope = ( 46 | extendsConfigs: OxlintConfig[], 47 | config: OxlintConfig 48 | ) => { 49 | let rules: OxlintConfigRules = readRulesFromConfig(config) ?? {}; 50 | const plugins: OxlintConfigPlugins = readPluginsFromConfig(config) ?? []; 51 | const overrides: OxlintConfigOverride[] = 52 | readOverridesFromConfig(config) ?? []; 53 | for (const extendConfig of extendsConfigs) { 54 | plugins.unshift(...(readPluginsFromConfig(extendConfig) ?? defaultPlugins)); 55 | rules = { ...readRulesFromConfig(extendConfig), ...rules }; 56 | overrides.unshift(...(readOverridesFromConfig(extendConfig) ?? [])); 57 | } 58 | if (plugins.length > 0) config.plugins = [...new Set(plugins)]; 59 | if (Object.keys(rules).length > 0) config.rules = rules; 60 | if (overrides.length > 0) config.overrides = overrides; 61 | }; 62 | 63 | /** 64 | * tries to return the content of the chain "extends" section from the config. 65 | */ 66 | export const readExtendsConfigsFromConfig = ( 67 | config: OxlintConfig 68 | ): OxlintConfig[] => { 69 | const extendsFiles = readExtendsFromConfig(config); 70 | if (!extendsFiles?.length) return []; 71 | 72 | const extendsConfigs: OxlintConfig[] = []; 73 | for (const file of extendsFiles) { 74 | const extendConfig = getConfigContent(file); 75 | if (!extendConfig) continue; 76 | 77 | extendConfig.__misc = { 78 | filePath: file, 79 | }; 80 | 81 | resolveRelativeExtendsPaths(extendConfig); 82 | 83 | extendsConfigs.push( 84 | extendConfig, 85 | ...readExtendsConfigsFromConfig(extendConfig) 86 | ); 87 | } 88 | return extendsConfigs; 89 | }; 90 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/ignore-patterns.ts: -------------------------------------------------------------------------------- 1 | import { 2 | EslintPluginOxlintConfig, 3 | OxlintConfig, 4 | OxlintConfigIgnorePatterns, 5 | } from './types.js'; 6 | 7 | /** 8 | * adds the ignore patterns to the config 9 | */ 10 | export const handleIgnorePatternsScope = ( 11 | ignorePatterns: OxlintConfigIgnorePatterns, 12 | config: EslintPluginOxlintConfig 13 | ): void => { 14 | config.ignores = ignorePatterns; 15 | }; 16 | /** 17 | * tries to return the "ignorePattern" section from the config. 18 | * it returns `undefined` when not found or invalid. 19 | */ 20 | export const readIgnorePatternsFromConfig = ( 21 | config: OxlintConfig 22 | ): OxlintConfigIgnorePatterns | undefined => { 23 | return 'ignorePatterns' in config && Array.isArray(config.ignorePatterns) 24 | ? (config.ignorePatterns as OxlintConfigIgnorePatterns) 25 | : undefined; 26 | }; 27 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/index.ts: -------------------------------------------------------------------------------- 1 | import { 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 { 10 | handleIgnorePatternsScope, 11 | readIgnorePatternsFromConfig, 12 | } from './ignore-patterns.js'; 13 | import { handleOverridesScope, readOverridesFromConfig } from './overrides.js'; 14 | import { splitDisabledRulesForVueAndSvelteFiles } from '../config-helper.js'; 15 | import { 16 | handleExtendsScope, 17 | readExtendsConfigsFromConfig, 18 | resolveRelativeExtendsPaths, 19 | } from './extends.js'; 20 | import { getConfigContent } from './utilities.js'; 21 | import path from 'node:path'; 22 | 23 | /** 24 | * builds turned off rules, which are supported by oxlint. 25 | * It accepts an object similar to the .oxlintrc.json file. 26 | */ 27 | export const buildFromOxlintConfig = ( 28 | config: OxlintConfig 29 | ): EslintPluginOxlintConfig[] => { 30 | resolveRelativeExtendsPaths(config); 31 | 32 | const extendConfigs = readExtendsConfigsFromConfig(config); 33 | if (extendConfigs.length > 0) { 34 | handleExtendsScope(extendConfigs, config); 35 | } 36 | 37 | const rules: Record = {}; 38 | const plugins = readPluginsFromConfig(config) ?? defaultPlugins; 39 | const categories = readCategoriesFromConfig(config) ?? defaultCategories; 40 | 41 | // it is not a plugin but it is activated by default 42 | plugins.push('eslint'); 43 | 44 | // oxc handles "react-hooks" rules inside "react" plugin 45 | // our generator split them into own plugins 46 | if (plugins.includes('react')) { 47 | plugins.push('react-hooks'); 48 | } 49 | 50 | handleCategoriesScope(plugins, categories, rules); 51 | 52 | const configRules = readRulesFromConfig(config); 53 | 54 | if (configRules !== undefined) { 55 | handleRulesScope(configRules, rules); 56 | } 57 | 58 | const baseConfig = { 59 | name: 'oxlint/from-oxlint-config', 60 | rules, 61 | }; 62 | 63 | const ignorePatterns = readIgnorePatternsFromConfig(config); 64 | 65 | if (ignorePatterns !== undefined) { 66 | handleIgnorePatternsScope(ignorePatterns, baseConfig); 67 | } 68 | 69 | const overrides = readOverridesFromConfig(config); 70 | const configs = splitDisabledRulesForVueAndSvelteFiles( 71 | baseConfig 72 | ) as EslintPluginOxlintConfig[]; 73 | 74 | if (overrides !== undefined) { 75 | handleOverridesScope(overrides, configs, categories); 76 | } 77 | 78 | return configs; 79 | }; 80 | 81 | /** 82 | * builds turned off rules, which are supported by oxlint. 83 | * It accepts an filepath to the .oxlintrc.json file. 84 | * 85 | * It the .oxlintrc.json file could not be found or parsed, 86 | * no rules will be deactivated and an error to `console.error` will be emitted 87 | */ 88 | export const buildFromOxlintConfigFile = ( 89 | oxlintConfigFile: string 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); 104 | }; 105 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | EslintPluginOxlintConfig, 6 | OxlintConfig, 7 | OxlintConfigCategories, 8 | OxlintConfigOverride, 9 | } from './types.js'; 10 | 11 | export const handleOverridesScope = ( 12 | overrides: OxlintConfigOverride[], 13 | configs: EslintPluginOxlintConfig[], 14 | baseCategories?: OxlintConfigCategories 15 | ): void => { 16 | for (const overrideIndex in overrides) { 17 | const override = overrides[overrideIndex]; 18 | const eslintRules: Record = {}; 19 | const eslintConfig: EslintPluginOxlintConfig = { 20 | name: `oxlint/from-oxlint-config-override-${overrideIndex}`, 21 | }; 22 | 23 | // expect that oxlint `files` syntax is the same as eslint 24 | eslintConfig.files = override.files; 25 | 26 | const plugins = readPluginsFromConfig(override); 27 | if (baseCategories !== undefined && plugins !== undefined) { 28 | handleCategoriesScope(plugins, baseCategories, eslintRules); 29 | } 30 | 31 | const rules = readRulesFromConfig(override); 32 | if (rules !== undefined) { 33 | handleRulesScope(rules, eslintRules); 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/build-from-oxlint-config/plugins.ts: -------------------------------------------------------------------------------- 1 | import { 2 | OxlintConfig, 3 | OxlintConfigOverride, 4 | OxlintConfigPlugins, 5 | } from './types.js'; 6 | 7 | // default plugins, see 8 | export const defaultPlugins: OxlintConfigPlugins = ['unicorn', 'typescript']; 9 | 10 | /** 11 | * tries to return the "plugins" section from the config. 12 | * it returns `undefined` when not found or invalid. 13 | */ 14 | export const readPluginsFromConfig = ( 15 | config: OxlintConfig | OxlintConfigOverride 16 | ): OxlintConfigPlugins | undefined => { 17 | return 'plugins' in config && Array.isArray(config.plugins) 18 | ? (config.plugins as OxlintConfigPlugins) 19 | : undefined; 20 | }; 21 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/rules.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { handleRulesScope } from './rules.js'; 3 | import { 4 | unicornRulesExtendEslintRules, 5 | viteTestCompatibleRules, 6 | } from '../constants.js'; 7 | 8 | describe('handleRulesScope', () => { 9 | for (const ruleSetting of [ 10 | 'error', 11 | ['error'], 12 | 'warn', 13 | ['warn'], 14 | 1, 15 | [1], 16 | 2, 17 | [2], 18 | ]) { 19 | it(`detect active rule ${JSON.stringify(ruleSetting)} inside "rules" scope`, () => { 20 | const rules = {}; 21 | handleRulesScope( 22 | { 23 | eqeqeq: ruleSetting, 24 | }, 25 | rules 26 | ); 27 | 28 | expect(rules).toStrictEqual({ 29 | eqeqeq: 'off', 30 | }); 31 | }); 32 | } 33 | 34 | for (const ruleSetting of ['off', ['off'], 0, [0]]) { 35 | it(`skip deactive rule ${JSON.stringify(ruleSetting)} inside "rules" scope`, () => { 36 | const rules = {}; 37 | handleRulesScope( 38 | { 39 | eqeqeq: ruleSetting, 40 | }, 41 | rules 42 | ); 43 | 44 | expect(rules).toStrictEqual({}); 45 | }); 46 | } 47 | 48 | for (const ruleSetting of ['on', ['on'], 3, [3]]) { 49 | it(`skip invalid ${JSON.stringify(ruleSetting)} inside "rules" scope`, () => { 50 | const rules = {}; 51 | handleRulesScope( 52 | { 53 | eqeqeq: ruleSetting, 54 | }, 55 | rules 56 | ); 57 | 58 | expect(rules).toStrictEqual({}); 59 | }); 60 | } 61 | 62 | // look here: 63 | it('detects oxlint rules with plugin alias inside rules block', () => { 64 | const rules = {}; 65 | handleRulesScope( 66 | { 67 | 'eslint/eqeqeq': 'warn', 68 | 'typescript/no-unused-vars': 'warn', 69 | 'react_perf/jsx-no-new-array-as-prop': 'warn', 70 | 'nextjs/no-img-element': 'warn', 71 | 'jsx_a11y/alt-text': 'warn', 72 | 'react/rules-of-hooks': 'warn', 73 | 'import-x/namespace': 'warn', 74 | // 'deepscan/xxx': 'warn', 75 | }, 76 | rules 77 | ); 78 | 79 | expect(rules).toStrictEqual({ 80 | eqeqeq: 'off', 81 | '@typescript-eslint/no-unused-vars': 'off', 82 | 'react-perf/jsx-no-new-array-as-prop': 'off', 83 | '@next/next/no-img-element': 'off', 84 | 'jsx-a11y/alt-text': 'off', 85 | 'react-hooks/rules-of-hooks': 'off', 86 | 'import/namespace': 'off', 87 | }); 88 | }); 89 | 90 | it('detects rules without plugin name', () => { 91 | const rules = {}; 92 | handleRulesScope( 93 | { 94 | 'no-unused-vars': 'warn', 95 | 'jsx-no-new-array-as-prop': 'warn', 96 | 'no-img-element': 'warn', 97 | 'no-array-reduce': 'warn', 98 | }, 99 | rules 100 | ); 101 | 102 | expect(rules).toStrictEqual({ 103 | 'no-unused-vars': 'off', // ToDo: should be @typescript-eslint/ 104 | 'react-perf/jsx-no-new-array-as-prop': 'off', 105 | '@next/next/no-img-element': 'off', 106 | 'unicorn/no-array-reduce': 'off', 107 | }); 108 | }); 109 | 110 | it('skips unknown oxlint rules', () => { 111 | const rules = {}; 112 | handleRulesScope( 113 | { 114 | unknown: 'warn', 115 | 'typescript/no-img-element': 'warn', // valid rule, but wrong plugin-name 116 | }, 117 | rules 118 | ); 119 | 120 | expect(rules).toStrictEqual({}); 121 | }); 122 | 123 | for (const alias of viteTestCompatibleRules) { 124 | it(`disables vitest jest alias rules for ${alias}`, () => { 125 | for (const rule of [`jest/${alias}`, `vitest/${alias}`]) { 126 | const rules = {}; 127 | handleRulesScope( 128 | { 129 | [rule]: 'warn', 130 | }, 131 | rules 132 | ); 133 | 134 | expect(rules).toStrictEqual({ 135 | [rule]: 'off', 136 | }); 137 | } 138 | }); 139 | } 140 | 141 | for (const alias of unicornRulesExtendEslintRules) { 142 | it(`disables unicorn eslint alias rules for ${alias}`, () => { 143 | for (const rule of [`unicorn/${alias}`, alias]) { 144 | const rules = {}; 145 | handleRulesScope( 146 | { 147 | [rule]: 'warn', 148 | }, 149 | rules 150 | ); 151 | 152 | expect(rules).toStrictEqual({ 153 | [rule]: 'off', 154 | }); 155 | } 156 | }); 157 | } 158 | }); 159 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/rules.ts: -------------------------------------------------------------------------------- 1 | import { 2 | aliasPluginNames, 3 | reactHookRulesInsideReactScope, 4 | } from '../constants.js'; 5 | import { 6 | OxlintConfig, 7 | OxlintConfigOverride, 8 | OxlintConfigRules, 9 | } from './types.js'; 10 | import configByCategory from '../generated/configs-by-category.js'; 11 | import { isObject } from './utilities.js'; 12 | 13 | const allRulesObjects = Object.values(configByCategory).map( 14 | (config) => config.rules 15 | ); 16 | const allRules: string[] = allRulesObjects.flatMap((rulesObject) => 17 | Object.keys(rulesObject) 18 | ); 19 | 20 | const getEsLintRuleName = (rule: string): string | undefined => { 21 | // there is no plugin prefix, it can be all plugin 22 | if (!rule.includes('/')) { 23 | return allRules.find( 24 | (search) => search.endsWith(`/${rule}`) || search === rule 25 | ); 26 | } 27 | 28 | // greedy works with `@next/next/no-img-element` as an example 29 | const match = rule.match(/(^.*)\/(.*)/); 30 | 31 | if (match === null) { 32 | return undefined; 33 | } 34 | 35 | const pluginName = match[1]; 36 | const ruleName = match[2]; 37 | 38 | // map to the right eslint plugin 39 | let esPluginName = 40 | pluginName in aliasPluginNames ? aliasPluginNames[pluginName] : pluginName; 41 | 42 | // special case for eslint-plugin-react-hooks 43 | if ( 44 | esPluginName === 'react' && 45 | reactHookRulesInsideReactScope.includes(ruleName) 46 | ) { 47 | esPluginName = 'react-hooks'; 48 | } 49 | 50 | // extra check for eslint 51 | const expectedRule = 52 | esPluginName === '' ? ruleName : `${esPluginName}/${ruleName}`; 53 | 54 | return allRules.find((rule) => rule == expectedRule); 55 | }; 56 | 57 | /** 58 | * checks if value is validSet, or if validSet is an array, check if value is first value of it 59 | */ 60 | const isValueInSet = (value: unknown, validSet: unknown[]) => 61 | validSet.includes(value) || 62 | (Array.isArray(value) && validSet.includes(value[0])); 63 | 64 | /** 65 | * check if the value is "off", 0, ["off", ...], or [0, ...] 66 | */ 67 | const isDeactivateValue = (value: unknown) => isValueInSet(value, ['off', 0]); 68 | 69 | /** 70 | * check if the value is "error", "warn", 1, 2, ["error", ...], ["warn", ...], [1, ...], or [2, ...] 71 | */ 72 | const isActiveValue = (value: unknown) => 73 | isValueInSet(value, ['error', 'warn', 1, 2]); 74 | 75 | /** 76 | * checks if the oxlint rule is activated/deactivated and append/remove it. 77 | */ 78 | export const handleRulesScope = ( 79 | oxlintRules: OxlintConfigRules, 80 | rules: Record 81 | ): void => { 82 | for (const rule in oxlintRules) { 83 | const eslintName = getEsLintRuleName(rule); 84 | 85 | if (eslintName === undefined) { 86 | continue; 87 | } 88 | 89 | // is this rules not turned off 90 | if (isActiveValue(oxlintRules[rule])) { 91 | rules[eslintName] = 'off'; 92 | } else if (rule in rules && isDeactivateValue(oxlintRules[rule])) { 93 | // rules extended by categories or plugins can be disabled manually 94 | delete rules[eslintName]; 95 | } 96 | } 97 | }; 98 | 99 | /** 100 | * tries to return the "rules" section from the config. 101 | * it returns `undefined` when not found or invalid. 102 | */ 103 | export const readRulesFromConfig = ( 104 | config: OxlintConfig | OxlintConfigOverride 105 | ): OxlintConfigRules | undefined => { 106 | return 'rules' in config && isObject(config.rules) 107 | ? (config.rules as OxlintConfigRules) 108 | : undefined; 109 | }; 110 | -------------------------------------------------------------------------------- /src/build-from-oxlint-config/types.ts: -------------------------------------------------------------------------------- 1 | import type { Linter } from 'eslint'; 2 | 3 | export type OxlintConfigExtends = string[]; 4 | 5 | export type OxlintConfigPlugins = string[]; 6 | 7 | export type OxlintConfigCategories = Record; 8 | 9 | export type OxlintConfigRules = Record; 10 | 11 | export type OxlintConfigIgnorePatterns = string[]; 12 | 13 | export type EslintPluginOxlintConfig = Linter.Config>; 14 | 15 | export type OxlintConfigOverride = { 16 | files: string[]; 17 | plugins?: OxlintConfigPlugins; 18 | rules?: OxlintConfigRules; 19 | }; 20 | 21 | export type OxlintConfig = { 22 | [key: string]: unknown; 23 | extends?: OxlintConfigExtends; 24 | plugins?: OxlintConfigPlugins; 25 | categories?: OxlintConfigCategories; 26 | rules?: OxlintConfigRules; 27 | ignorePatterns?: OxlintConfigIgnorePatterns; 28 | 29 | // extra properties only used by `eslint-plugin-oxlint` 30 | __misc?: { 31 | // absolute path to the config file 32 | filePath: string; 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /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 = ( 17 | oxlintConfigFile: string 18 | ): OxlintConfig | undefined => { 19 | try { 20 | const content = fs.readFileSync(oxlintConfigFile, 'utf8'); 21 | 22 | try { 23 | const configContent = JSONCParser.parse(content); 24 | 25 | if (!isObject(configContent)) { 26 | throw new TypeError('not an valid config file'); 27 | } 28 | 29 | return configContent; 30 | } catch { 31 | console.error( 32 | `eslint-plugin-oxlint: could not parse oxlint config file: ${oxlintConfigFile}` 33 | ); 34 | return undefined; 35 | } 36 | } catch { 37 | console.error( 38 | `eslint-plugin-oxlint: could not find oxlint config file: ${oxlintConfigFile}` 39 | ); 40 | return undefined; 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /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/config-helper.ts: -------------------------------------------------------------------------------- 1 | import { rulesDisabledForVueAndSvelteFiles } from './constants.js'; 2 | import type { Linter } from 'eslint'; 3 | 4 | // for eslint legacy configuration 5 | export const overrideDisabledRulesForVueAndSvelteFiles = ( 6 | config: Linter.LegacyConfig> 7 | ): Linter.LegacyConfig> => { 8 | const foundRules = Object.keys(config.rules!).filter((rule) => 9 | rulesDisabledForVueAndSvelteFiles.includes(rule) 10 | ); 11 | 12 | if (foundRules.length === 0) { 13 | return config; 14 | } 15 | 16 | const newConfig = structuredClone(config); 17 | 18 | newConfig.overrides = [ 19 | { 20 | // classic configs use glob syntax 21 | files: ['*.*'], 22 | excludedFiles: ['*.vue', '*.svelte'], 23 | rules: {}, 24 | }, 25 | ]; 26 | 27 | for (const rule of foundRules) { 28 | delete newConfig.rules![rule]; 29 | newConfig.overrides[0].rules![rule] = 'off'; 30 | } 31 | 32 | return newConfig; 33 | }; 34 | 35 | // for eslint flat configuration 36 | export const splitDisabledRulesForVueAndSvelteFiles = ( 37 | config: Linter.Config 38 | ): Linter.Config[] => { 39 | const foundRules = Object.keys(config.rules!).filter((rule) => 40 | rulesDisabledForVueAndSvelteFiles.includes(rule) 41 | ); 42 | 43 | if (foundRules.length === 0) { 44 | return [config]; 45 | } 46 | 47 | const oldConfig = structuredClone(config); 48 | 49 | const newConfig: Linter.Config = { 50 | // flat configs use minimatch syntax 51 | name: 'oxlint/vue-svelte-exceptions', 52 | ignores: ['**/*.vue', '**/*.svelte'], 53 | rules: {}, 54 | }; 55 | 56 | for (const rule of foundRules) { 57 | delete oldConfig.rules![rule]; 58 | newConfig.rules![rule] = 'off'; 59 | } 60 | 61 | return [oldConfig, newConfig]; 62 | }; 63 | -------------------------------------------------------------------------------- /src/configs.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest'; 2 | import { ESLint } from 'eslint'; 3 | import { ESLintTestConfig } from '../test/helpers.js'; 4 | 5 | it('contains all the oxlint rules', async () => { 6 | const eslint = new ESLint(ESLintTestConfig); 7 | const config = await eslint.calculateConfigForFile('index.js'); 8 | expect(config.rules).toMatchSnapshot(); 9 | }); 10 | -------------------------------------------------------------------------------- /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 | } from './config-helper.js'; 9 | import type { Linter } from 'eslint'; 10 | 11 | type UnionToIntersection = ( 12 | U extends unknown ? (x: U) => void : never 13 | ) extends (x: infer I) => void 14 | ? I 15 | : never; 16 | 17 | type RulesGroups = keyof typeof ruleMapsByScope; 18 | type AllRules = (typeof ruleMapsByScope)[RulesGroups]; 19 | 20 | const allRules: UnionToIntersection = Object.assign( 21 | {}, 22 | ...Object.values(ruleMapsByScope) 23 | ); 24 | 25 | const splitDisabledRulesForVueAndSvelteFilesDeep = ( 26 | config: Record 27 | ): Record => { 28 | const entries = Object.entries(config).map( 29 | ([name, config]) => [name, splitDisabledRulesForVueAndSvelteFiles(config)] 30 | ); 31 | 32 | return Object.fromEntries(entries); 33 | }; 34 | 35 | export default { 36 | recommended: overrideDisabledRulesForVueAndSvelteFiles({ 37 | plugins: ['oxlint'], 38 | rules: ruleMapsByCategory.correctnessRules, 39 | }), 40 | all: overrideDisabledRulesForVueAndSvelteFiles({ 41 | plugins: ['oxlint'], 42 | rules: allRules, 43 | }), 44 | 'flat/all': splitDisabledRulesForVueAndSvelteFiles({ 45 | name: 'oxlint/all', 46 | rules: allRules, 47 | }), 48 | 'flat/recommended': splitDisabledRulesForVueAndSvelteFiles({ 49 | name: 'oxlint/recommended', 50 | rules: ruleMapsByCategory.correctnessRules, 51 | }), 52 | ...splitDisabledRulesForVueAndSvelteFilesDeep(configByScope), 53 | ...splitDisabledRulesForVueAndSvelteFilesDeep(configByCategory), 54 | }; 55 | -------------------------------------------------------------------------------- /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-restricted-jest-methods', 62 | 'no-restricted-matchers', 63 | 'no-standalone-expect', 64 | 'no-test-prefixes', 65 | 'no-test-return-statement', 66 | 'prefer-comparison-matcher', 67 | 'prefer-each', 68 | 'prefer-equality-matcher', 69 | 'prefer-expect-resolves', 70 | 'prefer-hooks-in-order', 71 | 'prefer-hooks-on-top', 72 | 'prefer-lowercase-title', 73 | 'prefer-mock-promise-shorthand', 74 | 'prefer-strict-equal', 75 | 'prefer-to-have-length', 76 | 'prefer-todo', 77 | 'require-to-throw-message', 78 | 'require-top-level-describe', 79 | 'valid-describe-callback', 80 | 'valid-expect', 81 | ]; 82 | 83 | export const unicornRulesExtendEslintRules = ['no-negated-condition']; 84 | 85 | // All rules from `eslint-plugin-react-hooks` 86 | // Since oxlint supports these rules under react/*, we need to remap them. 87 | export const reactHookRulesInsideReactScope = [ 88 | 'rules-of-hooks', 89 | 'exhaustive-deps', 90 | ]; 91 | 92 | // These rules are disabled for vue and svelte files 93 | // because oxlint can not parse currently the HTML 94 | export const rulesDisabledForVueAndSvelteFiles = [ 95 | 'no-unused-vars', 96 | '@typescript-eslint/no-unused-vars', 97 | 'react-hooks/rules-of-hooks', // disabled because its react 98 | ]; 99 | -------------------------------------------------------------------------------- /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 | }; 9 | 10 | const suspiciousConfig = { 11 | name: 'oxlint/suspicious', 12 | rules: rules.suspiciousRules, 13 | }; 14 | 15 | const styleConfig = { 16 | name: 'oxlint/style', 17 | rules: rules.styleRules, 18 | }; 19 | 20 | const restrictionConfig = { 21 | name: 'oxlint/restriction', 22 | rules: rules.restrictionRules, 23 | }; 24 | 25 | const correctnessConfig = { 26 | name: 'oxlint/correctness', 27 | rules: rules.correctnessRules, 28 | }; 29 | 30 | const perfConfig = { 31 | name: 'oxlint/perf', 32 | rules: rules.perfRules, 33 | }; 34 | 35 | const configByCategory = { 36 | 'flat/pedantic': pedanticConfig, 37 | 'flat/suspicious': suspiciousConfig, 38 | 'flat/style': styleConfig, 39 | 'flat/restriction': restrictionConfig, 40 | 'flat/correctness': correctnessConfig, 41 | 'flat/perf': perfConfig, 42 | }; 43 | 44 | export default configByCategory; 45 | -------------------------------------------------------------------------------- /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 | }; 9 | 10 | const importConfig = { 11 | name: 'oxlint/import', 12 | rules: rules.importRules, 13 | }; 14 | 15 | const jestConfig = { 16 | name: 'oxlint/jest', 17 | rules: rules.jestRules, 18 | }; 19 | 20 | const jsdocConfig = { 21 | name: 'oxlint/jsdoc', 22 | rules: rules.jsdocRules, 23 | }; 24 | 25 | const jsxA11yConfig = { 26 | name: 'oxlint/jsx-a11y', 27 | rules: rules.jsxA11yRules, 28 | }; 29 | 30 | const nextjsConfig = { 31 | name: 'oxlint/nextjs', 32 | rules: rules.nextjsRules, 33 | }; 34 | 35 | const nodeConfig = { 36 | name: 'oxlint/node', 37 | rules: rules.nodeRules, 38 | }; 39 | 40 | const promiseConfig = { 41 | name: 'oxlint/promise', 42 | rules: rules.promiseRules, 43 | }; 44 | 45 | const reactConfig = { 46 | name: 'oxlint/react', 47 | rules: rules.reactRules, 48 | }; 49 | 50 | const reactHooksConfig = { 51 | name: 'oxlint/react-hooks', 52 | rules: rules.reactHooksRules, 53 | }; 54 | 55 | const reactPerfConfig = { 56 | name: 'oxlint/react-perf', 57 | rules: rules.reactPerfRules, 58 | }; 59 | 60 | const typescriptConfig = { 61 | name: 'oxlint/typescript', 62 | rules: rules.typescriptRules, 63 | }; 64 | 65 | const unicornConfig = { 66 | name: 'oxlint/unicorn', 67 | rules: rules.unicornRules, 68 | }; 69 | 70 | const vitestConfig = { 71 | name: 'oxlint/vitest', 72 | rules: rules.vitestRules, 73 | }; 74 | 75 | const configByScope = { 76 | 'flat/eslint': eslintConfig, 77 | 'flat/import': importConfig, 78 | 'flat/jest': jestConfig, 79 | 'flat/jsdoc': jsdocConfig, 80 | 'flat/jsx-a11y': jsxA11yConfig, 81 | 'flat/nextjs': nextjsConfig, 82 | 'flat/node': nodeConfig, 83 | 'flat/promise': promiseConfig, 84 | 'flat/react': reactConfig, 85 | 'flat/react-hooks': reactHooksConfig, 86 | 'flat/react-perf': reactPerfConfig, 87 | 'flat/typescript': typescriptConfig, 88 | 'flat/unicorn': unicornConfig, 89 | 'flat/vitest': vitestConfig, 90 | }; 91 | 92 | export default configByScope; 93 | -------------------------------------------------------------------------------- /src/generated/rules-by-category.ts: -------------------------------------------------------------------------------- 1 | // These rules are automatically generated by scripts/generate-rules.ts 2 | 3 | const pedanticRules: Record = { 4 | 'array-callback-return': 'off', 5 | eqeqeq: 'off', 6 | 'max-classes-per-file': 'off', 7 | 'max-depth': 'off', 8 | 'max-lines-per-function': 'off', 9 | 'max-lines': 'off', 10 | 'max-nested-callbacks': 'off', 11 | 'no-array-constructor': 'off', 12 | 'no-case-declarations': 'off', 13 | 'no-lonely-if': 'off', 14 | 'no-object-constructor': 'off', 15 | 'no-constructor-return': 'off', 16 | 'no-else-return': 'off', 17 | 'no-fallthrough': 'off', 18 | 'no-inner-declarations': 'off', 19 | 'no-negated-condition': 'off', 20 | 'no-new-wrappers': 'off', 21 | 'no-prototype-builtins': 'off', 22 | 'no-redeclare': 'off', 23 | 'no-self-compare': 'off', 24 | 'no-throw-literal': 'off', 25 | radix: 'off', 26 | 'require-await': 'off', 27 | 'sort-vars': 'off', 28 | 'symbol-description': 'off', 29 | 'import/max-dependencies': 'off', 30 | 'jest/no-conditional-in-test': 'off', 31 | 'jsdoc/require-param': 'off', 32 | 'jsdoc/require-param-description': 'off', 33 | 'jsdoc/require-param-name': 'off', 34 | 'jsdoc/require-param-type': 'off', 35 | 'jsdoc/require-returns': 'off', 36 | 'jsdoc/require-returns-description': 'off', 37 | 'jsdoc/require-returns-type': 'off', 38 | 'react/checked-requires-onchange-or-readonly': 'off', 39 | 'react/jsx-no-useless-fragment': 'off', 40 | 'react/no-unescaped-entities': 'off', 41 | 'react-hooks/rules-of-hooks': 'off', 42 | '@typescript-eslint/ban-ts-comment': 'off', 43 | '@typescript-eslint/ban-types': 'off', 44 | '@typescript-eslint/no-unsafe-function-type': 'off', 45 | '@typescript-eslint/prefer-enum-initializers': 'off', 46 | '@typescript-eslint/prefer-ts-expect-error': 'off', 47 | 'unicorn/consistent-assert': 'off', 48 | 'unicorn/consistent-empty-array-spread': 'off', 49 | 'unicorn/escape-case': 'off', 50 | 'unicorn/explicit-length-check': 'off', 51 | 'unicorn/new-for-builtins': 'off', 52 | 'unicorn/no-unnecessary-array-flat-depth': 'off', 53 | 'unicorn/no-unnecessary-slice-end': 'off', 54 | 'unicorn/no-hex-escape': 'off', 55 | 'unicorn/no-instanceof-array': 'off', 56 | 'unicorn/no-lonely-if': 'off', 57 | 'unicorn/no-negation-in-equality-check': 'off', 58 | 'unicorn/no-new-buffer': 'off', 59 | 'unicorn/no-object-as-default-parameter': 'off', 60 | 'unicorn/no-static-only-class': 'off', 61 | 'unicorn/no-this-assignment': 'off', 62 | 'unicorn/no-typeof-undefined': 'off', 63 | 'unicorn/no-unreadable-iife': 'off', 64 | 'unicorn/no-useless-promise-resolve-reject': 'off', 65 | 'unicorn/no-useless-switch-case': 'off', 66 | 'unicorn/no-useless-undefined': 'off', 67 | 'unicorn/prefer-array-flat': 'off', 68 | 'unicorn/prefer-array-some': 'off', 69 | 'unicorn/prefer-blob-reading-methods': 'off', 70 | 'unicorn/prefer-code-point': 'off', 71 | 'unicorn/prefer-date-now': 'off', 72 | 'unicorn/prefer-dom-node-append': 'off', 73 | 'unicorn/prefer-dom-node-dataset': 'off', 74 | 'unicorn/prefer-dom-node-remove': 'off', 75 | 'unicorn/prefer-event-target': 'off', 76 | 'unicorn/prefer-math-min-max': 'off', 77 | 'unicorn/prefer-math-trunc': 'off', 78 | 'unicorn/prefer-native-coercion-functions': 'off', 79 | 'unicorn/prefer-prototype-methods': 'off', 80 | 'unicorn/prefer-query-selector': 'off', 81 | 'unicorn/prefer-regexp-test': 'off', 82 | 'unicorn/prefer-string-replace-all': 'off', 83 | 'unicorn/prefer-string-slice': 'off', 84 | 'unicorn/prefer-type-error': 'off', 85 | 'unicorn/require-number-to-fixed-digits-argument': 'off', 86 | '@typescript-eslint/no-array-constructor': 'off', 87 | 'unicorn/no-negated-condition': 'off', 88 | '@typescript-eslint/no-redeclare': 'off', 89 | 'vitest/no-conditional-in-test': 'off', 90 | }; 91 | 92 | const suspiciousRules: Record = { 93 | 'block-scoped-var': 'off', 94 | 'no-unneeded-ternary': 'off', 95 | 'no-extend-native': 'off', 96 | 'no-new': 'off', 97 | 'no-unexpected-multiline': 'off', 98 | 'no-useless-concat': 'off', 99 | 'no-useless-constructor': 'off', 100 | 'import/no-unassigned-import': 'off', 101 | 'import/no-empty-named-blocks': 'off', 102 | 'import/no-absolute-path': 'off', 103 | 'import/no-duplicates': 'off', 104 | 'import/no-named-as-default': 'off', 105 | 'import/no-named-as-default-member': 'off', 106 | 'import/no-self-import': 'off', 107 | 'jest/no-commented-out-tests': 'off', 108 | 'promise/no-promise-in-callback': 'off', 109 | 'react/iframe-missing-sandbox': 'off', 110 | 'react/jsx-no-comment-textnodes': 'off', 111 | 'react/jsx-no-script-url': 'off', 112 | 'react/no-namespace': 'off', 113 | 'react/react-in-jsx-scope': 'off', 114 | 'react/style-prop-object': 'off', 115 | '@typescript-eslint/no-confusing-non-null-assertion': 'off', 116 | '@typescript-eslint/no-extraneous-class': 'off', 117 | '@typescript-eslint/no-unnecessary-type-constraint': 'off', 118 | 'unicorn/consistent-function-scoping': 'off', 119 | 'unicorn/no-instanceof-builtins': 'off', 120 | 'unicorn/no-accessor-recursion': 'off', 121 | 'unicorn/prefer-add-event-listener': 'off', 122 | 'unicorn/require-post-message-target-origin': 'off', 123 | '@typescript-eslint/no-useless-constructor': 'off', 124 | 'vitest/no-commented-out-tests': 'off', 125 | }; 126 | 127 | const styleRules: Record = { 128 | curly: 'off', 129 | 'default-case-last': 'off', 130 | 'default-param-last': 'off', 131 | 'func-style': 'off', 132 | 'func-names': 'off', 133 | 'grouped-accessor-pairs': 'off', 134 | 'guard-for-in': 'off', 135 | 'init-declarations': 'off', 136 | 'max-params': 'off', 137 | 'new-cap': 'off', 138 | 'no-duplicate-imports': 'off', 139 | 'no-extra-label': 'off', 140 | 'no-labels': 'off', 141 | 'no-lone-blocks': 'off', 142 | 'no-multi-assign': 'off', 143 | 'no-nested-ternary': 'off', 144 | 'no-continue': 'off', 145 | 'no-label-var': 'off', 146 | 'no-magic-numbers': 'off', 147 | 'no-multi-str': 'off', 148 | 'no-new-func': 'off', 149 | 'no-return-assign': 'off', 150 | 'no-script-url': 'off', 151 | 'no-template-curly-in-string': 'off', 152 | 'no-ternary': 'off', 153 | 'operator-assignment': 'off', 154 | 'prefer-promise-reject-errors': 'off', 155 | 'prefer-exponentiation-operator': 'off', 156 | 'prefer-numeric-literals': 'off', 157 | 'prefer-object-has-own': 'off', 158 | 'prefer-object-spread': 'off', 159 | 'prefer-rest-params': 'off', 160 | 'prefer-spread': 'off', 161 | 'sort-imports': 'off', 162 | 'sort-keys': 'off', 163 | 'vars-on-top': 'off', 164 | yoda: 'off', 165 | 'import/consistent-type-specifier-style': 'off', 166 | 'import/exports-last': 'off', 167 | 'import/first': 'off', 168 | 'import/group-exports': 'off', 169 | 'import/no-anonymous-default-export': 'off', 170 | 'import/no-mutable-exports': 'off', 171 | 'import/no-named-default': 'off', 172 | 'import/no-namespace': 'off', 173 | 'jest/consistent-test-it': 'off', 174 | 'jest/max-expects': 'off', 175 | 'jest/max-nested-describe': 'off', 176 | 'jest/no-alias-methods': 'off', 177 | 'jest/no-confusing-set-timeout': 'off', 178 | 'jest/no-deprecated-functions': 'off', 179 | 'jest/no-done-callback': 'off', 180 | 'jest/no-duplicate-hooks': 'off', 181 | 'jest/no-hooks': 'off', 182 | 'jest/no-identical-title': 'off', 183 | 'jest/no-interpolation-in-snapshots': 'off', 184 | 'jest/no-jasmine-globals': 'off', 185 | 'jest/no-large-snapshots': 'off', 186 | 'jest/no-mocks-import': 'off', 187 | 'jest/no-restricted-jest-methods': 'off', 188 | 'jest/no-restricted-matchers': 'off', 189 | 'jest/no-test-prefixes': 'off', 190 | 'jest/no-test-return-statement': 'off', 191 | 'jest/no-untyped-mock-factory': 'off', 192 | 'jest/prefer-each': 'off', 193 | 'jest/prefer-called-with': 'off', 194 | 'jest/prefer-comparison-matcher': 'off', 195 | 'jest/prefer-equality-matcher': 'off', 196 | 'jest/prefer-expect-resolves': 'off', 197 | 'jest/prefer-hooks-in-order': 'off', 198 | 'jest/prefer-hooks-on-top': 'off', 199 | 'jest/prefer-jest-mocked': 'off', 200 | 'jest/prefer-lowercase-title': 'off', 201 | 'jest/prefer-mock-promise-shorthand': 'off', 202 | 'jest/prefer-spy-on': 'off', 203 | 'jest/prefer-strict-equal': 'off', 204 | 'jest/prefer-to-be': 'off', 205 | 'jest/prefer-to-contain': 'off', 206 | 'jest/prefer-to-have-length': 'off', 207 | 'jest/prefer-todo': 'off', 208 | 'jest/require-hook': 'off', 209 | 'jest/require-top-level-describe': 'off', 210 | 'node/no-exports-assign': 'off', 211 | 'promise/avoid-new': 'off', 212 | 'promise/no-return-wrap': 'off', 213 | 'promise/no-nesting': 'off', 214 | 'promise/param-names': 'off', 215 | 'promise/prefer-catch': 'off', 216 | 'promise/prefer-await-to-callbacks': 'off', 217 | 'promise/prefer-await-to-then': 'off', 218 | 'react/jsx-boolean-value': 'off', 219 | 'react/jsx-curly-brace-presence': 'off', 220 | 'react/no-set-state': 'off', 221 | 'react/prefer-es6-class': 'off', 222 | 'react/self-closing-comp': 'off', 223 | '@typescript-eslint/adjacent-overload-signatures': 'off', 224 | '@typescript-eslint/array-type': 'off', 225 | '@typescript-eslint/ban-tslint-comment': 'off', 226 | '@typescript-eslint/consistent-generic-constructors': 'off', 227 | '@typescript-eslint/consistent-indexed-object-style': 'off', 228 | '@typescript-eslint/consistent-type-definitions': 'off', 229 | '@typescript-eslint/consistent-type-imports': 'off', 230 | '@typescript-eslint/no-inferrable-types': 'off', 231 | '@typescript-eslint/no-empty-interface': 'off', 232 | '@typescript-eslint/prefer-for-of': 'off', 233 | '@typescript-eslint/prefer-function-type': 'off', 234 | '@typescript-eslint/prefer-namespace-keyword': 'off', 235 | 'unicorn/catch-error-name': 'off', 236 | 'unicorn/consistent-date-clone': 'off', 237 | 'unicorn/consistent-existence-index-check': 'off', 238 | 'unicorn/empty-brace-spaces': 'off', 239 | 'unicorn/error-message': 'off', 240 | 'unicorn/filename-case': 'off', 241 | 'unicorn/no-array-method-this-argument': 'off', 242 | 'unicorn/no-await-expression-member': 'off', 243 | 'unicorn/no-console-spaces': 'off', 244 | 'unicorn/no-null': 'off', 245 | 'unicorn/no-unreadable-array-destructuring': 'off', 246 | 'unicorn/no-zero-fractions': 'off', 247 | 'unicorn/number-literal-case': 'off', 248 | 'unicorn/numeric-separators-style': 'off', 249 | 'unicorn/prefer-global-this': 'off', 250 | 'unicorn/prefer-object-from-entries': 'off', 251 | 'unicorn/prefer-array-index-of': 'off', 252 | 'unicorn/prefer-spread': 'off', 253 | 'unicorn/prefer-array-flat-map': 'off', 254 | 'unicorn/prefer-dom-node-text-content': 'off', 255 | 'unicorn/prefer-includes': 'off', 256 | 'unicorn/prefer-logical-operator-over-ternary': 'off', 257 | 'unicorn/prefer-modern-dom-apis': 'off', 258 | 'unicorn/prefer-negative-index': 'off', 259 | 'unicorn/prefer-optional-catch-binding': 'off', 260 | 'unicorn/prefer-reflect-apply': 'off', 261 | 'unicorn/prefer-string-raw': 'off', 262 | 'unicorn/prefer-string-trim-start-end': 'off', 263 | 'unicorn/prefer-structured-clone': 'off', 264 | 'unicorn/require-array-join-separator': 'off', 265 | 'unicorn/switch-case-braces': 'off', 266 | 'unicorn/text-encoding-identifier-case': 'off', 267 | 'unicorn/throw-new-error': 'off', 268 | 'vitest/no-import-node-test': 'off', 269 | 'vitest/prefer-to-be-falsy': 'off', 270 | 'vitest/prefer-to-be-object': 'off', 271 | 'vitest/prefer-to-be-truthy': 'off', 272 | '@typescript-eslint/default-param-last': 'off', 273 | '@typescript-eslint/init-declarations': 'off', 274 | '@typescript-eslint/max-params': 'off', 275 | '@typescript-eslint/no-magic-numbers': 'off', 276 | 'vitest/consistent-test-it': 'off', 277 | 'vitest/max-expects': 'off', 278 | 'vitest/max-nested-describe': 'off', 279 | 'vitest/no-alias-methods': 'off', 280 | 'vitest/no-duplicate-hooks': 'off', 281 | 'vitest/no-hooks': 'off', 282 | 'vitest/no-identical-title': 'off', 283 | 'vitest/no-interpolation-in-snapshots': 'off', 284 | 'vitest/no-restricted-jest-methods': 'off', 285 | 'vitest/no-restricted-matchers': 'off', 286 | 'vitest/no-test-prefixes': 'off', 287 | 'vitest/no-test-return-statement': 'off', 288 | 'vitest/prefer-each': 'off', 289 | 'vitest/prefer-comparison-matcher': 'off', 290 | 'vitest/prefer-equality-matcher': 'off', 291 | 'vitest/prefer-expect-resolves': 'off', 292 | 'vitest/prefer-hooks-in-order': 'off', 293 | 'vitest/prefer-hooks-on-top': 'off', 294 | 'vitest/prefer-lowercase-title': 'off', 295 | 'vitest/prefer-mock-promise-shorthand': 'off', 296 | 'vitest/prefer-strict-equal': 'off', 297 | 'vitest/prefer-to-have-length': 'off', 298 | 'vitest/prefer-todo': 'off', 299 | 'vitest/require-top-level-describe': 'off', 300 | }; 301 | 302 | const restrictionRules: Record = { 303 | 'default-case': 'off', 304 | 'no-alert': 'off', 305 | 'no-bitwise': 'off', 306 | 'no-restricted-imports': 'off', 307 | 'no-console': 'off', 308 | 'no-div-regex': 'off', 309 | 'no-empty-function': 'off', 310 | 'no-empty': 'off', 311 | 'no-eq-null': 'off', 312 | 'no-iterator': 'off', 313 | 'no-plusplus': 'off', 314 | 'no-proto': 'off', 315 | 'no-regex-spaces': 'off', 316 | 'no-restricted-globals': 'off', 317 | 'no-undefined': 'off', 318 | 'no-unused-expressions': 'off', 319 | 'no-var': 'off', 320 | 'no-void': 'off', 321 | 'unicode-bom': 'off', 322 | 'import/no-amd': 'off', 323 | 'import/no-commonjs': 'off', 324 | 'import/no-cycle': 'off', 325 | 'import/no-default-export': 'off', 326 | 'import/no-dynamic-require': 'off', 327 | 'import/no-webpack-loader-syntax': 'off', 328 | 'import/unambiguous': 'off', 329 | 'jsdoc/check-access': 'off', 330 | 'jsdoc/empty-tags': 'off', 331 | 'jsx-a11y/anchor-ambiguous-text': 'off', 332 | 'node/no-new-require': 'off', 333 | 'promise/catch-or-return': 'off', 334 | 'promise/spec-only': 'off', 335 | 'react/button-has-type': 'off', 336 | 'react/forbid-elements': 'off', 337 | 'react/jsx-filename-extension': 'off', 338 | 'react/no-danger': 'off', 339 | 'react/no-unknown-property': 'off', 340 | '@typescript-eslint/explicit-function-return-type': 'off', 341 | '@typescript-eslint/no-dynamic-delete': 'off', 342 | '@typescript-eslint/no-empty-object-type': 'off', 343 | '@typescript-eslint/no-explicit-any': 'off', 344 | '@typescript-eslint/no-import-type-side-effects': 'off', 345 | '@typescript-eslint/no-namespace': 'off', 346 | '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'off', 347 | '@typescript-eslint/no-non-null-assertion': 'off', 348 | '@typescript-eslint/no-require-imports': 'off', 349 | '@typescript-eslint/no-var-requires': 'off', 350 | '@typescript-eslint/prefer-literal-enum-member': 'off', 351 | 'unicorn/no-abusive-eslint-disable': 'off', 352 | 'unicorn/no-anonymous-default-export': 'off', 353 | 'unicorn/no-array-for-each': 'off', 354 | 'unicorn/no-array-reduce': 'off', 355 | 'unicorn/no-document-cookie': 'off', 356 | 'unicorn/no-length-as-slice-end': 'off', 357 | 'unicorn/no-magic-array-flat-depth': 'off', 358 | 'unicorn/no-nested-ternary': 'off', 359 | 'unicorn/no-process-exit': 'off', 360 | 'unicorn/prefer-modern-math-apis': 'off', 361 | 'unicorn/prefer-node-protocol': 'off', 362 | 'unicorn/prefer-number-properties': 'off', 363 | '@typescript-eslint/no-restricted-imports': 'off', 364 | '@typescript-eslint/no-empty-function': 'off', 365 | '@typescript-eslint/no-unused-expressions': 'off', 366 | }; 367 | 368 | const correctnessRules: Record = { 369 | 'for-direction': 'off', 370 | 'no-async-promise-executor': 'off', 371 | 'no-caller': 'off', 372 | 'no-class-assign': 'off', 373 | 'no-useless-backreference': 'off', 374 | 'no-compare-neg-zero': 'off', 375 | 'no-cond-assign': 'off', 376 | 'no-const-assign': 'off', 377 | 'no-constant-binary-expression': 'off', 378 | 'no-constant-condition': 'off', 379 | 'no-control-regex': 'off', 380 | 'no-debugger': 'off', 381 | 'no-delete-var': 'off', 382 | 'no-dupe-class-members': 'off', 383 | 'no-dupe-else-if': 'off', 384 | 'no-dupe-keys': 'off', 385 | 'no-duplicate-case': 'off', 386 | 'no-empty-character-class': 'off', 387 | 'no-empty-pattern': 'off', 388 | 'no-empty-static-block': 'off', 389 | 'no-eval': 'off', 390 | 'no-ex-assign': 'off', 391 | 'no-extra-boolean-cast': 'off', 392 | 'no-func-assign': 'off', 393 | 'no-global-assign': 'off', 394 | 'no-import-assign': 'off', 395 | 'no-invalid-regexp': 'off', 396 | 'no-irregular-whitespace': 'off', 397 | 'no-loss-of-precision': 'off', 398 | 'no-new-native-nonconstructor': 'off', 399 | 'no-nonoctal-decimal-escape': 'off', 400 | 'no-obj-calls': 'off', 401 | 'no-self-assign': 'off', 402 | 'no-setter-return': 'off', 403 | 'no-shadow-restricted-names': 'off', 404 | 'no-sparse-arrays': 'off', 405 | 'no-this-before-super': 'off', 406 | 'no-unsafe-finally': 'off', 407 | 'no-unsafe-negation': 'off', 408 | 'no-unsafe-optional-chaining': 'off', 409 | 'no-unused-labels': 'off', 410 | 'no-unused-private-class-members': 'off', 411 | 'no-unused-vars': 'off', 412 | 'no-useless-catch': 'off', 413 | 'no-useless-escape': 'off', 414 | 'no-useless-rename': 'off', 415 | 'no-with': 'off', 416 | 'require-yield': 'off', 417 | 'use-isnan': 'off', 418 | 'valid-typeof': 'off', 419 | 'import/default': 'off', 420 | 'import/namespace': 'off', 421 | 'jest/expect-expect': 'off', 422 | 'jest/no-conditional-expect': 'off', 423 | 'jest/no-disabled-tests': 'off', 424 | 'jest/no-export': 'off', 425 | 'jest/no-focused-tests': 'off', 426 | 'jest/no-standalone-expect': 'off', 427 | 'jest/require-to-throw-message': 'off', 428 | 'jest/valid-describe-callback': 'off', 429 | 'jest/valid-expect': 'off', 430 | 'jest/valid-title': 'off', 431 | 'jsdoc/check-property-names': 'off', 432 | 'jsdoc/check-tag-names': 'off', 433 | 'jsdoc/implements-on-classes': 'off', 434 | 'jsdoc/no-defaults': 'off', 435 | 'jsdoc/require-property': 'off', 436 | 'jsdoc/require-property-description': 'off', 437 | 'jsdoc/require-property-name': 'off', 438 | 'jsdoc/require-property-type': 'off', 439 | 'jsdoc/require-yields': 'off', 440 | 'jsx-a11y/alt-text': 'off', 441 | 'jsx-a11y/anchor-has-content': 'off', 442 | 'jsx-a11y/anchor-is-valid': 'off', 443 | 'jsx-a11y/aria-activedescendant-has-tabindex': 'off', 444 | 'jsx-a11y/aria-props': 'off', 445 | 'jsx-a11y/aria-role': 'off', 446 | 'jsx-a11y/aria-unsupported-elements': 'off', 447 | 'jsx-a11y/autocomplete-valid': 'off', 448 | 'jsx-a11y/click-events-have-key-events': 'off', 449 | 'jsx-a11y/heading-has-content': 'off', 450 | 'jsx-a11y/html-has-lang': 'off', 451 | 'jsx-a11y/iframe-has-title': 'off', 452 | 'jsx-a11y/img-redundant-alt': 'off', 453 | 'jsx-a11y/label-has-associated-control': 'off', 454 | 'jsx-a11y/lang': 'off', 455 | 'jsx-a11y/media-has-caption': 'off', 456 | 'jsx-a11y/mouse-events-have-key-events': 'off', 457 | 'jsx-a11y/no-noninteractive-tabindex': 'off', 458 | 'jsx-a11y/no-access-key': 'off', 459 | 'jsx-a11y/no-aria-hidden-on-focusable': 'off', 460 | 'jsx-a11y/no-autofocus': 'off', 461 | 'jsx-a11y/no-distracting-elements': 'off', 462 | 'jsx-a11y/no-redundant-roles': 'off', 463 | 'jsx-a11y/prefer-tag-over-role': 'off', 464 | 'jsx-a11y/role-has-required-aria-props': 'off', 465 | 'jsx-a11y/role-supports-aria-props': 'off', 466 | 'jsx-a11y/scope': 'off', 467 | 'jsx-a11y/tabindex-no-positive': 'off', 468 | '@next/next/google-font-display': 'off', 469 | '@next/next/google-font-preconnect': 'off', 470 | '@next/next/inline-script-id': 'off', 471 | '@next/next/next-script-for-ga': 'off', 472 | '@next/next/no-assign-module-variable': 'off', 473 | '@next/next/no-async-client-component': 'off', 474 | '@next/next/no-before-interactive-script-outside-document': 'off', 475 | '@next/next/no-css-tags': 'off', 476 | '@next/next/no-document-import-in-page': 'off', 477 | '@next/next/no-duplicate-head': 'off', 478 | '@next/next/no-head-element': 'off', 479 | '@next/next/no-head-import-in-document': 'off', 480 | '@next/next/no-img-element': 'off', 481 | '@next/next/no-page-custom-font': 'off', 482 | '@next/next/no-script-component-in-head': 'off', 483 | '@next/next/no-styled-jsx-in-document': 'off', 484 | '@next/next/no-sync-scripts': 'off', 485 | '@next/next/no-title-in-document-head': 'off', 486 | '@next/next/no-typos': 'off', 487 | '@next/next/no-unwanted-polyfillio': 'off', 488 | 'promise/no-callback-in-promise': 'off', 489 | 'promise/no-new-statics': 'off', 490 | 'promise/valid-params': 'off', 491 | 'react-hooks/exhaustive-deps': 'off', 492 | 'react/forward-ref-uses-ref': 'off', 493 | 'react/jsx-key': 'off', 494 | 'react/jsx-no-duplicate-props': 'off', 495 | 'react/jsx-no-target-blank': 'off', 496 | 'react/jsx-no-undef': 'off', 497 | 'react/jsx-props-no-spread-multi': 'off', 498 | 'react/no-children-prop': 'off', 499 | 'react/no-danger-with-children': 'off', 500 | 'react/no-direct-mutation-state': 'off', 501 | 'react/no-find-dom-node': 'off', 502 | 'react/no-is-mounted': 'off', 503 | 'react/no-render-return-value': 'off', 504 | 'react/no-string-refs': 'off', 505 | 'react/void-dom-elements-no-children': 'off', 506 | '@typescript-eslint/no-duplicate-enum-values': 'off', 507 | '@typescript-eslint/no-extra-non-null-assertion': 'off', 508 | '@typescript-eslint/no-misused-new': 'off', 509 | '@typescript-eslint/no-non-null-asserted-optional-chain': 'off', 510 | '@typescript-eslint/no-this-alias': 'off', 511 | '@typescript-eslint/no-unnecessary-parameter-property-assignment': 'off', 512 | '@typescript-eslint/no-unsafe-declaration-merging': 'off', 513 | '@typescript-eslint/no-useless-empty-export': 'off', 514 | '@typescript-eslint/no-wrapper-object-types': 'off', 515 | '@typescript-eslint/prefer-as-const': 'off', 516 | '@typescript-eslint/triple-slash-reference': 'off', 517 | 'unicorn/no-invalid-fetch-options': 'off', 518 | 'unicorn/no-await-in-promise-methods': 'off', 519 | 'unicorn/no-empty-file': 'off', 520 | 'unicorn/no-invalid-remove-event-listener': 'off', 521 | 'unicorn/no-new-array': 'off', 522 | 'unicorn/no-single-promise-in-promise-methods': 'off', 523 | 'unicorn/no-thenable': 'off', 524 | 'unicorn/no-unnecessary-await': 'off', 525 | 'unicorn/no-useless-fallback-in-spread': 'off', 526 | 'unicorn/no-useless-length-check': 'off', 527 | 'unicorn/no-useless-spread': 'off', 528 | 'unicorn/prefer-set-size': 'off', 529 | 'unicorn/prefer-string-starts-ends-with': 'off', 530 | 'vitest/no-conditional-tests': 'off', 531 | 'vitest/require-local-test-context-for-concurrent-snapshots': 'off', 532 | '@typescript-eslint/no-dupe-class-members': 'off', 533 | '@typescript-eslint/no-loss-of-precision': 'off', 534 | '@typescript-eslint/no-unused-vars': 'off', 535 | 'vitest/expect-expect': 'off', 536 | 'vitest/no-conditional-expect': 'off', 537 | 'vitest/no-disabled-tests': 'off', 538 | 'vitest/no-focused-tests': 'off', 539 | 'vitest/no-standalone-expect': 'off', 540 | 'vitest/require-to-throw-message': 'off', 541 | 'vitest/valid-describe-callback': 'off', 542 | 'vitest/valid-expect': 'off', 543 | }; 544 | 545 | const perfRules: Record = { 546 | 'no-await-in-loop': 'off', 547 | 'no-useless-call': 'off', 548 | 'react/no-array-index-key': 'off', 549 | 'react-perf/jsx-no-jsx-as-prop': 'off', 550 | 'react-perf/jsx-no-new-array-as-prop': 'off', 551 | 'react-perf/jsx-no-new-function-as-prop': 'off', 552 | 'react-perf/jsx-no-new-object-as-prop': 'off', 553 | 'unicorn/prefer-array-find': 'off', 554 | 'unicorn/prefer-set-has': 'off', 555 | }; 556 | 557 | export { 558 | pedanticRules, 559 | suspiciousRules, 560 | styleRules, 561 | restrictionRules, 562 | correctnessRules, 563 | perfRules, 564 | }; 565 | -------------------------------------------------------------------------------- /src/generated/rules-by-scope.ts: -------------------------------------------------------------------------------- 1 | // These rules are automatically generated by scripts/generate-rules.ts 2 | 3 | const eslintRules: Record = { 4 | 'array-callback-return': 'off', 5 | 'block-scoped-var': 'off', 6 | curly: 'off', 7 | 'default-case': 'off', 8 | 'default-case-last': 'off', 9 | 'default-param-last': 'off', 10 | eqeqeq: 'off', 11 | 'for-direction': 'off', 12 | 'func-style': 'off', 13 | 'func-names': 'off', 14 | 'grouped-accessor-pairs': 'off', 15 | 'guard-for-in': 'off', 16 | 'init-declarations': 'off', 17 | 'max-classes-per-file': 'off', 18 | 'max-depth': 'off', 19 | 'max-lines-per-function': 'off', 20 | 'max-lines': 'off', 21 | 'max-nested-callbacks': 'off', 22 | 'max-params': 'off', 23 | 'new-cap': 'off', 24 | 'no-alert': 'off', 25 | 'no-array-constructor': 'off', 26 | 'no-async-promise-executor': 'off', 27 | 'no-await-in-loop': 'off', 28 | 'no-bitwise': 'off', 29 | 'no-caller': 'off', 30 | 'no-case-declarations': 'off', 31 | 'no-class-assign': 'off', 32 | 'no-duplicate-imports': 'off', 33 | 'no-extra-label': 'off', 34 | 'no-labels': 'off', 35 | 'no-lone-blocks': 'off', 36 | 'no-lonely-if': 'off', 37 | 'no-multi-assign': 'off', 38 | 'no-nested-ternary': 'off', 39 | 'no-object-constructor': 'off', 40 | 'no-restricted-imports': 'off', 41 | 'no-unneeded-ternary': 'off', 42 | 'no-useless-backreference': 'off', 43 | 'no-useless-call': 'off', 44 | 'no-compare-neg-zero': 'off', 45 | 'no-cond-assign': 'off', 46 | 'no-console': 'off', 47 | 'no-const-assign': 'off', 48 | 'no-constant-binary-expression': 'off', 49 | 'no-constant-condition': 'off', 50 | 'no-constructor-return': 'off', 51 | 'no-continue': 'off', 52 | 'no-control-regex': 'off', 53 | 'no-debugger': 'off', 54 | 'no-delete-var': 'off', 55 | 'no-div-regex': 'off', 56 | 'no-dupe-class-members': 'off', 57 | 'no-dupe-else-if': 'off', 58 | 'no-dupe-keys': 'off', 59 | 'no-duplicate-case': 'off', 60 | 'no-else-return': 'off', 61 | 'no-empty-character-class': 'off', 62 | 'no-empty-function': 'off', 63 | 'no-empty-pattern': 'off', 64 | 'no-empty-static-block': 'off', 65 | 'no-empty': 'off', 66 | 'no-eq-null': 'off', 67 | 'no-eval': 'off', 68 | 'no-ex-assign': 'off', 69 | 'no-extend-native': 'off', 70 | 'no-extra-boolean-cast': 'off', 71 | 'no-fallthrough': 'off', 72 | 'no-func-assign': 'off', 73 | 'no-global-assign': 'off', 74 | 'no-import-assign': 'off', 75 | 'no-inner-declarations': 'off', 76 | 'no-invalid-regexp': 'off', 77 | 'no-irregular-whitespace': 'off', 78 | 'no-iterator': 'off', 79 | 'no-label-var': 'off', 80 | 'no-loss-of-precision': 'off', 81 | 'no-magic-numbers': 'off', 82 | 'no-negated-condition': 'off', 83 | 'no-multi-str': 'off', 84 | 'no-new-func': 'off', 85 | 'no-new-native-nonconstructor': 'off', 86 | 'no-new-wrappers': 'off', 87 | 'no-new': 'off', 88 | 'no-nonoctal-decimal-escape': 'off', 89 | 'no-obj-calls': 'off', 90 | 'no-plusplus': 'off', 91 | 'no-proto': 'off', 92 | 'no-prototype-builtins': 'off', 93 | 'no-redeclare': 'off', 94 | 'no-regex-spaces': 'off', 95 | 'no-restricted-globals': 'off', 96 | 'no-return-assign': 'off', 97 | 'no-script-url': 'off', 98 | 'no-self-assign': 'off', 99 | 'no-self-compare': 'off', 100 | 'no-setter-return': 'off', 101 | 'no-shadow-restricted-names': 'off', 102 | 'no-sparse-arrays': 'off', 103 | 'no-template-curly-in-string': 'off', 104 | 'no-ternary': 'off', 105 | 'no-this-before-super': 'off', 106 | 'no-throw-literal': 'off', 107 | 'no-undefined': 'off', 108 | 'no-unexpected-multiline': 'off', 109 | 'no-unsafe-finally': 'off', 110 | 'no-unsafe-negation': 'off', 111 | 'no-unsafe-optional-chaining': 'off', 112 | 'no-unused-expressions': 'off', 113 | 'no-unused-labels': 'off', 114 | 'no-unused-private-class-members': 'off', 115 | 'no-unused-vars': 'off', 116 | 'no-useless-catch': 'off', 117 | 'no-useless-concat': 'off', 118 | 'no-useless-constructor': 'off', 119 | 'no-useless-escape': 'off', 120 | 'no-useless-rename': 'off', 121 | 'no-var': 'off', 122 | 'no-void': 'off', 123 | 'no-with': 'off', 124 | 'operator-assignment': 'off', 125 | 'prefer-promise-reject-errors': 'off', 126 | 'prefer-exponentiation-operator': 'off', 127 | 'prefer-numeric-literals': 'off', 128 | 'prefer-object-has-own': 'off', 129 | 'prefer-object-spread': 'off', 130 | 'prefer-rest-params': 'off', 131 | 'prefer-spread': 'off', 132 | radix: 'off', 133 | 'require-await': 'off', 134 | 'require-yield': 'off', 135 | 'sort-imports': 'off', 136 | 'sort-keys': 'off', 137 | 'sort-vars': 'off', 138 | 'symbol-description': 'off', 139 | 'unicode-bom': 'off', 140 | 'use-isnan': 'off', 141 | 'valid-typeof': 'off', 142 | 'vars-on-top': 'off', 143 | yoda: 'off', 144 | }; 145 | 146 | const importRules: Record = { 147 | 'import/consistent-type-specifier-style': 'off', 148 | 'import/default': 'off', 149 | 'import/exports-last': 'off', 150 | 'import/first': 'off', 151 | 'import/group-exports': 'off', 152 | 'import/no-unassigned-import': 'off', 153 | 'import/no-empty-named-blocks': 'off', 154 | 'import/no-anonymous-default-export': 'off', 155 | 'import/no-absolute-path': 'off', 156 | 'import/no-mutable-exports': 'off', 157 | 'import/no-named-default': 'off', 158 | 'import/no-namespace': 'off', 159 | 'import/max-dependencies': 'off', 160 | 'import/namespace': 'off', 161 | 'import/no-amd': 'off', 162 | 'import/no-commonjs': 'off', 163 | 'import/no-cycle': 'off', 164 | 'import/no-default-export': 'off', 165 | 'import/no-duplicates': 'off', 166 | 'import/no-dynamic-require': 'off', 167 | 'import/no-named-as-default': 'off', 168 | 'import/no-named-as-default-member': 'off', 169 | 'import/no-self-import': 'off', 170 | 'import/no-webpack-loader-syntax': 'off', 171 | 'import/unambiguous': 'off', 172 | }; 173 | 174 | const jestRules: Record = { 175 | 'jest/consistent-test-it': 'off', 176 | 'jest/expect-expect': 'off', 177 | 'jest/max-expects': 'off', 178 | 'jest/max-nested-describe': 'off', 179 | 'jest/no-alias-methods': 'off', 180 | 'jest/no-commented-out-tests': 'off', 181 | 'jest/no-conditional-expect': 'off', 182 | 'jest/no-conditional-in-test': 'off', 183 | 'jest/no-confusing-set-timeout': 'off', 184 | 'jest/no-deprecated-functions': 'off', 185 | 'jest/no-disabled-tests': 'off', 186 | 'jest/no-done-callback': 'off', 187 | 'jest/no-duplicate-hooks': 'off', 188 | 'jest/no-export': 'off', 189 | 'jest/no-focused-tests': 'off', 190 | 'jest/no-hooks': 'off', 191 | 'jest/no-identical-title': 'off', 192 | 'jest/no-interpolation-in-snapshots': 'off', 193 | 'jest/no-jasmine-globals': 'off', 194 | 'jest/no-large-snapshots': 'off', 195 | 'jest/no-mocks-import': 'off', 196 | 'jest/no-restricted-jest-methods': 'off', 197 | 'jest/no-restricted-matchers': 'off', 198 | 'jest/no-standalone-expect': 'off', 199 | 'jest/no-test-prefixes': 'off', 200 | 'jest/no-test-return-statement': 'off', 201 | 'jest/no-untyped-mock-factory': 'off', 202 | 'jest/prefer-each': 'off', 203 | 'jest/prefer-called-with': 'off', 204 | 'jest/prefer-comparison-matcher': 'off', 205 | 'jest/prefer-equality-matcher': 'off', 206 | 'jest/prefer-expect-resolves': 'off', 207 | 'jest/prefer-hooks-in-order': 'off', 208 | 'jest/prefer-hooks-on-top': 'off', 209 | 'jest/prefer-jest-mocked': 'off', 210 | 'jest/prefer-lowercase-title': 'off', 211 | 'jest/prefer-mock-promise-shorthand': 'off', 212 | 'jest/prefer-spy-on': 'off', 213 | 'jest/prefer-strict-equal': 'off', 214 | 'jest/prefer-to-be': 'off', 215 | 'jest/prefer-to-contain': 'off', 216 | 'jest/prefer-to-have-length': 'off', 217 | 'jest/prefer-todo': 'off', 218 | 'jest/require-hook': 'off', 219 | 'jest/require-to-throw-message': 'off', 220 | 'jest/require-top-level-describe': 'off', 221 | 'jest/valid-describe-callback': 'off', 222 | 'jest/valid-expect': 'off', 223 | 'jest/valid-title': 'off', 224 | }; 225 | 226 | const jsdocRules: Record = { 227 | 'jsdoc/check-access': 'off', 228 | 'jsdoc/check-property-names': 'off', 229 | 'jsdoc/check-tag-names': 'off', 230 | 'jsdoc/empty-tags': 'off', 231 | 'jsdoc/implements-on-classes': 'off', 232 | 'jsdoc/no-defaults': 'off', 233 | 'jsdoc/require-param': 'off', 234 | 'jsdoc/require-param-description': 'off', 235 | 'jsdoc/require-param-name': 'off', 236 | 'jsdoc/require-param-type': 'off', 237 | 'jsdoc/require-property': 'off', 238 | 'jsdoc/require-property-description': 'off', 239 | 'jsdoc/require-property-name': 'off', 240 | 'jsdoc/require-property-type': 'off', 241 | 'jsdoc/require-returns': 'off', 242 | 'jsdoc/require-returns-description': 'off', 243 | 'jsdoc/require-returns-type': 'off', 244 | 'jsdoc/require-yields': 'off', 245 | }; 246 | 247 | const jsxA11yRules: Record = { 248 | 'jsx-a11y/alt-text': 'off', 249 | 'jsx-a11y/anchor-has-content': 'off', 250 | 'jsx-a11y/anchor-is-valid': 'off', 251 | 'jsx-a11y/aria-activedescendant-has-tabindex': 'off', 252 | 'jsx-a11y/aria-props': 'off', 253 | 'jsx-a11y/aria-role': 'off', 254 | 'jsx-a11y/aria-unsupported-elements': 'off', 255 | 'jsx-a11y/autocomplete-valid': 'off', 256 | 'jsx-a11y/click-events-have-key-events': 'off', 257 | 'jsx-a11y/heading-has-content': 'off', 258 | 'jsx-a11y/html-has-lang': 'off', 259 | 'jsx-a11y/iframe-has-title': 'off', 260 | 'jsx-a11y/img-redundant-alt': 'off', 261 | 'jsx-a11y/label-has-associated-control': 'off', 262 | 'jsx-a11y/lang': 'off', 263 | 'jsx-a11y/media-has-caption': 'off', 264 | 'jsx-a11y/mouse-events-have-key-events': 'off', 265 | 'jsx-a11y/no-noninteractive-tabindex': 'off', 266 | 'jsx-a11y/no-access-key': 'off', 267 | 'jsx-a11y/no-aria-hidden-on-focusable': 'off', 268 | 'jsx-a11y/no-autofocus': 'off', 269 | 'jsx-a11y/no-distracting-elements': 'off', 270 | 'jsx-a11y/no-redundant-roles': 'off', 271 | 'jsx-a11y/prefer-tag-over-role': 'off', 272 | 'jsx-a11y/role-has-required-aria-props': 'off', 273 | 'jsx-a11y/role-supports-aria-props': 'off', 274 | 'jsx-a11y/scope': 'off', 275 | 'jsx-a11y/tabindex-no-positive': 'off', 276 | 'jsx-a11y/anchor-ambiguous-text': 'off', 277 | }; 278 | 279 | const nextjsRules: Record = { 280 | '@next/next/google-font-display': 'off', 281 | '@next/next/google-font-preconnect': 'off', 282 | '@next/next/inline-script-id': 'off', 283 | '@next/next/next-script-for-ga': 'off', 284 | '@next/next/no-assign-module-variable': 'off', 285 | '@next/next/no-async-client-component': 'off', 286 | '@next/next/no-before-interactive-script-outside-document': 'off', 287 | '@next/next/no-css-tags': 'off', 288 | '@next/next/no-document-import-in-page': 'off', 289 | '@next/next/no-duplicate-head': 'off', 290 | '@next/next/no-head-element': 'off', 291 | '@next/next/no-head-import-in-document': 'off', 292 | '@next/next/no-img-element': 'off', 293 | '@next/next/no-page-custom-font': 'off', 294 | '@next/next/no-script-component-in-head': 'off', 295 | '@next/next/no-styled-jsx-in-document': 'off', 296 | '@next/next/no-sync-scripts': 'off', 297 | '@next/next/no-title-in-document-head': 'off', 298 | '@next/next/no-typos': 'off', 299 | '@next/next/no-unwanted-polyfillio': 'off', 300 | }; 301 | 302 | const nodeRules: Record = { 303 | 'node/no-exports-assign': 'off', 304 | 'node/no-new-require': 'off', 305 | }; 306 | 307 | const promiseRules: Record = { 308 | 'promise/avoid-new': 'off', 309 | 'promise/catch-or-return': 'off', 310 | 'promise/no-return-wrap': 'off', 311 | 'promise/no-nesting': 'off', 312 | 'promise/no-promise-in-callback': 'off', 313 | 'promise/no-callback-in-promise': 'off', 314 | 'promise/no-new-statics': 'off', 315 | 'promise/param-names': 'off', 316 | 'promise/prefer-catch': 'off', 317 | 'promise/prefer-await-to-callbacks': 'off', 318 | 'promise/prefer-await-to-then': 'off', 319 | 'promise/spec-only': 'off', 320 | 'promise/valid-params': 'off', 321 | }; 322 | 323 | const reactRules: Record = { 324 | 'react/button-has-type': 'off', 325 | 'react/checked-requires-onchange-or-readonly': 'off', 326 | 'react/forbid-elements': 'off', 327 | 'react/forward-ref-uses-ref': 'off', 328 | 'react/iframe-missing-sandbox': 'off', 329 | 'react/jsx-filename-extension': 'off', 330 | 'react/jsx-boolean-value': 'off', 331 | 'react/jsx-curly-brace-presence': 'off', 332 | 'react/jsx-key': 'off', 333 | 'react/jsx-no-comment-textnodes': 'off', 334 | 'react/jsx-no-duplicate-props': 'off', 335 | 'react/jsx-no-script-url': 'off', 336 | 'react/jsx-no-target-blank': 'off', 337 | 'react/jsx-no-undef': 'off', 338 | 'react/jsx-no-useless-fragment': 'off', 339 | 'react/jsx-props-no-spread-multi': 'off', 340 | 'react/no-namespace': 'off', 341 | 'react/no-array-index-key': 'off', 342 | 'react/no-children-prop': 'off', 343 | 'react/no-danger-with-children': 'off', 344 | 'react/no-danger': 'off', 345 | 'react/no-direct-mutation-state': 'off', 346 | 'react/no-find-dom-node': 'off', 347 | 'react/no-is-mounted': 'off', 348 | 'react/no-render-return-value': 'off', 349 | 'react/no-set-state': 'off', 350 | 'react/no-string-refs': 'off', 351 | 'react/no-unescaped-entities': 'off', 352 | 'react/no-unknown-property': 'off', 353 | 'react/prefer-es6-class': 'off', 354 | 'react/react-in-jsx-scope': 'off', 355 | 'react/self-closing-comp': 'off', 356 | 'react/style-prop-object': 'off', 357 | 'react/void-dom-elements-no-children': 'off', 358 | }; 359 | 360 | const reactHooksRules: Record = { 361 | 'react-hooks/exhaustive-deps': 'off', 362 | 'react-hooks/rules-of-hooks': 'off', 363 | }; 364 | 365 | const reactPerfRules: Record = { 366 | 'react-perf/jsx-no-jsx-as-prop': 'off', 367 | 'react-perf/jsx-no-new-array-as-prop': 'off', 368 | 'react-perf/jsx-no-new-function-as-prop': 'off', 369 | 'react-perf/jsx-no-new-object-as-prop': 'off', 370 | }; 371 | 372 | const typescriptRules: Record = { 373 | '@typescript-eslint/adjacent-overload-signatures': 'off', 374 | '@typescript-eslint/array-type': 'off', 375 | '@typescript-eslint/ban-ts-comment': 'off', 376 | '@typescript-eslint/ban-tslint-comment': 'off', 377 | '@typescript-eslint/ban-types': 'off', 378 | '@typescript-eslint/consistent-generic-constructors': 'off', 379 | '@typescript-eslint/consistent-indexed-object-style': 'off', 380 | '@typescript-eslint/consistent-type-definitions': 'off', 381 | '@typescript-eslint/consistent-type-imports': 'off', 382 | '@typescript-eslint/explicit-function-return-type': 'off', 383 | '@typescript-eslint/no-inferrable-types': 'off', 384 | '@typescript-eslint/no-confusing-non-null-assertion': 'off', 385 | '@typescript-eslint/no-duplicate-enum-values': 'off', 386 | '@typescript-eslint/no-dynamic-delete': 'off', 387 | '@typescript-eslint/no-empty-interface': 'off', 388 | '@typescript-eslint/no-empty-object-type': 'off', 389 | '@typescript-eslint/no-explicit-any': 'off', 390 | '@typescript-eslint/no-extra-non-null-assertion': 'off', 391 | '@typescript-eslint/no-extraneous-class': 'off', 392 | '@typescript-eslint/no-import-type-side-effects': 'off', 393 | '@typescript-eslint/no-misused-new': 'off', 394 | '@typescript-eslint/no-namespace': 'off', 395 | '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'off', 396 | '@typescript-eslint/no-non-null-asserted-optional-chain': 'off', 397 | '@typescript-eslint/no-non-null-assertion': 'off', 398 | '@typescript-eslint/no-require-imports': 'off', 399 | '@typescript-eslint/no-this-alias': 'off', 400 | '@typescript-eslint/no-unnecessary-parameter-property-assignment': 'off', 401 | '@typescript-eslint/no-unnecessary-type-constraint': 'off', 402 | '@typescript-eslint/no-unsafe-declaration-merging': 'off', 403 | '@typescript-eslint/no-unsafe-function-type': 'off', 404 | '@typescript-eslint/no-useless-empty-export': 'off', 405 | '@typescript-eslint/no-var-requires': 'off', 406 | '@typescript-eslint/no-wrapper-object-types': 'off', 407 | '@typescript-eslint/prefer-as-const': 'off', 408 | '@typescript-eslint/prefer-enum-initializers': 'off', 409 | '@typescript-eslint/prefer-for-of': 'off', 410 | '@typescript-eslint/prefer-function-type': 'off', 411 | '@typescript-eslint/prefer-literal-enum-member': 'off', 412 | '@typescript-eslint/prefer-namespace-keyword': 'off', 413 | '@typescript-eslint/prefer-ts-expect-error': 'off', 414 | '@typescript-eslint/triple-slash-reference': 'off', 415 | '@typescript-eslint/default-param-last': 'off', 416 | '@typescript-eslint/init-declarations': 'off', 417 | '@typescript-eslint/max-params': 'off', 418 | '@typescript-eslint/no-array-constructor': 'off', 419 | '@typescript-eslint/no-restricted-imports': 'off', 420 | '@typescript-eslint/no-dupe-class-members': 'off', 421 | '@typescript-eslint/no-empty-function': 'off', 422 | '@typescript-eslint/no-loss-of-precision': 'off', 423 | '@typescript-eslint/no-magic-numbers': 'off', 424 | '@typescript-eslint/no-redeclare': 'off', 425 | '@typescript-eslint/no-unused-expressions': 'off', 426 | '@typescript-eslint/no-unused-vars': 'off', 427 | '@typescript-eslint/no-useless-constructor': 'off', 428 | }; 429 | 430 | const unicornRules: Record = { 431 | 'unicorn/catch-error-name': 'off', 432 | 'unicorn/consistent-assert': 'off', 433 | 'unicorn/consistent-date-clone': 'off', 434 | 'unicorn/consistent-empty-array-spread': 'off', 435 | 'unicorn/consistent-existence-index-check': 'off', 436 | 'unicorn/consistent-function-scoping': 'off', 437 | 'unicorn/empty-brace-spaces': 'off', 438 | 'unicorn/error-message': 'off', 439 | 'unicorn/escape-case': 'off', 440 | 'unicorn/explicit-length-check': 'off', 441 | 'unicorn/filename-case': 'off', 442 | 'unicorn/new-for-builtins': 'off', 443 | 'unicorn/no-instanceof-builtins': 'off', 444 | 'unicorn/no-array-method-this-argument': 'off', 445 | 'unicorn/no-unnecessary-array-flat-depth': 'off', 446 | 'unicorn/no-unnecessary-slice-end': 'off', 447 | 'unicorn/no-accessor-recursion': 'off', 448 | 'unicorn/no-invalid-fetch-options': 'off', 449 | 'unicorn/no-abusive-eslint-disable': 'off', 450 | 'unicorn/no-anonymous-default-export': 'off', 451 | 'unicorn/no-array-for-each': 'off', 452 | 'unicorn/no-array-reduce': 'off', 453 | 'unicorn/no-await-expression-member': 'off', 454 | 'unicorn/no-await-in-promise-methods': 'off', 455 | 'unicorn/no-console-spaces': 'off', 456 | 'unicorn/no-document-cookie': 'off', 457 | 'unicorn/no-empty-file': 'off', 458 | 'unicorn/no-hex-escape': 'off', 459 | 'unicorn/no-instanceof-array': 'off', 460 | 'unicorn/no-invalid-remove-event-listener': 'off', 461 | 'unicorn/no-length-as-slice-end': 'off', 462 | 'unicorn/no-lonely-if': 'off', 463 | 'unicorn/no-magic-array-flat-depth': 'off', 464 | 'unicorn/no-negation-in-equality-check': 'off', 465 | 'unicorn/no-nested-ternary': 'off', 466 | 'unicorn/no-new-array': 'off', 467 | 'unicorn/no-new-buffer': 'off', 468 | 'unicorn/no-null': 'off', 469 | 'unicorn/no-object-as-default-parameter': 'off', 470 | 'unicorn/no-process-exit': 'off', 471 | 'unicorn/no-single-promise-in-promise-methods': 'off', 472 | 'unicorn/no-static-only-class': 'off', 473 | 'unicorn/no-thenable': 'off', 474 | 'unicorn/no-this-assignment': 'off', 475 | 'unicorn/no-typeof-undefined': 'off', 476 | 'unicorn/no-unnecessary-await': 'off', 477 | 'unicorn/no-unreadable-array-destructuring': 'off', 478 | 'unicorn/no-unreadable-iife': 'off', 479 | 'unicorn/no-useless-fallback-in-spread': 'off', 480 | 'unicorn/no-useless-length-check': 'off', 481 | 'unicorn/no-useless-promise-resolve-reject': 'off', 482 | 'unicorn/no-useless-spread': 'off', 483 | 'unicorn/no-useless-switch-case': 'off', 484 | 'unicorn/no-useless-undefined': 'off', 485 | 'unicorn/no-zero-fractions': 'off', 486 | 'unicorn/number-literal-case': 'off', 487 | 'unicorn/numeric-separators-style': 'off', 488 | 'unicorn/prefer-global-this': 'off', 489 | 'unicorn/prefer-object-from-entries': 'off', 490 | 'unicorn/prefer-array-find': 'off', 491 | 'unicorn/prefer-array-index-of': 'off', 492 | 'unicorn/prefer-spread': 'off', 493 | 'unicorn/prefer-add-event-listener': 'off', 494 | 'unicorn/prefer-array-flat': 'off', 495 | 'unicorn/prefer-array-flat-map': 'off', 496 | 'unicorn/prefer-array-some': 'off', 497 | 'unicorn/prefer-blob-reading-methods': 'off', 498 | 'unicorn/prefer-code-point': 'off', 499 | 'unicorn/prefer-date-now': 'off', 500 | 'unicorn/prefer-dom-node-append': 'off', 501 | 'unicorn/prefer-dom-node-dataset': 'off', 502 | 'unicorn/prefer-dom-node-remove': 'off', 503 | 'unicorn/prefer-dom-node-text-content': 'off', 504 | 'unicorn/prefer-event-target': 'off', 505 | 'unicorn/prefer-includes': 'off', 506 | 'unicorn/prefer-logical-operator-over-ternary': 'off', 507 | 'unicorn/prefer-math-min-max': 'off', 508 | 'unicorn/prefer-math-trunc': 'off', 509 | 'unicorn/prefer-modern-dom-apis': 'off', 510 | 'unicorn/prefer-modern-math-apis': 'off', 511 | 'unicorn/prefer-native-coercion-functions': 'off', 512 | 'unicorn/prefer-negative-index': 'off', 513 | 'unicorn/prefer-node-protocol': 'off', 514 | 'unicorn/prefer-number-properties': 'off', 515 | 'unicorn/prefer-optional-catch-binding': 'off', 516 | 'unicorn/prefer-prototype-methods': 'off', 517 | 'unicorn/prefer-query-selector': 'off', 518 | 'unicorn/prefer-reflect-apply': 'off', 519 | 'unicorn/prefer-regexp-test': 'off', 520 | 'unicorn/prefer-set-has': 'off', 521 | 'unicorn/prefer-set-size': 'off', 522 | 'unicorn/prefer-string-raw': 'off', 523 | 'unicorn/prefer-string-replace-all': 'off', 524 | 'unicorn/prefer-string-slice': 'off', 525 | 'unicorn/prefer-string-starts-ends-with': 'off', 526 | 'unicorn/prefer-string-trim-start-end': 'off', 527 | 'unicorn/prefer-structured-clone': 'off', 528 | 'unicorn/prefer-type-error': 'off', 529 | 'unicorn/require-post-message-target-origin': 'off', 530 | 'unicorn/require-array-join-separator': 'off', 531 | 'unicorn/require-number-to-fixed-digits-argument': 'off', 532 | 'unicorn/switch-case-braces': 'off', 533 | 'unicorn/text-encoding-identifier-case': 'off', 534 | 'unicorn/throw-new-error': 'off', 535 | 'unicorn/no-negated-condition': 'off', 536 | }; 537 | 538 | const vitestRules: Record = { 539 | 'vitest/no-conditional-tests': 'off', 540 | 'vitest/no-import-node-test': 'off', 541 | 'vitest/prefer-to-be-falsy': 'off', 542 | 'vitest/prefer-to-be-object': 'off', 543 | 'vitest/prefer-to-be-truthy': 'off', 544 | 'vitest/require-local-test-context-for-concurrent-snapshots': 'off', 545 | 'vitest/consistent-test-it': 'off', 546 | 'vitest/expect-expect': 'off', 547 | 'vitest/max-expects': 'off', 548 | 'vitest/max-nested-describe': 'off', 549 | 'vitest/no-alias-methods': 'off', 550 | 'vitest/no-commented-out-tests': 'off', 551 | 'vitest/no-conditional-expect': 'off', 552 | 'vitest/no-conditional-in-test': 'off', 553 | 'vitest/no-disabled-tests': 'off', 554 | 'vitest/no-duplicate-hooks': 'off', 555 | 'vitest/no-focused-tests': 'off', 556 | 'vitest/no-hooks': 'off', 557 | 'vitest/no-identical-title': 'off', 558 | 'vitest/no-interpolation-in-snapshots': 'off', 559 | 'vitest/no-restricted-jest-methods': 'off', 560 | 'vitest/no-restricted-matchers': 'off', 561 | 'vitest/no-standalone-expect': 'off', 562 | 'vitest/no-test-prefixes': 'off', 563 | 'vitest/no-test-return-statement': 'off', 564 | 'vitest/prefer-each': 'off', 565 | 'vitest/prefer-comparison-matcher': 'off', 566 | 'vitest/prefer-equality-matcher': 'off', 567 | 'vitest/prefer-expect-resolves': 'off', 568 | 'vitest/prefer-hooks-in-order': 'off', 569 | 'vitest/prefer-hooks-on-top': 'off', 570 | 'vitest/prefer-lowercase-title': 'off', 571 | 'vitest/prefer-mock-promise-shorthand': 'off', 572 | 'vitest/prefer-strict-equal': 'off', 573 | 'vitest/prefer-to-have-length': 'off', 574 | 'vitest/prefer-todo': 'off', 575 | 'vitest/require-to-throw-message': 'off', 576 | 'vitest/require-top-level-describe': 'off', 577 | 'vitest/valid-describe-callback': 'off', 578 | 'vitest/valid-expect': 'off', 579 | }; 580 | 581 | export { 582 | eslintRules, 583 | importRules, 584 | jestRules, 585 | jsdocRules, 586 | jsxA11yRules, 587 | nextjsRules, 588 | nodeRules, 589 | promiseRules, 590 | reactRules, 591 | reactHooksRules, 592 | reactPerfRules, 593 | typescriptRules, 594 | unicornRules, 595 | vitestRules, 596 | }; 597 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path'; 2 | import dts from 'vite-plugin-dts'; 3 | import { defineConfig, Plugin } 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 | }) as Plugin, 33 | ], 34 | }); 35 | --------------------------------------------------------------------------------