eslint 9.33.0 → 9.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/lib/cli-engine/file-enumerator.js +1 -1
  3. package/lib/cli.js +62 -266
  4. package/lib/config/flat-config-schema.js +1 -1
  5. package/lib/eslint/eslint-helpers.js +426 -6
  6. package/lib/eslint/eslint.js +381 -313
  7. package/lib/eslint/worker.js +164 -0
  8. package/lib/languages/js/source-code/source-code.js +3 -4
  9. package/lib/linter/esquery.js +3 -0
  10. package/lib/linter/interpolate.js +1 -1
  11. package/lib/linter/linter.js +3 -4
  12. package/lib/options.js +23 -9
  13. package/lib/rule-tester/rule-tester.js +3 -0
  14. package/lib/rules/array-callback-return.js +0 -1
  15. package/lib/rules/dot-notation.js +1 -1
  16. package/lib/rules/grouped-accessor-pairs.js +8 -7
  17. package/lib/rules/indent-legacy.js +1 -1
  18. package/lib/rules/index.js +1 -0
  19. package/lib/rules/no-alert.js +1 -1
  20. package/lib/rules/no-empty-function.js +20 -1
  21. package/lib/rules/no-empty-static-block.js +25 -1
  22. package/lib/rules/no-empty.js +37 -0
  23. package/lib/rules/no-eval.js +3 -1
  24. package/lib/rules/no-irregular-whitespace.js +2 -2
  25. package/lib/rules/no-loss-of-precision.js +26 -3
  26. package/lib/rules/no-mixed-spaces-and-tabs.js +1 -0
  27. package/lib/rules/no-octal.js +1 -4
  28. package/lib/rules/no-trailing-spaces.js +2 -1
  29. package/lib/rules/no-useless-escape.js +1 -1
  30. package/lib/rules/prefer-regex-literals.js +1 -1
  31. package/lib/rules/preserve-caught-error.js +509 -0
  32. package/lib/rules/strict.js +2 -1
  33. package/lib/rules/utils/ast-utils.js +1 -1
  34. package/lib/rules/utils/char-source.js +1 -1
  35. package/lib/rules/yoda.js +2 -2
  36. package/lib/services/suppressions-service.js +3 -0
  37. package/lib/services/warning-service.js +13 -0
  38. package/lib/shared/naming.js +1 -1
  39. package/lib/shared/translate-cli-options.js +281 -0
  40. package/lib/types/index.d.ts +7 -0
  41. package/lib/types/rules.d.ts +22 -14
  42. package/package.json +3 -3
