├── .gitattributes ├── validation ├── universal │ ├── file.txt │ ├── module.js │ ├── module.mjs │ ├── module.cjs │ ├── like-commonjs.cjs │ ├── index.html │ └── code.js ├── import-export-order │ ├── a.js │ ├── b.js │ ├── c.js │ ├── d.js │ ├── e.js │ ├── h.js │ ├── j.js │ ├── i.ts │ ├── f.js │ ├── g.js │ ├── index.cjs │ ├── index.js │ └── ts-index.ts ├── style.css ├── dirty-package │ ├── my-dirty-modules.js │ ├── module-js-common.js │ ├── my-module.js │ ├── other-module.cjs │ ├── package.json │ └── file.js ├── named-exports.js ├── hashbang.js ├── module-package │ ├── my-module.js │ ├── cjs-module.cjs │ ├── other-module.cjs │ ├── package.json │ └── file.js ├── browser-es5 │ ├── markdown.md │ └── code.js ├── module.d.ts ├── commonjs-package │ ├── my-module-1.js │ ├── my-module.js │ ├── other-module.cjs │ ├── package.json │ └── file.js ├── webpack │ ├── package.json │ └── file.js ├── typescript-node-support │ ├── other-module.ts │ ├── maths.cts │ ├── my-other-code.mts │ ├── my-other-code.ts │ ├── other-maths.js │ └── my-other-code.cts ├── browser │ ├── module.js │ └── code.js ├── module.js ├── my-module.ts ├── browser-outdated │ ├── module.js │ └── code.js ├── directive.cjs ├── react │ ├── package.json │ ├── index.jsx │ └── App.jsx ├── react-typescript │ ├── App.tsx │ ├── index.tsx │ └── package.json ├── module.cjs ├── code.test.js ├── file.md ├── package-json-test │ └── package.json ├── code.mjs ├── code.cjs ├── my-ts-code.ts └── code.js ├── .prettierrc.js ├── index.js ├── configs ├── package-json.js ├── webpack-special.js ├── utils │ ├── extensions.js │ ├── is-typescript-installed.js │ └── get-json-file.js ├── index.js ├── react.js ├── browser.js ├── stylistic.js ├── markdown.js ├── jest.js ├── node.js ├── typescript.js └── javascript.js ├── .editorconfig ├── prettier-config.js ├── prettier-config-es5.js ├── .gitignore ├── LICENSE ├── .github └── workflows │ └── test.yml ├── plugins ├── package-json │ ├── index.js │ └── rules │ │ └── order-properties.js └── webpack │ ├── index.js │ └── rules │ └── require-license-comment.js ├── ignore-paths.js ├── README.md ├── eslint.config.js ├── package.json ├── configs.js └── CHANGELOG.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /validation/universal/file.txt: -------------------------------------------------------------------------------- 1 | Text 2 | -------------------------------------------------------------------------------- /validation/universal/module.js: -------------------------------------------------------------------------------- 1 | export default 40; 2 | -------------------------------------------------------------------------------- /validation/universal/module.mjs: -------------------------------------------------------------------------------- 1 | export default 40; 2 | -------------------------------------------------------------------------------- /validation/import-export-order/a.js: -------------------------------------------------------------------------------- 1 | export default "a"; 2 | -------------------------------------------------------------------------------- /validation/import-export-order/b.js: -------------------------------------------------------------------------------- 1 | export default "b"; 2 | -------------------------------------------------------------------------------- /validation/import-export-order/c.js: -------------------------------------------------------------------------------- 1 | export default "c"; 2 | -------------------------------------------------------------------------------- /validation/import-export-order/d.js: -------------------------------------------------------------------------------- 1 | export default "d"; 2 | -------------------------------------------------------------------------------- /validation/import-export-order/e.js: -------------------------------------------------------------------------------- 1 | export default "d"; 2 | -------------------------------------------------------------------------------- /validation/import-export-order/h.js: -------------------------------------------------------------------------------- 1 | export default "h"; 2 | -------------------------------------------------------------------------------- /validation/import-export-order/j.js: -------------------------------------------------------------------------------- 1 | export default "i"; 2 | -------------------------------------------------------------------------------- /validation/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: aliceblue; 3 | } 4 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | export { default } from "./prettier-config.js"; 2 | -------------------------------------------------------------------------------- /validation/dirty-package/my-dirty-modules.js: -------------------------------------------------------------------------------- 1 | export default 1; 2 | -------------------------------------------------------------------------------- /validation/dirty-package/module-js-common.js: -------------------------------------------------------------------------------- 1 | module.exports = 100; 2 | -------------------------------------------------------------------------------- /validation/named-exports.js: -------------------------------------------------------------------------------- 1 | export const a = 1; 2 | export const b = 2; 3 | -------------------------------------------------------------------------------- /validation/universal/module.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = 40; 4 | -------------------------------------------------------------------------------- /validation/dirty-package/my-module.js: -------------------------------------------------------------------------------- 1 | export const a = 1; 2 | export const b = 2; 3 | -------------------------------------------------------------------------------- /validation/hashbang.js: -------------------------------------------------------------------------------- 1 | #!foo 2 | 3 | const day = "great"; 4 | 5 | String(day); 6 | -------------------------------------------------------------------------------- /validation/module-package/my-module.js: -------------------------------------------------------------------------------- 1 | export const a = 1; 2 | export const b = 2; 3 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import configs from "./configs.js"; 2 | 3 | export default configs.recommended; 4 | -------------------------------------------------------------------------------- /validation/browser-es5/markdown.md: -------------------------------------------------------------------------------- 1 | ```js 2 | import myMod from "my-mod"; 3 | 4 | console.log(myMod); 5 | ``` 6 | -------------------------------------------------------------------------------- /validation/module.d.ts: -------------------------------------------------------------------------------- 1 | export function getArrayLength(arr: any[]): number; 2 | export const maxInterval: 12; 3 | -------------------------------------------------------------------------------- /validation/commonjs-package/my-module-1.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports.a = 1; 4 | module.exports.b = 2; 5 | -------------------------------------------------------------------------------- /validation/commonjs-package/my-module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports.a = 1; 4 | module.exports.b = 2; 5 | -------------------------------------------------------------------------------- /validation/dirty-package/other-module.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports.a = 1; 4 | module.exports.b = 2; 5 | -------------------------------------------------------------------------------- /validation/module-package/cjs-module.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports.a = 1; 4 | module.exports.b = 2; 5 | -------------------------------------------------------------------------------- /validation/module-package/other-module.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports.a = 1; 4 | module.exports.b = 2; 5 | -------------------------------------------------------------------------------- /validation/commonjs-package/other-module.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports.a = 1; 4 | module.exports.b = 2; 5 | -------------------------------------------------------------------------------- /validation/webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-package", 3 | "version": "1.0.0", 4 | "description": "Test" 5 | } 6 | -------------------------------------------------------------------------------- /validation/dirty-package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dirty-package", 3 | "version": "1.0.0", 4 | "description": "Test" 5 | } 6 | -------------------------------------------------------------------------------- /validation/commonjs-package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "commonjs-package", 3 | "version": "1.0.0", 4 | "description": "Test" 5 | } 6 | -------------------------------------------------------------------------------- /validation/import-export-order/i.ts: -------------------------------------------------------------------------------- 1 | export default "i"; 2 | 3 | export type A = "A"; 4 | export type B = "B"; 5 | export type C = "C"; 6 | -------------------------------------------------------------------------------- /validation/import-export-order/f.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable id-length */ 2 | 3 | const a = "a"; 4 | const b = "b"; 5 | const c = "c"; 6 | 7 | export { a, b, c }; 8 | -------------------------------------------------------------------------------- /validation/import-export-order/g.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable id-length */ 2 | 3 | const a = "a"; 4 | const b = "b"; 5 | const c = "c"; 6 | 7 | export { a, b, c }; 8 | -------------------------------------------------------------------------------- /validation/module-package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "module-package", 3 | "version": "1.0.0", 4 | "description": "Test", 5 | "type": "module" 6 | } 7 | -------------------------------------------------------------------------------- /validation/universal/like-commonjs.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const value = require("./module"); 4 | 5 | // eslint-disable-next-line no-console 6 | console.log(value.default); 7 | -------------------------------------------------------------------------------- /validation/typescript-node-support/other-module.ts: -------------------------------------------------------------------------------- 1 | const a = 100; 2 | const b = 100; 3 | 4 | type MyType = number; 5 | 6 | export default a; 7 | export { b }; 8 | export { type MyType }; 9 | -------------------------------------------------------------------------------- /validation/browser/module.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} a number 3 | * @param {number} b number 4 | * @returns {number} sum 5 | */ 6 | export default function sum(a, b) { 7 | return a + b; 8 | } 9 | -------------------------------------------------------------------------------- /validation/module.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} a first value 3 | * @param {number} b second value 4 | * @returns {number} sum 5 | */ 6 | export default function sum(a, b) { 7 | return a + b; 8 | } 9 | -------------------------------------------------------------------------------- /validation/my-module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} a first value 3 | * @param {number} b second value 4 | * @returns {number} sum 5 | */ 6 | export default function sum(a, b) { 7 | return a + b; 8 | } 9 | -------------------------------------------------------------------------------- /validation/browser-outdated/module.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} a number 3 | * @param {number} b number 4 | * @returns {number} sum 5 | */ 6 | export default function sum(a, b) { 7 | return a + b; 8 | } 9 | -------------------------------------------------------------------------------- /validation/browser-es5/code.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /* eslint-disable no-unused-vars */ 4 | 5 | // eslint-disable-next-line no-undef 6 | var eslint = require("eslint"); 7 | 8 | var a = 1; 9 | var b = "test".indexOf("e") > 0; 10 | -------------------------------------------------------------------------------- /configs/package-json.js: -------------------------------------------------------------------------------- 1 | import { configs } from "../plugins/package-json/index.js"; 2 | 3 | const recommendedBrowserConfig = { 4 | ...configs.recommended, 5 | }; 6 | 7 | export default { 8 | "package-json/recommended": recommendedBrowserConfig, 9 | }; 10 | -------------------------------------------------------------------------------- /configs/webpack-special.js: -------------------------------------------------------------------------------- 1 | import { configs } from "../plugins/webpack/index.js"; 2 | 3 | const recommendedWebpackSpecialConfig = { 4 | ...configs.recommended, 5 | }; 6 | 7 | export default { 8 | "webpack/special": recommendedWebpackSpecialConfig, 9 | }; 10 | -------------------------------------------------------------------------------- /validation/directive.cjs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; // eslint-disable-next-line @stylistic/padding-line-between-statements 6 | const test = 1; 7 | 8 | Number(test); 9 | -------------------------------------------------------------------------------- /validation/typescript-node-support/maths.cts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function absolute(num: number) { 4 | if (num < 0) return num * -1; 5 | return num; 6 | } 7 | 8 | module.exports = { 9 | pi: 3.14, 10 | squareTwo: 1.41, 11 | phi: 1.61, 12 | absolute, 13 | }; 14 | -------------------------------------------------------------------------------- /validation/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "module-package", 3 | "version": "1.0.0", 4 | "description": "Test", 5 | "type": "module", 6 | "dependencies": { 7 | "prop-types": "^15.8.1", 8 | "react": "^19.1.0", 9 | "react-dom": "^19.1.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /validation/typescript-node-support/my-other-code.mts: -------------------------------------------------------------------------------- 1 | import maths from "./maths.cts"; 2 | import a, { b } from "./other-module.ts"; 3 | 4 | function getNumber(value: number): number { 5 | return value; 6 | } 7 | 8 | getNumber(a); 9 | getNumber(maths.pi); 10 | getNumber(b); 11 | -------------------------------------------------------------------------------- /validation/typescript-node-support/my-other-code.ts: -------------------------------------------------------------------------------- 1 | import maths from "./maths.cts"; 2 | import a, { b } from "./other-module.ts"; 3 | 4 | function getNumber(value: number): number { 5 | return value; 6 | } 7 | 8 | getNumber(a); 9 | getNumber(maths.pi); 10 | getNumber(b); 11 | -------------------------------------------------------------------------------- /configs/utils/extensions.js: -------------------------------------------------------------------------------- 1 | const javascriptExtensions = [".js", ".jsx", ".cjs", ".mjs"]; 2 | const typescriptExtensions = [".ts", ".tsx", ".cts", ".mts"]; 3 | const allExtensions = [...javascriptExtensions, ...typescriptExtensions]; 4 | 5 | export { allExtensions, javascriptExtensions, typescriptExtensions }; 6 | -------------------------------------------------------------------------------- /validation/react-typescript/App.tsx: -------------------------------------------------------------------------------- 1 | import PropTypes from "prop-types"; 2 | 3 | interface AppProps { 4 | name: string; 5 | } 6 | 7 | function App(props: AppProps) { 8 | return

Hello, {props.name}!

; 9 | } 10 | 11 | App.propTypes = { 12 | name: PropTypes.string, 13 | }; 14 | 15 | export default App; 16 | -------------------------------------------------------------------------------- /validation/react/index.jsx: -------------------------------------------------------------------------------- 1 | /* global document */ 2 | 3 | import { StrictMode } from "react"; 4 | import { createRoot } from "react-dom/client"; 5 | import App from "./App.jsx"; 6 | 7 | createRoot(document.getElementById("root")).render( 8 | 9 | 10 | , 11 | ); 12 | -------------------------------------------------------------------------------- /validation/react-typescript/index.tsx: -------------------------------------------------------------------------------- 1 | /* global document */ 2 | 3 | import { StrictMode } from "react"; 4 | import { createRoot } from "react-dom/client"; 5 | import App from "./App.jsx"; 6 | 7 | createRoot(document.getElementById("root")).render( 8 | 9 | 10 | , 11 | ); 12 | -------------------------------------------------------------------------------- /validation/typescript-node-support/other-maths.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number} num number 3 | * @returns {number} result 4 | */ 5 | function absolute(num) { 6 | if (num < 0) return num * -1; 7 | return num; 8 | } 9 | 10 | export const pi = 3.14; 11 | export const squareTwo = 3.14; 12 | export const phi = 3.14; 13 | export { absolute }; 14 | -------------------------------------------------------------------------------- /validation/webpack/file.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Alexander Akait @alexander-akait 4 | */ 5 | 6 | "use strict"; 7 | 8 | /** 9 | * @param {number} a a 10 | * @param {number} b b 11 | * @returns {number} result 12 | */ 13 | function sum(a, b) { 14 | return a + b; 15 | } 16 | 17 | sum(1, 2); 18 | -------------------------------------------------------------------------------- /validation/react-typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "module-package", 3 | "version": "1.0.0", 4 | "description": "Test", 5 | "type": "module", 6 | "dependencies": { 7 | "@types/react": "^19.1.8", 8 | "@types/react-dom": "^19.1.6", 9 | "prop-types": "^15.8.1", 10 | "react": "^19.1.0", 11 | "react-dom": "^19.1.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /validation/react/App.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from "prop-types"; 2 | 3 | /** 4 | * @param {{ name: string }} props props 5 | * @returns {JSX.Element} Application 6 | * @constructor 7 | */ 8 | function App(props) { 9 | return

Hello, {props.name}!

; 10 | } 11 | 12 | App.propTypes = { 13 | name: PropTypes.string, 14 | }; 15 | 16 | export default App; 17 | -------------------------------------------------------------------------------- /validation/browser/code.js: -------------------------------------------------------------------------------- 1 | import sum from "./module.js"; 2 | 3 | const heading = document.querySelector("h1"); 4 | 5 | // Change the text content 6 | heading.textContent = "New Heading Text"; 7 | 8 | // Add a new element 9 | const newParagraph = document.createElement("p"); 10 | 11 | newParagraph.textContent = `Sum (1 + 2) = ${sum(1, 2)}`; 12 | 13 | heading.append(newParagraph); 14 | -------------------------------------------------------------------------------- /validation/browser-outdated/code.js: -------------------------------------------------------------------------------- 1 | import sum from "./module.js"; 2 | 3 | const heading = document.querySelector("h1"); 4 | 5 | // Change the text content 6 | heading.textContent = "New Heading Text"; 7 | 8 | // Add a new element 9 | const newParagraph = document.createElement("p"); 10 | 11 | newParagraph.textContent = `Sum (1 + 2) = ${sum(1, 2)}`; 12 | 13 | heading.appendChild(newParagraph); 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | max_line_length = 80 11 | 12 | [*.{yml,yaml,json}] 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | 19 | [*.snap] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /prettier-config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | printWidth: 80, 3 | useTabs: true, 4 | tabWidth: 2, 5 | trailingComma: "all", 6 | arrowParens: "always", 7 | overrides: [ 8 | { 9 | files: "*.json", 10 | options: { 11 | parser: "json", 12 | useTabs: false, 13 | }, 14 | }, 15 | { 16 | files: "*.{cts,mts,ts}", 17 | options: { 18 | parser: "typescript", 19 | }, 20 | }, 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /prettier-config-es5.js: -------------------------------------------------------------------------------- 1 | export default { 2 | printWidth: 80, 3 | useTabs: true, 4 | tabWidth: 2, 5 | trailingComma: "es5", 6 | arrowParens: "always", 7 | overrides: [ 8 | { 9 | files: "*.json", 10 | options: { 11 | parser: "json", 12 | useTabs: false, 13 | }, 14 | }, 15 | { 16 | files: "*.{cts,mts,ts}", 17 | options: { 18 | parser: "typescript", 19 | }, 20 | }, 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /validation/universal/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | browser 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /validation/module.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * @returns {void} 5 | */ 6 | function test() { 7 | // Nothing 8 | } 9 | 10 | /** 11 | * @returns {string} result 12 | */ 13 | module.exports = function test() { 14 | return "test"; 15 | }; 16 | 17 | /** 18 | * @returns {string} result 19 | */ 20 | 21 | module.exports.aaa = 1; 22 | 23 | test(); 24 | 25 | module.exports.bbb = 1; 26 | module.exports.other = function other() { 27 | return "test"; 28 | }; 29 | -------------------------------------------------------------------------------- /validation/typescript-node-support/my-other-code.cts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const maths = require("./maths.cts"); 4 | const mathsOther = require("./other-maths"); 5 | const a = require("./other-module.ts"); 6 | 7 | const myType: import("./other-module.ts").MyType = 5; 8 | 9 | function getNumber(value: number): number { 10 | return value; 11 | } 12 | 13 | getNumber(a); 14 | getNumber(maths.pi); 15 | getNumber(mathsOther.pi); 16 | getNumber(a.b); 17 | getNumber(myType); 18 | -------------------------------------------------------------------------------- /validation/code.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment jsdom 3 | */ 4 | 5 | describe("name", () => { 6 | it("test #1", () => { 7 | const data = { type: "coords", x: 1, y: 2 }; 8 | const { type, ...coords } = data; 9 | 10 | expect(type).toBe(data.type); 11 | expect(coords.x).toBe(data.x); 12 | }); 13 | 14 | it("test #2", () => { 15 | // object rest no-unused-vars ignoreRestSiblings 16 | const data = { type: "coords", x: 1, y: 2 }; 17 | const { type, ...coords } = data; 18 | 19 | expect(data.type).toBe(type); 20 | expect(data.y).toBe(coords.y); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /validation/module-package/file.js: -------------------------------------------------------------------------------- 1 | import otherMod from "././other-module.cjs"; 2 | import { a, b } from "./my-module.js"; 3 | 4 | // eslint-disable-next-line no-unused-vars, no-undef 5 | const commonJSModule = require("./cjs-module.cjs"); 6 | 7 | const foo = 1; 8 | const bar = 2; 9 | 10 | /** 11 | * @param {number} a a 12 | * @param {number} b b 13 | * @returns {number} sum 14 | */ 15 | function sum(a, b) { 16 | return a + b; 17 | } 18 | 19 | sum(foo, bar); 20 | sum(a, b); 21 | sum(otherMod.a, otherMod.b); 22 | 23 | /** 24 | * @returns {Promise} run 25 | */ 26 | async function run() { 27 | // Nothing 28 | } 29 | 30 | await run(); 31 | -------------------------------------------------------------------------------- /validation/file.md: -------------------------------------------------------------------------------- 1 | # Test 2 | 3 | ```js 4 | const path = require("node:path"); 5 | 6 | console.log(path.resolve(__dirname, "./test")); 7 | 8 | function run() { 9 | console.log("RUN"); 10 | } 11 | 12 | run(); 13 | ``` 14 | 15 | ```js 16 | import { dirname } from "node:path"; 17 | import { fileURLToPath } from "node:url"; 18 | 19 | const __filename = fileURLToPath(import.meta.url); 20 | 21 | console.log(path.resolve(__dirname, "./test")); 22 | 23 | function run() { 24 | console.log("RUN"); 25 | } 26 | 27 | run(); 28 | ``` 29 | 30 | ```js 31 | new URL("./test"); 32 | ``` 33 | 34 | ```typescript 35 | type MyRecord = Record; 36 | ``` 37 | -------------------------------------------------------------------------------- /configs/utils/is-typescript-installed.js: -------------------------------------------------------------------------------- 1 | import getJsonFile from "./get-json-file.js"; 2 | 3 | /** 4 | * @returns {boolean} true when typescript is supported by project, otherwise false 5 | */ 6 | function isTypescriptInstalled() { 7 | const packageJson = getJsonFile("package.json"); 8 | 9 | if (packageJson === null) { 10 | return []; 11 | } 12 | 13 | const dependencies = packageJson.dependencies || []; 14 | const devDependencies = packageJson.devDependencies || []; 15 | 16 | return Boolean( 17 | typeof dependencies.typescript !== "undefined" || 18 | typeof devDependencies.typescript !== "undefined", 19 | ); 20 | } 21 | 22 | export default isTypescriptInstalled; 23 | -------------------------------------------------------------------------------- /validation/dirty-package/file.js: -------------------------------------------------------------------------------- 1 | import otherMod from "././other-module.cjs"; 2 | import myNumber from "./my-dirty-modules"; 3 | import { a, b } from "./my-module.js"; 4 | 5 | const commonJSModule = require("./module-js-common.js"); 6 | 7 | require("../typescript-node-support/my-other-code.ts"); 8 | require("../typescript-node-support/my-other-code"); 9 | 10 | const foo = 1; 11 | const bar = 2; 12 | 13 | /** 14 | * @param {number} a a 15 | * @param {number} b b 16 | * @returns {number} sum 17 | */ 18 | function sum(a, b) { 19 | return a + b; 20 | } 21 | 22 | sum(foo, bar); 23 | sum(a, b); 24 | sum(otherMod.a, otherMod.b); 25 | sum(commonJSModule.a, commonJSModule.b); 26 | sum(myNumber, myNumber); 27 | -------------------------------------------------------------------------------- /validation/package-json-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-awesome-package", 3 | "version": "1.0.0", 4 | "description": "A brief description of the project.", 5 | "keywords": ["node", "example", "package"], 6 | "homepage": "https://github.com/your-username/your-repository#readme", 7 | "bugs": { 8 | "url": "https://github.com/your-username/your-repository/issues" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/your-username/your-repository.git" 13 | }, 14 | "license": "MIT", 15 | "author": "Your Name", 16 | "main": "index.js", 17 | "scripts": { 18 | "start": "node index.js", 19 | "test": "mocha" 20 | }, 21 | "dependencies": {}, 22 | "devDependencies": {} 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # IDE 12 | .idea 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # node-waf configuration 27 | .lock-wscript 28 | 29 | # Compiled binary addons (http://nodejs.org/api/addons.html) 30 | build/Release 31 | 32 | # Dependency directories 33 | node_modules 34 | 35 | # Cache directories 36 | .npm 37 | .eslintcache 38 | 39 | # Optional REPL history 40 | .node_repl_history 41 | -------------------------------------------------------------------------------- /validation/commonjs-package/file.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const mod = require("./my-module"); 4 | // eslint-disable-next-line import/extensions 5 | const modAgain = require("./my-module.js"); 6 | const otherMod = require("./other-module.cjs"); 7 | 8 | require("../typescript-node-support/my-other-code.ts"); 9 | require("../typescript-node-support/my-other-code"); 10 | // eslint-disable-next-line import/extensions 11 | require("./my-module-1.js"); 12 | 13 | const foo = 1; 14 | const bar = 2; 15 | 16 | /** 17 | * @param {number} a a 18 | * @param {number} b b 19 | * @returns {number} sum 20 | */ 21 | function sum(a, b) { 22 | return a + b; 23 | } 24 | 25 | sum(foo, bar); 26 | sum(mod.a, mod.b); 27 | sum(otherMod.a, otherMod.b); 28 | sum(modAgain.a, modAgain.b); 29 | -------------------------------------------------------------------------------- /configs/index.js: -------------------------------------------------------------------------------- 1 | import browserConfig from "./browser.js"; 2 | import javascriptConfig from "./javascript.js"; 3 | import jestConfig from "./jest.js"; 4 | import markdownConfig from "./markdown.js"; 5 | import nodeConfig from "./node.js"; 6 | import packageJSON from "./package-json.js"; 7 | import reactConfig from "./react.js"; 8 | import stylisticConfig from "./stylistic.js"; 9 | import typescriptConfig from "./typescript.js"; 10 | import webpackSpecial from "./webpack-special.js"; 11 | 12 | const configs = { 13 | ...browserConfig, 14 | ...javascriptConfig, 15 | ...jestConfig, 16 | ...markdownConfig, 17 | ...nodeConfig, 18 | ...stylisticConfig, 19 | ...typescriptConfig, 20 | ...reactConfig, 21 | ...packageJSON, 22 | ...webpackSpecial, 23 | }; 24 | 25 | export default configs; 26 | -------------------------------------------------------------------------------- /validation/code.mjs: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/extensions 2 | import otherSum from "./module"; 3 | import code from "./module.cjs"; 4 | // eslint-disable-next-line no-duplicate-imports 5 | import { aaa, bbb } from "./module.cjs"; 6 | import myOtherCode from "./module.js"; 7 | 8 | // eslint-disable-next-line no-unused-vars, import/order, import/no-extraneous-dependencies 9 | import * as scope from "eslint-scope"; 10 | 11 | // eslint-disable-next-line import/no-unresolved 12 | import("./unknown.ext"); 13 | 14 | import("./style.css"); 15 | 16 | otherSum(aaa, 2); 17 | 18 | otherSum(bbb, 2); 19 | 20 | myOtherCode(aaa, bbb); 21 | 22 | code(); 23 | otherSum(1, 2); 24 | 25 | /** 26 | * @param {number} a a 27 | * @param {number} b b 28 | * @returns {number} result 29 | */ 30 | function sum(a, b) { 31 | return a + b; 32 | } 33 | 34 | export default sum; 35 | -------------------------------------------------------------------------------- /validation/code.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const otherSum = require("./module"); 4 | const code = require("./module.cjs"); 5 | 6 | const { aaa } = require("./module.cjs"); 7 | 8 | otherSum(aaa, 2); 9 | 10 | const { bbb } = require("./module.cjs"); 11 | 12 | otherSum(bbb, 2); 13 | 14 | // eslint-disable-next-line no-unused-vars, import/extensions 15 | const myOtherCode = require("./module.js"); 16 | 17 | // eslint-disable-next-line import/no-unresolved 18 | require("./unknown.unknown"); 19 | 20 | require("./style.css"); 21 | 22 | // eslint-disable-next-line no-unused-vars, import/order, import/no-extraneous-dependencies 23 | const scope = require("eslint-scope"); 24 | 25 | code(); 26 | otherSum(1, 2); 27 | 28 | /** 29 | * @param {number} a a 30 | * @param {number} b b 31 | * @returns {number} result 32 | */ 33 | function sum(a, b) { 34 | return a + b; 35 | } 36 | 37 | module.exports = sum; 38 | -------------------------------------------------------------------------------- /validation/import-export-order/index.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /* eslint-disable id-length */ 4 | 5 | const a = require("./a"); 6 | const b = require("./b"); 7 | const c = require("./c"); 8 | const e = require("./e"); 9 | /* eslint-disable-next-line import/order */ 10 | const d = require("./d"); 11 | const { a: a1, b: b1, c: c1 } = require("./f"); 12 | /* eslint-disable-next-line import/order */ 13 | const { c: c2, a: a2, b: b2 } = require("./g"); 14 | const z = require("./h"); 15 | 16 | /** 17 | * @template T 18 | * @param {T} val value 19 | * @returns {T} value 20 | */ 21 | function get(val) { 22 | return val; 23 | } 24 | 25 | get(a); 26 | get(b); 27 | get(c); 28 | get(d); 29 | get(e); 30 | get(a1); 31 | get(b1); 32 | get(c1); 33 | get(a2); 34 | get(b2); 35 | get(c2); 36 | get(z); 37 | 38 | module.exports.a = "a"; 39 | module.exports.c = "c"; 40 | // eslint-disable-next-line import/order 41 | module.exports.b = "b"; 42 | -------------------------------------------------------------------------------- /validation/import-export-order/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable id-length */ 2 | 3 | import a from "./a.js"; 4 | import b from "./b.js"; 5 | import c from "./c.js"; 6 | import e from "./e.js"; 7 | /* eslint-disable-next-line import/order */ 8 | import d from "./d.js"; 9 | import { a as a1, b as b1, c as c1 } from "./f.js"; 10 | /* eslint-disable-next-line import/order */ 11 | import { c as c2, a as a2, b as b2 } from "./g.js"; 12 | import z from "./h.js"; 13 | 14 | /** 15 | * @template T 16 | * @param {T} val value 17 | * @returns {T} value 18 | */ 19 | function get(val) { 20 | return val; 21 | } 22 | 23 | get(a); 24 | get(b); 25 | get(c); 26 | get(d); 27 | get(e); 28 | get(a1); 29 | get(b1); 30 | get(c1); 31 | get(a2); 32 | get(b2); 33 | get(c2); 34 | get(z); 35 | 36 | export const p = "p"; 37 | export const q = "q"; 38 | export const r = "r"; 39 | 40 | const s = "s"; 41 | const t = "t"; 42 | const u = "u"; 43 | 44 | // eslint-disable-next-line import/order 45 | export { s, u, t }; 46 | -------------------------------------------------------------------------------- /configs/react.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @returns {Promise>} config 3 | */ 4 | async function getReactRecommendedConfig() { 5 | let reactPlugin; 6 | 7 | try { 8 | reactPlugin = (await import("eslint-plugin-react")).default; 9 | // eslint-disable-next-line unicorn/prefer-optional-catch-binding 10 | } catch (_err) { 11 | // Nothing 12 | } 13 | 14 | const { recommended, "jsx-runtime": jsxRuntime } = (reactPlugin && 15 | reactPlugin.configs && 16 | reactPlugin.configs.flat) || { recommended: {}, "jsx-runtime": {} }; 17 | 18 | return { 19 | ...recommended, 20 | files: ["**/*.{jsx,tsx}"], 21 | settings: { 22 | react: { 23 | version: "detect", 24 | defaultVersion: "19", 25 | }, 26 | }, 27 | languageOptions: { 28 | ...recommended.languageOptions, 29 | ...jsxRuntime.languageOptions, 30 | }, 31 | rules: { 32 | ...recommended.rules, 33 | ...jsxRuntime.rules, 34 | }, 35 | }; 36 | } 37 | 38 | const reactRecommendedConfig = await getReactRecommendedConfig(); 39 | 40 | export default { 41 | "react/recommended": reactRecommendedConfig, 42 | }; 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright JS Foundation and other contributors 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | 'Software'), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | test: 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | os: [ubuntu-latest, windows-latest, macos-latest] 17 | node-version: [20.x, 22.x, 24.x] 18 | runs-on: ${{ matrix.os }} 19 | steps: 20 | - uses: actions/checkout@v4 21 | - name: Use Node.js ${{ matrix.node-version }} 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | architecture: ${{ steps.calculate_architecture.outputs.result }} 26 | cache: "npm" 27 | - run: npm ci 28 | - run: npm run lint 29 | - uses: codecov/codecov-action@v5 30 | with: 31 | flags: integration 32 | token: ${{ secrets.CODECOV_TOKEN }} 33 | - run: node ./validation/typescript-node-support/my-other-code.ts && node ./validation/typescript-node-support/my-other-code.cts && node ./validation/typescript-node-support/my-other-code.mts 34 | if: matrix.node-version == '24.x' 35 | -------------------------------------------------------------------------------- /plugins/package-json/index.js: -------------------------------------------------------------------------------- 1 | import { createRequire } from "node:module"; 2 | import * as parserJsonc from "jsonc-eslint-parser"; 3 | import { rule as orderProperties } from "./rules/order-properties.js"; 4 | 5 | const require = createRequire(import.meta.url); 6 | 7 | const { version } = require("../../package.json"); 8 | 9 | const rules = { 10 | "order-properties": orderProperties, 11 | }; 12 | 13 | const recommendedRules = { 14 | ...Object.fromEntries( 15 | Object.entries(rules) 16 | .filter(([, rule]) => rule.meta.docs?.recommended) 17 | .map(([name]) => [`package-json/${name}`, "error"]), 18 | ), 19 | }; 20 | 21 | const configs = { 22 | recommended: { 23 | name: "package-json/recommended", 24 | files: ["**/package.json"], 25 | languageOptions: { 26 | parser: parserJsonc, 27 | }, 28 | plugins: { 29 | get "package-json"() { 30 | // eslint-disable-next-line no-use-before-define 31 | return plugin; 32 | }, 33 | }, 34 | rules: recommendedRules, 35 | }, 36 | }; 37 | 38 | const plugin = { 39 | configs, 40 | meta: { 41 | version, 42 | }, 43 | rules, 44 | }; 45 | 46 | export { configs, rules }; 47 | 48 | export default plugin; 49 | -------------------------------------------------------------------------------- /plugins/webpack/index.js: -------------------------------------------------------------------------------- 1 | import { createRequire } from "node:module"; 2 | import { allExtensions } from "../../configs/utils/extensions.js"; 3 | import { rule as requireLicenseComment } from "./rules/require-license-comment.js"; 4 | 5 | const require = createRequire(import.meta.url); 6 | 7 | const { version } = require("../../package.json"); 8 | 9 | const rules = { 10 | "require-license-comment": requireLicenseComment, 11 | }; 12 | 13 | const recommendedRules = { 14 | ...Object.fromEntries( 15 | Object.entries(rules) 16 | .filter(([, rule]) => rule.meta.docs?.recommended) 17 | .map(([name]) => [`webpack/${name}`, "error"]), 18 | ), 19 | }; 20 | 21 | const configs = { 22 | recommended: { 23 | name: "webpack/recommended", 24 | files: [`**/*.{${allExtensions.map((item) => item.slice(1)).join(",")}}`], 25 | plugins: { 26 | get webpack() { 27 | // eslint-disable-next-line no-use-before-define 28 | return plugin; 29 | }, 30 | }, 31 | rules: recommendedRules, 32 | }, 33 | }; 34 | 35 | const plugin = { 36 | configs, 37 | meta: { 38 | version, 39 | }, 40 | rules, 41 | }; 42 | 43 | export { configs, rules }; 44 | 45 | export default plugin; 46 | -------------------------------------------------------------------------------- /validation/import-export-order/ts-index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable id-length */ 2 | 3 | import a from "./a.js"; 4 | import b from "./b.js"; 5 | import c from "./c.js"; 6 | import e from "./e.js"; 7 | /* eslint-disable-next-line import/order */ 8 | import d from "./d.js"; 9 | import { a as a1, b as b1, c as c1 } from "./f.js"; 10 | /* eslint-disable-next-line import/order */ 11 | import { c as c2, a as a2, b as b2 } from "./g.js"; 12 | import z from "./h.js"; 13 | import { type A, type B, type C } from "./i.js"; 14 | import j from "./j.js"; 15 | 16 | const A: A = "A"; 17 | const B: B = "B"; 18 | const C: C = "C"; 19 | 20 | /** 21 | * @template T 22 | * @param {T} val value 23 | * @returns {T} value 24 | */ 25 | function get(val) { 26 | return val; 27 | } 28 | 29 | get(a); 30 | get(b); 31 | get(c); 32 | get(d); 33 | get(e); 34 | get(a1); 35 | get(b1); 36 | get(c1); 37 | get(a2); 38 | get(b2); 39 | get(c2); 40 | get(z); 41 | get(j); 42 | get(A); 43 | get(B); 44 | get(C); 45 | 46 | export const p = "p"; 47 | export const q = "q"; 48 | export const r = "r"; 49 | 50 | const s = "s"; 51 | const t = "t"; 52 | const u = "u"; 53 | 54 | // eslint-disable-next-line import/order 55 | export { s, u, t }; 56 | -------------------------------------------------------------------------------- /ignore-paths.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | // OS 3 | ".DS_Store", 4 | "Thumbs.db", 5 | 6 | // IDE 7 | ".idea", 8 | "*.iml", 9 | ".vscode", 10 | ".vscode-test", 11 | "*.sublime-project", 12 | "*.sublime-workspace", 13 | 14 | // Logs 15 | "logs/**/*", 16 | "*.log", 17 | "npm-debug.log*", 18 | "yarn-debug.log*", 19 | "yarn-error.log*", 20 | "lerna-debug.log*", 21 | 22 | // Diagnostic reports (https://nodejs.org/api/report.html) 23 | "report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json", 24 | 25 | // Runtime data 26 | "pids", 27 | "*.pid", 28 | "*.seed", 29 | "*.pid.lock", 30 | 31 | // Directory for instrumented libs generated by jscoverage/JSCover 32 | "lib-cov/**/*", 33 | 34 | // Coverage directory used by tools like istanbul 35 | "coverage/**/*", 36 | "*.lcov", 37 | 38 | // nyc test coverage 39 | ".nyc_output", 40 | 41 | // Compiled binary addons (https://nodejs.org/api/addons.html) 42 | "build/Release/**/*", 43 | 44 | // Dependencies 45 | "node_modules/**/*", 46 | 47 | // Optional REPL history 48 | ".node_repl_history", 49 | 50 | // Generated code 51 | "dist/**/*", 52 | 53 | // Test 54 | "test/fixtures/**/*", 55 | "test/outputs/**/*", 56 | 57 | // Generated types 58 | "types/**/*", 59 | 60 | // Reports 61 | "reports/**/*", 62 | 63 | // Caches 64 | ".cache/**/*", 65 | ".eslintcache", 66 | ".stylelintcache", 67 | ".cspellcache", 68 | "*.tsbuildinfo", 69 | ]; 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm][npm]][npm-url] 2 | [![test][test]][test-url] 3 | [![discussions](https://img.shields.io/github/discussions/webpack/webpack)](https://github.com/webpack/webpack/discussions) 4 | 5 |
6 | 7 | 9 | 10 | 11 | 12 |

