eslint 10.0.0-alpha.1 → 10.0.0-rc.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
@@ -340,10 +340,10 @@ The following companies, organizations, and individuals support ESLint's ongoing
340
340
  to get your logo on our READMEs and [website](https://eslint.org/sponsors).
341
341
 
342
342
  <h3>Platinum Sponsors</h3>
343
- <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>
343
+ <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
344
  <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
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://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://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465" alt="Mercedes-Benz Group" 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>
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>
347
347
  <h3>Technology Sponsors</h3>
348
348
  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
349
  <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>
package/lib/api.js CHANGED
@@ -19,8 +19,8 @@ const { SourceCode } = require("./languages/js/source-code");
19
19
  //-----------------------------------------------------------------------------
20
20
 
21
21
  /**
22
- * Loads the correct ESLint constructor given the options.
23
- * @returns {Promise<ESLint>} The ESLint constructor
22
+ * Loads the correct `ESLint` constructor.
23
+ * @returns {Promise<ESLint>} The ESLint constructor.
24
24
  */
25
25
  async function loadESLint() {
26
26
  return ESLint;
@@ -4,14 +4,30 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
- const chalk = require("chalk"),
8
- util = require("node:util"),
7
+ const util = require("node:util"),
9
8
  table = require("../../shared/text-table");
10
9
 
11
10
  //------------------------------------------------------------------------------
12
11
  // Helpers
13
12
  //------------------------------------------------------------------------------
14
13
 
14
+ /**
15
+ * Returns a styling function based on the color option.
16
+ * @param {boolean|undefined} color Indicates whether to use colors.
17
+ * @returns {Function} A function that styles text.
18
+ */
19
+ function getStyleText(color) {
20
+ if (typeof color === "undefined") {
21
+ return (format, text) =>
22
+ util.styleText(format, text, { validateStream: true });
23
+ }
24
+ if (color) {
25
+ return (format, text) =>
26
+ util.styleText(format, text, { validateStream: false });
27
+ }
28
+ return (_, text) => text;
29
+ }
30
+
15
31
  /**
16
32
  * Given a word and a count, append an s if count is not one.
17
33
  * @param {string} word A word in its singular form.
@@ -26,7 +42,9 @@ function pluralize(word, count) {
26
42
  // Public Interface
27
43
  //------------------------------------------------------------------------------
28
44
 
29
- module.exports = function (results) {
45
+ module.exports = function (results, data) {
46
+ const styleText = getStyleText(data?.color);
47
+
30
48
  let output = "\n",
31
49
  errorCount = 0,
32
50
  warningCount = 0,
@@ -46,17 +64,17 @@ module.exports = function (results) {
46
64
  fixableErrorCount += result.fixableErrorCount;
47
65
  fixableWarningCount += result.fixableWarningCount;
48
66
 
49
- output += `${chalk.underline(result.filePath)}\n`;
67
+ output += `${styleText("underline", result.filePath)}\n`;
50
68
 
51
69
  output += `${table(
52
70
  messages.map(message => {
53
71
  let messageType;
54
72
 
55
73
  if (message.fatal || message.severity === 2) {
56
- messageType = chalk.red("error");
74
+ messageType = styleText("red", "error");
57
75
  summaryColor = "red";
58
76
  } else {
59
- messageType = chalk.yellow("warning");
77
+ messageType = styleText("yellow", "warning");
60
78
  }
61
79
 
62
80
  return [
@@ -65,7 +83,7 @@ module.exports = function (results) {
65
83
  String(message.column || 0),
66
84
  messageType,
67
85
  message.message.replace(/([^ ])\.$/u, "$1"),
68
- chalk.dim(message.ruleId || ""),
86
+ message.ruleId ? styleText("dim", message.ruleId) : "",
69
87
  ];
70
88
  }),
71
89
  {
@@ -78,7 +96,7 @@ module.exports = function (results) {
78
96
  .split("\n")
79
97
  .map(el =>
80
98
  el.replace(/(\d+)\s+(\d+)/u, (m, p1, p2) =>
81
- chalk.dim(`${p1}:${p2}`),
99
+ styleText("dim", `${p1}:${p2}`),
82
100
  ),
83
101
  )
84
102
  .join("\n")}\n\n`;
@@ -86,37 +104,50 @@ module.exports = function (results) {
86
104
 
87
105
  const total = errorCount + warningCount;
88
106
 
107
+ /*
108
+ * We can't use a single `styleText` call like `styleText([summaryColor, "bold"], text)` here.
109
+ * This is a bug in `util.styleText` in Node.js versions earlier than v22.15.0 (https://github.com/nodejs/node/issues/56717).
110
+ * As a workaround, we use nested `styleText` calls.
111
+ */
89
112
  if (total > 0) {
90
- output += chalk[summaryColor].bold(
91
- [
92
- "\u2716 ",
93
- total,
94
- pluralize(" problem", total),
95
- " (",
96
- errorCount,
97
- pluralize(" error", errorCount),
98
- ", ",
99
- warningCount,
100
- pluralize(" warning", warningCount),
101
- ")\n",
102
- ].join(""),
103
- );
104
-
105
- if (fixableErrorCount > 0 || fixableWarningCount > 0) {
106
- output += chalk[summaryColor].bold(
113
+ output += `${styleText(
114
+ summaryColor,
115
+ styleText(
116
+ "bold",
107
117
  [
108
- " ",
109
- fixableErrorCount,
110
- pluralize(" error", fixableErrorCount),
111
- " and ",
112
- fixableWarningCount,
113
- pluralize(" warning", fixableWarningCount),
114
- " potentially fixable with the `--fix` option.\n",
118
+ "\u2716 ",
119
+ total,
120
+ pluralize(" problem", total),
121
+ " (",
122
+ errorCount,
123
+ pluralize(" error", errorCount),
124
+ ", ",
125
+ warningCount,
126
+ pluralize(" warning", warningCount),
127
+ ")",
115
128
  ].join(""),
116
- );
129
+ ),
130
+ )}\n`;
131
+
132
+ if (fixableErrorCount > 0 || fixableWarningCount > 0) {
133
+ output += `${styleText(
134
+ summaryColor,
135
+ styleText(
136
+ "bold",
137
+ [
138
+ " ",
139
+ fixableErrorCount,
140
+ pluralize(" error", fixableErrorCount),
141
+ " and ",
142
+ fixableWarningCount,
143
+ pluralize(" warning", fixableWarningCount),
144
+ " potentially fixable with the `--fix` option.",
145
+ ].join(""),
146
+ ),
147
+ )}\n`;
117
148
  }
118
149
  }
119
150
 
120
151
  // Resets output color, for prevent change on top level
121
- return total > 0 ? chalk.reset(output) : "";
152
+ return total > 0 ? styleText("reset", output) : "";
122
153
  };
package/lib/cli.js CHANGED
@@ -454,14 +454,24 @@ const cli = {
454
454
  const tooManyWarnings =
455
455
  options.maxWarnings >= 0 &&
456
456
  resultCounts.warningCount > options.maxWarnings;
457
- const resultsMeta = tooManyWarnings
458
- ? {
459
- maxWarningsExceeded: {
460
- maxWarnings: options.maxWarnings,
461
- foundWarnings: resultCounts.warningCount,
462
- },
463
- }
464
- : {};
457
+ const resultsMeta = /** @type {ResultsMeta} */ ({});
458
+
459
+ /*
460
+ * `--color` was set, `options.color` is `true`.
461
+ * `--no-color` was set, `options.color` is `false`.
462
+ * Neither option was provided, `options.color` is omitted, so `undefined`.
463
+ */
464
+ if (options.color !== void 0) {
465
+ debug(`Color setting for output: ${options.color}`);
466
+ resultsMeta.color = options.color;
467
+ }
468
+
469
+ if (tooManyWarnings) {
470
+ resultsMeta.maxWarningsExceeded = {
471
+ maxWarnings: options.maxWarnings,
472
+ foundWarnings: resultCounts.warningCount,
473
+ };
474
+ }
465
475
 
466
476
  if (
467
477
  await printResults(
@@ -1207,6 +1207,7 @@ class ESLint {
1207
1207
  const { cwd } = privateMembers.get(this).options;
1208
1208
 
1209
1209
  let formatterPath;
1210
+ let isBuiltInFormatter = false;
1210
1211
 
1211
1212
  // if there's a slash, then it's a file (TODO: this check seems dubious for scoped npm packages)
1212
1213
  if (!namespace && normalizedFormatName.includes("/")) {
@@ -1228,6 +1229,7 @@ class ESLint {
1228
1229
  "formatters",
1229
1230
  `${normalizedFormatName}.js`,
1230
1231
  );
1232
+ isBuiltInFormatter = true;
1231
1233
  }
1232
1234
  }
1233
1235
 
@@ -1237,7 +1239,7 @@ class ESLint {
1237
1239
  formatter = (await import(pathToFileURL(formatterPath))).default;
1238
1240
  } catch (ex) {
1239
1241
  // check for formatters that have been removed
1240
- if (removedFormatters.has(name)) {
1242
+ if (isBuiltInFormatter && removedFormatters.has(name)) {
1241
1243
  ex.message = `The ${name} formatter is no longer part of core ESLint. Install it manually with \`npm install -D eslint-formatter-${name}\``;
1242
1244
  } else {
1243
1245
  ex.message = `There was a problem loading formatter: ${formatterPath}\nError: ${ex.message}`;
@@ -1344,7 +1346,7 @@ class ESLint {
1344
1346
 
1345
1347
  /**
1346
1348
  * Returns whether flat config should be used.
1347
- * @returns {Promise<boolean>} Whether flat config should be used.
1349
+ * @returns {Promise<true>} Whether flat config should be used.
1348
1350
  */
1349
1351
  async function shouldUseFlatConfig() {
1350
1352
  return true;
@@ -55,6 +55,7 @@ function analyzeScope(ast, languageOptions, visitorKeys) {
55
55
  sourceType: languageOptions.sourceType || "script",
56
56
  childVisitorKeys: visitorKeys || evk.KEYS,
57
57
  fallback: evk.getKeys,
58
+ jsx: ecmaFeatures.jsx,
58
59
  });
59
60
  }
60
61
 
@@ -248,7 +249,6 @@ module.exports = {
248
249
  {
249
250
  loc: true,
250
251
  range: true,
251
- raw: true,
252
252
  tokens: true,
253
253
  comment: true,
254
254
  eslintVisitorKeys: true,
@@ -54,10 +54,7 @@ const { FileReport, updateLocationInformation } = require("./file-report");
54
54
 
55
55
  /** @import { Language, LanguageOptions, RuleConfig, RuleDefinition } from "@eslint/core" */
56
56
 
57
- /** @typedef {import("../types").Linter.Config} Config */
58
- /** @typedef {import("../types").ESLint.ConfigData} ConfigData */
59
- /** @typedef {import("../types").ESLint.Environment} Environment */
60
- /** @typedef {import("../types").Linter.GlobalConf} GlobalConf */
57
+ /** @typedef {import("../types").Linter.Config} ConfigObject */
61
58
  /** @typedef {import("../types").Linter.LanguageOptions} JSLanguageOptions */
62
59
  /** @typedef {import("../types").Linter.LintMessage} LintMessage */
63
60
  /** @typedef {import("../types").Linter.Parser} Parser */
@@ -87,7 +84,7 @@ const { FileReport, updateLocationInformation } = require("./file-report");
87
84
  /**
88
85
  * The private data for `Linter` instance.
89
86
  * @typedef {Object} LinterInternalSlots
90
- * @property {ConfigArray|null} lastConfigArray The `ConfigArray` instance that the last `verify()` call used.
87
+ * @property {FlatConfigArray|null} lastConfigArray The `ConfigArray` instance that the last `verify()` call used.
91
88
  * @property {SourceCode|null} lastSourceCode The `SourceCode` instance that the last `verify()` call used.
92
89
  * @property {SuppressedLintMessage[]} lastSuppressedMessages The `SuppressedLintMessage[]` instance that the last `verify()` call produced.
93
90
  * @property {{ passes: TimePass[]; }} times The times spent on applying a rule to a file (see `stats` option).
@@ -349,7 +346,7 @@ function normalizeFilename(filename) {
349
346
  * Normalizes the possible options for `linter.verify` and `linter.verifyAndFix` to a
350
347
  * consistent shape.
351
348
  * @param {VerifyOptions} providedOptions Options
352
- * @param {Config|ConfigData} config Config.
349
+ * @param {Config} config Config.
353
350
  * @returns {Required<VerifyOptions> & InternalOptions} Normalized options
354
351
  */
355
352
  function normalizeVerifyOptions(providedOptions, config) {
@@ -473,6 +470,7 @@ function analyzeScope(ast, languageOptions, visitorKeys) {
473
470
  sourceType: languageOptions.sourceType || "script",
474
471
  childVisitorKeys: visitorKeys || evk.KEYS,
475
472
  fallback: Traverser.getKeys,
473
+ jsx: ecmaFeatures.jsx,
476
474
  });
477
475
  }
478
476
 
@@ -822,7 +820,7 @@ class Linter {
822
820
  /**
823
821
  * Verifies the text against the rules specified by the second argument.
824
822
  * @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
825
- * @param {ConfigData|ConfigArray} config An ESLintConfig instance to configure everything.
823
+ * @param {ConfigObject|ConfigObject[]} config The ESLint config object or array to use.
826
824
  * @param {(string|(VerifyOptions&ProcessorOptions))} [filenameOrOptions] The optional filename of the file being checked.
827
825
  * If this is not set, the filename will default to '<input>' in the rule context. If
828
826
  * an object, then it has "filename", "allowInlineConfig", and some properties.
@@ -1466,12 +1464,12 @@ class Linter {
1466
1464
  * Performs multiple autofix passes over the text until as many fixes as possible
1467
1465
  * have been applied.
1468
1466
  * @param {string} text The source text to apply fixes to.
1469
- * @param {ConfigData|ConfigArray|FlatConfigArray} config The ESLint config object to use.
1470
- * @param {VerifyOptions&ProcessorOptions&FixOptions} options The ESLint options object to use.
1467
+ * @param {ConfigObject|ConfigObject[]} config The ESLint config object or array to use.
1468
+ * @param {string|(VerifyOptions&ProcessorOptions&FixOptions)} [filenameOrOptions] The filename or ESLint options object to use.
1471
1469
  * @returns {{fixed:boolean,messages:LintMessage[],output:string}} The result of the fix operation as returned from the
1472
1470
  * SourceCodeFixer.
1473
1471
  */
1474
- verifyAndFix(text, config, options) {
1472
+ verifyAndFix(text, config, filenameOrOptions) {
1475
1473
  let messages,
1476
1474
  fixedResult,
1477
1475
  fixed = false,
@@ -1479,10 +1477,14 @@ class Linter {
1479
1477
  currentText = text,
1480
1478
  secondPreviousText,
1481
1479
  previousText;
1480
+ const options =
1481
+ typeof filenameOrOptions === "string"
1482
+ ? { filename: filenameOrOptions }
1483
+ : filenameOrOptions || {};
1482
1484
  const debugTextDescription =
1483
- (options && options.filename) || `${text.slice(0, 10)}...`;
1485
+ options.filename || `${text.slice(0, 10)}...`;
1484
1486
  const shouldFix =
1485
- options && typeof options.fix !== "undefined" ? options.fix : true;
1487
+ typeof options.fix !== "undefined" ? options.fix : true;
1486
1488
  const stats = options?.stats;
1487
1489
 
1488
1490
  const slots = internalSlotsMap.get(this);
@@ -1574,7 +1576,7 @@ class Linter {
1574
1576
  `Circular fixes detected after pass ${passNumber}. Exiting fix loop.`,
1575
1577
  );
1576
1578
  slots.warningService.emitCircularFixesWarning(
1577
- options?.filename ?? "text",
1579
+ options.filename ?? "text",
1578
1580
  );
1579
1581
  break;
1580
1582
  }
@@ -1609,13 +1611,4 @@ class Linter {
1609
1611
 
1610
1612
  module.exports = {
1611
1613
  Linter,
1612
-
1613
- /**
1614
- * Get the internal slots of a given Linter instance for tests.
1615
- * @param {Linter} instance The Linter instance to get.
1616
- * @returns {LinterInternalSlots} The internal slots.
1617
- */
1618
- getLinterInternalSlots(instance) {
1619
- return internalSlotsMap.get(instance);
1620
- },
1621
1614
  };