eslint-config-webpack 4.9.4 → 4.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -690,6 +690,8 @@ const unicornRules = {
690
690
  // No need
691
691
  // "unicorn/consistent-function-scoping": "off",
692
692
 
693
+ "unicorn/consistent-template-literal-escape": "error",
694
+
693
695
  // No need
694
696
  // "unicorn/custom-error-definition": "off",
695
697
 
@@ -706,7 +708,7 @@ const unicornRules = {
706
708
  // No need
707
709
  // "unicorn/explicit-length-check": "off",
708
710
 
709
- // TODO
711
+ // TODO enable in future?
710
712
  "unicorn/filename-case": [
711
713
  "off",
712
714
  {
@@ -834,7 +836,7 @@ const unicornRules = {
834
836
  // No need
835
837
  // "unicorn/no-this-assignment": "off",
836
838
 
837
- // TODO - enable in future?
839
+ // TODO enable in future? Need to check performance
838
840
  // "unicorn/no-typeof-undefined": "off",
839
841
 
840
842
  "unicorn/no-unnecessary-array-flat-depth": "error",
@@ -862,6 +864,8 @@ const unicornRules = {
862
864
 
863
865
  "unicorn/no-useless-fallback-in-spread": "error",
864
866
 
867
+ "unicorn/no-useless-iterator-to-array": "error",
868
+
865
869
  "unicorn/no-useless-length-check": "error",
866
870
 
867
871
  "unicorn/no-useless-promise-resolve-reject": "error",
@@ -993,6 +997,9 @@ const unicornRules = {
993
997
  // No need
994
998
  // "unicorn/prefer-set-size": "off",
995
999
 
1000
+ // TODO enable in the next major release
1001
+ // "unicorn/prefer-simple-condition-first": "error",
1002
+
996
1003
  // No need
997
1004
  // "unicorn/prefer-single-call": "off",
998
1005
 
@@ -1045,6 +1052,10 @@ const unicornRules = {
1045
1052
  // No need
1046
1053
  // "unicorn/switch-case-braces": "off",
1047
1054
 
1055
+ // TODO maybe?
1056
+ // No need
1057
+ // "unicorn/switch-case-break-position": "off",
1058
+
1048
1059
  // No need
1049
1060
  // "unicorn/template-indent": "off",
1050
1061
 
@@ -1230,7 +1241,7 @@ const baseConfig = {
1230
1241
  import: importPlugin,
1231
1242
  },
1232
1243
  linterOptions: {
1233
- reportUnusedDisableDirectives: true,
1244
+ reportUnusedDisableDirectives: "error",
1234
1245
  reportUnusedInlineConfigs: "error",
1235
1246
  },
1236
1247
  rules: {
@@ -12,6 +12,7 @@ async function getMarkdownRecommendedConfig() {
12
12
  files: ["**/*.md"],
13
13
  processor: "markdown/markdown",
14
14
  plugins: {
15
+ // @ts-expect-error bad types
15
16
  markdown: markdownPlugin,
16
17
  },
17
18
  },
package/configs/node.js CHANGED
@@ -73,8 +73,7 @@ const commonRules = {
73
73
  // No need
74
74
  // "n/no-top-level-await": "error",
75
75
 
76
- // From recommended
77
- // "n/no-unpublished-bin": "error",
76
+ "n/no-unpublished-bin": "error",
78
77
 
79
78
  // From recommended
80
79
  // "n/no-unpublished-import": "error",
@@ -95,8 +94,53 @@ const commonRules = {
95
94
 
96
95
  // "n/no-unsupported-features/es-syntax": "error",
97
96
 
98
- // From recommended
99
- // "n/no-unsupported-features/node-builtins": "error",
97
+ // From recommended (override to ignore test module)
98
+ "n/no-unsupported-features/node-builtins": [
99
+ "error",
100
+ {
101
+ ignores: [
102
+ "test",
103
+ "test.after",
104
+ "test.afterEach",
105
+ "test.assert",
106
+ "test.assert.register",
107
+ "test.before",
108
+ "test.beforeEach",
109
+ "test.describe",
110
+ "test.describe.only",
111
+ "test.describe.skip",
112
+ "test.describe.todo",
113
+ "test.it",
114
+ "test.it.only",
115
+ "test.it.skip",
116
+ "test.it.todo",
117
+ "test.mock",
118
+ "test.mock.fn",
119
+ "test.mock.getter",
120
+ "test.mock.method",
121
+ "test.mock.module",
122
+ "test.mock.reset",
123
+ "test.mock.restoreAll",
124
+ "test.mock.setter",
125
+ "test.mock.timers",
126
+ "test.mock.timers.enable",
127
+ "test.mock.timers.reset",
128
+ "test.mock.timers.tick",
129
+ "test.only",
130
+ "test.run",
131
+ "test.snapshot",
132
+ "test.snapshot.setDefaultSnapshotSerializers",
133
+ "test.snapshot.setResolveSnapshotPath",
134
+ "test.skip",
135
+ "test.suite",
136
+ "test.test",
137
+ "test.test.only",
138
+ "test.test.skip",
139
+ "test.test.todo",
140
+ "test.todo",
141
+ ],
142
+ },
143
+ ],
100
144
 
101
145
  "n/prefer-global/buffer": ["error", "always"],
102
146
 
@@ -0,0 +1,83 @@
1
+ import semver from "semver";
2
+
3
+ /**
4
+ * @typedef {2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | 2023 | 2024 | 2025} EsVersion
5
+ */
6
+
7
+ /**
8
+ * Ordered table from Node version ranges to the highest ES version every
9
+ * release in the range fully supports.
10
+ *
11
+ * The table respects minor versions where they shift the supported feature
12
+ * set, e.g. async/await landed in Node 7.6 (ES2017), and `Object.hasOwn`
13
+ * landed in Node 16.11 (ES2022) — the last ES2022 feature to ship on the
14
+ * Node 16 line.
15
+ *
16
+ * Sources: https://node.green/ and the Node.js/V8 release notes. Entries
17
+ * are sorted newest-to-oldest; the first matching entry wins.
18
+ * @type {ReadonlyArray<{ range: string, esVersion: EsVersion }>}
19
+ */
20
+ const ranges = [
21
+ // ES2025: Node 22.0 ships all ES2025 syntax — import attributes
22
+ // (`with { type: "json" }`), the RegExp `/v` flag, and duplicate named
23
+ // capturing groups. Stdlib gaps (e.g. Promise.try lands at 22.5) are
24
+ // caught by eslint-plugin-n's `no-unsupported-features/node-builtins`,
25
+ // so we don't need a separate ES2024 row here.
26
+ { range: ">=22.0.0", esVersion: 2025 },
27
+ // ES2023: Array-by-copy (toSorted/toReversed/toSpliced/with), findLast/findLastIndex.
28
+ { range: ">=20.0.0", esVersion: 2023 },
29
+ // ES2022: Object.hasOwn is the last ES2022 feature to land on the Node 16 line.
30
+ // Earlier 16.x has top-level await (>=14.8), `.at()` (>=16.6) and Error cause (>=16.9)
31
+ // but not Object.hasOwn, so it can't safely claim full ES2022.
32
+ { range: ">=16.11.0", esVersion: 2022 },
33
+ // ES2021: logical assignment operators, String#replaceAll, Promise.any, WeakRef.
34
+ { range: ">=15.0.0", esVersion: 2021 },
35
+ // ES2020: optional chaining, nullish coalescing, BigInt, Promise.allSettled, globalThis.
36
+ { range: ">=14.0.0", esVersion: 2020 },
37
+ // ES2019: Object.fromEntries, Array#flat/flatMap, String#trimStart/trimEnd.
38
+ { range: ">=12.0.0", esVersion: 2019 },
39
+ // ES2018: object rest/spread, async iteration, RegExp lookbehind, Promise#finally.
40
+ { range: ">=10.0.0", esVersion: 2018 },
41
+ // ES2017: async/await is the gating feature — landed in 7.6, not 7.0.
42
+ { range: ">=7.6.0", esVersion: 2017 },
43
+ // ES2016: ** exponentiation operator and Array#includes (both require >=7.0).
44
+ { range: ">=7.0.0", esVersion: 2016 },
45
+ ];
46
+
47
+ /**
48
+ * Resolves the highest ES version every Node release matched by
49
+ * `nodeRange` fully supports.
50
+ *
51
+ * Returns `undefined` when the lower bound is older than our oldest entry
52
+ * (Node 7.0), so callers can apply project-specific fallbacks — for example
53
+ * Node 6, which has `Array#includes` (from 6.5) but never gained the `**`
54
+ * operator and so doesn't map cleanly to a single ES year.
55
+ * @param {string} nodeRange a semver range, typically `package.json#engines.node`
56
+ * @returns {EsVersion | undefined} matching ES version, or `undefined`
57
+ */
58
+ function getEsVersionFromNode(nodeRange) {
59
+ // `semver.minVersion` throws on unparseable input. Guard against a
60
+ // malformed `engines.node` so loading the ESLint config never crashes
61
+ // the consuming project — we just fall back to the caller's default.
62
+ let minVersion;
63
+ try {
64
+ minVersion = semver.minVersion(nodeRange);
65
+ // eslint-disable-next-line unicorn/prefer-optional-catch-binding
66
+ } catch (_err) {
67
+ return undefined;
68
+ }
69
+
70
+ if (!minVersion) {
71
+ return undefined;
72
+ }
73
+
74
+ for (const { range, esVersion } of ranges) {
75
+ if (semver.satisfies(minVersion, range)) {
76
+ return esVersion;
77
+ }
78
+ }
79
+
80
+ return undefined;
81
+ }
82
+
83
+ export default getEsVersionFromNode;
package/configs.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { globalIgnores } from "eslint/config";
2
2
  import semver from "semver";
3
3
  import configs from "./configs/index.js";
4
+ import getEsVersionFromNode from "./configs/utils/get-es-version-from-node.js";
4
5
  import getJsonFile from "./configs/utils/get-json-file.js";
5
6
  import ignorePaths from "./ignore-paths.js";
6
7
 
@@ -15,73 +16,56 @@ const isModule =
15
16
  * @returns {import("eslint").Linter.Config} javascript configuration
16
17
  */
17
18
  function getJavascriptConfig() {
18
- if (packageJson.engines && packageJson.engines.node) {
19
- const minVersion = semver.minVersion(packageJson.engines.node);
20
- const minMajorVersion = minVersion ? minVersion.major : undefined;
21
-
22
- // https://node.green/
23
- // https://github.com/microsoft/TypeScript/wiki/Node-Target-Mapping
24
- switch (minMajorVersion) {
25
- case 6: {
26
- const config = {
27
- ...configs["javascript/es2016"],
28
- rules: { ...configs["javascript/es2016"].rules },
29
- };
30
-
31
- config.rules["prefer-exponentiation-operator"] = "off";
32
-
33
- return config;
34
- }
35
- case 7:
36
- return configs["javascript/es2016"];
37
- case 8:
38
- case 9:
39
- return configs["javascript/es2017"];
40
- case 10:
41
- case 11:
42
- return configs["javascript/es2018"];
43
- case 12:
44
- case 13: {
45
- /** @type {import("eslint").Linter.Config["languageOptions"]} */
46
- const original = configs["javascript/es2019"].languageOptions;
47
- /** @type {import("eslint").Linter.Config["languageOptions"]} */
48
- const languageOptions = {
49
- ...original,
50
- globals: {
51
- // @ts-expect-error always exist
52
- ...original.globals,
53
- Promise: false,
54
- BigInt: false,
55
- },
56
- };
57
-
58
- return { ...configs["javascript/es2019"], languageOptions };
59
- }
60
- case 14:
61
- return configs["javascript/es2020"];
62
-
63
- case 15:
64
- return configs["javascript/es2021"];
65
- case 16:
66
- case 17:
67
- case 18:
68
- case 19:
69
- return configs["javascript/es2022"];
70
- case 20:
71
- case 21:
72
- return configs["javascript/es2023"];
73
- case 22:
74
- case 23:
75
- return configs["javascript/es2024"];
76
- case 24:
77
- case 25:
78
- return configs["javascript/es2025"];
79
- default:
80
- return configs["javascript/recommended"];
81
- }
19
+ if (!packageJson.engines || !packageJson.engines.node) {
20
+ return configs["javascript/recommended"];
82
21
  }
83
22
 
84
- return configs["javascript/recommended"];
23
+ const nodeRange = packageJson.engines.node;
24
+ const minVersion = semver.minVersion(nodeRange);
25
+
26
+ // Node 6 has Array#includes (from 6.5) but never gained the `**` operator,
27
+ // so it doesn't map cleanly to a single ES year — handle it inline.
28
+ // https://node.green/
29
+ if (minVersion && minVersion.major === 6) {
30
+ const base = configs["javascript/es2016"];
31
+ return {
32
+ ...base,
33
+ rules: {
34
+ ...base.rules,
35
+ "prefer-exponentiation-operator": "off",
36
+ },
37
+ };
38
+ }
39
+
40
+ const esVersion = getEsVersionFromNode(nodeRange);
41
+
42
+ if (esVersion === undefined) {
43
+ return configs["javascript/recommended"];
44
+ }
45
+
46
+ const config = configs[`javascript/es${esVersion}`];
47
+
48
+ // The `globals.es2019` set from the `globals` package doesn't declare
49
+ // `Promise` or `BigInt`, but both are available at runtime on Node 12+.
50
+ // Re-add them as readonly globals so `no-undef` doesn't flag usage.
51
+ if (esVersion === 2019) {
52
+ /** @type {import("eslint").Linter.Config["languageOptions"]} */
53
+ const original = config.languageOptions;
54
+ /** @type {import("eslint").Linter.Config["languageOptions"]} */
55
+ const languageOptions = {
56
+ ...original,
57
+ globals: {
58
+ // @ts-expect-error always exist
59
+ ...original.globals,
60
+ Promise: false,
61
+ BigInt: false,
62
+ },
63
+ };
64
+
65
+ return { ...config, languageOptions };
66
+ }
67
+
68
+ return config;
85
69
  }
86
70
 
87
71
  const javascriptConfig = getJavascriptConfig();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-config-webpack",
3
- "version": "4.9.4",
3
+ "version": "4.9.6",
4
4
  "description": "Provides Webpack's eslint rules as an extensible shared config",
5
5
  "keywords": [
6
6
  "eslint",
@@ -40,36 +40,36 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@eslint/js": "^9.39.2",
43
- "@eslint/markdown": "^7.5.1",
43
+ "@eslint/markdown": "^8.0.1",
44
44
  "@stylistic/eslint-plugin": "^5.10.0",
45
45
  "detect-indent": "^7.0.2",
46
46
  "eslint-config-prettier": "^10.1.8",
47
47
  "eslint-plugin-import": "^2.32.0",
48
- "eslint-plugin-jest": "^29.15.0",
49
- "eslint-plugin-jsdoc": "^62.8.0",
50
- "eslint-plugin-n": "^17.24.0",
48
+ "eslint-plugin-jest": "^29.15.2",
49
+ "eslint-plugin-jsdoc": "^62.9.0",
50
+ "eslint-plugin-n": "^18.0.1",
51
51
  "eslint-plugin-prettier": "^5.5.5",
52
52
  "eslint-plugin-react": "^7.37.5",
53
- "eslint-plugin-react-hooks": "^7.0.1",
54
- "eslint-plugin-unicorn": "^63.0.0",
55
- "globals": "^17.4.0",
53
+ "eslint-plugin-react-hooks": "^7.1.1",
54
+ "eslint-plugin-unicorn": "^64.0.0",
55
+ "globals": "^17.6.0",
56
56
  "jsonc-eslint-parser": "^3.1.0",
57
- "semver": "^7.7.4",
57
+ "semver": "^7.8.0",
58
58
  "sort-package-json": "^3.6.0",
59
- "typescript-eslint": "^8.57.2"
59
+ "typescript-eslint": "^8.59.3"
60
60
  },
61
61
  "devDependencies": {
62
- "@changesets/cli": "^2.30.0",
62
+ "@changesets/cli": "^2.31.0",
63
63
  "@changesets/get-github-info": "^0.8.0",
64
64
  "@types/semver": "^7.7.1",
65
65
  "eslint": "^9.39.2",
66
66
  "eslint-find-rules": "^5.0.0",
67
- "jest": "^30.3.0",
68
- "prettier": "^3.8.1",
69
- "react": "^19.2.3",
70
- "react-dom": "^19.2.3",
71
- "type-fest": "^5.5.0",
72
- "typescript": "^5.9.3"
67
+ "jest": "^30.4.2",
68
+ "prettier": "^3.8.3",
69
+ "react": "^19.2.6",
70
+ "react-dom": "^19.2.6",
71
+ "type-fest": "^5.6.0",
72
+ "typescript": "^6.0.3"
73
73
  },
74
74
  "peerDependencies": {
75
75
  "eslint": ">= 9.28.0",