eslint 9.5.0 → 9.7.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 +7 -8
- package/conf/ecma-version.js +1 -1
- package/conf/globals.js +6 -1
- package/lib/cli.js +23 -2
- package/lib/eslint/eslint-helpers.js +5 -0
- package/lib/eslint/eslint.js +16 -2
- package/lib/eslint/legacy-eslint.js +14 -0
- package/lib/languages/js/index.js +10 -5
- package/lib/languages/js/source-code/source-code.js +16 -6
- package/lib/linter/apply-disable-directives.js +6 -3
- package/lib/linter/config-comment-parser.js +3 -16
- package/lib/linter/linter.js +194 -149
- package/lib/linter/node-event-generator.js +2 -4
- package/lib/linter/vfile.js +7 -0
- package/lib/options.js +13 -1
- package/lib/rules/no-restricted-imports.js +12 -5
- package/lib/rules/no-unused-vars.js +34 -32
- package/lib/rules/no-useless-backreference.js +81 -31
- package/lib/rules/utils/regular-expressions.js +1 -1
- package/lib/shared/flags.js +26 -0
- package/lib/shared/logging.js +10 -1
- package/lib/shared/types.js +1 -1
- package/package.json +9 -13
package/lib/linter/linter.js
CHANGED
@@ -45,6 +45,7 @@ const { RuleValidator } = require("../config/rule-validator");
|
|
45
45
|
const { assertIsRuleSeverity } = require("../config/flat-config-schema");
|
46
46
|
const { normalizeSeverityToString } = require("../shared/severity");
|
47
47
|
const jslang = require("../languages/js");
|
48
|
+
const { activeFlags } = require("../shared/flags");
|
48
49
|
const debug = require("debug")("eslint:linter");
|
49
50
|
const MAX_AUTOFIX_PASSES = 10;
|
50
51
|
const DEFAULT_PARSER_NAME = "espree";
|
@@ -71,6 +72,10 @@ const STEP_KIND_CALL = 2;
|
|
71
72
|
/** @typedef {import("../shared/types").Processor} Processor */
|
72
73
|
/** @typedef {import("../shared/types").Rule} Rule */
|
73
74
|
/** @typedef {import("../shared/types").Times} Times */
|
75
|
+
/** @typedef {import("@eslint/core").Language} Language */
|
76
|
+
/** @typedef {import("@eslint/core").RuleSeverity} RuleSeverity */
|
77
|
+
/** @typedef {import("@eslint/core").RuleConfig} RuleConfig */
|
78
|
+
|
74
79
|
|
75
80
|
/* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
|
76
81
|
/**
|
@@ -275,7 +280,7 @@ function updateLocationInformation({ line, column, endLine, endColumn }, languag
|
|
275
280
|
* @param {string} [options.ruleId] the ruleId to report
|
276
281
|
* @param {Object} [options.loc] the loc to report
|
277
282
|
* @param {string} [options.message] the error message to report
|
278
|
-
* @param {
|
283
|
+
* @param {RuleSeverity} [options.severity] the error message to report
|
279
284
|
* @param {Language} [options.language] the language to use to adjust the location information
|
280
285
|
* @returns {LintMessage} created problem, returns a missing-rule problem if only provided ruleId.
|
281
286
|
* @private
|
@@ -317,9 +322,10 @@ function createLintingProblem(options) {
|
|
317
322
|
* @param {string} options.justification The justification of the directive
|
318
323
|
* @param {ASTNode|token} options.node The Comment node/token.
|
319
324
|
* @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
|
325
|
+
* @param {Language} language The language to use to adjust the location information.
|
320
326
|
* @returns {Object} Directives and problems from the comment
|
321
327
|
*/
|
322
|
-
function createDisableDirectives({ type, value, justification, node }, ruleMapper) {
|
328
|
+
function createDisableDirectives({ type, value, justification, node }, ruleMapper, language) {
|
323
329
|
const ruleIds = Object.keys(commentParser.parseListConfig(value));
|
324
330
|
const directiveRules = ruleIds.length ? ruleIds : [null];
|
325
331
|
const result = {
|
@@ -333,26 +339,36 @@ function createDisableDirectives({ type, value, justification, node }, ruleMappe
|
|
333
339
|
// push to directives, if the rule is defined(including null, e.g. /*eslint enable*/)
|
334
340
|
if (ruleId === null || !!ruleMapper(ruleId)) {
|
335
341
|
if (type === "disable-next-line") {
|
342
|
+
const { line, column } = updateLocationInformation(
|
343
|
+
node.loc.end,
|
344
|
+
language
|
345
|
+
);
|
346
|
+
|
336
347
|
result.directives.push({
|
337
348
|
parentDirective,
|
338
349
|
type,
|
339
|
-
line
|
340
|
-
column
|
350
|
+
line,
|
351
|
+
column,
|
341
352
|
ruleId,
|
342
353
|
justification
|
343
354
|
});
|
344
355
|
} else {
|
356
|
+
const { line, column } = updateLocationInformation(
|
357
|
+
node.loc.start,
|
358
|
+
language
|
359
|
+
);
|
360
|
+
|
345
361
|
result.directives.push({
|
346
362
|
parentDirective,
|
347
363
|
type,
|
348
|
-
line
|
349
|
-
column
|
364
|
+
line,
|
365
|
+
column,
|
350
366
|
ruleId,
|
351
367
|
justification
|
352
368
|
});
|
353
369
|
}
|
354
370
|
} else {
|
355
|
-
result.directiveProblems.push(createLintingProblem({ ruleId, loc: node.loc }));
|
371
|
+
result.directiveProblems.push(createLintingProblem({ ruleId, loc: node.loc, language }));
|
356
372
|
}
|
357
373
|
}
|
358
374
|
return result;
|
@@ -430,7 +446,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
430
446
|
value: directiveValue,
|
431
447
|
justification: justificationPart,
|
432
448
|
node: comment
|
433
|
-
}, ruleMapper);
|
449
|
+
}, ruleMapper, jslang);
|
434
450
|
|
435
451
|
disableDirectives.push(...directives);
|
436
452
|
problems.push(...directiveProblems);
|
@@ -470,7 +486,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
470
486
|
break;
|
471
487
|
|
472
488
|
case "eslint": {
|
473
|
-
const parseResult = commentParser.parseJsonConfig(directiveValue
|
489
|
+
const parseResult = commentParser.parseJsonConfig(directiveValue);
|
474
490
|
|
475
491
|
if (parseResult.success) {
|
476
492
|
Object.keys(parseResult.config).forEach(name => {
|
@@ -557,7 +573,14 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
557
573
|
configuredRules[name] = ruleOptions;
|
558
574
|
});
|
559
575
|
} else {
|
560
|
-
|
576
|
+
const problem = createLintingProblem({
|
577
|
+
ruleId: null,
|
578
|
+
loc: comment.loc,
|
579
|
+
message: parseResult.error.message
|
580
|
+
});
|
581
|
+
|
582
|
+
problem.fatal = true;
|
583
|
+
problems.push(problem);
|
561
584
|
}
|
562
585
|
|
563
586
|
break;
|
@@ -588,22 +611,24 @@ function getDirectiveCommentsForFlatConfig(sourceCode, ruleMapper, language) {
|
|
588
611
|
const disableDirectives = [];
|
589
612
|
const problems = [];
|
590
613
|
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
614
|
+
if (sourceCode.getDisableDirectives) {
|
615
|
+
const {
|
616
|
+
directives: directivesSources,
|
617
|
+
problems: directivesProblems
|
618
|
+
} = sourceCode.getDisableDirectives();
|
595
619
|
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
620
|
+
problems.push(...directivesProblems.map(directiveProblem => createLintingProblem({
|
621
|
+
...directiveProblem,
|
622
|
+
language
|
623
|
+
})));
|
600
624
|
|
601
|
-
|
602
|
-
|
625
|
+
directivesSources.forEach(directive => {
|
626
|
+
const { directives, directiveProblems } = createDisableDirectives(directive, ruleMapper, language);
|
603
627
|
|
604
|
-
|
605
|
-
|
606
|
-
|
628
|
+
disableDirectives.push(...directives);
|
629
|
+
problems.push(...directiveProblems);
|
630
|
+
});
|
631
|
+
}
|
607
632
|
|
608
633
|
return {
|
609
634
|
problems,
|
@@ -856,7 +881,7 @@ function storeTime(time, timeOpts, slots) {
|
|
856
881
|
|
857
882
|
/**
|
858
883
|
* Get the options for a rule (not including severity), if any
|
859
|
-
* @param {
|
884
|
+
* @param {RuleConfig} ruleConfig rule configuration
|
860
885
|
* @returns {Array} of rule options, empty Array if none
|
861
886
|
*/
|
862
887
|
function getRuleOptions(ruleConfig) {
|
@@ -920,7 +945,7 @@ function parse(file, language, languageOptions) {
|
|
920
945
|
nodeType: null,
|
921
946
|
fatal: true,
|
922
947
|
severity: 2,
|
923
|
-
message: error.message
|
948
|
+
message: `Parsing error: ${error.message}`,
|
924
949
|
line: error.line,
|
925
950
|
column: error.column
|
926
951
|
}))
|
@@ -1122,9 +1147,9 @@ function runRules(
|
|
1122
1147
|
});
|
1123
1148
|
|
1124
1149
|
const eventGenerator = new NodeEventGenerator(emitter, {
|
1125
|
-
visitorKeys: sourceCode.visitorKeys,
|
1150
|
+
visitorKeys: sourceCode.visitorKeys ?? language.visitorKeys,
|
1126
1151
|
fallback: Traverser.getKeys,
|
1127
|
-
matchClass: language.matchesSelectorClass,
|
1152
|
+
matchClass: language.matchesSelectorClass ?? (() => false),
|
1128
1153
|
nodeTypeKey: language.nodeTypeKey
|
1129
1154
|
});
|
1130
1155
|
|
@@ -1253,11 +1278,13 @@ class Linter {
|
|
1253
1278
|
* Initialize the Linter.
|
1254
1279
|
* @param {Object} [config] the config object
|
1255
1280
|
* @param {string} [config.cwd] path to a directory that should be considered as the current working directory, can be undefined.
|
1281
|
+
* @param {Array<string>} [config.flags] the feature flags to enable.
|
1256
1282
|
* @param {"flat"|"eslintrc"} [config.configType="flat"] the type of config used.
|
1257
1283
|
*/
|
1258
|
-
constructor({ cwd, configType = "flat" } = {}) {
|
1284
|
+
constructor({ cwd, configType = "flat", flags = [] } = {}) {
|
1259
1285
|
internalSlotsMap.set(this, {
|
1260
1286
|
cwd: normalizeCwd(cwd),
|
1287
|
+
flags: flags.filter(flag => activeFlags.has(flag)),
|
1261
1288
|
lastConfigArray: null,
|
1262
1289
|
lastSourceCode: null,
|
1263
1290
|
lastSuppressedMessages: [],
|
@@ -1278,6 +1305,15 @@ class Linter {
|
|
1278
1305
|
return pkg.version;
|
1279
1306
|
}
|
1280
1307
|
|
1308
|
+
/**
|
1309
|
+
* Indicates if the given feature flag is enabled for this instance.
|
1310
|
+
* @param {string} flag The feature flag to check.
|
1311
|
+
* @returns {boolean} `true` if the feature flag is enabled, `false` if not.
|
1312
|
+
*/
|
1313
|
+
hasFlag(flag) {
|
1314
|
+
return internalSlotsMap.get(this).flags.includes(flag);
|
1315
|
+
}
|
1316
|
+
|
1281
1317
|
/**
|
1282
1318
|
* Same as linter.verify, except without support for processors.
|
1283
1319
|
* @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
|
@@ -1679,8 +1715,13 @@ class Linter {
|
|
1679
1715
|
/*
|
1680
1716
|
* If the given source code object as the first argument does not have scopeManager, analyze the scope.
|
1681
1717
|
* This is for backward compatibility (SourceCode is frozen so it cannot rebind).
|
1718
|
+
*
|
1719
|
+
* We check explicitly for `null` to ensure that this is a JS-flavored language.
|
1720
|
+
* For non-JS languages we don't want to do this.
|
1721
|
+
*
|
1722
|
+
* TODO: Remove this check when we stop exporting the `SourceCode` object.
|
1682
1723
|
*/
|
1683
|
-
if (
|
1724
|
+
if (slots.lastSourceCode.scopeManager === null) {
|
1684
1725
|
slots.lastSourceCode = new SourceCode({
|
1685
1726
|
text: slots.lastSourceCode.text,
|
1686
1727
|
ast: slots.lastSourceCode.ast,
|
@@ -1699,7 +1740,7 @@ class Linter {
|
|
1699
1740
|
* this is primarily about adding variables into the global scope
|
1700
1741
|
* to account for ecmaVersion and configured globals.
|
1701
1742
|
*/
|
1702
|
-
sourceCode.applyLanguageOptions(languageOptions);
|
1743
|
+
sourceCode.applyLanguageOptions?.(languageOptions);
|
1703
1744
|
|
1704
1745
|
const mergedInlineConfig = {
|
1705
1746
|
rules: {}
|
@@ -1716,147 +1757,151 @@ class Linter {
|
|
1716
1757
|
|
1717
1758
|
// if inline config should warn then add the warnings
|
1718
1759
|
if (options.warnInlineConfig) {
|
1719
|
-
sourceCode.getInlineConfigNodes
|
1720
|
-
|
1721
|
-
|
1722
|
-
|
1723
|
-
|
1724
|
-
|
1725
|
-
|
1726
|
-
|
1760
|
+
if (sourceCode.getInlineConfigNodes) {
|
1761
|
+
sourceCode.getInlineConfigNodes().forEach(node => {
|
1762
|
+
inlineConfigProblems.push(createLintingProblem({
|
1763
|
+
ruleId: null,
|
1764
|
+
message: `'${sourceCode.text.slice(node.range[0], node.range[1])}' has no effect because you have 'noInlineConfig' setting in ${options.warnInlineConfig}.`,
|
1765
|
+
loc: node.loc,
|
1766
|
+
severity: 1,
|
1767
|
+
language: config.language
|
1768
|
+
}));
|
1727
1769
|
|
1728
|
-
|
1770
|
+
});
|
1771
|
+
}
|
1729
1772
|
} else {
|
1730
|
-
const inlineConfigResult = sourceCode.applyInlineConfig();
|
1731
|
-
|
1732
|
-
|
1733
|
-
|
1734
|
-
|
1735
|
-
|
1736
|
-
problem
|
1737
|
-
|
1738
|
-
|
1739
|
-
|
1773
|
+
const inlineConfigResult = sourceCode.applyInlineConfig?.();
|
1774
|
+
|
1775
|
+
if (inlineConfigResult) {
|
1776
|
+
inlineConfigProblems.push(
|
1777
|
+
...inlineConfigResult.problems
|
1778
|
+
.map(problem => createLintingProblem({ ...problem, language: config.language }))
|
1779
|
+
.map(problem => {
|
1780
|
+
problem.fatal = true;
|
1781
|
+
return problem;
|
1782
|
+
})
|
1783
|
+
);
|
1784
|
+
|
1785
|
+
// next we need to verify information about the specified rules
|
1786
|
+
const ruleValidator = new RuleValidator();
|
1787
|
+
|
1788
|
+
for (const { config: inlineConfig, loc } of inlineConfigResult.configs) {
|
1789
|
+
|
1790
|
+
Object.keys(inlineConfig.rules).forEach(ruleId => {
|
1791
|
+
const rule = getRuleFromConfig(ruleId, config);
|
1792
|
+
const ruleValue = inlineConfig.rules[ruleId];
|
1793
|
+
|
1794
|
+
if (!rule) {
|
1795
|
+
inlineConfigProblems.push(createLintingProblem({
|
1796
|
+
ruleId,
|
1797
|
+
loc,
|
1798
|
+
language: config.language
|
1799
|
+
}));
|
1800
|
+
return;
|
1801
|
+
}
|
1740
1802
|
|
1741
|
-
|
1742
|
-
|
1803
|
+
if (Object.hasOwn(mergedInlineConfig.rules, ruleId)) {
|
1804
|
+
inlineConfigProblems.push(createLintingProblem({
|
1805
|
+
message: `Rule "${ruleId}" is already configured by another configuration comment in the preceding code. This configuration is ignored.`,
|
1806
|
+
loc,
|
1807
|
+
language: config.language
|
1808
|
+
}));
|
1809
|
+
return;
|
1810
|
+
}
|
1743
1811
|
|
1744
|
-
|
1812
|
+
try {
|
1745
1813
|
|
1746
|
-
|
1747
|
-
const rule = getRuleFromConfig(ruleId, config);
|
1748
|
-
const ruleValue = inlineConfig.rules[ruleId];
|
1814
|
+
let ruleOptions = Array.isArray(ruleValue) ? ruleValue : [ruleValue];
|
1749
1815
|
|
1750
|
-
|
1751
|
-
inlineConfigProblems.push(createLintingProblem({
|
1752
|
-
ruleId,
|
1753
|
-
loc: node.loc,
|
1754
|
-
language: config.language
|
1755
|
-
}));
|
1756
|
-
return;
|
1757
|
-
}
|
1816
|
+
assertIsRuleSeverity(ruleId, ruleOptions[0]);
|
1758
1817
|
|
1759
|
-
|
1760
|
-
|
1761
|
-
|
1762
|
-
|
1763
|
-
|
1764
|
-
|
1765
|
-
|
1766
|
-
|
1818
|
+
/*
|
1819
|
+
* If the rule was already configured, inline rule configuration that
|
1820
|
+
* only has severity should retain options from the config and just override the severity.
|
1821
|
+
*
|
1822
|
+
* Example:
|
1823
|
+
*
|
1824
|
+
* {
|
1825
|
+
* rules: {
|
1826
|
+
* curly: ["error", "multi"]
|
1827
|
+
* }
|
1828
|
+
* }
|
1829
|
+
*
|
1830
|
+
* /* eslint curly: ["warn"] * /
|
1831
|
+
*
|
1832
|
+
* Results in:
|
1833
|
+
*
|
1834
|
+
* curly: ["warn", "multi"]
|
1835
|
+
*/
|
1767
1836
|
|
1768
|
-
|
1837
|
+
let shouldValidateOptions = true;
|
1769
1838
|
|
1770
|
-
|
1839
|
+
if (
|
1771
1840
|
|
1772
|
-
|
1841
|
+
/*
|
1842
|
+
* If inline config for the rule has only severity
|
1843
|
+
*/
|
1844
|
+
ruleOptions.length === 1 &&
|
1773
1845
|
|
1774
|
-
|
1775
|
-
|
1776
|
-
|
1777
|
-
|
1778
|
-
|
1779
|
-
*
|
1780
|
-
* {
|
1781
|
-
* rules: {
|
1782
|
-
* curly: ["error", "multi"]
|
1783
|
-
* }
|
1784
|
-
* }
|
1785
|
-
*
|
1786
|
-
* /* eslint curly: ["warn"] * /
|
1787
|
-
*
|
1788
|
-
* Results in:
|
1789
|
-
*
|
1790
|
-
* curly: ["warn", "multi"]
|
1791
|
-
*/
|
1846
|
+
/*
|
1847
|
+
* And the rule was already configured
|
1848
|
+
*/
|
1849
|
+
config.rules && Object.hasOwn(config.rules, ruleId)
|
1850
|
+
) {
|
1792
1851
|
|
1793
|
-
|
1852
|
+
/*
|
1853
|
+
* Then use severity from the inline config and options from the provided config
|
1854
|
+
*/
|
1855
|
+
ruleOptions = [
|
1856
|
+
ruleOptions[0], // severity from the inline config
|
1857
|
+
...config.rules[ruleId].slice(1) // options from the provided config
|
1858
|
+
];
|
1794
1859
|
|
1795
|
-
|
1860
|
+
// if the rule was enabled, the options have already been validated
|
1861
|
+
if (config.rules[ruleId][0] > 0) {
|
1862
|
+
shouldValidateOptions = false;
|
1863
|
+
}
|
1864
|
+
}
|
1796
1865
|
|
1797
|
-
|
1798
|
-
|
1799
|
-
|
1800
|
-
|
1866
|
+
if (shouldValidateOptions) {
|
1867
|
+
ruleValidator.validate({
|
1868
|
+
plugins: config.plugins,
|
1869
|
+
rules: {
|
1870
|
+
[ruleId]: ruleOptions
|
1871
|
+
}
|
1872
|
+
});
|
1873
|
+
}
|
1801
1874
|
|
1802
|
-
|
1803
|
-
|
1804
|
-
*/
|
1805
|
-
config.rules && Object.hasOwn(config.rules, ruleId)
|
1806
|
-
) {
|
1875
|
+
mergedInlineConfig.rules[ruleId] = ruleOptions;
|
1876
|
+
} catch (err) {
|
1807
1877
|
|
1808
1878
|
/*
|
1809
|
-
*
|
1879
|
+
* If the rule has invalid `meta.schema`, throw the error because
|
1880
|
+
* this is not an invalid inline configuration but an invalid rule.
|
1810
1881
|
*/
|
1811
|
-
|
1812
|
-
|
1813
|
-
...config.rules[ruleId].slice(1) // options from the provided config
|
1814
|
-
];
|
1815
|
-
|
1816
|
-
// if the rule was enabled, the options have already been validated
|
1817
|
-
if (config.rules[ruleId][0] > 0) {
|
1818
|
-
shouldValidateOptions = false;
|
1882
|
+
if (err.code === "ESLINT_INVALID_RULE_OPTIONS_SCHEMA") {
|
1883
|
+
throw err;
|
1819
1884
|
}
|
1820
|
-
}
|
1821
1885
|
|
1822
|
-
|
1823
|
-
|
1824
|
-
|
1825
|
-
|
1826
|
-
|
1827
|
-
}
|
1828
|
-
});
|
1829
|
-
}
|
1886
|
+
let baseMessage = err.message.slice(
|
1887
|
+
err.message.startsWith("Key \"rules\":")
|
1888
|
+
? err.message.indexOf(":", 12) + 1
|
1889
|
+
: err.message.indexOf(":") + 1
|
1890
|
+
).trim();
|
1830
1891
|
|
1831
|
-
|
1832
|
-
|
1833
|
-
|
1834
|
-
/*
|
1835
|
-
* If the rule has invalid `meta.schema`, throw the error because
|
1836
|
-
* this is not an invalid inline configuration but an invalid rule.
|
1837
|
-
*/
|
1838
|
-
if (err.code === "ESLINT_INVALID_RULE_OPTIONS_SCHEMA") {
|
1839
|
-
throw err;
|
1840
|
-
}
|
1841
|
-
|
1842
|
-
let baseMessage = err.message.slice(
|
1843
|
-
err.message.startsWith("Key \"rules\":")
|
1844
|
-
? err.message.indexOf(":", 12) + 1
|
1845
|
-
: err.message.indexOf(":") + 1
|
1846
|
-
).trim();
|
1892
|
+
if (err.messageTemplate) {
|
1893
|
+
baseMessage += ` You passed "${ruleValue}".`;
|
1894
|
+
}
|
1847
1895
|
|
1848
|
-
|
1849
|
-
|
1896
|
+
inlineConfigProblems.push(createLintingProblem({
|
1897
|
+
ruleId,
|
1898
|
+
message: `Inline configuration for rule "${ruleId}" is invalid:\n\t${baseMessage}\n`,
|
1899
|
+
loc,
|
1900
|
+
language: config.language
|
1901
|
+
}));
|
1850
1902
|
}
|
1851
|
-
|
1852
|
-
|
1853
|
-
ruleId,
|
1854
|
-
message: `Inline configuration for rule "${ruleId}" is invalid:\n\t${baseMessage}\n`,
|
1855
|
-
loc: node.loc,
|
1856
|
-
language: config.language
|
1857
|
-
}));
|
1858
|
-
}
|
1859
|
-
});
|
1903
|
+
});
|
1904
|
+
}
|
1860
1905
|
}
|
1861
1906
|
}
|
1862
1907
|
}
|
@@ -1873,7 +1918,7 @@ class Linter {
|
|
1873
1918
|
|
1874
1919
|
let lintingProblems;
|
1875
1920
|
|
1876
|
-
sourceCode.finalize();
|
1921
|
+
sourceCode.finalize?.();
|
1877
1922
|
|
1878
1923
|
try {
|
1879
1924
|
lintingProblems = runRules(
|
@@ -334,10 +334,8 @@ class NodeEventGenerator {
|
|
334
334
|
* @returns {void}
|
335
335
|
*/
|
336
336
|
enterNode(node) {
|
337
|
-
if (node.parent) {
|
338
|
-
this.currentAncestry.unshift(node.parent);
|
339
|
-
}
|
340
337
|
this.applySelectors(node, false);
|
338
|
+
this.currentAncestry.unshift(node);
|
341
339
|
}
|
342
340
|
|
343
341
|
/**
|
@@ -346,8 +344,8 @@ class NodeEventGenerator {
|
|
346
344
|
* @returns {void}
|
347
345
|
*/
|
348
346
|
leaveNode(node) {
|
349
|
-
this.applySelectors(node, true);
|
350
347
|
this.currentAncestry.shift();
|
348
|
+
this.applySelectors(node, true);
|
351
349
|
}
|
352
350
|
}
|
353
351
|
|
package/lib/linter/vfile.js
CHANGED
@@ -5,6 +5,12 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//-----------------------------------------------------------------------------
|
9
|
+
// Type Definitions
|
10
|
+
//-----------------------------------------------------------------------------
|
11
|
+
|
12
|
+
/** @typedef {import("@eslint/core").File} File */
|
13
|
+
|
8
14
|
//------------------------------------------------------------------------------
|
9
15
|
// Helpers
|
10
16
|
//------------------------------------------------------------------------------
|
@@ -54,6 +60,7 @@ function stripUnicodeBOM(value) {
|
|
54
60
|
|
55
61
|
/**
|
56
62
|
* Represents a virtual file inside of ESLint.
|
63
|
+
* @implements {File}
|
57
64
|
*/
|
58
65
|
class VFile {
|
59
66
|
|
package/lib/options.js
CHANGED
@@ -30,6 +30,7 @@ const optionator = require("optionator");
|
|
30
30
|
* @property {boolean} errorOnUnmatchedPattern Prevent errors when pattern is unmatched
|
31
31
|
* @property {boolean} eslintrc Disable use of configuration from .eslintrc.*
|
32
32
|
* @property {string[]} [ext] Specify JavaScript file extensions
|
33
|
+
* @property {string[]} [flag] Feature flags
|
33
34
|
* @property {boolean} fix Automatically fix problems
|
34
35
|
* @property {boolean} fixDryRun Automatically fix problems without saving the changes to the file system
|
35
36
|
* @property {("directive" | "problem" | "suggestion" | "layout")[]} [fixType] Specify the types of fixes to apply (directive, problem, suggestion, layout)
|
@@ -176,6 +177,16 @@ module.exports = function(usingFlatConfig) {
|
|
176
177
|
};
|
177
178
|
}
|
178
179
|
|
180
|
+
let flagFlag;
|
181
|
+
|
182
|
+
if (usingFlatConfig) {
|
183
|
+
flagFlag = {
|
184
|
+
option: "flag",
|
185
|
+
type: "[String]",
|
186
|
+
description: "Enable a feature flag"
|
187
|
+
};
|
188
|
+
}
|
189
|
+
|
179
190
|
return optionator({
|
180
191
|
prepend: "eslint [options] file.js [file.js] [dir]",
|
181
192
|
defaults: {
|
@@ -424,7 +435,8 @@ module.exports = function(usingFlatConfig) {
|
|
424
435
|
type: "path::String",
|
425
436
|
description: "Print the configuration for the given file"
|
426
437
|
},
|
427
|
-
statsFlag
|
438
|
+
statsFlag,
|
439
|
+
flagFlag
|
428
440
|
].filter(value => !!value)
|
429
441
|
});
|
430
442
|
};
|
@@ -89,6 +89,9 @@ const arrayOfStringsOrObjectPatterns = {
|
|
89
89
|
minItems: 1,
|
90
90
|
uniqueItems: true
|
91
91
|
},
|
92
|
+
regex: {
|
93
|
+
type: "string"
|
94
|
+
},
|
92
95
|
importNamePattern: {
|
93
96
|
type: "string"
|
94
97
|
},
|
@@ -104,7 +107,6 @@ const arrayOfStringsOrObjectPatterns = {
|
|
104
107
|
}
|
105
108
|
},
|
106
109
|
additionalProperties: false,
|
107
|
-
required: ["group"],
|
108
110
|
not: {
|
109
111
|
anyOf: [
|
110
112
|
{ required: ["importNames", "allowImportNames"] },
|
@@ -113,7 +115,11 @@ const arrayOfStringsOrObjectPatterns = {
|
|
113
115
|
{ required: ["importNamePattern", "allowImportNames"] },
|
114
116
|
{ required: ["allowImportNames", "allowImportNamePattern"] }
|
115
117
|
]
|
116
|
-
}
|
118
|
+
},
|
119
|
+
oneOf: [
|
120
|
+
{ required: ["group"] },
|
121
|
+
{ required: ["regex"] }
|
122
|
+
]
|
117
123
|
},
|
118
124
|
uniqueItems: true
|
119
125
|
}
|
@@ -235,9 +241,10 @@ module.exports = {
|
|
235
241
|
|
236
242
|
// relative paths are supported for this rule
|
237
243
|
const restrictedPatternGroups = restrictedPatterns.map(
|
238
|
-
({ group, message, caseSensitive, importNames, importNamePattern, allowImportNames, allowImportNamePattern }) => (
|
244
|
+
({ group, regex, message, caseSensitive, importNames, importNamePattern, allowImportNames, allowImportNamePattern }) => (
|
239
245
|
{
|
240
|
-
matcher: ignore({ allowRelativePaths: true, ignorecase: !caseSensitive }).add(group),
|
246
|
+
...(group ? { matcher: ignore({ allowRelativePaths: true, ignorecase: !caseSensitive }).add(group) } : {}),
|
247
|
+
...(typeof regex === "string" ? { regexMatcher: new RegExp(regex, caseSensitive ? "u" : "iu") } : {}),
|
241
248
|
customMessage: message,
|
242
249
|
importNames,
|
243
250
|
importNamePattern,
|
@@ -493,7 +500,7 @@ module.exports = {
|
|
493
500
|
* @private
|
494
501
|
*/
|
495
502
|
function isRestrictedPattern(importSource, group) {
|
496
|
-
return group.matcher.ignores(importSource);
|
503
|
+
return group.regexMatcher ? group.regexMatcher.test(importSource) : group.matcher.ignores(importSource);
|
497
504
|
}
|
498
505
|
|
499
506
|
/**
|