eslint 9.3.0 → 9.4.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.
package/lib/cli.js CHANGED
@@ -448,14 +448,14 @@ const cli = {
448
448
 
449
449
  if (options.inspectConfig) {
450
450
 
451
- log.info("You can also run this command directly using 'npx @eslint/config-inspector' in the same directory as your configuration file.");
451
+ log.info("You can also run this command directly using 'npx @eslint/config-inspector@latest' in the same directory as your configuration file.");
452
452
 
453
453
  try {
454
454
  const flatOptions = await translateOptions(options, "flat");
455
455
  const spawn = require("cross-spawn");
456
456
  const flags = await cli.calculateInspectConfigFlags(flatOptions.overrideConfigFile);
457
457
 
458
- spawn.sync("npx", ["@eslint/config-inspector", ...flags], { encoding: "utf8", stdio: "inherit" });
458
+ spawn.sync("npx", ["@eslint/config-inspector@latest", ...flags], { encoding: "utf8", stdio: "inherit" });
459
459
  } catch (error) {
460
460
  log.error(error);
461
461
  return 2;
@@ -9,7 +9,7 @@
9
9
  // Requirements
10
10
  //-----------------------------------------------------------------------------
11
11
 
12
- const { ConfigArray, ConfigArraySymbol } = require("@humanwhocodes/config-array");
12
+ const { ConfigArray, ConfigArraySymbol } = require("@eslint/config-array");
13
13
  const { flatConfigSchema } = require("./flat-config-schema");
14
14
  const { RuleValidator } = require("./rule-validator");
15
15
  const { defaultConfig } = require("./default-config");
@@ -335,15 +335,15 @@ async function globSearch({
335
335
 
336
336
  /*
337
337
  * We updated the unmatched patterns set only if the path
338
- * matches and the file isn't ignored. If the file is
339
- * ignored, that means there wasn't a match for the
338
+ * matches and the file has a config. If the file has no
339
+ * config, that means there wasn't a match for the
340
340
  * pattern so it should not be removed.
341
341
  *
342
- * Performance note: isFileIgnored() aggressively caches
342
+ * Performance note: `getConfig()` aggressively caches
343
343
  * results so there is no performance penalty for calling
344
- * it twice with the same argument.
344
+ * it multiple times with the same argument.
345
345
  */
346
- if (pathMatches && !configs.isFileIgnored(entry.path)) {
346
+ if (pathMatches && configs.getConfig(entry.path)) {
347
347
  unmatchedPatterns.delete(matcher.pattern);
348
348
  }
349
349
 
@@ -351,7 +351,7 @@ async function globSearch({
351
351
  }, false)
352
352
  : matchers.some(matcher => matcher.match(relativePath));
353
353
 
354
- return matchesPattern && !configs.isFileIgnored(entry.path);
354
+ return matchesPattern && configs.getConfig(entry.path) !== void 0;
355
355
  })
356
356
  },
357
357
  (error, entries) => {
@@ -545,7 +545,7 @@ async function findFiles({
545
545
  if (stat.isFile()) {
546
546
  results.push({
547
547
  filePath,
548
- ignored: configs.isFileIgnored(filePath)
548
+ ignored: !configs.getConfig(filePath)
549
549
  });
550
550
  }
551
551
 
@@ -509,7 +509,7 @@ function verifyText({
509
509
  * @returns {boolean} `true` if the linter should adopt the code block.
510
510
  */
511
511
  filterCodeBlock(blockFilename) {
512
- return configs.isExplicitMatch(blockFilename);
512
+ return configs.getConfig(blockFilename) !== void 0;
513
513
  }
514
514
  }
515
515
  );
@@ -881,15 +881,6 @@ class ESLint {
881
881
 
882
882
  const config = configs.getConfig(filePath);
883
883
 
884
- /*
885
- * Sometimes a file found through a glob pattern will
886
- * be ignored. In this case, `config` will be undefined
887
- * and we just silently ignore the file.
888
- */
889
- if (!config) {
890
- return void 0;
891
- }
892
-
893
884
  // Skip if there is cached result.
894
885
  if (lintResultCache) {
895
886
  const cachedResult =
@@ -28,7 +28,7 @@ const ajv = require("../shared/ajv")({ strictDefaults: true });
28
28
 
29
29
  const parserSymbol = Symbol.for("eslint.RuleTester.parser");
30
30
  const { SourceCode } = require("../source-code");
31
- const { ConfigArraySymbol } = require("@humanwhocodes/config-array");
31
+ const { ConfigArraySymbol } = require("@eslint/config-array");
32
32
  const { isSerializable } = require("../shared/serialization");
33
33
 
34
34
  //------------------------------------------------------------------------------
@@ -14,7 +14,7 @@ module.exports = {
14
14
  type: "suggestion",
15
15
 
16
16
  docs: {
17
- description: "Enforce the consistent use of either `function` declarations or expressions",
17
+ description: "Enforce the consistent use of either `function` declarations or expressions assigned to variables",
18
18
  recommended: false,
19
19
  url: "https://eslint.org/docs/latest/rules/func-style"
20
20
  },
@@ -100,7 +100,7 @@ module.exports = {
100
100
  stack.pop();
101
101
  },
102
102
 
103
- ThisExpression() {
103
+ "ThisExpression, Super"() {
104
104
  if (stack.length > 0) {
105
105
  stack[stack.length - 1] = true;
106
106
  }
@@ -113,9 +113,9 @@ module.exports = {
113
113
  };
114
114
 
115
115
  nodesToCheck["ArrowFunctionExpression:exit"] = function(node) {
116
- const hasThisExpr = stack.pop();
116
+ const hasThisOrSuperExpr = stack.pop();
117
117
 
118
- if (!hasThisExpr && node.parent.type === "VariableDeclarator") {
118
+ if (!hasThisOrSuperExpr && node.parent.type === "VariableDeclarator") {
119
119
  if (
120
120
  enforceDeclarations &&
121
121
  (typeof exportFunctionStyle === "undefined" || node.parent.parent.parent.type !== "ExportNamedDeclaration")
@@ -49,7 +49,7 @@ module.exports = {
49
49
  if (
50
50
  last.parent.type === "MethodDefinition" &&
51
51
  last.parent.kind === "constructor" &&
52
- (node.parent.parent === last || node.argument)
52
+ node.argument
53
53
  ) {
54
54
  context.report({
55
55
  node,
@@ -9,140 +9,16 @@
9
9
  // Helpers
10
10
  //------------------------------------------------------------------------------
11
11
 
12
- /**
13
- * Gets the containing loop node of a specified node.
14
- *
15
- * We don't need to check nested functions, so this ignores those.
16
- * `Scope.through` contains references of nested functions.
17
- * @param {ASTNode} node An AST node to get.
18
- * @returns {ASTNode|null} The containing loop node of the specified node, or
19
- * `null`.
20
- */
21
- function getContainingLoopNode(node) {
22
- for (let currentNode = node; currentNode.parent; currentNode = currentNode.parent) {
23
- const parent = currentNode.parent;
24
-
25
- switch (parent.type) {
26
- case "WhileStatement":
27
- case "DoWhileStatement":
28
- return parent;
29
-
30
- case "ForStatement":
31
-
32
- // `init` is outside of the loop.
33
- if (parent.init !== currentNode) {
34
- return parent;
35
- }
36
- break;
37
-
38
- case "ForInStatement":
39
- case "ForOfStatement":
40
-
41
- // `right` is outside of the loop.
42
- if (parent.right !== currentNode) {
43
- return parent;
44
- }
45
- break;
46
-
47
- case "ArrowFunctionExpression":
48
- case "FunctionExpression":
49
- case "FunctionDeclaration":
50
-
51
- // We don't need to check nested functions.
52
- return null;
53
-
54
- default:
55
- break;
56
- }
57
- }
58
-
59
- return null;
60
- }
61
12
 
62
13
  /**
63
- * Gets the containing loop node of a given node.
64
- * If the loop was nested, this returns the most outer loop.
65
- * @param {ASTNode} node A node to get. This is a loop node.
66
- * @param {ASTNode|null} excludedNode A node that the result node should not
67
- * include.
68
- * @returns {ASTNode} The most outer loop node.
14
+ * Identifies is a node is a FunctionExpression which is part of an IIFE
15
+ * @param {ASTNode} node Node to test
16
+ * @returns {boolean} True if it's an IIFE
69
17
  */
70
- function getTopLoopNode(node, excludedNode) {
71
- const border = excludedNode ? excludedNode.range[1] : 0;
72
- let retv = node;
73
- let containingLoopNode = node;
74
-
75
- while (containingLoopNode && containingLoopNode.range[0] >= border) {
76
- retv = containingLoopNode;
77
- containingLoopNode = getContainingLoopNode(containingLoopNode);
78
- }
79
-
80
- return retv;
18
+ function isIIFE(node) {
19
+ return (node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") && node.parent && node.parent.type === "CallExpression" && node.parent.callee === node;
81
20
  }
82
21
 
83
- /**
84
- * Checks whether a given reference which refers to an upper scope's variable is
85
- * safe or not.
86
- * @param {ASTNode} loopNode A containing loop node.
87
- * @param {eslint-scope.Reference} reference A reference to check.
88
- * @returns {boolean} `true` if the reference is safe or not.
89
- */
90
- function isSafe(loopNode, reference) {
91
- const variable = reference.resolved;
92
- const definition = variable && variable.defs[0];
93
- const declaration = definition && definition.parent;
94
- const kind = (declaration && declaration.type === "VariableDeclaration")
95
- ? declaration.kind
96
- : "";
97
-
98
- // Variables which are declared by `const` is safe.
99
- if (kind === "const") {
100
- return true;
101
- }
102
-
103
- /*
104
- * Variables which are declared by `let` in the loop is safe.
105
- * It's a different instance from the next loop step's.
106
- */
107
- if (kind === "let" &&
108
- declaration.range[0] > loopNode.range[0] &&
109
- declaration.range[1] < loopNode.range[1]
110
- ) {
111
- return true;
112
- }
113
-
114
- /*
115
- * WriteReferences which exist after this border are unsafe because those
116
- * can modify the variable.
117
- */
118
- const border = getTopLoopNode(
119
- loopNode,
120
- (kind === "let") ? declaration : null
121
- ).range[0];
122
-
123
- /**
124
- * Checks whether a given reference is safe or not.
125
- * The reference is every reference of the upper scope's variable we are
126
- * looking now.
127
- *
128
- * It's safe if the reference matches one of the following condition.
129
- * - is readonly.
130
- * - doesn't exist inside a local function and after the border.
131
- * @param {eslint-scope.Reference} upperRef A reference to check.
132
- * @returns {boolean} `true` if the reference is safe.
133
- */
134
- function isSafeReference(upperRef) {
135
- const id = upperRef.identifier;
136
-
137
- return (
138
- !upperRef.isWrite() ||
139
- variable.scope.variableScope === upperRef.from.variableScope &&
140
- id.range[0] < border
141
- );
142
- }
143
-
144
- return Boolean(variable) && variable.references.every(isSafeReference);
145
- }
146
22
 
147
23
  //------------------------------------------------------------------------------
148
24
  // Rule Definition
@@ -168,8 +44,147 @@ module.exports = {
168
44
 
169
45
  create(context) {
170
46
 
47
+ const SKIPPED_IIFE_NODES = new Set();
171
48
  const sourceCode = context.sourceCode;
172
49
 
50
+ /**
51
+ * Gets the containing loop node of a specified node.
52
+ *
53
+ * We don't need to check nested functions, so this ignores those, with the exception of IIFE.
54
+ * `Scope.through` contains references of nested functions.
55
+ * @param {ASTNode} node An AST node to get.
56
+ * @returns {ASTNode|null} The containing loop node of the specified node, or
57
+ * `null`.
58
+ */
59
+ function getContainingLoopNode(node) {
60
+ for (let currentNode = node; currentNode.parent; currentNode = currentNode.parent) {
61
+ const parent = currentNode.parent;
62
+
63
+ switch (parent.type) {
64
+ case "WhileStatement":
65
+ case "DoWhileStatement":
66
+ return parent;
67
+
68
+ case "ForStatement":
69
+
70
+ // `init` is outside of the loop.
71
+ if (parent.init !== currentNode) {
72
+ return parent;
73
+ }
74
+ break;
75
+
76
+ case "ForInStatement":
77
+ case "ForOfStatement":
78
+
79
+ // `right` is outside of the loop.
80
+ if (parent.right !== currentNode) {
81
+ return parent;
82
+ }
83
+ break;
84
+
85
+ case "ArrowFunctionExpression":
86
+ case "FunctionExpression":
87
+ case "FunctionDeclaration":
88
+
89
+ // We need to check nested functions only in case of IIFE.
90
+ if (SKIPPED_IIFE_NODES.has(parent)) {
91
+ break;
92
+ }
93
+
94
+ return null;
95
+ default:
96
+ break;
97
+ }
98
+ }
99
+
100
+ return null;
101
+ }
102
+
103
+ /**
104
+ * Gets the containing loop node of a given node.
105
+ * If the loop was nested, this returns the most outer loop.
106
+ * @param {ASTNode} node A node to get. This is a loop node.
107
+ * @param {ASTNode|null} excludedNode A node that the result node should not
108
+ * include.
109
+ * @returns {ASTNode} The most outer loop node.
110
+ */
111
+ function getTopLoopNode(node, excludedNode) {
112
+ const border = excludedNode ? excludedNode.range[1] : 0;
113
+ let retv = node;
114
+ let containingLoopNode = node;
115
+
116
+ while (containingLoopNode && containingLoopNode.range[0] >= border) {
117
+ retv = containingLoopNode;
118
+ containingLoopNode = getContainingLoopNode(containingLoopNode);
119
+ }
120
+
121
+ return retv;
122
+ }
123
+
124
+ /**
125
+ * Checks whether a given reference which refers to an upper scope's variable is
126
+ * safe or not.
127
+ * @param {ASTNode} loopNode A containing loop node.
128
+ * @param {eslint-scope.Reference} reference A reference to check.
129
+ * @returns {boolean} `true` if the reference is safe or not.
130
+ */
131
+ function isSafe(loopNode, reference) {
132
+ const variable = reference.resolved;
133
+ const definition = variable && variable.defs[0];
134
+ const declaration = definition && definition.parent;
135
+ const kind = (declaration && declaration.type === "VariableDeclaration")
136
+ ? declaration.kind
137
+ : "";
138
+
139
+ // Variables which are declared by `const` is safe.
140
+ if (kind === "const") {
141
+ return true;
142
+ }
143
+
144
+ /*
145
+ * Variables which are declared by `let` in the loop is safe.
146
+ * It's a different instance from the next loop step's.
147
+ */
148
+ if (kind === "let" &&
149
+ declaration.range[0] > loopNode.range[0] &&
150
+ declaration.range[1] < loopNode.range[1]
151
+ ) {
152
+ return true;
153
+ }
154
+
155
+ /*
156
+ * WriteReferences which exist after this border are unsafe because those
157
+ * can modify the variable.
158
+ */
159
+ const border = getTopLoopNode(
160
+ loopNode,
161
+ (kind === "let") ? declaration : null
162
+ ).range[0];
163
+
164
+ /**
165
+ * Checks whether a given reference is safe or not.
166
+ * The reference is every reference of the upper scope's variable we are
167
+ * looking now.
168
+ *
169
+ * It's safe if the reference matches one of the following condition.
170
+ * - is readonly.
171
+ * - doesn't exist inside a local function and after the border.
172
+ * @param {eslint-scope.Reference} upperRef A reference to check.
173
+ * @returns {boolean} `true` if the reference is safe.
174
+ */
175
+ function isSafeReference(upperRef) {
176
+ const id = upperRef.identifier;
177
+
178
+ return (
179
+ !upperRef.isWrite() ||
180
+ variable.scope.variableScope === upperRef.from.variableScope &&
181
+ id.range[0] < border
182
+ );
183
+ }
184
+
185
+ return Boolean(variable) && variable.references.every(isSafeReference);
186
+ }
187
+
173
188
  /**
174
189
  * Reports functions which match the following condition:
175
190
  *
@@ -186,6 +201,23 @@ module.exports = {
186
201
  }
187
202
 
188
203
  const references = sourceCode.getScope(node).through;
204
+
205
+ // Check if the function is not asynchronous or a generator function
206
+ if (!(node.async || node.generator)) {
207
+ if (isIIFE(node)) {
208
+
209
+ const isFunctionExpression = node.type === "FunctionExpression";
210
+
211
+ // Check if the function is referenced elsewhere in the code
212
+ const isFunctionReferenced = isFunctionExpression && node.id ? references.some(r => r.identifier.name === node.id.name) : false;
213
+
214
+ if (!isFunctionReferenced) {
215
+ SKIPPED_IIFE_NODES.add(node);
216
+ return;
217
+ }
218
+ }
219
+ }
220
+
189
221
  const unsafeRefs = references.filter(r => r.resolved && !isSafe(loopNode, r)).map(r => r.identifier.name);
190
222
 
191
223
  if (unsafeRefs.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "9.3.0",
3
+ "version": "9.4.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {
@@ -68,9 +68,9 @@
68
68
  "dependencies": {
69
69
  "@eslint-community/eslint-utils": "^4.2.0",
70
70
  "@eslint-community/regexpp": "^4.6.1",
71
+ "@eslint/config-array": "^0.15.1",
71
72
  "@eslint/eslintrc": "^3.1.0",
72
- "@eslint/js": "9.3.0",
73
- "@humanwhocodes/config-array": "^0.13.0",
73
+ "@eslint/js": "9.4.0",
74
74
  "@humanwhocodes/module-importer": "^1.0.1",
75
75
  "@humanwhocodes/retry": "^0.3.0",
76
76
  "@nodelib/fs.walk": "^1.2.8",