├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml ├── dependabot.yml └── workflows │ ├── auto-merge.yml │ ├── sonar.yml │ └── tests.yml ├── .gitignore ├── LICENSE ├── README.md ├── index.d.ts ├── jest.config.ts ├── package-lock.json ├── package.json ├── src └── index.ts ├── tests ├── v8 │ ├── configs.ts │ ├── eslint.ts │ └── rule.test.ts └── v9 │ ├── configs.ts │ ├── eslint.ts │ └── rule.test.ts ├── tsconfig.build.json └── tsconfig.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [chiefmikey] 2 | patreon: chiefmikey 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | title: "[Bug]: " 4 | labels: [bug, triage] 5 | assignees: 6 | - chiefmikey 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | Thanks for taking the time to fill out this bug report! 12 | - type: input 13 | id: contact 14 | attributes: 15 | label: Contact Details 16 | description: How can we get in touch with you if we need more info? 17 | placeholder: ex. email@example.com 18 | validations: 19 | required: false 20 | - type: textarea 21 | id: what-happened 22 | attributes: 23 | label: What happened? 24 | description: Also tell us, what did you expect to happen? 25 | validations: 26 | required: true 27 | - type: input 28 | id: version 29 | attributes: 30 | label: Version 31 | description: What version are you running? 32 | placeholder: ex. 0.1.3 33 | validations: 34 | required: true 35 | - type: dropdown 36 | id: browsers 37 | attributes: 38 | label: What browsers are you seeing the problem on? 39 | multiple: true 40 | options: 41 | - Firefox 42 | - Chrome 43 | - Safari 44 | - Microsoft Edge 45 | - type: textarea 46 | id: logs 47 | attributes: 48 | label: Relevant log output 49 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 50 | render: shell 51 | - type: checkboxes 52 | id: terms 53 | attributes: 54 | label: Code of Conduct 55 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://chiefmikey.github.io/CODE_OF_CONDUCT/) 56 | options: 57 | - label: I agree to follow this project's Code of Conduct 58 | required: true 59 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest an idea for this project 3 | title: "[Feature]: " 4 | labels: [enhancement] 5 | assignees: 6 | - chiefmikey 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | Thanks for taking the time to fill out this feature request! 12 | - type: input 13 | id: contact 14 | attributes: 15 | label: Contact Details 16 | description: How can we get in touch with you if we need more info? 17 | placeholder: ex. email@example.com 18 | validations: 19 | required: false 20 | - type: textarea 21 | id: feature 22 | attributes: 23 | label: Describe the feature you'd like 24 | description: Include a clear and concise description of what you want to happen. 25 | placeholder: ex. A potential feature could be [...] 26 | validations: 27 | required: true 28 | - type: textarea 29 | id: related 30 | attributes: 31 | label: Is your feature request related to a problem? 32 | description: Include a clear and concise description of what the problem is. 33 | placeholder: ex. I'm always frustrated when [...] 34 | validations: 35 | required: false 36 | - type: textarea 37 | id: additional 38 | attributes: 39 | label: Additional context 40 | description: Add any other context or screenshots about the feature request here. 41 | validations: 42 | required: false 43 | - type: checkboxes 44 | id: terms 45 | attributes: 46 | label: Code of Conduct 47 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://chiefmikey.github.io/CODE_OF_CONDUCT/) 48 | options: 49 | - label: I agree to follow this project's Code of Conduct 50 | required: true 51 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | time: "12:00" 8 | timezone: "America/Denver" 9 | commit-message: 10 | prefix: "actions" 11 | labels: 12 | - "dependencies" 13 | open-pull-requests-limit: 100 14 | -------------------------------------------------------------------------------- /.github/workflows/auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: Auto-Merge 2 | 3 | on: pull_request 4 | 5 | permissions: 6 | contents: write 7 | pull-requests: write 8 | 9 | jobs: 10 | auto-merge: 11 | name: Auto-Merge 12 | runs-on: ubuntu-latest 13 | if: ${{ github.actor == 'dependabot[bot]' }} 14 | steps: 15 | - name: Enable auto-merge for Dependabot PRs 16 | run: gh pr merge "$PR_URL" --auto --squash --delete-branch --subject "$COMMIT_MESSAGE" 17 | env: 18 | PR_URL: ${{ github.event.pull_request.html_url }} 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | COMMIT_MESSAGE: ${{ github.event.pull_request.title }} 21 | -------------------------------------------------------------------------------- /.github/workflows/sonar.yml: -------------------------------------------------------------------------------- 1 | name: SonarCloud Analysis 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | types: 9 | - opened 10 | - synchronize 11 | - reopened 12 | 13 | jobs: 14 | sonarcloud: 15 | name: SonarCloud 16 | runs-on: ubuntu-latest 17 | if: ${{ github.actor != 'dependabot[bot]' }} 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 23 | 24 | - name: SonarCloud Scan 25 | uses: SonarSource/sonarcloud-github-action@master 26 | env: 27 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 28 | SONAR_TOKEN: ${{secrets.SONAR_TOKEN}} 29 | with: 30 | args: 31 | -Dsonar.projectKey=chiefmikey_eslint-plugin-disable-autofix 32 | -Dsonar.organization=chiefmikey 33 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | types: [opened, synchronize] 9 | 10 | jobs: 11 | test-v9: 12 | name: Test v9 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Check Out 16 | uses: actions/checkout@v4 17 | 18 | - name: Set Up 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: 'latest' 22 | 23 | - name: Install 24 | run: npm i 25 | 26 | - name: Test v9 27 | run: npm run gh-test 28 | 29 | test-v8: 30 | name: Test v8 31 | runs-on: ubuntu-latest 32 | steps: 33 | - name: Check Out 34 | uses: actions/checkout@v4 35 | 36 | - name: Set Up 37 | uses: actions/setup-node@v4 38 | with: 39 | node-version: 'latest' 40 | 41 | - name: Install 42 | run: | 43 | npm i 44 | npm rm eslint 45 | npm i eslint@8 46 | 47 | - name: Test v8 48 | run: npm run gh-test-v8 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .yalc 2 | lib 3 | node_modules 4 | dist 5 | .vscode 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Mikl Wolfe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eslint-plugin-disable-autofix 2 | 3 | Disable autofix for ESLint rules and prevent them from being formatted without 4 | having to turn them off. 5 | 6 | ## Usage 7 | 8 | ### Install 9 | 10 | ```sh 11 | npm i -D eslint-plugin-disable-autofix 12 | ``` 13 | 14 | ### Configure 15 | 16 | Import and include `disable-autofix` in the `plugins` object 17 | 18 | Add prefix `disable-autofix/` to the rule and disable the original 19 | 20 | ```ts 21 | import disableAutofix from 'eslint-plugin-disable-autofix'; 22 | 23 | export default [ 24 | { 25 | plugins: { 26 | 'disable-autofix': disableAutofix, 27 | }, 28 | rules: { 29 | 'prefer-const': 'off', 30 | 'disable-autofix/prefer-const': 'warn', 31 | }, 32 | }, 33 | ]; 34 | ``` 35 | 36 | Using 3rd-party Rules 37 | 38 | ```ts 39 | import disableAutofix from 'eslint-plugin-disable-autofix'; 40 | import react from 'eslint-plugin-react'; 41 | 42 | export default [ 43 | { 44 | plugins: { 45 | 'disable-autofix': disableAutofix, 46 | react, 47 | }, 48 | rules: { 49 | 'react/jsx-indent': 'off', 50 | 'disable-autofix/react/jsx-indent': 'error', 51 | }, 52 | }, 53 | ]; 54 | ``` 55 | 56 | Using Scoped Rules 57 | 58 | ```ts 59 | import disableAutofix from 'eslint-plugin-disable-autofix'; 60 | import htmlEslint from '@html-eslint/eslint-plugin'; 61 | 62 | export default [ 63 | { 64 | plugins: { 65 | 'disable-autofix': disableAutofix, 66 | '@html-eslint': htmlEslint, 67 | }, 68 | rules: { 69 | '@html-eslint/require-closing-tags': 'off', 70 | 'disable-autofix/@html-eslint/require-closing-tags': [ 71 | 'error', 72 | { selfClosing: 'always' }, 73 | ], 74 | }, 75 | }, 76 | ]; 77 | ``` 78 | 79 | ### Configure Legacy 80 | 81 | Include `disable-autofix` in the `eslintrc` plugins array 82 | 83 | Add prefix `disable-autofix/` to the rule and disable the original 84 | 85 | ```js 86 | module.exports = { 87 | plugins: ['disable-autofix'], 88 | rules: { 89 | 'prefer-const': 'off', 90 | 'disable-autofix/prefer-const': 'warn', 91 | }, 92 | }; 93 | ``` 94 | 95 | Using 3rd-party Rules 96 | 97 | ```js 98 | module.exports = { 99 | plugins: ['disable-autofix', 'react'], 100 | rules: { 101 | 'react/jsx-indent': 'off', 102 | 'disable-autofix/react/jsx-indent': 'error', 103 | }, 104 | }; 105 | ``` 106 | 107 | Using Scoped Rules 108 | 109 | ```js 110 | module.exports = { 111 | plugins: ['disable-autofix', '@html-eslint'], 112 | rules: { 113 | '@html-eslint/require-closing-tags': 'off', 114 | 'disable-autofix/@html-eslint/require-closing-tags': [ 115 | 'error', 116 | { selfClosing: 'always' }, 117 | ], 118 | }, 119 | }; 120 | ``` 121 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'eslint-rule-composer'; 2 | declare module 'eslint-plugin-unicorn'; 3 | declare module '@babel/eslint-plugin'; 4 | declare module 'eslint-plugin-disable-autofix'; 5 | declare module 'eslint/lib/rules/*'; 6 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { JestConfigWithTsJest } from 'ts-jest'; 2 | 3 | const jestConfig: JestConfigWithTsJest = { 4 | testEnvironment: 'node', 5 | preset: 'ts-jest', 6 | modulePathIgnorePatterns: ['/dist/'], 7 | testMatch: ['**/*.test.ts'], 8 | }; 9 | 10 | export default jestConfig; 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-disable-autofix", 3 | "version": "5.0.1", 4 | "description": "Disable autofix for ESLint rules without turning them off", 5 | "scripts": { 6 | "build": "tsc -p tsconfig.build.json && sed -i '' -e 's/exports\\.default/module.exports/g' ./dist/index.js && sed -i '' -e 's/export default/export =/g' ./dist/index.d.ts", 7 | "gh-build": "tsc -p tsconfig.build.json && sed -i -e 's/exports\\.default/module.exports/g' ./dist/index.js && sed -i -e 's/export default/export =/g' ./dist/index.d.ts", 8 | "gh-test": "npm run gh-build && jest tests/v9", 9 | "gh-test-v8": "npm run gh-build && jest tests/v8", 10 | "major": "npm version major; cd dist && npm version major", 11 | "minor": "npm version minor; cd dist && npm version minor", 12 | "ncu": "ncu -u; cd dist && ncu -u;", 13 | "node": "node dist/index.js", 14 | "patch": "npm version patch; cd dist && npm version patch", 15 | "publish": "cd dist && npm publish --access public", 16 | "test": "npm run build && npm i ./dist && jest --config=jest.config.ts tests/v9", 17 | "test:pin": "jest --config=jest.config.ts tests/v9" 18 | }, 19 | "dependencies": { 20 | "app-root-path": "^3.1.0", 21 | "eslint": "^9.3.0", 22 | "eslint-plugin-disable-autofix": "file:dist", 23 | "eslint-rule-composer": "^0.3.0", 24 | "lodash": "^4.17.21" 25 | }, 26 | "devDependencies": { 27 | "@babel/eslint-plugin": "^7.24.6", 28 | "@babel/preset-env": "^7.24.6", 29 | "@types/eslint": "^8.56.10", 30 | "@types/eslint__eslintrc": "^2.1.1", 31 | "@types/eslint__js": "^8.42.3", 32 | "@types/jest": "^29.5.12", 33 | "@types/lodash": "^4.17.4", 34 | "babel-jest": "^29.7.0", 35 | "eslint-plugin-unicorn": "^53.0.0", 36 | "jest": "^29.7.0", 37 | "mikey-pro": "^7.5.2", 38 | "ts-jest": "^29.1.4", 39 | "ts-node": "^10.9.2", 40 | "typescript": "^5.4.5" 41 | }, 42 | "homepage": "https://github.com/chiefmikey/eslint-plugin-disable-autofix", 43 | "repository": "https://github.com/chiefmikey/eslint-plugin-disable-autofix", 44 | "bugs": "https://github.com/chiefmikey/eslint-plugin-disable-autofix/issues", 45 | "license": "MIT", 46 | "keywords": [ 47 | "eslint", 48 | "eslintplugin", 49 | "eslint plugin", 50 | "disable autofix", 51 | "disable --fix", 52 | "disable fix" 53 | ], 54 | "author": "Mikl Wolfe (https://github.com/chiefmikey)", 55 | "prettier": "@mikey-pro/prettier-config", 56 | "eslintConfig": { 57 | "extends": "@mikey-pro/eslint-config" 58 | }, 59 | "stylelint": { 60 | "extends": "@mikey-pro/stylelint-config" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import path from 'node:path'; 3 | 4 | import appRoot from 'app-root-path'; 5 | import type { Rule, AST, SourceCode } from 'eslint'; 6 | import ruleComposer from 'eslint-rule-composer'; 7 | import _ from 'lodash'; 8 | 9 | interface EslintPlugin { 10 | rules: Record; 11 | id: string; 12 | } 13 | 14 | interface Problem { 15 | message: string; 16 | messageId: string | undefined; 17 | data: object | undefined; 18 | loc: AST.SourceLocation; 19 | fix: undefined; 20 | } 21 | 22 | interface Metadata { 23 | sourceCode: SourceCode; 24 | settings?: object; 25 | filename: string; 26 | } 27 | 28 | type DisabledRules = Record; 29 | 30 | type Predicate = (problem: Problem, metadata: Metadata) => T; 31 | 32 | type MapReports = ( 33 | rule: Rule.RuleModule, 34 | iteratee: Predicate, 35 | ) => Rule.RuleModule; 36 | 37 | const disabledRules: DisabledRules = {}; 38 | const dirname = appRoot.toString(); 39 | const nodeModules = 'node_modules/'; 40 | const importedPlugins = []; 41 | 42 | const map = (ruleComposer as { mapReports: MapReports }).mapReports; 43 | 44 | // delete metadata fixable property 45 | const disableMeta = (rule: Rule.RuleModule): Rule.RuleModule => { 46 | if (rule.meta?.fixable) { 47 | delete rule.meta.fixable; 48 | } 49 | return rule; 50 | }; 51 | 52 | // delete map reports fix method 53 | const disableFix = (rule: Rule.RuleModule): Rule.RuleModule => { 54 | const disableReports = map(rule, (problem) => { 55 | delete problem.fix; 56 | return problem; 57 | }); 58 | return disableMeta(disableReports); 59 | }; 60 | 61 | // handle name conversion 62 | const convertPluginId = (pluginId: string): string => { 63 | return pluginId.includes('@') 64 | ? // `@angular-eslint/eslint-plugin` -> `@angular-eslint` 65 | // `@angular-eslint/eslint-plugin-template` -> `@angular-eslint/template` 66 | pluginId.replace(/eslint-plugin(-|)/u, '').replace(/\/$/, '') 67 | : // `eslint-plugin-react` -> `react` 68 | pluginId.replace(/^eslint-plugin-/u, ''); 69 | }; 70 | 71 | // // import eslint rules 72 | const eslintRules = fs 73 | .readdirSync(path.join(dirname, nodeModules, 'eslint/lib/rules')) 74 | .filter((rule) => rule.endsWith('.js') && !rule.includes('index')); 75 | 76 | for (const rule of eslintRules) { 77 | const rulePath = path.posix.join( 78 | dirname, 79 | nodeModules, 80 | 'eslint/lib/rules', 81 | rule, 82 | ); 83 | const importedRule = require(rulePath); 84 | const ruleName = rule.replace('.js', ''); 85 | disabledRules[ruleName] = disableFix(_.cloneDeep(importedRule)); 86 | } 87 | 88 | // read eslint plugins 89 | const eslintPlugins = fs 90 | .readdirSync(path.join(dirname, nodeModules)) 91 | .filter( 92 | (plugin) => 93 | (plugin.startsWith('eslint-plugin') || plugin.startsWith('@')) && 94 | !plugin.startsWith('@types') && 95 | plugin !== 'eslint-plugin-disable-autofix' && 96 | plugin !== '@eslint', 97 | ); 98 | 99 | // import eslint plugins 100 | for (const plugin of eslintPlugins) { 101 | if (plugin.includes('@')) { 102 | const pluginDirectories = fs 103 | .readdirSync(path.join(dirname, nodeModules, plugin)) 104 | .filter((read) => read.startsWith('eslint-plugin')); 105 | for (const pluginDirectory of pluginDirectories) { 106 | const scopedPlugin = path.posix.join(plugin, pluginDirectory); 107 | const importedPlugin = require(scopedPlugin) as EslintPlugin; 108 | importedPlugin.id = scopedPlugin.replace( 109 | path.join(dirname, nodeModules), 110 | '', 111 | ); 112 | importedPlugins.push(importedPlugin); 113 | } 114 | } else { 115 | const imported = require(plugin) as EslintPlugin; 116 | imported.id = plugin; 117 | importedPlugins.push(imported); 118 | } 119 | } 120 | 121 | // disable plugin rules 122 | for (const plugin of importedPlugins) { 123 | const pluginRules = plugin.rules || {}; 124 | const pluginId = plugin.id || ''; 125 | const pluginName = convertPluginId(pluginId); 126 | for (const ruleId of Object.keys(pluginRules)) { 127 | disabledRules[`${pluginName}/${ruleId}`] = disableFix( 128 | _.cloneDeep(pluginRules[ruleId]), 129 | ); 130 | } 131 | } 132 | 133 | const plugin = { 134 | meta: { 135 | name: 'eslint-plugin-disable-autofix', 136 | version: '4.3.0', 137 | }, 138 | configs: {}, 139 | rules: disabledRules, 140 | processors: {}, 141 | }; 142 | 143 | export default plugin; 144 | -------------------------------------------------------------------------------- /tests/v8/configs.ts: -------------------------------------------------------------------------------- 1 | import { ESLint } from 'eslint'; 2 | 3 | const baseConfig = { 4 | root: true, 5 | env: { 6 | es2024: true, 7 | }, 8 | parserOptions: { 9 | ecmaVersion: 'latest', 10 | sourceType: 'module', 11 | }, 12 | }; 13 | 14 | export const builtin = { 15 | fix: { 16 | ...baseConfig, 17 | extends: ['eslint:all'], 18 | rules: { 19 | 'no-unused-vars': 0, 20 | 'prefer-const': 1, 21 | 'eol-last': 0, 22 | }, 23 | } as ESLint.ConfigData, 24 | disable: { 25 | ...baseConfig, 26 | plugins: ['disable-autofix'], 27 | extends: ['eslint:all'], 28 | rules: { 29 | 'prefer-const': 0, 30 | 'disable-autofix/prefer-const': 1, 31 | 'no-unused-vars': 0, 32 | 'eol-last': 0, 33 | }, 34 | } as ESLint.ConfigData, 35 | }; 36 | 37 | export const unicorn = { 38 | fix: { 39 | ...baseConfig, 40 | plugins: ['unicorn'], 41 | extends: ['plugin:unicorn/recommended'], 42 | rules: { 43 | 'unicorn/prevent-abbreviations': 1, 44 | 'eol-last': 0, 45 | }, 46 | } as ESLint.ConfigData, 47 | disable: { 48 | ...baseConfig, 49 | plugins: ['disable-autofix', 'unicorn'], 50 | extends: ['plugin:unicorn/recommended'], 51 | rules: { 52 | 'unicorn/prevent-abbreviations': 0, 53 | 'disable-autofix/unicorn/prevent-abbreviations': 1, 54 | 'eol-last': 0, 55 | }, 56 | } as ESLint.ConfigData, 57 | }; 58 | 59 | export const babel = { 60 | fix: { 61 | ...baseConfig, 62 | plugins: ['@babel'], 63 | rules: { 64 | '@babel/object-curly-spacing': 1, 65 | 'eol-last': 0, 66 | }, 67 | } as ESLint.ConfigData, 68 | disable: { 69 | ...baseConfig, 70 | plugins: ['disable-autofix', '@babel'], 71 | rules: { 72 | '@babel/object-curly-spacing': 0, 73 | 'disable-autofix/@babel/object-curly-spacing': 1, 74 | 'eol-last': 0, 75 | }, 76 | } as ESLint.ConfigData, 77 | }; 78 | -------------------------------------------------------------------------------- /tests/v8/eslint.ts: -------------------------------------------------------------------------------- 1 | import { ESLint } from 'eslint'; 2 | 3 | const eslint = async (text: string, config: ESLint.ConfigData) => { 4 | try { 5 | const options: ESLint.Options = { 6 | fix: true, 7 | overrideConfig: config, 8 | useEslintrc: false, 9 | plugins: { 10 | 'eslint-plugin-disable-autofix': require('eslint-plugin-disable-autofix') as ESLint.Plugin, 11 | }, 12 | }; 13 | 14 | const eslint = new ESLint(options); 15 | const results = await eslint.lintText(text); 16 | 17 | return results[0].output; 18 | } catch (error) { 19 | return error; 20 | } 21 | }; 22 | 23 | export default eslint; 24 | -------------------------------------------------------------------------------- /tests/v8/rule.test.ts: -------------------------------------------------------------------------------- 1 | import { builtin, unicorn, babel } from './configs'; 2 | import eslint from './eslint'; 3 | 4 | interface Results { 5 | output: string | undefined; 6 | message: string | undefined; 7 | } 8 | 9 | describe('test rule fix disable', () => { 10 | it('fixes the builtin rule', async () => { 11 | expect.hasAssertions(); 12 | const inputText = 'let test = true;'; 13 | const outputText = 'const test = true;'; 14 | const results = (await eslint(inputText, builtin.fix)) as Results; 15 | expect(results).toBe(outputText); 16 | }); 17 | 18 | it('does not fix the builtin rule', async () => { 19 | expect.hasAssertions(); 20 | const inputText = 'let test = true;'; 21 | const results = (await eslint(inputText, builtin.disable)) as Results; 22 | expect(results).toBeUndefined(); 23 | }); 24 | 25 | it('fixes the plugin rule', async () => { 26 | expect.hasAssertions(); 27 | const inputText = 'const env = true'; 28 | const outputText = 'const environment = true'; 29 | const results = (await eslint(inputText, unicorn.fix)) as Results; 30 | expect(results).toBe(outputText); 31 | }); 32 | 33 | it('does not fix the plugin rule', async () => { 34 | expect.hasAssertions(); 35 | const inputText = 'const environment = true'; 36 | const results = (await eslint(inputText, unicorn.disable)) as Results; 37 | expect(results).toBeUndefined(); 38 | }); 39 | 40 | it('fixes the scoped plugin rule', async () => { 41 | expect.hasAssertions(); 42 | const inputText = 'const object = { property: true }'; 43 | const outputText = 'const object = {property: true}'; 44 | const results = (await eslint(inputText, babel.fix)) as Results; 45 | expect(results).toBe(outputText); 46 | }); 47 | 48 | it('does not fix the scoped plugin rule', async () => { 49 | expect.hasAssertions(); 50 | const inputText = 'const object = { property: true }'; 51 | const results = (await eslint(inputText, babel.disable)) as Results; 52 | expect(results).toBeUndefined(); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /tests/v9/configs.ts: -------------------------------------------------------------------------------- 1 | import babelPlugin from '@babel/eslint-plugin'; 2 | import type { Linter } from 'eslint'; 3 | import eslintPluginUnicorn from 'eslint-plugin-unicorn'; 4 | 5 | import disableAutofix from 'eslint-plugin-disable-autofix'; 6 | 7 | const baseConfig = { 8 | languageOptions: { 9 | ecmaVersion: 2024, 10 | }, 11 | }; 12 | 13 | export const builtin = { 14 | fix: { 15 | ...baseConfig, 16 | rules: { 17 | 'no-unused-vars': 'off', 18 | 'prefer-const': 'warn', 19 | 'eol-last': 'off', 20 | }, 21 | } as Linter.FlatConfig, 22 | disable: { 23 | ...baseConfig, 24 | plugins: { 'disable-autofix': disableAutofix }, 25 | rules: { 26 | 'prefer-const': 'off', 27 | 'disable-autofix/prefer-const': 'warn', 28 | 'no-unused-vars': 'off', 29 | 'eol-last': 'off', 30 | }, 31 | } as Linter.FlatConfig, 32 | }; 33 | 34 | export const unicorn = { 35 | fix: { 36 | ...baseConfig, 37 | plugins: { unicorn: eslintPluginUnicorn }, 38 | rules: { 39 | 'unicorn/prevent-abbreviations': 'warn', 40 | 'eol-last': 'off', 41 | }, 42 | } as Linter.FlatConfig, 43 | disable: { 44 | ...baseConfig, 45 | plugins: { 46 | 'disable-autofix': disableAutofix, 47 | unicorn: eslintPluginUnicorn, 48 | }, 49 | rules: { 50 | 'unicorn/prevent-abbreviations': 'off', 51 | 'disable-autofix/unicorn/prevent-abbreviations': 'warn', 52 | 'eol-last': 'off', 53 | }, 54 | } as Linter.FlatConfig, 55 | }; 56 | 57 | export const babel = { 58 | fix: { 59 | ...baseConfig, 60 | plugins: { '@babel': babelPlugin }, 61 | rules: { 62 | '@babel/object-curly-spacing': 'warn', 63 | 'eol-last': 'off', 64 | }, 65 | } as Linter.FlatConfig, 66 | disable: { 67 | ...baseConfig, 68 | plugins: { 'disable-autofix': disableAutofix, '@babel': babelPlugin }, 69 | rules: { 70 | '@babel/object-curly-spacing': 'off', 71 | 'disable-autofix/@babel/object-curly-spacing': 'warn', 72 | 'eol-last': 'off', 73 | }, 74 | } as Linter.FlatConfig, 75 | }; 76 | -------------------------------------------------------------------------------- /tests/v9/eslint.ts: -------------------------------------------------------------------------------- 1 | import { Linter } from 'eslint'; 2 | 3 | const eslint = async (text: string, config: Linter.FlatConfig) => { 4 | try { 5 | const linter = new Linter(); 6 | return linter.verifyAndFix(text, [config]); 7 | } catch (error) { 8 | return error; 9 | } 10 | }; 11 | 12 | export default eslint; 13 | -------------------------------------------------------------------------------- /tests/v9/rule.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from '@jest/globals'; 2 | import type { Linter } from 'eslint'; 3 | 4 | import { builtin, unicorn, babel } from './configs'; 5 | import eslint from './eslint'; 6 | 7 | describe('test rule fix disable', () => { 8 | it('fixes the builtin rule', async () => { 9 | expect.hasAssertions(); 10 | const inputText = 'let test = true;'; 11 | const outputText = 'const test = true;'; 12 | const results = (await eslint(inputText, builtin.fix)) as Linter.FixReport; 13 | expect(results.output).toBe(outputText); 14 | expect(results.fixed).toBe(true); 15 | }); 16 | 17 | it('does not fix the builtin rule', async () => { 18 | expect.hasAssertions(); 19 | const inputText = 'let test = true;'; 20 | const results = (await eslint( 21 | inputText, 22 | builtin.disable, 23 | )) as Linter.FixReport; 24 | expect(results.output).toBe(inputText); 25 | expect(results.fixed).toBe(false); 26 | }); 27 | 28 | it('fixes the plugin rule', async () => { 29 | expect.hasAssertions(); 30 | const inputText = 'const env = true'; 31 | const outputText = 'const environment = true'; 32 | const results = (await eslint(inputText, unicorn.fix)) as Linter.FixReport; 33 | expect(results.output).toBe(outputText); 34 | expect(results.fixed).toBe(true); 35 | }); 36 | 37 | it('does not fix the plugin rule', async () => { 38 | expect.hasAssertions(); 39 | const inputText = 'const environment = true'; 40 | const results = (await eslint( 41 | inputText, 42 | unicorn.disable, 43 | )) as Linter.FixReport; 44 | expect(results.output).toBe(inputText); 45 | expect(results.fixed).toBe(false); 46 | }); 47 | 48 | it('fixes the scoped plugin rule', async () => { 49 | expect.hasAssertions(); 50 | const inputText = 'const object = { property: true }'; 51 | const outputText = 'const object = {property: true}'; 52 | const results = (await eslint(inputText, babel.fix)) as Linter.FixReport; 53 | expect(results.output).toBe(outputText); 54 | expect(results.fixed).toBe(true); 55 | }); 56 | 57 | it('does not fix the scoped plugin rule', async () => { 58 | expect.hasAssertions(); 59 | const inputText = 'const object = { property: true }'; 60 | const results = (await eslint( 61 | inputText, 62 | babel.disable, 63 | )) as Linter.FixReport; 64 | expect(results.output).toBe(inputText); 65 | expect(results.fixed).toBe(false); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ 4 | "es6" 5 | ], 6 | "moduleResolution": "Node", 7 | "module": "CommonJS", 8 | "target": "es6", 9 | "sourceMap": false, 10 | "allowJs": true, 11 | "strict": true, 12 | "esModuleInterop": true, 13 | "allowSyntheticDefaultImports": true, 14 | "skipLibCheck": false, 15 | "forceConsistentCasingInFileNames": true, 16 | "declaration": true, 17 | "noEmit": false, 18 | "noImplicitAny": true, 19 | "removeComments": true, 20 | "preserveConstEnums": true, 21 | "outDir": "./dist", 22 | "paths": { 23 | "./*.ts": [ 24 | "./*" 25 | ] 26 | } 27 | }, 28 | "files": [ 29 | "./src/index.ts" 30 | ], 31 | "include": [ 32 | "./index.d.ts" 33 | ], 34 | "exclude": [ 35 | "node_modules", 36 | "dist" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": [ 4 | "es6" 5 | ], 6 | "moduleResolution": "Node", 7 | "module": "ESNext", 8 | "target": "es6", 9 | "sourceMap": false, 10 | "allowJs": true, 11 | "strict": true, 12 | "esModuleInterop": true, 13 | "allowSyntheticDefaultImports": true, 14 | "skipLibCheck": false, 15 | "forceConsistentCasingInFileNames": true, 16 | "noEmit": false, 17 | "noImplicitAny": true, 18 | "removeComments": true, 19 | "preserveConstEnums": true, 20 | "outDir": "./dist", 21 | "types": [ 22 | "jest" 23 | ], 24 | "paths": { 25 | "./*.ts": [ 26 | "./*" 27 | ] 28 | } 29 | }, 30 | "include": [ 31 | "index.d.ts", 32 | "tests/**/*.ts", 33 | "src/**/*.ts", 34 | "jest.config.*" 35 | ], 36 | "exclude": [ 37 | "node_modules", 38 | "dist" 39 | ] 40 | } 41 | --------------------------------------------------------------------------------