eslint 9.0.0-rc.0 → 9.1.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 -11
- package/conf/globals.js +1 -0
- package/lib/cli.js +42 -1
- package/lib/config/flat-config-array.js +110 -4
- package/lib/eslint/eslint-helpers.js +84 -45
- package/lib/eslint/eslint.js +24 -5
- package/lib/linter/apply-disable-directives.js +24 -24
- package/lib/linter/linter.js +181 -66
- package/lib/linter/timing.js +16 -8
- package/lib/options.js +27 -3
- package/lib/rule-tester/rule-tester.js +18 -2
- package/lib/rules/camelcase.js +3 -5
- package/lib/rules/constructor-super.js +62 -93
- package/lib/rules/no-constant-condition.js +18 -7
- package/lib/rules/no-lone-blocks.js +1 -1
- package/lib/rules/no-unused-vars.js +179 -29
- package/lib/rules/use-isnan.js +2 -2
- package/lib/shared/stats.js +30 -0
- package/lib/shared/string-utils.js +9 -11
- package/lib/shared/types.js +34 -0
- package/lib/source-code/source-code.js +128 -0
- package/lib/source-code/token-store/backward-token-cursor.js +3 -3
- package/lib/source-code/token-store/cursors.js +4 -2
- package/lib/source-code/token-store/forward-token-comment-cursor.js +3 -3
- package/lib/source-code/token-store/forward-token-cursor.js +3 -3
- package/messages/plugin-conflict.js +1 -1
- package/messages/plugin-invalid.js +1 -1
- package/messages/plugin-missing.js +1 -1
- package/package.json +11 -9
@@ -38,16 +38,16 @@ function compareLocations(itemA, itemB) {
|
|
38
38
|
* @param {Iterable<Directive>} directives Unused directives to be removed.
|
39
39
|
* @returns {Directive[][]} Directives grouped by their parent comment.
|
40
40
|
*/
|
41
|
-
function
|
41
|
+
function groupByParentDirective(directives) {
|
42
42
|
const groups = new Map();
|
43
43
|
|
44
44
|
for (const directive of directives) {
|
45
|
-
const { unprocessedDirective: {
|
45
|
+
const { unprocessedDirective: { parentDirective } } = directive;
|
46
46
|
|
47
|
-
if (groups.has(
|
48
|
-
groups.get(
|
47
|
+
if (groups.has(parentDirective)) {
|
48
|
+
groups.get(parentDirective).push(directive);
|
49
49
|
} else {
|
50
|
-
groups.set(
|
50
|
+
groups.set(parentDirective, [directive]);
|
51
51
|
}
|
52
52
|
}
|
53
53
|
|
@@ -57,19 +57,19 @@ function groupByParentComment(directives) {
|
|
57
57
|
/**
|
58
58
|
* Creates removal details for a set of directives within the same comment.
|
59
59
|
* @param {Directive[]} directives Unused directives to be removed.
|
60
|
-
* @param {Token}
|
60
|
+
* @param {Token} node The backing Comment token.
|
61
61
|
* @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
|
62
62
|
*/
|
63
|
-
function createIndividualDirectivesRemoval(directives,
|
63
|
+
function createIndividualDirectivesRemoval(directives, node) {
|
64
64
|
|
65
65
|
/*
|
66
|
-
* `
|
66
|
+
* `node.value` starts right after `//` or `/*`.
|
67
67
|
* All calculated offsets will be relative to this index.
|
68
68
|
*/
|
69
|
-
const commentValueStart =
|
69
|
+
const commentValueStart = node.range[0] + "//".length;
|
70
70
|
|
71
71
|
// Find where the list of rules starts. `\S+` matches with the directive name (e.g. `eslint-disable-line`)
|
72
|
-
const listStartOffset = /^\s*\S+\s+/u.exec(
|
72
|
+
const listStartOffset = /^\s*\S+\s+/u.exec(node.value)[0].length;
|
73
73
|
|
74
74
|
/*
|
75
75
|
* Get the list text without any surrounding whitespace. In order to preserve the original
|
@@ -78,7 +78,7 @@ function createIndividualDirectivesRemoval(directives, commentToken) {
|
|
78
78
|
* // eslint-disable-line rule-one , rule-two , rule-three -- comment
|
79
79
|
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
80
80
|
*/
|
81
|
-
const listText =
|
81
|
+
const listText = node.value
|
82
82
|
.slice(listStartOffset) // remove directive name and all whitespace before the list
|
83
83
|
.split(/\s-{2,}\s/u)[0] // remove `-- comment`, if it exists
|
84
84
|
.trimEnd(); // remove all whitespace after the list
|
@@ -159,13 +159,13 @@ function createIndividualDirectivesRemoval(directives, commentToken) {
|
|
159
159
|
}
|
160
160
|
|
161
161
|
/**
|
162
|
-
* Creates a description of deleting an entire unused disable
|
162
|
+
* Creates a description of deleting an entire unused disable directive.
|
163
163
|
* @param {Directive[]} directives Unused directives to be removed.
|
164
|
-
* @param {Token}
|
165
|
-
* @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output
|
164
|
+
* @param {Token} node The backing Comment token.
|
165
|
+
* @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output problem.
|
166
166
|
*/
|
167
|
-
function
|
168
|
-
const { range } =
|
167
|
+
function createDirectiveRemoval(directives, node) {
|
168
|
+
const { range } = node;
|
169
169
|
const ruleIds = directives.filter(directive => directive.ruleId).map(directive => `'${directive.ruleId}'`);
|
170
170
|
|
171
171
|
return {
|
@@ -186,20 +186,20 @@ function createCommentRemoval(directives, commentToken) {
|
|
186
186
|
* @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
|
187
187
|
*/
|
188
188
|
function processUnusedDirectives(allDirectives) {
|
189
|
-
const directiveGroups =
|
189
|
+
const directiveGroups = groupByParentDirective(allDirectives);
|
190
190
|
|
191
191
|
return directiveGroups.flatMap(
|
192
192
|
directives => {
|
193
|
-
const {
|
194
|
-
const remainingRuleIds = new Set(
|
193
|
+
const { parentDirective } = directives[0].unprocessedDirective;
|
194
|
+
const remainingRuleIds = new Set(parentDirective.ruleIds);
|
195
195
|
|
196
196
|
for (const directive of directives) {
|
197
197
|
remainingRuleIds.delete(directive.ruleId);
|
198
198
|
}
|
199
199
|
|
200
200
|
return remainingRuleIds.size
|
201
|
-
? createIndividualDirectivesRemoval(directives,
|
202
|
-
: [
|
201
|
+
? createIndividualDirectivesRemoval(directives, parentDirective.node)
|
202
|
+
: [createDirectiveRemoval(directives, parentDirective.node)];
|
203
203
|
}
|
204
204
|
);
|
205
205
|
}
|
@@ -372,7 +372,7 @@ function applyDirectives(options) {
|
|
372
372
|
|
373
373
|
const unusedDirectives = processed
|
374
374
|
.map(({ description, fix, unprocessedDirective }) => {
|
375
|
-
const {
|
375
|
+
const { parentDirective, type, line, column } = unprocessedDirective;
|
376
376
|
|
377
377
|
let message;
|
378
378
|
|
@@ -388,8 +388,8 @@ function applyDirectives(options) {
|
|
388
388
|
return {
|
389
389
|
ruleId: null,
|
390
390
|
message,
|
391
|
-
line: type === "disable-next-line" ?
|
392
|
-
column: type === "disable-next-line" ?
|
391
|
+
line: type === "disable-next-line" ? parentDirective.node.loc.start.line : line,
|
392
|
+
column: type === "disable-next-line" ? parentDirective.node.loc.start.column + 1 : column,
|
393
393
|
severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2,
|
394
394
|
nodeType: null,
|
395
395
|
...options.disableFixes ? {} : { fix }
|
package/lib/linter/linter.js
CHANGED
@@ -41,6 +41,7 @@ const
|
|
41
41
|
ruleReplacements = require("../../conf/replacements.json");
|
42
42
|
const { getRuleFromConfig } = require("../config/flat-config-helpers");
|
43
43
|
const { FlatConfigArray } = require("../config/flat-config-array");
|
44
|
+
const { startTime, endTime } = require("../shared/stats");
|
44
45
|
const { RuleValidator } = require("../config/rule-validator");
|
45
46
|
const { assertIsRuleSeverity } = require("../config/flat-config-schema");
|
46
47
|
const { normalizeSeverityToString } = require("../shared/severity");
|
@@ -68,6 +69,7 @@ const STEP_KIND_CALL = 2;
|
|
68
69
|
/** @typedef {import("../shared/types").LanguageOptions} LanguageOptions */
|
69
70
|
/** @typedef {import("../shared/types").Processor} Processor */
|
70
71
|
/** @typedef {import("../shared/types").Rule} Rule */
|
72
|
+
/** @typedef {import("../shared/types").Times} Times */
|
71
73
|
|
72
74
|
/* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
|
73
75
|
/**
|
@@ -92,6 +94,7 @@ const STEP_KIND_CALL = 2;
|
|
92
94
|
* @property {SourceCode|null} lastSourceCode The `SourceCode` instance that the last `verify()` call used.
|
93
95
|
* @property {SuppressedLintMessage[]} lastSuppressedMessages The `SuppressedLintMessage[]` instance that the last `verify()` call produced.
|
94
96
|
* @property {Map<string, Parser>} parserMap The loaded parsers.
|
97
|
+
* @property {Times} times The times spent on applying a rule to a file (see `stats` option).
|
95
98
|
* @property {Rules} ruleMap The loaded rules.
|
96
99
|
*/
|
97
100
|
|
@@ -270,23 +273,21 @@ function createLintingProblem(options) {
|
|
270
273
|
* Creates a collection of disable directives from a comment
|
271
274
|
* @param {Object} options to create disable directives
|
272
275
|
* @param {("disable"|"enable"|"disable-line"|"disable-next-line")} options.type The type of directive comment
|
273
|
-
* @param {token} options.commentToken The Comment token
|
274
276
|
* @param {string} options.value The value after the directive in the comment
|
275
277
|
* comment specified no specific rules, so it applies to all rules (e.g. `eslint-disable`)
|
276
278
|
* @param {string} options.justification The justification of the directive
|
277
|
-
* @param {
|
279
|
+
* @param {ASTNode|token} options.node The Comment node/token.
|
280
|
+
* @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
|
278
281
|
* @returns {Object} Directives and problems from the comment
|
279
282
|
*/
|
280
|
-
function createDisableDirectives(
|
281
|
-
const { commentToken, type, value, justification, ruleMapper } = options;
|
283
|
+
function createDisableDirectives({ type, value, justification, node }, ruleMapper) {
|
282
284
|
const ruleIds = Object.keys(commentParser.parseListConfig(value));
|
283
285
|
const directiveRules = ruleIds.length ? ruleIds : [null];
|
284
286
|
const result = {
|
285
287
|
directives: [], // valid disable directives
|
286
288
|
directiveProblems: [] // problems in directives
|
287
289
|
};
|
288
|
-
|
289
|
-
const parentComment = { commentToken, ruleIds };
|
290
|
+
const parentDirective = { node, ruleIds };
|
290
291
|
|
291
292
|
for (const ruleId of directiveRules) {
|
292
293
|
|
@@ -294,25 +295,25 @@ function createDisableDirectives(options) {
|
|
294
295
|
if (ruleId === null || !!ruleMapper(ruleId)) {
|
295
296
|
if (type === "disable-next-line") {
|
296
297
|
result.directives.push({
|
297
|
-
|
298
|
+
parentDirective,
|
298
299
|
type,
|
299
|
-
line:
|
300
|
-
column:
|
300
|
+
line: node.loc.end.line,
|
301
|
+
column: node.loc.end.column + 1,
|
301
302
|
ruleId,
|
302
303
|
justification
|
303
304
|
});
|
304
305
|
} else {
|
305
306
|
result.directives.push({
|
306
|
-
|
307
|
+
parentDirective,
|
307
308
|
type,
|
308
|
-
line:
|
309
|
-
column:
|
309
|
+
line: node.loc.start.line,
|
310
|
+
column: node.loc.start.column + 1,
|
310
311
|
ruleId,
|
311
312
|
justification
|
312
313
|
});
|
313
314
|
}
|
314
315
|
} else {
|
315
|
-
result.directiveProblems.push(createLintingProblem({ ruleId, loc:
|
316
|
+
result.directiveProblems.push(createLintingProblem({ ruleId, loc: node.loc }));
|
316
317
|
}
|
317
318
|
}
|
318
319
|
return result;
|
@@ -385,8 +386,12 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
385
386
|
case "eslint-disable-next-line":
|
386
387
|
case "eslint-disable-line": {
|
387
388
|
const directiveType = directiveText.slice("eslint-".length);
|
388
|
-
const
|
389
|
-
|
389
|
+
const { directives, directiveProblems } = createDisableDirectives({
|
390
|
+
type: directiveType,
|
391
|
+
value: directiveValue,
|
392
|
+
justification: justificationPart,
|
393
|
+
node: comment
|
394
|
+
}, ruleMapper);
|
390
395
|
|
391
396
|
disableDirectives.push(...directives);
|
392
397
|
problems.push(...directiveProblems);
|
@@ -540,53 +545,21 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
|
|
540
545
|
* A collection of the directive comments that were found, along with any problems that occurred when parsing
|
541
546
|
*/
|
542
547
|
function getDirectiveCommentsForFlatConfig(sourceCode, ruleMapper) {
|
543
|
-
const problems = [];
|
544
548
|
const disableDirectives = [];
|
549
|
+
const problems = [];
|
545
550
|
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
if (!match) {
|
552
|
-
return;
|
553
|
-
}
|
554
|
-
const directiveText = match[1];
|
555
|
-
const lineCommentSupported = /^eslint-disable-(next-)?line$/u.test(directiveText);
|
556
|
-
|
557
|
-
if (comment.type === "Line" && !lineCommentSupported) {
|
558
|
-
return;
|
559
|
-
}
|
560
|
-
|
561
|
-
if (directiveText === "eslint-disable-line" && comment.loc.start.line !== comment.loc.end.line) {
|
562
|
-
const message = `${directiveText} comment should not span multiple lines.`;
|
563
|
-
|
564
|
-
problems.push(createLintingProblem({
|
565
|
-
ruleId: null,
|
566
|
-
message,
|
567
|
-
loc: comment.loc
|
568
|
-
}));
|
569
|
-
return;
|
570
|
-
}
|
571
|
-
|
572
|
-
const directiveValue = directivePart.slice(match.index + directiveText.length);
|
551
|
+
const {
|
552
|
+
directives: directivesSources,
|
553
|
+
problems: directivesProblems
|
554
|
+
} = sourceCode.getDisableDirectives();
|
573
555
|
|
574
|
-
|
575
|
-
case "eslint-disable":
|
576
|
-
case "eslint-enable":
|
577
|
-
case "eslint-disable-next-line":
|
578
|
-
case "eslint-disable-line": {
|
579
|
-
const directiveType = directiveText.slice("eslint-".length);
|
580
|
-
const options = { commentToken: comment, type: directiveType, value: directiveValue, justification: justificationPart, ruleMapper };
|
581
|
-
const { directives, directiveProblems } = createDisableDirectives(options);
|
556
|
+
problems.push(...directivesProblems.map(createLintingProblem));
|
582
557
|
|
583
|
-
|
584
|
-
|
585
|
-
break;
|
586
|
-
}
|
558
|
+
directivesSources.forEach(directive => {
|
559
|
+
const { directives, directiveProblems } = createDisableDirectives(directive, ruleMapper);
|
587
560
|
|
588
|
-
|
589
|
-
|
561
|
+
disableDirectives.push(...directives);
|
562
|
+
problems.push(...directiveProblems);
|
590
563
|
});
|
591
564
|
|
592
565
|
return {
|
@@ -736,6 +709,7 @@ function normalizeVerifyOptions(providedOptions, config) {
|
|
736
709
|
: null,
|
737
710
|
reportUnusedDisableDirectives,
|
738
711
|
disableFixes: Boolean(providedOptions.disableFixes),
|
712
|
+
stats: providedOptions.stats,
|
739
713
|
ruleFilter
|
740
714
|
};
|
741
715
|
}
|
@@ -825,6 +799,36 @@ function stripUnicodeBOM(text) {
|
|
825
799
|
return text;
|
826
800
|
}
|
827
801
|
|
802
|
+
/**
|
803
|
+
* Store time measurements in map
|
804
|
+
* @param {number} time Time measurement
|
805
|
+
* @param {Object} timeOpts Options relating which time was measured
|
806
|
+
* @param {WeakMap<Linter, LinterInternalSlots>} slots Linter internal slots map
|
807
|
+
* @returns {void}
|
808
|
+
*/
|
809
|
+
function storeTime(time, timeOpts, slots) {
|
810
|
+
const { type, key } = timeOpts;
|
811
|
+
|
812
|
+
if (!slots.times) {
|
813
|
+
slots.times = { passes: [{}] };
|
814
|
+
}
|
815
|
+
|
816
|
+
const passIndex = slots.fixPasses;
|
817
|
+
|
818
|
+
if (passIndex > slots.times.passes.length - 1) {
|
819
|
+
slots.times.passes.push({});
|
820
|
+
}
|
821
|
+
|
822
|
+
if (key) {
|
823
|
+
slots.times.passes[passIndex][type] ??= {};
|
824
|
+
slots.times.passes[passIndex][type][key] ??= { total: 0 };
|
825
|
+
slots.times.passes[passIndex][type][key].total += time;
|
826
|
+
} else {
|
827
|
+
slots.times.passes[passIndex][type] ??= { total: 0 };
|
828
|
+
slots.times.passes[passIndex][type].total += time;
|
829
|
+
}
|
830
|
+
}
|
831
|
+
|
828
832
|
/**
|
829
833
|
* Get the options for a rule (not including severity), if any
|
830
834
|
* @param {Array|number} ruleConfig rule configuration
|
@@ -986,10 +990,13 @@ function createRuleListeners(rule, ruleContext) {
|
|
986
990
|
* @param {string | undefined} cwd cwd of the cli
|
987
991
|
* @param {string} physicalFilename The full path of the file on disk without any code block information
|
988
992
|
* @param {Function} ruleFilter A predicate function to filter which rules should be executed.
|
993
|
+
* @param {boolean} stats If true, stats are collected appended to the result
|
994
|
+
* @param {WeakMap<Linter, LinterInternalSlots>} slots InternalSlotsMap of linter
|
989
995
|
* @returns {LintMessage[]} An array of reported problems
|
990
996
|
* @throws {Error} If traversal into a node fails.
|
991
997
|
*/
|
992
|
-
function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageOptions, settings, filename, disableFixes, cwd, physicalFilename, ruleFilter
|
998
|
+
function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageOptions, settings, filename, disableFixes, cwd, physicalFilename, ruleFilter,
|
999
|
+
stats, slots) {
|
993
1000
|
const emitter = createEmitter();
|
994
1001
|
|
995
1002
|
// must happen first to assign all node.parent properties
|
@@ -1088,7 +1095,14 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageO
|
|
1088
1095
|
)
|
1089
1096
|
);
|
1090
1097
|
|
1091
|
-
const
|
1098
|
+
const ruleListenersReturn = (timing.enabled || stats)
|
1099
|
+
? timing.time(ruleId, createRuleListeners, stats)(rule, ruleContext) : createRuleListeners(rule, ruleContext);
|
1100
|
+
|
1101
|
+
const ruleListeners = stats ? ruleListenersReturn.result : ruleListenersReturn;
|
1102
|
+
|
1103
|
+
if (stats) {
|
1104
|
+
storeTime(ruleListenersReturn.tdiff, { type: "rules", key: ruleId }, slots);
|
1105
|
+
}
|
1092
1106
|
|
1093
1107
|
/**
|
1094
1108
|
* Include `ruleId` in error logs
|
@@ -1098,7 +1112,15 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageO
|
|
1098
1112
|
function addRuleErrorHandler(ruleListener) {
|
1099
1113
|
return function ruleErrorHandler(...listenerArgs) {
|
1100
1114
|
try {
|
1101
|
-
|
1115
|
+
const ruleListenerReturn = ruleListener(...listenerArgs);
|
1116
|
+
|
1117
|
+
const ruleListenerResult = stats ? ruleListenerReturn.result : ruleListenerReturn;
|
1118
|
+
|
1119
|
+
if (stats) {
|
1120
|
+
storeTime(ruleListenerReturn.tdiff, { type: "rules", key: ruleId }, slots);
|
1121
|
+
}
|
1122
|
+
|
1123
|
+
return ruleListenerResult;
|
1102
1124
|
} catch (e) {
|
1103
1125
|
e.ruleId = ruleId;
|
1104
1126
|
throw e;
|
@@ -1112,9 +1134,8 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageO
|
|
1112
1134
|
|
1113
1135
|
// add all the selectors from the rule as listeners
|
1114
1136
|
Object.keys(ruleListeners).forEach(selector => {
|
1115
|
-
const ruleListener = timing.enabled
|
1116
|
-
? timing.time(ruleId, ruleListeners[selector])
|
1117
|
-
: ruleListeners[selector];
|
1137
|
+
const ruleListener = (timing.enabled || stats)
|
1138
|
+
? timing.time(ruleId, ruleListeners[selector], stats) : ruleListeners[selector];
|
1118
1139
|
|
1119
1140
|
emitter.on(
|
1120
1141
|
selector,
|
@@ -1236,7 +1257,6 @@ function assertEslintrcConfig(linter) {
|
|
1236
1257
|
}
|
1237
1258
|
}
|
1238
1259
|
|
1239
|
-
|
1240
1260
|
//------------------------------------------------------------------------------
|
1241
1261
|
// Public Interface
|
1242
1262
|
//------------------------------------------------------------------------------
|
@@ -1342,12 +1362,25 @@ class Linter {
|
|
1342
1362
|
});
|
1343
1363
|
|
1344
1364
|
if (!slots.lastSourceCode) {
|
1365
|
+
let t;
|
1366
|
+
|
1367
|
+
if (options.stats) {
|
1368
|
+
t = startTime();
|
1369
|
+
}
|
1370
|
+
|
1345
1371
|
const parseResult = parse(
|
1346
1372
|
text,
|
1347
1373
|
languageOptions,
|
1348
1374
|
options.filename
|
1349
1375
|
);
|
1350
1376
|
|
1377
|
+
if (options.stats) {
|
1378
|
+
const time = endTime(t);
|
1379
|
+
const timeOpts = { type: "parse" };
|
1380
|
+
|
1381
|
+
storeTime(time, timeOpts, slots);
|
1382
|
+
}
|
1383
|
+
|
1351
1384
|
if (!parseResult.success) {
|
1352
1385
|
return [parseResult.error];
|
1353
1386
|
}
|
@@ -1398,7 +1431,9 @@ class Linter {
|
|
1398
1431
|
options.disableFixes,
|
1399
1432
|
slots.cwd,
|
1400
1433
|
providedOptions.physicalFilename,
|
1401
|
-
null
|
1434
|
+
null,
|
1435
|
+
options.stats,
|
1436
|
+
slots
|
1402
1437
|
);
|
1403
1438
|
} catch (err) {
|
1404
1439
|
err.message += `\nOccurred while linting ${options.filename}`;
|
@@ -1626,12 +1661,24 @@ class Linter {
|
|
1626
1661
|
const settings = config.settings || {};
|
1627
1662
|
|
1628
1663
|
if (!slots.lastSourceCode) {
|
1664
|
+
let t;
|
1665
|
+
|
1666
|
+
if (options.stats) {
|
1667
|
+
t = startTime();
|
1668
|
+
}
|
1669
|
+
|
1629
1670
|
const parseResult = parse(
|
1630
1671
|
text,
|
1631
1672
|
languageOptions,
|
1632
1673
|
options.filename
|
1633
1674
|
);
|
1634
1675
|
|
1676
|
+
if (options.stats) {
|
1677
|
+
const time = endTime(t);
|
1678
|
+
|
1679
|
+
storeTime(time, { type: "parse" }, slots);
|
1680
|
+
}
|
1681
|
+
|
1635
1682
|
if (!parseResult.success) {
|
1636
1683
|
return [parseResult.error];
|
1637
1684
|
}
|
@@ -1841,7 +1888,9 @@ class Linter {
|
|
1841
1888
|
options.disableFixes,
|
1842
1889
|
slots.cwd,
|
1843
1890
|
providedOptions.physicalFilename,
|
1844
|
-
options.ruleFilter
|
1891
|
+
options.ruleFilter,
|
1892
|
+
options.stats,
|
1893
|
+
slots
|
1845
1894
|
);
|
1846
1895
|
} catch (err) {
|
1847
1896
|
err.message += `\nOccurred while linting ${options.filename}`;
|
@@ -2081,6 +2130,22 @@ class Linter {
|
|
2081
2130
|
return internalSlotsMap.get(this).lastSourceCode;
|
2082
2131
|
}
|
2083
2132
|
|
2133
|
+
/**
|
2134
|
+
* Gets the times spent on (parsing, fixing, linting) a file.
|
2135
|
+
* @returns {LintTimes} The times.
|
2136
|
+
*/
|
2137
|
+
getTimes() {
|
2138
|
+
return internalSlotsMap.get(this).times ?? { passes: [] };
|
2139
|
+
}
|
2140
|
+
|
2141
|
+
/**
|
2142
|
+
* Gets the number of autofix passes that were made in the last run.
|
2143
|
+
* @returns {number} The number of autofix passes.
|
2144
|
+
*/
|
2145
|
+
getFixPassCount() {
|
2146
|
+
return internalSlotsMap.get(this).fixPasses ?? 0;
|
2147
|
+
}
|
2148
|
+
|
2084
2149
|
/**
|
2085
2150
|
* Gets the list of SuppressedLintMessage produced in the last running.
|
2086
2151
|
* @returns {SuppressedLintMessage[]} The list of SuppressedLintMessage
|
@@ -2157,6 +2222,7 @@ class Linter {
|
|
2157
2222
|
currentText = text;
|
2158
2223
|
const debugTextDescription = options && options.filename || `${text.slice(0, 10)}...`;
|
2159
2224
|
const shouldFix = options && typeof options.fix !== "undefined" ? options.fix : true;
|
2225
|
+
const stats = options?.stats;
|
2160
2226
|
|
2161
2227
|
/**
|
2162
2228
|
* This loop continues until one of the following is true:
|
@@ -2167,15 +2233,46 @@ class Linter {
|
|
2167
2233
|
* That means anytime a fix is successfully applied, there will be another pass.
|
2168
2234
|
* Essentially, guaranteeing a minimum of two passes.
|
2169
2235
|
*/
|
2236
|
+
const slots = internalSlotsMap.get(this);
|
2237
|
+
|
2238
|
+
// Remove lint times from the last run.
|
2239
|
+
if (stats) {
|
2240
|
+
delete slots.times;
|
2241
|
+
slots.fixPasses = 0;
|
2242
|
+
}
|
2243
|
+
|
2170
2244
|
do {
|
2171
2245
|
passNumber++;
|
2246
|
+
let tTotal;
|
2247
|
+
|
2248
|
+
if (stats) {
|
2249
|
+
tTotal = startTime();
|
2250
|
+
}
|
2172
2251
|
|
2173
2252
|
debug(`Linting code for ${debugTextDescription} (pass ${passNumber})`);
|
2174
2253
|
messages = this.verify(currentText, config, options);
|
2175
2254
|
|
2176
2255
|
debug(`Generating fixed text for ${debugTextDescription} (pass ${passNumber})`);
|
2256
|
+
let t;
|
2257
|
+
|
2258
|
+
if (stats) {
|
2259
|
+
t = startTime();
|
2260
|
+
}
|
2261
|
+
|
2177
2262
|
fixedResult = SourceCodeFixer.applyFixes(currentText, messages, shouldFix);
|
2178
2263
|
|
2264
|
+
if (stats) {
|
2265
|
+
|
2266
|
+
if (fixedResult.fixed) {
|
2267
|
+
const time = endTime(t);
|
2268
|
+
|
2269
|
+
storeTime(time, { type: "fix" }, slots);
|
2270
|
+
slots.fixPasses++;
|
2271
|
+
} else {
|
2272
|
+
storeTime(0, { type: "fix" }, slots);
|
2273
|
+
}
|
2274
|
+
}
|
2275
|
+
|
2179
2276
|
/*
|
2180
2277
|
* stop if there are any syntax errors.
|
2181
2278
|
* 'fixedResult.output' is a empty string.
|
@@ -2190,6 +2287,13 @@ class Linter {
|
|
2190
2287
|
// update to use the fixed output instead of the original text
|
2191
2288
|
currentText = fixedResult.output;
|
2192
2289
|
|
2290
|
+
if (stats) {
|
2291
|
+
tTotal = endTime(tTotal);
|
2292
|
+
const passIndex = slots.times.passes.length - 1;
|
2293
|
+
|
2294
|
+
slots.times.passes[passIndex].total = tTotal;
|
2295
|
+
}
|
2296
|
+
|
2193
2297
|
} while (
|
2194
2298
|
fixedResult.fixed &&
|
2195
2299
|
passNumber < MAX_AUTOFIX_PASSES
|
@@ -2200,7 +2304,18 @@ class Linter {
|
|
2200
2304
|
* the most up-to-date information.
|
2201
2305
|
*/
|
2202
2306
|
if (fixedResult.fixed) {
|
2307
|
+
let tTotal;
|
2308
|
+
|
2309
|
+
if (stats) {
|
2310
|
+
tTotal = startTime();
|
2311
|
+
}
|
2312
|
+
|
2203
2313
|
fixedResult.messages = this.verify(currentText, config, options);
|
2314
|
+
|
2315
|
+
if (stats) {
|
2316
|
+
storeTime(0, { type: "fix" }, slots);
|
2317
|
+
slots.times.passes.at(-1).total = endTime(tTotal);
|
2318
|
+
}
|
2204
2319
|
}
|
2205
2320
|
|
2206
2321
|
// ensure the last result properly reflects if fixes were done
|
package/lib/linter/timing.js
CHANGED
@@ -5,6 +5,8 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
const { startTime, endTime } = require("../shared/stats");
|
9
|
+
|
8
10
|
//------------------------------------------------------------------------------
|
9
11
|
// Helpers
|
10
12
|
//------------------------------------------------------------------------------
|
@@ -128,21 +130,27 @@ module.exports = (function() {
|
|
128
130
|
* Time the run
|
129
131
|
* @param {any} key key from the data object
|
130
132
|
* @param {Function} fn function to be called
|
133
|
+
* @param {boolean} stats if 'stats' is true, return the result and the time difference
|
131
134
|
* @returns {Function} function to be executed
|
132
135
|
* @private
|
133
136
|
*/
|
134
|
-
function time(key, fn) {
|
135
|
-
if (typeof data[key] === "undefined") {
|
136
|
-
data[key] = 0;
|
137
|
-
}
|
137
|
+
function time(key, fn, stats) {
|
138
138
|
|
139
139
|
return function(...args) {
|
140
|
-
|
140
|
+
|
141
|
+
const t = startTime();
|
141
142
|
const result = fn(...args);
|
143
|
+
const tdiff = endTime(t);
|
144
|
+
|
145
|
+
if (enabled) {
|
146
|
+
if (typeof data[key] === "undefined") {
|
147
|
+
data[key] = 0;
|
148
|
+
}
|
149
|
+
|
150
|
+
data[key] += tdiff;
|
151
|
+
}
|
142
152
|
|
143
|
-
|
144
|
-
data[key] += t[0] * 1e3 + t[1] / 1e6;
|
145
|
-
return result;
|
153
|
+
return stats ? { result, tdiff } : result;
|
146
154
|
};
|
147
155
|
}
|
148
156
|
|