eslint 8.48.0 → 8.50.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
@@ -288,8 +288,8 @@ The following companies, organizations, and individuals support ESLint's ongoing
288
288
  <h3>Platinum Sponsors</h3>
289
289
  <p><a href="#"><img src="https://images.opencollective.com/2021-frameworks-fund/logo.png" alt="Chrome Frameworks Fund" height="undefined"></a> <a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
290
290
  <p><a href="https://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a></p><h3>Silver Sponsors</h3>
291
- <p><a href="https://sentry.io"><img src="https://avatars.githubusercontent.com/u/1396951?v=4" alt="Sentry" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a></p><h3>Bronze Sponsors</h3>
292
- <p><a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" 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://github.com/about"><img src="https://avatars.githubusercontent.com/u/9919?v=4" alt="GitHub" height="32"></a> <a href="https://transloadit.com/"><img src="https://avatars.githubusercontent.com/u/125754?v=4" alt="Transloadit" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://quickbookstoolhub.com"><img src="https://avatars.githubusercontent.com/u/95090305?u=e5bc398ef775c9ed19f955c675cdc1fb6abf01df&v=4" alt="QuickBooks Tool hub" height="32"></a></p>
291
+ <p><a href="https://sentry.io"><img src="https://avatars.githubusercontent.com/u/1396951?v=4" alt="Sentry" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://opensource.siemens.com"><img src="https://avatars.githubusercontent.com/u/624020?v=4" alt="Siemens" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a></p><h3>Bronze Sponsors</h3>
292
+ <p><a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://transloadit.com/"><img src="https://avatars.githubusercontent.com/u/125754?v=4" alt="Transloadit" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://quickbookstoolhub.com"><img src="https://avatars.githubusercontent.com/u/95090305?u=e5bc398ef775c9ed19f955c675cdc1fb6abf01df&v=4" alt="QuickBooks Tool hub" height="32"></a></p>
293
293
  <!--sponsorsend-->
294
294
 
295
295
  ## Technology Sponsors
