eslint 10.0.0-rc.0 → 10.0.0-rc.2

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/README.md CHANGED
@@ -35,6 +35,7 @@ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/J
35
35
  1. [Releases](#releases)
36
36
  1. [Security Policy](#security-policy)
37
37
  1. [Semantic Versioning Policy](#semantic-versioning-policy)
38
+ 1. [ESM Dependencies](#esm-dependencies)
38
39
  1. [License](#license)
39
40
  1. [Team](#team)
40
41
  1. [Sponsors](#sponsors)
@@ -203,6 +204,14 @@ ESLint follows [semantic versioning](https://semver.org). However, due to the na
203
204
 
204
205
  According to our policy, any minor update may report more linting errors than the previous release (ex: from a bug fix). As such, we recommend using the tilde (`~`) in `package.json` e.g. `"eslint": "~3.1.0"` to guarantee the results of your builds.
205
206
 
207
+ ## ESM Dependencies
208
+
209
+ Since ESLint is a CommonJS package, there are restrictions on which ESM-only packages can be used as dependencies.
210
+
211
+ Packages that are controlled by the ESLint team and have no external dependencies can be safely loaded synchronously using [`require(esm)`](https://nodejs.org/api/modules.html#loading-ecmascript-modules-using-require) and therefore used in any contexts.
212
+
213
+ For external packages, we don't use `require(esm)` because a package could add a top-level `await` and thus break ESLint. We can use an external ESM-only package only in case it is needed only in asynchronous code, in which case it can be loaded using dynamic `import()`.
214
+
206
215
  ## License
207
216
 
208
217
  MIT License
@@ -342,8 +351,8 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
342
351
  <h3>Platinum Sponsors</h3>
343
352
  <p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a></p><h3>Gold Sponsors</h3>
344
353
  <p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a> <a href="https://shopify.engineering/"><img src="https://avatars.githubusercontent.com/u/8085" alt="Shopify" height="96"></a></p><h3>Silver Sponsors</h3>
345
- <p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/e6d15e1/logo.png" alt="Vite" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/2d6c3b6/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301" alt="American Express" height="64"></a> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
346
- <p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://www.crawljobs.com/"><img src="https://images.opencollective.com/crawljobs-poland/fa43a17/logo.png" alt="CrawlJobs" height="32"></a> <a href="https://syntax.fm"><img src="https://github.com/syntaxfm.png" alt="Syntax" height="32"></a> <a href="https://www.n-ix.com/"><img src="https://images.opencollective.com/n-ix-ltd/575a7a5/logo.png" alt="N-iX Ltd" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="LambdaTest" height="32"></a></p>
354
+ <p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/d472863/logo.png" alt="Vite" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/2d6c3b6/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301" alt="American Express" height="64"></a> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
355
+ <p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://opensource.sap.com"><img src="https://avatars.githubusercontent.com/u/2531208" alt="SAP" height="32"></a> <a href="https://www.crawljobs.com/"><img src="https://images.opencollective.com/crawljobs-poland/fa43a17/logo.png" alt="CrawlJobs" height="32"></a> <a href="https://syntax.fm"><img src="https://github.com/syntaxfm.png" alt="Syntax" height="32"></a> <a href="https://www.n-ix.com/"><img src="https://images.opencollective.com/n-ix-ltd/575a7a5/logo.png" alt="N-iX Ltd" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="LambdaTest" height="32"></a></p>
347
356
  <h3>Technology Sponsors</h3>
348
357
  Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
349
358
  <p><a href="https://netlify.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/netlify-icon.svg" alt="Netlify" height="32"></a> <a href="https://algolia.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/algolia-icon.svg" alt="Algolia" height="32"></a> <a href="https://1password.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/1password-icon.svg" alt="1Password" height="32"></a></p>
@@ -657,14 +657,25 @@ function assertInvalidTestCase(
657
657
  /**
658
658
  * Gets the invocation location from the stack trace for later use.
659
659
  * @param {Function} relative The function before the invocation point.
660
- * @returns {string} The invocation location.
660
+ * @returns {{ sourceFile: string; sourceLine: number; sourceColumn: number; }} The invocation location.
661
661
  */
662
662
  function getInvocationLocation(relative = getInvocationLocation) {
663
663
  const dummyObject = {};
664
- Error.captureStackTrace(dummyObject, relative);
665
- const { stack } = dummyObject;
666
-
667
- return stack.split("\n")[1].replace(/.*?\(/u, "").replace(/\)$/u, "");
664
+ let location;
665
+ const { prepareStackTrace } = Error;
666
+ Error.prepareStackTrace = (_, [callSite]) => {
667
+ location = {
668
+ sourceFile:
669
+ callSite.getFileName() ??
670
+ `${callSite.getEvalOrigin()}, <anonymous>`,
671
+ sourceLine: callSite.getLineNumber() ?? 1,
672
+ sourceColumn: callSite.getColumnNumber() ?? 1,
673
+ };
674
+ };
675
+ Error.captureStackTrace(dummyObject, relative); // invoke Error.prepareStackTrace in Bun
676
+ void dummyObject.stack; // invoke Error.prepareStackTrace in Node.js
677
+ Error.prepareStackTrace = prepareStackTrace;
678
+ return location;
668
679
  }
669
680
 
670
681
  /**
@@ -677,14 +688,10 @@ function buildLazyTestLocationEstimator(invoker) {
677
688
  let testLocations = null;
678
689
  return key => {
679
690
  if (testLocations === null) {
680
- let [sourceFile, sourceLine = "1", sourceColumn = "1"] =
681
- invocationLocation
682
- .replace(/:\\/u, "\\\\") // Windows workaround for C:\\
683
- .split(":");
684
- sourceFile = sourceFile.replace(/\\\\/u, ":\\");
685
- sourceLine = Number(sourceLine);
686
- sourceColumn = Number(sourceColumn);
687
- testLocations = { root: invocationLocation };
691
+ const { sourceFile, sourceLine, sourceColumn } = invocationLocation;
692
+ testLocations = {
693
+ root: `${sourceFile}:${sourceLine}:${sourceColumn}`,
694
+ };
688
695
 
689
696
  if (existsSync(sourceFile)) {
690
697
  let content = readFileSync(sourceFile, "utf8")
@@ -700,10 +707,10 @@ function buildLazyTestLocationEstimator(invoker) {
700
707
 
701
708
  // Roots
702
709
  const validStartIndex = content.findIndex(line =>
703
- /\bvalid:/u.test(line),
710
+ /\bvalid\s*:/u.test(line),
704
711
  );
705
712
  const invalidStartIndex = content.findIndex(line =>
706
- /\binvalid:/u.test(line),
713
+ /\binvalid\s*:/u.test(line),
707
714
  );
708
715
 
709
716
  testLocations.valid = `${sourceFile}:${
@@ -346,32 +346,97 @@ module.exports = {
346
346
  }
347
347
 
348
348
  /**
349
- * Checks if a variable is inside the initializer of scopeVar.
349
+ * Finds the uppermost expression node that can evaluate to the given one.
350
350
  *
351
- * To avoid reporting at declarations such as `var a = function a() {};`.
352
- * But it should report `var a = function(a) {};` or `var a = function() { function a() {} };`.
353
- * @param {Object} variable The variable to check.
354
- * @param {Object} scopeVar The scope variable to look for.
355
- * @returns {boolean} Whether or not the variable is inside initializer of scopeVar.
351
+ * Examples:
352
+ * If given `a` in `a || foo`, it returns the `a || foo` node.
353
+ * If given `a` in `foo ? a : bar`, it returns the `foo ? a : bar` node.
354
+ * If given `a` in `foo ? bar : (baz && a)`, it returns the `foo ? bar : (baz && a)` node.
355
+ * If given `a` in `a ? foo : bar`, it returns the `a` node.
356
+ * If given `a` in `foo(a)`, it returns the `a` node.
357
+ * @param {ASTNode} expression The expression node to unwrap.
358
+ * @returns {ASTNode} The uppermost ancestor that can evaluate to the given node
359
+ * or the given node if there is no such ancestor.
356
360
  */
357
- function isOnInitializer(variable, scopeVar) {
358
- const outerScope = scopeVar.scope;
359
- const outerDef = scopeVar.defs[0];
360
- const outer = outerDef && outerDef.parent && outerDef.parent.range;
361
- const innerScope = variable.scope;
362
- const innerDef = variable.defs[0];
363
- const inner = innerDef && innerDef.name.range;
361
+ function unwrapExpression(expression) {
362
+ const { parent } = expression;
364
363
 
365
- return (
366
- outer &&
367
- inner &&
368
- outer[0] < inner[0] &&
369
- inner[1] < outer[1] &&
370
- ((innerDef.type === "FunctionName" &&
371
- innerDef.node.type === "FunctionExpression") ||
372
- innerDef.node.type === "ClassExpression") &&
373
- outerScope === innerScope.upper
374
- );
364
+ const shouldUnwrap =
365
+ parent.type === "LogicalExpression" ||
366
+ (parent.type === "ConditionalExpression" &&
367
+ parent.test !== expression);
368
+
369
+ return shouldUnwrap ? unwrapExpression(parent) : expression;
370
+ }
371
+
372
+ /**
373
+ * Checks if inner variable is the name of a function or class
374
+ * that is assigned to outer variable as its initializer.
375
+ *
376
+ * To avoid reporting at declarations such as:
377
+ * var a = function a() {};
378
+ * var A = class A {};
379
+ * var a = foo || function a() {};
380
+ * var a = foo ? function a() {} : bar;
381
+ * var { a = function a() {} } = foo;
382
+ *
383
+ * But it should report at declarations such as:
384
+ * var a = function(a) {};
385
+ * var a = function() { function a() {} };
386
+ * var a = wrap(function a() {});
387
+ * @param {Object} innerVariable The inner variable to check.
388
+ * @param {Object} outerVariable The outer variable.
389
+ * @returns {boolean} Whether or not inner variable is the name of a
390
+ * function or class that is assigned to outer variable as its initializer.
391
+ */
392
+ function isFunctionNameInitializerException(
393
+ innerVariable,
394
+ outerVariable,
395
+ ) {
396
+ const outerDef = outerVariable.defs[0];
397
+ const innerDef = innerVariable.defs[0];
398
+
399
+ if (!outerDef || !innerDef) {
400
+ return false;
401
+ }
402
+
403
+ if (
404
+ !(
405
+ (innerDef.type === "FunctionName" &&
406
+ innerDef.node.type === "FunctionExpression") ||
407
+ (innerDef.type === "ClassName" &&
408
+ innerDef.node.type === "ClassExpression")
409
+ )
410
+ ) {
411
+ return false;
412
+ }
413
+
414
+ const outerIdentifier = outerDef.name;
415
+ let initializerNode;
416
+
417
+ if (outerIdentifier.parent.type === "VariableDeclarator") {
418
+ initializerNode = outerIdentifier.parent.init;
419
+ } else if (outerIdentifier.parent.type === "AssignmentPattern") {
420
+ initializerNode = outerIdentifier.parent.right;
421
+ }
422
+
423
+ if (!initializerNode) {
424
+ return false;
425
+ }
426
+
427
+ const nodeToCheck = innerDef.node; // FunctionExpression or ClassExpression node
428
+
429
+ // Exit early if the node to check isn't inside the initializer
430
+ if (
431
+ !(
432
+ initializerNode.range[0] <= nodeToCheck.range[0] &&
433
+ nodeToCheck.range[1] <= initializerNode.range[1]
434
+ )
435
+ ) {
436
+ return false;
437
+ }
438
+
439
+ return initializerNode === unwrapExpression(nodeToCheck);
375
440
  }
376
441
 
377
442
  /**
@@ -577,7 +642,7 @@ module.exports = {
577
642
  shadowed &&
578
643
  (shadowed.identifiers.length > 0 ||
579
644
  (builtinGlobals && "writeable" in shadowed)) &&
580
- !isOnInitializer(variable, shadowed) &&
645
+ !isFunctionNameInitializerException(variable, shadowed) &&
581
646
  !(
582
647
  ignoreOnInitialization &&
583
648
  isInitPatternNode(variable, shadowed)
@@ -268,6 +268,7 @@ module.exports = {
268
268
  }
269
269
  }
270
270
 
271
+ /** @type {import('../types').Rule.RuleListener} */
271
272
  const rule = {
272
273
  Program(node) {
273
274
  const useStrictDirectives = getUseStrictDirectives(node.body);
@@ -281,7 +282,16 @@ module.exports = {
281
282
  node.body.length > 0 &&
282
283
  useStrictDirectives.length === 0
283
284
  ) {
284
- context.report({ node, messageId: "global" });
285
+ /*
286
+ * Report the range as v9 does
287
+ */
288
+ context.report({
289
+ loc: {
290
+ start: node.body[0].loc.start,
291
+ end: node.body.at(-1).loc.end,
292
+ },
293
+ messageId: "global",
294
+ });
285
295
  }
286
296
  reportAllExceptFirst(useStrictDirectives, "multiple", true);
287
297
  } else {
@@ -70,6 +70,7 @@ import type {
70
70
  SuggestedEditBase,
71
71
  SuggestedEdit,
72
72
  ViolationReport,
73
+ MessagePlaceholderData,
73
74
  } from "@eslint/core";
74
75
 
75
76
  //------------------------------------------------------------------------------
@@ -1458,7 +1459,7 @@ export namespace RuleTester {
1458
1459
  interface SuggestionOutput {
1459
1460
  messageId?: string;
1460
1461
  desc?: string;
1461
- data?: Record<string, unknown> | undefined;
1462
+ data?: MessagePlaceholderData | undefined;
1462
1463
  output: string;
1463
1464
  }
1464
1465
 
@@ -1470,7 +1471,7 @@ export namespace RuleTester {
1470
1471
  interface TestCaseError {
1471
1472
  message?: string | RegExp;
1472
1473
  messageId?: string;
1473
- data?: any;
1474
+ data?: MessagePlaceholderData | undefined;
1474
1475
  line?: number | undefined;
1475
1476
  column?: number | undefined;
1476
1477
  endLine?: number | undefined;
@@ -18,9 +18,4 @@ const builtinRules = require("./rules");
18
18
  // Exports
19
19
  //-----------------------------------------------------------------------------
20
20
 
21
- module.exports = {
22
- builtinRules,
23
- FlatESLint: null,
24
- LegacyESLint: null,
25
- shouldUseFlatConfig,
26
- };
21
+ module.exports = { builtinRules, shouldUseFlatConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "10.0.0-rc.0",
3
+ "version": "10.0.0-rc.2",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "type": "commonjs",
@@ -120,10 +120,10 @@
120
120
  "cross-spawn": "^7.0.6",
121
121
  "debug": "^4.3.2",
122
122
  "escape-string-regexp": "^4.0.0",
123
- "eslint-scope": "^9.0.0",
123
+ "eslint-scope": "^9.1.0",
124
124
  "eslint-visitor-keys": "^5.0.0",
125
- "espree": "^11.0.0",
126
- "esquery": "^1.5.0",
125
+ "espree": "^11.1.0",
126
+ "esquery": "^1.7.0",
127
127
  "esutils": "^2.0.2",
128
128
  "fast-deep-equal": "^3.1.3",
129
129
  "file-entry-cache": "^8.0.0",