@@ -0,0 +1,164 @@
1
+ /**
2
+ * @fileoverview Worker thread for multithread linting.
3
+ * @author Francesco Trotta
4
+ */
5
+
6
+ "use strict";
7
+
8
+ const hrtimeBigint = process.hrtime.bigint;
9
+
10
+ const startTime = hrtimeBigint();
11
+
12
+ // eslint-disable-next-line n/no-unsupported-features/node-builtins -- enable V8's code cache if supported
13
+ require("node:module").enableCompileCache?.();
14
+
15
+ //------------------------------------------------------------------------------
16
+ // Requirements
17
+ //------------------------------------------------------------------------------
18
+
19
+ const { parentPort, threadId, workerData } = require("node:worker_threads");
20
+ const createDebug = require("debug");
21
+ const {
22
+ createConfigLoader,
23
+ createDefaultConfigs,
24
+ createLinter,
25
+ createLintResultCache,
26
+ getCacheFile,
27
+ lintFile,
28
+ loadOptionsFromModule,
29
+ processOptions,
30
+ } = require("./eslint-helpers");
31
+ const { WarningService } = require("../services/warning-service");
32
+
33
+ const depsLoadedTime = hrtimeBigint();
34
+
35
+ //------------------------------------------------------------------------------
36
+ // Typedefs
37
+ //------------------------------------------------------------------------------
38
+
39
+ /** @typedef {import("../types").ESLint.LintResult} LintResult */
40
+ /** @typedef {import("../types").ESLint.Options} ESLintOptions */
41
+ /** @typedef {LintResult & { index?: number; }} IndexedLintResult */
42
+ /** @typedef {IndexedLintResult[] & { netLintingDuration: bigint; }} WorkerLintResults */
43
+ /**
44
+ * @typedef {Object} WorkerData - Data passed to the worker thread.
45
+ * @property {ESLintOptions | string} eslintOptionsOrURL - The unprocessed ESLint options or the URL of the options module.
46
+ * @property {Uint32Array<SharedArrayBuffer>} filePathIndexArray - Shared counter used to track the next file to lint.
47
+ * @property {string[]} filePaths - File paths to lint.
48
+ */
49
+
50
+ //------------------------------------------------------------------------------
51
+ // Helpers
52
+ //------------------------------------------------------------------------------
53
+
54
+ const debug = createDebug(`eslint:worker:thread-${threadId}`);
55
+ createDebug.formatters.t = timeDiff =>
56
+ `${(timeDiff + 500_000n) / 1_000_000n} ms`;
57
+
58
+ //------------------------------------------------------------------------------
59
+ // Main
60
+ //------------------------------------------------------------------------------
61
+
62
+ debug("Dependencies loaded in %t", depsLoadedTime - startTime);
63
+
64
+ (async () => {
65
+ /** @type {WorkerData} */
66
+ const { eslintOptionsOrURL, filePathIndexArray, filePaths } = workerData;
67
+ const eslintOptions =
68
+ typeof eslintOptionsOrURL === "object"
69
+ ? eslintOptionsOrURL
70
+ : await loadOptionsFromModule(eslintOptionsOrURL);
71
+ const processedESLintOptions = processOptions(eslintOptions);
72
+
73
+ const warningService = new WarningService();
74
+
75
+ // These warnings are always emitted by the controlling thread.
76
+ warningService.emitEmptyConfigWarning =
77
+ warningService.emitInactiveFlagWarning = () => {};
78
+
79
+ const linter = createLinter(processedESLintOptions, warningService);
80
+
81
+ const cacheFilePath = getCacheFile(
82
+ processedESLintOptions.cacheLocation,
83
+ processedESLintOptions.cwd,
84
+ );
85
+
86
+ const lintResultCache = createLintResultCache(
87
+ processedESLintOptions,
88
+ cacheFilePath,
89
+ );
90
+ const defaultConfigs = createDefaultConfigs(eslintOptions.plugins);
91
+
92
+ const configLoader = createConfigLoader(
93
+ processedESLintOptions,
94
+ defaultConfigs,
95
+ linter,
96
+ warningService,
97
+ );
98
+
99
+ /** @type {WorkerLintResults} */
100
+ const indexedResults = [];
101
+ let loadConfigTotalDuration = 0n;
102
+ const readFileCounter = { duration: 0n };
103
+
104
+ const lintingStartTime = hrtimeBigint();
105
+ debug(
106
+ "Linting started %t after dependencies loaded",
107
+ lintingStartTime - depsLoadedTime,
108
+ );
109
+
110
+ for (;;) {
111
+ const fileLintingStartTime = hrtimeBigint();
112
+
113
+ // It seems hard to produce an arithmetic overflow under realistic conditions here.
114
+ const index = Atomics.add(filePathIndexArray, 0, 1);
115
+
116
+ const filePath = filePaths[index];
117
+ if (!filePath) {
118
+ break;
119
+ }
120
+
121
+ const loadConfigEnterTime = hrtimeBigint();
122
+ const configs = await configLoader.loadConfigArrayForFile(filePath);
123
+ const loadConfigExitTime = hrtimeBigint();
124
+ const loadConfigDuration = loadConfigExitTime - loadConfigEnterTime;
125
+ debug(
126
+ 'Config array for file "%s" loaded in %t',
127
+ filePath,
128
+ loadConfigDuration,
129
+ );
130
+ loadConfigTotalDuration += loadConfigDuration;
131
+
132
+ /** @type {IndexedLintResult} */
133
+ const result = await lintFile(
134
+ filePath,
135
+ configs,
136
+ processedESLintOptions,
137
+ linter,
138
+ lintResultCache,
139
+ readFileCounter,
140
+ );
141
+ if (result) {
142
+ result.index = index;
143
+ indexedResults.push(result);
144
+ }
145
+
146
+ const fileLintingEndTime = hrtimeBigint();
147
+ debug(
148
+ 'File "%s" processed in %t',
149
+ filePath,
150
+ fileLintingEndTime - fileLintingStartTime,
151
+ );
152
+ }
153
+
154
+ const lintingDuration = hrtimeBigint() - lintingStartTime;
155
+
156
+ /*
157
+ * The net linting duration is the total linting time minus the time spent loading configs and reading files.
158
+ * It captures the processing time dedicated to computation-intensive tasks that are highly parallelizable and not repeated across threads.
159
+ */
160
+ indexedResults.netLintingDuration =
161
+ lintingDuration - loadConfigTotalDuration - readFileCounter.duration;
162
+
163
+ parentPort.postMessage(indexedResults);
164
+ })();
@@ -1056,7 +1056,7 @@ class SourceCode extends TokenStore {
1056
1056
  // only certain comment types are supported as line comments
1057
1057
  return (
1058
1058
  comment.type !== "Line" ||
1059
- !!/^eslint-disable-(next-)?line$/u.test(directive.label)
1059
+ !!/^eslint-disable-(?:next-)?line$/u.test(directive.label)
1060
1060
  );
1061
1061
  });
