eslint 8.49.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 +1 -1
- package/lib/config/flat-config-schema.js +11 -1
- package/lib/linter/linter.js +172 -57
- package/lib/rule-tester/flat-rule-tester.js +50 -5
- package/lib/rule-tester/rule-tester.js +78 -20
- package/lib/rules/array-callback-return.js +139 -14
- package/lib/rules/index.js +1 -0
- package/lib/rules/no-misleading-character-class.js +65 -15
- package/lib/rules/no-new-object.js +7 -0
- package/lib/rules/no-object-constructor.js +118 -0
- package/lib/source-code/source-code.js +350 -3
- package/package.json +2 -2
package/README.md
CHANGED
@@ -289,7 +289,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
|
|
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
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://
|
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
|
-
|
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
|
+
};
|
package/lib/linter/linter.js
CHANGED
@@ -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 {
|
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(
|
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
|
-
|
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.
|
@@ -1313,7 +1353,7 @@ class Linter {
|
|
1313
1353
|
|
1314
1354
|
const sourceCode = slots.lastSourceCode;
|
1315
1355
|
const commentDirectives = options.allowInlineConfig
|
1316
|
-
? getDirectiveComments(sourceCode
|
1356
|
+
? getDirectiveComments(sourceCode, ruleId => getRule(slots, ruleId), options.warnInlineConfig)
|
1317
1357
|
: { configuredRules: {}, enabledGlobals: {}, exportedVariables: {}, problems: [], disableDirectives: [] };
|
1318
1358
|
|
1319
1359
|
// augment global scope with declared global variables
|
@@ -1324,7 +1364,6 @@ class Linter {
|
|
1324
1364
|
);
|
1325
1365
|
|
1326
1366
|
const configuredRules = Object.assign({}, config.rules, commentDirectives.configuredRules);
|
1327
|
-
|
1328
1367
|
let lintingProblems;
|
1329
1368
|
|
1330
1369
|
try {
|
@@ -1540,19 +1579,6 @@ class Linter {
|
|
1540
1579
|
languageOptions.ecmaVersion
|
1541
1580
|
);
|
1542
1581
|
|
1543
|
-
/*
|
1544
|
-
* add configured globals and language globals
|
1545
|
-
*
|
1546
|
-
* using Object.assign instead of object spread for performance reasons
|
1547
|
-
* https://github.com/eslint/eslint/issues/16302
|
1548
|
-
*/
|
1549
|
-
const configuredGlobals = Object.assign(
|
1550
|
-
{},
|
1551
|
-
getGlobalsForEcmaVersion(languageOptions.ecmaVersion),
|
1552
|
-
languageOptions.sourceType === "commonjs" ? globals.commonjs : void 0,
|
1553
|
-
languageOptions.globals
|
1554
|
-
);
|
1555
|
-
|
1556
1582
|
// double check that there is a parser to avoid mysterious error messages
|
1557
1583
|
if (!languageOptions.parser) {
|
1558
1584
|
throw new TypeError(`No parser specified for ${options.filename}`);
|
@@ -1608,25 +1634,113 @@ class Linter {
|
|
1608
1634
|
}
|
1609
1635
|
|
1610
1636
|
const sourceCode = slots.lastSourceCode;
|
1611
|
-
const commentDirectives = options.allowInlineConfig
|
1612
|
-
? getDirectiveComments(
|
1613
|
-
sourceCode.ast,
|
1614
|
-
ruleId => getRuleFromConfig(ruleId, config),
|
1615
|
-
options.warnInlineConfig
|
1616
|
-
)
|
1617
|
-
: { configuredRules: {}, enabledGlobals: {}, exportedVariables: {}, problems: [], disableDirectives: [] };
|
1618
1637
|
|
1619
|
-
|
1620
|
-
|
1621
|
-
|
1622
|
-
|
1623
|
-
|
1624
|
-
);
|
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);
|
1625
1644
|
|
1626
|
-
const
|
1645
|
+
const mergedInlineConfig = {
|
1646
|
+
rules: {}
|
1647
|
+
};
|
1648
|
+
const inlineConfigProblems = [];
|
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
|
+
}
|
1627
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);
|
1628
1740
|
let lintingProblems;
|
1629
1741
|
|
1742
|
+
sourceCode.finalize();
|
1743
|
+
|
1630
1744
|
try {
|
1631
1745
|
lintingProblems = runRules(
|
1632
1746
|
sourceCode,
|
@@ -1667,6 +1781,7 @@ class Linter {
|
|
1667
1781
|
disableFixes: options.disableFixes,
|
1668
1782
|
problems: lintingProblems
|
1669
1783
|
.concat(commentDirectives.problems)
|
1784
|
+
.concat(inlineConfigProblems)
|
1670
1785
|
.sort((problemA, problemB) => problemA.line - problemB.line || problemA.column - problemB.column),
|
1671
1786
|
reportUnusedDisableDirectives: options.reportUnusedDisableDirectives
|
1672
1787
|
});
|
@@ -133,6 +133,15 @@ const suggestionObjectParameters = new Set([
|
|
133
133
|
]);
|
134
134
|
const friendlySuggestionObjectParameterList = `[${[...suggestionObjectParameters].map(key => `'${key}'`).join(", ")}]`;
|
135
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
|
+
|
136
145
|
const hasOwnProperty = Function.call.bind(Object.hasOwnProperty);
|
137
146
|
|
138
147
|
/**
|
@@ -291,6 +300,34 @@ function emitCodePathCurrentSegmentsWarning(ruleName) {
|
|
291
300
|
}
|
292
301
|
}
|
293
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
|
+
|
294
331
|
//------------------------------------------------------------------------------
|
295
332
|
// Public Interface
|
296
333
|
//------------------------------------------------------------------------------
|
@@ -498,6 +535,7 @@ class FlatRuleTester {
|
|
498
535
|
}
|
499
536
|
|
500
537
|
const baseConfig = [
|
538
|
+
{ files: ["**"] }, // Make sure the default config matches for all files
|
501
539
|
{
|
502
540
|
plugins: {
|
503
541
|
|
@@ -679,11 +717,6 @@ class FlatRuleTester {
|
|
679
717
|
}
|
680
718
|
}
|
681
719
|
|
682
|
-
// Verify the code.
|
683
|
-
const { getComments } = SourceCode.prototype;
|
684
|
-
const originalCurrentSegments = Object.getOwnPropertyDescriptor(CodePath.prototype, "currentSegments");
|
685
|
-
let messages;
|
686
|
-
|
687
720
|
// check for validation errors
|
688
721
|
try {
|
689
722
|
configs.normalizeSync();
|
@@ -693,6 +726,11 @@ class FlatRuleTester {
|
|
693
726
|
throw error;
|
694
727
|
}
|
695
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
|
+
|
696
734
|
try {
|
697
735
|
SourceCode.prototype.getComments = getCommentsDeprecation;
|
698
736
|
Object.defineProperty(CodePath.prototype, "currentSegments", {
|
@@ -702,10 +740,17 @@ class FlatRuleTester {
|
|
702
740
|
}
|
703
741
|
});
|
704
742
|
|
743
|
+
forbiddenMethods.forEach(methodName => {
|
744
|
+
SourceCode.prototype[methodName] = throwForbiddenMethodError(methodName, SourceCode.prototype);
|
745
|
+
});
|
746
|
+
|
705
747
|
messages = linter.verify(code, configs, filename);
|
706
748
|
} finally {
|
707
749
|
SourceCode.prototype.getComments = getComments;
|
708
750
|
Object.defineProperty(CodePath.prototype, "currentSegments", originalCurrentSegments);
|
751
|
+
SourceCode.prototype.applyInlineConfig = applyInlineConfig;
|
752
|
+
SourceCode.prototype.applyLanguageOptions = applyLanguageOptions;
|
753
|
+
SourceCode.prototype.finalize = finalize;
|
709
754
|
}
|
710
755
|
|
711
756
|
|
@@ -163,6 +163,12 @@ const suggestionObjectParameters = new Set([
|
|
163
163
|
]);
|
164
164
|
const friendlySuggestionObjectParameterList = `[${[...suggestionObjectParameters].map(key => `'${key}'`).join(", ")}]`;
|
165
165
|
|
166
|
+
const forbiddenMethods = [
|
167
|
+
"applyInlineConfig",
|
168
|
+
"applyLanguageOptions",
|
169
|
+
"finalize"
|
170
|
+
];
|
171
|
+
|
166
172
|
const hasOwnProperty = Function.call.bind(Object.hasOwnProperty);
|
167
173
|
|
168
174
|
const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
|
@@ -186,7 +192,12 @@ const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
|
|
186
192
|
getTokens: "getTokens",
|
187
193
|
getTokensAfter: "getTokensAfter",
|
188
194
|
getTokensBefore: "getTokensBefore",
|
189
|
-
getTokensBetween: "getTokensBetween"
|
195
|
+
getTokensBetween: "getTokensBetween",
|
196
|
+
|
197
|
+
getScope: "getScope",
|
198
|
+
getAncestors: "getAncestors",
|
199
|
+
getDeclaredVariables: "getDeclaredVariables",
|
200
|
+
markVariableAsUsed: "markVariableAsUsed"
|
190
201
|
};
|
191
202
|
|
192
203
|
/**
|
@@ -330,6 +341,19 @@ function getCommentsDeprecation() {
|
|
330
341
|
);
|
331
342
|
}
|
332
343
|
|
344
|
+
/**
|
345
|
+
* Function to replace forbidden `SourceCode` methods.
|
346
|
+
* @param {string} methodName The name of the method to forbid.
|
347
|
+
* @returns {Function} The function that throws the error.
|
348
|
+
*/
|
349
|
+
function throwForbiddenMethodError(methodName) {
|
350
|
+
return () => {
|
351
|
+
throw new Error(
|
352
|
+
`\`SourceCode#${methodName}()\` cannot be called inside a rule.`
|
353
|
+
);
|
354
|
+
};
|
355
|
+
}
|
356
|
+
|
333
357
|
/**
|
334
358
|
* Emit a deprecation warning if function-style format is being used.
|
335
359
|
* @param {string} ruleName Name of the rule.
|
@@ -391,6 +415,22 @@ function emitCodePathCurrentSegmentsWarning(ruleName) {
|
|
391
415
|
}
|
392
416
|
}
|
393
417
|
|
418
|
+
/**
|
419
|
+
* Emit a deprecation warning if `context.parserServices` is used.
|
420
|
+
* @param {string} ruleName Name of the rule.
|
421
|
+
* @returns {void}
|
422
|
+
*/
|
423
|
+
function emitParserServicesWarning(ruleName) {
|
424
|
+
if (!emitParserServicesWarning[`warned-${ruleName}`]) {
|
425
|
+
emitParserServicesWarning[`warned-${ruleName}`] = true;
|
426
|
+
process.emitWarning(
|
427
|
+
`"${ruleName}" rule is using \`context.parserServices\`, which is deprecated and will be removed in ESLint v9. Please use \`sourceCode.parserServices\` instead.`,
|
428
|
+
"DeprecationWarning"
|
429
|
+
);
|
430
|
+
}
|
431
|
+
}
|
432
|
+
|
433
|
+
|
394
434
|
//------------------------------------------------------------------------------
|
395
435
|
// Public Interface
|
396
436
|
//------------------------------------------------------------------------------
|
@@ -622,26 +662,37 @@ class RuleTester {
|
|
622
662
|
freezeDeeply(context.settings);
|
623
663
|
freezeDeeply(context.parserOptions);
|
624
664
|
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
)
|
665
|
+
// wrap all deprecated methods
|
666
|
+
const newContext = Object.create(
|
667
|
+
context,
|
668
|
+
Object.fromEntries(Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).map(methodName => [
|
669
|
+
methodName,
|
670
|
+
{
|
671
|
+
value(...args) {
|
672
|
+
|
673
|
+
// emit deprecation warning
|
674
|
+
emitDeprecatedContextMethodWarning(ruleName, methodName);
|
675
|
+
|
676
|
+
// call the original method
|
677
|
+
return context[methodName].call(this, ...args);
|
678
|
+
},
|
679
|
+
enumerable: true
|
680
|
+
}
|
681
|
+
]))
|
643
682
|
);
|
644
683
|
|
684
|
+
// emit warning about context.parserServices
|
685
|
+
const parserServices = context.parserServices;
|
686
|
+
|
687
|
+
Object.defineProperty(newContext, "parserServices", {
|
688
|
+
get() {
|
689
|
+
emitParserServicesWarning(ruleName);
|
690
|
+
return parserServices;
|
691
|
+
}
|
692
|
+
});
|
693
|
+
|
694
|
+
Object.freeze(newContext);
|
695
|
+
|
645
696
|
return (typeof rule === "function" ? rule : rule.create)(newContext);
|
646
697
|
}
|
647
698
|
}));
|
@@ -761,7 +812,7 @@ class RuleTester {
|
|
761
812
|
validate(config, "rule-tester", id => (id === ruleName ? rule : null));
|
762
813
|
|
763
814
|
// Verify the code.
|
764
|
-
const { getComments } = SourceCode.prototype;
|
815
|
+
const { getComments, applyLanguageOptions, applyInlineConfig, finalize } = SourceCode.prototype;
|
765
816
|
const originalCurrentSegments = Object.getOwnPropertyDescriptor(CodePath.prototype, "currentSegments");
|
766
817
|
let messages;
|
767
818
|
|
@@ -774,10 +825,17 @@ class RuleTester {
|
|
774
825
|
}
|
775
826
|
});
|
776
827
|
|
828
|
+
forbiddenMethods.forEach(methodName => {
|
829
|
+
SourceCode.prototype[methodName] = throwForbiddenMethodError(methodName);
|
830
|
+
});
|
831
|
+
|
777
832
|
messages = linter.verify(code, config, filename);
|
778
833
|
} finally {
|
779
834
|
SourceCode.prototype.getComments = getComments;
|
780
835
|
Object.defineProperty(CodePath.prototype, "currentSegments", originalCurrentSegments);
|
836
|
+
SourceCode.prototype.applyInlineConfig = applyInlineConfig;
|
837
|
+
SourceCode.prototype.applyLanguageOptions = applyLanguageOptions;
|
838
|
+
SourceCode.prototype.finalize = finalize;
|
781
839
|
}
|
782
840
|
|
783
841
|
const fatalErrorMessage = messages.find(m => m.fatal);
|