eslint 9.4.0 → 9.6.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 +3 -3
- package/conf/ecma-version.js +1 -1
- package/conf/globals.js +6 -1
- package/lib/api.js +1 -1
- package/lib/cli.js +23 -2
- package/lib/config/default-config.js +5 -0
- package/lib/config/flat-config-array.js +71 -8
- package/lib/config/flat-config-schema.js +46 -62
- package/lib/eslint/eslint-helpers.js +32 -17
- package/lib/eslint/eslint.js +30 -12
- package/lib/eslint/legacy-eslint.js +14 -0
- package/lib/languages/js/index.js +247 -0
- package/lib/{source-code → languages/js/source-code}/source-code.js +46 -22
- package/lib/languages/js/validate-language-options.js +181 -0
- package/lib/linter/apply-disable-directives.js +8 -3
- package/lib/linter/config-comment-parser.js +3 -16
- package/lib/linter/linter.js +291 -249
- package/lib/linter/report-translator.js +14 -7
- package/lib/linter/vfile.js +104 -0
- package/lib/options.js +13 -1
- package/lib/rule-tester/rule-tester.js +5 -2
- package/lib/rules/no-sparse-arrays.js +26 -3
- package/lib/rules/no-unused-vars.js +33 -31
- package/lib/shared/flags.js +26 -0
- package/lib/shared/logging.js +10 -1
- package/lib/shared/types.js +1 -1
- package/messages/all-matched-files-ignored.js +21 -0
- package/package.json +13 -18
- /package/lib/{source-code → languages/js/source-code}/index.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/backward-token-comment-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/backward-token-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/cursors.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/decorative-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/filter-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/forward-token-comment-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/forward-token-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/index.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/limit-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/padded-token-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/skip-cursor.js +0 -0
- /package/lib/{source-code → languages/js/source-code}/token-store/utils.js +0 -0
package/lib/linter/linter.js
CHANGED
@@ -17,7 +17,6 @@ const
|
|
17
17
|
espree = require("espree"),
|
18
18
|
merge = require("lodash.merge"),
|
19
19
|
pkg = require("../../package.json"),
|
20
|
-
astUtils = require("../shared/ast-utils"),
|
21
20
|
{
|
22
21
|
directivesPattern
|
23
22
|
} = require("../shared/directives"),
|
@@ -29,7 +28,7 @@ const
|
|
29
28
|
}
|
30
29
|
} = require("@eslint/eslintrc/universal"),
|
31
30
|
Traverser = require("../shared/traverser"),
|
32
|
-
{ SourceCode } = require("../source-code"),
|
31
|
+
{ SourceCode } = require("../languages/js/source-code"),
|
33
32
|
applyDisableDirectives = require("./apply-disable-directives"),
|
34
33
|
ConfigCommentParser = require("./config-comment-parser"),
|
35
34
|
NodeEventGenerator = require("./node-event-generator"),
|
@@ -45,6 +44,8 @@ const { startTime, endTime } = require("../shared/stats");
|
|
45
44
|
const { RuleValidator } = require("../config/rule-validator");
|
46
45
|
const { assertIsRuleSeverity } = require("../config/flat-config-schema");
|
47
46
|
const { normalizeSeverityToString } = require("../shared/severity");
|
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";
|
@@ -53,6 +54,7 @@ const commentParser = new ConfigCommentParser();
|
|
53
54
|
const DEFAULT_ERROR_LOC = { start: { line: 1, column: 0 }, end: { line: 1, column: 1 } };
|
54
55
|
const parserSymbol = Symbol.for("eslint.RuleTester.parser");
|
55
56
|
const { LATEST_ECMA_VERSION } = require("../../conf/ecma-version");
|
57
|
+
const { VFile } = require("./vfile");
|
56
58
|
const STEP_KIND_VISIT = 1;
|
57
59
|
const STEP_KIND_CALL = 2;
|
58
60
|
|
@@ -239,6 +241,35 @@ function createMissingRuleMessage(ruleId) {
|
|
239
241
|
: `Definition for rule '${ruleId}' was not found.`;
|
240
242
|
}
|
241
243
|
|
244
|
+
/**
|
245
|
+
* Updates a given location based on the language offsets. This allows us to
|
246
|
+
* change 0-based locations to 1-based locations. We always want ESLint
|
247
|
+
* reporting lines and columns starting from 1.
|
248
|
+
* @param {Object} location The location to update.
|
249
|
+
* @param {number} location.line The starting line number.
|
250
|
+
* @param {number} location.column The starting column number.
|
251
|
+
* @param {number} [location.endLine] The ending line number.
|
252
|
+
* @param {number} [location.endColumn] The ending column number.
|
253
|
+
* @param {Language} language The language to use to adjust the location information.
|
254
|
+
* @returns {Object} The updated location.
|
255
|
+
*/
|
256
|
+
function updateLocationInformation({ line, column, endLine, endColumn }, language) {
|
257
|
+
|
258
|
+
const columnOffset = language.columnStart === 1 ? 0 : 1;
|
259
|
+
const lineOffset = language.lineStart === 1 ? 0 : 1;
|
260
|
+
|
261
|
+
// calculate separately to account for undefined
|
262
|
+
const finalEndLine = endLine === void 0 ? endLine : endLine + lineOffset;
|
263
|
+
const finalEndColumn = endColumn === void 0 ? endColumn : endColumn + columnOffset;
|
264
|
+
|
265
|
+
return {
|
266
|
+
line: line + lineOffset,
|
267
|
+
column: column + columnOffset,
|
268
|
+
endLine: finalEndLine,
|
269
|
+
endColumn: finalEndColumn
|
270
|
+
};
|
271
|
+
}
|
272
|
+
|
242
273
|
/**
|
243
274
|
* creates a linting problem
|
244
275
|
* @param {Object} options to create linting error
|
@@ -246,6 +277,7 @@ function createMissingRuleMessage(ruleId) {
|
|
246
277
|
* @param {Object} [options.loc] the loc to report
|
247
278
|
* @param {string} [options.message] the error message to report
|
248
279
|
* @param {string} [options.severity] the error message to report
|
280
|
+
* @param {Language} [options.language] the language to use to adjust the location information
|
249
281
|
* @returns {LintMessage} created problem, returns a missing-rule problem if only provided ruleId.
|
250
282
|
* @private
|
251
283
|
*/
|
@@ -254,16 +286,24 @@ function createLintingProblem(options) {
|
|
254
286
|
ruleId = null,
|
255
287
|
loc = DEFAULT_ERROR_LOC,
|
256
288
|
message = createMissingRuleMessage(options.ruleId),
|
257
|
-
severity = 2
|
289
|
+
severity = 2,
|
290
|
+
|
291
|
+
// fallback for eslintrc mode
|
292
|
+
language = {
|
293
|
+
columnStart: 0,
|
294
|
+
lineStart: 1
|
295
|
+
}
|
258
296
|
} = options;
|
259
297
|
|
260
298
|
return {
|
261
299
|
ruleId,
|
262
300
|
message,
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
301
|
+
...updateLocationInformation({
|
302
|
+
line: loc.start.line,
|
303
|
+
column: loc.start.column,
|
304
|
+
endLine: loc.end.line,
|
305
|
+
endColumn: loc.end.column
|
306
|
+
}, language),
|
267
307
|
severity,
|
268
308
|
nodeType: null
|
269
309
|
};
|
@@ -278,9 +318,10 @@ function createLintingProblem(options) {
|
|
278
318
|
* @param {string} options.justification The justification of the directive
|
279
319
|
* @param {ASTNode|token} options.node The Comment node/token.
|
280
320
|
* @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
|
321
|
+
* @param {Language} language The language to use to adjust the location information.
|
281
322
|
* @returns {Object} Directives and problems from the comment
|
282
323
|
*/
|
283
|
-
function createDisableDirectives({ type, value, justification, node }, ruleMapper) {
|
324
|
+
function createDisableDirectives({ type, value, justification, node }, ruleMapper, language) {
|
284
325
|
const ruleIds = Object.keys(commentParser.parseListConfig(value));
|
285
326
|
const directiveRules = ruleIds.length ? ruleIds : [null];
|
286
327
|
const result = {
|
@@ -294,26 +335,36 @@ function createDisableDirectives({ type, value, justification, node }, ruleMappe
|
|
294
335
|
// push to directives, if the rule is defined(including null, e.g. /*eslint enable*/)
|
295
336
|
if (ruleId === null || !!ruleMapper(ruleId)) {
|
296
337
|
if (type === "disable-next-line") {
|
338
|
+
const { line, column } = updateLocationInformation(
|
339
|
+
node.loc.end,
|
340
|
+
language
|
341
|
+
);
|
342
|
+
|
297
343
|
result.directives.push({
|
298
344
|
parentDirective,
|
299
345
|
type,
|
300
|
-
line
|
301
|
-
column
|
346
|
+
line,
|
347
|
+
column,
|
302
348
|
ruleId,
|
303
349
|
justification
|
304
350
|
});
|
305
351
|
} else {
|
352
|
+
const { line, column } = updateLocationInformation(
|
353
|
+
node.loc.start,
|
354
|
+
language
|
355
|
+
);
|
356
|
+
|
306
357
|
result.directives.push({
|
307
358
|
parentDirective,
|
308
359
|
type,
|
309
|
-
line
|
310
|
-
column
|
360
|
+
line,
|
361
|
+
column,
|
311
362
|
ruleId,
|
312
363
|
justification
|
313
364
|
});
|
314
365
|
}
|
315
366
|
} else {
|
316
|
-
result.directiveProblems.push(createLintingProblem({ ruleId, loc: node.loc }));
|
367
|
+
result.directiveProblems.push(createLintingProblem({ ruleId, loc: node.loc, language }));
|
317
368
|
}
|
318
369
|
}
|
319
370
|
return result;
|
@@ -391,7 +442,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
391
442
|
value: directiveValue,
|
392
443
|
justification: justificationPart,
|
393
444
|
node: comment
|
394
|
-
}, ruleMapper);
|
445
|
+
}, ruleMapper, jslang);
|
395
446
|
|
396
447
|
disableDirectives.push(...directives);
|
397
448
|
problems.push(...directiveProblems);
|
@@ -431,7 +482,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
431
482
|
break;
|
432
483
|
|
433
484
|
case "eslint": {
|
434
|
-
const parseResult = commentParser.parseJsonConfig(directiveValue
|
485
|
+
const parseResult = commentParser.parseJsonConfig(directiveValue);
|
435
486
|
|
436
487
|
if (parseResult.success) {
|
437
488
|
Object.keys(parseResult.config).forEach(name => {
|
@@ -518,7 +569,14 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
518
569
|
configuredRules[name] = ruleOptions;
|
519
570
|
});
|
520
571
|
} else {
|
521
|
-
|
572
|
+
const problem = createLintingProblem({
|
573
|
+
ruleId: null,
|
574
|
+
loc: comment.loc,
|
575
|
+
message: parseResult.error.message
|
576
|
+
});
|
577
|
+
|
578
|
+
problem.fatal = true;
|
579
|
+
problems.push(problem);
|
522
580
|
}
|
523
581
|
|
524
582
|
break;
|
@@ -541,26 +599,32 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
541
599
|
* Parses comments in file to extract disable directives.
|
542
600
|
* @param {SourceCode} sourceCode The SourceCode object to get comments from.
|
543
601
|
* @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
|
602
|
+
* @param {Language} language The language to use to adjust the location information
|
544
603
|
* @returns {{problems: LintMessage[], disableDirectives: DisableDirective[]}}
|
545
604
|
* A collection of the directive comments that were found, along with any problems that occurred when parsing
|
546
605
|
*/
|
547
|
-
function getDirectiveCommentsForFlatConfig(sourceCode, ruleMapper) {
|
606
|
+
function getDirectiveCommentsForFlatConfig(sourceCode, ruleMapper, language) {
|
548
607
|
const disableDirectives = [];
|
549
608
|
const problems = [];
|
550
609
|
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
610
|
+
if (sourceCode.getDisableDirectives) {
|
611
|
+
const {
|
612
|
+
directives: directivesSources,
|
613
|
+
problems: directivesProblems
|
614
|
+
} = sourceCode.getDisableDirectives();
|
555
615
|
|
556
|
-
|
616
|
+
problems.push(...directivesProblems.map(directiveProblem => createLintingProblem({
|
617
|
+
...directiveProblem,
|
618
|
+
language
|
619
|
+
})));
|
557
620
|
|
558
|
-
|
559
|
-
|
621
|
+
directivesSources.forEach(directive => {
|
622
|
+
const { directives, directiveProblems } = createDisableDirectives(directive, ruleMapper, language);
|
560
623
|
|
561
|
-
|
562
|
-
|
563
|
-
|
624
|
+
disableDirectives.push(...directives);
|
625
|
+
problems.push(...directiveProblems);
|
626
|
+
});
|
627
|
+
}
|
564
628
|
|
565
629
|
return {
|
566
630
|
problems,
|
@@ -781,24 +845,6 @@ function resolveGlobals(providedGlobals, enabledEnvironments) {
|
|
781
845
|
);
|
782
846
|
}
|
783
847
|
|
784
|
-
/**
|
785
|
-
* Strips Unicode BOM from a given text.
|
786
|
-
* @param {string} text A text to strip.
|
787
|
-
* @returns {string} The stripped text.
|
788
|
-
*/
|
789
|
-
function stripUnicodeBOM(text) {
|
790
|
-
|
791
|
-
/*
|
792
|
-
* Check Unicode BOM.
|
793
|
-
* In JavaScript, string data is stored as UTF-16, so BOM is 0xFEFF.
|
794
|
-
* http://www.ecma-international.org/ecma-262/6.0/#sec-unicode-format-control-characters
|
795
|
-
*/
|
796
|
-
if (text.charCodeAt(0) === 0xFEFF) {
|
797
|
-
return text.slice(1);
|
798
|
-
}
|
799
|
-
return text;
|
800
|
-
}
|
801
|
-
|
802
848
|
/**
|
803
849
|
* Store time measurements in map
|
804
850
|
* @param {number} time Time measurement
|
@@ -866,93 +912,40 @@ function analyzeScope(ast, languageOptions, visitorKeys) {
|
|
866
912
|
}
|
867
913
|
|
868
914
|
/**
|
869
|
-
* Parses
|
915
|
+
* Parses file into an AST. Moved out here because the try-catch prevents
|
870
916
|
* optimization of functions, so it's best to keep the try-catch as isolated
|
871
917
|
* as possible
|
872
|
-
* @param {
|
918
|
+
* @param {VFile} file The file to parse.
|
919
|
+
* @param {Language} language The language to use.
|
873
920
|
* @param {LanguageOptions} languageOptions Options to pass to the parser
|
874
|
-
* @param {string} filePath The path to the file being parsed.
|
875
921
|
* @returns {{success: false, error: LintMessage}|{success: true, sourceCode: SourceCode}}
|
876
922
|
* An object containing the AST and parser services if parsing was successful, or the error if parsing failed
|
877
923
|
* @private
|
878
924
|
*/
|
879
|
-
function parse(
|
880
|
-
const textToParse = stripUnicodeBOM(text).replace(astUtils.shebangPattern, (match, captured) => `//${captured}`);
|
881
|
-
const { ecmaVersion, sourceType, parser } = languageOptions;
|
882
|
-
const parserOptions = Object.assign(
|
883
|
-
{ ecmaVersion, sourceType },
|
884
|
-
languageOptions.parserOptions,
|
885
|
-
{
|
886
|
-
loc: true,
|
887
|
-
range: true,
|
888
|
-
raw: true,
|
889
|
-
tokens: true,
|
890
|
-
comment: true,
|
891
|
-
eslintVisitorKeys: true,
|
892
|
-
eslintScopeManager: true,
|
893
|
-
filePath
|
894
|
-
}
|
895
|
-
);
|
896
|
-
|
897
|
-
/*
|
898
|
-
* Check for parsing errors first. If there's a parsing error, nothing
|
899
|
-
* else can happen. However, a parsing error does not throw an error
|
900
|
-
* from this method - it's just considered a fatal error message, a
|
901
|
-
* problem that ESLint identified just like any other.
|
902
|
-
*/
|
903
|
-
try {
|
904
|
-
debug("Parsing:", filePath);
|
905
|
-
const parseResult = (typeof parser.parseForESLint === "function")
|
906
|
-
? parser.parseForESLint(textToParse, parserOptions)
|
907
|
-
: { ast: parser.parse(textToParse, parserOptions) };
|
908
|
-
|
909
|
-
debug("Parsing successful:", filePath);
|
910
|
-
const ast = parseResult.ast;
|
911
|
-
const parserServices = parseResult.services || {};
|
912
|
-
const visitorKeys = parseResult.visitorKeys || evk.KEYS;
|
913
|
-
|
914
|
-
debug("Scope analysis:", filePath);
|
915
|
-
const scopeManager = parseResult.scopeManager || analyzeScope(ast, languageOptions, visitorKeys);
|
925
|
+
function parse(file, language, languageOptions) {
|
916
926
|
|
917
|
-
|
927
|
+
const result = language.parse(file, { languageOptions });
|
918
928
|
|
929
|
+
if (result.ok) {
|
919
930
|
return {
|
920
931
|
success: true,
|
921
|
-
|
922
|
-
/*
|
923
|
-
* Save all values that `parseForESLint()` returned.
|
924
|
-
* If a `SourceCode` object is given as the first parameter instead of source code text,
|
925
|
-
* linter skips the parsing process and reuses the source code object.
|
926
|
-
* In that case, linter needs all the values that `parseForESLint()` returned.
|
927
|
-
*/
|
928
|
-
sourceCode: new SourceCode({
|
929
|
-
text,
|
930
|
-
ast,
|
931
|
-
parserServices,
|
932
|
-
scopeManager,
|
933
|
-
visitorKeys
|
934
|
-
})
|
935
|
-
};
|
936
|
-
} catch (ex) {
|
937
|
-
|
938
|
-
// If the message includes a leading line number, strip it:
|
939
|
-
const message = `Parsing error: ${ex.message.replace(/^line \d+:/iu, "").trim()}`;
|
940
|
-
|
941
|
-
debug("%s\n%s", message, ex.stack);
|
942
|
-
|
943
|
-
return {
|
944
|
-
success: false,
|
945
|
-
error: {
|
946
|
-
ruleId: null,
|
947
|
-
fatal: true,
|
948
|
-
severity: 2,
|
949
|
-
message,
|
950
|
-
line: ex.lineNumber,
|
951
|
-
column: ex.column,
|
952
|
-
nodeType: null
|
953
|
-
}
|
932
|
+
sourceCode: language.createSourceCode(file, result, { languageOptions })
|
954
933
|
};
|
955
934
|
}
|
935
|
+
|
936
|
+
// if we made it to here there was an error
|
937
|
+
return {
|
938
|
+
success: false,
|
939
|
+
errors: result.errors.map(error => ({
|
940
|
+
ruleId: null,
|
941
|
+
nodeType: null,
|
942
|
+
fatal: true,
|
943
|
+
severity: 2,
|
944
|
+
message: error.message,
|
945
|
+
line: error.line,
|
946
|
+
column: error.column
|
947
|
+
}))
|
948
|
+
};
|
956
949
|
}
|
957
950
|
|
958
951
|
/**
|
@@ -983,6 +976,7 @@ function createRuleListeners(rule, ruleContext) {
|
|
983
976
|
* @param {Object} configuredRules The rules configuration
|
984
977
|
* @param {function(string): Rule} ruleMapper A mapper function from rule names to rules
|
985
978
|
* @param {string | undefined} parserName The name of the parser in the config
|
979
|
+
* @param {Language} language The language object used for parsing.
|
986
980
|
* @param {LanguageOptions} languageOptions The options for parsing the code.
|
987
981
|
* @param {Object} settings The settings that were enabled in the config
|
988
982
|
* @param {string} filename The reported filename of the code
|
@@ -995,8 +989,11 @@ function createRuleListeners(rule, ruleContext) {
|
|
995
989
|
* @returns {LintMessage[]} An array of reported problems
|
996
990
|
* @throws {Error} If traversal into a node fails.
|
997
991
|
*/
|
998
|
-
function runRules(
|
999
|
-
|
992
|
+
function runRules(
|
993
|
+
sourceCode, configuredRules, ruleMapper, parserName, language, languageOptions,
|
994
|
+
settings, filename, disableFixes, cwd, physicalFilename, ruleFilter,
|
995
|
+
stats, slots
|
996
|
+
) {
|
1000
997
|
const emitter = createEmitter();
|
1001
998
|
|
1002
999
|
// must happen first to assign all node.parent properties
|
@@ -1043,7 +1040,7 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageO
|
|
1043
1040
|
const rule = ruleMapper(ruleId);
|
1044
1041
|
|
1045
1042
|
if (!rule) {
|
1046
|
-
lintingProblems.push(createLintingProblem({ ruleId }));
|
1043
|
+
lintingProblems.push(createLintingProblem({ ruleId, language }));
|
1047
1044
|
return;
|
1048
1045
|
}
|
1049
1046
|
|
@@ -1073,7 +1070,8 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageO
|
|
1073
1070
|
severity,
|
1074
1071
|
sourceCode,
|
1075
1072
|
messageIds,
|
1076
|
-
disableFixes
|
1073
|
+
disableFixes,
|
1074
|
+
language
|
1077
1075
|
});
|
1078
1076
|
}
|
1079
1077
|
const problem = reportTranslator(...args);
|
@@ -1144,7 +1142,12 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageO
|
|
1144
1142
|
});
|
1145
1143
|
});
|
1146
1144
|
|
1147
|
-
const eventGenerator = new NodeEventGenerator(emitter, {
|
1145
|
+
const eventGenerator = new NodeEventGenerator(emitter, {
|
1146
|
+
visitorKeys: sourceCode.visitorKeys ?? language.visitorKeys,
|
1147
|
+
fallback: Traverser.getKeys,
|
1148
|
+
matchClass: language.matchesSelectorClass ?? (() => false),
|
1149
|
+
nodeTypeKey: language.nodeTypeKey
|
1150
|
+
});
|
1148
1151
|
|
1149
1152
|
for (const step of eventQueue) {
|
1150
1153
|
switch (step.kind) {
|
@@ -1271,11 +1274,13 @@ class Linter {
|
|
1271
1274
|
* Initialize the Linter.
|
1272
1275
|
* @param {Object} [config] the config object
|
1273
1276
|
* @param {string} [config.cwd] path to a directory that should be considered as the current working directory, can be undefined.
|
1277
|
+
* @param {Array<string>} [config.flags] the feature flags to enable.
|
1274
1278
|
* @param {"flat"|"eslintrc"} [config.configType="flat"] the type of config used.
|
1275
1279
|
*/
|
1276
|
-
constructor({ cwd, configType = "flat" } = {}) {
|
1280
|
+
constructor({ cwd, configType = "flat", flags = [] } = {}) {
|
1277
1281
|
internalSlotsMap.set(this, {
|
1278
1282
|
cwd: normalizeCwd(cwd),
|
1283
|
+
flags: flags.filter(flag => activeFlags.has(flag)),
|
1279
1284
|
lastConfigArray: null,
|
1280
1285
|
lastSourceCode: null,
|
1281
1286
|
lastSuppressedMessages: [],
|
@@ -1296,6 +1301,15 @@ class Linter {
|
|
1296
1301
|
return pkg.version;
|
1297
1302
|
}
|
1298
1303
|
|
1304
|
+
/**
|
1305
|
+
* Indicates if the given feature flag is enabled for this instance.
|
1306
|
+
* @param {string} flag The feature flag to check.
|
1307
|
+
* @returns {boolean} `true` if the feature flag is enabled, `false` if not.
|
1308
|
+
*/
|
1309
|
+
hasFlag(flag) {
|
1310
|
+
return internalSlotsMap.get(this).flags.includes(flag);
|
1311
|
+
}
|
1312
|
+
|
1299
1313
|
/**
|
1300
1314
|
* Same as linter.verify, except without support for processors.
|
1301
1315
|
* @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
|
@@ -1360,6 +1374,9 @@ class Linter {
|
|
1360
1374
|
parser,
|
1361
1375
|
parserOptions
|
1362
1376
|
});
|
1377
|
+
const file = new VFile(options.filename, text, {
|
1378
|
+
physicalPath: providedOptions.physicalFilename
|
1379
|
+
});
|
1363
1380
|
|
1364
1381
|
if (!slots.lastSourceCode) {
|
1365
1382
|
let t;
|
@@ -1369,9 +1386,9 @@ class Linter {
|
|
1369
1386
|
}
|
1370
1387
|
|
1371
1388
|
const parseResult = parse(
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1389
|
+
file,
|
1390
|
+
jslang,
|
1391
|
+
languageOptions
|
1375
1392
|
);
|
1376
1393
|
|
1377
1394
|
if (options.stats) {
|
@@ -1382,7 +1399,7 @@ class Linter {
|
|
1382
1399
|
}
|
1383
1400
|
|
1384
1401
|
if (!parseResult.success) {
|
1385
|
-
return
|
1402
|
+
return parseResult.errors;
|
1386
1403
|
}
|
1387
1404
|
|
1388
1405
|
slots.lastSourceCode = parseResult.sourceCode;
|
@@ -1396,6 +1413,7 @@ class Linter {
|
|
1396
1413
|
slots.lastSourceCode = new SourceCode({
|
1397
1414
|
text: slots.lastSourceCode.text,
|
1398
1415
|
ast: slots.lastSourceCode.ast,
|
1416
|
+
hasBOM: slots.lastSourceCode.hasBOM,
|
1399
1417
|
parserServices: slots.lastSourceCode.parserServices,
|
1400
1418
|
visitorKeys: slots.lastSourceCode.visitorKeys,
|
1401
1419
|
scopeManager: analyzeScope(slots.lastSourceCode.ast, languageOptions)
|
@@ -1408,7 +1426,6 @@ class Linter {
|
|
1408
1426
|
? getDirectiveComments(sourceCode, ruleId => getRule(slots, ruleId), options.warnInlineConfig, config)
|
1409
1427
|
: { configuredRules: {}, enabledGlobals: {}, exportedVariables: {}, problems: [], disableDirectives: [] };
|
1410
1428
|
|
1411
|
-
// augment global scope with declared global variables
|
1412
1429
|
addDeclaredGlobals(
|
1413
1430
|
sourceCode.scopeManager.scopes[0],
|
1414
1431
|
configuredGlobals,
|
@@ -1425,6 +1442,7 @@ class Linter {
|
|
1425
1442
|
configuredRules,
|
1426
1443
|
ruleId => getRule(slots, ruleId),
|
1427
1444
|
parserName,
|
1445
|
+
jslang,
|
1428
1446
|
languageOptions,
|
1429
1447
|
settings,
|
1430
1448
|
options.filename,
|
@@ -1457,6 +1475,7 @@ class Linter {
|
|
1457
1475
|
}
|
1458
1476
|
|
1459
1477
|
return applyDisableDirectives({
|
1478
|
+
language: jslang,
|
1460
1479
|
directives: commentDirectives.disableDirectives,
|
1461
1480
|
disableFixes: options.disableFixes,
|
1462
1481
|
problems: lintingProblems
|
@@ -1659,6 +1678,9 @@ class Linter {
|
|
1659
1678
|
}
|
1660
1679
|
|
1661
1680
|
const settings = config.settings || {};
|
1681
|
+
const file = new VFile(options.filename, text, {
|
1682
|
+
physicalPath: providedOptions.physicalFilename
|
1683
|
+
});
|
1662
1684
|
|
1663
1685
|
if (!slots.lastSourceCode) {
|
1664
1686
|
let t;
|
@@ -1668,9 +1690,9 @@ class Linter {
|
|
1668
1690
|
}
|
1669
1691
|
|
1670
1692
|
const parseResult = parse(
|
1671
|
-
|
1672
|
-
|
1673
|
-
|
1693
|
+
file,
|
1694
|
+
config.language,
|
1695
|
+
languageOptions
|
1674
1696
|
);
|
1675
1697
|
|
1676
1698
|
if (options.stats) {
|
@@ -1680,7 +1702,7 @@ class Linter {
|
|
1680
1702
|
}
|
1681
1703
|
|
1682
1704
|
if (!parseResult.success) {
|
1683
|
-
return
|
1705
|
+
return parseResult.errors;
|
1684
1706
|
}
|
1685
1707
|
|
1686
1708
|
slots.lastSourceCode = parseResult.sourceCode;
|
@@ -1689,11 +1711,17 @@ class Linter {
|
|
1689
1711
|
/*
|
1690
1712
|
* If the given source code object as the first argument does not have scopeManager, analyze the scope.
|
1691
1713
|
* This is for backward compatibility (SourceCode is frozen so it cannot rebind).
|
1714
|
+
*
|
1715
|
+
* We check explicitly for `null` to ensure that this is a JS-flavored language.
|
1716
|
+
* For non-JS languages we don't want to do this.
|
1717
|
+
*
|
1718
|
+
* TODO: Remove this check when we stop exporting the `SourceCode` object.
|
1692
1719
|
*/
|
1693
|
-
if (
|
1720
|
+
if (slots.lastSourceCode.scopeManager === null) {
|
1694
1721
|
slots.lastSourceCode = new SourceCode({
|
1695
1722
|
text: slots.lastSourceCode.text,
|
1696
1723
|
ast: slots.lastSourceCode.ast,
|
1724
|
+
hasBOM: slots.lastSourceCode.hasBOM,
|
1697
1725
|
parserServices: slots.lastSourceCode.parserServices,
|
1698
1726
|
visitorKeys: slots.lastSourceCode.visitorKeys,
|
1699
1727
|
scopeManager: analyzeScope(slots.lastSourceCode.ast, languageOptions)
|
@@ -1708,7 +1736,7 @@ class Linter {
|
|
1708
1736
|
* this is primarily about adding variables into the global scope
|
1709
1737
|
* to account for ecmaVersion and configured globals.
|
1710
1738
|
*/
|
1711
|
-
sourceCode.applyLanguageOptions(languageOptions);
|
1739
|
+
sourceCode.applyLanguageOptions?.(languageOptions);
|
1712
1740
|
|
1713
1741
|
const mergedInlineConfig = {
|
1714
1742
|
rules: {}
|
@@ -1725,140 +1753,151 @@ class Linter {
|
|
1725
1753
|
|
1726
1754
|
// if inline config should warn then add the warnings
|
1727
1755
|
if (options.warnInlineConfig) {
|
1728
|
-
sourceCode.getInlineConfigNodes
|
1729
|
-
|
1730
|
-
|
1731
|
-
|
1732
|
-
|
1733
|
-
|
1734
|
-
|
1756
|
+
if (sourceCode.getInlineConfigNodes) {
|
1757
|
+
sourceCode.getInlineConfigNodes().forEach(node => {
|
1758
|
+
inlineConfigProblems.push(createLintingProblem({
|
1759
|
+
ruleId: null,
|
1760
|
+
message: `'${sourceCode.text.slice(node.range[0], node.range[1])}' has no effect because you have 'noInlineConfig' setting in ${options.warnInlineConfig}.`,
|
1761
|
+
loc: node.loc,
|
1762
|
+
severity: 1,
|
1763
|
+
language: config.language
|
1764
|
+
}));
|
1735
1765
|
|
1736
|
-
|
1766
|
+
});
|
1767
|
+
}
|
1737
1768
|
} else {
|
1738
|
-
const inlineConfigResult = sourceCode.applyInlineConfig();
|
1739
|
-
|
1740
|
-
|
1741
|
-
|
1742
|
-
.
|
1743
|
-
|
1744
|
-
problem
|
1745
|
-
|
1746
|
-
|
1747
|
-
|
1769
|
+
const inlineConfigResult = sourceCode.applyInlineConfig?.();
|
1770
|
+
|
1771
|
+
if (inlineConfigResult) {
|
1772
|
+
inlineConfigProblems.push(
|
1773
|
+
...inlineConfigResult.problems
|
1774
|
+
.map(problem => createLintingProblem({ ...problem, language: config.language }))
|
1775
|
+
.map(problem => {
|
1776
|
+
problem.fatal = true;
|
1777
|
+
return problem;
|
1778
|
+
})
|
1779
|
+
);
|
1780
|
+
|
1781
|
+
// next we need to verify information about the specified rules
|
1782
|
+
const ruleValidator = new RuleValidator();
|
1783
|
+
|
1784
|
+
for (const { config: inlineConfig, loc } of inlineConfigResult.configs) {
|
1785
|
+
|
1786
|
+
Object.keys(inlineConfig.rules).forEach(ruleId => {
|
1787
|
+
const rule = getRuleFromConfig(ruleId, config);
|
1788
|
+
const ruleValue = inlineConfig.rules[ruleId];
|
1789
|
+
|
1790
|
+
if (!rule) {
|
1791
|
+
inlineConfigProblems.push(createLintingProblem({
|
1792
|
+
ruleId,
|
1793
|
+
loc,
|
1794
|
+
language: config.language
|
1795
|
+
}));
|
1796
|
+
return;
|
1797
|
+
}
|
1748
1798
|
|
1749
|
-
|
1750
|
-
|
1799
|
+
if (Object.hasOwn(mergedInlineConfig.rules, ruleId)) {
|
1800
|
+
inlineConfigProblems.push(createLintingProblem({
|
1801
|
+
message: `Rule "${ruleId}" is already configured by another configuration comment in the preceding code. This configuration is ignored.`,
|
1802
|
+
loc,
|
1803
|
+
language: config.language
|
1804
|
+
}));
|
1805
|
+
return;
|
1806
|
+
}
|
1751
1807
|
|
1752
|
-
|
1808
|
+
try {
|
1753
1809
|
|
1754
|
-
|
1755
|
-
const rule = getRuleFromConfig(ruleId, config);
|
1756
|
-
const ruleValue = inlineConfig.rules[ruleId];
|
1810
|
+
let ruleOptions = Array.isArray(ruleValue) ? ruleValue : [ruleValue];
|
1757
1811
|
|
1758
|
-
|
1759
|
-
inlineConfigProblems.push(createLintingProblem({ ruleId, loc: node.loc }));
|
1760
|
-
return;
|
1761
|
-
}
|
1812
|
+
assertIsRuleSeverity(ruleId, ruleOptions[0]);
|
1762
1813
|
|
1763
|
-
|
1764
|
-
|
1765
|
-
|
1766
|
-
|
1767
|
-
|
1768
|
-
|
1769
|
-
|
1814
|
+
/*
|
1815
|
+
* If the rule was already configured, inline rule configuration that
|
1816
|
+
* only has severity should retain options from the config and just override the severity.
|
1817
|
+
*
|
1818
|
+
* Example:
|
1819
|
+
*
|
1820
|
+
* {
|
1821
|
+
* rules: {
|
1822
|
+
* curly: ["error", "multi"]
|
1823
|
+
* }
|
1824
|
+
* }
|
1825
|
+
*
|
1826
|
+
* /* eslint curly: ["warn"] * /
|
1827
|
+
*
|
1828
|
+
* Results in:
|
1829
|
+
*
|
1830
|
+
* curly: ["warn", "multi"]
|
1831
|
+
*/
|
1770
1832
|
|
1771
|
-
|
1833
|
+
let shouldValidateOptions = true;
|
1772
1834
|
|
1773
|
-
|
1835
|
+
if (
|
1774
1836
|
|
1775
|
-
|
1837
|
+
/*
|
1838
|
+
* If inline config for the rule has only severity
|
1839
|
+
*/
|
1840
|
+
ruleOptions.length === 1 &&
|
1776
1841
|
|
1777
|
-
|
1778
|
-
|
1779
|
-
|
1780
|
-
|
1781
|
-
|
1782
|
-
*
|
1783
|
-
* {
|
1784
|
-
* rules: {
|
1785
|
-
* curly: ["error", "multi"]
|
1786
|
-
* }
|
1787
|
-
* }
|
1788
|
-
*
|
1789
|
-
* /* eslint curly: ["warn"] * /
|
1790
|
-
*
|
1791
|
-
* Results in:
|
1792
|
-
*
|
1793
|
-
* curly: ["warn", "multi"]
|
1794
|
-
*/
|
1842
|
+
/*
|
1843
|
+
* And the rule was already configured
|
1844
|
+
*/
|
1845
|
+
config.rules && Object.hasOwn(config.rules, ruleId)
|
1846
|
+
) {
|
1795
1847
|
|
1796
|
-
|
1848
|
+
/*
|
1849
|
+
* Then use severity from the inline config and options from the provided config
|
1850
|
+
*/
|
1851
|
+
ruleOptions = [
|
1852
|
+
ruleOptions[0], // severity from the inline config
|
1853
|
+
...config.rules[ruleId].slice(1) // options from the provided config
|
1854
|
+
];
|
1797
1855
|
|
1798
|
-
|
1856
|
+
// if the rule was enabled, the options have already been validated
|
1857
|
+
if (config.rules[ruleId][0] > 0) {
|
1858
|
+
shouldValidateOptions = false;
|
1859
|
+
}
|
1860
|
+
}
|
1799
1861
|
|
1800
|
-
|
1801
|
-
|
1802
|
-
|
1803
|
-
|
1862
|
+
if (shouldValidateOptions) {
|
1863
|
+
ruleValidator.validate({
|
1864
|
+
plugins: config.plugins,
|
1865
|
+
rules: {
|
1866
|
+
[ruleId]: ruleOptions
|
1867
|
+
}
|
1868
|
+
});
|
1869
|
+
}
|
1804
1870
|
|
1805
|
-
|
1806
|
-
|
1807
|
-
*/
|
1808
|
-
config.rules && Object.hasOwn(config.rules, ruleId)
|
1809
|
-
) {
|
1871
|
+
mergedInlineConfig.rules[ruleId] = ruleOptions;
|
1872
|
+
} catch (err) {
|
1810
1873
|
|
1811
1874
|
/*
|
1812
|
-
*
|
1875
|
+
* If the rule has invalid `meta.schema`, throw the error because
|
1876
|
+
* this is not an invalid inline configuration but an invalid rule.
|
1813
1877
|
*/
|
1814
|
-
|
1815
|
-
|
1816
|
-
...config.rules[ruleId].slice(1) // options from the provided config
|
1817
|
-
];
|
1818
|
-
|
1819
|
-
// if the rule was enabled, the options have already been validated
|
1820
|
-
if (config.rules[ruleId][0] > 0) {
|
1821
|
-
shouldValidateOptions = false;
|
1878
|
+
if (err.code === "ESLINT_INVALID_RULE_OPTIONS_SCHEMA") {
|
1879
|
+
throw err;
|
1822
1880
|
}
|
1823
|
-
}
|
1824
|
-
|
1825
|
-
if (shouldValidateOptions) {
|
1826
|
-
ruleValidator.validate({
|
1827
|
-
plugins: config.plugins,
|
1828
|
-
rules: {
|
1829
|
-
[ruleId]: ruleOptions
|
1830
|
-
}
|
1831
|
-
});
|
1832
|
-
}
|
1833
1881
|
|
1834
|
-
|
1835
|
-
|
1836
|
-
|
1837
|
-
|
1838
|
-
|
1839
|
-
* this is not an invalid inline configuration but an invalid rule.
|
1840
|
-
*/
|
1841
|
-
if (err.code === "ESLINT_INVALID_RULE_OPTIONS_SCHEMA") {
|
1842
|
-
throw err;
|
1843
|
-
}
|
1882
|
+
let baseMessage = err.message.slice(
|
1883
|
+
err.message.startsWith("Key \"rules\":")
|
1884
|
+
? err.message.indexOf(":", 12) + 1
|
1885
|
+
: err.message.indexOf(":") + 1
|
1886
|
+
).trim();
|
1844
1887
|
|
1845
|
-
|
1846
|
-
|
1847
|
-
|
1848
|
-
: err.message.indexOf(":") + 1
|
1849
|
-
).trim();
|
1888
|
+
if (err.messageTemplate) {
|
1889
|
+
baseMessage += ` You passed "${ruleValue}".`;
|
1890
|
+
}
|
1850
1891
|
|
1851
|
-
|
1852
|
-
|
1892
|
+
inlineConfigProblems.push(createLintingProblem({
|
1893
|
+
ruleId,
|
1894
|
+
message: `Inline configuration for rule "${ruleId}" is invalid:\n\t${baseMessage}\n`,
|
1895
|
+
loc,
|
1896
|
+
language: config.language
|
1897
|
+
}));
|
1853
1898
|
}
|
1854
|
-
|
1855
|
-
|
1856
|
-
ruleId,
|
1857
|
-
message: `Inline configuration for rule "${ruleId}" is invalid:\n\t${baseMessage}\n`,
|
1858
|
-
loc: node.loc
|
1859
|
-
}));
|
1860
|
-
}
|
1861
|
-
});
|
1899
|
+
});
|
1900
|
+
}
|
1862
1901
|
}
|
1863
1902
|
}
|
1864
1903
|
}
|
@@ -1866,7 +1905,8 @@ class Linter {
|
|
1866
1905
|
const commentDirectives = options.allowInlineConfig && !options.warnInlineConfig
|
1867
1906
|
? getDirectiveCommentsForFlatConfig(
|
1868
1907
|
sourceCode,
|
1869
|
-
ruleId => getRuleFromConfig(ruleId, config)
|
1908
|
+
ruleId => getRuleFromConfig(ruleId, config),
|
1909
|
+
config.language
|
1870
1910
|
)
|
1871
1911
|
: { problems: [], disableDirectives: [] };
|
1872
1912
|
|
@@ -1874,7 +1914,7 @@ class Linter {
|
|
1874
1914
|
|
1875
1915
|
let lintingProblems;
|
1876
1916
|
|
1877
|
-
sourceCode.finalize();
|
1917
|
+
sourceCode.finalize?.();
|
1878
1918
|
|
1879
1919
|
try {
|
1880
1920
|
lintingProblems = runRules(
|
@@ -1882,6 +1922,7 @@ class Linter {
|
|
1882
1922
|
configuredRules,
|
1883
1923
|
ruleId => getRuleFromConfig(ruleId, config),
|
1884
1924
|
void 0,
|
1925
|
+
config.language,
|
1885
1926
|
languageOptions,
|
1886
1927
|
settings,
|
1887
1928
|
options.filename,
|
@@ -1915,6 +1956,7 @@ class Linter {
|
|
1915
1956
|
}
|
1916
1957
|
|
1917
1958
|
return applyDisableDirectives({
|
1959
|
+
language: config.language,
|
1918
1960
|
directives: commentDirectives.disableDirectives,
|
1919
1961
|
disableFixes: options.disableFixes,
|
1920
1962
|
problems: lintingProblems
|