1062
1062
 
@@ -1091,9 +1091,8 @@ class SourceCode extends TokenStore {
1091
1091
  } = commentParser.parseDirective(comment.value);
1092
1092
 
1093
1093
  // Step 2: Extract the directive value
1094
- const lineCommentSupported = /^eslint-disable-(next-)?line$/u.test(
1095
- label,
1096
- );
1094
+ const lineCommentSupported =
1095
+ /^eslint-disable-(?:next-)?line$/u.test(label);
1097
1096
 
1098
1097
  if (comment.type === "Line" && !lineCommentSupported) {
1099
1098
  return;
@@ -271,6 +271,9 @@ function tryParseSelector(selector) {
271
271
  ) {
272
272
  throw new SyntaxError(
273
273
  `Syntax error in selector "${selector}" at position ${err.location.start.offset}: ${err.message}`,
274
+ {
275
+ cause: err,
276
+ },
274
277
  );
275
278
  }
276
279
  throw err;
@@ -14,7 +14,7 @@
14
14
  * @returns {RegExp} Global regular expression matching placeholders
15
15
  */
16
16
  function getPlaceholderMatcher() {
17
- return /\{\{([^{}]+?)\}\}/gu;
17
+ return /\{\{([^{}]+)\}\}/gu;
18
18
  }
19
19
 
20
20
  /**
@@ -438,9 +438,8 @@ function getDirectiveComments(
438
438
  justification: justificationPart,
439
439
  } = directive;
440
440
 
441
- const lineCommentSupported = /^eslint-disable-(next-)?line$/u.test(
442
- label,
443
- );
441
+ const lineCommentSupported =
442
+ /^eslint-disable-(?:next-)?line$/u.test(label);
444
443
 
445
444
  if (comment.type === "Line" && !lineCommentSupported) {
446
445
  return;
@@ -739,7 +738,7 @@ function normalizeEcmaVersionForLanguageOptions(ecmaVersion) {
739
738
  return LATEST_ECMA_VERSION;
740
739
  }
741
740
 
742
- const eslintEnvPattern = /\/\*\s*eslint-env\s(.+?)(?:\*\/|$)/gsu;
741
+ const eslintEnvPattern = /\/\*\s*eslint-env\s.+?(?:\*\/|$)/gsu;
743
742
 
744
743
  /**
745
744
  * Checks whether or not there is a comment which has "eslint-env *" in a given text.
package/lib/options.js CHANGED
@@ -23,6 +23,7 @@ const optionator = require("optionator");
23
23
  * @property {string} [cacheLocation] Path to the cache file or directory
24
24
  * @property {"metadata" | "content"} cacheStrategy Strategy to use for detecting changed files in the cache
25
25
  * @property {boolean} [color] Force enabling/disabling of color
26
+ * @property {number | "auto" | "off"} [concurrency] Number of linting threads, "auto" to choose automatically, "off" for no multithreading
26
27
  * @property {string} [config] Use this configuration, overriding .eslintrc.* config options if present
27
28
  * @property {boolean} debug Output debugging information
28
29
  * @property {string[]} [env] Specify environments
@@ -46,27 +47,27 @@ const optionator = require("optionator");
46
47
  * @property {string} [outputFile] Specify file to write report to
47
48
  * @property {string} [parser] Specify the parser to be used
48
49
  * @property {Object} [parserOptions] Specify parser options
50
+ * @property {boolean} [passOnNoPatterns=false] When set to true, missing patterns cause
51
+ * the linting operation to short circuit and not report any failures.
52
+ * @property {boolean} [passOnUnprunedSuppressions] Ignore unused suppressions
49
53
  * @property {string[]} [plugin] Specify plugins
50
54
  * @property {string} [printConfig] Print the configuration for the given file
55
+ * @property {boolean} [pruneSuppressions] Prune unused suppressions
56
+ * @property {boolean} quiet Report errors only
51
57
  * @property {boolean | undefined} reportUnusedDisableDirectives Adds reported errors for unused eslint-disable and eslint-enable directives
52
58
  * @property {string | undefined} reportUnusedDisableDirectivesSeverity A severity string indicating if and how unused disable and enable directives should be tracked and reported.
53
59
  * @property {string} [resolvePluginsRelativeTo] A folder where plugins should be resolved from, CWD by default
54
60
  * @property {Object} [rule] Specify rules
55
61
  * @property {string[]} [rulesdir] Load additional rules from this directory. Deprecated: Use rules from plugins
62
+ * @property {boolean} [stats] Report additional statistics
56
63
  * @property {boolean} stdin Lint code provided on <STDIN>
57
64
  * @property {string} [stdinFilename] Specify filename to process STDIN as
58
- * @property {boolean} quiet Report errors only
65
+ * @property {boolean} [suppressAll] Suppress all error violations
66
+ * @property {string} [suppressionsLocation] Path to the suppressions file or directory
67
+ * @property {string[]} [suppressRule] Suppress specific rules
59
68
  * @property {boolean} [version] Output the version number
60
69
  * @property {boolean} warnIgnored Show warnings when the file list includes ignored files
61
- * @property {boolean} [passOnNoPatterns=false] When set to true, missing patterns cause
62
- * the linting operation to short circuit and not report any failures.
63
70
  * @property {string[]} _ Positional filenames or patterns
64
- * @property {boolean} [stats] Report additional statistics
65
- * @property {boolean} [suppressAll] Suppress all error violations
66
- * @property {string[]} [suppressRule] Suppress specific rules
67
- * @property {string} [suppressionsLocation] Path to the suppressions file or directory
68
- * @property {boolean} [pruneSuppressions] Prune unused suppressions
69
- * @property {boolean} [passOnUnprunedSuppressions] Ignore unused suppressions
70
71
  */
71
72
 
72
73
  //------------------------------------------------------------------------------
@@ -224,6 +225,18 @@ module.exports = function (usingFlatConfig) {
224
225
  };
225
226
  }
