eslint 9.0.0-beta.2 → 9.0.0

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.
Files changed (38) hide show
  1. package/README.md +8 -12
  2. package/bin/eslint.js +14 -1
  3. package/lib/cli.js +30 -11
  4. package/lib/config/flat-config-schema.js +1 -2
  5. package/lib/eslint/eslint-helpers.js +5 -1
  6. package/lib/eslint/eslint.js +16 -1
  7. package/lib/linter/code-path-analysis/code-path-analyzer.js +0 -1
  8. package/lib/linter/index.js +1 -3
  9. package/lib/linter/linter.js +184 -40
  10. package/lib/linter/timing.js +16 -8
  11. package/lib/options.js +25 -1
  12. package/lib/rule-tester/index.js +3 -1
  13. package/lib/rule-tester/rule-tester.js +18 -2
  14. package/lib/rules/camelcase.js +3 -5
  15. package/lib/rules/constructor-super.js +98 -99
  16. package/lib/rules/no-fallthrough.js +41 -16
  17. package/lib/rules/no-lone-blocks.js +1 -1
  18. package/lib/rules/no-this-before-super.js +28 -9
  19. package/lib/rules/no-unused-vars.js +179 -29
  20. package/lib/rules/no-useless-return.js +7 -2
  21. package/lib/rules/use-isnan.js +2 -2
  22. package/lib/rules/utils/lazy-loading-rule-map.js +1 -1
  23. package/lib/rules/utils/unicode/index.js +9 -4
  24. package/lib/shared/runtime-info.js +1 -0
  25. package/lib/shared/stats.js +30 -0
  26. package/lib/shared/types.js +34 -0
  27. package/lib/source-code/index.js +3 -1
  28. package/lib/source-code/source-code.js +165 -1
  29. package/lib/source-code/token-store/backward-token-cursor.js +3 -3
  30. package/lib/source-code/token-store/cursors.js +4 -2
  31. package/lib/source-code/token-store/forward-token-comment-cursor.js +3 -3
  32. package/lib/source-code/token-store/forward-token-cursor.js +3 -3
  33. package/messages/plugin-conflict.js +1 -1
  34. package/messages/plugin-invalid.js +1 -1
  35. package/messages/plugin-missing.js +1 -1
  36. package/package.json +12 -8
  37. package/lib/cli-engine/xml-escape.js +0 -34
  38. package/lib/shared/deprecation-warnings.js +0 -58
@@ -18,8 +18,12 @@ const
18
18
  directivesPattern
19
19
  } = require("../shared/directives"),
20
20
 
21
- /* eslint-disable-next-line n/no-restricted-require -- Too messy to figure out right now. */
21
+ /* eslint-disable n/no-restricted-require -- Should eventually be moved into SourceCode. */
22
+ CodePathAnalyzer = require("../linter/code-path-analysis/code-path-analyzer"),
23
+ createEmitter = require("../linter/safe-emitter"),
22
24
  ConfigCommentParser = require("../linter/config-comment-parser"),
25
+ /* eslint-enable n/no-restricted-require -- Should eventually be moved into SourceCode. */
26
+
23
27
  eslintScope = require("eslint-scope");
24
28
 
25
29
  //------------------------------------------------------------------------------
@@ -34,6 +38,16 @@ const
34
38
 
35
39
  const commentParser = new ConfigCommentParser();
36
40
 
