eslint 9.10.0 → 9.11.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/README.md CHANGED
@@ -297,9 +297,9 @@ The following companies, organizations, and individuals support ESLint's ongoing
297
297
  <!--sponsorsstart-->
298
298
  <h3>Platinum Sponsors</h3>
299
299
  <p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="128"></a></p><h3>Gold Sponsors</h3>
300
- <p><a href="https://trunk.io/"><img src="https://images.opencollective.com/trunkio/fb92d60/avatar.png" alt="trunk.io" height="96"></a> <a href="https://opensource.siemens.com"><img src="https://avatars.githubusercontent.com/u/624020?v=4" alt="Siemens" height="96"></a></p><h3>Silver Sponsors</h3>
300
+ <p><a href="https://trunk.io/"><img src="https://images.opencollective.com/trunkio/fb92d60/avatar.png" alt="trunk.io" height="96"></a></p><h3>Silver Sponsors</h3>
301
301
  <p><a href="https://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/fe76f99/logo.png" alt="JetBrains" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a> <a href="https://www.workleap.com"><img src="https://avatars.githubusercontent.com/u/53535748?u=d1e55d7661d724bf2281c1bfd33cb8f99fe2465f&v=4" alt="Workleap" height="64"></a></p><h3>Bronze Sponsors</h3>
302
- <p><a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" 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.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104?v=4" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://usenextbase.com"><img src="https://avatars.githubusercontent.com/u/145838380?v=4" alt="Nextbase Starter Kit" height="32"></a></p>
302
+ <p><a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" 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://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104?v=4" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://usenextbase.com"><img src="https://avatars.githubusercontent.com/u/145838380?v=4" alt="Nextbase Starter Kit" height="32"></a></p>
303
303
  <!--sponsorsend-->
304
304
 
305
305
  <!--techsponsorsstart-->