226
227
 
228
+ let concurrencyFlag;
229
+
230
+ if (usingFlatConfig) {
231
+ concurrencyFlag = {
232
+ option: "concurrency",
233
+ type: "Int|String",
234
+ default: "off",
235
+ description:
236
+ "Number of linting threads, auto to choose automatically, off for no multithreading",
237
+ };
238
+ }
239
+
227
240
  return optionator({
228
241
  prepend: "eslint [options] file.js [file.js] [dir]",
229
242
  defaults: {
@@ -517,6 +530,7 @@ module.exports = function (usingFlatConfig) {
517
530
  statsFlag,
518
531
  flagFlag,
519
532
  mcpFlag,
533
+ concurrencyFlag,
520
534
  ].filter(value => !!value),
521
535
  });
522
536
  };
@@ -851,6 +851,9 @@ class RuleTester {
851
851
  } catch (err) {
852
852
  throw new Error(
853
853
  `Schema for rule ${ruleName} is invalid: ${err.message}`,
854
+ {
855
+ cause: err,
856
+ },
854
857
  );
855
858
  }
856
859
  }
@@ -233,7 +233,6 @@ module.exports = {
233
233
  url: "https://eslint.org/docs/latest/rules/array-callback-return",
234
234
  },
235
235
 
236
- // eslint-disable-next-line eslint-plugin/require-meta-has-suggestions -- false positive
237
236
  hasSuggestions: true,
238
237
 
