eslint 9.32.0 → 9.34.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.
@@ -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;
@@ -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
  };
@@ -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)
@@ -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
  /**
@@ -162,7 +162,7 @@ module.exports = {
162
162
  }
163
163
  }
164
164
 
165
- if (/(g|s)etters|methods$/iu.test(kind)) {
165
+ if (/(?:g|s)etters|methods$/iu.test(kind)) {
166
166
  if (
167
167
  (node.parent.decorators?.length &&
168
168
  allow.includes("decoratedFunctions")) ||
@@ -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);
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",
@@ -4,6 +4,12 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
+ //------------------------------------------------------------------------------
8
+ // Requirements
9
+ //------------------------------------------------------------------------------
10
+
11
+ const astUtils = require("./utils/ast-utils");
12
+
7
13
  //------------------------------------------------------------------------------
8
14
  // Helpers
9
15
  //------------------------------------------------------------------------------
@@ -16,10 +22,34 @@ const TYPE_NODES = new Set([
16
22
  "TSQualifiedName",
17
23
  ]);
18
24
 
25
+ const GLOBAL_OBJECTS = new Set(["globalThis", "self", "window"]);
26
+
19
27
  //------------------------------------------------------------------------------
20
28
  // Rule Definition
21
29
  //------------------------------------------------------------------------------
22
30
 
31
+ const arrayOfGlobals = {
32
+ type: "array",
33
+ items: {
34
+ oneOf: [
35
+ {
36
+ type: "string",
37
+ },
38
+ {
39
+ type: "object",
40
+ properties: {
41
+ name: { type: "string" },
42
+ message: { type: "string" },
43
+ },
44
+ required: ["name"],
45
+ additionalProperties: false,
46
+ },
47
+ ],
48
+ },
49
+ uniqueItems: true,
50
+ minItems: 0,
51
+ };
52
+
23
53
  /** @type {import('../types').Rule.RuleModule} */
24
54
  module.exports = {
25
55
  meta: {
@@ -34,25 +64,33 @@ module.exports = {
34
64
  },
35
65
 
36
66
  schema: {
37
- type: "array",
38
- items: {
39
- oneOf: [
40
- {
41
- type: "string",
42
- },
43
- {
44
- type: "object",
45
- properties: {
46
- name: { type: "string" },
47
- message: { type: "string" },
67
+ anyOf: [
68
+ arrayOfGlobals,
69
+ {
70
+ type: "array",
71
+ items: [
72
+ {
73
+ type: "object",
74
+ properties: {
75
+ globals: arrayOfGlobals,
76
+ checkGlobalObject: {
77
+ type: "boolean",
78
+ },
79
+ globalObjects: {
80
+ type: "array",
81
+ items: {
82
+ type: "string",
83
+ },
84
+ uniqueItems: true,
85
+ },
86
+ },
87
+ required: ["globals"],
88
+ additionalProperties: false,
48
89
  },
49
- required: ["name"],
50
- additionalProperties: false,
51
- },
52
- ],
53
- },
54
- uniqueItems: true,
55
- minItems: 0,
90
+ ],
91
+ additionalItems: false,
92
+ },
93
+ ],
56
94
  },
57
95
 
58
96
  messages: {
@@ -63,14 +101,33 @@ module.exports = {
63
101
  },
64
102
 
65
103
  create(context) {
66
- const sourceCode = context.sourceCode;
104
+ const { sourceCode, options } = context;
105
+
106
+ const isGlobalsObject =
107
+ typeof options[0] === "object" &&
108
+ Object.hasOwn(options[0], "globals");
109
+
110
+ const restrictedGlobals = isGlobalsObject
111
+ ? options[0].globals
112
+ : options;
113
+ const checkGlobalObject = isGlobalsObject
114
+ ? options[0].checkGlobalObject
115
+ : false;
116
+ const userGlobalObjects = isGlobalsObject
117
+ ? options[0].globalObjects || []
118
+ : [];
119
+
120
+ const globalObjects = new Set([
121
+ ...GLOBAL_OBJECTS,
122
+ ...userGlobalObjects,
123
+ ]);
67
124
 
68
125
  // If no globals are restricted, we don't need to do anything
69
- if (context.options.length === 0) {
126
+ if (restrictedGlobals.length === 0) {
70
127
  return {};
71
128
  }
72
129
 
73
- const restrictedGlobalMessages = context.options.reduce(
130
+ const restrictedGlobalMessages = restrictedGlobals.reduce(
74
131
  (memo, option) => {
75
132
  if (typeof option === "string") {
76
133
  memo[option] = null;
@@ -151,6 +208,59 @@ module.exports = {
151
208
  }
152
209
  });
153
210
  },
211
+
212
+ "Program:exit"(node) {
213
+ if (!checkGlobalObject) {
214
+ return;
215
+ }
216
+
217
+ const globalScope = sourceCode.getScope(node);
218
+ globalObjects.forEach(globalObjectName => {
219
+ const variable = astUtils.getVariableByName(
220
+ globalScope,
221
+ globalObjectName,
222
+ );
223
+
224
+ if (!variable) {
225
+ return;
226
+ }
227
+
228
+ variable.references.forEach(reference => {
229
+ const identifier = reference.identifier;
230
+ let parent = identifier.parent;
231
+
232
+ // To detect code like `window.window.Promise`.
233
+ while (
234
+ astUtils.isSpecificMemberAccess(
235
+ parent,
236
+ null,
237
+ globalObjectName,
238
+ )
239
+ ) {
240
+ parent = parent.parent;
241
+ }
242
+
243
+ const propertyName =
244
+ astUtils.getStaticPropertyName(parent);
245
+ if (propertyName && isRestricted(propertyName)) {
246
+ const customMessage =
247
+ restrictedGlobalMessages[propertyName];
248
+ const messageId = customMessage
249
+ ? "customMessage"
250
+ : "defaultMessage";
251
+
252
+ context.report({
253
+ node: parent.property,
254
+ messageId,
255
+ data: {
256
+ name: propertyName,
257
+ customMessage,
258
+ },
259
+ });
260
+ }
261
+ });
262
+ });
263
+ },
154
264
  };
155
265
  },
156
266
  };
@@ -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))) {