@@ -65,13 +65,9 @@ function parseRuleId(ruleId) {
65
65
  * or undefined if not.
66
66
  */
67
67
  function getRuleFromConfig(ruleId, config) {
68
-
69
68
  const { pluginName, ruleName } = parseRuleId(ruleId);
70
69
 
71
- const plugin = config.plugins && config.plugins[pluginName];
72
- const rule = plugin && plugin.rules && plugin.rules[ruleName];
73
-
74
- return rule;
70
+ return config.plugins?.[pluginName]?.rules?.[ruleName];
75
71
  }
76
72
 
77
73
  /**
@@ -20,7 +20,7 @@ const
20
20
 
21
21
  CodePathAnalyzer = require("../../../linter/code-path-analysis/code-path-analyzer"),
22
22
  createEmitter = require("../../../linter/safe-emitter"),
23
- { ConfigCommentParser, VisitNodeStep, CallMethodStep } = require("@eslint/plugin-kit"),
23
+ { ConfigCommentParser, VisitNodeStep, CallMethodStep, Directive } = require("@eslint/plugin-kit"),
24
24
 
25
25
  eslintScope = require("eslint-scope");
26
26
 
@@ -316,57 +316,6 @@ function markExportedVariables(globalScope, variables) {
316
316
 
317
317
  }
318
318
 
319
- /**
320
- * A class to represent a directive comment.
321
- * @implements {IDirective}
322
- */
323
- class Directive {
324
-
325
- /**
326
- * The type of directive.
327
- * @type {"disable"|"enable"|"disable-next-line"|"disable-line"}
328
- * @readonly
329
- */
330
- type;
331
-
332
- /**
333
- * The node representing the directive.
334
- * @type {ASTNode|Comment}
335
- * @readonly
336
- */
337
- node;
338
-
339
- /**
340
- * Everything after the "eslint-disable" portion of the directive,
341
- * but before the "--" that indicates the justification.
342
- * @type {string}
343
- * @readonly
344
- */
345
- value;
346
-
347
- /**
348
- * The justification for the directive.
349
- * @type {string}
350
- * @readonly
351
- */
352
- justification;
353
-
354
- /**
355
- * Creates a new instance.
356
- * @param {Object} options The options for the directive.
357
- * @param {"disable"|"enable"|"disable-next-line"|"disable-line"} options.type The type of directive.
358
- * @param {ASTNode|Comment} options.node The node representing the directive.
359
- * @param {string} options.value The value of the directive.
360
- * @param {string} options.justification The justification for the directive.
361
- */
362
- constructor({ type, node, value, justification }) {
363
- this.type = type;
364
- this.node = node;
365
- this.value = value;
366
- this.justification = justification;
367
- }
368
- }
369
-
370
319
  //------------------------------------------------------------------------------
371
320
  // Public Interface
372
321
  //------------------------------------------------------------------------------
@@ -54,6 +54,7 @@ const { LATEST_ECMA_VERSION } = require("../../conf/ecma-version");
54
54
  const { VFile } = require("./vfile");
55
55
  const { ParserService } = require("../services/parser-service");
56
56
  const { FileContext } = require("./file-context");
57
+ const { ProcessorService } = require("../services/processor-service");
57
58
  const STEP_KIND_VISIT = 1;
58
59
  const STEP_KIND_CALL = 2;
59
60
 
@@ -1292,27 +1293,18 @@ class Linter {
1292
1293
  }
1293
1294
 
1294
1295
  /**
1295
- * Same as linter.verify, except without support for processors.
1296
- * @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
1296
+ * Lint using eslintrc and without processors.
1297
+ * @param {VFile} file The file to lint.
1297
1298
  * @param {ConfigData} providedConfig An ESLintConfig instance to configure everything.
1298
1299
  * @param {VerifyOptions} [providedOptions] The optional filename of the file being checked.
1299
1300
  * @throws {Error} If during rule execution.
1300
1301
  * @returns {(LintMessage|SuppressedLintMessage)[]} The results as an array of messages or an empty array if no messages.
1301
1302
  */
1302
- _verifyWithoutProcessors(textOrSourceCode, providedConfig, providedOptions) {
1303
+ #eslintrcVerifyWithoutProcessors(file, providedConfig, providedOptions) {
1304
+
1303
1305
  const slots = internalSlotsMap.get(this);
1304
1306
  const config = providedConfig || {};
1305
1307
  const options = normalizeVerifyOptions(providedOptions, config);
1306
- let text;
1307
-
1308
- // evaluate arguments
1309
- if (typeof textOrSourceCode === "string") {
1310
- slots.lastSourceCode = null;
1311
- text = textOrSourceCode;
1312
- } else {
1313
- slots.lastSourceCode = textOrSourceCode;
1314
- text = textOrSourceCode.text;
1315
- }
1316
1308
 
1317
1309
  // Resolve parser.
1318
1310
  let parserName = DEFAULT_PARSER_NAME;
@@ -1339,7 +1331,7 @@ class Linter {
1339
1331
 
1340
1332
  // search and apply "eslint-env *".
1341
1333
  const envInFile = options.allowInlineConfig && !options.warnInlineConfig
1342
- ? findEslintEnv(text)
1334
+ ? findEslintEnv(file.body)
1343
1335
  : {};
1344
1336
  const resolvedEnvConfig = Object.assign({ builtin: true }, config.env, envInFile);
1345
1337
  const enabledEnvs = Object.keys(resolvedEnvConfig)
@@ -1355,9 +1347,6 @@ class Linter {
1355
1347
  parser,
1356
1348
  parserOptions
1357
1349
  });
1358
- const file = new VFile(options.filename, text, {
1359
- physicalPath: providedOptions.physicalFilename
1360
- });
1361
1350
 
1362
1351
  if (!slots.lastSourceCode) {
1363
1352
  let t;
@@ -1468,6 +1457,36 @@ class Linter {
1468
1457
  .sort((problemA, problemB) => problemA.line - problemB.line || problemA.column - problemB.column),
1469
1458
  reportUnusedDisableDirectives: options.reportUnusedDisableDirectives
1470
1459
  });
1460
+
1461
+ }
1462
+
1463
+ /**
1464
+ * Same as linter.verify, except without support for processors.
1465
+ * @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
1466
+ * @param {ConfigData} providedConfig An ESLintConfig instance to configure everything.
1467
+ * @param {VerifyOptions} [providedOptions] The optional filename of the file being checked.
1468
+ * @throws {Error} If during rule execution.
1469
+ * @returns {(LintMessage|SuppressedLintMessage)[]} The results as an array of messages or an empty array if no messages.
1470
+ */
1471
+ _verifyWithoutProcessors(textOrSourceCode, providedConfig, providedOptions) {
1472
+ const slots = internalSlotsMap.get(this);
1473
+ const filename = normalizeFilename(providedOptions.filename || "<input>");
1474
+ let text;
1475
+
1476
+ // evaluate arguments
1477
+ if (typeof textOrSourceCode === "string") {
1478
+ slots.lastSourceCode = null;
1479
+ text = textOrSourceCode;
1480
+ } else {
1481
+ slots.lastSourceCode = textOrSourceCode;
1482
+ text = textOrSourceCode.text;
1483
+ }
1484
+
1485
+ const file = new VFile(filename, text, {
1486
+ physicalPath: providedOptions.physicalFilename
1487
+ });
1488
+
1489
+ return this.#eslintrcVerifyWithoutProcessors(file, providedConfig, providedOptions);
1471
1490
  }
1472
1491
 
1473
1492
  /**
@@ -1537,102 +1556,91 @@ class Linter {
1537
1556
  * @returns {(LintMessage|SuppressedLintMessage)[]} The found problems.
1538
1557
  */
1539
1558
  _verifyWithFlatConfigArrayAndProcessor(textOrSourceCode, config, options, configForRecursive) {
1559
+ const slots = internalSlotsMap.get(this);
1540
1560
  const filename = options.filename || "<input>";
1541
1561
  const filenameToExpose = normalizeFilename(filename);
1542
1562
  const physicalFilename = options.physicalFilename || filenameToExpose;
1543
1563
  const text = ensureText(textOrSourceCode);
1564
+ const file = new VFile(filenameToExpose, text, {
1565
+ physicalPath: physicalFilename
1566
+ });
1567
+
1544
1568
  const preprocess = options.preprocess || (rawText => [rawText]);
1545
1569
  const postprocess = options.postprocess || (messagesList => messagesList.flat());
1570
+
1571
+ const processorService = new ProcessorService();
1572
+ const preprocessResult = processorService.preprocessSync(file, {
1573
+ processor: {
1574
+ preprocess,
1575
+ postprocess
1576
+ }
1577
+ });
1578
+
1579
+ if (!preprocessResult.ok) {
1580
+ return preprocessResult.errors;
1581
+ }
1582
+
1546
1583
  const filterCodeBlock =
1547
1584
  options.filterCodeBlock ||
1548
1585
  (blockFilename => blockFilename.endsWith(".js"));
1549
1586
  const originalExtname = path.extname(filename);
1587
+ const { files } = preprocessResult;
1550
1588
 
1551
- let blocks;
1552
-
1553
- try {
1554
- blocks = preprocess(text, filenameToExpose);
1555
- } catch (ex) {
1556
-
1557
- // If the message includes a leading line number, strip it:
1558
- const message = `Preprocessing error: ${ex.message.replace(/^line \d+:/iu, "").trim()}`;
1559
-
1560
- debug("%s\n%s", message, ex.stack);
1561
-
1562
- return [
1563
- {
1564
- ruleId: null,
1565
- fatal: true,
1566
- severity: 2,
1567
- message,
1568
- line: ex.lineNumber,
1569
- column: ex.column,
1570
- nodeType: null
1571
- }
1572
- ];
1573
- }
1574
-
1575
- const messageLists = blocks.map((block, i) => {
1576
- debug("A code block was found: %o", block.filename || "(unnamed)");
1589
+ const messageLists = files.map(block => {
1590
+ debug("A code block was found: %o", block.path || "(unnamed)");
1577
1591
 
1578
1592
  // Keep the legacy behavior.
1579
1593
  if (typeof block === "string") {
1580
1594
  return this._verifyWithFlatConfigArrayAndWithoutProcessors(block, config, options);
1581
1595
  }
1582
1596
 
1583
- const blockText = block.text;
1584
- const blockName = path.join(filename, `${i}_${block.filename}`);
1585
-
1586
1597
  // Skip this block if filtered.
1587
- if (!filterCodeBlock(blockName, blockText)) {
1598
+ if (!filterCodeBlock(block.path, block.body)) {
1588
1599
  debug("This code block was skipped.");
1589
1600
  return [];
1590
1601
  }
1591
1602
 
1592
1603
  // Resolve configuration again if the file content or extension was changed.
1593
- if (configForRecursive && (text !== blockText || path.extname(blockName) !== originalExtname)) {
1604
+ if (configForRecursive && (text !== block.rawBody || path.extname(block.path) !== originalExtname)) {
1594
1605
  debug("Resolving configuration again because the file content or extension was changed.");
1595
1606
  return this._verifyWithFlatConfigArray(
1596
- blockText,
1607
+ block.rawBody,
1597
1608
  configForRecursive,
1598
- { ...options, filename: blockName, physicalFilename }
1609
+ { ...options, filename: block.path, physicalFilename: block.physicalPath }
1599
1610
  );
1600
1611
  }
1601
1612
 
1613
+ slots.lastSourceCode = null;
1614
+
1602
1615
  // Does lint.
1603
- return this._verifyWithFlatConfigArrayAndWithoutProcessors(
1604
- blockText,
1616
+ return this.#flatVerifyWithoutProcessors(
1617
+ block,
1605
1618
  config,
1606
- { ...options, filename: blockName, physicalFilename }
1619
+ { ...options, filename: block.path, physicalFilename: block.physicalPath }
1607
1620
  );
1608
1621
  });
1609
1622
 
1610
- return postprocess(messageLists, filenameToExpose);
1623
+ return processorService.postprocessSync(file, messageLists, {
1624
+ processor: {
1625
+ preprocess,
1626
+ postprocess
1627
+ }
1628
+ });
1611
1629
  }
1612
1630
 
1613
1631
  /**
1614
- * Same as linter.verify, except without support for processors.
1615
- * @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
1632
+ * Verify using flat config and without any processors.
1633
+ * @param {VFile} file The file to lint.
1616
1634
  * @param {FlatConfig} providedConfig An ESLintConfig instance to configure everything.
1617
1635
  * @param {VerifyOptions} [providedOptions] The optional filename of the file being checked.
1618
1636
  * @throws {Error} If during rule execution.
1619
1637
  * @returns {(LintMessage|SuppressedLintMessage)[]} The results as an array of messages or an empty array if no messages.
1620
1638
  */
1621
- _verifyWithFlatConfigArrayAndWithoutProcessors(textOrSourceCode, providedConfig, providedOptions) {
1639
+ #flatVerifyWithoutProcessors(file, providedConfig, providedOptions) {
1640
+
1622
1641
  const slots = internalSlotsMap.get(this);
1623
1642
  const config = providedConfig || {};
1624
1643
  const options = normalizeVerifyOptions(providedOptions, config);
1625
- let text;
1626
-
1627
- // evaluate arguments
1628
- if (typeof textOrSourceCode === "string") {
1629
- slots.lastSourceCode = null;
1630
- text = textOrSourceCode;
1631
- } else {
1632
- slots.lastSourceCode = textOrSourceCode;
1633
- text = textOrSourceCode.text;
1634
- }
1635
-
1636
1644
  const languageOptions = config.languageOptions;
1637
1645
 
1638
1646
  languageOptions.ecmaVersion = normalizeEcmaVersionForLanguageOptions(
@@ -1663,9 +1671,6 @@ class Linter {
1663
1671
  }
1664
1672
 
1665
1673
  const settings = config.settings || {};
1666
- const file = new VFile(options.filename, text, {
1667
- physicalPath: providedOptions.physicalFilename
1668
- });
1669
1674
 
1670
1675
  if (!slots.lastSourceCode) {
1671
1676
  let t;
@@ -1957,6 +1962,37 @@ class Linter {
1957
1962
  ruleFilter: options.ruleFilter,
1958
1963
  configuredRules
1959
1964
  });
1965
+
1966
+
1967
+ }
1968
+
1969
+ /**
1970
+ * Same as linter.verify, except without support for processors.
1971
+ * @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
1972
+ * @param {FlatConfig} providedConfig An ESLintConfig instance to configure everything.
1973
+ * @param {VerifyOptions} [providedOptions] The optional filename of the file being checked.
1974
+ * @throws {Error} If during rule execution.
1975
+ * @returns {(LintMessage|SuppressedLintMessage)[]} The results as an array of messages or an empty array if no messages.
1976
+ */
1977
+ _verifyWithFlatConfigArrayAndWithoutProcessors(textOrSourceCode, providedConfig, providedOptions) {
1978
+ const slots = internalSlotsMap.get(this);
1979
+ const filename = normalizeFilename(providedOptions.filename || "<input>");
1980
+ let text;
1981
+
1982
+ // evaluate arguments
1983
+ if (typeof textOrSourceCode === "string") {
1984
+ slots.lastSourceCode = null;
1985
+ text = textOrSourceCode;
1986
+ } else {
1987
+ slots.lastSourceCode = textOrSourceCode;
1988
+ text = textOrSourceCode.text;
1989
+ }
1990
+
1991
+ const file = new VFile(filename, text, {
1992
+ physicalPath: providedOptions.physicalFilename
1993
+ });
1994
+
1995
+ return this.#flatVerifyWithoutProcessors(file, providedConfig, providedOptions);
1960
1996
  }
1961
1997
 
1962
1998
  /**
@@ -2057,77 +2093,78 @@ class Linter {
2057
2093
  * @returns {(LintMessage|SuppressedLintMessage)[]} The found problems.
2058
2094
  */
2059
2095
  _verifyWithProcessor(textOrSourceCode, config, options, configForRecursive) {
2096
+ const slots = internalSlotsMap.get(this);
2060
2097
  const filename = options.filename || "<input>";
2061
2098
  const filenameToExpose = normalizeFilename(filename);
2062
2099
  const physicalFilename = options.physicalFilename || filenameToExpose;
2063
2100
  const text = ensureText(textOrSourceCode);
2101
+ const file = new VFile(filenameToExpose, text, {
2102
+ physicalPath: physicalFilename
2103
+ });
2104
+
2064
2105
  const preprocess = options.preprocess || (rawText => [rawText]);
2065
2106
  const postprocess = options.postprocess || (messagesList => messagesList.flat());
2066
- const filterCodeBlock =
2067
- options.filterCodeBlock ||
2068
- (blockFilename => blockFilename.endsWith(".js"));
2069
- const originalExtname = path.extname(filename);
2070
-
2071
- let blocks;
2072
2107
 
2073
- try {
2074
- blocks = preprocess(text, filenameToExpose);
2075
- } catch (ex) {
2108
+ const processorService = new ProcessorService();
2109
+ const preprocessResult = processorService.preprocessSync(file, {
2110
+ processor: {
2111
+ preprocess,
2112
+ postprocess
2113
+ }
2114
+ });
2076
2115
 
2077
- // If the message includes a leading line number, strip it:
2078
- const message = `Preprocessing error: ${ex.message.replace(/^line \d+:/iu, "").trim()}`;
2116
+ if (!preprocessResult.ok) {
2117
+ return preprocessResult.errors;
2118
+ }
2079
2119
 
2080
- debug("%s\n%s", message, ex.stack);
2120
+ const filterCodeBlock =
2121
+ options.filterCodeBlock ||
2122
+ (blockFilePath => blockFilePath.endsWith(".js"));
2123
+ const originalExtname = path.extname(filename);
2081
2124
 
2082
- return [
2083
- {
2084
- ruleId: null,
2085
- fatal: true,
2086
- severity: 2,
2087
- message,
2088
- line: ex.lineNumber,
2089
- column: ex.column,
2090
- nodeType: null
2091
- }
2092
- ];
2093
- }
2125
+ const { files } = preprocessResult;
2094
2126
 
2095
- const messageLists = blocks.map((block, i) => {
2096
- debug("A code block was found: %o", block.filename || "(unnamed)");
2127
+ const messageLists = files.map(block => {
2128
+ debug("A code block was found: %o", block.path ?? "(unnamed)");
2097
2129
 
2098
2130
  // Keep the legacy behavior.
2099
2131
  if (typeof block === "string") {
2100
2132
  return this._verifyWithoutProcessors(block, config, options);
2101
2133
  }
2102
2134
 
2103
- const blockText = block.text;
2104
- const blockName = path.join(filename, `${i}_${block.filename}`);
2105
-
2106
2135
  // Skip this block if filtered.
2107
- if (!filterCodeBlock(blockName, blockText)) {
2136
+ if (!filterCodeBlock(block.path, block.body)) {
2108
2137
  debug("This code block was skipped.");
2109
2138
  return [];
2110
2139
  }
2111
2140
 
2112
2141
  // Resolve configuration again if the file content or extension was changed.
2113
- if (configForRecursive && (text !== blockText || path.extname(blockName) !== originalExtname)) {
2142
+ if (configForRecursive && (text !== block.rawBody || path.extname(block.path) !== originalExtname)) {
2114
2143
  debug("Resolving configuration again because the file content or extension was changed.");
2115
2144
  return this._verifyWithConfigArray(
2116
- blockText,
2145
+ block.rawBody,
2117
2146
  configForRecursive,
2118
- { ...options, filename: blockName, physicalFilename }
2147
+ { ...options, filename: block.path, physicalFilename: block.physicalPath }
2119
2148
  );
2120
2149
  }
2121
2150
 
2151
+ slots.lastSourceCode = null;
2152
+
2122
2153
  // Does lint.
2123
- return this._verifyWithoutProcessors(
2124
- blockText,
2154
+ return this.#eslintrcVerifyWithoutProcessors(
2155
+ block,
2125
2156
  config,
2126
- { ...options, filename: blockName, physicalFilename }
2157
+ { ...options, filename: block.path, physicalFilename: block.physicalPath }
2127
2158
  );
2128
2159
  });
2129
2160
 
2130
- return postprocess(messageLists, filenameToExpose);
2161
+ return processorService.postprocessSync(file, messageLists, {
2162
+ processor: {
2163
+ preprocess,
2164
+ postprocess
2165
+ }
2166
+ });
2167
+
2131
2168
  }
2132
2169
 
2133
2170
  /**
@@ -85,6 +85,13 @@ class VFile {
85
85
  */
86
86
  body;
87
87
 
88
+ /**
89
+ * The raw body of the file, including a BOM if present.
90
+ * @type {string|Uint8Array}
91
+ * @readonly
92
+ */
93
+ rawBody;
94
+
88
95
  /**
89
96
  * Indicates whether the file has a byte order mark (BOM).
90
97
  * @type {boolean}
@@ -104,8 +111,8 @@ class VFile {
104
111
  this.physicalPath = physicalPath ?? path;
105
112
  this.bom = hasUnicodeBOM(body);
106
113
  this.body = stripUnicodeBOM(body);
114
+ this.rawBody = body;
107
115
  }
108
-
109
116
  }
110
117
 
111
118
  module.exports = { VFile };
@@ -11,6 +11,7 @@
11
11
  //------------------------------------------------------------------------------
12
12
 
13
13
  const { getGraphemeCount } = require("../shared/string-utils");
14
+ const { getModuleExportName } = require("./utils/ast-utils");
14
15
 
15
16
  //------------------------------------------------------------------------------
16
17
  // Rule Definition
@@ -116,6 +117,12 @@ module.exports = {
116
117
  }
117
118
  return properties && !parent.computed && parent.key.name === node.name;
118
119
  },
120
+ ImportSpecifier(parent, node) {
121
+ return (
122
+ parent.local === node &&
123
+ getModuleExportName(parent.imported) !== getModuleExportName(parent.local)
124
+ );
125
+ },
119
126
  ImportDefaultSpecifier: true,
120
127
  ImportNamespaceSpecifier: true,
121
128
  RestElement: true,
@@ -4,6 +4,8 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
+ const astUtils = require("./utils/ast-utils");
8
+
7
9
  //------------------------------------------------------------------------------
8
10
  // Helpers
9
11
  //------------------------------------------------------------------------------
@@ -143,10 +145,13 @@ module.exports = {
143
145
  url: "https://eslint.org/docs/latest/rules/no-useless-constructor"
144
146
  },
145
147
 
148
+ hasSuggestions: true,
149
+
146
150
  schema: [],
147
151
 
148
152
  messages: {
149
- noUselessConstructor: "Useless constructor."
153
+ noUselessConstructor: "Useless constructor.",
154
+ removeConstructor: "Remove the constructor."
150
155
  }
151
156
  },
152
157
 
@@ -177,7 +182,18 @@ module.exports = {
177
182
  if (superClass ? isRedundantSuperCall(body, ctorParams) : (body.length === 0)) {
178
183
  context.report({
179
184
  node,
180
- messageId: "noUselessConstructor"
185
+ messageId: "noUselessConstructor",
186
+ suggest: [
187
+ {
188
+ messageId: "removeConstructor",
189
+ *fix(fixer) {
190
+ const nextToken = context.sourceCode.getTokenAfter(node);
191
+ const addSemiColon = nextToken.type === "Punctuator" && nextToken.value === "[" && astUtils.needsPrecedingSemicolon(context.sourceCode, node);
192
+
193
+ yield fixer.replaceText(node, addSemiColon ? ";" : "");
194
+ }
195
+ }
196
+ ]
181
197
  });
182
198
  }
183
199
  }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * @fileoverview ESLint Processor Service
3
+ * @author Nicholas C. Zakas
4
+ */
5
+ /* eslint class-methods-use-this: off -- Anticipate future constructor arguments. */
6
+
7
+ "use strict";
8
+
9
+ //-----------------------------------------------------------------------------
10
+ // Requirements
11
+ //-----------------------------------------------------------------------------
12
+
13
+ const path = require("node:path");
14
+ const { VFile } = require("../linter/vfile.js");
15
+
16
+ //-----------------------------------------------------------------------------
17
+ // Types
18
+ //-----------------------------------------------------------------------------
19
+
20
+ /** @typedef {import("../shared/types.js").LintMessage} LintMessage */
21
+ /** @typedef {import("../linter/vfile.js").VFile} VFile */
22
+ /** @typedef {import("@eslint/core").Language} Language */
23
+ /** @typedef {import("@eslint/core").LanguageOptions} LanguageOptions */
24
+ /** @typedef {import("eslint").Linter.Processor} Processor */
25
+
26
+ //-----------------------------------------------------------------------------
27
+ // Exports
28
+ //-----------------------------------------------------------------------------
29
+
30
+ /**
31
+ * The service that applies processors to files.
32
+ */
33
+ class ProcessorService {
34
+
35
+ /**
36
+ * Preprocesses the given file synchronously.
37
+ * @param {VFile} file The file to preprocess.
38
+ * @param {{processor:Processor}} config The configuration to use.
39
+ * @returns {{ok:boolean, files?: Array<VFile>, errors?: Array<LintMessage>}} An array of preprocessed files or errors.
40
+ * @throws {Error} If the preprocessor returns a promise.
41
+ */
42
+ preprocessSync(file, config) {
43
+
44
+ const { processor } = config;
45
+ let blocks;
46
+
47
+ try {
48
+ blocks = processor.preprocess(file.rawBody, file.path);
49
+ } catch (ex) {
50
+
51
+ // If the message includes a leading line number, strip it:
52
+ const message = `Preprocessing error: ${ex.message.replace(/^line \d+:/iu, "").trim()}`;
53
+
54
+ return {
55
+ ok: false,
56
+ errors: [
57
+ {
58
+ ruleId: null,
59
+ fatal: true,
60
+ severity: 2,
61
+ message,
62
+ line: ex.lineNumber,
63
+ column: ex.column,
64
+ nodeType: null
65
+ }
66
+ ]
67
+ };
68
+ }
69
+
70
+ if (typeof blocks.then === "function") {
71
+ throw new Error("Unsupported: Preprocessor returned a promise.");
72
+ }
73
+
74
+ return {
75
+ ok: true,
76
+ files: blocks.map((block, i) => {
77
+
78
+ // Legacy behavior: return the block as a string
79
+ if (typeof block === "string") {
80
+ return block;
81
+ }
82
+
83
+ const filePath = path.join(file.path, `${i}_${block.filename}`);
84
+
85
+ return new VFile(filePath, block.text, {
86
+ physicalPath: file.physicalPath
87
+ });
88
+ })
89
+ };
90
+
91
+ }
92
+
93
+ /**
94
+ * Postprocesses the given messages synchronously.
95
+ * @param {VFile} file The file to postprocess.
96
+ * @param {LintMessage[][]} messages The messages to postprocess.
97
+ * @param {{processor:Processor}} config The configuration to use.
98
+ * @returns {LintMessage[]} The postprocessed messages.
99
+ */
100
+ postprocessSync(file, messages, config) {
101
+
102
+ const { processor } = config;
103
+
104
+ return processor.postprocess(messages, file.path);
105
+ }
106
+
107
+ }
108
+
109
+ module.exports = { ProcessorService };
@@ -245,6 +245,6 @@ module.exports = {};
245
245
  * A formatter function.
246
246
  * @callback FormatterFunction
247
247
  * @param {LintResult[]} results The list of linting results.
248
- * @param {{cwd: string, maxWarningsExceeded?: MaxWarningsExceeded, rulesMeta: Record<string, RuleMeta>}} [context] A context object.
248
+ * @param {{cwd: string, maxWarningsExceeded?: MaxWarningsExceeded, rulesMeta: Record<string, RuleMeta>}} context A context object.
249
249
  * @returns {string | Promise<string>} Formatted text.
250
250
  */
@@ -26,6 +26,7 @@
26
26
  */
27
27
 
28
28
  import * as ESTree from "estree";
29
+ import { Language } from "@eslint/core";
29
30
  import { JSONSchema4 } from "json-schema";
30
31
  import { LegacyESLint } from "./use-at-your-own-risk.js";
31
32
 
@@ -1265,6 +1266,13 @@ export namespace Linter {
1265
1266
  */
1266
1267
  ignores?: string[];
1267
1268
 
1269
+ /**
1270
+ * The name of the language used for linting. This is used to determine the
1271
+ * parser and other language-specific settings.
1272
+ * @since 9.7.0
1273
+ */
1274
+ language?: string;
1275
+
1268
1276
  /**
1269
1277
  * An object containing settings related to how JavaScript is configured for
1270
1278
  * linting.
@@ -1411,7 +1419,7 @@ export class ESLint {
1411
1419
 
1412
1420
  isPathIgnored(filePath: string): Promise<boolean>;
1413
1421
 
1414
- loadFormatter(nameOrPath?: string): Promise<ESLint.Formatter>;
1422
+ loadFormatter(nameOrPath?: string): Promise<ESLint.LoadedFormatter>;
1415
1423
  }
1416
1424
 
1417
1425
  export namespace ESLint {
@@ -1441,6 +1449,7 @@ export namespace ESLint {
1441
1449
  interface Plugin extends ObjectMetaProperties {
1442
1450
  configs?: Record<string, Linter.LegacyConfig | Linter.Config | Linter.Config[]> | undefined;
1443
1451
  environments?: Record<string, Environment> | undefined;
1452
+ languages?: Record<string, Language> | undefined;
1444
1453
  processors?: Record<string, Linter.Processor> | undefined;
1445
1454
  rules?: Record<string, Rule.RuleModule> | undefined;
1446
1455
  }
@@ -1555,14 +1564,38 @@ export namespace ESLint {
1555
1564
  replacedBy: string[];
1556
1565
  }
1557
1566
 
1558
- interface Formatter {
1559
- format(results: LintResult[], data?: LintResultData): string | Promise<string>;
1567
+ interface ResultsMeta {
1568
+ maxWarningsExceeded?: MaxWarningsExceeded | undefined;
1560
1569
  }
1561
1570
 
1571
+ /** The type of an object resolved by {@link ESLint.loadFormatter}. */
1572
+ interface LoadedFormatter {
1573
+
1574
+ /**
1575
+ * Used to call the underlying formatter.
1576
+ * @param results An array of lint results to format.
1577
+ * @param resultsMeta An object with an optional `maxWarningsExceeded` property that will be
1578
+ * passed to the underlying formatter function along with other properties set by ESLint.
1579
+ * This argument can be omitted if `maxWarningsExceeded` is not needed.
1580
+ * @return The formatter output.
1581
+ */
1582
+ format(results: LintResult[], resultsMeta?: ResultsMeta): string | Promise<string>;
1583
+ }
1584
+
1585
+ // The documented type name is `LoadedFormatter`, but `Formatter` has been historically more used.
1586
+ type Formatter = LoadedFormatter;
1587
+
1588
+ /**
1589
+ * The expected signature of a custom formatter.
1590
+ * @param results An array of lint results to format.
1591
+ * @param context Additional information for the formatter.
1592
+ * @return The formatter output.
1593
+ */
1594
+ type FormatterFunction =
1595
+ (results: LintResult[], context: LintResultData) => string | Promise<string>;
1596
+
1562
1597
  // Docs reference the types by those name
1563
1598
  type EditInfo = Rule.Fix;
1564
- type LoadedFormatter = Formatter;
1565
- type ResultsMeta = LintResultData;
1566
1599
  }
1567
1600
 
1568
1601
  // #endregion
@@ -324,6 +324,8 @@ export interface BestPractices extends Linter.RulesRecord {
324
324
  | "getters"
325
325
  | "setters"
326
326
  | "constructors"
327
+ | "asyncFunctions"
328
+ | "asyncMethods"
327
329
  >;
328
330
  }>,
329
331
  ]
@@ -472,7 +474,7 @@ export interface BestPractices extends Linter.RulesRecord {
472
474
  /**
473
475
  * @default []
474
476
  */
475
- allow: Array<"~" | "!!" | "+" | "*">;
477
+ allow: Array<"~" | "!!" | "+" | "- -" | "-" | "*">;
476
478
  }>,
477
479
  ]
478
480
  >;
@@ -683,16 +685,22 @@ export interface BestPractices extends Linter.RulesRecord {
683
685
  */
684
686
  "no-param-reassign": Linter.RuleEntry<
685
687
  [
686
- Partial<{
687
- /**
688
- * @default false
689
- */
690
- props: boolean;
691
- /**
692
- * @default []
693
- */
694
- ignorePropertyModificationsFor: string[];
695
- }>,
688
+ | {
689
+ props?: false;
690
+ }
691
+ | ({
692
+ props: true;
693
+ } & Partial<{
694
+ /**
695
+ * @default []
696
+ */
697
+ ignorePropertyModificationsFor: string[];
698
+ /**
699
+ * @since 6.6.0
700
+ * @default []
701
+ */
702
+ ignorePropertyModificationsForRegex: string[];
703
+ }>),
696
704
  ]
697
705
  >;
698
706
 
@@ -966,7 +974,7 @@ export interface BestPractices extends Linter.RulesRecord {
966
974
  /**
967
975
  * Disallow use of `Object.prototype.hasOwnProperty.call()` and prefer use of `Object.hasOwn()`.
968
976
  *
969
- * @since 3.5.0
977
+ * @since 8.5.0
970
978
  * @see https://eslint.org/docs/rules/prefer-object-has-own
971
979
  */
972
980
  "prefer-object-has-own": Linter.RuleEntry<[]>;
@@ -231,6 +231,52 @@ export interface ECMAScript6 extends Linter.RulesRecord {
231
231
  */
232
232
  "no-new-symbol": Linter.RuleEntry<[]>;
233
233
 
234
+ /**
235
+ * Rule to disallow specified names in exports.
236
+ *
237
+ * @since 7.0.0-alpha.0
238
+ * @see https://eslint.org/docs/rules/no-restricted-exports
239
+ */
240
+ "no-restricted-exports": Linter.RuleEntry<
241
+ [
242
+ Partial<{
243
+ /**
244
+ * @default []
245
+ */
246
+ restrictedNamedExports: string[];
247
+ /**
248
+ * @since 9.3.0
249
+ */
250
+ restrictedNamedExportsPattern: string;
251
+ /**
252
+ * @since 8.33.0
253
+ */
254
+ restrictDefaultExports: Partial<{
255
+ /**
256
+ * @default false
257
+ */
258
+ direct: boolean;
259
+ /**
260
+ * @default false
261
+ */
262
+ named: boolean;
263
+ /**
264
+ * @default false
265
+ */
266
+ defaultFrom: boolean;
267
+ /**
268
+ * @default false
269
+ */
270
+ namedFrom: boolean;
271
+ /**
272
+ * @default false
273
+ */
274
+ namespaceFrom: boolean;
275
+ }>;
276
+ }>,
277
+ ]
278
+ >;
279
+
234
280
  /**
235
281
  * Rule to disallow specified modules when loaded by `import`.
236
282
  *
@@ -251,7 +251,30 @@ export interface PossibleErrors extends Linter.RulesRecord {
251
251
  * @since 0.4.0
252
252
  * @see https://eslint.org/docs/rules/no-extra-boolean-cast
253
253
  */
254
- "no-extra-boolean-cast": Linter.RuleEntry<[]>;
254
+ "no-extra-boolean-cast": Linter.RuleEntry<
255
+ [
256
+ | Partial<{
257
+ /**
258
+ * @since 9.3.0
259
+ * @default false
260
+ */
261
+ enforceForInnerExpressions: boolean;
262
+ /**
263
+ * @deprecated
264
+ */
265
+ enforceForLogicalOperands: never;
266
+ }>
267
+ | Partial<{
268
+ /**
269
+ * @deprecated
270
+ * @since 7.0.0-alpha.2
271
+ * @default false
272
+ */
273
+ enforceForLogicalOperands: boolean;
274
+ enforceForInnerExpressions: never;
275
+ }>,
276
+ ]
277
+ >;
255
278
 
256
279
  /**
257
280
  * Rule to disallow unnecessary parentheses.
@@ -391,7 +414,17 @@ export interface PossibleErrors extends Linter.RulesRecord {
391
414
  * @since 5.3.0
392
415
  * @see https://eslint.org/docs/rules/no-misleading-character-class
393
416
  */
394
- "no-misleading-character-class": Linter.RuleEntry<[]>;
417
+ "no-misleading-character-class": Linter.RuleEntry<
418
+ [
419
+ Partial<{
420
+ /**
421
+ * @since 9.3.0
422
+ * @default false
423
+ */
424
+ allowEscape: boolean;
425
+ }>,
426
+ ]
427
+ >;
395
428
 
396
429
  /**
397
430
  * Rule to disallow calling global object properties as functions.
@@ -165,6 +165,16 @@ export interface StylisticIssues extends Linter.RulesRecord {
165
165
  * @default false
166
166
  */
167
167
  ignoreDestructuring: boolean;
168
+ /**
169
+ * @since 6.7.0
170
+ * @default false
171
+ */
172
+ ignoreImports: boolean;
173
+ /**
174
+ * @since 7.4.0
175
+ * @default false
176
+ */
177
+ ignoreGlobals: boolean;
168
178
  /**
169
179
  * @remarks
170
180
  * Also accept for regular expression patterns
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @fileoverview typings for "eslint/universal" module
3
+ * @author 唯然<weiran.zsd@outlook.com>
4
+ */
5
+
6
+ export { Linter } from "./index";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @fileoverview exports for browsers
3
+ * @author 唯然<weiran.zsd@outlook.com>
4
+ */
5
+
6
+ "use strict";
7
+
8
+ const { Linter } = require("./linter/linter");
9
+
10
+ module.exports = { Linter };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "9.10.0",
3
+ "version": "9.11.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {
@@ -20,6 +20,10 @@
20
20
  },
21
21
  "./rules": {
22
22
  "types": "./lib/types/rules/index.d.ts"
23
+ },
24
+ "./universal": {
25
+ "types": "./lib/types/universal.d.ts",
26
+ "default": "./lib/universal.js"
23
27
  }
24
28
  },
25
29
  "scripts": {
@@ -36,7 +40,8 @@
36
40
  "lint:fix:docs:js": "trunk check -y --ignore=** --ignore=!docs/**/*.js -a --flter=eslint && trunk check -y --ignore=** --ignore=!docs/**/*.js",
37
41
  "release:generate:alpha": "node Makefile.js generatePrerelease -- alpha",
38
42
  "release:generate:beta": "node Makefile.js generatePrerelease -- beta",
39
- "release:generate:latest": "node Makefile.js generateRelease",
43
+ "release:generate:latest": "node Makefile.js generateRelease -- latest",
44
+ "release:generate:maintenance": "node Makefile.js generateRelease -- maintenance",
40
45
  "release:generate:rc": "node Makefile.js generatePrerelease -- rc",
41
46
  "release:publish": "node Makefile.js publishRelease",
42
47
  "test": "node Makefile.js test",
@@ -81,8 +86,8 @@
81
86
  "@eslint-community/regexpp": "^4.11.0",
82
87
  "@eslint/config-array": "^0.18.0",
83
88
  "@eslint/eslintrc": "^3.1.0",
84
- "@eslint/js": "9.10.0",
85
- "@eslint/plugin-kit": "^0.1.0",
89
+ "@eslint/js": "9.11.0",
90
+ "@eslint/plugin-kit": "^0.2.0",
86
91
  "@humanwhocodes/module-importer": "^1.0.1",
87
92
  "@humanwhocodes/retry": "^0.3.0",
88
93
  "@nodelib/fs.walk": "^1.2.8",
@@ -115,12 +120,13 @@
115
120
  "devDependencies": {
116
121
  "@babel/core": "^7.4.3",
117
122
  "@babel/preset-env": "^7.4.3",
118
- "@eslint/core": "^0.5.0",
123
+ "@eslint/core": "^0.6.0",
119
124
  "@eslint/json": "^0.4.0",
120
125
  "@trunkio/launcher": "^1.3.0",
121
126
  "@types/estree": "^1.0.5",
122
127
  "@types/json-schema": "^7.0.15",
123
128
  "@types/node": "^20.11.5",
129
+ "@typescript-eslint/parser": "^8.4.0",
124
130
  "@wdio/browser-runner": "^9.0.5",
125
131
  "@wdio/cli": "^9.0.5",
126
132
  "@wdio/concise-reporter": "^9.0.4",
@@ -135,8 +141,9 @@
135
141
  "eslint": "file:.",
136
142
  "eslint-config-eslint": "file:packages/eslint-config-eslint",
137
143
  "eslint-plugin-eslint-plugin": "^6.0.0",
144
+ "eslint-plugin-expect-type": "^0.4.0",
138
145
  "eslint-plugin-yml": "^1.14.0",
139
- "eslint-release": "^3.2.2",
146
+ "eslint-release": "^3.3.0",
140
147
  "eslint-rule-composer": "^0.3.0",
141
148
  "eslump": "^3.0.0",
142
149
  "esprima": "^4.0.1",