239
238
  schema: [
@@ -15,7 +15,7 @@ const keywords = require("./utils/keywords");
15
15
  // Rule Definition
16
16
  //------------------------------------------------------------------------------
17
17
 
18
- const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/u;
18
+ const validIdentifier = /^[a-zA-Z_$][\w$]*$/u;
19
19
 
20
20
  // `null` literal must be handled separately.
21
21
  const literalTypesToCheck = new Set(["string", "boolean"]);
@@ -87,8 +87,6 @@ function isAccessorKind(node) {
87
87
  return node.kind === "get" || node.kind === "set";
88
88
  }
89
89
 
90
- const DEFAULT_ORDER = "anyOrder";
91
-
92
90
  //------------------------------------------------------------------------------
93
91
  // Rule Definition
94
92
  //------------------------------------------------------------------------------
@@ -98,7 +96,12 @@ module.exports = {
98
96
  meta: {
99
97
  type: "suggestion",
100
98
 
101
- defaultOptions: [DEFAULT_ORDER],
99
+ defaultOptions: [
100
+ "anyOrder",
101
+ {
102
+ enforceForTSTypes: false,
103
+ },
104
+ ],
102
105
 
103
106
  docs: {
104
107
  description:
@@ -129,10 +132,8 @@ module.exports = {
129
132
  },
130
133
 
131
134
  create(context) {
132
- const order = context.options[0] ?? DEFAULT_ORDER;
133
- const enforceForTSTypes =
134
- context.options[1]?.enforceForTSTypes ?? false;
135
- const sourceCode = context.sourceCode;
135
+ const [order, { enforceForTSTypes }] = context.options;
136
+ const { sourceCode } = context;
136
137
 
137
138
  /**
138
139
  * Reports the given accessor pair.
@@ -1162,7 +1162,7 @@ module.exports = {
1162
1162
  * @returns {boolean} the result
1163
1163
  */
1164
1164
  function isWrappedInParenthesis(node) {
1165
- const regex = /^return\s*?\(\s*?\);*?/u;
1165
+ const regex = /^return\s*\(\s*\)/u;
1166
1166
 
1167
1167
  const statementWithoutArgument = sourceCode
1168
1168
  .getText(node)
@@ -293,6 +293,7 @@ module.exports = new LazyLoadingRuleMap(
293
293
  "prefer-rest-params": () => require("./prefer-rest-params"),
294
294
  "prefer-spread": () => require("./prefer-spread"),
295
295
  "prefer-template": () => require("./prefer-template"),
296
+ "preserve-caught-error": () => require("./preserve-caught-error"),
296
297
  "quote-props": () => require("./quote-props"),
297
298
  quotes: () => require("./quotes"),
298
299
  radix: () => require("./radix"),
@@ -24,7 +24,7 @@ const {
24
24
  * @returns {boolean} Whether or not the name is prohibited.
25
25
  */
26
26
  function isProhibitedIdentifier(name) {
27
- return /^(alert|confirm|prompt)$/u.test(name);
27
+ return /^(?:alert|confirm|prompt)$/u.test(name);
28
28
  }
29
29
 
30
30
  /**
@@ -105,6 +105,7 @@ module.exports = {
105
105
  meta: {
106
106
  dialects: ["javascript", "typescript"],
107
107
  language: "javascript",
108
+ hasSuggestions: true,
108
109
  type: "suggestion",
109
110
 
110
111
  defaultOptions: [{ allow: [] }],
@@ -131,6 +132,7 @@ module.exports = {
131
132
 
132
133
  messages: {
133
134
  unexpected: "Unexpected empty {{name}}.",
135
+ suggestComment: "Add comment inside empty {{name}}.",
134
136
  },
135
137
  },
136
138
 
@@ -162,7 +164,7 @@ module.exports = {
162
164
  }
163
165
  }
164
166
 
165
- if (/(g|s)etters|methods$/iu.test(kind)) {
167
+ if (/(?:g|s)etters|methods$/iu.test(kind)) {
166
168
  if (
167
169
  (node.parent.decorators?.length &&
168
170
  allow.includes("decoratedFunctions")) ||
@@ -204,6 +206,23 @@ module.exports = {
204
206
  loc: node.body.loc,
205
207
  messageId: "unexpected",
206
208
  data: { name },
209
+ suggest: [
210
+ {
211
+ messageId: "suggestComment",
212
+ data: { name },
213
+ fix(fixer) {
214
+ const range = [
215
+ node.body.range[0] + 1,
216
+ node.body.range[1] - 1,
217
+ ];
218
+
219
+ return fixer.replaceTextRange(
220
+ range,
221
+ " /* empty */ ",
222
+ );
223
+ },
224
+ },
225
+ ],
207
226
  });
208
227
  }
209
228
  }