41
+ const CODE_PATH_EVENTS = [
42
+ "onCodePathStart",
43
+ "onCodePathEnd",
44
+ "onCodePathSegmentStart",
45
+ "onCodePathSegmentEnd",
46
+ "onCodePathSegmentLoop",
47
+ "onUnreachableCodePathSegmentStart",
48
+ "onUnreachableCodePathSegmentEnd"
49
+ ];
50
+
37
51
  /**
38
52
  * Validates that the given AST has the required information.
39
53
  * @param {ASTNode} ast The Program node of the AST to check.
@@ -300,6 +314,65 @@ function markExportedVariables(globalScope, variables) {
300
314
 
301
315
  }
302
316
 
317
+ const STEP_KIND = {
318
+ visit: 1,
319
+ call: 2
320
+ };
321
+
322
+ /**
323
+ * A class to represent a step in the traversal process.
324
+ */
325
+ class TraversalStep {
326
+
327
+ /**
328
+ * The type of the step.
329
+ * @type {string}
330
+ */
331
+ type;
332
+
333
+ /**
334
+ * The kind of the step. Represents the same data as the `type` property
335
+ * but it's a number for performance.
336
+ * @type {number}
337
+ */
338
+ kind;
339
+
340
+ /**
341
+ * The target of the step.
342
+ * @type {ASTNode|string}
343
+ */
344
+ target;
345
+
346
+ /**
347
+ * The phase of the step.
348
+ * @type {number|undefined}
349
+ */
350
+ phase;
351
+
352
+ /**
353
+ * The arguments of the step.
354
+ * @type {Array<any>}
355
+ */
356
+ args;
357
+
358
+ /**
359
+ * Creates a new instance.
360
+ * @param {Object} options The options for the step.
361
+ * @param {string} options.type The type of the step.
362
+ * @param {ASTNode|string} options.target The target of the step.
363
+ * @param {number|undefined} [options.phase] The phase of the step.
364
+ * @param {Array<any>} options.args The arguments of the step.
365
+ * @returns {void}
366
+ */
367
+ constructor({ type, target, phase, args }) {
368
+ this.type = type;
369
+ this.kind = STEP_KIND[type];
370
+ this.target = target;
371
+ this.phase = phase;
372
+ this.args = args;
373
+ }
374
+ }
375
+
303
376
  //------------------------------------------------------------------------------
304
377
  // Public Interface
305
378
  //------------------------------------------------------------------------------
@@ -311,6 +384,12 @@ const caches = Symbol("caches");
311
384
  */
312
385
  class SourceCode extends TokenStore {
313
386
 
387
+ /**
388
+ * The cache of steps that were taken while traversing the source code.
389
+ * @type {Array<TraversalStep>}
390
+ */
391
+ #steps;
392
+
314
393
  /**
315
394
  * @param {string|Object} textOrConfig The source code text or config object.
316
395
  * @param {string} textOrConfig.text The source code text.
@@ -972,6 +1051,91 @@ class SourceCode extends TokenStore {
972
1051
 
973
1052
  }
974
1053
 
1054
+ /**
1055
+ * Traverse the source code and return the steps that were taken.
1056
+ * @returns {Array<TraversalStep>} The steps that were taken while traversing the source code.
1057
+ */
1058
+ traverse() {
1059
+
1060
+ // Because the AST doesn't mutate, we can cache the steps
1061
+ if (this.#steps) {
1062
+ return this.#steps;
1063
+ }
1064
+
1065
+ const steps = this.#steps = [];
1066
+
1067
+ /*
1068
+ * This logic works for any AST, not just ESTree. Because ESLint has allowed
1069
+ * custom parsers to return any AST, we need to ensure that the traversal
1070
+ * logic works for any AST.
1071
+ */
1072
+ const emitter = createEmitter();
1073
+ let analyzer = {
1074
+ enterNode(node) {
1075
+ steps.push(new TraversalStep({
1076
+ type: "visit",
1077
+ target: node,
1078
+ phase: 1,
1079
+ args: [node, node.parent]
1080
+ }));
1081
+ },
1082
+ leaveNode(node) {
1083
+ steps.push(new TraversalStep({
1084
+ type: "visit",
1085
+ target: node,
1086
+ phase: 2,
1087
+ args: [node, node.parent]
1088
+ }));
1089
+ },
1090
+ emitter
1091
+ };
1092
+
1093
+ /*
1094
+ * We do code path analysis for ESTree only. Code path analysis is not
1095
+ * necessary for other ASTs, and it's also not possible to do for other
1096
+ * ASTs because the necessary information is not available.
1097
+ *
1098
+ * Generally speaking, we can tell that the AST is an ESTree if it has a
1099
+ * Program node at the top level. This is not a perfect heuristic, but it
1100
+ * is good enough for now.
1101
+ */
1102
+ const isESTree = this.ast.type === "Program";
1103
+
1104
+ if (isESTree) {
1105
+ analyzer = new CodePathAnalyzer(analyzer);
1106
+
1107
+ CODE_PATH_EVENTS.forEach(eventName => {
1108
+ emitter.on(eventName, (...args) => {
1109
+ steps.push(new TraversalStep({
1110
+ type: "call",
1111
+ target: eventName,
1112
+ args
1113
+ }));
1114
+ });
1115
+ });
1116
+ }
1117
+
1118
+ /*
1119
+ * The actual AST traversal is done by the `Traverser` class. This class
1120
+ * is responsible for walking the AST and calling the appropriate methods
1121
+ * on the `analyzer` object, which is appropriate for the given AST.
1122
+ */
1123
+ Traverser.traverse(this.ast, {
1124
+ enter(node, parent) {
1125
+
1126
+ // save the parent node on a property for backwards compatibility
1127
+ node.parent = parent;
1128
+
1129
+ analyzer.enterNode(node);
1130
+ },
1131
+ leave(node) {
1132
+ analyzer.leaveNode(node);
1133
+ },
1134
+ visitorKeys: this.visitorKeys
1135
+ });
1136
+
1137
+ return steps;
1138
+ }
975
1139
  }
