├── .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 | [](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 |
--------------------------------------------------------------------------------