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 +2 -2
- package/lib/config/flat-config-schema.js +11 -1
- package/lib/config/rule-validator.js +2 -1
- package/lib/linter/code-path-analysis/code-path-analyzer.js +32 -24
- package/lib/linter/code-path-analysis/code-path.js +1 -0
- package/lib/linter/linter.js +173 -57
- package/lib/rule-tester/flat-rule-tester.js +77 -5
- package/lib/rule-tester/rule-tester.js +146 -3
- package/lib/rules/array-callback-return.js +175 -25
- package/lib/rules/consistent-return.js +32 -7
- package/lib/rules/constructor-super.js +37 -14
- package/lib/rules/getter-return.js +33 -8
- package/lib/rules/index.js +1 -0
- package/lib/rules/lines-between-class-members.js +92 -7
- package/lib/rules/no-fallthrough.js +42 -14
- 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/rules/no-this-before-super.js +38 -11
- package/lib/rules/no-unreachable-loop.js +47 -12
- package/lib/rules/no-unreachable.js +39 -10
- package/lib/rules/no-useless-return.js +35 -4
- package/lib/rules/require-atomic-updates.js +21 -7
- package/lib/source-code/source-code.js +350 -3
- package/package.json +11 -9
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
|
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
|
-
|
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
|
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
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
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
|
-
|
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
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
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(
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
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 = [];
|
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.
|
@@ -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
|
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
|
-
|
1619
|
-
|
1620
|
-
|
1621
|
-
|
1622
|
-
|
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
|
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}`);
|