976
1140
 
977
1141
  module.exports = SourceCode;
@@ -9,7 +9,7 @@
9
9
  //------------------------------------------------------------------------------
10
10
 
11
11
  const Cursor = require("./cursor");
12
- const utils = require("./utils");
12
+ const { getLastIndex, getFirstIndex } = require("./utils");
13
13
 
14
14
  //------------------------------------------------------------------------------
15
15
  // Exports
@@ -31,8 +31,8 @@ module.exports = class BackwardTokenCursor extends Cursor {
31
31
  constructor(tokens, comments, indexMap, startLoc, endLoc) {
32
32
  super();
33
33
  this.tokens = tokens;
34
- this.index = utils.getLastIndex(tokens, indexMap, endLoc);
35
- this.indexEnd = utils.getFirstIndex(tokens, indexMap, startLoc);
34
+ this.index = getLastIndex(tokens, indexMap, endLoc);
35
+ this.indexEnd = getFirstIndex(tokens, indexMap, startLoc);
36
36
  }
37
37
 
38
38
  /** @inheritdoc */
@@ -86,5 +86,7 @@ class CursorFactory {
86
86
  // Exports
87
87
  //------------------------------------------------------------------------------
88
88
 
89
- exports.forward = new CursorFactory(ForwardTokenCursor, ForwardTokenCommentCursor);
90
- exports.backward = new CursorFactory(BackwardTokenCursor, BackwardTokenCommentCursor);
89
+ module.exports = {
90
+ forward: new CursorFactory(ForwardTokenCursor, ForwardTokenCommentCursor),
91
+ backward: new CursorFactory(BackwardTokenCursor, BackwardTokenCommentCursor)
92
+ };
@@ -9,7 +9,7 @@
9
9
  //------------------------------------------------------------------------------
10
10
 
11
11
  const Cursor = require("./cursor");
12
- const utils = require("./utils");
12
+ const { getFirstIndex, search } = require("./utils");
13
13
 
14
14
  //------------------------------------------------------------------------------
15
15
  // Exports
@@ -32,8 +32,8 @@ module.exports = class ForwardTokenCommentCursor extends Cursor {
32
32
  super();
33
33
  this.tokens = tokens;
34
34
  this.comments = comments;
35
- this.tokenIndex = utils.getFirstIndex(tokens, indexMap, startLoc);
36
- this.commentIndex = utils.search(comments, startLoc);
35
+ this.tokenIndex = getFirstIndex(tokens, indexMap, startLoc);
36
+ this.commentIndex = search(comments, startLoc);
37
37
  this.border = endLoc;
38
38
  }
39
39
 
@@ -9,7 +9,7 @@
9
9
  //------------------------------------------------------------------------------
10
10
 
11
11
  const Cursor = require("./cursor");
12
- const utils = require("./utils");
12
+ const { getFirstIndex, getLastIndex } = require("./utils");
13
13
 
14
14
  //------------------------------------------------------------------------------
15
15
  // Exports
@@ -31,8 +31,8 @@ module.exports = class ForwardTokenCursor extends Cursor {
31
31
  constructor(tokens, comments, indexMap, startLoc, endLoc) {
32
32
  super();
33
33
  this.tokens = tokens;
34
- this.index = utils.getFirstIndex(tokens, indexMap, startLoc);
35
- this.indexEnd = utils.getLastIndex(tokens, indexMap, endLoc);
34
+ this.index = getFirstIndex(tokens, indexMap, startLoc);
35
+ this.indexEnd = getLastIndex(tokens, indexMap, endLoc);
36
36
  }
37
37
 
38
38
  /** @inheritdoc */
@@ -15,7 +15,7 @@ module.exports = function(it) {
15
15
 
16
16
  Please remove the "plugins" setting from either config or remove either plugin installation.
17
17
 
18
- If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.
18
+ If you still can't figure out the problem, please see https://eslint.org/docs/latest/use/troubleshooting.
19
19
  `;
20
20
 
21
21
  return result;
@@ -11,6 +11,6 @@ module.exports = function(it) {
11
11
 
12
12
  "${configName}" was referenced from the config file in "${importerName}".
13
13
 
14
- If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.
14
+ If you still can't figure out the problem, please see https://eslint.org/docs/latest/use/troubleshooting.
15
15
  `.trimStart();
16
16
  };
@@ -14,6 +14,6 @@ It's likely that the plugin isn't installed correctly. Try reinstalling by runni
14
14
 
15
15
  The plugin "${pluginName}" was referenced from the config file in "${importerName}".
16
16
 
17
- If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team.
17
+ If you still can't figure out the problem, please see https://eslint.org/docs/latest/use/troubleshooting.
18
18
  `.trimStart();
19
19
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "9.0.0-beta.2",
3
+ "version": "9.0.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {
@@ -23,12 +23,14 @@
23
23
  "lint:docs:rule-examples": "node Makefile.js checkRuleExamples",
24
24
  "lint:fix": "node Makefile.js lint -- fix",
25
25
  "lint:fix:docs:js": "node Makefile.js lintDocsJS -- fix",
26
+ "lint:unused": "knip",
26
27
  "release:generate:alpha": "node Makefile.js generatePrerelease -- alpha",
27
28
  "release:generate:beta": "node Makefile.js generatePrerelease -- beta",
28
29
  "release:generate:latest": "node Makefile.js generateRelease",
29
30
  "release:generate:rc": "node Makefile.js generatePrerelease -- rc",
30
31
  "release:publish": "node Makefile.js publishRelease",
31
32
  "test": "node Makefile.js test",
33
+ "test:browser": "node Makefile.js wdio",
32
34
  "test:cli": "mocha",
33
35
  "test:fuzz": "node Makefile.js fuzz",
34
36
  "test:performance": "node Makefile.js perf"
@@ -48,7 +50,7 @@
48
50
  "node tools/fetch-docs-links.js",
49
51
  "git add docs/src/_data/further_reading_links.json"
50
52
  ],
51
- "docs/**/*.svg": "npx svgo -r --multipass"
53
+ "docs/**/*.svg": "npx -y svgo -r --multipass"
52
54
  },
53
55
  "files": [
54
56
  "LICENSE",
@@ -66,8 +68,8 @@
66
68
  "@eslint-community/eslint-utils": "^4.2.0",
67
69
  "@eslint-community/regexpp": "^4.6.1",
68
70
  "@eslint/eslintrc": "^3.0.2",
69
- "@eslint/js": "9.0.0-beta.2",
70
- "@humanwhocodes/config-array": "^0.11.14",
71
+ "@eslint/js": "9.0.0",
72
+ "@humanwhocodes/config-array": "^0.12.3",
71
73
  "@humanwhocodes/module-importer": "^1.0.1",
72
74
  "@nodelib/fs.walk": "^1.2.8",
73
75
  "ajv": "^6.12.4",
@@ -75,7 +77,7 @@
75
77
  "cross-spawn": "^7.0.2",
76
78
  "debug": "^4.3.2",
77
79
  "escape-string-regexp": "^4.0.0",
78
- "eslint-scope": "^8.0.0",
80
+ "eslint-scope": "^8.0.1",
79
81
  "eslint-visitor-keys": "^4.0.0",
80
82
  "espree": "^10.0.1",
81
83
  "esquery": "^1.4.2",
@@ -101,6 +103,8 @@
101
103
  "devDependencies": {
102
104
  "@babel/core": "^7.4.3",
103
105
  "@babel/preset-env": "^7.4.3",
106
+ "@types/estree": "^1.0.5",
107
+ "@types/node": "^20.11.5",
104
108
  "@wdio/browser-runner": "^8.14.6",
105
109
  "@wdio/cli": "^8.14.6",
106
110
  "@wdio/concise-reporter": "^8.14.0",
@@ -131,14 +135,14 @@
131
135
  "got": "^11.8.3",
132
136
  "gray-matter": "^4.0.3",
133
137
  "js-yaml": "^4.1.0",
138
+ "knip": "^5.8.0",
134
139
  "lint-staged": "^11.0.0",
135
140
  "load-perf": "^0.2.0",
136
141
  "markdown-it": "^12.2.0",
137
142
  "markdown-it-container": "^3.0.0",
138
- "markdownlint": "^0.33.0",
143
+ "markdownlint": "^0.34.0",
139
144
  "markdownlint-cli": "^0.39.0",
140
145
  "marked": "^4.0.8",
141
- "memfs": "^3.0.1",
142
146
  "metascraper": "^5.25.7",
143
147
  "metascraper-description": "^5.25.7",
144
148
  "metascraper-image": "^5.29.3",
@@ -157,8 +161,8 @@
157
161
  "semver": "^7.5.3",
158
162
  "shelljs": "^0.8.5",
159
163
  "sinon": "^11.0.0",
164
+ "typescript": "^5.3.3",
160
165
  "vite-plugin-commonjs": "^0.10.0",
161
- "webdriverio": "^8.14.6",
162
166
  "webpack": "^5.23.0",
163
167
  "webpack-cli": "^4.5.0",
164
168
  "yorkie": "^2.0.0"
@@ -1,34 +0,0 @@
1
- /**
2
- * @fileoverview XML character escaper
3
- * @author George Chung
4
- */
5
- "use strict";
6
-
7
- //------------------------------------------------------------------------------
8
- // Public Interface
9
- //------------------------------------------------------------------------------
10
-
11
- /**
12
- * Returns the escaped value for a character
13
- * @param {string} s string to examine
14
- * @returns {string} severity level
15
- * @private
16
- */
17
- module.exports = function(s) {
18
- return (`${s}`).replace(/[<>&"'\x00-\x1F\x7F\u0080-\uFFFF]/gu, c => { // eslint-disable-line no-control-regex -- Converting controls to entities
19
- switch (c) {
20
- case "<":
21
- return "&lt;";
22
- case ">":
23
- return "&gt;";
24
- case "&":
25
- return "&amp;";
26
- case "\"":
27
- return "&quot;";
28
- case "'":
29
- return "&apos;";
30
- default:
31
- return `&#${c.charCodeAt(0)};`;
32
- }
33
- });
34
- };
@@ -1,58 +0,0 @@
1
- /**
2
- * @fileoverview Provide the function that emits deprecation warnings.
3
- * @author Toru Nagashima <http://github.com/mysticatea>
4
- */
5
- "use strict";
6
-
7
- //------------------------------------------------------------------------------
8
- // Requirements
9
- //------------------------------------------------------------------------------
10
-
11
- const path = require("path");
12
-
13
- //------------------------------------------------------------------------------
14
- // Private
15
- //------------------------------------------------------------------------------
16
-
17
- // Definitions for deprecation warnings.
18
- const deprecationWarningMessages = {
19
- ESLINT_LEGACY_ECMAFEATURES:
20
- "The 'ecmaFeatures' config file property is deprecated and has no effect."
21
- };
22
-
23
- const sourceFileErrorCache = new Set();
24
-
25
- /**
26
- * Emits a deprecation warning containing a given filepath. A new deprecation warning is emitted
27
- * for each unique file path, but repeated invocations with the same file path have no effect.
28
- * No warnings are emitted if the `--no-deprecation` or `--no-warnings` Node runtime flags are active.
29
- * @param {string} source The name of the configuration source to report the warning for.
30
- * @param {string} errorCode The warning message to show.
31
- * @returns {void}
32
- */
33
- function emitDeprecationWarning(source, errorCode) {
34
- const cacheKey = JSON.stringify({ source, errorCode });
35
-
36
- if (sourceFileErrorCache.has(cacheKey)) {
37
- return;
38
- }
39
-
40
- sourceFileErrorCache.add(cacheKey);
41
-
42
- const rel = path.relative(process.cwd(), source);
43
- const message = deprecationWarningMessages[errorCode];
44
-
45
- process.emitWarning(
46
- `${message} (found in "${rel}")`,
47
- "DeprecationWarning",
48
- errorCode
49
- );
50
- }
51
-
52
- //------------------------------------------------------------------------------
53
- // Public Interface
54
- //------------------------------------------------------------------------------
55
-
56
- module.exports = {
57
- emitDeprecationWarning
58
- };