eslint 8.22.0 → 8.23.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/bin/eslint.js CHANGED
@@ -9,9 +9,6 @@
9
9
 
10
10
  "use strict";
11
11
 
12
- // to use V8's code cache to speed up instantiation time
13
- require("v8-compile-cache");
14
-
15
12
  // must do this initialization *before* other requires in order to work
16
13
  if (process.argv.includes("--debug")) {
17
14
  require("debug").enable("eslint:*,-eslint:code-path,eslintrc:*");
@@ -137,6 +134,7 @@ ${message}`);
137
134
  // Otherwise, call the CLI.
138
135
  process.exitCode = await require("../lib/cli").execute(
139
136
  process.argv,
140
- process.argv.includes("--stdin") ? await readStdin() : null
137
+ process.argv.includes("--stdin") ? await readStdin() : null,
138
+ true
141
139
  );
142
140
  }()).catch(onFatalError);
package/conf/globals.js CHANGED
@@ -124,6 +124,10 @@ const es2022 = {
124
124
  ...es2021
125
125
  };
126
126
 
127
+ const es2023 = {
128
+ ...es2022
129
+ };
130
+
127
131
 
128
132
  //-----------------------------------------------------------------------------
129
133
  // Exports
@@ -140,5 +144,6 @@ module.exports = {
140
144
  es2019,
141
145
  es2020,
142
146
  es2021,
143
- es2022
147
+ es2022,
148
+ es2023
144
149
  };
package/lib/cli.js CHANGED
@@ -6,7 +6,7 @@
6
6
  "use strict";
7
7
 
8
8
  /*
9
- * The CLI object should *not* call process.exit() directly. It should only return
9
+ * NOTE: The CLI object should *not* call process.exit() directly. It should only return
10
10
  * exit codes. This allows other programs to use the CLI object and still control
11
11
  * when the program exits.
12
12
  */
@@ -19,9 +19,14 @@ const fs = require("fs"),
19
19
  path = require("path"),
20
20
  { promisify } = require("util"),
21
21
  { ESLint } = require("./eslint"),
22
- CLIOptions = require("./options"),
22
+ { FlatESLint } = require("./eslint/flat-eslint"),
23
+ createCLIOptions = require("./options"),
23
24
  log = require("./shared/logging"),
24
25
  RuntimeInfo = require("./shared/runtime-info");
26
+ const { Legacy: { naming } } = require("@eslint/eslintrc");
27
+ const { findFlatConfigFile } = require("./eslint/flat-eslint");
28
+ const { gitignoreToMinimatch } = require("@humanwhocodes/gitignore-to-minimatch");
29
+ const { ModuleImporter } = require("@humanwhocodes/module-importer");
25
30
 
26
31
  const debug = require("debug")("eslint:cli");
27
32
 
@@ -54,17 +59,20 @@ function quietFixPredicate(message) {
54
59
  }
55
60
 
56
61
  /**
57
- * Translates the CLI options into the options expected by the CLIEngine.
62
+ * Translates the CLI options into the options expected by the ESLint constructor.
58
63
  * @param {ParsedCLIOptions} cliOptions The CLI options to translate.
59
- * @returns {ESLintOptions} The options object for the CLIEngine.
64
+ * @param {"flat"|"eslintrc"} [configType="eslintrc"] The format of the
65
+ * config to generate.
66
+ * @returns {Promise<ESLintOptions>} The options object for the ESLint constructor.
60
67
  * @private
61
68
  */
62
- function translateOptions({
69
+ async function translateOptions({
63
70
  cache,
64
71
  cacheFile,
65
72
  cacheLocation,
66
73
  cacheStrategy,
67
74
  config,
75
+ configLookup,
68
76
  env,
69
77
  errorOnUnmatchedPattern,
70
78
  eslintrc,
@@ -85,19 +93,66 @@ function translateOptions({
85
93
  resolvePluginsRelativeTo,
86
94
  rule,
87
95
  rulesdir
88
- }) {
89
- return {
90
- allowInlineConfig: inlineConfig,
91
- cache,
92
- cacheLocation: cacheLocation || cacheFile,
93
- cacheStrategy,
94
- errorOnUnmatchedPattern,
95
- extensions: ext,
96
- fix: (fix || fixDryRun) && (quiet ? quietFixPredicate : true),
97
- fixTypes: fixType,
98
- ignore,
99
- ignorePath,
100
- overrideConfig: {
96
+ }, configType) {
97
+
98
+ let overrideConfig, overrideConfigFile;
99
+ const importer = new ModuleImporter();
100
+
101
+ if (configType === "flat") {
102
+ overrideConfigFile = (typeof config === "string") ? config : !configLookup;
103
+ if (overrideConfigFile === false) {
104
+ overrideConfigFile = void 0;
105
+ }
106
+
107
+ let globals = {};
108
+
109
+ if (global) {
110
+ globals = global.reduce((obj, name) => {
111
+ if (name.endsWith(":true")) {
112
+ obj[name.slice(0, -5)] = "writable";
113
+ } else {
114
+ obj[name] = "readonly";
115
+ }
116
+ return obj;
117
+ }, globals);
118
+ }
119
+
120
+ overrideConfig = [{
121
+ languageOptions: {
122
+ globals,
123
+ parserOptions: parserOptions || {}
124
+ },
125
+ rules: rule ? rule : {}
126
+ }];
127
+
128
+ if (parser) {
129
+ overrideConfig[0].languageOptions.parser = await importer.import(parser);
130
+ }
131
+
132
+ if (plugin) {
133
+ const plugins = {};
134
+
135
+ for (const pluginName of plugin) {
136
+
137
+ const shortName = naming.getShorthandName(pluginName, "eslint-plugin");
138
+ const longName = naming.normalizePackageName(pluginName, "eslint-plugin");
139
+
140
+ plugins[shortName] = await importer.import(longName);
141
+ }
142
+
143
+ overrideConfig[0].plugins = plugins;
144
+ }
145
+
146
+ if (ignorePattern) {
147
+ overrideConfig.push({
148
+ ignores: ignorePattern.map(gitignoreToMinimatch)
149
+ });
150
+ }
151
+
152
+ } else {
153
+ overrideConfigFile = config;
154
+
155
+ overrideConfig = {
101
156
  env: env && env.reduce((obj, name) => {
102
157
  obj[name] = true;
103
158
  return obj;
@@ -115,13 +170,32 @@ function translateOptions({
115
170
  parserOptions,
116
171
  plugins: plugin,
117
172
  rules: rule
118
- },
119
- overrideConfigFile: config,
120
- reportUnusedDisableDirectives: reportUnusedDisableDirectives ? "error" : void 0,
121
- resolvePluginsRelativeTo,
122
- rulePaths: rulesdir,
123
- useEslintrc: eslintrc
173
+ };
174
+ }
175
+
176
+ const options = {
177
+ allowInlineConfig: inlineConfig,
178
+ cache,
179
+ cacheLocation: cacheLocation || cacheFile,
180
+ cacheStrategy,
181
+ errorOnUnmatchedPattern,
182
+ fix: (fix || fixDryRun) && (quiet ? quietFixPredicate : true),
183
+ fixTypes: fixType,
184
+ ignore,
185
+ ignorePath,
186
+ overrideConfig,
187
+ overrideConfigFile,
188
+ reportUnusedDisableDirectives: reportUnusedDisableDirectives ? "error" : void 0
124
189
  };
190
+
191
+ if (configType !== "flat") {
192
+ options.resolvePluginsRelativeTo = resolvePluginsRelativeTo;
193
+ options.rulePaths = rulesdir;
194
+ options.useEslintrc = eslintrc;
195
+ options.extensions = ext;
196
+ }
197
+
198
+ return options;
125
199
  }
126
200
 
127
201
  /**
@@ -218,19 +292,34 @@ const cli = {
218
292
  * Executes the CLI based on an array of arguments that is passed in.
219
293
  * @param {string|Array|Object} args The arguments to process.
220
294
  * @param {string} [text] The text to lint (used for TTY).
295
+ * @param {boolean} [allowFlatConfig] Whether or not to allow flat config.
221
296
  * @returns {Promise<number>} The exit code for the operation.
222
297
  */
223
- async execute(args, text) {
298
+ async execute(args, text, allowFlatConfig) {
224
299
  if (Array.isArray(args)) {
225
300
  debug("CLI args: %o", args.slice(2));
226
301
  }
227
302
 
303
+ /*
304
+ * Before doing anything, we need to see if we are using a
305
+ * flat config file. If so, then we need to change the way command
306
+ * line args are parsed. This is temporary, and when we fully
307
+ * switch to flat config we can remove this logic.
308
+ */
309
+
310
+ const usingFlatConfig = allowFlatConfig && !!(await findFlatConfigFile(process.cwd()));
311
+
312
+ debug("Using flat config?", usingFlatConfig);
313
+
314
+ const CLIOptions = createCLIOptions(usingFlatConfig);
315
+
228
316
  /** @type {ParsedCLIOptions} */
229
317
  let options;
230
318
 
231
319
  try {
232
320
  options = CLIOptions.parse(args);
233
321
  } catch (error) {
322
+ debug("Error parsing CLI options:", error.message);
234
323
  log.error(error.message);
235
324
  return 2;
236
325
  }
@@ -251,6 +340,7 @@ const cli = {
251
340
  log.info(RuntimeInfo.environment());
252
341
  return 0;
253
342
  } catch (err) {
343
+ debug("Error retrieving environment info");
254
344
  log.error(err.message);
255
345
  return 2;
256
346
  }
@@ -266,7 +356,9 @@ const cli = {
266
356
  return 2;
267
357
  }
268
358
 
269
- const engine = new ESLint(translateOptions(options));
359
+ const engine = usingFlatConfig
360
+ ? new FlatESLint(await translateOptions(options, "flat"))
361
+ : new ESLint(await translateOptions(options));
270
362
  const fileConfig =
271
363
  await engine.calculateConfigForFile(options.printConfig);
272
364
 
@@ -289,7 +381,9 @@ const cli = {
289
381
  return 2;
290
382
  }
291
383
 
292
- const engine = new ESLint(translateOptions(options));
384
+ const ActiveESLint = usingFlatConfig ? FlatESLint : ESLint;
385
+
386
+ const engine = new ActiveESLint(await translateOptions(options, usingFlatConfig ? "flat" : "eslintrc"));
293
387
  let results;
294
388
 
295
389
  if (useStdin) {
@@ -303,14 +397,14 @@ const cli = {
303
397
 
304
398
  if (options.fix) {
305
399
  debug("Fix mode enabled - applying fixes");
306
- await ESLint.outputFixes(results);
400
+ await ActiveESLint.outputFixes(results);
307
401
  }
308
402
 
309
403
  let resultsToPrint = results;
310
404
 
311
405
  if (options.quiet) {
312
406
  debug("Quiet mode enabled - filtering out warnings");
313
- resultsToPrint = ESLint.getErrorResults(resultsToPrint);
407
+ resultsToPrint = ActiveESLint.getErrorResults(resultsToPrint);
314
408
  }
315
409
 
316
410
  if (await printResults(engine, resultsToPrint, options.format, options.outputFile)) {
@@ -104,6 +104,8 @@ function isGlobPattern(pattern) {
104
104
  * false to not interpret glob patterns.
105
105
  * @param {string} args.cwd The current working directory to find from.
106
106
  * @param {FlatConfigArray} args.configs The configs for the current run.
107
+ * @param {boolean} args.errorOnUnmatchedPattern Determines if an unmatched pattern
108
+ * should throw an error.
107
109
  * @returns {Promise<Array<string>>} The fully resolved file paths.
108
110
  * @throws {AllFilesIgnoredError} If there are no results due to an ignore pattern.
109
111
  * @throws {NoFilesFoundError} If no files matched the given patterns.
@@ -112,7 +114,8 @@ async function findFiles({
112
114
  patterns,
113
115
  globInputPaths,
114
116
  cwd,
115
- configs
117
+ configs,
118
+ errorOnUnmatchedPattern
116
119
  }) {
117
120
 
118
121
  const results = [];
@@ -222,14 +225,16 @@ async function findFiles({
222
225
  }
223
226
 
224
227
  // no files were found
225
- throw new NoFilesFoundError(globbyPattern, globInputPaths);
228
+ if (errorOnUnmatchedPattern) {
229
+ throw new NoFilesFoundError(globbyPattern, globInputPaths);
230
+ }
226
231
  }
227
232
  /* eslint-enable no-unreachable-loop -- Go back to normal. */
228
233
 
229
234
  }
230
235
 
231
236
  // there were patterns that didn't match anything, tell the user
232
- if (missingPatterns.length) {
237
+ if (errorOnUnmatchedPattern && missingPatterns.length) {
233
238
  throw new NoFilesFoundError(missingPatterns[0], globInputPaths);
234
239
  }
235
240
 
@@ -322,6 +327,7 @@ function createIgnoreResult(filePath, baseDir) {
322
327
  message
323
328
  }
324
329
  ],
330
+ suppressedMessages: [],
325
331
  errorCount: 0,
326
332
  warningCount: 1,
327
333
  fatalErrorCount: 0,
@@ -378,7 +384,6 @@ function processOptions({
378
384
  cacheStrategy = "metadata",
379
385
  cwd = process.cwd(),
380
386
  errorOnUnmatchedPattern = true,
381
- extensions = null, // ← should be null by default because if it's an array then it suppresses RFC20 feature.
382
387
  fix = false,
383
388
  fixTypes = null, // ← should be null by default because if it's an array then it suppresses rules that don't have the `meta.type` property.
384
389
  globInputPaths = true,
@@ -405,6 +410,9 @@ function processOptions({
405
410
  if (unknownOptionKeys.includes("envs")) {
406
411
  errors.push("'envs' has been removed.");
407
412
  }
413
+ if (unknownOptionKeys.includes("extensions")) {
414
+ errors.push("'extensions' has been removed.");
415
+ }
408
416
  if (unknownOptionKeys.includes("resolvePluginsRelativeTo")) {
409
417
  errors.push("'resolvePluginsRelativeTo' has been removed.");
410
418
  }
@@ -451,9 +459,6 @@ function processOptions({
451
459
  if (typeof errorOnUnmatchedPattern !== "boolean") {
452
460
  errors.push("'errorOnUnmatchedPattern' must be a boolean.");
453
461
  }
454
- if (!isArrayOfNonEmptyString(extensions) && extensions !== null) {
455
- errors.push("'extensions' must be an array of non-empty strings or null.");
456
- }
457
462
  if (typeof fix !== "boolean" && typeof fix !== "function") {
458
463
  errors.push("'fix' must be a boolean or a function.");
459
464
  }
@@ -507,7 +512,6 @@ function processOptions({
507
512
  overrideConfig,
508
513
  cwd,
509
514
  errorOnUnmatchedPattern,
510
- extensions,
511
515
  fix,
512
516
  fixTypes,
513
517
  globInputPaths,
@@ -73,7 +73,6 @@ const LintResultCache = require("../cli-engine/lint-result-cache");
73
73
  * @property {"metadata" | "content"} [cacheStrategy] The strategy used to detect changed files.
74
74
  * @property {string} [cwd] The value to use for the current working directory.
75
75
  * @property {boolean} [errorOnUnmatchedPattern] If `false` then `ESLint#lintFiles()` doesn't throw even if no target files found. Defaults to `true`.
76
- * @property {string[]} [extensions] An array of file extensions to check.
77
76
  * @property {boolean|Function} [fix] Execute in autofix mode. If a function, should return a boolean.
78
77
  * @property {string[]} [fixTypes] Array of rule types to apply fixes for.
79
78
  * @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file.
@@ -321,8 +320,7 @@ async function calculateConfigArray(eslint, {
321
320
  configFile,
322
321
  ignore: shouldIgnore,
323
322
  ignorePath,
324
- ignorePatterns,
325
- extensions
323
+ ignorePatterns
326
324
  }) {
327
325
 
328
326
  // check for cached instance
@@ -365,13 +363,6 @@ async function calculateConfigArray(eslint, {
365
363
  // add in any configured defaults
366
364
  configs.push(...slots.defaultConfigs);
367
365
 
368
- // if there are any extensions, create configs for them for easier matching
369
- if (extensions && extensions.length) {
370
- configs.push({
371
- files: extensions.map(ext => `**/*${ext}`)
372
- });
373
- }
374
-
375
366
  let allIgnorePatterns = [];
376
367
  let ignoreFilePath;
377
368
 
@@ -515,6 +506,7 @@ function verifyText({
515
506
  const result = {
516
507
  filePath: filePath === "<text>" ? filePath : path.resolve(filePath),
517
508
  messages,
509
+ suppressedMessages: linter.getSuppressedMessages(),
518
510
  ...calculateStatsPerFile(messages)
519
511
  };
520
512
 
@@ -689,11 +681,13 @@ class FlatESLint {
689
681
 
690
682
  results.forEach(result => {
691
683
  const filteredMessages = result.messages.filter(isErrorMessage);
684
+ const filteredSuppressedMessages = result.suppressedMessages.filter(isErrorMessage);
692
685
 
693
686
  if (filteredMessages.length > 0) {
694
687
  filtered.push({
695
688
  ...result,
696
689
  messages: filteredMessages,
690
+ suppressedMessages: filteredSuppressedMessages,
697
691
  errorCount: filteredMessages.length,
698
692
  warningCount: 0,
699
693
  fixableErrorCount: result.fixableErrorCount,
@@ -746,11 +740,12 @@ class FlatESLint {
746
740
  * calculated config for the given file.
747
741
  */
748
742
  const config = configs.getConfig(filePath);
743
+ const allMessages = result.messages.concat(result.suppressedMessages);
749
744
 
750
- for (const { ruleId } of result.messages) {
745
+ for (const { ruleId } of allMessages) {
751
746
  const rule = getRuleFromConfig(ruleId, config);
752
747
 
753
- // ensure the rule exists exists
748
+ // ensure the rule exists
754
749
  if (!rule) {
755
750
  throw new TypeError(`Could not find the rule "${ruleId}".`);
756
751
  }
@@ -786,8 +781,8 @@ class FlatESLint {
786
781
  fix,
787
782
  fixTypes,
788
783
  reportUnusedDisableDirectives,
789
- extensions,
790
- globInputPaths
784
+ globInputPaths,
785
+ errorOnUnmatchedPattern
791
786
  } = eslintOptions;
792
787
  const startTime = Date.now();
793
788
  const usedConfigs = [];
@@ -812,9 +807,9 @@ class FlatESLint {
812
807
  const filePaths = await findFiles({
813
808
  patterns: typeof patterns === "string" ? [patterns] : patterns,
814
809
  cwd,
815
- extensions,
816
810
  globInputPaths,
817
- configs
811
+ configs,
812
+ errorOnUnmatchedPattern
818
813
  });
819
814
 
820
815
  debug(`${filePaths.length} files found in: ${Date.now() - startTime}ms`);
@@ -1127,6 +1122,7 @@ class FlatESLint {
1127
1122
  results.sort(compareResultsByFilePath);
1128
1123
 
1129
1124
  return formatter(results, {
1125
+ cwd,
1130
1126
  get rulesMeta() {
1131
1127
  if (!rulesMeta) {
1132
1128
  rulesMeta = eslint.getRulesMetaForResults(results);
@@ -1175,5 +1171,6 @@ class FlatESLint {
1175
1171
  //------------------------------------------------------------------------------
1176
1172
 
1177
1173
  module.exports = {
1178
- FlatESLint
1174
+ FlatESLint,
1175
+ findFlatConfigFile
1179
1176
  };
@@ -131,8 +131,7 @@ module.exports = class ConfigCommentParser {
131
131
 
132
132
  const items = {};
133
133
 
134
- // Collapse whitespace around commas
135
- string.replace(/\s*,\s*/gu, ",").split(/,+/u).forEach(name => {
134
+ string.split(",").forEach(name => {
136
135
  const trimmedName = name.trim();
137
136
 
138
137
  if (trimmedName) {
package/lib/options.js CHANGED
@@ -63,261 +63,309 @@ const optionator = require("optionator");
63
63
  //------------------------------------------------------------------------------
64
64
 
65
65
  // exports "parse(args)", "generateHelp()", and "generateHelpForOption(optionName)"
66
- module.exports = optionator({
67
- prepend: "eslint [options] file.js [file.js] [dir]",
68
- defaults: {
69
- concatRepeatedArrays: true,
70
- mergeRepeatedObjects: true
71
- },
72
- options: [
73
- {
74
- heading: "Basic configuration"
75
- },
76
- {
66
+
67
+ /**
68
+ * Creates the CLI options for ESLint.
69
+ * @param {boolean} usingFlatConfig Indicates if flat config is being used.
70
+ * @returns {Object} The opinionator instance.
71
+ */
72
+ module.exports = function(usingFlatConfig) {
73
+
74
+ let lookupFlag;
75
+
76
+ if (usingFlatConfig) {
77
+ lookupFlag = {
78
+ option: "config-lookup",
79
+ type: "Boolean",
80
+ default: "true",
81
+ description: "Disable look up for eslint.config.js"
82
+ };
83
+ } else {
84
+ lookupFlag = {
77
85
  option: "eslintrc",
78
86
  type: "Boolean",
79
87
  default: "true",
80
88
  description: "Disable use of configuration from .eslintrc.*"
81
- },
82
- {
83
- option: "config",
84
- alias: "c",
85
- type: "path::String",
86
- description: "Use this configuration, overriding .eslintrc.* config options if present"
87
- },
88
- {
89
+ };
90
+ }
91
+
92
+ let envFlag;
93
+
94
+ if (!usingFlatConfig) {
95
+ envFlag = {
89
96
  option: "env",
90
97
  type: "[String]",
91
98
  description: "Specify environments"
92
- },
93
- {
99
+ };
100
+ }
101
+
102
+ let extFlag;
103
+
104
+ if (!usingFlatConfig) {
105
+ extFlag = {
94
106
  option: "ext",
95
107
  type: "[String]",
96
108
  description: "Specify JavaScript file extensions"
97
- },
98
- {
99
- option: "global",
100
- type: "[String]",
101
- description: "Define global variables"
102
- },
103
- {
104
- option: "parser",
105
- type: "String",
106
- description: "Specify the parser to be used"
107
- },
108
- {
109
- option: "parser-options",
110
- type: "Object",
111
- description: "Specify parser options"
112
- },
113
- {
109
+ };
110
+ }
111
+
112
+ let resolvePluginsFlag;
113
+
114
+ if (!usingFlatConfig) {
115
+ resolvePluginsFlag = {
114
116
  option: "resolve-plugins-relative-to",
115
117
  type: "path::String",
116
118
  description: "A folder where plugins should be resolved from, CWD by default"
117
- },
118
- {
119
- heading: "Specifying rules and plugins"
120
- },
121
- {
122
- option: "plugin",
123
- type: "[String]",
124
- description: "Specify plugins"
125
- },
126
- {
127
- option: "rule",
128
- type: "Object",
129
- description: "Specify rules"
130
- },
131
- {
119
+ };
120
+ }
121
+
122
+ let rulesDirFlag;
123
+
124
+ if (!usingFlatConfig) {
125
+ rulesDirFlag = {
132
126
  option: "rulesdir",
133
127
  type: "[path::String]",
134
128
  description: "Load additional rules from this directory. Deprecated: Use rules from plugins"
135
- },
136
- {
137
- heading: "Fixing problems"
138
- },
139
- {
140
- option: "fix",
141
- type: "Boolean",
142
- default: false,
143
- description: "Automatically fix problems"
144
- },
145
- {
146
- option: "fix-dry-run",
147
- type: "Boolean",
148
- default: false,
149
- description: "Automatically fix problems without saving the changes to the file system"
150
- },
151
- {
152
- option: "fix-type",
153
- type: "Array",
154
- description: "Specify the types of fixes to apply (directive, problem, suggestion, layout)"
155
- },
156
- {
157
- heading: "Ignoring files"
158
- },
159
- {
160
- option: "ignore-path",
161
- type: "path::String",
162
- description: "Specify path of ignore file"
163
- },
164
- {
165
- option: "ignore",
166
- type: "Boolean",
167
- default: "true",
168
- description: "Disable use of ignore files and patterns"
169
- },
170
- {
171
- option: "ignore-pattern",
172
- type: "[String]",
173
- description: "Pattern of files to ignore (in addition to those in .eslintignore)",
174
- concatRepeatedArrays: [true, {
175
- oneValuePerFlag: true
176
- }]
177
- },
178
- {
179
- heading: "Using stdin"
180
- },
181
- {
182
- option: "stdin",
183
- type: "Boolean",
184
- default: "false",
185
- description: "Lint code provided on <STDIN>"
186
- },
187
- {
188
- option: "stdin-filename",
189
- type: "String",
190
- description: "Specify filename to process STDIN as"
191
- },
192
- {
193
- heading: "Handling warnings"
194
- },
195
- {
196
- option: "quiet",
197
- type: "Boolean",
198
- default: "false",
199
- description: "Report errors only"
200
- },
201
- {
202
- option: "max-warnings",
203
- type: "Int",
204
- default: "-1",
205
- description: "Number of warnings to trigger nonzero exit code"
206
- },
207
- {
208
- heading: "Output"
209
- },
210
- {
211
- option: "output-file",
212
- alias: "o",
213
- type: "path::String",
214
- description: "Specify file to write report to"
215
- },
216
- {
217
- option: "format",
218
- alias: "f",
219
- type: "String",
220
- default: "stylish",
221
- description: "Use a specific output format"
222
- },
223
- {
224
- option: "color",
225
- type: "Boolean",
226
- alias: "no-color",
227
- description: "Force enabling/disabling of color"
228
- },
229
- {
230
- heading: "Inline configuration comments"
231
- },
232
- {
233
- option: "inline-config",
234
- type: "Boolean",
235
- default: "true",
236
- description: "Prevent comments from changing config or rules"
237
- },
238
- {
239
- option: "report-unused-disable-directives",
240
- type: "Boolean",
241
- default: void 0,
242
- description: "Adds reported errors for unused eslint-disable directives"
243
- },
244
- {
245
- heading: "Caching"
246
- },
247
- {
248
- option: "cache",
249
- type: "Boolean",
250
- default: "false",
251
- description: "Only check changed files"
252
- },
253
- {
254
- option: "cache-file",
255
- type: "path::String",
256
- default: ".eslintcache",
257
- description: "Path to the cache file. Deprecated: use --cache-location"
258
- },
259
- {
260
- option: "cache-location",
261
- type: "path::String",
262
- description: "Path to the cache file or directory"
263
- },
264
- {
265
- option: "cache-strategy",
266
- dependsOn: ["cache"],
267
- type: "String",
268
- default: "metadata",
269
- enum: ["metadata", "content"],
270
- description: "Strategy to use for detecting changed files in the cache"
271
- },
272
- {
273
- heading: "Miscellaneous"
274
- },
275
- {
276
- option: "init",
277
- type: "Boolean",
278
- default: "false",
279
- description: "Run config initialization wizard"
280
- },
281
- {
282
- option: "env-info",
283
- type: "Boolean",
284
- default: "false",
285
- description: "Output execution environment information"
286
- },
287
- {
288
- option: "error-on-unmatched-pattern",
289
- type: "Boolean",
290
- default: "true",
291
- description: "Prevent errors when pattern is unmatched"
292
- },
293
- {
294
- option: "exit-on-fatal-error",
295
- type: "Boolean",
296
- default: "false",
297
- description: "Exit with exit code 2 in case of fatal error"
298
- },
299
- {
300
- option: "debug",
301
- type: "Boolean",
302
- default: false,
303
- description: "Output debugging information"
304
- },
305
- {
306
- option: "help",
307
- alias: "h",
308
- type: "Boolean",
309
- description: "Show help"
310
- },
311
- {
312
- option: "version",
313
- alias: "v",
314
- type: "Boolean",
315
- description: "Output the version number"
316
- },
317
- {
318
- option: "print-config",
319
- type: "path::String",
320
- description: "Print the configuration for the given file"
321
- }
322
- ]
323
- });
129
+ };
130
+ }
131
+
132
+ return optionator({
133
+ prepend: "eslint [options] file.js [file.js] [dir]",
134
+ defaults: {
135
+ concatRepeatedArrays: true,
136
+ mergeRepeatedObjects: true
137
+ },
138
+ options: [
139
+ {
140
+ heading: "Basic configuration"
141
+ },
142
+ lookupFlag,
143
+ {
144
+ option: "config",
145
+ alias: "c",
146
+ type: "path::String",
147
+ description: usingFlatConfig
148
+ ? "Use this configuration instead of eslint.config.js"
149
+ : "Use this configuration, overriding .eslintrc.* config options if present"
150
+ },
151
+ envFlag,
152
+ extFlag,
153
+ {
154
+ option: "global",
155
+ type: "[String]",
156
+ description: "Define global variables"
157
+ },
158
+ {
159
+ option: "parser",
160
+ type: "String",
161
+ description: "Specify the parser to be used"
162
+ },
163
+ {
164
+ option: "parser-options",
165
+ type: "Object",
166
+ description: "Specify parser options"
167
+ },
168
+ resolvePluginsFlag,
169
+ {
170
+ heading: "Specifying rules and plugins"
171
+ },
172
+ {
173
+ option: "plugin",
174
+ type: "[String]",
175
+ description: "Specify plugins"
176
+ },
177
+ {
178
+ option: "rule",
179
+ type: "Object",
180
+ description: "Specify rules"
181
+ },
182
+ rulesDirFlag,
183
+ {
184
+ heading: "Fixing problems"
185
+ },
186
+ {
187
+ option: "fix",
188
+ type: "Boolean",
189
+ default: false,
190
+ description: "Automatically fix problems"
191
+ },
192
+ {
193
+ option: "fix-dry-run",
194
+ type: "Boolean",
195
+ default: false,
196
+ description: "Automatically fix problems without saving the changes to the file system"
197
+ },
198
+ {
199
+ option: "fix-type",
200
+ type: "Array",
201
+ description: "Specify the types of fixes to apply (directive, problem, suggestion, layout)"
202
+ },
203
+ {
204
+ heading: "Ignoring files"
205
+ },
206
+ {
207
+ option: "ignore-path",
208
+ type: "path::String",
209
+ description: "Specify path of ignore file"
210
+ },
211
+ {
212
+ option: "ignore",
213
+ type: "Boolean",
214
+ default: "true",
215
+ description: "Disable use of ignore files and patterns"
216
+ },
217
+ {
218
+ option: "ignore-pattern",
219
+ type: "[String]",
220
+ description: "Pattern of files to ignore (in addition to those in .eslintignore)",
221
+ concatRepeatedArrays: [true, {
222
+ oneValuePerFlag: true
223
+ }]
224
+ },
225
+ {
226
+ heading: "Using stdin"
227
+ },
228
+ {
229
+ option: "stdin",
230
+ type: "Boolean",
231
+ default: "false",
232
+ description: "Lint code provided on <STDIN>"
233
+ },
234
+ {
235
+ option: "stdin-filename",
236
+ type: "String",
237
+ description: "Specify filename to process STDIN as"
238
+ },
239
+ {
240
+ heading: "Handling warnings"
241
+ },
242
+ {
243
+ option: "quiet",
244
+ type: "Boolean",
245
+ default: "false",
246
+ description: "Report errors only"
247
+ },
248
+ {
249
+ option: "max-warnings",
250
+ type: "Int",
251
+ default: "-1",
252
+ description: "Number of warnings to trigger nonzero exit code"
253
+ },
254
+ {
255
+ heading: "Output"
256
+ },
257
+ {
258
+ option: "output-file",
259
+ alias: "o",
260
+ type: "path::String",
261
+ description: "Specify file to write report to"
262
+ },
263
+ {
264
+ option: "format",
265
+ alias: "f",
266
+ type: "String",
267
+ default: "stylish",
268
+ description: "Use a specific output format"
269
+ },
270
+ {
271
+ option: "color",
272
+ type: "Boolean",
273
+ alias: "no-color",
274
+ description: "Force enabling/disabling of color"
275
+ },
276
+ {
277
+ heading: "Inline configuration comments"
278
+ },
279
+ {
280
+ option: "inline-config",
281
+ type: "Boolean",
282
+ default: "true",
283
+ description: "Prevent comments from changing config or rules"
284
+ },
285
+ {
286
+ option: "report-unused-disable-directives",
287
+ type: "Boolean",
288
+ default: void 0,
289
+ description: "Adds reported errors for unused eslint-disable directives"
290
+ },
291
+ {
292
+ heading: "Caching"
293
+ },
294
+ {
295
+ option: "cache",
296
+ type: "Boolean",
297
+ default: "false",
298
+ description: "Only check changed files"
299
+ },
300
+ {
301
+ option: "cache-file",
302
+ type: "path::String",
303
+ default: ".eslintcache",
304
+ description: "Path to the cache file. Deprecated: use --cache-location"
305
+ },
306
+ {
307
+ option: "cache-location",
308
+ type: "path::String",
309
+ description: "Path to the cache file or directory"
310
+ },
311
+ {
312
+ option: "cache-strategy",
313
+ dependsOn: ["cache"],
314
+ type: "String",
315
+ default: "metadata",
316
+ enum: ["metadata", "content"],
317
+ description: "Strategy to use for detecting changed files in the cache"
318
+ },
319
+ {
320
+ heading: "Miscellaneous"
321
+ },
322
+ {
323
+ option: "init",
324
+ type: "Boolean",
325
+ default: "false",
326
+ description: "Run config initialization wizard"
327
+ },
328
+ {
329
+ option: "env-info",
330
+ type: "Boolean",
331
+ default: "false",
332
+ description: "Output execution environment information"
333
+ },
334
+ {
335
+ option: "error-on-unmatched-pattern",
336
+ type: "Boolean",
337
+ default: "true",
338
+ description: "Prevent errors when pattern is unmatched"
339
+ },
340
+ {
341
+ option: "exit-on-fatal-error",
342
+ type: "Boolean",
343
+ default: "false",
344
+ description: "Exit with exit code 2 in case of fatal error"
345
+ },
346
+ {
347
+ option: "debug",
348
+ type: "Boolean",
349
+ default: false,
350
+ description: "Output debugging information"
351
+ },
352
+ {
353
+ option: "help",
354
+ alias: "h",
355
+ type: "Boolean",
356
+ description: "Show help"
357
+ },
358
+ {
359
+ option: "version",
360
+ alias: "v",
361
+ type: "Boolean",
362
+ description: "Output the version number"
363
+ },
364
+ {
365
+ option: "print-config",
366
+ type: "path::String",
367
+ description: "Print the configuration for the given file"
368
+ }
369
+ ].filter(value => !!value)
370
+ });
371
+ };
@@ -4,7 +4,7 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
- /* eslint-env mocha -- Mocha/Jest wrapper */
7
+ /* globals describe, it -- Mocha globals */
8
8
 
9
9
  //------------------------------------------------------------------------------
10
10
  // Requirements
@@ -4,7 +4,7 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
- /* eslint-env mocha -- Mocha wrapper */
7
+ /* globals describe, it -- Mocha globals */
8
8
 
9
9
  /*
10
10
  * This is a wrapper around mocha to allow for DRY unittests for eslint
@@ -76,6 +76,10 @@ module.exports = {
76
76
  commentPattern: {
77
77
  type: "string",
78
78
  default: ""
79
+ },
80
+ allowEmptyCase: {
81
+ type: "boolean",
82
+ default: false
79
83
  }
80
84
  },
81
85
  additionalProperties: false
@@ -91,6 +95,7 @@ module.exports = {
91
95
  const options = context.options[0] || {};
92
96
  let currentCodePath = null;
93
97
  const sourceCode = context.getSourceCode();
98
+ const allowEmptyCase = options.allowEmptyCase || false;
94
99
 
95
100
  /*
96
101
  * We need to use leading comments of the next SwitchCase node because
@@ -104,7 +109,6 @@ module.exports = {
104
109
  } else {
105
110
  fallthroughCommentPattern = DEFAULT_FALLTHROUGH_COMMENT;
106
111
  }
107
-
108
112
  return {
109
113
  onCodePathStart(codePath) {
110
114
  currentCodePath = codePath;
@@ -119,7 +123,8 @@ module.exports = {
119
123
  * Checks whether or not there is a fallthrough comment.
120
124
  * And reports the previous fallthrough node if that does not exist.
121
125
  */
122
- if (fallthroughCase && !hasFallthroughComment(fallthroughCase, node, context, fallthroughCommentPattern)) {
126
+
127
+ if (fallthroughCase && (!hasFallthroughComment(fallthroughCase, node, context, fallthroughCommentPattern))) {
123
128
  context.report({
124
129
  messageId: node.test ? "case" : "default",
125
130
  node
@@ -137,7 +142,7 @@ module.exports = {
137
142
  * And allows empty cases and the last case.
138
143
  */
139
144
  if (currentCodePath.currentSegments.some(isReachable) &&
140
- (node.consequent.length > 0 || hasBlankLinesBetween(node, nextToken)) &&
145
+ (node.consequent.length > 0 || (!allowEmptyCase && hasBlankLinesBetween(node, nextToken))) &&
141
146
  node.parent.cases[node.parent.cases.length - 1] !== node) {
142
147
  fallthroughCase = node;
143
148
  }
@@ -91,7 +91,7 @@ module.exports = {
91
91
  };
92
92
 
93
93
  // ES6: report blocks without block-level bindings, or that's only child of another block
94
- if (context.parserOptions.ecmaVersion >= 6) {
94
+ if (context.languageOptions.ecmaVersion >= 2015) {
95
95
  ruleDef = {
96
96
  BlockStatement(node) {
97
97
  if (isLoneBlock(node)) {
@@ -37,6 +37,15 @@ module.exports = {
37
37
  },
38
38
  location: {
39
39
  enum: ["start", "anywhere"]
40
+ },
41
+ decoration: {
42
+ type: "array",
43
+ items: {
44
+ type: "string",
45
+ pattern: "^\\S$"
46
+ },
47
+ minItems: 1,
48
+ uniqueItems: true
40
49
  }
41
50
  },
42
51
  additionalProperties: false
@@ -53,6 +62,7 @@ module.exports = {
53
62
  configuration = context.options[0] || {},
54
63
  warningTerms = configuration.terms || ["todo", "fixme", "xxx"],
55
64
  location = configuration.location || "start",
65
+ decoration = [...configuration.decoration || []].join(""),
56
66
  selfConfigRegEx = /\bno-warning-comments\b/u;
57
67
 
58
68
  /**
@@ -64,6 +74,7 @@ module.exports = {
64
74
  */
65
75
  function convertToRegExp(term) {
66
76
  const escaped = escapeRegExp(term);
77
+ const escapedDecoration = escapeRegExp(decoration);
67
78
 
68
79
  /*
69
80
  * When matching at the start, ignore leading whitespace, and
@@ -74,18 +85,23 @@ module.exports = {
74
85
  * e.g. terms ["TODO"] matches `//TODO something`
75
86
  * $ handles any terms at the end of a comment
76
87
  * e.g. terms ["TODO"] matches `// something TODO`
77
- * \s* handles optional leading spaces (for "start" location only)
78
- * e.g. terms ["TODO"] matches `// TODO something`
79
88
  * \b handles terms preceded/followed by word boundary
80
89
  * e.g. terms: ["!FIX", "FIX!"] matches `// FIX!something` or `// something!FIX`
81
90
  * terms: ["FIX"] matches `// FIX!` or `// !FIX`, but not `// fixed or affix`
91
+ *
92
+ * For location start:
93
+ * [\s]* handles optional leading spaces
94
+ * e.g. terms ["TODO"] matches `// TODO something`
95
+ * [\s\*]* (where "\*" is the escaped string of decoration)
96
+ * handles optional leading spaces or decoration characters (for "start" location only)
97
+ * e.g. terms ["TODO"] matches `/**** TODO something ... `
82
98
  */
83
99
  const wordBoundary = "\\b";
84
100
 
85
101
  let prefix = "";
86
102
 
87
103
  if (location === "start") {
88
- prefix = "^\\s*";
104
+ prefix = `^[\\s${escapedDecoration}]*`;
89
105
  } else if (/^\w/u.test(term)) {
90
106
  prefix = wordBoundary;
91
107
  }
@@ -95,12 +111,15 @@ module.exports = {
95
111
 
96
112
  /*
97
113
  * For location "start", the typical regex is:
98
- * /^\s*ESCAPED_TERM\b/iu.
114
+ * /^[\s]*ESCAPED_TERM\b/iu.
115
+ * Or if decoration characters are specified (e.g. "*"), then any of
116
+ * those characters may appear in any order at the start:
117
+ * /^[\s\*]*ESCAPED_TERM\b/iu.
99
118
  *
100
119
  * For location "anywhere" the typical regex is
101
120
  * /\bESCAPED_TERM\b/iu
102
121
  *
103
- * If it starts or ends with non-word character, the prefix and suffix empty, respectively.
122
+ * If it starts or ends with non-word character, the prefix and suffix are empty, respectively.
104
123
  */
105
124
  return new RegExp(`${prefix}${escaped}${suffix}`, flags);
106
125
  }
@@ -1978,7 +1978,7 @@ module.exports = {
1978
1978
  if (comments.length) {
1979
1979
  const lastComment = comments[comments.length - 1];
1980
1980
 
1981
- if (lastComment.range[0] > leftToken.range[0]) {
1981
+ if (!leftToken || lastComment.range[0] > leftToken.range[0]) {
1982
1982
  leftToken = lastComment;
1983
1983
  }
1984
1984
  }
@@ -1986,7 +1986,13 @@ module.exports = {
1986
1986
  leftToken = leftValue;
1987
1987
  }
1988
1988
 
1989
- if (leftToken.type === "Shebang") {
1989
+ /*
1990
+ * If a hashbang comment was passed as a token object from SourceCode,
1991
+ * its type will be "Shebang" because of the way ESLint itself handles hashbangs.
1992
+ * If a hashbang comment was passed in a string and then tokenized in this function,
1993
+ * its type will be "Hashbang" because of the way Espree tokenizes hashbangs.
1994
+ */
1995
+ if (leftToken.type === "Shebang" || leftToken.type === "Hashbang") {
1990
1996
  return false;
1991
1997
  }
1992
1998
 
@@ -2007,7 +2013,7 @@ module.exports = {
2007
2013
  if (comments.length) {
2008
2014
  const firstComment = comments[0];
2009
2015
 
2010
- if (firstComment.range[0] < rightToken.range[0]) {
2016
+ if (!rightToken || firstComment.range[0] < rightToken.range[0]) {
2011
2017
  rightToken = firstComment;
2012
2018
  }
2013
2019
  }
@@ -21,7 +21,7 @@ module.exports = {};
21
21
  /**
22
22
  * @typedef {Object} ParserOptions
23
23
  * @property {EcmaFeatures} [ecmaFeatures] The optional features.
24
- * @property {3|5|6|7|8|9|10|11|12|13|2015|2016|2017|2018|2019|2020|2021|2022} [ecmaVersion] The ECMAScript version (or revision number).
24
+ * @property {3|5|6|7|8|9|10|11|12|13|14|2015|2016|2017|2018|2019|2020|2021|2022|2023} [ecmaVersion] The ECMAScript version (or revision number).
25
25
  * @property {"script"|"module"} [sourceType] The source code type.
26
26
  * @property {boolean} [allowReserved] Allowing the use of reserved words as identifiers in ES3.
27
27
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "8.22.0",
3
+ "version": "8.23.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {
@@ -55,9 +55,10 @@
55
55
  "homepage": "https://eslint.org",
56
56
  "bugs": "https://github.com/eslint/eslint/issues/",
57
57
  "dependencies": {
58
- "@eslint/eslintrc": "^1.3.0",
58
+ "@eslint/eslintrc": "^1.3.1",
59
59
  "@humanwhocodes/config-array": "^0.10.4",
60
60
  "@humanwhocodes/gitignore-to-minimatch": "^1.0.2",
61
+ "@humanwhocodes/module-importer": "^1.0.1",
61
62
  "ajv": "^6.10.0",
62
63
  "chalk": "^4.0.0",
63
64
  "cross-spawn": "^7.0.2",
@@ -67,7 +68,7 @@
67
68
  "eslint-scope": "^7.1.1",
68
69
  "eslint-utils": "^3.0.0",
69
70
  "eslint-visitor-keys": "^3.3.0",
70
- "espree": "^9.3.3",
71
+ "espree": "^9.4.0",
71
72
  "esquery": "^1.4.0",
72
73
  "esutils": "^2.0.2",
73
74
  "fast-deep-equal": "^3.1.3",
@@ -92,8 +93,7 @@
92
93
  "regexpp": "^3.2.0",
93
94
  "strip-ansi": "^6.0.1",
94
95
  "strip-json-comments": "^3.1.0",
95
- "text-table": "^0.2.0",
96
- "v8-compile-cache": "^2.0.3"
96
+ "text-table": "^0.2.0"
97
97
  },
98
98
  "devDependencies": {
99
99
  "@babel/core": "^7.4.3",