ESLint Config Webpack

13 |

Provides Webpacks's eslint config as an extensible shared config.

14 |

15 | 16 | # eslint-config-webpack 17 | 18 | ## Install 19 | 20 | ```bash 21 | npm i -D eslint-config-webpack 22 | ``` 23 | 24 | ## Usage 25 | 26 | Webpack's eslint config contains all of our ESLint rules. 27 | 28 | _In your eslint.config.js add ..._ 29 | 30 | ```js 31 | import { defineConfig } from "eslint/config"; 32 | import config from "eslint-config-webpack"; 33 | 34 | export default defineConfig([ 35 | { 36 | extends: [config] 37 | } 38 | ]); 39 | ``` 40 | 41 | [npm]: https://img.shields.io/npm/v/eslint-config-webpack.svg 42 | [npm-url]: https://npmjs.com/package/eslint-config-webpack 43 | [test]: https://github.com/webpack/eslint-config-webpack/actions/workflows/test.yml/badge.svg 44 | [test-url]: https://github.com/webpack/eslint-config-webpack/actions/workflows/test.yml 45 | -------------------------------------------------------------------------------- /plugins/webpack/rules/require-license-comment.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import("eslint").Rule} rule 3 | */ 4 | export const rule = { 5 | create(context) { 6 | const sourceCode = context.getSourceCode(); 7 | 8 | return { 9 | "Program:exit"(program) { 10 | const comments = sourceCode.getAllComments(); 11 | const licenseComment = comments.find( 12 | (comment) => 13 | comment.type === "Block" && 14 | /\n\s*MIT License http:\/\/www\.opensource\.org\/licenses\/mit-license\.php\n\s*(?:(Authors? .+)\n)?\s*/g.test( 15 | comment.value, 16 | ), 17 | ); 18 | 19 | if (!licenseComment) { 20 | context.report({ 21 | loc: program.loc, 22 | message: "Expected license comment.", 23 | }); 24 | 25 | return; 26 | } 27 | 28 | const afterComment = sourceCode.text[licenseComment.end]; 29 | 30 | if (afterComment !== "\n") { 31 | context.report({ 32 | loc: licenseComment.loc, 33 | message: "Expected newline after license comment.", 34 | }); 35 | 36 | return; 37 | } 38 | 39 | const afterAfterComment = sourceCode.text[licenseComment.end + 1]; 40 | 41 | if (afterAfterComment !== "\n") { 42 | context.report({ 43 | loc: licenseComment.loc, 44 | message: "Expected newline after license comment.", 45 | }); 46 | } 47 | }, 48 | }; 49 | }, 50 | meta: { 51 | docs: { 52 | category: "Best Practices", 53 | description: "Require license comment", 54 | recommended: true, 55 | }, 56 | fixable: "code", 57 | type: "layout", 58 | }, 59 | }; 60 | -------------------------------------------------------------------------------- /validation/universal/code.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {URL} url url 3 | * @returns {Promise} result 4 | */ 5 | async function readFile(url) { 6 | if (globalThis === globalThis.global) { 7 | const fs = (await import("node:fs")).default; 8 | 9 | return fs.promises.readFile(url, "utf8"); 10 | } 11 | 12 | const response = await fetch(url); 13 | 14 | return response.text(); 15 | } 16 | 17 | /** 18 | * @returns {Promise<[number, number, number]>} random numbers 19 | */ 20 | async function getRandomNumbers() { 21 | const crypto = 22 | globalThis === globalThis.global 23 | ? (await import("node:crypto")).webcrypto 24 | : globalThis.crypto; 25 | 26 | const array = new Uint8Array(3); 27 | const randomNumbers = crypto.getRandomValues(array); 28 | 29 | return [randomNumbers[0], randomNumbers[1], randomNumbers[2]]; 30 | } 31 | 32 | let content; 33 | 34 | try { 35 | content = await readFile(new URL("file.txt", import.meta.url)); 36 | } catch (err) { 37 | throw new Error("Error fetching data", { cause: err }); 38 | } 39 | 40 | /** 41 | * @returns {Promise} number 42 | */ 43 | async function loadModule() { 44 | if (globalThis === globalThis.global) { 45 | const { createRequire } = await import("node:module"); 46 | const require = createRequire(import.meta.url); 47 | 48 | return require("./module.cjs"); 49 | } 50 | 51 | const module = (await import("./module.mjs")).default; 52 | 53 | return module; 54 | } 55 | 56 | /** 57 | * @returns {string} title 58 | */ 59 | function getTitle() { 60 | return (globalThis.process && process.title) || document.title; 61 | } 62 | 63 | // eslint-disable-next-line 64 | console.log(content); 65 | // eslint-disable-next-line 66 | console.log(await getRandomNumbers()); 67 | // eslint-disable-next-line 68 | console.log(await loadModule()); 69 | // eslint-disable-next-line 70 | console.log(getTitle()); 71 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "eslint/config"; 2 | import configs from "./configs.js"; 3 | import config from "./index.js"; 4 | 5 | export default defineConfig([ 6 | { 7 | ignores: [ 8 | "./validation/commonjs-package/**/*", 9 | "./validation/module-package/**/*", 10 | "./validation/dirty-package/**/*", 11 | "./validation/browser/**/*", 12 | "./validation/browser-es5/**/*", 13 | "./validation/browser-outdated/**/*", 14 | "./validation/universal/**/*", 15 | ], 16 | extends: [config], 17 | }, 18 | // For test purposes 19 | { 20 | files: ["./validation/commonjs-package/**/*"], 21 | extends: [configs["node-recommended-commonjs"]], 22 | rules: { 23 | "n/no-unpublished-require": "off", 24 | }, 25 | }, 26 | { 27 | files: ["./validation/module-package/**/*"], 28 | extends: [configs["node-recommended-module"]], 29 | rules: { 30 | "n/no-unpublished-require": "off", 31 | }, 32 | }, 33 | { 34 | files: ["./validation/dirty-package/**/*"], 35 | extends: [configs["node-recommended-dirty"]], 36 | rules: { 37 | "n/no-unpublished-require": "off", 38 | }, 39 | }, 40 | { 41 | files: ["./validation/browser/**/*"], 42 | extends: [configs["browser-recommended"]], 43 | }, 44 | { 45 | files: ["./validation/browser-es5/**/*"], 46 | extends: [configs["browser-outdated-recommended-script"]], 47 | rules: { 48 | "n/no-unpublished-require": "off", 49 | }, 50 | }, 51 | { 52 | files: ["./validation/browser-outdated/**/*"], 53 | extends: [configs["browser-outdated-recommended"]], 54 | }, 55 | { 56 | files: ["./validation/universal/**/*"], 57 | extends: [configs["universal-recommended"]], 58 | rules: { 59 | "n/no-unsupported-features/node-builtins": "off", 60 | }, 61 | }, 62 | { 63 | files: ["./validation/hashbang.js"], 64 | rules: { 65 | "n/hashbang": "off", 66 | }, 67 | }, 68 | { 69 | files: ["./validation/webpack/**/*"], 70 | extends: [configs["node-recommended-commonjs"], configs["webpack/special"]], 71 | }, 72 | ]); 73 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-config-webpack", 3 | "version": "4.7.3", 4 | "description": "Provides Webpack's eslint rules as an extensible shared config", 5 | "keywords": [ 6 | "eslint", 7 | "eslintconfig", 8 | "config", 9 | "webpack", 10 | "javascript", 11 | "typescript" 12 | ], 13 | "homepage": "https://github.com/webpack/eslint-config-webpack#readme", 14 | "bugs": { 15 | "url": "https://github.com/webpack/eslint-config-webpack/issues" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/webpack/eslint-config-webpack.git" 20 | }, 21 | "license": "MIT", 22 | "author": "Joshua Wiens (https://twitter.com/@d3viant0ne)", 23 | "type": "module", 24 | "main": "index.js", 25 | "files": [ 26 | "index.js", 27 | "plugins", 28 | "prettier-config.js", 29 | "prettier-config-es5.js", 30 | "configs.js", 31 | "ignore-paths.js", 32 | "configs" 33 | ], 34 | "scripts": { 35 | "lint": "npm run lint:code", 36 | "lint:code": "eslint --cache .", 37 | "release": "standard-version" 38 | }, 39 | "dependencies": { 40 | "@eslint/js": "^9.39.2", 41 | "@eslint/markdown": "^7.5.1", 42 | "@stylistic/eslint-plugin": "^5.6.1", 43 | "detect-indent": "^7.0.2", 44 | "eslint-config-prettier": "^10.1.8", 45 | "eslint-plugin-import": "^2.32.0", 46 | "eslint-plugin-jest": "^29.5.0", 47 | "eslint-plugin-jsdoc": "^61.5.0", 48 | "eslint-plugin-n": "^17.23.1", 49 | "eslint-plugin-prettier": "^5.5.3", 50 | "eslint-plugin-react": "^7.37.5", 51 | "eslint-plugin-unicorn": "^62.0.0", 52 | "globals": "^16.5.0", 53 | "jsonc-eslint-parser": "^2.4.2", 54 | "semver": "^7.7.3", 55 | "sort-package-json": "^3.6.0", 56 | "typescript-eslint": "^8.50.0" 57 | }, 58 | "devDependencies": { 59 | "eslint": "^9.39.2", 60 | "eslint-find-rules": "^5.0.0", 61 | "jest": "^30.2.0", 62 | "prettier": "^3.7.4", 63 | "react": "^19.2.3", 64 | "react-dom": "^19.2.3", 65 | "standard-version": "^9.5.0", 66 | "typescript": "^5.9.3" 67 | }, 68 | "peerDependencies": { 69 | "eslint": ">= 9.28.0" 70 | }, 71 | "engines": { 72 | "node": ">= 20.9.0" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /configs/browser.js: -------------------------------------------------------------------------------- 1 | import importPlugin from "eslint-plugin-import"; 2 | import unicornPlugin from "eslint-plugin-unicorn"; 3 | import globals from "globals"; 4 | 5 | const recommendedBrowserOutdatedScriptConfig = { 6 | name: "browser/recommended-outdated-script", 7 | languageOptions: { 8 | sourceType: "script", 9 | globals: { 10 | ...globals.browser, 11 | }, 12 | }, 13 | }; 14 | 15 | const recommendedBrowserOutdatedCommonjsConfig = { 16 | name: "browser/recommended-outdated-commonjs", 17 | languageOptions: { 18 | sourceType: "commonjs", 19 | globals: { 20 | ...globals.browser, 21 | }, 22 | }, 23 | }; 24 | 25 | const recommendedBrowserOutdatedModuleConfig = { 26 | name: "browser/recommended-outdated-module", 27 | languageOptions: { 28 | sourceType: "module", 29 | globals: { 30 | ...globals.browser, 31 | }, 32 | }, 33 | }; 34 | 35 | const recommendedBrowserConfig = { 36 | name: "browser/recommended", 37 | languageOptions: { 38 | sourceType: "module", 39 | globals: { 40 | ...globals.browser, 41 | }, 42 | }, 43 | plugins: { 44 | unicorn: unicornPlugin, 45 | import: importPlugin, 46 | }, 47 | rules: { 48 | "unicorn/prefer-classlist-toggle": "error", 49 | 50 | "unicorn/prefer-dom-node-append": "error", 51 | 52 | "unicorn/prefer-dom-node-dataset": "error", 53 | 54 | "unicorn/prefer-dom-node-remove": "error", 55 | 56 | "unicorn/prefer-dom-node-text-content": "error", 57 | 58 | "unicorn/prefer-modern-dom-apis": "error", 59 | 60 | "unicorn/prefer-keyboard-event-key": "error", 61 | 62 | "unicorn/prefer-query-selector": "error", 63 | 64 | "import/extensions": [ 65 | "error", 66 | "always", 67 | { ignorePackages: true, checkTypeImports: true }, 68 | ], 69 | }, 70 | }; 71 | 72 | export default { 73 | "browser/recommended": recommendedBrowserConfig, 74 | // TODO remove me in the next major release 75 | "browser/recommended-outdated": recommendedBrowserOutdatedModuleConfig, 76 | // Useful when you need to generate outdated es5 code using babel/swc/etc 77 | "browser/recommended-outdated-script": recommendedBrowserOutdatedScriptConfig, 78 | "browser/recommended-outdated-commonjs": 79 | recommendedBrowserOutdatedCommonjsConfig, 80 | "browser/recommended-outdated-module": recommendedBrowserOutdatedModuleConfig, 81 | }; 82 | -------------------------------------------------------------------------------- /configs/stylistic.js: -------------------------------------------------------------------------------- 1 | import stylisticPlugin from "@stylistic/eslint-plugin"; 2 | import prettierPlugin from "eslint-plugin-prettier"; 3 | import prettierConfig from "eslint-plugin-prettier/recommended"; 4 | 5 | const recommendedConfig = { 6 | ...prettierConfig, 7 | name: "stylistic/recommended", 8 | plugins: { 9 | "@stylistic": stylisticPlugin, 10 | prettier: prettierPlugin, 11 | }, 12 | rules: { 13 | "prettier/prettier": "error", 14 | curly: ["error", "multi-line", "consistent"], 15 | "@stylistic/lines-between-class-members": "error", 16 | "@stylistic/quotes": [ 17 | "error", 18 | "double", 19 | { avoidEscape: true, allowTemplateLiterals: "never" }, 20 | ], 21 | "@stylistic/spaced-comment": [ 22 | "error", 23 | "always", 24 | { 25 | line: { 26 | // Space here to support sprockets directives 27 | markers: ["=", "!"], 28 | exceptions: ["-", "+"], 29 | }, 30 | block: { 31 | // Space here to support sprockets directives 32 | markers: ["=", "!"], 33 | exceptions: ["-", "+"], 34 | balanced: true, 35 | }, 36 | }, 37 | ], 38 | "@stylistic/padding-line-between-statements": [ 39 | "error", 40 | { blankLine: "always", prev: ["cjs-export"], next: "*" }, 41 | { blankLine: "always", prev: "*", next: ["cjs-export"] }, 42 | { 43 | blankLine: "any", 44 | prev: ["cjs-export"], 45 | next: ["cjs-export"], 46 | }, 47 | // Require newline between blocks of `cjs-import` and any lines between `cjs-import` 48 | { blankLine: "always", prev: ["cjs-import"], next: "*" }, 49 | { blankLine: "always", prev: "*", next: ["cjs-import"] }, 50 | { 51 | blankLine: "any", 52 | prev: ["cjs-import"], 53 | next: ["cjs-import"], 54 | }, 55 | // Require newline between blocks of `export` and any lines between `export` 56 | { blankLine: "always", prev: ["export"], next: "*" }, 57 | { blankLine: "always", prev: "*", next: ["export"] }, 58 | { 59 | blankLine: "any", 60 | prev: ["export"], 61 | next: ["export"], 62 | }, 63 | // Require newline between blocks of `import` and any lines between `import` 64 | { blankLine: "always", prev: ["import"], next: "*" }, 65 | { blankLine: "always", prev: "*", next: ["import"] }, 66 | { 67 | blankLine: "any", 68 | prev: ["import"], 69 | next: ["import"], 70 | }, 71 | // Require newline after directives 72 | { blankLine: "always", prev: ["directive"], next: "*" }, 73 | ], 74 | "@stylistic/lines-around-comment": [ 75 | "error", 76 | { 77 | beforeBlockComment: false, 78 | afterBlockComment: false, 79 | beforeLineComment: false, 80 | afterLineComment: false, 81 | afterHashbangComment: true, 82 | }, 83 | ], 84 | }, 85 | }; 86 | 87 | export default { 88 | "stylistic/recommended": recommendedConfig, 89 | }; 90 | -------------------------------------------------------------------------------- /configs/markdown.js: -------------------------------------------------------------------------------- 1 | import isTypescriptInstalled from "./utils/is-typescript-installed.js"; 2 | 3 | /** 4 | * @returns {Promise>} config 5 | */ 6 | async function getMarkdownRecommendedConfig() { 7 | let markdownPlugin; 8 | 9 | try { 10 | markdownPlugin = (await import("@eslint/markdown")).default; 11 | // eslint-disable-next-line unicorn/prefer-optional-catch-binding 12 | } catch (_err) { 13 | // Nothing 14 | } 15 | 16 | return [ 17 | { 18 | name: "markdown/code-blocks", 19 | files: ["**/*.md"], 20 | processor: "markdown/markdown", 21 | plugins: { 22 | markdown: markdownPlugin, 23 | }, 24 | }, 25 | { 26 | name: "markdown/code-blocks/js", 27 | files: isTypescriptInstalled() 28 | ? ["**/*.md/*.js", "**/*.md/*.ts"] 29 | : ["**/*.md/*.js"], 30 | languageOptions: { 31 | sourceType: "module", 32 | ecmaVersion: "latest", 33 | parserOptions: { 34 | ecmaFeatures: { 35 | globalReturn: true, 36 | impliedStrict: true, 37 | }, 38 | }, 39 | }, 40 | rules: { 41 | strict: "off", 42 | 43 | // For different examples 44 | camelcase: "off", 45 | 46 | "unicode-bom": "off", 47 | 48 | "eol-last": "off", 49 | 50 | "no-undef": "off", 51 | 52 | "no-unused-private-class-members": "off", 53 | 54 | "no-unused-vars": "off", 55 | 56 | "no-unused-expressions": "off", 57 | 58 | "no-unused-labels": "off", 59 | 60 | "no-console": "off", 61 | 62 | "no-new": "off", 63 | 64 | "unicorn/no-unused-properties": "off", 65 | 66 | // Allow to use any packages in documentation 67 | "n/no-unpublished-require": "off", 68 | 69 | // Allow to use any packages in documentation 70 | "n/no-unpublished-import": "off", 71 | 72 | // Allow to use any ES builtins in documentation 73 | "m/no-unsupported-features/es-builtins": "off", 74 | 75 | // Allow to use any ES syntax in documentation 76 | "n/no-unsupported-features/es-syntax": "off", 77 | 78 | // Allow to use any Node.js API in documentation 79 | "n/no-unsupported-features/node-builtins": "off", 80 | 81 | // Allow to use any packages in documentation 82 | "n/no-missing-import": "off", 83 | 84 | // Allow to use any packages in documentation 85 | "n/no-missing-require": "off", 86 | 87 | // Useful for documentation 88 | "n/no-process-exit": "off", 89 | 90 | "import/no-unresolved": "off", 91 | 92 | "import/no-extraneous-dependencies": "off", 93 | 94 | "jsdoc/require-jsdoc": "off", 95 | 96 | "@typescript-eslint/no-unused-vars": "off", 97 | 98 | "@typescript-eslint/no-explicit-any": "off", 99 | 100 | "@typescript-eslint/triple-slash-reference": "off", 101 | }, 102 | }, 103 | ]; 104 | } 105 | 106 | export default { 107 | "markdown/recommended": await getMarkdownRecommendedConfig(), 108 | }; 109 | -------------------------------------------------------------------------------- /configs/utils/get-json-file.js: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import path from "node:path"; 3 | 4 | const SKIP_TIME = 5000; 5 | 6 | class Cache { 7 | /** 8 | * Initialize this cache instance. 9 | */ 10 | constructor() { 11 | this.map = new Map(); 12 | } 13 | 14 | /** 15 | * Get the cached value of the given key. 16 | * @param {string} key The key to get. 17 | * @returns {import('type-fest').JsonObject} The cached value or null. 18 | */ 19 | get(key) { 20 | const entry = this.map.get(key); 21 | const now = Date.now(); 22 | 23 | if (entry) { 24 | if (entry.expire > now) { 25 | entry.expire = now + SKIP_TIME; 26 | return entry.value; 27 | } 28 | this.map.delete(key); 29 | } 30 | return null; 31 | } 32 | 33 | /** 34 | * Set the value of the given key. 35 | * @param {string} key The key to set. 36 | * @param {import('type-fest').JsonObject} value The value to set. 37 | * @returns {void} 38 | */ 39 | set(key, value) { 40 | const entry = this.map.get(key); 41 | const expire = Date.now() + SKIP_TIME; 42 | 43 | if (entry) { 44 | entry.value = value; 45 | entry.expire = expire; 46 | } else { 47 | this.map.set(key, { value, expire }); 48 | } 49 | } 50 | } 51 | 52 | const cache = new Cache(); 53 | 54 | /** 55 | * Reads the `package.json` data in a given path. 56 | * 57 | * Don't cache the data. 58 | * @param {string} dir The path to a directory to read. 59 | * @param {string} filename The filename. 60 | * @returns {import('type-fest').JsonObject|null} The read `package.json` data, or null. 61 | */ 62 | function readJsonFile(dir, filename) { 63 | const filePath = path.join(dir, filename); 64 | try { 65 | const text = fs.readFileSync(filePath, "utf8"); 66 | const data = JSON.parse(text); 67 | 68 | if ( 69 | data !== null && 70 | typeof data === "object" && 71 | Array.isArray(data) === false 72 | ) { 73 | data.filePath = filePath; 74 | return data; 75 | } 76 | // eslint-disable-next-line unicorn/prefer-optional-catch-binding 77 | } catch (_err) { 78 | // do nothing. 79 | } 80 | 81 | return null; 82 | } 83 | 84 | /** 85 | * Gets a `package.json` data. 86 | * The data is cached if found, then it's used after. 87 | * @param {string} filename The filename. 88 | * @param {string=} startPath A file path to lookup. 89 | * @returns {import('type-fest').JsonObject | null} A found `package.json` data or `null`. 90 | * This object have additional property `filePath`. 91 | */ 92 | function getJsonFile(filename, startPath = "a.js") { 93 | const startDir = path.dirname(path.resolve(startPath)); 94 | let dir = startDir; 95 | let prevDir = ""; 96 | let data = null; 97 | 98 | do { 99 | data = cache.get(dir + filename); 100 | if (data) { 101 | if (dir !== startDir) { 102 | cache.set(startDir + filename, data); 103 | } 104 | return data; 105 | } 106 | 107 | data = readJsonFile(dir, filename); 108 | if (data) { 109 | cache.set(dir + filename, data); 110 | cache.set(startDir + filename, data); 111 | return data; 112 | } 113 | 114 | // Go to next. 115 | prevDir = dir; 116 | dir = path.resolve(dir, ".."); 117 | } while (dir !== prevDir); 118 | 119 | cache.set(startDir + filename, null); 120 | return null; 121 | } 122 | 123 | export default getJsonFile; 124 | -------------------------------------------------------------------------------- /validation/my-ts-code.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from "node:fs"; 2 | // eslint-disable-next-line no-duplicate-imports 3 | import { type PathLike, type PathOrFileDescriptor } from "node:fs"; 4 | // eslint-disable-next-line import/consistent-type-specifier-style, no-duplicate-imports 5 | import type { TimeLike } from "node:fs"; 6 | 7 | import sum from "./module.js"; 8 | // eslint-disable-next-line import/extensions 9 | import otherSumAgain from "./my-module"; 10 | import { type MyType, b as otherB } from "./other-module.js"; 11 | 12 | require("./typescript-node-support/my-other-code.ts"); 13 | require("./typescript-node-support/my-other-code.js"); 14 | 15 | sum(a, b); 16 | otherSum(a, b); 17 | otherSumAgain(a, b); 18 | 19 | function getSomething(value: T): T { 20 | return value; 21 | } 22 | 23 | const isFunction = typeof readFileSync === "function"; 24 | 25 | getSomething(isFunction); 26 | 27 | const myPath: PathLike = new URL("file.txt", import.meta.url); 28 | const myPath2: PathOrFileDescriptor = new URL("file.text", import.meta.url); 29 | const myTime: TimeLike = new Date(); 30 | 31 | getSomething(myPath); 32 | getSomething(myPath2); 33 | getSomething(myTime); 34 | 35 | getSomething(otherB); 36 | 37 | const myValue: MyType = 5; 38 | 39 | getSomething(myValue); 40 | 41 | // Declaring variables with types 42 | // eslint-disable-next-line prefer-const 43 | let message = "Hello, TypeScript!"; 44 | const count = 10; 45 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 46 | const isDone = false; 47 | 48 | type myStringType = string; 49 | 50 | // Function with type annotations 51 | function add(a: number, b: number): number { 52 | return a + b; 53 | } 54 | 55 | getSomething(message); 56 | getSomething(add(count, 5)); 57 | 58 | // Defining an interface 59 | interface Person { 60 | name: myStringType; 61 | age: number; 62 | occupation?: string; // Optional property 63 | } 64 | 65 | // Using the interface 66 | const user: Person = { 67 | name: "John Doe", 68 | age: 30, 69 | occupation: "Software Engineer", 70 | }; 71 | 72 | function greet(person: Person): string { 73 | return `Hello, ${person.name}!`; 74 | } 75 | 76 | getSomething(greet(user)); 77 | 78 | // Defining a class 79 | class Animal { 80 | name: string; 81 | 82 | constructor(name: string) { 83 | this.name = name; 84 | } 85 | 86 | makeSound(): void { 87 | getSomething("Generic animal sound"); 88 | } 89 | } 90 | 91 | // Subclassing 92 | class Dog extends Animal { 93 | breed: string; 94 | 95 | constructor(name: string, breed: string) { 96 | super(name); 97 | this.breed = breed; 98 | } 99 | 100 | override makeSound(): void { 101 | getSomething("Woof!"); 102 | } 103 | } 104 | 105 | const animal = new Animal("Animal"); 106 | const dog = new Dog("Buddy", "Golden Retriever"); 107 | 108 | animal.makeSound(); 109 | dog.makeSound(); 110 | 111 | function getString(myVar) { 112 | return myVar; 113 | } 114 | 115 | getString("test"); 116 | 117 | // Generic function 118 | function identity(arg: T): T { 119 | return arg; 120 | } 121 | 122 | const myString: string = identity("hello"); 123 | const myNumber: number = identity(123); 124 | 125 | getSomething(myString); 126 | getSomething(myNumber); 127 | 128 | const myArray = [1, 2, 3] as const; 129 | 130 | myArray.map((value) => sum(value, 1)); 131 | 132 | function myFuncBar(myArray: T): T { 133 | return myArray; 134 | } 135 | 136 | myFuncBar(["foo", "bar"]); 137 | 138 | type T = string; 139 | 140 | const myConst: T = "string"; 141 | 142 | interface MyT { 143 | x: number; 144 | } 145 | 146 | const myX: MyT = { x: 100 }; 147 | 148 | identity(myConst); 149 | identity(myX); 150 | 151 | interface ButtonProps { 152 | onClick: () => void; 153 | } 154 | 155 | class Button implements ButtonProps { 156 | onClick = () => getSomething("button!"); 157 | } 158 | 159 | // eslint-disable-next-line @typescript-eslint/no-empty-function 160 | function test() {} 161 | 162 | const arrowFn = () => "test"; 163 | 164 | class Test { 165 | // Should indicate that no value is returned (void) 166 | method() { 167 | // Method 168 | } 169 | } 170 | 171 | function myTest(): void { 172 | // Function 173 | } 174 | 175 | class MyAnimal { 176 | constructor( 177 | public breed, 178 | name, 179 | ) { 180 | // Parameter property and constructor 181 | this.animalName = name; 182 | } 183 | 184 | private animalName: string; // Property 185 | 186 | get name(): string { 187 | // get accessor 188 | return this.animalName; 189 | } 190 | 191 | set name(value: string) { 192 | // set accessor 193 | this.animalName = value; 194 | } 195 | 196 | // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility 197 | public walk() { 198 | // method 199 | } 200 | 201 | otherWalk() { 202 | // Public method too 203 | } 204 | } 205 | 206 | // eslint-disable-next-line prefer-exponentiation-operator 207 | const foo = Math.pow(2, 8); 208 | 209 | sum(1, foo); 210 | 211 | export { Button, MyAnimal, Test, arrowFn, myTest, test }; 212 | export type { ButtonProps }; 213 | -------------------------------------------------------------------------------- /plugins/package-json/rules/order-properties.js: -------------------------------------------------------------------------------- 1 | import detectIndent from "detect-indent"; 2 | import { sortOrder } from "sort-package-json"; 3 | 4 | /** 5 | * @param {string} string string 6 | * @returns {"\r\n" | "\n"} detected newline 7 | */ 8 | function detectNewline(string) { 9 | if (typeof string !== "string") { 10 | throw new TypeError("Expected a string"); 11 | } 12 | 13 | const newlines = string.match(/(?:\r?\n)/g) || []; 14 | 15 | if (newlines.length === 0) { 16 | return; 17 | } 18 | 19 | const crlf = newlines.filter((newline) => newline === "\r\n").length; 20 | const lf = newlines.length - crlf; 21 | 22 | return crlf > lf ? "\r\n" : "\n"; 23 | } 24 | 25 | /** 26 | * @param {string} string string 27 | * @returns {string} result 28 | */ 29 | function detectNewlineGraceful(string) { 30 | return detectNewline(string) || "\n"; 31 | } 32 | 33 | // eslint-disable-next-line jsdoc/reject-any-type 34 | /** @typedef {Record} ObjectToSort */ 35 | 36 | /** 37 | * @param {ObjectToSort} object object to sort 38 | * @param {(a: number, b: number) => number} sortWith function to sort 39 | * @returns {ObjectToSort} object with sorted properties 40 | */ 41 | function sortObjectKeys(object, sortWith) { 42 | let keys; 43 | let sortFn; 44 | 45 | if (typeof sortWith === "function") { 46 | sortFn = sortWith; 47 | } else { 48 | keys = sortWith; 49 | } 50 | 51 | const objectKeys = Object.keys(object); 52 | 53 | return (keys || objectKeys.toSorted(sortFn)).reduce((total, key) => { 54 | if (Object.hasOwn(object, key)) { 55 | total[key] = object[key]; 56 | } 57 | 58 | return total; 59 | }, Object.create(null)); 60 | } 61 | 62 | /** 63 | * @param {string} filePath file path 64 | * @returns {boolean} result 65 | */ 66 | export const isPackageJson = (filePath) => 67 | /(?:^|[/\\])package.json$/.test(filePath); 68 | 69 | /* eslint-disable jsdoc/reject-any-type */ 70 | /** 71 | * @typedef {import("eslint").AST.Program} PackageJsonAst 72 | * @property {[any]} body body 73 | */ 74 | /* eslint-enable jsdoc/reject-any-type */ 75 | 76 | /** 77 | * @typedef {import("eslint").SourceCode} PackageJsonSourceCode 78 | * @property {PackageJsonAst} ast ast 79 | */ 80 | 81 | /** 82 | * @template {unknown[]} [Options=unknown[]] 83 | * @typedef {import("eslint").RuleContext} PackageJsonRuleContext 84 | * @property {Options} options options 85 | * @property {PackageJsonSourceCode} sourceCode source code 86 | */ 87 | 88 | /** 89 | * @template {unknown[]} Options 90 | * @typedef {object} PackageJsonRuleModule 91 | * @property {import("eslint").Rule.RuleMetaData} meta meta 92 | * @property {(context: PackageJsonRuleContext) => import("eslint").RuleListener} create function to create 93 | */ 94 | 95 | /** 96 | * @type {import("eslint").Rule} rule 97 | */ 98 | export const rule = { 99 | create(context) { 100 | if (!isPackageJson(context.filename)) { 101 | return {}; 102 | } 103 | 104 | return { 105 | "Program:exit"() { 106 | const { ast, text } = context.sourceCode; 107 | 108 | const options = { 109 | order: "sort-package-json", 110 | ...context.options[0], 111 | }; 112 | 113 | const requiredOrder = 114 | options.order === "sort-package-json" ? sortOrder : options.order; 115 | 116 | const json = JSON.parse(text); 117 | const orderedSource = sortObjectKeys(json, [ 118 | ...requiredOrder, 119 | ...Object.keys(json), 120 | ]); 121 | const orderedKeys = Object.keys(orderedSource); 122 | 123 | const { properties } = ast.body[0].expression; 124 | 125 | for (let i = 0; i < properties.length; i += 1) { 126 | const property = properties[i].key; 127 | const { value } = property; 128 | 129 | if (value === orderedKeys[i]) { 130 | continue; 131 | } 132 | 133 | context.report({ 134 | data: { 135 | property: value, 136 | }, 137 | fix(fixer) { 138 | const { indent, type } = detectIndent(text); 139 | const endCharacters = text.endsWith("\n") ? "\n" : ""; 140 | const newline = detectNewlineGraceful(text); 141 | let result = 142 | JSON.stringify( 143 | orderedSource, 144 | null, 145 | type === "tab" ? "\t" : indent, 146 | ) + endCharacters; 147 | if (newline === "\r\n") { 148 | result = result.replaceAll("\n", newline); 149 | } 150 | 151 | return fixer.replaceText(context.sourceCode.ast, result); 152 | }, 153 | loc: properties[i].loc, 154 | messageId: "incorrectOrder", 155 | }); 156 | } 157 | }, 158 | }; 159 | }, 160 | meta: { 161 | docs: { 162 | category: "Best Practices", 163 | description: "Package properties must be declared in standard order", 164 | recommended: true, 165 | }, 166 | fixable: "code", 167 | messages: { 168 | incorrectOrder: 169 | 'Package top-level property "{{property}}" is not ordered in the npm standard way. Run the ESLint auto-fixer to correct.', 170 | }, 171 | schema: [ 172 | { 173 | properties: { 174 | order: { 175 | anyOf: [ 176 | { 177 | enum: ["sort-package-json"], 178 | type: ["string"], 179 | }, 180 | ], 181 | }, 182 | }, 183 | type: "object", 184 | }, 185 | ], 186 | type: "layout", 187 | }, 188 | }; 189 | -------------------------------------------------------------------------------- /configs/jest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @returns {Promise>} config 3 | */ 4 | async function getJestRecommendedConfig() { 5 | let jestPlugin; 6 | 7 | try { 8 | jestPlugin = (await import("eslint-plugin-jest")).default; 9 | // eslint-disable-next-line unicorn/prefer-optional-catch-binding 10 | } catch (_err) { 11 | // Nothing 12 | } 13 | 14 | const jsdocConfig = 15 | (jestPlugin && jestPlugin.configs["flat/recommended"]) || {}; 16 | 17 | return { 18 | ...jsdocConfig, 19 | name: "jest/recommended", 20 | files: [ 21 | "**/{tests,test,__tests__}/**/*.?(c|m)[jt]s?(x)", 22 | "**/?(*.)+(spec|test).[jt]s?(x)", 23 | "**/test-*.[jt]s?(x)", 24 | ], 25 | ignores: [ 26 | "**/{tests,test,__tests__}/**/{helper,helpers,__helper__,__helpers__,fixture,fixtures,__fixture__,__fixtures__}/**/*", 27 | "**/helper?(s).{js,cjs,mjs}", 28 | ], 29 | rules: { 30 | ...jsdocConfig.rules, 31 | 32 | "jest/consistent-test-it": "error", 33 | 34 | "jest/expect-expect": "error", 35 | 36 | // No need 37 | // "jest/max-expects": "error", 38 | 39 | // No need 40 | // "jest/max-nested-describe": "error", 41 | 42 | // From recommended 43 | // "jest/no-alias-methods": "error", 44 | 45 | "jest/no-commented-out-tests": "error", 46 | 47 | // No need 48 | // Makes too much noise, testing conditions can often be different 49 | "jest/no-conditional-expect": "off", 50 | 51 | // No need 52 | // "jest/no-conditional-in-test": "off", 53 | 54 | "jest/no-confusing-set-timeout": "error", 55 | 56 | // From recommended 57 | // "jest/no-deprecated-functions": "error", 58 | 59 | "jest/no-disabled-tests": "error", 60 | 61 | // No need 62 | // Adding extra `await new Promise(...)` is very inconvenient when you have a lot of callback api 63 | "jest/no-done-callback": "off", 64 | 65 | "jest/no-duplicate-hooks": "error", 66 | 67 | // From recommended 68 | // "jest/no-export": "error", 69 | 70 | "jest/no-focused-tests": "error", 71 | 72 | // No need 73 | // "jest/no-hooks": "error", 74 | 75 | // From recommended 76 | // "jest/no-identical-title": "error", 77 | 78 | // From recommended 79 | // "jest/no-interpolation-in-snapshots": "error", 80 | 81 | // From recommended 82 | // "jest/no-jasmine-globals": "error", 83 | 84 | // No need 85 | // "jest/no-large-snapshots": "error", 86 | 87 | // From recommended 88 | // "jest/no-mocks-import": "error", 89 | 90 | // No need 91 | // "jest/no-restricted-jest-methods": ["error", {}], 92 | 93 | // No need 94 | // "jest/no-restricted-matchers": ["error", {}], 95 | 96 | // From recommended 97 | // "jest/no-standalone-expect": "error", 98 | 99 | // From recommended 100 | // "jest/no-test-prefixes": "error", 101 | 102 | // No need 103 | // "jest/no-test-return-statement": "error", 104 | 105 | "jest/no-unneeded-async-expect-function": "error", 106 | 107 | // No need 108 | // "jest/no-untyped-mock-factory": "error", 109 | 110 | "jest/padding-around-after-all-blocks": "error", 111 | 112 | "jest/padding-around-after-each-blocks": "error", 113 | 114 | // Not all padding required 115 | // "jest/padding-around-all": "off", 116 | 117 | "jest/padding-around-before-all-blocks": "error", 118 | 119 | "jest/padding-around-before-each-blocks": "error", 120 | 121 | "jest/padding-around-describe-blocks": "error", 122 | 123 | "jest/padding-around-expect-groups": "off", 124 | 125 | "jest/padding-around-test-blocks": "error", 126 | 127 | // No need 128 | // "jest/prefer-called-with": "error", 129 | 130 | "jest/prefer-comparison-matcher": "error", 131 | 132 | // No need 133 | // "jest/prefer-each": "error", 134 | 135 | "jest/prefer-equality-matcher": "error", 136 | 137 | // No need 138 | // "jest/prefer-expect-assertions": "error", 139 | 140 | // No need 141 | // "jest/prefer-expect-resolves": "error", 142 | 143 | "jest/prefer-hooks-in-order": "error", 144 | 145 | "jest/prefer-hooks-on-top": "error", 146 | 147 | // No need 148 | // "jest/prefer-importing-jest-globals": "error", 149 | 150 | "jest/prefer-jest-mocked": "error", 151 | 152 | // Allow to use `MyClass` title in describe for class testing 153 | "jest/prefer-lowercase-title": ["error", { ignore: ["describe"] }], 154 | 155 | "jest/prefer-mock-promise-shorthand": "error", 156 | 157 | // No need 158 | // "jest/prefer-snapshot-hint": "error", 159 | 160 | "jest/prefer-spy-on": "error", 161 | 162 | // No need 163 | // "jest/prefer-strict-equal": "off", 164 | 165 | "jest/prefer-to-be": "error", 166 | 167 | "jest/prefer-to-contain": "error", 168 | 169 | "jest/prefer-to-have-been-called": "error", 170 | 171 | "jest/prefer-to-have-been-called-times": "error", 172 | 173 | "jest/prefer-to-have-length": "error", 174 | 175 | // No need 176 | // "jest/prefer-todo": "error" 177 | 178 | // No need 179 | // Does not allow using the function as test generation 180 | "jest/require-hook": "off", 181 | 182 | "jest/require-to-throw-message": "error", 183 | 184 | "jest/require-top-level-describe": "error", 185 | 186 | // From recommended 187 | // "jest/valid-describe-callback": "error", 188 | 189 | // From recommended 190 | // "jest/valid-expect": "error", 191 | 192 | // From recommended 193 | // "jest/valid-expect-in-promise": "error", 194 | 195 | // No need 196 | // "valid-mock-module-path": "off", 197 | 198 | // From recommended 199 | "jest/valid-title": [ 200 | "error", 201 | { 202 | // Allow to use variables in tests 203 | ignoreTypeOfDescribeName: true, 204 | // Allow to use variables in tests 205 | ignoreTypeOfTestName: true, 206 | }, 207 | ], 208 | 209 | // Disable it for tests, because often you can use `a`, `b`, `c` and etc variables 210 | "id-length": "off", 211 | 212 | // In tests, we can have any names 213 | camelcase: "off", 214 | 215 | // Allow to output information in tests 216 | "no-console": "off", 217 | 218 | // Allow to use `eval` for tests 219 | "no-eval": "off", 220 | 221 | // Allow to have any regexps in tests, useful to clean up/etc 222 | "no-control-regex": "off", 223 | 224 | // Allow to use any builtins, syntax and node API in tests 225 | "n/no-unsupported-features/es-builtins": "off", 226 | "n/no-unsupported-features/es-syntax": "off", 227 | "n/no-unsupported-features/node-builtins": "off", 228 | 229 | // Allow to `require` and `import` any things in tests 230 | "n/no-unpublished-require": "off", 231 | "n/no-unpublished-import": "off", 232 | 233 | // Doesn't require jsdoc for tests, they are either redundant or we have a separate task for checking types of tests 234 | "jsdoc/require-jsdoc": "off", 235 | }, 236 | }; 237 | } 238 | 239 | export default { 240 | "jest/recommended": await getJestRecommendedConfig(), 241 | }; 242 | -------------------------------------------------------------------------------- /configs/node.js: -------------------------------------------------------------------------------- 1 | import importPlugin from "eslint-plugin-import"; 2 | import globals from "globals"; 3 | import isTypescriptInstalled from "./utils/is-typescript-installed.js"; 4 | 5 | const commonRules = { 6 | // No need 7 | // "n/callback-return": "error", 8 | 9 | // Depends on `sourceType` and enabled below only for commonjs 10 | // "n/exports-style": "error", 11 | 12 | // We have the `import/extensions` rule 13 | "n/file-extension-in-import": "off", 14 | 15 | // There is no need, as in some cases we want to load a module lazily. 16 | // "n/global-require": "error", 17 | 18 | // No need 19 | // "n/handle-callback-err": "error" 20 | 21 | // From recommended 22 | // "n/hashbang": "error", 23 | 24 | // No need 25 | // "n/no-callback-literal": "error", 26 | 27 | // From recommended 28 | // "n/no-deprecated-api": "error", 29 | 30 | // From recommended 31 | // "n/no-exports-assign": "error", 32 | 33 | // We have `import/no-extraneous-dependencies` rule 34 | "n/no-extraneous-import": "off", 35 | 36 | // We have `import/no-extraneous-dependencies` rule 37 | "n/no-extraneous-require": "off", 38 | 39 | // Deprecated 40 | // "n/no-hide-core-modules": "error", 41 | 42 | // We have `import/no-unresolved` rule 43 | "n/no-missing-import": "off", 44 | 45 | // We have `import/no-unresolved` rule 46 | "n/no-missing-require": "off", 47 | 48 | // No need 49 | // "n/no-mixed-requires": "error", 50 | 51 | // No need 52 | // "n/no-new-require": "error", 53 | 54 | // Depends on `sourceType` and enabled below only for commonjs 55 | // "n/no-path-concat": "error", 56 | 57 | // No need 58 | // "n/no-process-env": "error", 59 | 60 | // From recommended 61 | // "n/no-process-exit": "error", 62 | 63 | // No need 64 | // "n/no-restricted-import": ["error", []], 65 | 66 | // No need 67 | // "n/no-restricted-require": ["error", []] 68 | 69 | // No need 70 | // "n/no-sync": "error", 71 | 72 | // No need 73 | // "n/no-top-level-await": "error", 74 | 75 | // From recommended 76 | // "n/no-unpublished-bin": "error", 77 | 78 | // From recommended 79 | // "n/no-unpublished-import": "error", 80 | 81 | // From recommended 82 | // "n/no-unpublished-require": "error", 83 | 84 | // From recommended 85 | // "n/no-unsupported-features/es-builtins": "error", 86 | 87 | // From recommended (override) 88 | "n/no-unsupported-features/es-syntax": [ 89 | "error", 90 | { 91 | ignores: ["error-cause"], 92 | }, 93 | ], 94 | 95 | // "n/no-unsupported-features/es-syntax": "error", 96 | 97 | // From recommended 98 | // "n/no-unsupported-features/node-builtins": "error", 99 | 100 | "n/prefer-global/buffer": ["error", "always"], 101 | 102 | "n/prefer-global/console": ["error", "always"], 103 | 104 | "n/prefer-global/process": ["error", "always"], 105 | 106 | "n/prefer-global/text-decoder": ["error", "always"], 107 | 108 | "n/prefer-global/text-encoder": ["error", "always"], 109 | 110 | "n/prefer-global/url": ["error", "always"], 111 | 112 | "n/prefer-global/url-search-params": ["error", "always"], 113 | 114 | "n/prefer-node-protocol": "error", 115 | 116 | // No need 117 | // "n/prefer-promises/dns": "error", 118 | 119 | // No need 120 | // "n/prefer-promises/fs": "error", 121 | 122 | // From recommended 123 | // "n/process-exit-as-throw": "error", 124 | }; 125 | 126 | let nodePlugin; 127 | 128 | const ignores = ["**/*.d.ts"]; 129 | 130 | /** 131 | * @returns {Promise>} config 132 | */ 133 | async function getCommonJSConfig() { 134 | if (!nodePlugin) { 135 | try { 136 | nodePlugin = (await import("eslint-plugin-n")).default; 137 | // eslint-disable-next-line unicorn/prefer-optional-catch-binding 138 | } catch (_err) { 139 | // Nothing 140 | } 141 | } 142 | 143 | const nodeConfig = 144 | (nodePlugin && nodePlugin.configs["flat/recommended-script"]) || {}; 145 | 146 | return { 147 | ...nodeConfig, 148 | name: "node/commonjs", 149 | ignores, 150 | plugins: { 151 | ...nodeConfig.plugins, 152 | import: importPlugin, 153 | }, 154 | rules: { 155 | ...nodeConfig.rules, 156 | ...commonRules, 157 | "n/exports-style": "error", 158 | "n/no-path-concat": "error", 159 | "import/extensions": [ 160 | "error", 161 | "always", 162 | { 163 | ignorePackages: true, 164 | checkTypeImports: true, 165 | pattern: { 166 | js: "never", 167 | ts: "never", 168 | }, 169 | // Allow to have `ts` extension in `require` for compatibility with Node.js built-in typescript support 170 | pathGroupOverrides: [ 171 | { 172 | pattern: "*.ts", 173 | patternOptions: { 174 | matchBase: true, 175 | }, 176 | action: "ignore", 177 | }, 178 | ], 179 | }, 180 | ], 181 | }, 182 | }; 183 | } 184 | 185 | /** 186 | * @returns {Promise>} config 187 | */ 188 | async function getModuleConfig() { 189 | let nodePlugin; 190 | 191 | if (!nodePlugin) { 192 | try { 193 | nodePlugin = (await import("eslint-plugin-n")).default; 194 | // eslint-disable-next-line unicorn/prefer-optional-catch-binding 195 | } catch (_err) { 196 | // Nothing 197 | } 198 | } 199 | 200 | const nodeConfig = 201 | (nodePlugin && nodePlugin.configs["flat/recommended-module"]) || {}; 202 | 203 | return { 204 | ...nodeConfig, 205 | name: "node/module", 206 | ignores, 207 | plugins: { 208 | ...nodeConfig.plugins, 209 | import: importPlugin, 210 | }, 211 | rules: { 212 | ...nodeConfig.rules, 213 | ...commonRules, 214 | "n/no-unsupported-features/es-syntax": [ 215 | "error", 216 | { 217 | ignores: ["modules", "error-cause"], 218 | }, 219 | ], 220 | "import/extensions": [ 221 | "error", 222 | "always", 223 | { ignorePackages: true, checkTypeImports: true }, 224 | ], 225 | }, 226 | }; 227 | } 228 | 229 | const commonjsConfig = await getCommonJSConfig(); 230 | const moduleConfig = await getModuleConfig(); 231 | 232 | /** 233 | * @returns {Promise>} config 234 | */ 235 | async function getDirtyConfig() { 236 | let nodePlugin; 237 | 238 | if (!nodePlugin) { 239 | try { 240 | nodePlugin = (await import("eslint-plugin-n")).default; 241 | // eslint-disable-next-line unicorn/prefer-optional-catch-binding 242 | } catch (_err) { 243 | // Nothing 244 | } 245 | } 246 | 247 | return { 248 | name: "node/dirty", 249 | plugins: { 250 | n: nodePlugin, 251 | import: importPlugin, 252 | }, 253 | ignores: [...new Set([...commonjsConfig.ignores, ...moduleConfig.ignores])], 254 | languageOptions: { 255 | sourceType: "module", 256 | parserOptions: { 257 | ecmaFeatures: { globalReturn: true }, 258 | }, 259 | globals: { 260 | ...globals.node, 261 | __dirname: "readonly", 262 | __filename: "readonly", 263 | exports: "writable", 264 | module: "readonly", 265 | require: "readonly", 266 | }, 267 | }, 268 | rules: { 269 | ...commonjsConfig.rules, 270 | ...moduleConfig.rules, 271 | 272 | // Disable for dirty modules 273 | "import/extensions": ["off"], 274 | }, 275 | }; 276 | } 277 | 278 | const dirtyConfig = await getDirtyConfig(); 279 | 280 | const jsExtensions = isTypescriptInstalled() 281 | ? ["**/*.{js,jsx,ts,tsx}"] 282 | : ["**/*.{js,jsx}"]; 283 | const cjsExtensions = isTypescriptInstalled() 284 | ? ["**/*.{cjs,cts}"] 285 | : ["**/*.{cjs}"]; 286 | const mjsExtensions = isTypescriptInstalled() 287 | ? ["**/*.{mjs,mts}"] 288 | : ["**/*.{mjs}"]; 289 | 290 | export default { 291 | "node/dirty": dirtyConfig, 292 | "node/commonjs": commonjsConfig, 293 | "node/module": moduleConfig, 294 | "node/recommended": moduleConfig, 295 | "node/mixed-dirty": [ 296 | { 297 | files: jsExtensions, 298 | ...dirtyConfig, 299 | }, 300 | { 301 | files: cjsExtensions, 302 | ...commonjsConfig, 303 | }, 304 | { 305 | files: mjsExtensions, 306 | ...moduleConfig, 307 | }, 308 | ], 309 | "node/mixed-module-and-commonjs": [ 310 | { 311 | files: jsExtensions, 312 | ...moduleConfig, 313 | }, 314 | { 315 | files: cjsExtensions, 316 | ...commonjsConfig, 317 | }, 318 | { 319 | files: mjsExtensions, 320 | ...moduleConfig, 321 | }, 322 | ], 323 | "node/mixed-commonjs-and-module": [ 324 | { 325 | files: jsExtensions, 326 | ...commonjsConfig, 327 | }, 328 | { 329 | files: cjsExtensions, 330 | ...commonjsConfig, 331 | }, 332 | { 333 | files: mjsExtensions, 334 | ...moduleConfig, 335 | }, 336 | ], 337 | }; 338 | -------------------------------------------------------------------------------- /configs.js: -------------------------------------------------------------------------------- 1 | import { globalIgnores } from "eslint/config"; 2 | import semver from "semver"; 3 | import configs from "./configs/index.js"; 4 | import { typescriptExtensions } from "./configs/utils/extensions.js"; 5 | import getJsonFile from "./configs/utils/get-json-file.js"; 6 | import isTypescriptInstalled from "./configs/utils/is-typescript-installed.js"; 7 | import ignorePaths from "./ignore-paths.js"; 8 | 9 | const packageJson = getJsonFile("package.json"); 10 | const isModule = 11 | packageJson !== null && 12 | typeof packageJson === "object" && 13 | "type" in packageJson && 14 | packageJson.type === "module"; 15 | 16 | /** 17 | * @returns {Record} javascript configuration 18 | */ 19 | function getJavascriptConfig() { 20 | if (packageJson.engines && packageJson.engines.node) { 21 | const minVersion = semver.minVersion(packageJson.engines.node).major; 22 | 23 | // https://node.green/ 24 | // https://github.com/microsoft/TypeScript/wiki/Node-Target-Mapping 25 | switch (minVersion) { 26 | case 6: { 27 | const config = { ...configs["javascript/es2016"] }; 28 | 29 | config.rules["prefer-exponentiation-operator"] = "off"; 30 | 31 | return config; 32 | } 33 | case 7: 34 | return configs["javascript/es2016"]; 35 | case 8: 36 | case 9: 37 | return configs["javascript/es2017"]; 38 | case 10: 39 | case 11: 40 | return configs["javascript/es2018"]; 41 | case 12: 42 | case 13: { 43 | const languageOptions = { 44 | ...configs["javascript/es2019"].languageOptions, 45 | }; 46 | 47 | languageOptions.globals.Promise = false; 48 | languageOptions.globals.BigInt = false; 49 | 50 | return { ...configs["javascript/es2019"], languageOptions }; 51 | } 52 | case 14: 53 | return configs["javascript/es2020"]; 54 | 55 | case 15: 56 | return configs["javascript/es2021"]; 57 | case 16: 58 | case 17: 59 | case 18: 60 | case 19: 61 | return configs["javascript/es2022"]; 62 | case 20: 63 | case 21: 64 | return configs["javascript/es2023"]; 65 | case 22: 66 | case 23: 67 | return configs["javascript/es2024"]; 68 | case 24: 69 | case 25: 70 | return configs["javascript/es2025"]; 71 | default: 72 | return configs["javascript/recommended"]; 73 | } 74 | } 75 | 76 | return configs["javascript/recommended"]; 77 | } 78 | 79 | /** 80 | * @returns {Promise>} config 81 | */ 82 | function getTypescriptJSdocConfig() { 83 | return isTypescriptInstalled() ? configs["typescript/jsdoc"] : []; 84 | } 85 | 86 | /** 87 | * @returns {Promise>} config 88 | */ 89 | function getTypescriptConfig() { 90 | if (!isTypescriptInstalled()) { 91 | return {}; 92 | } 93 | 94 | const tsconfigJson = getJsonFile("tsconfig.json"); 95 | 96 | const isNoEmitEnabled = 97 | (tsconfigJson && 98 | tsconfigJson.compilerOptions && 99 | tsconfigJson.compilerOptions.noEmit) || 100 | false; 101 | 102 | if (isNoEmitEnabled) { 103 | return {}; 104 | } 105 | 106 | const isStrict = 107 | (tsconfigJson && 108 | tsconfigJson.compilerOptions && 109 | tsconfigJson.compilerOptions.strict) || 110 | true; 111 | 112 | return [ 113 | configs["typescript/recommended"], 114 | isStrict 115 | ? { 116 | files: [ 117 | `**/*.{${typescriptExtensions.map((item) => item.slice(1)).join(",")}}`, 118 | ], 119 | ignores: ["**/*.d.ts"], 120 | rules: { strict: "off" }, 121 | } 122 | : {}, 123 | ]; 124 | } 125 | 126 | /** 127 | * @returns {Promise>} config 128 | */ 129 | function getReactConfig() { 130 | if (packageJson === null) { 131 | return []; 132 | } 133 | 134 | const dependencies = packageJson.dependencies || []; 135 | const devDependencies = packageJson.devDependencies || []; 136 | 137 | return typeof dependencies.react !== "undefined" || 138 | typeof devDependencies.react !== "undefined" 139 | ? configs["react/recommended"] 140 | : []; 141 | } 142 | 143 | /** 144 | * @returns {Promise>} config 145 | */ 146 | function getJestConfig() { 147 | if (packageJson === null) { 148 | return []; 149 | } 150 | 151 | const dependencies = packageJson.dependencies || []; 152 | const devDependencies = packageJson.devDependencies || []; 153 | 154 | return typeof dependencies.jest !== "undefined" || 155 | typeof devDependencies.jest !== "undefined" 156 | ? configs["jest/recommended"] 157 | : []; 158 | } 159 | 160 | const javascriptConfig = getJavascriptConfig(); 161 | const typescriptJSDocConfig = getTypescriptJSdocConfig(); 162 | const typescriptConfig = getTypescriptConfig(); 163 | const reactConfig = getReactConfig(); 164 | const jestConfig = getJestConfig(); 165 | 166 | const recommended = [ 167 | globalIgnores(ignorePaths), 168 | isModule 169 | ? configs["node/mixed-module-and-commonjs"] 170 | : configs["node/mixed-commonjs-and-module"], 171 | javascriptConfig, 172 | typescriptJSDocConfig, 173 | typescriptConfig, 174 | reactConfig, 175 | jestConfig, 176 | configs["markdown/recommended"], 177 | configs["stylistic/recommended"], 178 | configs["package-json/recommended"], 179 | ]; 180 | 181 | // TODO remove me in the next major release 182 | configs.recommended = recommended; 183 | 184 | configs["node-recommended"] = recommended; 185 | 186 | const nodeRecommendedModule = [ 187 | globalIgnores(ignorePaths), 188 | configs["node/mixed-module-and-commonjs"], 189 | javascriptConfig, 190 | typescriptJSDocConfig, 191 | typescriptConfig, 192 | reactConfig, 193 | jestConfig, 194 | configs["markdown/recommended"], 195 | configs["stylistic/recommended"], 196 | configs["package-json/recommended"], 197 | ]; 198 | 199 | // TODO remove me in the next major release 200 | configs["recommended-module"] = nodeRecommendedModule; 201 | 202 | configs["node-recommended-module"] = nodeRecommendedModule; 203 | 204 | const nodeRecommendedCommonJS = [ 205 | globalIgnores(ignorePaths), 206 | configs["node/mixed-commonjs-and-module"], 207 | javascriptConfig, 208 | typescriptJSDocConfig, 209 | typescriptConfig, 210 | reactConfig, 211 | jestConfig, 212 | configs["markdown/recommended"], 213 | configs["stylistic/recommended"], 214 | configs["package-json/recommended"], 215 | ]; 216 | 217 | // TODO remove me in the next major release 218 | configs["recommended-commonjs"] = nodeRecommendedCommonJS; 219 | 220 | configs["node-recommended-commonjs"] = nodeRecommendedCommonJS; 221 | 222 | const nodeRecommendedDirty = [ 223 | globalIgnores(ignorePaths), 224 | configs["node/mixed-dirty"], 225 | javascriptConfig, 226 | typescriptJSDocConfig, 227 | typescriptConfig, 228 | jestConfig, 229 | configs["markdown/recommended"], 230 | configs["stylistic/recommended"], 231 | configs["package-json/recommended"], 232 | ]; 233 | // TODO remove me in the next major release 234 | configs["recommended-dirty"] = nodeRecommendedDirty; 235 | 236 | configs["node-recommended-dirty"] = nodeRecommendedDirty; 237 | 238 | const browserRecommended = [ 239 | globalIgnores(ignorePaths), 240 | configs["browser/recommended"], 241 | javascriptConfig, 242 | typescriptJSDocConfig, 243 | typescriptConfig, 244 | jestConfig, 245 | configs["markdown/recommended"], 246 | configs["stylistic/recommended"], 247 | configs["package-json/recommended"], 248 | ]; 249 | 250 | configs["browser-recommended"] = browserRecommended; 251 | 252 | const browserOutdatedRecommendedScript = [ 253 | globalIgnores(ignorePaths), 254 | configs["browser/recommended-outdated-script"], 255 | configs["javascript/es5"], 256 | typescriptJSDocConfig, 257 | typescriptConfig, 258 | jestConfig, 259 | configs["markdown/recommended"], 260 | configs["stylistic/recommended"], 261 | configs["package-json/recommended"], 262 | ]; 263 | 264 | configs["browser-outdated-recommended-script"] = 265 | browserOutdatedRecommendedScript; 266 | 267 | const browserOutdatedRecommendedCommonjs = [ 268 | globalIgnores(ignorePaths), 269 | configs["browser/recommended-outdated-commonjs"], 270 | configs["javascript/es5"], 271 | typescriptJSDocConfig, 272 | typescriptConfig, 273 | jestConfig, 274 | configs["markdown/recommended"], 275 | configs["stylistic/recommended"], 276 | configs["package-json/recommended"], 277 | ]; 278 | 279 | configs["browser-outdated-recommended-commonjs"] = 280 | browserOutdatedRecommendedCommonjs; 281 | 282 | const browserOutdatedRecommendedModule = [ 283 | globalIgnores(ignorePaths), 284 | configs["browser/recommended-outdated-module"], 285 | { 286 | ...configs["javascript/es5"], 287 | languageOptions: { 288 | ecmaVersion: "latest", 289 | }, 290 | }, 291 | typescriptJSDocConfig, 292 | typescriptConfig, 293 | jestConfig, 294 | configs["markdown/recommended"], 295 | configs["stylistic/recommended"], 296 | configs["package-json/recommended"], 297 | ]; 298 | 299 | // TODO remove in the next major release 300 | configs["browser-outdated-recommended"] = browserOutdatedRecommendedModule; 301 | 302 | configs["browser-outdated-recommended-module"] = 303 | browserOutdatedRecommendedModule; 304 | 305 | const universalRecommended = [ 306 | globalIgnores(ignorePaths), 307 | configs["browser/recommended"], 308 | isModule 309 | ? configs["node/mixed-module-and-commonjs"] 310 | : configs["node/mixed-commonjs-and-module"], 311 | javascriptConfig, 312 | typescriptJSDocConfig, 313 | typescriptConfig, 314 | jestConfig, 315 | configs["markdown/recommended"], 316 | configs["stylistic/recommended"], 317 | configs["package-json/recommended"], 318 | ]; 319 | 320 | configs["universal-recommended"] = universalRecommended; 321 | 322 | export { default } from "./configs/index.js"; 323 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [4.7.3](https://github.com/webpack/eslint-config-webpack/compare/v4.7.2...v4.7.3) (2025-12-15) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * regression with `n/no-unsupported-features/es-syntax` rule and ES modules ([#94](https://github.com/webpack/eslint-config-webpack/issues/94)) ([fb4afb2](https://github.com/webpack/eslint-config-webpack/commit/fb4afb27ab501f0722b0c00e5c92a559b2560c8d)) 11 | 12 | ### [4.7.2](https://github.com/webpack/eslint-config-webpack/compare/v4.7.1...v4.7.2) (2025-12-15) 13 | 14 | 15 | ### Bug Fixes 16 | 17 | * always allow to user `error-cause` feature without errors ([#93](https://github.com/webpack/eslint-config-webpack/issues/93)) ([8605885](https://github.com/webpack/eslint-config-webpack/commit/8605885a722288dc2e2bc4c1836ef27cba96c4e0)) 18 | 19 | ### [4.7.1](https://github.com/webpack/eslint-config-webpack/compare/v4.7.0...v4.7.1) (2025-12-15) 20 | 21 | 22 | ### Bug Fixes 23 | 24 | * disable `unicorn/no-array-sort` rule for non supported ES versions ([#92](https://github.com/webpack/eslint-config-webpack/issues/92)) ([970d358](https://github.com/webpack/eslint-config-webpack/commit/970d358d9692c5c1ec50c83885d93e8799465a8e)) 25 | 26 | ## [4.7.0](https://github.com/webpack/eslint-config-webpack/compare/v4.6.3...v4.7.0) (2025-12-15) 27 | 28 | 29 | ### Features 30 | 31 | * new rules ([#91](https://github.com/webpack/eslint-config-webpack/issues/91)) ([76ea01f](https://github.com/webpack/eslint-config-webpack/commit/76ea01fb93bb5c782bec42078852135f306f096d)) 32 | 33 | ### [4.6.3](https://github.com/webpack/eslint-config-webpack/compare/v4.6.2...v4.6.3) (2025-08-22) 34 | 35 | 36 | ### Bug Fixes 37 | 38 | * typescript loading logic ([#90](https://github.com/webpack/eslint-config-webpack/issues/90)) ([a5018d6](https://github.com/webpack/eslint-config-webpack/commit/a5018d6038d2ed9a8adca6482939459b825c2d5d)) 39 | 40 | ### [4.6.2](https://github.com/webpack/eslint-config-webpack/compare/v4.6.1...v4.6.2) (2025-08-22) 41 | 42 | 43 | ### Bug Fixes 44 | 45 | * do not apply typescript preset without tsconfig ([#89](https://github.com/webpack/eslint-config-webpack/issues/89)) ([f883516](https://github.com/webpack/eslint-config-webpack/commit/f883516201d635ad9068d8bf6093ff8d0d3e8057)) 46 | 47 | ## [4.6.1](https://github.com/webpack/eslint-config-webpack/compare/v4.6.0...v4.7.0) (2025-08-20) 48 | 49 | 50 | ### Bug Fixes 51 | 52 | * allow to use any Node.js features and ECMA syntax in documentation ([#88](https://github.com/webpack/eslint-config-webpack/issues/88)) ([bb600e0](https://github.com/webpack/eslint-config-webpack/commit/bb600e06fba1f11d68de53edf7285526905fcdf2)) 53 | 54 | ## [4.6.0](https://github.com/webpack/eslint-config-webpack/compare/v4.5.1...v4.6.0) (2025-08-12) 55 | 56 | 57 | ### Features 58 | 59 | * new browsers configurations ([#86](https://github.com/webpack/eslint-config-webpack/issues/86)) ([494fd38](https://github.com/webpack/eslint-config-webpack/commit/494fd3873dfcaf215690aa6544a3a6cb288008f7)) 60 | 61 | 62 | ### Bug Fixes 63 | 64 | * disable `n/no-extraneous-import` due `import/no-extraneous-dependencies` ([#85](https://github.com/webpack/eslint-config-webpack/issues/85)) ([c600bab](https://github.com/webpack/eslint-config-webpack/commit/c600babfce7f0cec17e40f1afd7b921a77b5a9d0)) 65 | 66 | ### [4.5.1](https://github.com/webpack/eslint-config-webpack/compare/v4.5.0...v4.5.1) (2025-07-25) 67 | 68 | 69 | ### Bug Fixes 70 | 71 | * disable `unicorn/prefer-class-fields` for old es versions ([#84](https://github.com/webpack/eslint-config-webpack/issues/84)) ([83b9784](https://github.com/webpack/eslint-config-webpack/commit/83b978453d37f650caacffe2b68767e73b331544)) 72 | 73 | ## [4.5.0](https://github.com/webpack/eslint-config-webpack/compare/v4.4.2...v4.5.0) (2025-07-23) 74 | 75 | 76 | ### Features 77 | 78 | * update deps ([#82](https://github.com/webpack/eslint-config-webpack/issues/82)) ([651d96b](https://github.com/webpack/eslint-config-webpack/commit/651d96b6420e3d833baad8169a7bb543b0ba308d)) 79 | 80 | 81 | ### Bug Fixes 82 | 83 | * deprecation warning ([#81](https://github.com/webpack/eslint-config-webpack/issues/81)) ([d5f6101](https://github.com/webpack/eslint-config-webpack/commit/d5f61014038d59ad01e3fd91a7e5478a0bdf6a73)) 84 | * the `engines` field ([#83](https://github.com/webpack/eslint-config-webpack/issues/83)) ([abfc975](https://github.com/webpack/eslint-config-webpack/commit/abfc9758834f49336db1dd850d222feb125df2a3)) 85 | 86 | ### [4.4.2](https://github.com/webpack/eslint-config-webpack/compare/v4.4.1...v4.4.2) (2025-07-22) 87 | 88 | 89 | ### Bug Fixes 90 | 91 | * allow to use `any` in docs ([#79](https://github.com/webpack/eslint-config-webpack/issues/79)) ([e8056d7](https://github.com/webpack/eslint-config-webpack/commit/e8056d7aac139bb45c6ce7999133a0597a648dc7)) 92 | 93 | ### [4.4.1](https://github.com/webpack/eslint-config-webpack/compare/v4.4.0...v4.4.1) (2025-07-17) 94 | 95 | 96 | ### Bug Fixes 97 | 98 | * disable `unicorn/prefer-object-from-entries` for old es versions ([#78](https://github.com/webpack/eslint-config-webpack/issues/78)) ([bb28a00](https://github.com/webpack/eslint-config-webpack/commit/bb28a0039102ef8ec4bca29b37e68ee34d0d8f70)) 99 | 100 | ## [4.4.0](https://github.com/webpack/eslint-config-webpack/compare/v4.3.4...v4.4.0) (2025-07-15) 101 | 102 | 103 | ### Features 104 | 105 | * new browsers and universal presets ([63c9d13](https://github.com/webpack/eslint-config-webpack/commit/63c9d13326113a130c96af9a1cd1e09ea38f7bbf)) 106 | 107 | 108 | ### Bug Fixes 109 | 110 | * add `jest-environment-options` to ignore typescript jsdoc errors ([#76](https://github.com/webpack/eslint-config-webpack/issues/76)) ([08c3da3](https://github.com/webpack/eslint-config-webpack/commit/08c3da39aa47e7d1bcc82e30d6d936512bdc0779)) 111 | 112 | ### [4.3.4](https://github.com/webpack/eslint-config-webpack/compare/v4.3.3...v4.3.4) (2025-07-14) 113 | 114 | 115 | ### Bug Fixes 116 | 117 | * browser preset ([#75](https://github.com/webpack/eslint-config-webpack/issues/75)) ([9be2a9c](https://github.com/webpack/eslint-config-webpack/commit/9be2a9c78cbefdeae08c8a18fd235772e9920da0)) 118 | 119 | ### [4.3.3](https://github.com/webpack/eslint-config-webpack/compare/v4.3.2...v4.3.3) (2025-07-14) 120 | 121 | 122 | ### Bug Fixes 123 | 124 | * avoid jest special comments to lint ([#72](https://github.com/webpack/eslint-config-webpack/issues/72)) ([4fbe4b0](https://github.com/webpack/eslint-config-webpack/commit/4fbe4b0115c3603a568c1b899affa0d6a3076948)) 125 | * disable `@typescript-eslint/triple-slash-reference` for markdown ([#74](https://github.com/webpack/eslint-config-webpack/issues/74)) ([a11f2cb](https://github.com/webpack/eslint-config-webpack/commit/a11f2cb0019c9d3f5665b59d2df9c94ba7a8a8a7)) 126 | * disable `no-new` rule for markdown ([#73](https://github.com/webpack/eslint-config-webpack/issues/73)) ([4b2e171](https://github.com/webpack/eslint-config-webpack/commit/4b2e17151e543dac5cbc40329683088899952540)) 127 | 128 | ### [4.3.2](https://github.com/webpack/eslint-config-webpack/compare/v4.3.1...v4.3.2) (2025-07-14) 129 | 130 | 131 | ### Bug Fixes 132 | 133 | * loading react plugin ([44a2d90](https://github.com/webpack/eslint-config-webpack/commit/44a2d9096f0276ffff8561a2dc5549094d4dfec1)) 134 | * markdown files glob ([#71](https://github.com/webpack/eslint-config-webpack/issues/71)) ([fae0010](https://github.com/webpack/eslint-config-webpack/commit/fae0010e081100639d7d6df3f9b33e04df682d31)) 135 | 136 | ### [4.3.1](https://github.com/webpack/eslint-config-webpack/compare/v4.3.0...v4.3.1) (2025-07-14) 137 | 138 | 139 | ### Bug Fixes 140 | 141 | * disable `@typescript-eslint/no-unused-vars` for markdown files ([#69](https://github.com/webpack/eslint-config-webpack/issues/69)) ([fcbf2a3](https://github.com/webpack/eslint-config-webpack/commit/fcbf2a346ca6b39b591733bf078f0f617ef6bbd3)) 142 | * improve react support ([64c1204](https://github.com/webpack/eslint-config-webpack/commit/64c12048e4e5e9b543b83fff15f66a0014cbb320)) 143 | * improve typescript support ([#67](https://github.com/webpack/eslint-config-webpack/issues/67)) ([6750fc3](https://github.com/webpack/eslint-config-webpack/commit/6750fc3b2fe46933b8ca86956e38855b0159de37)) 144 | 145 | ## [4.3.0](https://github.com/webpack/eslint-config-webpack/compare/v4.2.2...v4.3.0) (2025-07-01) 146 | 147 | 148 | ### Features 149 | 150 | * added special webpack rule to check license comments ([#66](https://github.com/webpack/eslint-config-webpack/issues/66)) ([1a6a880](https://github.com/webpack/eslint-config-webpack/commit/1a6a8800e1cd5618440ce2043f6b3ab7d3eb6e24)) 151 | 152 | ### [4.2.2](https://github.com/webpack/eslint-config-webpack/compare/v4.2.1...v4.2.2) (2025-07-01) 153 | 154 | 155 | ### Bug Fixes 156 | 157 | * allow to use `process-exit` in docs ([#65](https://github.com/webpack/eslint-config-webpack/issues/65)) ([06570d4](https://github.com/webpack/eslint-config-webpack/commit/06570d4927d610e465798e7c12d29939e2522cf7)) 158 | 159 | ### [4.2.1](https://github.com/webpack/eslint-config-webpack/compare/v4.2.0...v4.2.1) (2025-07-01) 160 | 161 | 162 | ### Bug Fixes 163 | 164 | * allow to use `eval` in tests ([#62](https://github.com/webpack/eslint-config-webpack/issues/62)) ([9b98e49](https://github.com/webpack/eslint-config-webpack/commit/9b98e496afaec20d6a60bf7e4d056d1464ddf035)) 165 | * apply DOM rules only for the browser target ([#61](https://github.com/webpack/eslint-config-webpack/issues/61)) ([4607d8f](https://github.com/webpack/eslint-config-webpack/commit/4607d8f71032530485ae285d9058eb975dbbdd1a)) 166 | * disable `no-control-regex` for tests ([#60](https://github.com/webpack/eslint-config-webpack/issues/60)) ([71b3d96](https://github.com/webpack/eslint-config-webpack/commit/71b3d96662c737f0bf787697d3a8464945395830)) 167 | * relax test rules ([#64](https://github.com/webpack/eslint-config-webpack/issues/64)) ([98b7b67](https://github.com/webpack/eslint-config-webpack/commit/98b7b67628bc98f6a0e3027c18228c218cb984f0)) 168 | * use node.js recommended rules by default ([#63](https://github.com/webpack/eslint-config-webpack/issues/63)) ([4745549](https://github.com/webpack/eslint-config-webpack/commit/4745549aa7d706146b57d2357b69da5930d439ee)) 169 | 170 | ## [4.2.0](https://github.com/webpack/eslint-config-webpack/compare/v4.1.4...v4.2.0) (2025-06-27) 171 | 172 | 173 | ### Features 174 | 175 | * enable rule for order of imports and exports ([#59](https://github.com/webpack/eslint-config-webpack/issues/59)) ([387c167](https://github.com/webpack/eslint-config-webpack/commit/387c167b288384371039214cf74213870014b63e)) 176 | * plugin and rule to sort package.json properties ([ca57ab4](https://github.com/webpack/eslint-config-webpack/commit/ca57ab4feb40a9d99f96136365ef5d8d4599536f)) 177 | 178 | 179 | ### Bug Fixes 180 | 181 | * enable `reportUnusedInlineConfigs` ([#56](https://github.com/webpack/eslint-config-webpack/issues/56)) ([f53c2ba](https://github.com/webpack/eslint-config-webpack/commit/f53c2ba8cceca47d7e0ffee04eed9eccc47d22de)) 182 | * more path to ignore ([#57](https://github.com/webpack/eslint-config-webpack/issues/57)) ([8272ec4](https://github.com/webpack/eslint-config-webpack/commit/8272ec4d1bd98e65d4bbe38a6003ee1f6dbccbfd)) 183 | 184 | ### [4.1.4](https://github.com/webpack/eslint-config-webpack/compare/v4.1.3...v4.1.4) (2025-06-25) 185 | 186 | 187 | ### Bug Fixes 188 | 189 | * ignore ts extension for `require` in typescript code ([#55](https://github.com/webpack/eslint-config-webpack/issues/55)) ([1b2558f](https://github.com/webpack/eslint-config-webpack/commit/1b2558f7a8c9c07c0ddfe85809d1392dc83f38be)) 190 | 191 | ### [4.1.3](https://github.com/webpack/eslint-config-webpack/compare/v4.1.2...v4.1.3) (2025-06-24) 192 | 193 | 194 | ### Bug Fixes 195 | 196 | * better configuration for module/commonjs/dirty format ([#51](https://github.com/webpack/eslint-config-webpack/issues/51)) ([7de7ddf](https://github.com/webpack/eslint-config-webpack/commit/7de7ddf4cfcde0f64b99a39813b42fb1bfa5f5b1)) 197 | * disable `guard-for-in` for extra noise ([#52](https://github.com/webpack/eslint-config-webpack/issues/52)) ([0e894a5](https://github.com/webpack/eslint-config-webpack/commit/0e894a5302daeb9959ca7dadbd73bc1e69bcd511)) 198 | * improve jest `files` ([#50](https://github.com/webpack/eslint-config-webpack/issues/50)) ([90a979e](https://github.com/webpack/eslint-config-webpack/commit/90a979eb4ab3c5303908a11b06cfb38855c3a9f0)) 199 | * more newlines ([#49](https://github.com/webpack/eslint-config-webpack/issues/49)) ([7e17d6b](https://github.com/webpack/eslint-config-webpack/commit/7e17d6b83f79b7216afd435d1fd9acf39742f8ac)) 200 | 201 | ### [4.1.2](https://github.com/webpack/eslint-config-webpack/compare/v4.1.1...v4.1.2) (2025-06-23) 202 | 203 | 204 | ### Bug Fixes 205 | 206 | * do not apply typescript rules when emit disabled ([#48](https://github.com/webpack/eslint-config-webpack/issues/48)) ([5bed1ab](https://github.com/webpack/eslint-config-webpack/commit/5bed1ab9e3a8c7c350e4a929efe4ac3001f58253)) 207 | 208 | ### [4.1.1](https://github.com/webpack/eslint-config-webpack/compare/v4.1.0...v4.1.1) (2025-06-20) 209 | 210 | 211 | ### Bug Fixes 212 | 213 | * crash without `typescript-eslint` ([#47](https://github.com/webpack/eslint-config-webpack/issues/47)) ([9534172](https://github.com/webpack/eslint-config-webpack/commit/95341721e3c8a6588deb9787a97a97d5dcefed52)) 214 | 215 | ## [4.1.0](https://github.com/webpack/eslint-config-webpack/compare/v4.0.10...v4.1.0) (2025-06-12) 216 | 217 | 218 | ### Features 219 | 220 | * added `typescript/recommended` preset ([fea16fb](https://github.com/webpack/eslint-config-webpack/commit/fea16fbf6b1e789fd49fac270aed912988b71793)) 221 | 222 | ### [4.0.10](https://github.com/webpack/eslint-config-webpack/compare/v4.0.9...v4.0.10) (2025-06-11) 223 | 224 | 225 | ### Bug Fixes 226 | 227 | * use dirty parser configuration for markdown ([#46](https://github.com/webpack/eslint-config-webpack/issues/46)) ([404c7c8](https://github.com/webpack/eslint-config-webpack/commit/404c7c867f6e180e56521219bebecd5f63c584fb)) 228 | 229 | ### [4.0.9](https://github.com/webpack/eslint-config-webpack/compare/v4.0.8...v4.0.9) (2025-06-11) 230 | 231 | 232 | ### Bug Fixes 233 | 234 | * adding default prettier config for old projects ([86f7be6](https://github.com/webpack/eslint-config-webpack/commit/86f7be6aa2643d181c767246eda50fa654d46a4c)) 235 | * disable `no-console` for tests ([#45](https://github.com/webpack/eslint-config-webpack/issues/45)) ([ab1798f](https://github.com/webpack/eslint-config-webpack/commit/ab1798f77881a2dec90191c3d8488900d1d53320)) 236 | 237 | ### [4.0.8](https://github.com/webpack/eslint-config-webpack/compare/v4.0.7...v4.0.8) (2025-06-11) 238 | 239 | 240 | ### Bug Fixes 241 | 242 | * node 6 and node 7 support ([#44](https://github.com/webpack/eslint-config-webpack/issues/44)) ([c6f129f](https://github.com/webpack/eslint-config-webpack/commit/c6f129f069ba56bb1c31c2debcf9575f6a846abb)) 243 | 244 | ### [4.0.7](https://github.com/webpack/eslint-config-webpack/compare/v4.0.6...v4.0.7) (2025-06-11) 245 | 246 | 247 | ### Bug Fixes 248 | 249 | * `engines` in package.json ([#43](https://github.com/webpack/eslint-config-webpack/issues/43)) ([046a2b4](https://github.com/webpack/eslint-config-webpack/commit/046a2b40499c9f30c1df2f73aee1eb1c21b061d2)) 250 | 251 | ### [4.0.6](https://github.com/webpack/eslint-config-webpack/compare/v4.0.5...v4.0.6) (2025-06-10) 252 | 253 | 254 | ### Bug Fixes 255 | 256 | * lazy loading configs in different deps ([#42](https://github.com/webpack/eslint-config-webpack/issues/42)) ([8a855ab](https://github.com/webpack/eslint-config-webpack/commit/8a855abe8aa8210316a835c1b84675838424a452)) 257 | 258 | ### [4.0.5](https://github.com/webpack/eslint-config-webpack/compare/v4.0.4...v4.0.5) (2025-06-10) 259 | 260 | 261 | ### Bug Fixes 262 | 263 | * lazy load jest and typescript configuration ([#40](https://github.com/webpack/eslint-config-webpack/issues/40)) ([196e040](https://github.com/webpack/eslint-config-webpack/commit/196e040fff0661633070fab57c57dd2baa486d09)) 264 | * lazy load optional plugins ([#41](https://github.com/webpack/eslint-config-webpack/issues/41)) ([d634eaf](https://github.com/webpack/eslint-config-webpack/commit/d634eaf555be2b68a7b7079d8a928804b1589bdb)) 265 | * relax `jest/prefer-lowercase-title` ([#39](https://github.com/webpack/eslint-config-webpack/issues/39)) ([496c8e2](https://github.com/webpack/eslint-config-webpack/commit/496c8e2ff1b4337a65325568ce93596b99732572)) 266 | 267 | ### [4.0.4](https://github.com/webpack/eslint-config-webpack/compare/v4.0.3...v4.0.4) (2025-06-10) 268 | 269 | 270 | ### Bug Fixes 271 | 272 | * disable `require-await` rule ([a787ef9](https://github.com/webpack/eslint-config-webpack/commit/a787ef96d293382b90a0c0e107ebc037d64b2204)) 273 | 274 | ### [4.0.3](https://github.com/webpack/eslint-config-webpack/compare/v4.0.2...v4.0.3) (2025-06-10) 275 | 276 | 277 | ### Bug Fixes 278 | 279 | * add `test/outputs` to global ignore ([#36](https://github.com/webpack/eslint-config-webpack/issues/36)) ([2d3253b](https://github.com/webpack/eslint-config-webpack/commit/2d3253bf0a3156d360e5c84bb894abc74969dbc3)) 280 | * improve jest default configuration ([#37](https://github.com/webpack/eslint-config-webpack/issues/37)) ([e846ec3](https://github.com/webpack/eslint-config-webpack/commit/e846ec360179cc8551212714470ce3ad38d2a1b8)) 281 | 282 | ### [4.0.2](https://github.com/webpack/eslint-config-webpack/compare/v4.0.1...v4.0.2) (2025-06-10) 283 | 284 | 285 | ### Bug Fixes 286 | 287 | * adding `eslint-config-prettier` to peer deps ([#33](https://github.com/webpack/eslint-config-webpack/issues/33)) ([df28ab8](https://github.com/webpack/eslint-config-webpack/commit/df28ab8bd7c9a269ba35a8ff4fe1d9cd6d641998)) 288 | * disable `jest/no-done-callback` rule ([#35](https://github.com/webpack/eslint-config-webpack/issues/35)) ([cb2b634](https://github.com/webpack/eslint-config-webpack/commit/cb2b6342f66d9fc9c57bd99f07e53b3528941b07)) 289 | * unresolved import in `eslint-config` ([#34](https://github.com/webpack/eslint-config-webpack/issues/34)) ([a73ed8e](https://github.com/webpack/eslint-config-webpack/commit/a73ed8e6bba2dd76540f3af4e31aabdeb6e958dc)) 290 | 291 | ### [4.0.1](https://github.com/webpack/eslint-config-webpack/compare/v4.0.0...v4.0.1) (2025-06-06) 292 | 293 | 294 | ### Bug Fixes 295 | 296 | * publish ([36d0812](https://github.com/webpack/eslint-config-webpack/commit/36d0812ea29472e1ab9a8ea0e279199a1a7e94f8)) 297 | 298 | ## [4.0.0](https://github.com/webpack/eslint-config-webpack/compare/v3.0.0...v4.0.0) (2025-06-06) 299 | 300 | 301 | ### ⚠ BREAKING CHANGES 302 | 303 | * update all rules and switch to eslint@9 (#30) 304 | 305 | ### Features 306 | 307 | * update all rules and switch to eslint@9 ([#30](https://github.com/webpack/eslint-config-webpack/issues/30)) ([fcddab9](https://github.com/webpack/eslint-config-webpack/commit/fcddab9409e5fa03d7062160d2c4b6842b332049)) 308 | 309 | ## [2.0.0](https://github.com/webpack/eslint-config-webpack/compare/v3.0.0...v2.0.0) (2025-06-06) 310 | 311 | 312 | ### ⚠ BREAKING CHANGES 313 | 314 | * update all rules and switch to eslint@9 (#30) 315 | 316 | ### Features 317 | 318 | * update all rules and switch to eslint@9 ([#30](https://github.com/webpack/eslint-config-webpack/issues/30)) ([fcddab9](https://github.com/webpack/eslint-config-webpack/commit/fcddab9409e5fa03d7062160d2c4b6842b332049)) 319 | -------------------------------------------------------------------------------- /validation/code.js: -------------------------------------------------------------------------------- 1 | import { CLIEngine, linter } from "eslint"; 2 | import sumFn from "./module.js"; 3 | // eslint-disable-next-line import/named, no-duplicate-imports 4 | import { notFoo } from "./module.js"; 5 | import * as names from "./named-exports.js"; 6 | 7 | // eslint-disable-next-line no-unused-vars, import/extensions, import/no-unresolved 8 | import { myVar } from "./without-extension"; 9 | 10 | import "./style.css"; 11 | 12 | // eslint-disable-next-line no-unused-vars, import/order, import/no-extraneous-dependencies 13 | import * as scope from "eslint-scope"; 14 | 15 | notFoo(); 16 | 17 | const result = sumFn(1, 2); 18 | 19 | sumFn(names.a, names.b); 20 | 21 | const console = { 22 | log(message) { 23 | return message; 24 | }, 25 | }; 26 | 27 | // eslint-disable-next-line @stylistic/quotes 28 | console.log(`test`); 29 | 30 | /** 31 | * @param {string} str output 32 | */ 33 | function print(str) { 34 | console.log(str); 35 | } 36 | 37 | print(result); 38 | 39 | const constVariable = 1; 40 | const arrayVariable = [1, 2]; 41 | const booleanVariable = true; 42 | const stringVariable = "Capt. Janeway"; 43 | const errorMessage = 44 | "This is a super long error that was thrown because of Batman. " + 45 | "When you stop to think about how Batman had anything to do with this, you would get nowhere fast."; 46 | const quotedFoo = "'this' is \"quoted\""; 47 | const quotedBar = `'this' is "quoted" ${constVariable}`; 48 | 49 | arrayVariable.push(stringVariable); 50 | 51 | /** 52 | * @param {string} prefix prefix 53 | * @param {string} key key 54 | * @returns {string} new key 55 | */ 56 | function getKey(prefix = "", key = null) { 57 | let realKey = key || null; 58 | 59 | if (!realKey) { 60 | realKey = Object.hasOwn({ key: "key", value: "value" }, "key") 61 | ? "very very very very very very very very very long key" 62 | : 1; 63 | } 64 | 65 | return `${prefix}_${key}_value_${realKey}_${new Date(2016, 10, 10)}`; 66 | } 67 | 68 | /* 69 | function deferFn(fn) { 70 | return fn(); 71 | } 72 | */ 73 | 74 | const objectVariable = { 75 | addValue(value) { 76 | return this.value + value; 77 | }, 78 | 79 | "data-blah": 5, 80 | 81 | getAge() { 82 | return this.value; 83 | }, 84 | 85 | getId() { 86 | return this.id; 87 | }, 88 | 89 | getSome() { 90 | return "some"; 91 | }, 92 | 93 | id: 5, 94 | 95 | name: "San Francisco", 96 | 97 | [getKey("enabled")]: true, 98 | 99 | setAge(value) { 100 | this.value = value; 101 | }, 102 | 103 | value: 1, 104 | 105 | // Need path in eslint, see https://github.com/eslint/eslint/issues/6196 106 | // defer: deferFn((config) => (config.exist ? 'exist' : 'non exist')) 107 | }; 108 | 109 | /** 110 | * @param {string[]} items items 111 | * @param {Record} options options 112 | * @returns {string} result 113 | */ 114 | objectVariable.log = function log(items, options = {}) { 115 | const concatenateAll = (...args) => args.join("").join(Object.keys(options)); 116 | 117 | return concatenateAll(this.addValue(1).addValue(items), options); 118 | }; 119 | 120 | arrayVariable.push(constVariable); 121 | 122 | /** 123 | * @returns {string} test 124 | */ 125 | function handler() { 126 | return "test"; 127 | } 128 | 129 | const obj = { 130 | // __proto__ 131 | __proto__: Object.getPrototypeOf({}), 132 | // Shorthand for ‘handler: handler’ 133 | handler, 134 | // Methods 135 | toString() { 136 | // Super calls 137 | return `d ${super.toString()}`; 138 | }, 139 | // Computed (dynamic) property names 140 | [`prop_${(() => 42)()}`]: 42, 141 | }; 142 | 143 | console.log(obj); 144 | 145 | const has = Object.hasOwn; 146 | 147 | handler(); 148 | handler(); 149 | handler(); 150 | 151 | { 152 | let foobaz = 2 * 10 + objectVariable.value; 153 | const bar = 1; 154 | 155 | foobaz = bar + 1; 156 | 157 | if (foobaz > bar) { 158 | foobaz += bar; 159 | } 160 | } 161 | 162 | handler(); 163 | 164 | let letVariable = 1; 165 | 166 | if (booleanVariable) { 167 | letVariable += 1; 168 | } 169 | 170 | if (!booleanVariable) { 171 | letVariable -= 1; 172 | } 173 | 174 | if (!booleanVariable) { 175 | letVariable -= 1; 176 | } 177 | 178 | handler(); 179 | 180 | switch (letVariable) { 181 | case 1: 182 | letVariable = 10; 183 | break; 184 | case 2: 185 | letVariable = 20; 186 | break; 187 | default: 188 | letVariable = 0; 189 | break; 190 | } 191 | 192 | switch (letVariable) { 193 | case 1: 194 | letVariable = 10; 195 | break; 196 | case 2: 197 | letVariable = 20; 198 | break; 199 | default: 200 | letVariable = 0; 201 | break; 202 | } 203 | 204 | switch (letVariable) { 205 | case 10: 206 | case 20: 207 | case 30: 208 | letVariable = 50; 209 | break; 210 | default: 211 | letVariable = 0; 212 | break; 213 | } 214 | 215 | switch (letVariable) { 216 | case 1: 217 | handler(1); 218 | break; 219 | case 2: 220 | handler(2); 221 | break; 222 | default: 223 | handler(0); 224 | } 225 | 226 | arrayVariable.push(letVariable); 227 | 228 | Object.hasOwn(objectVariable, "id"); 229 | has.call(objectVariable, "id"); 230 | 231 | const itemsCopy = [...arrayVariable]; 232 | 233 | /** 234 | * @param {(a: number, b: number) -> number} func function 235 | * @param {number} interval interval 236 | * @returns {(a: number, b: number) -> number} function 237 | */ 238 | function fooFunc(func, interval) { 239 | if (interval > 100) { 240 | throw new Error("Invalid interval"); 241 | } 242 | 243 | return func; 244 | } 245 | 246 | /** 247 | * @param {(a: number, b: number) -> number} func func 248 | * @param {number} interval interval 249 | * @returns {(a: number, b: number) -> number} result 250 | */ 251 | function fooFuncExtra(func, interval) { 252 | if (interval > 500) { 253 | handler(); 254 | 255 | throw new Error("Invalid interval"); 256 | } 257 | 258 | return func; 259 | } 260 | 261 | fooFuncExtra(); 262 | 263 | fooFunc(() => { 264 | itemsCopy.push("random string"); 265 | }, 1000); 266 | 267 | [1, 2, 3].map((value) => { 268 | const result = value + 1; 269 | 270 | return value * result; 271 | }); 272 | 273 | const flat = {}; 274 | 275 | [ 276 | [0, 1], 277 | [2, 3], 278 | [4, 5], 279 | ].reduce((memo, item, index) => { 280 | const flatten = [...memo, ...item]; 281 | 282 | flat[index] = flatten; 283 | 284 | return flatten; 285 | }); 286 | 287 | itemsCopy.filter((msg) => { 288 | const { subject, author } = msg; 289 | 290 | if (subject === "Mockingbird") { 291 | return author === "Harper Lee"; 292 | } 293 | 294 | return false; 295 | }); 296 | 297 | /** 298 | * @param {{ firstName: string, lastName: string }} user user 299 | * @returns {string} full name 300 | */ 301 | function getFullName(user) { 302 | const { firstName, lastName } = user; 303 | 304 | return `${firstName} ${lastName}`; 305 | } 306 | 307 | /** 308 | * @param {[number, number, number, number]} input input 309 | * @returns {{ bottom: 0, left: 0, right: 0, top: 0 }} result 310 | */ 311 | function processInput(input = [0, 0, 0, 0]) { 312 | const [left, right, top, bottom] = input; 313 | 314 | // then a miracle occurs 315 | return { 316 | bottom, 317 | left, 318 | right, 319 | top, 320 | }; 321 | } 322 | 323 | /** 324 | * @param {Record} data data 325 | * @param {[number, number, number, number]} position position 326 | * @returns {{ bottom: 0, left: 0, right: 0, top: 0 }} result 327 | */ 328 | function render(data = {}, position = [0, 0, 0, 0]) { 329 | return processInput(data, position); 330 | } 331 | 332 | /** 333 | * @param {{ firstName: string, lastName: string }} root root 334 | * @returns {string} result 335 | */ 336 | function getFullNameWithDash({ firstName, lastName }) { 337 | return `${firstName}-${lastName}`; 338 | } 339 | 340 | const username = getFullName({ 341 | firstName: "foo", 342 | lastName: "bar", 343 | }); 344 | const usernameWithDash = getFullNameWithDash({ 345 | firstName: "foo", 346 | lastName: "bar", 347 | }); 348 | const [left, right, top, bottom] = arrayVariable; 349 | 350 | render(username, [left, right, top, bottom]); 351 | render(usernameWithDash, [left, right, top, bottom]); 352 | 353 | if (booleanVariable) { 354 | throw new Error(`${errorMessage}: ${quotedFoo} - ${quotedBar}`); 355 | } 356 | 357 | /** 358 | * @returns {string} test 359 | */ 360 | const myFunction = () => { 361 | const result = `test${Math.random()}`; 362 | 363 | return result; 364 | }; 365 | 366 | myFunction(); 367 | 368 | /** 369 | * @param {RuntimeSpec} a first 370 | * @param {RuntimeSpec} b second 371 | * @returns {RuntimeSpec} result 372 | */ 373 | const subtractRuntime = (a, b) => { 374 | if (a === undefined) { 375 | return; 376 | } else if (b === undefined) { 377 | return a; 378 | } else if (a === b) { 379 | return; 380 | } else if (typeof a === "string") { 381 | if (typeof b === "string") { 382 | return a; 383 | } else if (b.has(a)) { 384 | return; 385 | } 386 | return a; 387 | } 388 | 389 | if (typeof b === "string") { 390 | if (!a.has(b)) return a; 391 | if (a.size === 2) { 392 | for (const item of a) { 393 | if (item !== b) return item; 394 | } 395 | } 396 | const set = new Set(a); 397 | set.delete(b); 398 | return set; 399 | } 400 | 401 | const set = new Set(); 402 | 403 | for (const item of a) { 404 | if (!b.has(item)) set.add(item); 405 | } 406 | 407 | if (set.size === 0) return; 408 | if (set.size === 1) { 409 | const [item] = set; 410 | return item; 411 | } 412 | 413 | return set; 414 | }; 415 | 416 | subtractRuntime(new Set(["string"]), new Set(["string"])); 417 | 418 | (function log() { 419 | objectVariable.log("Welcome to the Internet. Please follow me."); 420 | })(); 421 | 422 | let test = null; 423 | 424 | if (booleanVariable) { 425 | test = () => { 426 | objectVariable.log("Yup."); 427 | }; 428 | 429 | test(); 430 | } 431 | 432 | [1, 2, 3].map((number, index) => ({ 433 | index: number, 434 | number: index, 435 | })); 436 | 437 | [1, 2, 3].map( 438 | (number) => 439 | `As time went by, the string containing the ${number} became much ` + 440 | "longer. So we needed to break it over multiple lines.", 441 | ); 442 | 443 | const itemHeightFoo = (item) => { 444 | const smallSize = 100; 445 | const largeSize = 200; 446 | 447 | return item.height > 256 ? smallSize : largeSize; 448 | }; 449 | 450 | const itemHeightBar = (item) => { 451 | const { height, largeSize, smallSize } = item; 452 | 453 | return height > 256 ? largeSize : smallSize; 454 | }; 455 | 456 | arrayVariable.push([itemHeightFoo, itemHeightBar]); 457 | 458 | CLIEngine.prototype.foo = () => "foo"; 459 | 460 | const sum = [1, 2, 3].reduce((total, num) => total + num, 0); 461 | 462 | render(sum); 463 | 464 | // Comment 465 | const age = 21; // Comment 466 | const hasAge = Boolean(age); 467 | 468 | if (hasAge) { 469 | const anotherValue = { 470 | foo: 1, 471 | }; 472 | const realValue = render({ 473 | age, 474 | }).valueLongLognLongLongValue.valueLongLognLongLongValue 475 | .valueLongLognLongLongValue.valueLongLognLongLongValue; 476 | 477 | render({ 478 | anotherValue, 479 | realValue, 480 | }); 481 | } 482 | 483 | handler(); 484 | 485 | try { 486 | render(); 487 | } catch (err) { 488 | render({ 489 | err, 490 | }); 491 | } 492 | 493 | handler(); 494 | 495 | /** 496 | * @param {number} multiplier multiplier 497 | * @param {Array} theArgs the args 498 | * @returns {number[]} result 499 | */ 500 | function multiply(multiplier, ...theArgs) { 501 | return theArgs.map((element) => multiplier * element); 502 | } 503 | 504 | multiply(2, 1, 2, 3); 505 | 506 | const numbers = [0, 1, 2]; 507 | 508 | multiply(1, ...numbers); 509 | 510 | /** 511 | * @returns {Promise} result 512 | */ 513 | function fetch() { 514 | return new Promise((resolve) => { 515 | resolve("Some value"); 516 | }); 517 | } 518 | 519 | /** 520 | * @param {string} url url 521 | * @returns {Promise} result 522 | */ 523 | async function fetchJson(url) { 524 | try { 525 | const request = await fetch(url); 526 | const text = await request.text(); 527 | 528 | return JSON.parse(text); 529 | } catch (err) { 530 | multiply(2, 1, 2, 3); 531 | 532 | throw err; 533 | } 534 | } 535 | 536 | const { type, ...coords } = multiply(2, 1, 2, 3); 537 | 538 | await fetchJson(coords); 539 | 540 | await fetchJson("http://some-domain.com"); 541 | 542 | /** 543 | * @param {string} param1 param1 544 | * @param {string} param2 param2 545 | * @returns {string} result 546 | */ 547 | function trailingCommasInFUnctionSyntax(param1, param2) { 548 | return `${param1} ${param2}`; 549 | } 550 | 551 | trailingCommasInFUnctionSyntax("foo", "bar"); 552 | 553 | // const a **= 4; Uncomment after support in eslint 554 | 555 | const PolygonNotNamed = class { 556 | constructor(height, width) { 557 | this.height = height; 558 | this.width = width; 559 | } 560 | }; 561 | const PolygonNamed = class Polygon { 562 | constructor(height, width) { 563 | this.height = height; 564 | this.width = width; 565 | } 566 | }; 567 | 568 | class PolygonWithBody { 569 | constructor(height, width) { 570 | this.height = height; 571 | this.width = width; 572 | } 573 | 574 | get area() { 575 | return this.calcArea(); 576 | } 577 | 578 | calcArea() { 579 | return this.height * this.width; 580 | } 581 | } 582 | 583 | console.log(PolygonWithBody.area()); 584 | 585 | const squareNotNamed = new PolygonNotNamed(10, 10); 586 | const squareNamed = new PolygonNamed(10, 10); 587 | const squareWithBody = new PolygonWithBody(10, 10); 588 | 589 | console.log(squareNotNamed, squareNamed, squareWithBody); 590 | 591 | /** 592 | * @returns {void} 593 | */ 594 | function FooNewTarget() { 595 | if (!new.target) { 596 | throw new Error("Foo() must be called with new"); 597 | } 598 | 599 | // All good 600 | } 601 | 602 | console.log(new FooNewTarget()); 603 | 604 | /** 605 | * @returns {Promise} result 606 | */ 607 | function doSomething() { 608 | return new Promise((resolve) => { 609 | let result = 0; 610 | 611 | result = result * 2 + 10; 612 | 613 | resolve(result); 614 | }); 615 | } 616 | 617 | /** 618 | * @returns {Promise} result 619 | */ 620 | async function fooAsync() { 621 | const result = await doSomething(); 622 | 623 | for (const item of Object.keys(result)) { 624 | trailingCommasInFUnctionSyntax(item, item); 625 | } 626 | 627 | return result; 628 | } 629 | 630 | await fooAsync(); 631 | 632 | /** 633 | * @param {{ raw: string[] }} strings strings 634 | * @returns {string | undefined} result 635 | */ 636 | function tag(strings) { 637 | return strings.raw[0]; 638 | } 639 | 640 | tag`string text line 1 \n string text line 2`; 641 | 642 | /** 643 | * @returns {string} result 644 | */ 645 | function foo() { 646 | const before = tag("test"); 647 | 648 | if (before === "before") { 649 | return "before"; 650 | } 651 | 652 | fooAsync(); 653 | 654 | const aqw = 0; 655 | const bwe = 0; 656 | 657 | return tag(before + aqw + bwe); 658 | } 659 | 660 | foo(); 661 | 662 | let n = 0; 663 | let i = 0; 664 | let value = 10; 665 | 666 | while (n < 3) { 667 | n += 1; 668 | value += n; 669 | } 670 | 671 | while (i < 10) { 672 | i += 1; 673 | value += i; 674 | } 675 | 676 | handler(value); 677 | 678 | /** 679 | * @returns {{ handsValues: number[], lastIndex: number }} result 680 | */ 681 | function handleHands() { 682 | const hands = []; 683 | const handsValues = []; 684 | let lastIndex = 0; 685 | 686 | for (const element of hands) { 687 | for (const subElement of element) { 688 | for (const subSubElement of subElement) { 689 | handsValues.push(subSubElement); 690 | } 691 | } 692 | } 693 | 694 | for (let last = 0; last > hands.length; last--) { 695 | lastIndex = last; 696 | } 697 | 698 | return { 699 | handsValues, 700 | lastIndex, 701 | }; 702 | } 703 | 704 | handleHands(); 705 | 706 | for (i = 0; i < 10; i++) { 707 | if (i < 5) { 708 | continue; 709 | } 710 | 711 | break; 712 | } 713 | 714 | handleHands(); 715 | 716 | const exportObject = {}; 717 | 718 | exportObject.exports = { foo }; 719 | exportObject.exports.test = { foo }; 720 | 721 | handleHands(exportObject); 722 | 723 | let xBar = 1; 724 | const yBar = 1; 725 | const zBar = 1; 726 | const fooA = yBar || zBar; 727 | const fooB = yBar && zBar; 728 | const fooC = yBar > zBar; 729 | const fooD = yBar < zBar; 730 | 731 | xBar += yBar; 732 | 733 | handleHands(xBar, fooA, fooB, fooC, fooD); 734 | 735 | try { 736 | test = foo(); 737 | } catch { 738 | try { 739 | test = foo(1); 740 | } catch { 741 | throw new Error("test"); 742 | } 743 | } 744 | 745 | const aReg = /ab+c/iu; 746 | const bReg = new RegExp(`ab+c${xBar}`, "iu"); 747 | const cReg = new RegExp(/ab+c/iu, "iu"); 748 | 749 | foo(aReg, bReg, cReg); 750 | 751 | /** 752 | * @returns {Promise} result 753 | */ 754 | function myFunctionTest() { 755 | return Promise.all([Promise.resolve("a"), "b", Promise.resolve("c")]) 756 | .then((res) => { 757 | if (res[0] === 1) { 758 | throw new Error("test"); 759 | } 760 | 761 | return 1; 762 | }) 763 | .catch((err) => { 764 | throw err; 765 | }) 766 | .then(() => Promise.race([Promise.resolve("c"), Promise.resolve("d")])) 767 | .catch((err) => { 768 | throw err; 769 | }); 770 | } 771 | 772 | myFunctionTest(); 773 | 774 | class Base { 775 | constructor(name) { 776 | this.name = name; 777 | } 778 | 779 | sayHello() { 780 | return `Hello ${this.name}!`; 781 | } 782 | 783 | sayGoodbye() { 784 | return `Goodbye ${this.name}!`; 785 | } 786 | 787 | static logNbSides() { 788 | return "I have 4 sides"; 789 | } 790 | 791 | debug1() { 792 | console.log(this.name); 793 | } 794 | } 795 | 796 | class MyClass extends Base { 797 | constructor(name, catName) { 798 | super(name); 799 | 800 | this.catName = catName; 801 | } 802 | 803 | getCatName() { 804 | return `Cat name is ${this.catName}`; 805 | } 806 | 807 | debug2() { 808 | super.method1(); 809 | 810 | console.log(this.catName); 811 | } 812 | 813 | static logDescription() { 814 | return `${super.logNbSides()} which are all equal`; 815 | } 816 | } 817 | 818 | // eslint-disable-next-line no-unassigned-vars 819 | let barbazFoo; 820 | 821 | const object = { foo: "foo", bar: "bar" }; 822 | 823 | MyClass.logDescription(); 824 | delete object.foo; 825 | 826 | console.log(barbazFoo); 827 | console.log(linter); 828 | 829 | const GetSet = { 830 | get name() { 831 | return this.val; 832 | }, 833 | set name(newValue) { 834 | this.val = newValue; 835 | }, 836 | }; 837 | 838 | const Bar = class { 839 | static get name() { 840 | return this.val; 841 | } 842 | 843 | static set name(newValue) { 844 | this.val = newValue; 845 | } 846 | }; 847 | 848 | console.log(GetSet.name()); 849 | console.log(new Bar().name()); 850 | 851 | /** 852 | * @param {number} arg arg 853 | * @param {() => void} callback callback 854 | * @returns {Promise} 855 | */ 856 | function callbackInPromise(arg, callback) { 857 | return new Promise((resolve, reject) => { 858 | if (arg === 1) { 859 | reject(new Error("invalid")); 860 | 861 | return; 862 | } 863 | 864 | resolve(arg); 865 | }).then(() => callback()); 866 | } 867 | 868 | /** 869 | * @param {Error} error error 870 | * @param {string} data date 871 | * @returns {Promise} result 872 | */ 873 | function promiseInCallback(error, data) { 874 | if (error) { 875 | return Promise.reject(error); 876 | } 877 | 878 | return Promise.resolve(data).then(() => `${data}string`); 879 | } 880 | 881 | let myLet = 10; 882 | const myConst = 12; 883 | 884 | myLet += 2; 885 | 886 | console.log(myLet, myConst); 887 | 888 | let myLet1 = 10; 889 | 890 | const myConst1 = 12; 891 | 892 | myLet1 += 2; 893 | 894 | console.log(myLet1, myConst1); 895 | 896 | const myNumberObjects = [ 897 | { name: "foo", number: 1 }, 898 | { name: "bar", number: 2 }, 899 | { name: "foo", number: 3 }, 900 | ]; 901 | 902 | for (const myNumberObject of myNumberObjects) { 903 | let { name, number } = myNumberObject; 904 | 905 | if (name === "bar" && number === 2) { 906 | name = "foo"; 907 | } 908 | } 909 | 910 | let timer; 911 | 912 | /** 913 | * @returns {void} 914 | */ 915 | function initialize() { 916 | if (foo()) { 917 | clearInterval(timer); 918 | } 919 | } 920 | 921 | timer = setInterval(initialize, 100); 922 | 923 | export { foo, handler }; 924 | export { 925 | MyClass, 926 | age as ageExport, 927 | arrayVariable as arrayVariableExport, 928 | callbackInPromise, 929 | fooAsync, 930 | fooFuncExtra, 931 | promiseInCallback, 932 | }; 933 | 934 | /** 935 | * @returns {void} 936 | */ 937 | export default function myFunctionDeclaration() { 938 | // ... 939 | } 940 | 941 | /** 942 | * @returns {void} 943 | */ 944 | export const myArrayFunction = () => { 945 | // ... 946 | }; 947 | 948 | export const aaa = 1; 949 | 950 | sumFn(1, 2); 951 | 952 | export const bbb = 1; 953 | 954 | const backtick1 = `back 955 | tick`; 956 | // eslint-disable-next-line @stylistic/quotes 957 | const backtick2 = `back\ntick`; 958 | const backtick3 = tag`backtick`; 959 | // eslint-disable-next-line @stylistic/quotes 960 | const backtick4 = `single`; 961 | // eslint-disable-next-line @stylistic/quotes 962 | const backtick5 = `\``; 963 | // eslint-disable-next-line @stylistic/quotes 964 | const backtick6 = `"`; 965 | 966 | /** 967 | * @param {...string[]} args args 968 | * @returns {string} concated strings 969 | */ 970 | function concat(...args) { 971 | return args.join("\n"); 972 | } 973 | 974 | concat(backtick1, backtick2, backtick3, backtick4, backtick5, backtick6); 975 | -------------------------------------------------------------------------------- /configs/typescript.js: -------------------------------------------------------------------------------- 1 | import { 2 | javascriptExtensions, 3 | typescriptExtensions, 4 | } from "./utils/extensions.js"; 5 | 6 | /** 7 | * @returns {Promise>} config 8 | */ 9 | async function getTypescriptJSDocRecommendedConfig() { 10 | let jsdocPlugin; 11 | 12 | try { 13 | jsdocPlugin = (await import("eslint-plugin-jsdoc")).default; 14 | // eslint-disable-next-line unicorn/prefer-optional-catch-binding 15 | } catch (_err) { 16 | // Nothing 17 | } 18 | 19 | const jsdocConfig = 20 | (jsdocPlugin && 21 | jsdocPlugin.configs["flat/recommended-typescript-flavor-error"]) || 22 | {}; 23 | 24 | return { 25 | ...jsdocConfig, 26 | name: "typescript/jsdoc", 27 | files: [ 28 | `**/*.{${javascriptExtensions.map((item) => item.slice(1)).join(",")}}`, 29 | ], 30 | settings: { 31 | jsdoc: { 32 | mode: "typescript", 33 | // supported tags https://github.com/microsoft/TypeScript-wiki/blob/master/JSDoc-support-in-JavaScript.md 34 | tagNamePreference: { 35 | ...["memberof", "yields", "member"].reduce((acc, tag) => { 36 | acc[tag] = { 37 | message: `@${tag} currently not supported in TypeScript`, 38 | }; 39 | return acc; 40 | }, {}), 41 | extends: "extends", 42 | return: "returns", 43 | constructor: "constructor", 44 | prop: "property", 45 | property: "property", 46 | arg: "param", 47 | argument: "param", 48 | param: "param", 49 | augments: "extends", 50 | description: false, 51 | desc: false, 52 | inheritdoc: false, 53 | class: "constructor", 54 | returns: "returns", 55 | }, 56 | overrideReplacesDocs: false, 57 | }, 58 | }, 59 | rules: { 60 | ...jsdocConfig.rules, 61 | 62 | // From recommended 63 | // "jsdoc/check-access": "error", 64 | 65 | // From recommended 66 | // "jsdoc/check-alignment": "error", 67 | 68 | // No need 69 | // "jsdoc/check-examples": "error", 70 | 71 | "jsdoc/check-indentation": "error", 72 | 73 | "jsdoc/check-line-alignment": ["error", "never"], 74 | 75 | // From recommended 76 | // "jsdoc/check-param-names": "error", 77 | 78 | // From recommended 79 | // "jsdoc/check-property-names": "error", 80 | 81 | // No need 82 | // typescript does it 83 | // "jsdoc/check-syntax": "error", 84 | 85 | // Avoid conflict with jest special comment 86 | "jsdoc/check-tag-names": [ 87 | "error", 88 | { 89 | definedTags: ["jest-environment", "jest-environment-options"], 90 | }, 91 | ], 92 | 93 | // No need 94 | // "jsdoc/check-template-names": "error", 95 | 96 | // From recommended 97 | // "jsdoc/check-types": "error", 98 | 99 | // From recommended 100 | // "jsdoc/check-values": "error", 101 | 102 | // No need 103 | // "jsdoc/convert-to-jsdoc-comments": "error", 104 | 105 | // From recommended 106 | // "jsdoc/empty-tags": "error", 107 | 108 | // From recommended 109 | // "jsdoc/escape-inline-tags": "error", 110 | 111 | // "jsdoc/implements-on-classes": "error", 112 | 113 | // No need 114 | // "jsdoc/informative-docs": "error", 115 | 116 | // No need 117 | // "jsdoc/lines-before-block": "error", 118 | 119 | // No need 120 | // "jsdoc/match-description": "error", 121 | 122 | // No need 123 | // "jsdoc/match-name": "error", 124 | 125 | // From recommended 126 | // "jsdoc/multiline-blocks": "error", 127 | 128 | "jsdoc/no-bad-blocks": "error", 129 | 130 | "jsdoc/no-blank-block-descriptions": "error", 131 | 132 | "jsdoc/no-blank-blocks": "error", 133 | 134 | // From recommended 135 | // "jsdoc/no-defaults": "error", 136 | 137 | // No need 138 | // "jsdoc/no-missing-syntax": "error", 139 | 140 | // From recommended 141 | // "jsdoc/no-multi-asterisks": "error", 142 | 143 | "jsdoc/no-restricted-syntax": [ 144 | "error", 145 | { 146 | contexts: [ 147 | // Prefer TypeScript syntax for functions 148 | { 149 | comment: "JsdocBlock:has(JsdocTypeFunction[arrow=false])", 150 | message: 151 | "Please use TypeScript syntax - `(a: string, b: boolean) => number`", 152 | }, 153 | // Prefer `{string=}` over `{string} [arg]` 154 | { 155 | comment: 156 | "JsdocBlock:has(JsdocTag[tag=/^(property|param)$/][name=/[\\[\\]]/])", 157 | message: 158 | "Please use `@property {string=} property`/`@param {string=} arg` instead `[arg]` for optional properties and parameters", 159 | }, 160 | // No `?` type 161 | { 162 | comment: "JsdocBlock:has(JsdocTypeUnknown)", 163 | message: "Please use `unknown` or `any` (or `EXPECTED_ANY`) type", 164 | }, 165 | // No `Object` 166 | { 167 | comment: 168 | "JsdocBlock:has(JsdocTag[tag!=/^(typedef|template|param)$/]:has(JsdocTypeName[value=/^(Object|object)$/]))", 169 | message: 170 | "Please use provide types for object - `{ property: number:, result: () => number}` instead `Object`/`object` or use `EXPECTED_OBJECT` type", 171 | }, 172 | { 173 | comment: 174 | "JsdocBlock:has(JsdocTag[tag=typedef][parsedType.type!=JsdocTypeName]:has(JsdocTypeName[value=/^(Object|object)$/]))", 175 | message: 176 | "Please use provide types for object - `{ property: number:, result: () => number}` instead `Object`/`object` or use `EXPECTED_OBJECT` type", 177 | }, 178 | ], 179 | }, 180 | ], 181 | 182 | // No need 183 | // "jsdoc/no-types": "error", 184 | 185 | // No need 186 | // "jsdoc/no-undefined-types": "error", 187 | 188 | // TODO enable me in future 189 | // "jsdoc/prefer-import-tag": "error", 190 | 191 | "jsdoc/reject-any-type": "error", 192 | 193 | "jsdoc/reject-function-type": "error", 194 | 195 | "jsdoc/require-asterisk-prefix": "error", 196 | 197 | // No need 198 | // "jsdoc/require-description": "error", 199 | 200 | // No need 201 | // "jsdoc/require-description-complete-sentence": "error", 202 | 203 | // No need 204 | // "jsdoc/require-example": "error", 205 | 206 | // No need 207 | // "jsdoc/require-file-overview": "error", 208 | 209 | // No need 210 | "jsdoc/require-hyphen-before-param-description": ["error", "never"], 211 | 212 | // From recommended 213 | // "jsdoc/require-jsdoc": "error", 214 | 215 | // No need 216 | // "jsdoc/require-next-description":"error", 217 | 218 | // No need 219 | // "jsdoc/require-next-type": "error", 220 | 221 | // From recommended 222 | // "jsdoc/require-param": "error", 223 | 224 | // From recommended 225 | // "jsdoc/require-param-description": "error", 226 | 227 | // From recommended 228 | // "jsdoc/require-param-name": "error", 229 | 230 | // From recommended 231 | // "jsdoc/require-param-type": "error", 232 | 233 | // From recommended 234 | // "jsdoc/require-property": "error", 235 | 236 | // From recommended 237 | // "jsdoc/require-property-description": "error", 238 | 239 | // From recommended 240 | // "jsdoc/require-property-name": "error", 241 | 242 | // From recommended 243 | // "jsdoc/require-property-type": "error", 244 | 245 | // A lot of false positive with loops/`switch`/`if`/etc 246 | "jsdoc/require-returns-check": "off", 247 | 248 | // From recommended 249 | // "jsdoc/require-returns-description": "error", 250 | 251 | // From recommended 252 | // "jsdoc/require-returns-type": "error", 253 | 254 | // No need 255 | // "jsdoc/require-tags": "error", 256 | 257 | "jsdoc/require-template": "error", 258 | 259 | // No need 260 | // "jsdoc/require-template-description": "error", 261 | 262 | // No need 263 | // "jsdoc/require-throws": "error", 264 | 265 | // No need 266 | // "jsdoc/require-throws-description": "error", 267 | 268 | // No need 269 | // "jsdoc/require-throws-type": "error", 270 | 271 | // From recommended 272 | // "jsdoc/require-yields": "error", 273 | 274 | // No need 275 | // "jsdoc/require-yields-description": "error", 276 | 277 | // No need 278 | // "jsdoc/require-yields-type": "error", 279 | 280 | // From recommended 281 | // "jsdoc/require-yields-check": "error", 282 | 283 | // No need 284 | // "jsdoc/sort-tags": "error", 285 | 286 | // From recommended 287 | // "jsdoc/tag-lines": "error", 288 | 289 | // No need 290 | // "jsdoc/text-escaping": "error", 291 | 292 | // TODO enable after https://github.com/gajus/eslint-plugin-jsdoc/issues/1615 293 | // "jsdoc/type-formatting": "error", 294 | 295 | // Doesn't support function overloading/tuples/`readonly`/module keyword/etc 296 | // Also `typescript` reports this itself 297 | "jsdoc/valid-types": "off", 298 | }, 299 | }; 300 | } 301 | 302 | /** 303 | * @returns {Promise>} config 304 | */ 305 | async function getTypescriptRecommendedConfig() { 306 | let typescriptPlugin; 307 | 308 | try { 309 | typescriptPlugin = (await import("typescript-eslint")).default; 310 | // eslint-disable-next-line unicorn/prefer-optional-catch-binding 311 | } catch (_err) { 312 | // Nothing 313 | } 314 | 315 | const { configs } = typescriptPlugin || { 316 | configs: { 317 | base: { languageOptions: {} }, 318 | eslintRecommended: {}, 319 | recommended: [{ name: "typescript-eslint/recommended", rules: {} }], 320 | stylistic: [{ name: "typescript-eslint/stylistic", rules: {} }], 321 | }, 322 | }; 323 | const baseConfig = configs.base; 324 | const eslintRecommendedConfig = configs.eslintRecommended; 325 | const recommendedConfig = configs.recommended.find( 326 | (item) => item.name === "typescript-eslint/recommended", 327 | ); 328 | const stylisticConfig = configs.stylistic.find( 329 | (item) => item.name === "typescript-eslint/stylistic", 330 | ); 331 | 332 | const allExtensions = [...typescriptExtensions, ...javascriptExtensions]; 333 | 334 | return { 335 | ...baseConfig, 336 | name: "typescript/recommended", 337 | files: [ 338 | `**/*.{${typescriptExtensions.map((item) => item.slice(1)).join(",")}}`, 339 | ], 340 | ignores: ["**/*.d.ts"], 341 | languageOptions: { 342 | parser: baseConfig.languageOptions.parser, 343 | }, 344 | plugins: { 345 | ...baseConfig.plugins, 346 | }, 347 | settings: { 348 | "import/extensions": allExtensions, 349 | "import/external-module-folders": ["node_modules", "node_modules/@types"], 350 | "import/parsers": { 351 | "@typescript-eslint/parser": typescriptExtensions, 352 | }, 353 | "import/resolver": { 354 | node: { 355 | extensions: allExtensions, 356 | }, 357 | }, 358 | }, 359 | rules: { 360 | ...eslintRecommendedConfig.rules, 361 | ...recommendedConfig.rules, 362 | ...stylisticConfig.rules, 363 | 364 | // From recommended 365 | // "@typescript-eslint/adjacent-overload-signatures": "error", 366 | 367 | // From recommended 368 | // "@typescript-eslint/array-type": "error", 369 | 370 | // No need 371 | // "@typescript-eslint/await-thenable": "error", 372 | 373 | // From recommended 374 | // "@typescript-eslint/ban-ts-comment": "error", 375 | 376 | // From recommended 377 | // "@typescript-eslint/ban-tslint-comment": "error", 378 | 379 | // From recommended 380 | // "@typescript-eslint/class-literal-property-style": "error", 381 | 382 | // No need 383 | // "@typescript-eslint/class-methods-use-this": "error", 384 | 385 | // From recommended 386 | // "@typescript-eslint/consistent-generic-constructors": "error", 387 | 388 | // From recommended 389 | // "@typescript-eslint/consistent-indexed-object-style": "error", 390 | 391 | // No need 392 | // "@typescript-eslint/consistent-return": "error", 393 | 394 | // From recommended 395 | // "@typescript-eslint/consistent-type-assertions": "error", 396 | 397 | // From recommended 398 | // "@typescript-eslint/consistent-type-definitions": "error", 399 | 400 | // No need 401 | // "@typescript-eslint/consistent-type-exports": "error", 402 | 403 | // No need 404 | // "@typescript-eslint/consistent-type-imports": "error", 405 | 406 | // The same as `default-param-last` 407 | "default-param-last": "off", 408 | "@typescript-eslint/default-param-last": "error", 409 | 410 | // No need 411 | // we have `dot-notation` 412 | // "@typescript-eslint/dot-notation": "error", 413 | 414 | // No need 415 | // "@typescript-eslint/explicit-function-return-type": "error", 416 | 417 | "@typescript-eslint/explicit-member-accessibility": [ 418 | "error", 419 | { accessibility: "no-public" }, 420 | ], 421 | 422 | // No need 423 | // "@typescript-eslint/explicit-module-boundary-types": "error", 424 | 425 | // No need 426 | // "@typescript-eslint/init-declarations": "error", 427 | 428 | // No need 429 | // "@typescript-eslint/max-params": "error", 430 | 431 | // No need 432 | // "@typescript-eslint/member-ordering": "error", 433 | 434 | // No need 435 | // "@typescript-eslint/method-signature-style": "error", 436 | 437 | // No need 438 | // "@typescript-eslint/naming-convention": "error", 439 | 440 | // From recommended 441 | // "@typescript-eslint/no-array-constructor": "error", 442 | 443 | // No need 444 | // "@typescript-eslint/no-array-delete": "error", 445 | 446 | // No need 447 | // "@typescript-eslint/no-base-to-string": "error", 448 | 449 | // From recommended 450 | // "@typescript-eslint/no-confusing-non-null-assertion": "error", 451 | 452 | // No need 453 | // "@typescript-eslint/no-confusing-void-expression": "error", 454 | 455 | // No need 456 | // Good rule, but some packages can change their API often, and it will create noise in CI 457 | // "@typescript-eslint/no-deprecated": "error", 458 | 459 | // No need 460 | // "@typescript-eslint/no-dupe-class-members": "error", 461 | 462 | // From recommended 463 | // "@typescript-eslint/no-duplicate-enum-values": "error", 464 | 465 | // No need 466 | // "@typescript-eslint/no-dynamic-delete": "error", 467 | 468 | // From recommended 469 | // "@typescript-eslint/no-empty-function": "error", 470 | 471 | // From recommended 472 | // "@typescript-eslint/no-empty-object-type": "error", 473 | 474 | // From recommended 475 | // "@typescript-eslint/no-explicit-any": "error", 476 | 477 | // From recommended 478 | // "@typescript-eslint/no-extra-non-null-assertion": "error", 479 | 480 | // No need 481 | // "@typescript-eslint/no-extraneous-class": "error", 482 | 483 | // No need 484 | // "@typescript-eslint/no-floating-promises": "error", 485 | 486 | // No need 487 | // "@typescript-eslint/no-for-in-array": "error", 488 | 489 | // No need 490 | // "@typescript-eslint/no-implied-eval": "error", 491 | 492 | // No need 493 | // "@typescript-eslint/no-import-type-side-effects": "error", 494 | 495 | // From recommended 496 | // "@typescript-eslint/no-inferrable-types": "error", 497 | 498 | // No need 499 | // "@typescript-eslint/no-invalid-this": "error", 500 | 501 | // No need 502 | // "@typescript-eslint/no-invalid-void-type": "error", 503 | 504 | // The same as `no-loop-func` 505 | "no-loop-func": "off", 506 | "@typescript-eslint/no-loop-func": "error", 507 | 508 | // No need 509 | // "@typescript-eslint/no-magic-numbers": "error", 510 | 511 | // No need 512 | // "@typescript-eslint/no-meaningless-void-operator": "error", 513 | 514 | // From recommended 515 | // "@typescript-eslint/no-misused-new": "error", 516 | 517 | // No need 518 | // "@typescript-eslint/no-misused-promises": "error", 519 | 520 | // No need 521 | // "@typescript-eslint/no-misused-spread": "error", 522 | 523 | // No need 524 | // "@typescript-eslint/no-mixed-enums": "error", 525 | 526 | // No need 527 | // "@typescript-eslint/no-namespace": "error", 528 | 529 | // No need 530 | // "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error", 531 | 532 | // From recommended 533 | // "@typescript-eslint/no-non-null-asserted-optional-chain": "error", 534 | 535 | // No need 536 | // "@typescript-eslint/no-non-null-assertion": "error", 537 | 538 | // No need 539 | // "@typescript-eslint/no-redeclare": "error", 540 | 541 | // No need 542 | // "@typescript-eslint/no-redundant-type-constituents": "error", 543 | 544 | // Module system provided in `node/module`/`node/commonjs`/etc configurations 545 | "@typescript-eslint/no-require-imports": "off", 546 | 547 | // No need 548 | // "@typescript-eslint/no-restricted-imports": "error", 549 | 550 | // No need 551 | // "@typescript-eslint/no-restricted-types": "error", 552 | 553 | // No need 554 | // "@typescript-eslint/no-shadow": "error", 555 | 556 | // From recommended 557 | // "@typescript-eslint/no-this-alias": "error", 558 | 559 | // No need 560 | // "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error", 561 | 562 | // "@typescript-eslint/no-unnecessary-condition": "error", 563 | 564 | "@typescript-eslint/no-unnecessary-parameter-property-assignment": 565 | "error", 566 | 567 | // No need 568 | // "@typescript-eslint/no-unnecessary-qualifier": "error", 569 | 570 | // No need 571 | // "@typescript-eslint/no-unnecessary-template-expression": "error", 572 | 573 | // No need 574 | // "@typescript-eslint/no-unnecessary-type-arguments": "error", 575 | 576 | // No need 577 | // "@typescript-eslint/no-unnecessary-type-assertion": "error", 578 | 579 | // From recommended 580 | // "@typescript-eslint/no-unnecessary-type-constraint": "error", 581 | 582 | // No need 583 | // "@typescript-eslint/no-unnecessary-type-conversion": "error", 584 | 585 | // No need 586 | // "@typescript-eslint/no-unnecessary-type-parameters": "error", 587 | 588 | // No need 589 | // "@typescript-eslint/no-unsafe-argument": "error", 590 | 591 | // No need 592 | // "@typescript-eslint/no-unsafe-assignment": "error", 593 | 594 | // No need 595 | // "@typescript-eslint/no-unsafe-call": "error", 596 | 597 | // From recommended 598 | // "@typescript-eslint/no-unsafe-declaration-merging": "error", 599 | 600 | // No need 601 | // "@typescript-eslint/no-unsafe-enum-comparison": "error", 602 | 603 | // From recommended 604 | // "@typescript-eslint/no-unsafe-function-type": "error", 605 | 606 | // No need 607 | // "@typescript-eslint/no-unsafe-member-access": "error", 608 | 609 | // No need 610 | // "@typescript-eslint/no-unsafe-return": "error", 611 | 612 | // No need 613 | // "@typescript-eslint/no-unsafe-type-assertion": "error", 614 | 615 | // No need 616 | // "@typescript-eslint/no-unsafe-unary-minus": "error", 617 | 618 | // From recommended 619 | // "@typescript-eslint/no-unused-expressions": "error", 620 | 621 | "no-unused-private-class-members": "off", 622 | "@typescript-eslint/no-unused-private-class-members": "error", 623 | 624 | // Provide better options 625 | "no-unused-vars": "off", 626 | "@typescript-eslint/no-unused-vars": [ 627 | "error", 628 | { 629 | args: "after-used", 630 | argsIgnorePattern: "^_", 631 | caughtErrors: "all", 632 | caughtErrorsIgnorePattern: "^_", 633 | destructuredArrayIgnorePattern: "^_", 634 | ignoreRestSiblings: true, 635 | ignoreClassWithStaticInitBlock: false, 636 | reportUsedIgnorePattern: false, 637 | }, 638 | ], 639 | 640 | // From recommended 641 | // "@typescript-eslint/no-unused-vars": "error", 642 | 643 | // The same as `no-use-before-define` 644 | "no-use-before-define": "off", 645 | "@typescript-eslint/no-use-before-define": [ 646 | "error", 647 | { 648 | functions: true, 649 | classes: true, 650 | variables: true, 651 | enums: true, 652 | typedefs: true, 653 | }, 654 | ], 655 | 656 | // No need 657 | // "@typescript-eslint/no-useless-constructor": "error", 658 | 659 | // No need 660 | // "@typescript-eslint/no-useless-default-assignment": "error", 661 | 662 | "@typescript-eslint/no-useless-empty-export": "error", 663 | 664 | // From recommended 665 | "@typescript-eslint/no-wrapper-object-types": "error", 666 | 667 | // No need 668 | // "@typescript-eslint/non-nullable-type-assertion-style": "error", 669 | 670 | // No need 671 | // "@typescript-eslint/only-throw-error": "error", 672 | 673 | // No need 674 | // "@typescript-eslint/parameter-properties": "error", 675 | 676 | // From recommended 677 | // "@typescript-eslint/prefer-as-const": "error", 678 | 679 | // No need 680 | // "@typescript-eslint/prefer-destructuring": "error", 681 | 682 | // No need 683 | // "@typescript-eslint/prefer-enum-initializers": "error", 684 | 685 | // No need 686 | // "@typescript-eslint/prefer-find": "error", 687 | 688 | // From recommended 689 | // "@typescript-eslint/prefer-for-of": "error", 690 | 691 | // From recommended 692 | // "@typescript-eslint/prefer-function-type": "error", 693 | 694 | // No need 695 | // "@typescript-eslint/prefer-includes": "error", 696 | 697 | // No need 698 | // "@typescript-eslint/prefer-literal-enum-member": "error", 699 | 700 | // From recommended 701 | // "@typescript-eslint/prefer-namespace-keyword": "error", 702 | 703 | // No need 704 | // "@typescript-eslint/prefer-nullish-coalescing": "error", 705 | 706 | // No need 707 | // "@typescript-eslint/prefer-optional-chain": "error", 708 | 709 | // No need 710 | // "@typescript-eslint/prefer-promise-reject-errors": "error", 711 | 712 | // No need 713 | // "@typescript-eslint/prefer-readonly": "error", 714 | 715 | // No need 716 | // "@typescript-eslint/prefer-readonly-parameter-types": "error", 717 | 718 | // No need 719 | // "@typescript-eslint/prefer-reduce-type-parameter": "error", 720 | 721 | // No need 722 | // "@typescript-eslint/prefer-regexp-exec": "error", 723 | 724 | // No need 725 | // "@typescript-eslint/prefer-return-this-type": "error", 726 | 727 | // No need 728 | // "@typescript-eslint/prefer-string-starts-ends-with": "error", 729 | 730 | // No need 731 | // "@typescript-eslint/promise-function-async": "error", 732 | 733 | // No need 734 | // "@typescript-eslint/related-getter-setter-pairs": "error", 735 | 736 | // No need 737 | // "@typescript-eslint/require-array-sort-compare": "error", 738 | 739 | // No need 740 | // "@typescript-eslint/require-await": "error", 741 | 742 | // No need 743 | // "@typescript-eslint/restrict-plus-operands": "error", 744 | 745 | // No need 746 | // "@typescript-eslint/restrict-template-expressions": "error", 747 | 748 | // No need 749 | // "@typescript-eslint/return-await": "error", 750 | 751 | // No need 752 | // "@typescript-eslint/strict-boolean-expressions": "error", 753 | 754 | // No need 755 | // "@typescript-eslint/switch-exhaustiveness-check": "error", 756 | 757 | // From recommended 758 | // "@typescript-eslint/triple-slash-reference": "error", 759 | 760 | // No need 761 | // "@typescript-eslint/unbound-method": "error", 762 | 763 | // No need 764 | // "@typescript-eslint/unified-signatures": "error", 765 | 766 | // No need 767 | // "use-unknown-in-catch-callback-variable": "error", 768 | 769 | // TypeScript compilation already ensures that named imports exist in the referenced module 770 | "import/named": "off", 771 | 772 | // TypeScript handles this for us 773 | "import/no-unresolved": "off", 774 | }, 775 | }; 776 | } 777 | 778 | export default { 779 | "typescript/recommended": await getTypescriptRecommendedConfig(), 780 | "typescript/jsdoc": await getTypescriptJSDocRecommendedConfig(), 781 | }; 782 | -------------------------------------------------------------------------------- /configs/javascript.js: -------------------------------------------------------------------------------- 1 | import javascriptConfig from "@eslint/js"; 2 | import importPlugin from "eslint-plugin-import"; 3 | import unicornPlugin from "eslint-plugin-unicorn"; 4 | import globals from "globals"; 5 | import { allExtensions, javascriptExtensions } from "./utils/extensions.js"; 6 | import isTypescriptInstalled from "./utils/is-typescript-installed.js"; 7 | 8 | const possibleProblems = { 9 | "array-callback-return": [ 10 | "error", 11 | { 12 | allowImplicit: true, 13 | }, 14 | ], 15 | 16 | // From recommended 17 | // "constructor-super": "error" 18 | 19 | // From recommended 20 | // "for-direction": "error" 21 | 22 | // From recommended 23 | // "getter-return": "error" 24 | 25 | // From recommended 26 | // "no-async-promise-executor": "error" 27 | 28 | // No need 29 | // "no-await-in-loop": "off", 30 | 31 | // From recommended 32 | // "no-class-assign": "error", 33 | 34 | // From recommended 35 | // "no-compare-neg-zero": "error", 36 | 37 | // From recommended 38 | // "no-cond-assign": "error", 39 | 40 | // From recommended 41 | // "no-const-assign": "error", 42 | 43 | // From recommended 44 | // "no-constant-binary-expression": "error", 45 | 46 | // From recommended 47 | // "no-constant-condition": "error", 48 | 49 | "no-constructor-return": "error", 50 | 51 | // From recommended 52 | // "no-control-regex": "error", 53 | 54 | // From recommended 55 | // "no-debugger": "error", 56 | 57 | // From recommended 58 | // "no-dupe-args": "error", 59 | 60 | // From recommended 61 | // "no-dupe-class-members": "error", 62 | 63 | // From recommended 64 | // "no-dupe-else-if": "error", 65 | 66 | // From recommended 67 | // "no-dupe-keys": "error", 68 | 69 | // From recommended 70 | // "no-duplicate-case": "error", 71 | 72 | "no-duplicate-imports": "error", 73 | 74 | // From recommended 75 | // "no-empty-character-class": "error", 76 | 77 | // From recommended 78 | // "no-empty-pattern": "error", 79 | 80 | // From recommended 81 | // "no-ex-assign": "error", 82 | 83 | // From recommended 84 | // "no-fallthrough": "error", 85 | 86 | // From recommended 87 | // "no-func-assign": "error", 88 | 89 | // From recommended 90 | // "no-import-assign": "error", 91 | 92 | "no-inner-declarations": "error", 93 | 94 | // From recommended 95 | // "no-invalid-regexp": "error", 96 | 97 | // From recommended 98 | // "no-irregular-whitespace": "error", 99 | 100 | // From recommended 101 | // "no-loss-of-precision": "error", 102 | 103 | // From recommended 104 | // "no-misleading-character-class": "error", 105 | 106 | // From recommended 107 | // "no-new-native-nonconstructor": "error", 108 | 109 | // From recommended 110 | // "no-obj-calls": "error", 111 | 112 | "no-promise-executor-return": "error", 113 | 114 | // From recommended 115 | // "no-prototype-builtins": "error", 116 | 117 | // From recommended 118 | // "no-self-assign": "error", 119 | 120 | "no-self-compare": "error", 121 | 122 | // From recommended 123 | // "no-setter-return": "error", 124 | 125 | // From recommended 126 | // "no-sparse-arrays": "error", 127 | 128 | "no-template-curly-in-string": "error", 129 | 130 | // From recommended 131 | // "no-this-before-super": "error", 132 | 133 | "no-unassigned-vars": "error", 134 | 135 | // From recommended 136 | // "no-undef": "error", 137 | 138 | // From recommended 139 | // "no-unexpected-multiline": "error", 140 | 141 | "no-unmodified-loop-condition": "error", 142 | 143 | // From recommended 144 | // "no-unreachable": "error", 145 | 146 | "no-unreachable-loop": "error", 147 | 148 | // From recommended 149 | // "no-unsafe-finally": "error", 150 | 151 | // From recommended 152 | // "no-unsafe-negation": "error", 153 | 154 | // From recommended 155 | // "no-unsafe-optional-chaining": "error", 156 | 157 | // From recommended 158 | // "no-unused-private-class-members": "error", 159 | 160 | // From recommended 161 | "no-unused-vars": [ 162 | "error", 163 | { 164 | vars: "all", 165 | varsIgnorePattern: "^_", 166 | args: "after-used", 167 | argsIgnorePattern: "^_", 168 | caughtErrors: "all", 169 | caughtErrorsIgnorePattern: "^_", 170 | destructuredArrayIgnorePattern: "^_", 171 | ignoreRestSiblings: true, 172 | ignoreClassWithStaticInitBlock: false, 173 | reportUsedIgnorePattern: false, 174 | }, 175 | ], 176 | 177 | "no-use-before-define": [ 178 | "error", 179 | { functions: true, classes: true, variables: true }, 180 | ], 181 | 182 | // No need 183 | // "no-useless-assignment": "off", 184 | 185 | // From recommended 186 | // "no-useless-backreference": "error", 187 | 188 | // No need 189 | // "require-atomic-updates": "off", 190 | 191 | // From recommended 192 | // "use-isnan": "error", 193 | 194 | // From recommended 195 | // "valid-typeof": "error", 196 | }; 197 | 198 | const suggestions = { 199 | "accessor-pairs": "error", 200 | 201 | "arrow-body-style": ["error", "as-needed"], 202 | 203 | "block-scoped-var": "error", 204 | 205 | camelcase: [ 206 | "error", 207 | { 208 | allow: [ 209 | "^__webpack", 210 | "^__non_webpack", 211 | "^__system", 212 | "^_stream", 213 | "string_decoder", 214 | ], 215 | }, 216 | ], 217 | 218 | // No need 219 | // "capitalized-comments": "error" 220 | 221 | // No need 222 | // "class-methods-use-this": "error" 223 | 224 | // No need 225 | // "complexity": "error" 226 | 227 | // No need 228 | // "consistent-return": "error", 229 | 230 | // No need 231 | // "consistent-this": "error", 232 | 233 | // Configuration in `stylistic` 234 | // curly: "off", 235 | 236 | // No need 237 | // "default-case": "error", 238 | 239 | "default-case-last": "error", 240 | 241 | "default-param-last": "error", 242 | 243 | "dot-notation": "error", 244 | 245 | eqeqeq: "error", 246 | 247 | "func-name-matching": [ 248 | "error", 249 | "always", 250 | { 251 | considerPropertyDescriptor: true, 252 | includeCommonJSModuleExports: false, 253 | }, 254 | ], 255 | 256 | "func-names": ["error", "as-needed"], 257 | 258 | "func-style": [ 259 | "error", 260 | "declaration", 261 | { 262 | allowArrowFunctions: true, 263 | allowTypeAnnotation: true, 264 | }, 265 | ], 266 | 267 | "grouped-accessor-pairs": "error", 268 | 269 | // No need 270 | // In most cases, the developer understands what he is doing and where, so no additional checks are required 271 | // "guard-for-in": "error", 272 | 273 | // No need 274 | // "id-denylist": ["error", []] 275 | 276 | "id-length": [ 277 | "error", 278 | { 279 | min: 2, 280 | max: Number.POSITIVE_INFINITY, 281 | properties: "never", 282 | exceptions: [ 283 | // jQuery 284 | "$", 285 | // Loops 286 | "i", 287 | "j", 288 | "k", 289 | "v", 290 | "m", 291 | "n", 292 | "t", 293 | // Left and right 294 | "l", 295 | "r", 296 | // Lodash 297 | "_", 298 | // Comparison 299 | "a", 300 | "b", 301 | ], 302 | }, 303 | ], 304 | 305 | "id-match": [ 306 | "error", 307 | "^[$a-zA-Z_][$a-zA-Z0-9_]*$", 308 | { 309 | properties: true, 310 | }, 311 | ], 312 | 313 | // No need 314 | // "init-declarations": "error", 315 | 316 | "logical-assignment-operators": "error", 317 | 318 | // No need 319 | // "max-classes-per-file": "off", 320 | 321 | // No need 322 | // "max-depth": "off", 323 | 324 | // No need 325 | // "max-lines": "off", 326 | 327 | // No need 328 | // "max-lines-per-function": "off", 329 | 330 | // No need 331 | // "max-nested-callbacks": "off", 332 | 333 | // No need 334 | // "max-params": "off", 335 | 336 | // No need 337 | // "max-statements": "off", 338 | 339 | "new-cap": "error", 340 | 341 | "no-alert": "error", 342 | 343 | "no-array-constructor": "error", 344 | 345 | // No need 346 | // "no-bitwise": "error", 347 | 348 | "no-caller": "error", 349 | 350 | // From recommended 351 | // "no-case-declarations": "error", 352 | 353 | "no-console": "error", 354 | 355 | // No need 356 | // "no-continue": "error", 357 | 358 | // From recommended 359 | // "no-delete-var": "error", 360 | 361 | "no-div-regex": "error", 362 | 363 | "no-else-return": "error", 364 | 365 | // From recommended 366 | // "no-empty": "error", 367 | 368 | "no-empty-function": [ 369 | "error", 370 | { 371 | allow: [ 372 | "functions", 373 | "arrowFunctions", 374 | "asyncFunctions", 375 | "methods", 376 | "asyncMethods", 377 | "generatorMethods", 378 | ], 379 | }, 380 | ], 381 | // "no-empty-function": "error" 382 | 383 | // From recommended 384 | // "no-empty-static-block": "error" 385 | 386 | "no-eq-null": "error", 387 | 388 | "no-eval": "error", 389 | 390 | "no-extend-native": "error", 391 | 392 | "no-extra-bind": "error", 393 | 394 | // From recommended 395 | // "no-extra-boolean-cast": "error", 396 | 397 | "no-extra-label": "error", 398 | 399 | "no-global-assign": "error", 400 | 401 | "no-implicit-coercion": [ 402 | "error", 403 | { 404 | boolean: true, 405 | number: true, 406 | string: true, 407 | }, 408 | ], 409 | 410 | // No need 411 | // Make sense only for `browser` configuration for old browsers 412 | // "no-implicit-globals": "off", 413 | 414 | "no-implied-eval": "error", 415 | 416 | // No need 417 | // "no-inline-comments": "error", 418 | 419 | // No need 420 | // "no-invalid-this": "error", 421 | 422 | "no-iterator": "error", 423 | 424 | "no-label-var": "error", 425 | 426 | // No need 427 | // "no-labels": "error", 428 | 429 | "no-lone-blocks": "error", 430 | 431 | "no-lonely-if": "error", 432 | 433 | "no-loop-func": "error", 434 | 435 | // No need 436 | // "no-magic-numbers": "off", 437 | 438 | // No need 439 | // "no-multi-assign": "off", 440 | 441 | "no-multi-str": "error", 442 | 443 | // No need 444 | // "no-negated-condition": "off", 445 | 446 | // No need 447 | // "no-nested-ternary": "off", 448 | 449 | "no-new": "error", 450 | 451 | "no-new-func": "error", 452 | 453 | "no-new-wrappers": "error", 454 | 455 | // From recommended 456 | // "no-nonoctal-decimal-escape": "error", 457 | 458 | "no-object-constructor": "error", 459 | 460 | // From recommended 461 | // "no-octal": "error", 462 | 463 | "no-octal-escape": "error", 464 | 465 | // No need 466 | // "no-param-reassign": "off", 467 | 468 | // No need 469 | // "no-plusplus": "off", 470 | 471 | "no-proto": "error", 472 | 473 | // From recommended 474 | // "no-redeclare": "error", 475 | 476 | // From recommended 477 | // "no-regex-spaces": "error", 478 | 479 | // No need 480 | // "no-restricted-exports": "error", 481 | 482 | // No need 483 | // "no-restricted-globals": "error", 484 | 485 | // No need 486 | // "no-restricted-imports": "off", 487 | 488 | // No need 489 | // "no-restricted-properties": "off", 490 | 491 | // No need 492 | // "no-restricted-syntax": ["error", "DebuggerStatement", "WithStatement"], 493 | 494 | "no-return-assign": "error", 495 | 496 | "no-script-url": "error", 497 | 498 | "no-sequences": "error", 499 | 500 | // No need 501 | // "no-shadow": "off", 502 | 503 | // From recommended 504 | // "no-shadow-restricted-names": "error", 505 | 506 | // No need 507 | // "no-ternary": "error", 508 | 509 | "no-throw-literal": "error", 510 | 511 | "no-undef-init": "error", 512 | 513 | // No need 514 | // "no-undefined": "off", 515 | 516 | // No need 517 | // "no-underscore-dangle": "off", 518 | 519 | "no-unneeded-ternary": ["error", { defaultAssignment: false }], 520 | 521 | "no-unused-expressions": [ 522 | "error", 523 | { 524 | allowShortCircuit: false, 525 | allowTernary: false, 526 | allowTaggedTemplates: true, 527 | }, 528 | ], 529 | 530 | // From recommended 531 | // "no-unused-labels": "error", 532 | 533 | "no-useless-call": "error", 534 | 535 | // From recommended 536 | "no-useless-catch": "error", 537 | 538 | "no-useless-computed-key": "error", 539 | 540 | "no-useless-concat": "error", 541 | 542 | // No need 543 | // "no-useless-constructor": "error", 544 | 545 | "no-useless-escape": "error", 546 | 547 | "no-useless-rename": [ 548 | "error", 549 | { 550 | ignoreDestructuring: false, 551 | ignoreImport: false, 552 | ignoreExport: false, 553 | }, 554 | ], 555 | 556 | "no-useless-return": "error", 557 | 558 | "no-var": "error", 559 | 560 | "no-void": "error", 561 | 562 | // Disallow ts-ignore directive. Use ts-expect-error instead 563 | "no-warning-comments": ["error", { terms: ["@ts-ignore"] }], 564 | 565 | "no-with": "error", 566 | 567 | "object-shorthand": "error", 568 | 569 | "one-var": ["error", "never"], 570 | 571 | "operator-assignment": "error", 572 | 573 | "prefer-arrow-callback": [ 574 | "error", 575 | { 576 | allowNamedFunctions: false, 577 | allowUnboundThis: true, 578 | }, 579 | ], 580 | 581 | "prefer-const": [ 582 | "error", 583 | { 584 | destructuring: "all", 585 | ignoreReadBeforeAssign: true, 586 | }, 587 | ], 588 | 589 | "prefer-destructuring": [ 590 | "error", 591 | { 592 | VariableDeclarator: { 593 | array: true, 594 | object: true, 595 | }, 596 | AssignmentExpression: { 597 | array: true, 598 | // Avoid `({ property } = object);` because: 599 | // it is ugly, it is often not convenient, this does not allow to set types using typescript jsdoc 600 | object: false, 601 | }, 602 | }, 603 | { 604 | enforceForRenamedProperties: false, 605 | }, 606 | ], 607 | 608 | "prefer-exponentiation-operator": "error", 609 | 610 | // No need 611 | // "prefer-named-capture-group": "off", 612 | 613 | "prefer-numeric-literals": "error", 614 | 615 | "prefer-object-has-own": "error", 616 | 617 | "prefer-object-spread": "error", 618 | 619 | "prefer-promise-reject-errors": ["error", { allowEmptyReject: true }], 620 | 621 | "prefer-regex-literals": "error", 622 | 623 | "prefer-rest-params": "error", 624 | 625 | "prefer-spread": "error", 626 | 627 | "prefer-template": "error", 628 | 629 | "preserve-caught-error": "error", 630 | 631 | radix: ["error", "always"], 632 | 633 | // `require-await` doesn't work when the function returns Promise 634 | // "require-await": "off", 635 | 636 | // No need 637 | // "require-unicode-regexp": "error", 638 | 639 | // From recommended 640 | // "require-yield": "error", 641 | 642 | // No need 643 | // "sort-imports": "off", 644 | 645 | // No need 646 | // "sort-keys": "off", 647 | 648 | // No need 649 | // "sort-vars": "off", 650 | 651 | strict: ["error", "global"], 652 | 653 | "symbol-description": "error", 654 | 655 | // No need 656 | // "vars-on-top": "off", 657 | 658 | yoda: "error", 659 | }; 660 | 661 | const layoutAndFormatting = { 662 | "unicode-bom": ["error", "never"], 663 | }; 664 | 665 | const unicornRules = { 666 | // No need 667 | // "unicorn/better-regex": "off", 668 | 669 | "unicorn/catch-error-name": [ 670 | "error", 671 | { name: "err", ignore: [/(^_|[0-9]+$)/i, /^error$/] }, 672 | ], 673 | 674 | "unicorn/consistent-assert": "error", 675 | 676 | "unicorn/consistent-date-clone": "error", 677 | 678 | "unicorn/consistent-destructuring": "off", 679 | 680 | "unicorn/consistent-empty-array-spread": "error", 681 | 682 | // No need 683 | // "unicorn/consistent-existence-index-check": "off", 684 | 685 | // No need 686 | // "unicorn/consistent-function-scoping": "off", 687 | 688 | // No need 689 | // "unicorn/custom-error-definition": "off", 690 | 691 | // No need 692 | // "unicorn/empty-brace-spaces": "off", 693 | 694 | "unicorn/error-message": "error", 695 | 696 | "unicorn/escape-case": "error", 697 | 698 | // No need 699 | // "unicorn/expiring-todo-comments": "off", 700 | 701 | // No need 702 | // "unicorn/explicit-length-check": "off", 703 | 704 | // TODO 705 | "unicorn/filename-case": [ 706 | "off", 707 | { 708 | cases: { 709 | kebabCase: true, 710 | pascalCase: true, 711 | }, 712 | ignore: [ 713 | /^CHANGES.md$/, 714 | /^CHANGELOG.md$/, 715 | /^HISTORY.md$/, 716 | /^README.md$/, 717 | /^LICENSE.md$/, 718 | /^LICENCE.md$/, 719 | /^NOTICE.md$/, 720 | /^CODE_OF_CONDUCT.md$/, 721 | /^CONTRIBUTING.md$/, 722 | /^AUTHORS.md$/, 723 | /^SECURITY.md$/, 724 | /^ISSUE_TEMPLATE.md$/, 725 | /^PULL_REQUEST_TEMPLATE.md$/, 726 | ], 727 | }, 728 | ], 729 | 730 | // No need 731 | // "unicorn/import-style": "off", 732 | 733 | "unicorn/new-for-builtins": "error", 734 | 735 | "unicorn/no-abusive-eslint-disable": "error", 736 | 737 | "unicorn/no-accessor-recursion": "error", 738 | 739 | // No need 740 | // "unicorn/no-anonymous-default-export": "off", 741 | 742 | // No need 743 | // "unicorn/no-array-callback-reference": "off", 744 | 745 | "unicorn/no-array-for-each": "error", 746 | 747 | "unicorn/no-array-method-this-argument": "error", 748 | 749 | // No need 750 | // "unicorn/no-array-reduce": "off", 751 | 752 | // No need 753 | // "unicorn/no-array-reverse": "off", 754 | 755 | "unicorn/no-array-sort": "error", 756 | 757 | // No need 758 | // "unicorn/no-await-expression-member": "off", 759 | 760 | "unicorn/no-await-in-promise-methods": "error", 761 | 762 | "unicorn/no-console-spaces": "error", 763 | 764 | // No need 765 | // "unicorn/no-document-cookie": "off", 766 | 767 | // No need 768 | // "unicorn/no-empty-file": "off", 769 | 770 | // No need 771 | // "unicorn/no-for-loop": "off", 772 | 773 | "unicorn/no-hex-escape": "error", 774 | 775 | // No need 776 | // "unicorn/no-immediate-mutation": "error", 777 | 778 | "unicorn/no-instanceof-builtins": "error", 779 | 780 | "unicorn/no-invalid-fetch-options": "error", 781 | 782 | "unicorn/no-invalid-remove-event-listener": "error", 783 | 784 | // No need 785 | // "unicorn/no-keyword-prefix": "off", 786 | 787 | "unicorn/no-lonely-if": "error", 788 | 789 | // No need 790 | // "unicorn/no-magic-array-flat-depth": "error", 791 | 792 | // No need 793 | // "unicorn/no-named-default": "error", 794 | 795 | // No need 796 | // "unicorn/no-negated-condition": "off", 797 | 798 | // No need 799 | // "unicorn/no-negation-in-equality-check": "off", 800 | 801 | // No need 802 | // "unicorn/no-nested-ternary": "off", 803 | 804 | "unicorn/no-new-array": "error", 805 | 806 | // No need 807 | // We are catching this in `node/no-deprecated-api` rule 808 | // "unicorn/no-new-buffer": "off", 809 | 810 | // No need 811 | // "unicorn/no-null": "off", 812 | 813 | // No need 814 | // "unicorn/no-object-as-default-parameter": "off", 815 | 816 | // No need 817 | // "unicorn/no-process-exit": "off", 818 | 819 | "unicorn/no-single-promise-in-promise-methods": "error", 820 | 821 | // No need 822 | // "unicorn/no-static-only-class": "off", 823 | 824 | "unicorn/no-thenable": "error", 825 | 826 | // No need 827 | // "unicorn/no-this-assignment": "off", 828 | 829 | // TODO - enable in future? 830 | // "unicorn/no-typeof-undefined": "off", 831 | 832 | "unicorn/no-unnecessary-array-flat-depth": "error", 833 | 834 | "unicorn/no-unnecessary-array-splice-count": "error", 835 | 836 | "unicorn/no-unnecessary-await": "error", 837 | 838 | "unicorn/no-unnecessary-polyfills": "error", 839 | 840 | "unicorn/no-unnecessary-slice-end": "error", 841 | 842 | // No need 843 | // "unicorn/no-unreadable-array-destructuring": "off", 844 | 845 | // No need 846 | // "unicorn/no-unreadable-iife": "off", 847 | 848 | // No need 849 | // "unicorn/no-unused-properties": "off", 850 | 851 | "unicorn/no-useless-collection-argument": "error", 852 | 853 | "unicorn/no-useless-error-capture-stack-trace": "error", 854 | 855 | "unicorn/no-useless-fallback-in-spread": "error", 856 | 857 | "unicorn/no-useless-length-check": "error", 858 | 859 | "unicorn/no-useless-promise-resolve-reject": "error", 860 | 861 | "unicorn/no-useless-spread": "error", 862 | 863 | // No need 864 | // "unicorn/no-useless-switch-case": "off", 865 | 866 | // No need 867 | // "unicorn/no-useless-undefined": "off", 868 | 869 | "unicorn/no-zero-fractions": "error", 870 | 871 | // No need 872 | // `prettier` makes it 873 | // "unicorn/number-literal-case": "off", 874 | 875 | // No need 876 | // "unicorn/numeric-separators-style": "off", 877 | 878 | // No need 879 | // "unicorn/prefer-add-event-listener": "off", 880 | 881 | "unicorn/prefer-array-find": "error", 882 | 883 | "unicorn/prefer-array-flat": "error", 884 | 885 | "unicorn/prefer-array-flat-map": "error", 886 | 887 | "unicorn/prefer-array-index-of": "error", 888 | 889 | "unicorn/prefer-array-some": "error", 890 | 891 | // No need 892 | // "unicorn/prefer-at": "off", 893 | 894 | "unicorn/prefer-bigint-literals": "error", 895 | 896 | "unicorn/prefer-blob-reading-methods": "error", 897 | 898 | "unicorn/prefer-class-fields": "error", 899 | 900 | // Not here, define only for `browsers` 901 | // "unicorn/prefer-classlist-toggle": "error", 902 | 903 | // No need 904 | // "unicorn/prefer-code-point": "error", 905 | 906 | "unicorn/prefer-date-now": "error", 907 | 908 | "unicorn/prefer-default-parameters": "error", 909 | 910 | // Not here, define only for `browsers` 911 | // "unicorn/prefer-dom-node-append": "error", 912 | 913 | // Not here, define only for `browsers` 914 | // "unicorn/prefer-dom-node-dataset": "error", 915 | 916 | // Not here, define only for `browsers` 917 | // "unicorn/prefer-dom-node-remove": "error", 918 | 919 | // Not here, define only for `browsers` 920 | // "unicorn/prefer-dom-node-text-content": "error", 921 | 922 | "unicorn/prefer-event-target": "error", 923 | 924 | "unicorn/prefer-export-from": "error", 925 | 926 | "unicorn/prefer-global-this": "error", 927 | 928 | // No need 929 | // "unicorn/prefer-import-meta-properties": "off", 930 | 931 | "unicorn/prefer-includes": "error", 932 | 933 | // No need 934 | // "unicorn/prefer-json-parse-buffer": "off", 935 | 936 | // Not here, define only for `browsers` 937 | // "unicorn/prefer-keyboard-event-key": "error", 938 | 939 | "unicorn/prefer-logical-operator-over-ternary": "error", 940 | 941 | // No need 942 | // "unicorn/prefer-math-min-max": "off", 943 | 944 | // No need 945 | // `| 0` is faster 946 | // "unicorn/prefer-math-trunc": "off", 947 | 948 | // Not here, define only for `browsers` 949 | // "unicorn/prefer-modern-dom-apis": "error", 950 | 951 | "unicorn/prefer-modern-math-apis": "error", 952 | 953 | // No need 954 | // "unicorn/prefer-module": "off", 955 | 956 | "unicorn/prefer-native-coercion-functions": "error", 957 | 958 | "unicorn/prefer-negative-index": "error", 959 | 960 | // No need 961 | // We have `n/prefer-node-protocol` rule 962 | // "unicorn/prefer-node-protocol": "off", 963 | 964 | "unicorn/prefer-number-properties": "error", 965 | 966 | "unicorn/prefer-object-from-entries": "error", 967 | 968 | "unicorn/prefer-optional-catch-binding": "error", 969 | 970 | "unicorn/prefer-prototype-methods": "error", 971 | 972 | // Not here, define only for `browsers` 973 | // "unicorn/prefer-query-selector": "error", 974 | 975 | // No need 976 | // "unicorn/prefer-reflect-apply": "off", 977 | 978 | "unicorn/prefer-regexp-test": "error", 979 | 980 | "unicorn/prefer-response-static-json": "error", 981 | 982 | // No need 983 | // "unicorn/prefer-set-has": "off", 984 | 985 | // No need 986 | // "unicorn/prefer-set-size": "off", 987 | 988 | // No need 989 | // "unicorn/prefer-single-call": "off", 990 | 991 | "unicorn/prefer-spread": "error", 992 | 993 | // No need 994 | // "unicorn/prefer-string-raw": "error" 995 | 996 | "unicorn/prefer-string-replace-all": "error", 997 | 998 | "unicorn/prefer-string-slice": "error", 999 | 1000 | "unicorn/prefer-string-starts-ends-with": "error", 1001 | 1002 | "unicorn/prefer-string-trim-start-end": "error", 1003 | 1004 | "unicorn/prefer-structured-clone": "error", 1005 | 1006 | // No need 1007 | // "unicorn/prefer-switch": "error", 1008 | 1009 | "unicorn/prefer-ternary": ["error", "only-single-line"], 1010 | 1011 | "unicorn/prefer-top-level-await": "error", 1012 | 1013 | // No need 1014 | // "unicorn/prefer-type-error": "error", 1015 | 1016 | // No need 1017 | // "unicorn/prevent-abbreviations": "error", 1018 | 1019 | "unicorn/relative-url-style": "error", 1020 | 1021 | // No need 1022 | // "unicorn/require-array-join-separator": "error", 1023 | 1024 | "unicorn/require-module-attributes": "error", 1025 | 1026 | "unicorn/require-module-specifiers": "error", 1027 | 1028 | // No need 1029 | // "unicorn/require-number-to-fixed-digits-argument": "error", 1030 | 1031 | // No need 1032 | // "unicorn/require-post-message-target-origin": "error", 1033 | 1034 | // No need 1035 | // "unicorn/string-content": "off", 1036 | 1037 | // No need 1038 | // "unicorn/switch-case-braces": "off", 1039 | 1040 | // No need 1041 | // "unicorn/template-indent": "off", 1042 | 1043 | "unicorn/text-encoding-identifier-case": "error", 1044 | 1045 | "unicorn/throw-new-error": "error", 1046 | }; 1047 | 1048 | const importRules = { 1049 | ...importPlugin.flatConfigs.recommended.rules, 1050 | 1051 | // From recommended 1052 | // "import/export": "error", 1053 | 1054 | // No need 1055 | // "import/no-deprecated": "off", 1056 | 1057 | // No need, we have `unicorn/require-module-specifiers` 1058 | // "import/no-empty-named-blocks": "off", 1059 | 1060 | "import/no-extraneous-dependencies": "error", 1061 | 1062 | "import/no-mutable-exports": "error", 1063 | 1064 | "import/no-named-as-default": "error", 1065 | 1066 | "import/no-named-as-default-member": "error", 1067 | 1068 | // No need 1069 | // "import/no-unused-modules": "error", 1070 | 1071 | "import/no-amd": "error", 1072 | 1073 | // No need 1074 | // "import/no-commonjs": "off", 1075 | 1076 | // No need 1077 | // "import/no-import-module-exports": "off", 1078 | 1079 | // No need 1080 | // "import/no-nodejs-modules": "off", 1081 | 1082 | // No need 1083 | // "import/unambiguous": "off", 1084 | 1085 | // From recommended 1086 | // "import/default": "error", 1087 | 1088 | // No need 1089 | // We have `n/prefer-node-protocol` rule 1090 | // "enforce-node-protocol-usage": "off", 1091 | 1092 | // No need 1093 | // "import/enforce-node-protocol-usage": "off", 1094 | 1095 | // From recommended 1096 | // "import/named": "error", 1097 | 1098 | // From recommended 1099 | // "import/namespace": "error", 1100 | 1101 | "import/no-absolute-path": "error", 1102 | 1103 | // No need 1104 | // "import/no-cycle": "off", 1105 | 1106 | // No need 1107 | // "import/no-dynamic-require": "off", 1108 | 1109 | // No need 1110 | // "import/no-internal-modules": "off", 1111 | 1112 | // No need 1113 | // "import/no-relative-packages": "off", 1114 | 1115 | // No need 1116 | // "import/no-relative-parent-imports": "off", 1117 | 1118 | // No need 1119 | // "import/no-restricted-paths": "off", 1120 | 1121 | "import/no-self-import": "error", 1122 | 1123 | // From recommended 1124 | "import/no-unresolved": [ 1125 | "error", 1126 | { ignore: ["^eslint/config$", "^typescript-eslint$"], commonjs: true }, 1127 | ], 1128 | 1129 | // No need 1130 | // "import/no-useless-path-segments": "off", 1131 | 1132 | // No need 1133 | // "import/no-webpack-loader-syntax": "off", 1134 | 1135 | "import/consistent-type-specifier-style": ["error", "prefer-inline"], 1136 | 1137 | // No need 1138 | // "import/dynamic-import-chunkname": "off", 1139 | 1140 | // No need 1141 | // "import/exports-last": "off", 1142 | 1143 | // Not here 1144 | // "import/extensions": ["error", "always", { ignorePackages: true }], 1145 | 1146 | "import/first": "error", 1147 | 1148 | // No need 1149 | // "import/group-exports": "off", 1150 | 1151 | // No need 1152 | // "import/max-dependencies": "off", 1153 | 1154 | // No need 1155 | // We have `@stylistic/padding-line-between-statements` 1156 | // "import/newline-after-import": "off", 1157 | 1158 | // No need 1159 | // "import/no-anonymous-default-export": "off", 1160 | 1161 | // No need 1162 | // "import/no-default-export": "off", 1163 | 1164 | // No need 1165 | // We have `no-duplicate-imports` rule 1166 | "import/no-duplicates": "off", 1167 | 1168 | // No need 1169 | // "import/no-named-default": "off", 1170 | 1171 | // No need 1172 | // "import/no-named-export": "off", 1173 | 1174 | // No need 1175 | // "import/no-namespace": "off", 1176 | 1177 | // No need 1178 | // "import/no-unassigned-import": "off", 1179 | 1180 | "import/order": [ 1181 | "error", 1182 | { 1183 | named: { 1184 | enabled: true, 1185 | import: true, 1186 | export: true, 1187 | require: true, 1188 | cjsExports: true, 1189 | types: "mixed", 1190 | }, 1191 | alphabetize: { order: "asc" }, 1192 | }, 1193 | ], 1194 | 1195 | // No need 1196 | // "import/prefer-default-export": "off", 1197 | }; 1198 | 1199 | /** 1200 | * @param {number} esVersion es version 1201 | * @returns {Record} config 1202 | */ 1203 | function getConfig(esVersion) { 1204 | const extensions = isTypescriptInstalled() 1205 | ? allExtensions 1206 | : javascriptExtensions; 1207 | const config = { 1208 | ...javascriptConfig.configs.recommended, 1209 | name: `javascript/es${esVersion}`, 1210 | files: [`**/*.{${extensions.map((item) => item.slice(1)).join(",")}}`], 1211 | ignores: ["**/*.d.ts"], 1212 | settings: { 1213 | "import/extensions": extensions, 1214 | "import/ignore": [ 1215 | "eslint-plugin-.*", 1216 | "\\.(coffee|scss|css|less|hbs|svg|md|jpg|jpeg|png|gif|webp|avif)$", 1217 | ], 1218 | "import/resolver": { 1219 | node: { 1220 | extensions: [...extensions], 1221 | }, 1222 | }, 1223 | }, 1224 | plugins: { 1225 | unicorn: unicornPlugin, 1226 | import: importPlugin, 1227 | }, 1228 | languageOptions: { 1229 | ecmaVersion: esVersion, 1230 | globals: { 1231 | ...globals[`es${esVersion}`], 1232 | }, 1233 | }, 1234 | linterOptions: { 1235 | reportUnusedDisableDirectives: true, 1236 | reportUnusedInlineConfigs: "error", 1237 | }, 1238 | rules: { 1239 | ...javascriptConfig.configs.recommended.rules, 1240 | ...possibleProblems, 1241 | ...suggestions, 1242 | ...layoutAndFormatting, 1243 | ...unicornRules, 1244 | ...importRules, 1245 | }, 1246 | }; 1247 | 1248 | if (esVersion === 5) { 1249 | config.rules["arrow-body-style"] = "off"; 1250 | config.rules["object-shorthand"] = "off"; 1251 | config.rules["no-var"] = "off"; 1252 | config.rules["prefer-arrow-callback"] = "off"; 1253 | config.rules["prefer-const"] = "off"; 1254 | config.rules["prefer-destructuring"] = "off"; 1255 | config.rules["prefer-exponentiation-operator"] = "off"; 1256 | config.rules["prefer-object-spread"] = "off"; 1257 | config.rules["prefer-rest-params"] = "off"; 1258 | config.rules["prefer-spread"] = "off"; 1259 | config.rules["prefer-template"] = "off"; 1260 | config.rules["no-template-curly-in-string"] = "off"; 1261 | config.rules["unicorn/prefer-spread"] = "off"; 1262 | config.rules["unicorn/prefer-includes"] = "off"; 1263 | config.rules["unicorn/no-array-for-each"] = "off"; 1264 | } 1265 | 1266 | if (esVersion < 2018) { 1267 | config.rules["prefer-object-spread"] = "off"; 1268 | } 1269 | 1270 | if (esVersion < 2019) { 1271 | config.rules["unicorn/prefer-object-from-entries"] = "off"; 1272 | config.rules["unicorn/prefer-array-flat"] = "off"; 1273 | config.rules["unicorn/prefer-array-flat-map"] = "off"; 1274 | config.rules["unicorn/prefer-string-trim-start-end"] = "off"; 1275 | config.rules["unicorn/prefer-optional-catch-binding"] = "off"; 1276 | } 1277 | 1278 | if (esVersion < 2020) { 1279 | config.rules["unicorn/prefer-bigint-literals"] = "off"; 1280 | config.rules["unicorn/prefer-global-this"] = "off"; 1281 | config.rules["unicorn/prefer-logical-operator-over-ternary"] = "off"; 1282 | } 1283 | 1284 | if (esVersion < 2021) { 1285 | config.rules["logical-assignment-operators"] = "off"; 1286 | config.rules["unicorn/prefer-string-replace-all"] = "off"; 1287 | config.rules["unicorn/prefer-event-target"] = "off"; 1288 | } 1289 | 1290 | if (esVersion < 2022) { 1291 | config.rules["prefer-object-has-own"] = "off"; 1292 | config.rules["unicorn/prefer-structured-clone"] = "off"; 1293 | config.rules["unicorn/prefer-top-level-await"] = "off"; 1294 | config.rules["unicorn/prefer-class-fields"] = "off"; 1295 | } 1296 | 1297 | if (esVersion < 2023) { 1298 | config.rules["unicorn/no-array-sort"] = "off"; 1299 | } 1300 | 1301 | return config; 1302 | } 1303 | 1304 | const configs = {}; 1305 | const esVersions = Array.from({ length: 11 }, (_x, i) => 15 + i); 1306 | 1307 | for (const [i, esVersion] of esVersions.entries()) { 1308 | const year = 2000 + esVersion; 1309 | const config = getConfig(year); 1310 | 1311 | configs[`javascript/es${year}`] = config; 1312 | 1313 | if (i === esVersions.length - 1) { 1314 | configs["javascript/recommended"] = config; 1315 | } 1316 | } 1317 | 1318 | configs["javascript/es5"] = getConfig(5); 1319 | 1320 | export default configs; 1321 | --------------------------------------------------------------------------------