├── .github └── workflows │ └── lint-and-test.yml ├── .gitignore ├── Readme.md ├── eslint.config.js ├── package.json ├── src ├── get-eslint-modules.js └── only-warn.js └── tests └── only-warn.spec.js /.github/workflows/lint-and-test.yml: -------------------------------------------------------------------------------- 1 | name: Linting and Unittests 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: oven-sh/setup-bun@v1 16 | - run: bun install 17 | - run: bun run lint 18 | - run: bun run test 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /yarn.lock 3 | /package-lock.json 4 | /pnpm-lock.yaml 5 | /bun.lockb -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # eslint-plugin-only-warn 2 | 3 | ![status](https://github.com/bfanger/eslint-plugin-only-warn/actions/workflows/lint-and-test.yml/badge.svg) 4 | 5 | Downgrade [ESLint](http://eslint.org) errors to warnings. 6 | 7 | ## Installation 8 | 9 | ```sh 10 | npm install --save-dev eslint-plugin-only-warn 11 | ``` 12 | 13 | ## Usage 14 | 15 | Using [flat config files](https://eslint.org/docs/latest/use/configure/configuration-files): 16 | 17 | ```js 18 | // eslint.config.js 19 | import "eslint-plugin-only-warn"; 20 | 21 | export default [ 22 | ... 23 | ``` 24 | 25 | Or, when the package.json that doesn't have `"type": "module"`: 26 | 27 | ```js 28 | require("eslint-plugin-only-warn"); 29 | ``` 30 | 31 |
32 | ESLint 8.x and earlier 33 | 34 | Add `only-warn` to the plugins section of your `.eslintrc` configuration file: 35 | 36 | ```json 37 | { 38 | "plugins": ["only-warn"] 39 | } 40 | ``` 41 | 42 |
43 | 44 | ### --max-warnings=0 45 | 46 | Add [--max-warnings=0](https://eslint.org/docs/latest/use/command-line-interface#--max-warnings) to the eslint command in package.json 47 | 48 | ```json 49 | "lint": "eslint --max-warnings=0 ...", 50 | ``` 51 | 52 | Adding the option allows git hooks or CI pipelines to detect failed linting rules. 53 | Because the cli now has a nonzero exitcode when it encountered linting warnings. 54 | 55 | ### Git integration 56 | 57 | Use [Husky](https://typicode.github.io/husky/) and [lint-staged](https://github.com/okonet/lint-staged) to prevent committing code that contain eslint warnings. 58 | 59 | # Why only warnings? 60 | 61 | - Don't waste time thinking or discussing about when a rule should be an error or a warning, focus on enabling of disabling a rule 62 | - Warnings look different in editors, this allows you to quickly see that some tweaking is required, but your code still runs (ESLint rules generally don't block the code from executing and fatal errors are still reported as error) 63 | - Prevents noise, disallowing warnings to be committed in a codebase prevents clutter in the output of ESLint (use [special eslint comments](https://eslint.org/docs/latest/use/configure/rules#disabling-rules) for the instances when you need an exception to the rules) 64 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | require("./src/only-warn"); 2 | const js = require("@eslint/js"); 3 | const prettierConfig = require("eslint-config-prettier"); 4 | 5 | module.exports = [ 6 | js.configs.recommended, 7 | prettierConfig, 8 | { 9 | languageOptions: { 10 | globals: { require: true, module: true }, 11 | }, 12 | }, 13 | ]; 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-only-warn", 3 | "version": "1.1.0", 4 | "license": "MIT", 5 | "description": "Downgrade errors to warnings", 6 | "keywords": [ 7 | "eslint", 8 | "eslintplugin", 9 | "eslint-plugin" 10 | ], 11 | "author": "Bob Fanger", 12 | "homepage": "https://github.com/bfanger/eslint-plugin-only-warn", 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/bfanger/eslint-plugin-only-warn.git" 16 | }, 17 | "type": "commonjs", 18 | "main": "src/only-warn.js", 19 | "prettier": {}, 20 | "scripts": { 21 | "lint": "prettier --check . && eslint .", 22 | "format": "eslint --fix . && prettier --write .", 23 | "test": "vitest run", 24 | "test:watch": "vitest" 25 | }, 26 | "devDependencies": { 27 | "eslint": "^9.4.0", 28 | "eslint-config-prettier": "^9.1.0", 29 | "prettier": "^3.2.5", 30 | "vitest": "^1.6.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/get-eslint-modules.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const SEARCH_STR = `${path.sep}node_modules${path.sep}eslint${path.sep}`; 4 | 5 | module.exports = function getEslintModules() { 6 | const map = {}; 7 | Object.keys(require.cache).forEach((modulePath) => { 8 | const pos = modulePath.indexOf(SEARCH_STR); 9 | if (pos !== -1) { 10 | const eslintPath = modulePath.substr(0, pos + SEARCH_STR.length); 11 | if (!map[eslintPath]) { 12 | map[eslintPath] = require(eslintPath); 13 | } 14 | } 15 | }); 16 | 17 | let modules = Object.values(map); 18 | if (modules.length === 0) { 19 | modules = [require("eslint")]; 20 | } 21 | return modules; 22 | }; 23 | -------------------------------------------------------------------------------- /src/only-warn.js: -------------------------------------------------------------------------------- 1 | const getEslintModules = require("./get-eslint-modules"); 2 | 3 | const unpatchedVerify = Symbol("verify"); 4 | 5 | /** 6 | * Patch the verify method and downgrade the errors to warnings. 7 | */ 8 | function patch(LinterPrototype) { 9 | if (LinterPrototype[unpatchedVerify]) { 10 | return; 11 | } 12 | LinterPrototype[unpatchedVerify] = LinterPrototype.verify; 13 | LinterPrototype.verify = function () { 14 | const messages = LinterPrototype[unpatchedVerify].apply(this, arguments); 15 | messages.forEach((message) => { 16 | if (!message.fatal && message.severity === 2) { 17 | message.severity = 1; 18 | } 19 | }); 20 | return messages; 21 | }; 22 | } 23 | 24 | /** 25 | * Remove the patch 26 | */ 27 | function unpatch(LinterPrototype) { 28 | if (LinterPrototype[unpatchedVerify]) { 29 | LinterPrototype.verify = LinterPrototype[unpatchedVerify]; 30 | delete LinterPrototype[unpatchedVerify]; 31 | } 32 | } 33 | 34 | function enable() { 35 | for (const eslint of getEslintModules()) { 36 | patch((eslint.Linter && eslint.Linter.prototype) || eslint.linter); 37 | } 38 | } 39 | function disable() { 40 | for (const eslint of getEslintModules()) { 41 | unpatch((eslint.Linter && eslint.Linter.prototype) || eslint.linter); 42 | } 43 | } 44 | enable(); 45 | module.exports = { enable, disable }; 46 | -------------------------------------------------------------------------------- /tests/only-warn.spec.js: -------------------------------------------------------------------------------- 1 | import { Linter } from "eslint"; 2 | import { disable, enable } from "../src/only-warn"; // apply patch 3 | import { describe, it, expect } from "vitest"; 4 | 5 | describe("eslint-plugin-only-warn", () => { 6 | const linter = new Linter(); 7 | const config = { 8 | rules: { semi: 2 }, // error on missing `;` 9 | }; 10 | const sourceCode = "var foo"; 11 | it("should downgrade error(2) to warn(1)", () => { 12 | const messages = linter.verify(sourceCode, config); 13 | expect(messages[0].severity).toBe(1); 14 | }); 15 | 16 | const sourceCodeFatalError = "var foo = ( => {}"; 17 | it("should not downgrade fatal error(2)", () => { 18 | const messages = linter.verify(sourceCodeFatalError, config); 19 | expect(messages[0].fatal).toBe(true); 20 | expect(messages[0].severity).toBe(2); 21 | }); 22 | 23 | it("can be temporally disabled", () => { 24 | disable(); 25 | const messages1 = linter.verify(sourceCode, config); 26 | expect(messages1[0].severity).toBe(2); 27 | enable(); 28 | const messages2 = linter.verify(sourceCode, config); 29 | expect(messages2[0].severity).toBe(1); 30 | }); 31 | }); 32 | --------------------------------------------------------------------------------