@@ -11,6 +11,7 @@
11
11
  /** @type {import('../types').Rule.RuleModule} */
12
12
  module.exports = {
13
13
  meta: {
14
+ hasSuggestions: true,
14
15
  type: "suggestion",
15
16
 
16
17
  docs: {
@@ -23,6 +24,7 @@ module.exports = {
23
24
 
24
25
  messages: {
25
26
  unexpected: "Unexpected empty static block.",
27
+ suggestComment: "Add comment inside empty static block.",
26
28
  },
27
29
  },
28
30
 
@@ -32,14 +34,36 @@ module.exports = {
32
34
  return {
33
35
  StaticBlock(node) {
34
36
  if (node.body.length === 0) {
37
+ const openingBrace = sourceCode.getFirstToken(node, {
38
+ skip: 1,
39
+ });
35
40
  const closingBrace = sourceCode.getLastToken(node);
36
41
 
37
42
  if (
38
43
  sourceCode.getCommentsBefore(closingBrace).length === 0
39
44
  ) {
40
45
  context.report({
41
- node,
46
+ loc: {
47
+ start: openingBrace.loc.start,
48
+ end: closingBrace.loc.end,
49
+ },
42
50
  messageId: "unexpected",
51
+ suggest: [
52
+ {
53
+ messageId: "suggestComment",
54
+ fix(fixer) {
55
+ const range = [
56
+ openingBrace.range[1],
57
+ closingBrace.range[0],
58
+ ];
59
+
60
+ return fixer.replaceTextRange(
61
+ range,
62
+ " /* empty */ ",
63
+ );
64
+ },
65
+ },
66
+ ],
43
67
  });
44
68
  }
45
69
  }
@@ -104,10 +104,47 @@ module.exports = {
104
104
  typeof node.cases === "undefined" ||
105
105
  node.cases.length === 0
106
106
  ) {
107
+ const openingBrace = sourceCode.getTokenAfter(
108
+ node.discriminant,
109
+ astUtils.isOpeningBraceToken,
110
+ );
111
+
112
+ const closingBrace = sourceCode.getLastToken(node);
113
+
114
+ if (
115
+ sourceCode.commentsExistBetween(
116
+ openingBrace,
117
+ closingBrace,
118
+ )
119
+ ) {
120
+ return;
121
+ }
122
+
107
123
  context.report({
108
124
  node,
125
+ loc: {
126
+ start: openingBrace.loc.start,
127
+ end: closingBrace.loc.end,
128
+ },
109
129
  messageId: "unexpected",
110
130
  data: { type: "switch" },
131
+ suggest: [
132
+ {
133
+ messageId: "suggestComment",
134
+ data: { type: "switch" },
135
+ fix(fixer) {
136
+ const range = [
137
+ openingBrace.range[1],
138
+ closingBrace.range[0],
139
+ ];
140
+
141
+ return fixer.replaceTextRange(
142
+ range,
143
+ " /* empty */ ",
144
+ );
145
+ },
146
+ },
147
+ ],
111
148
  });
112
149
  }
113
150
  },
@@ -226,7 +226,9 @@ module.exports = {
226
226
 
227
227
  Program(node) {
228
228
  const scope = sourceCode.getScope(node),
229
- features = context.parserOptions.ecmaFeatures || {},
229
+ features =
230
+ context.languageOptions.parserOptions.ecmaFeatures ||
231
+ {},
230
232
  strict =
231
233
  scope.isStrict ||
232
234
  node.sourceType === "module" ||
@@ -19,8 +19,8 @@ const astUtils = require("./utils/ast-utils");
19
19
  const ALL_IRREGULARS =
20
20
  /[\f\v\u0085\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000\u2028\u2029]/u;
21
21
  const IRREGULAR_WHITESPACE =
22
- /[\f\v\u0085\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000]+/gmu;
23
- const IRREGULAR_LINE_TERMINATORS = /[\u2028\u2029]/gmu;
22
+ /[\f\v\u0085\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000]+/gu;
23
+ const IRREGULAR_LINE_TERMINATORS = /[\u2028\u2029]/gu;
24
24
  const LINE_BREAK = astUtils.createGlobalLinebreakMatcher();
25
25
 
26
26
  //------------------------------------------------------------------------------
@@ -189,9 +189,32 @@ module.exports = {
189
189
  * @returns {boolean} true if they do not match
190
190
  */
191
191
  function baseTenLosesPrecision(node) {
192
- const normalizedRawNumber = convertNumberToScientificNotation(
193
- getRaw(node),
194
- );
192
+ const rawNumber = getRaw(node).toLowerCase();
193
+
194
+ /*
195
+ * If trailing zeros equal the exponent, this is a valid representation
196
+ * like "9.00e2" where 00 = 2 (meaning 9.00 * 10^2 = 900)
197
+ * https://github.com/eslint/eslint/issues/19957
198
+ */
199
+ if (rawNumber.includes(".") && rawNumber.includes("e")) {
200
+ const parts = rawNumber.split("e");
201
+ const coefficient = parts[0];
202
+ const exponent = parseInt(parts[1], 10);
203
+
204
+ // Count trailing zeros after decimal
205
+ const decimalParts = coefficient.split(".");
206
+ if (decimalParts.length === 2) {
207
+ const decimalPart = decimalParts[1];
208
+ const trailingZeros = decimalPart.match(/0*$/u)[0].length;
209
+
210
+ if (trailingZeros === exponent) {
211
+ return false;
212
+ }
213
+ }
214
+ }
215
+
216
+ const normalizedRawNumber =
217
+ convertNumberToScientificNotation(rawNumber);
195
218
  const requestedPrecision = normalizedRawNumber
196
219
  .split("e")[0]
197
220
  .replace(".", "").length;
@@ -99,6 +99,7 @@ module.exports = {
99
99
  * At least one space followed by a tab
100
100
  * before non-tab/-space characters begin.
101
101
  */
102
+ // eslint-disable-next-line regexp/no-empty-lookarounds-assertion -- False positive
102
103
  regex = /^(?=(\t*))\1(?=( +))\2\t/u;
103
104
  }
104
105
 
@@ -30,10 +30,7 @@ module.exports = {
30
30
  create(context) {
31
31
  return {
32
32
  Literal(node) {
33
- if (
34
- typeof node.value === "number" &&
35
- /^0[0-9]/u.test(node.raw)
36
- ) {
33
+ if (typeof node.value === "number" && /^0\d/u.test(node.raw)) {
37
34
  context.report({
38
35
  node,
39
36
  messageId: "noOctal",
@@ -82,7 +82,8 @@ module.exports = {
82
82
  create(context) {
83
83
  const sourceCode = context.sourceCode;
84
84
 
85
- const BLANK_CLASS = "[ \t\u00a0\u2000-\u200b\u3000]",
85
+ const BLANK_CLASS =
86
+ "[ \t\u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u3000]",
86
87
  SKIP_BLANK = `^${BLANK_CLASS}*$`,
87
88
  NONBLANK = `${BLANK_CLASS}+$`;
88
89
 
@@ -387,7 +387,7 @@ module.exports = {
387
387
  const value = isTemplateElement
388
388
  ? sourceCode.getText(node)
389
389
  : node.raw;
390
- const pattern = /\\[^\d]/gu;
390
+ const pattern = /\\\D/gu;
391
391
  let match;
392
392
 
393
393
  while ((match = pattern.exec(value))) {
@@ -522,7 +522,7 @@ module.exports = {
522
522
  }
523
523
 
524
524
  if (
525
- !/^[-a-zA-Z0-9\\[\](){} \t\r\n\v\f!@#$%^&*+^_=/~`.><?,'"|:;]*$/u.test(
525
+ !/^[-\w\\[\](){} \t\r\n\v\f!@#$%^&*+=/~`.><?,'"|:;]*$/u.test(
526
526
  regexContent,
527
527
  )
528
528
  ) {