@@ -507,7 +507,7 @@ const eslintrcKeys = [
507
507
  // Full schema
508
508
  //-----------------------------------------------------------------------------
509
509
 
510
- exports.flatConfigSchema = {
510
+ const flatConfigSchema = {
511
511
 
512
512
  // eslintrc-style keys that should always error
513
513
  ...Object.fromEntries(eslintrcKeys.map(key => [key, createEslintrcErrorSchema(key)])),
@@ -533,3 +533,13 @@ exports.flatConfigSchema = {
533
533
  plugins: pluginsSchema,
534
534
  rules: rulesSchema
535
535
  };
536
+
537
+ //-----------------------------------------------------------------------------
538
+ // Exports
539
+ //-----------------------------------------------------------------------------
540
+
541
+ module.exports = {
542
+ flatConfigSchema,
543
+ assertIsRuleSeverity,
544
+ assertIsRuleOptions
545
+ };
@@ -9,7 +9,8 @@
9
9
  // Requirements
10
10
  //-----------------------------------------------------------------------------
11
11
 
12
- const ajv = require("../shared/ajv")();
12
+ const ajvImport = require("../shared/ajv");
13
+ const ajv = ajvImport();
13
14
  const {
14
15
  parseRuleId,
15
16
  getRuleFromConfig,
@@ -192,15 +192,18 @@ function forwardCurrentToHead(analyzer, node) {
192
192
  headSegment = headSegments[i];
193
193
 
194
194
  if (currentSegment !== headSegment && currentSegment) {
195
- debug.dump(`onCodePathSegmentEnd ${currentSegment.id}`);
196
195
 
197
- if (currentSegment.reachable) {
198
- analyzer.emitter.emit(
199
- "onCodePathSegmentEnd",
200
- currentSegment,
201
- node
202
- );
203
- }
196
+ const eventName = currentSegment.reachable
197
+ ? "onCodePathSegmentEnd"
198
+ : "onUnreachableCodePathSegmentEnd";
199
+
200
+ debug.dump(`${eventName} ${currentSegment.id}`);
201
+
202
+ analyzer.emitter.emit(
203
+ eventName,
204
+ currentSegment,
205
+ node
206
+ );
204
207
  }
205
208
  }
206
209
 
@@ -213,16 +216,19 @@ function forwardCurrentToHead(analyzer, node) {
213
216
  headSegment = headSegments[i];
214
217
 
215
218
  if (currentSegment !== headSegment && headSegment) {
216
- debug.dump(`onCodePathSegmentStart ${headSegment.id}`);
219
+
220
+ const eventName = headSegment.reachable
221
+ ? "onCodePathSegmentStart"
222
+ : "onUnreachableCodePathSegmentStart";
223
+
224
+ debug.dump(`${eventName} ${headSegment.id}`);
217
225
 
218
226
  CodePathSegment.markUsed(headSegment);
219
- if (headSegment.reachable) {
220
- analyzer.emitter.emit(
221
- "onCodePathSegmentStart",
222
- headSegment,
223
- node
224
- );
225
- }
227
+ analyzer.emitter.emit(
228
+ eventName,
229
+ headSegment,
230
+ node
231
+ );
226
232
  }
227
233
  }
228
234
 
@@ -241,15 +247,17 @@ function leaveFromCurrentSegment(analyzer, node) {
241
247
 
242
248
  for (let i = 0; i < currentSegments.length; ++i) {
243
249
  const currentSegment = currentSegments[i];
250
+ const eventName = currentSegment.reachable
251
+ ? "onCodePathSegmentEnd"
252
+ : "onUnreachableCodePathSegmentEnd";
244
253
 
245
- debug.dump(`onCodePathSegmentEnd ${currentSegment.id}`);
246
- if (currentSegment.reachable) {
247
- analyzer.emitter.emit(
248
- "onCodePathSegmentEnd",
249
- currentSegment,
250
- node
251
- );
252
- }
254
+ debug.dump(`${eventName} ${currentSegment.id}`);
255
+
256
+ analyzer.emitter.emit(
257
+ eventName,
258
+ currentSegment,
259
+ node
260
+ );
253
261
  }
254
262
 
255
263
  state.currentSegments = [];
@@ -117,6 +117,7 @@ class CodePath {
117
117
  /**
118
118
  * Current code path segments.
119
119
  * @type {CodePathSegment[]}
120
+ * @deprecated
120
121
  */
121
122
  get currentSegments() {
122
123
  return this.internal.currentSegments;
@@ -42,7 +42,8 @@ const
42
42
  ruleReplacements = require("../../conf/replacements.json");
43
43
  const { getRuleFromConfig } = require("../config/flat-config-helpers");
44
44
  const { FlatConfigArray } = require("../config/flat-config-array");
45
-
45
+ const { RuleValidator } = require("../config/rule-validator");
46
+ const { assertIsRuleOptions, assertIsRuleSeverity } = require("../config/flat-config-schema");
46
47
  const debug = require("debug")("eslint:linter");
47
48
  const MAX_AUTOFIX_PASSES = 10;
48
49
  const DEFAULT_PARSER_NAME = "espree";
@@ -50,7 +51,6 @@ const DEFAULT_ECMA_VERSION = 5;
50
51
  const commentParser = new ConfigCommentParser();
51
52
  const DEFAULT_ERROR_LOC = { start: { line: 1, column: 0 }, end: { line: 1, column: 1 } };
52
53
  const parserSymbol = Symbol.for("eslint.RuleTester.parser");
53
- const globals = require("../../conf/globals");
54
54
 
55
55
  //------------------------------------------------------------------------------
56
56
  // Typedefs
@@ -145,29 +145,6 @@ function isEspree(parser) {
145
145
  return !!(parser === espree || parser[parserSymbol] === espree);
146
146
  }
147
147
 
148
- /**
149
- * Retrieves globals for the given ecmaVersion.
150
- * @param {number} ecmaVersion The version to retrieve globals for.
151
- * @returns {Object} The globals for the given ecmaVersion.
152
- */
153
- function getGlobalsForEcmaVersion(ecmaVersion) {
154
-
155
- switch (ecmaVersion) {
156
- case 3:
157
- return globals.es3;
158
-
159
- case 5:
160
- return globals.es5;
161
-
162
- default:
163
- if (ecmaVersion < 2015) {
164
- return globals[`es${ecmaVersion + 2009}`];
165
- }
166
-
167
- return globals[`es${ecmaVersion}`];
168
- }
169
- }
170
-
171
148
  /**
172
149
  * Ensures that variables representing built-in properties of the Global Object,
173
150
  * and any globals declared by special block comments, are present in the global
@@ -361,13 +338,13 @@ function extractDirectiveComment(value) {
361
338
  * Parses comments in file to extract file-specific config of rules, globals
362
339
  * and environments and merges them with global config; also code blocks
363
340
  * where reporting is disabled or enabled and merges them with reporting config.
364
- * @param {ASTNode} ast The top node of the AST.
341
+ * @param {SourceCode} sourceCode The SourceCode object to get comments from.
365
342
  * @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
366
343
  * @param {string|null} warnInlineConfig If a string then it should warn directive comments as disabled. The string value is the config name what the setting came from.
367
344
  * @returns {{configuredRules: Object, enabledGlobals: {value:string,comment:Token}[], exportedVariables: Object, problems: LintMessage[], disableDirectives: DisableDirective[]}}
368
345
  * A collection of the directive comments that were found, along with any problems that occurred when parsing
369
346
  */
370
- function getDirectiveComments(ast, ruleMapper, warnInlineConfig) {
347
+ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig) {
371
348
  const configuredRules = {};
372
349
  const enabledGlobals = Object.create(null);
373
350
  const exportedVariables = {};
@@ -377,7 +354,7 @@ function getDirectiveComments(ast, ruleMapper, warnInlineConfig) {
377
354
  builtInRules: Rules
378
355
  });
379
356
 
380
- ast.comments.filter(token => token.type !== "Shebang").forEach(comment => {
357
+ sourceCode.getInlineConfigNodes().filter(token => token.type !== "Shebang").forEach(comment => {
381
358
  const { directivePart, justificationPart } = extractDirectiveComment(comment.value);
382
359
 
383
360
  const match = directivesPattern.exec(directivePart);
@@ -511,6 +488,69 @@ function getDirectiveComments(ast, ruleMapper, warnInlineConfig) {
511
488
  };
512
489
  }
513
490
 
491
+ /**
492
+ * Parses comments in file to extract disable directives.
493
+ * @param {SourceCode} sourceCode The SourceCode object to get comments from.
494
+ * @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
495
+ * @returns {{problems: LintMessage[], disableDirectives: DisableDirective[]}}
496
+ * A collection of the directive comments that were found, along with any problems that occurred when parsing
497
+ */
498
+ function getDirectiveCommentsForFlatConfig(sourceCode, ruleMapper) {
499
+ const problems = [];
500
+ const disableDirectives = [];
501
+
502
+ sourceCode.getInlineConfigNodes().filter(token => token.type !== "Shebang").forEach(comment => {
503
+ const { directivePart, justificationPart } = extractDirectiveComment(comment.value);
504
+
505
+ const match = directivesPattern.exec(directivePart);
506
+
507
+ if (!match) {
508
+ return;
509
+ }
510
+ const directiveText = match[1];
511
+ const lineCommentSupported = /^eslint-disable-(next-)?line$/u.test(directiveText);
512
+
513
+ if (comment.type === "Line" && !lineCommentSupported) {
514
+ return;
515
+ }
516
+
517
+ if (directiveText === "eslint-disable-line" && comment.loc.start.line !== comment.loc.end.line) {
518
+ const message = `${directiveText} comment should not span multiple lines.`;
519
+
520
+ problems.push(createLintingProblem({
521
+ ruleId: null,
522
+ message,
523
+ loc: comment.loc
524
+ }));
525
+ return;
526
+ }
527
+
528
+ const directiveValue = directivePart.slice(match.index + directiveText.length);
529
+
530
+ switch (directiveText) {
531
+ case "eslint-disable":
532
+ case "eslint-enable":
533
+ case "eslint-disable-next-line":
534
+ case "eslint-disable-line": {
535
+ const directiveType = directiveText.slice("eslint-".length);
536
+ const options = { commentToken: comment, type: directiveType, value: directiveValue, justification: justificationPart, ruleMapper };
537
+ const { directives, directiveProblems } = createDisableDirectives(options);
538
+
539
+ disableDirectives.push(...directives);
540
+ problems.push(...directiveProblems);
541
+ break;
542
+ }
543
+
544
+ // no default
545
+ }
546
+ });
547
+
548
+ return {
549
+ problems,
550
+ disableDirectives
551
+ };
552
+ }
553
+
514
554
  /**
515
555
  * Normalize ECMAScript version from the initial config
516
556
  * @param {Parser} parser The parser which uses this options.
@@ -898,6 +938,7 @@ const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
898
938
  getTokensBetween: "getTokensBetween"
899
939
  };
900
940
 
941
+
901
942
  const BASE_TRAVERSAL_CONTEXT = Object.freeze(
902
943
  Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).reduce(
903
944
  (contextInfo, methodName) =>
@@ -1312,7 +1353,7 @@ class Linter {
1312
1353
 
1313
1354
  const sourceCode = slots.lastSourceCode;
1314
1355
  const commentDirectives = options.allowInlineConfig
1315
- ? getDirectiveComments(sourceCode.ast, ruleId => getRule(slots, ruleId), options.warnInlineConfig)
1356
+ ? getDirectiveComments(sourceCode, ruleId => getRule(slots, ruleId), options.warnInlineConfig)
1316
1357
  : { configuredRules: {}, enabledGlobals: {}, exportedVariables: {}, problems: [], disableDirectives: [] };
1317
1358
 
1318
1359
  // augment global scope with declared global variables
@@ -1323,7 +1364,6 @@ class Linter {
1323
1364
  );
1324
1365
 
1325
1366
  const configuredRules = Object.assign({}, config.rules, commentDirectives.configuredRules);
1326
-
1327
1367
  let lintingProblems;
1328
1368
 
1329
1369
  try {
@@ -1539,19 +1579,6 @@ class Linter {
1539
1579
  languageOptions.ecmaVersion
1540
1580
  );
1541
1581
 
1542
- /*
1543
- * add configured globals and language globals
1544
- *
1545
- * using Object.assign instead of object spread for performance reasons
1546
- * https://github.com/eslint/eslint/issues/16302
1547
- */
1548
- const configuredGlobals = Object.assign(
1549
- {},
1550
- getGlobalsForEcmaVersion(languageOptions.ecmaVersion),
1551
- languageOptions.sourceType === "commonjs" ? globals.commonjs : void 0,
1552
- languageOptions.globals
1553
- );
1554
-
1555
1582
  // double check that there is a parser to avoid mysterious error messages
1556
1583
  if (!languageOptions.parser) {
1557
1584
  throw new TypeError(`No parser specified for ${options.filename}`);
@@ -1607,25 +1634,113 @@ class Linter {
1607
1634
  }
1608
1635
 
1609
1636
  const sourceCode = slots.lastSourceCode;
1610
- const commentDirectives = options.allowInlineConfig
1611
- ? getDirectiveComments(
1612
- sourceCode.ast,
1613
- ruleId => getRuleFromConfig(ruleId, config),
1614
- options.warnInlineConfig
1615
- )
1616
- : { configuredRules: {}, enabledGlobals: {}, exportedVariables: {}, problems: [], disableDirectives: [] };
1617
1637
 
1618
- // augment global scope with declared global variables
1619
- addDeclaredGlobals(
1620
- sourceCode.scopeManager.scopes[0],
1621
- configuredGlobals,
1622
- { exportedVariables: commentDirectives.exportedVariables, enabledGlobals: commentDirectives.enabledGlobals }
1623
- );
1638
+ /*
1639
+ * Make adjustments based on the language options. For JavaScript,
1640
+ * this is primarily about adding variables into the global scope
1641
+ * to account for ecmaVersion and configured globals.
1642
+ */
1643
+ sourceCode.applyLanguageOptions(languageOptions);
1624
1644
 
1625
- const configuredRules = Object.assign({}, config.rules, commentDirectives.configuredRules);
1645
+ const mergedInlineConfig = {
1646
+ rules: {}
1647
+ };
1648
+ const inlineConfigProblems = [];
1626
1649
 
1650
+ /*
1651
+ * Inline config can be either enabled or disabled. If disabled, it's possible
1652
+ * to detect the inline config and emit a warning (though this is not required).
1653
+ * So we first check to see if inline config is allowed at all, and if so, we
1654
+ * need to check if it's a warning or not.
1655
+ */
1656
+ if (options.allowInlineConfig) {
1657
+
1658
+ // if inline config should warn then add the warnings
1659
+ if (options.warnInlineConfig) {
1660
+ sourceCode.getInlineConfigNodes().forEach(node => {
1661
+ inlineConfigProblems.push(createLintingProblem({
1662
+ ruleId: null,
1663
+ message: `'${sourceCode.text.slice(node.range[0], node.range[1])}' has no effect because you have 'noInlineConfig' setting in ${options.warnInlineConfig}.`,
1664
+ loc: node.loc,
1665
+ severity: 1
1666
+ }));
1667
+
1668
+ });
1669
+ } else {
1670
+ const inlineConfigResult = sourceCode.applyInlineConfig();
1671
+
1672
+ inlineConfigProblems.push(
1673
+ ...inlineConfigResult.problems
1674
+ .map(createLintingProblem)
1675
+ .map(problem => {
1676
+ problem.fatal = true;
1677
+ return problem;
1678
+ })
1679
+ );
1680
+
1681
+ // next we need to verify information about the specified rules
1682
+ const ruleValidator = new RuleValidator();
1683
+
1684
+ for (const { config: inlineConfig, node } of inlineConfigResult.configs) {
1685
+
1686
+ Object.keys(inlineConfig.rules).forEach(ruleId => {
1687
+ const rule = getRuleFromConfig(ruleId, config);
1688
+ const ruleValue = inlineConfig.rules[ruleId];
1689
+
1690
+ if (!rule) {
1691
+ inlineConfigProblems.push(createLintingProblem({ ruleId, loc: node.loc }));
1692
+ return;
1693
+ }
1694
+
1695
+ try {
1696
+
1697
+ const ruleOptions = Array.isArray(ruleValue) ? ruleValue : [ruleValue];
1698
+
1699
+ assertIsRuleOptions(ruleId, ruleValue);
1700
+ assertIsRuleSeverity(ruleId, ruleOptions[0]);
1701
+
1702
+ ruleValidator.validate({
1703
+ plugins: config.plugins,
1704
+ rules: {
1705
+ [ruleId]: ruleOptions
1706
+ }
1707
+ });
1708
+ mergedInlineConfig.rules[ruleId] = ruleValue;
1709
+ } catch (err) {
1710
+
1711
+ let baseMessage = err.message.slice(
1712
+ err.message.startsWith("Key \"rules\":")
1713
+ ? err.message.indexOf(":", 12) + 1
1714
+ : err.message.indexOf(":") + 1
1715
+ ).trim();
1716
+
1717
+ if (err.messageTemplate) {
1718
+ baseMessage += ` You passed "${ruleValue}".`;
1719
+ }
1720
+
1721
+ inlineConfigProblems.push(createLintingProblem({
1722
+ ruleId,
1723
+ message: `Inline configuration for rule "${ruleId}" is invalid:\n\t${baseMessage}\n`,
1724
+ loc: node.loc
1725
+ }));
1726
+ }
1727
+ });
1728
+ }
1729
+ }
1730
+ }
1731
+
1732
+ const commentDirectives = options.allowInlineConfig && !options.warnInlineConfig
1733
+ ? getDirectiveCommentsForFlatConfig(
1734
+ sourceCode,
1735
+ ruleId => getRuleFromConfig(ruleId, config)
1736
+ )
1737
+ : { problems: [], disableDirectives: [] };
1738
+
1739
+ const configuredRules = Object.assign({}, config.rules, mergedInlineConfig.rules);
1627
1740
  let lintingProblems;
1628
1741
 
1742
+ sourceCode.finalize();
1743
+
1629
1744
  try {
1630
1745
  lintingProblems = runRules(
1631
1746
  sourceCode,
@@ -1666,6 +1781,7 @@ class Linter {
1666
1781
  disableFixes: options.disableFixes,
1667
1782
  problems: lintingProblems
1668
1783
  .concat(commentDirectives.problems)
1784
+ .concat(inlineConfigProblems)
1669
1785
  .sort((problemA, problemB) => problemA.line - problemB.line || problemA.column - problemB.column),
1670
1786
  reportUnusedDisableDirectives: options.reportUnusedDisableDirectives
1671
1787
  });
@@ -16,7 +16,9 @@ const
16
16
  equal = require("fast-deep-equal"),
17
17
  Traverser = require("../shared/traverser"),
18
18
  { getRuleOptionsSchema } = require("../config/flat-config-helpers"),
19
- { Linter, SourceCodeFixer, interpolate } = require("../linter");
19
+ { Linter, SourceCodeFixer, interpolate } = require("../linter"),
20
+ CodePath = require("../linter/code-path-analysis/code-path");
21
+
20
22
  const { FlatConfigArray } = require("../config/flat-config-array");
21
23
  const { defaultConfig } = require("../config/default-config");
22
24
 
@@ -131,6 +133,15 @@ const suggestionObjectParameters = new Set([
131
133
  ]);
132
134
  const friendlySuggestionObjectParameterList = `[${[...suggestionObjectParameters].map(key => `'${key}'`).join(", ")}]`;
133
135
 
136
+ const forbiddenMethods = [
137
+ "applyInlineConfig",
138
+ "applyLanguageOptions",
139
+ "finalize"
140
+ ];
141
+
142
+ /** @type {Map<string,WeakSet>} */
143
+ const forbiddenMethodCalls = new Map(forbiddenMethods.map(methodName => ([methodName, new WeakSet()])));
144
+
134
145
  const hasOwnProperty = Function.call.bind(Object.hasOwnProperty);
135
146
 
136
147
  /**
@@ -274,6 +285,49 @@ function getCommentsDeprecation() {
274
285
  );
275
286
  }
276
287
 
288
+ /**
289
+ * Emit a deprecation warning if rule uses CodePath#currentSegments.
290
+ * @param {string} ruleName Name of the rule.
291
+ * @returns {void}
292
+ */
293
+ function emitCodePathCurrentSegmentsWarning(ruleName) {
294
+ if (!emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`]) {
295
+ emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`] = true;
296
+ process.emitWarning(
297
+ `"${ruleName}" rule uses CodePath#currentSegments and will stop working in ESLint v9. Please read the documentation for how to update your code: https://eslint.org/docs/latest/extend/code-path-analysis#usage-examples`,
298
+ "DeprecationWarning"
299
+ );
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Function to replace forbidden `SourceCode` methods. Allows just one call per method.
305
+ * @param {string} methodName The name of the method to forbid.
306
+ * @param {Function} prototype The prototype with the original method to call.
307
+ * @returns {Function} The function that throws the error.
308
+ */
309
+ function throwForbiddenMethodError(methodName, prototype) {
310
+
311
+ const original = prototype[methodName];
312
+
313
+ return function(...args) {
314
+
315
+ const called = forbiddenMethodCalls.get(methodName);
316
+
317
+ /* eslint-disable no-invalid-this -- needed to operate as a method. */
318
+ if (!called.has(this)) {
319
+ called.add(this);
320
+
321
+ return original.apply(this, args);
322
+ }
323
+ /* eslint-enable no-invalid-this -- not needed past this point */
324
+
325
+ throw new Error(
326
+ `\`SourceCode#${methodName}()\` cannot be called inside a rule.`
327
+ );
328
+ };
329
+ }
330
+
277
331
  //------------------------------------------------------------------------------
278
332
  // Public Interface
279
333
  //------------------------------------------------------------------------------
@@ -481,6 +535,7 @@ class FlatRuleTester {
481
535
  }
482
536
 
483
537
  const baseConfig = [
538
+ { files: ["**"] }, // Make sure the default config matches for all files
484
539
  {
485
540
  plugins: {
486
541
 
@@ -662,10 +717,6 @@ class FlatRuleTester {
662
717
  }
663
718
  }
664
719
 
665
- // Verify the code.
666
- const { getComments } = SourceCode.prototype;
667
- let messages;
668
-
669
720
  // check for validation errors
670
721
  try {
671
722
  configs.normalizeSync();
@@ -675,13 +726,34 @@ class FlatRuleTester {
675
726
  throw error;
676
727
  }
677
728
 
729
+ // Verify the code.
730
+ const { getComments, applyLanguageOptions, applyInlineConfig, finalize } = SourceCode.prototype;
731
+ const originalCurrentSegments = Object.getOwnPropertyDescriptor(CodePath.prototype, "currentSegments");
732
+ let messages;
733
+
678
734
  try {
679
735
  SourceCode.prototype.getComments = getCommentsDeprecation;
736
+ Object.defineProperty(CodePath.prototype, "currentSegments", {
737
+ get() {
738
+ emitCodePathCurrentSegmentsWarning(ruleName);
739
+ return originalCurrentSegments.get.call(this);
740
+ }
741
+ });
742
+
743
+ forbiddenMethods.forEach(methodName => {
744
+ SourceCode.prototype[methodName] = throwForbiddenMethodError(methodName, SourceCode.prototype);
745
+ });
746
+
680
747
  messages = linter.verify(code, configs, filename);
681
748
  } finally {
682
749
  SourceCode.prototype.getComments = getComments;
750
+ Object.defineProperty(CodePath.prototype, "currentSegments", originalCurrentSegments);
751
+ SourceCode.prototype.applyInlineConfig = applyInlineConfig;
752
+ SourceCode.prototype.applyLanguageOptions = applyLanguageOptions;
753
+ SourceCode.prototype.finalize = finalize;
683
754
  }
684
755
 
756
+
685
757
  const fatalErrorMessage = messages.find(m => m.fatal);
686
758
 
687
759
  assert(!fatalErrorMessage, `A fatal parsing error occurred: ${fatalErrorMessage && fatalErrorMessage.message}`);