eslint 9.0.0-beta.2 → 9.0.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 +8 -12
- package/bin/eslint.js +14 -1
- package/lib/cli.js +30 -11
- package/lib/config/flat-config-schema.js +1 -2
- package/lib/eslint/eslint-helpers.js +5 -1
- package/lib/eslint/eslint.js +16 -1
- package/lib/linter/code-path-analysis/code-path-analyzer.js +0 -1
- package/lib/linter/index.js +1 -3
- package/lib/linter/linter.js +184 -40
- package/lib/linter/timing.js +16 -8
- package/lib/options.js +25 -1
- package/lib/rule-tester/index.js +3 -1
- package/lib/rule-tester/rule-tester.js +18 -2
- package/lib/rules/camelcase.js +3 -5
- package/lib/rules/constructor-super.js +98 -99
- package/lib/rules/no-fallthrough.js +41 -16
- package/lib/rules/no-lone-blocks.js +1 -1
- package/lib/rules/no-this-before-super.js +28 -9
- package/lib/rules/no-unused-vars.js +179 -29
- package/lib/rules/no-useless-return.js +7 -2
- package/lib/rules/use-isnan.js +2 -2
- package/lib/rules/utils/lazy-loading-rule-map.js +1 -1
- package/lib/rules/utils/unicode/index.js +9 -4
- package/lib/shared/runtime-info.js +1 -0
- package/lib/shared/stats.js +30 -0
- package/lib/shared/types.js +34 -0
- package/lib/source-code/index.js +3 -1
- package/lib/source-code/source-code.js +165 -1
- 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 +12 -8
- package/lib/cli-engine/xml-escape.js +0 -34
- package/lib/shared/deprecation-warnings.js +0 -58
package/lib/linter/linter.js
CHANGED
@@ -30,7 +30,6 @@ const
|
|
30
30
|
} = require("@eslint/eslintrc/universal"),
|
31
31
|
Traverser = require("../shared/traverser"),
|
32
32
|
{ SourceCode } = require("../source-code"),
|
33
|
-
CodePathAnalyzer = require("./code-path-analysis/code-path-analyzer"),
|
34
33
|
applyDisableDirectives = require("./apply-disable-directives"),
|
35
34
|
ConfigCommentParser = require("./config-comment-parser"),
|
36
35
|
NodeEventGenerator = require("./node-event-generator"),
|
@@ -42,6 +41,7 @@ const
|
|
42
41
|
ruleReplacements = require("../../conf/replacements.json");
|
43
42
|
const { getRuleFromConfig } = require("../config/flat-config-helpers");
|
44
43
|
const { FlatConfigArray } = require("../config/flat-config-array");
|
44
|
+
const { startTime, endTime } = require("../shared/stats");
|
45
45
|
const { RuleValidator } = require("../config/rule-validator");
|
46
46
|
const { assertIsRuleSeverity } = require("../config/flat-config-schema");
|
47
47
|
const { normalizeSeverityToString } = require("../shared/severity");
|
@@ -53,13 +53,13 @@ const commentParser = new ConfigCommentParser();
|
|
53
53
|
const DEFAULT_ERROR_LOC = { start: { line: 1, column: 0 }, end: { line: 1, column: 1 } };
|
54
54
|
const parserSymbol = Symbol.for("eslint.RuleTester.parser");
|
55
55
|
const { LATEST_ECMA_VERSION } = require("../../conf/ecma-version");
|
56
|
+
const STEP_KIND_VISIT = 1;
|
57
|
+
const STEP_KIND_CALL = 2;
|
56
58
|
|
57
59
|
//------------------------------------------------------------------------------
|
58
60
|
// Typedefs
|
59
61
|
//------------------------------------------------------------------------------
|
60
62
|
|
61
|
-
/** @typedef {InstanceType<import("../cli-engine/config-array").ConfigArray>} ConfigArray */
|
62
|
-
/** @typedef {InstanceType<import("../cli-engine/config-array").ExtractedConfig>} ExtractedConfig */
|
63
63
|
/** @typedef {import("../shared/types").ConfigData} ConfigData */
|
64
64
|
/** @typedef {import("../shared/types").Environment} Environment */
|
65
65
|
/** @typedef {import("../shared/types").GlobalConf} GlobalConf */
|
@@ -69,6 +69,7 @@ const { LATEST_ECMA_VERSION } = require("../../conf/ecma-version");
|
|
69
69
|
/** @typedef {import("../shared/types").LanguageOptions} LanguageOptions */
|
70
70
|
/** @typedef {import("../shared/types").Processor} Processor */
|
71
71
|
/** @typedef {import("../shared/types").Rule} Rule */
|
72
|
+
/** @typedef {import("../shared/types").Times} Times */
|
72
73
|
|
73
74
|
/* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
|
74
75
|
/**
|
@@ -93,6 +94,7 @@ const { LATEST_ECMA_VERSION } = require("../../conf/ecma-version");
|
|
93
94
|
* @property {SourceCode|null} lastSourceCode The `SourceCode` instance that the last `verify()` call used.
|
94
95
|
* @property {SuppressedLintMessage[]} lastSuppressedMessages The `SuppressedLintMessage[]` instance that the last `verify()` call produced.
|
95
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).
|
96
98
|
* @property {Rules} ruleMap The loaded rules.
|
97
99
|
*/
|
98
100
|
|
@@ -737,6 +739,7 @@ function normalizeVerifyOptions(providedOptions, config) {
|
|
737
739
|
: null,
|
738
740
|
reportUnusedDisableDirectives,
|
739
741
|
disableFixes: Boolean(providedOptions.disableFixes),
|
742
|
+
stats: providedOptions.stats,
|
740
743
|
ruleFilter
|
741
744
|
};
|
742
745
|
}
|
@@ -826,6 +829,36 @@ function stripUnicodeBOM(text) {
|
|
826
829
|
return text;
|
827
830
|
}
|
828
831
|
|
832
|
+
/**
|
833
|
+
* Store time measurements in map
|
834
|
+
* @param {number} time Time measurement
|
835
|
+
* @param {Object} timeOpts Options relating which time was measured
|
836
|
+
* @param {WeakMap<Linter, LinterInternalSlots>} slots Linter internal slots map
|
837
|
+
* @returns {void}
|
838
|
+
*/
|
839
|
+
function storeTime(time, timeOpts, slots) {
|
840
|
+
const { type, key } = timeOpts;
|
841
|
+
|
842
|
+
if (!slots.times) {
|
843
|
+
slots.times = { passes: [{}] };
|
844
|
+
}
|
845
|
+
|
846
|
+
const passIndex = slots.fixPasses;
|
847
|
+
|
848
|
+
if (passIndex > slots.times.passes.length - 1) {
|
849
|
+
slots.times.passes.push({});
|
850
|
+
}
|
851
|
+
|
852
|
+
if (key) {
|
853
|
+
slots.times.passes[passIndex][type] ??= {};
|
854
|
+
slots.times.passes[passIndex][type][key] ??= { total: 0 };
|
855
|
+
slots.times.passes[passIndex][type][key].total += time;
|
856
|
+
} else {
|
857
|
+
slots.times.passes[passIndex][type] ??= { total: 0 };
|
858
|
+
slots.times.passes[passIndex][type].total += time;
|
859
|
+
}
|
860
|
+
}
|
861
|
+
|
829
862
|
/**
|
830
863
|
* Get the options for a rule (not including severity), if any
|
831
864
|
* @param {Array|number} ruleConfig rule configuration
|
@@ -987,23 +1020,17 @@ function createRuleListeners(rule, ruleContext) {
|
|
987
1020
|
* @param {string | undefined} cwd cwd of the cli
|
988
1021
|
* @param {string} physicalFilename The full path of the file on disk without any code block information
|
989
1022
|
* @param {Function} ruleFilter A predicate function to filter which rules should be executed.
|
1023
|
+
* @param {boolean} stats If true, stats are collected appended to the result
|
1024
|
+
* @param {WeakMap<Linter, LinterInternalSlots>} slots InternalSlotsMap of linter
|
990
1025
|
* @returns {LintMessage[]} An array of reported problems
|
1026
|
+
* @throws {Error} If traversal into a node fails.
|
991
1027
|
*/
|
992
|
-
function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageOptions, settings, filename, disableFixes, cwd, physicalFilename, ruleFilter
|
1028
|
+
function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageOptions, settings, filename, disableFixes, cwd, physicalFilename, ruleFilter,
|
1029
|
+
stats, slots) {
|
993
1030
|
const emitter = createEmitter();
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
Traverser.traverse(sourceCode.ast, {
|
998
|
-
enter(node, parent) {
|
999
|
-
node.parent = parent;
|
1000
|
-
nodeQueue.push({ isEntering: true, node });
|
1001
|
-
},
|
1002
|
-
leave(node) {
|
1003
|
-
nodeQueue.push({ isEntering: false, node });
|
1004
|
-
},
|
1005
|
-
visitorKeys: sourceCode.visitorKeys
|
1006
|
-
});
|
1031
|
+
|
1032
|
+
// must happen first to assign all node.parent properties
|
1033
|
+
const eventQueue = sourceCode.traverse();
|
1007
1034
|
|
1008
1035
|
/*
|
1009
1036
|
* Create a frozen object with the ruleContext properties and methods that are shared by all rules.
|
@@ -1098,7 +1125,14 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageO
|
|
1098
1125
|
)
|
1099
1126
|
);
|
1100
1127
|
|
1101
|
-
const
|
1128
|
+
const ruleListenersReturn = (timing.enabled || stats)
|
1129
|
+
? timing.time(ruleId, createRuleListeners, stats)(rule, ruleContext) : createRuleListeners(rule, ruleContext);
|
1130
|
+
|
1131
|
+
const ruleListeners = stats ? ruleListenersReturn.result : ruleListenersReturn;
|
1132
|
+
|
1133
|
+
if (stats) {
|
1134
|
+
storeTime(ruleListenersReturn.tdiff, { type: "rules", key: ruleId }, slots);
|
1135
|
+
}
|
1102
1136
|
|
1103
1137
|
/**
|
1104
1138
|
* Include `ruleId` in error logs
|
@@ -1108,7 +1142,15 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageO
|
|
1108
1142
|
function addRuleErrorHandler(ruleListener) {
|
1109
1143
|
return function ruleErrorHandler(...listenerArgs) {
|
1110
1144
|
try {
|
1111
|
-
|
1145
|
+
const ruleListenerReturn = ruleListener(...listenerArgs);
|
1146
|
+
|
1147
|
+
const ruleListenerResult = stats ? ruleListenerReturn.result : ruleListenerReturn;
|
1148
|
+
|
1149
|
+
if (stats) {
|
1150
|
+
storeTime(ruleListenerReturn.tdiff, { type: "rules", key: ruleId }, slots);
|
1151
|
+
}
|
1152
|
+
|
1153
|
+
return ruleListenerResult;
|
1112
1154
|
} catch (e) {
|
1113
1155
|
e.ruleId = ruleId;
|
1114
1156
|
throw e;
|
@@ -1122,9 +1164,8 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageO
|
|
1122
1164
|
|
1123
1165
|
// add all the selectors from the rule as listeners
|
1124
1166
|
Object.keys(ruleListeners).forEach(selector => {
|
1125
|
-
const ruleListener = timing.enabled
|
1126
|
-
? timing.time(ruleId, ruleListeners[selector])
|
1127
|
-
: ruleListeners[selector];
|
1167
|
+
const ruleListener = (timing.enabled || stats)
|
1168
|
+
? timing.time(ruleId, ruleListeners[selector], stats) : ruleListeners[selector];
|
1128
1169
|
|
1129
1170
|
emitter.on(
|
1130
1171
|
selector,
|
@@ -1133,25 +1174,34 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageO
|
|
1133
1174
|
});
|
1134
1175
|
});
|
1135
1176
|
|
1136
|
-
|
1137
|
-
const eventGenerator = nodeQueue[0].node.type === "Program"
|
1138
|
-
? new CodePathAnalyzer(new NodeEventGenerator(emitter, { visitorKeys: sourceCode.visitorKeys, fallback: Traverser.getKeys }))
|
1139
|
-
: new NodeEventGenerator(emitter, { visitorKeys: sourceCode.visitorKeys, fallback: Traverser.getKeys });
|
1177
|
+
const eventGenerator = new NodeEventGenerator(emitter, { visitorKeys: sourceCode.visitorKeys, fallback: Traverser.getKeys });
|
1140
1178
|
|
1141
|
-
|
1142
|
-
|
1179
|
+
for (const step of eventQueue) {
|
1180
|
+
switch (step.kind) {
|
1181
|
+
case STEP_KIND_VISIT: {
|
1182
|
+
try {
|
1183
|
+
if (step.phase === 1) {
|
1184
|
+
eventGenerator.enterNode(step.target);
|
1185
|
+
} else {
|
1186
|
+
eventGenerator.leaveNode(step.target);
|
1187
|
+
}
|
1188
|
+
} catch (err) {
|
1189
|
+
err.currentNode = step.target;
|
1190
|
+
throw err;
|
1191
|
+
}
|
1192
|
+
break;
|
1193
|
+
}
|
1143
1194
|
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
} else {
|
1148
|
-
eventGenerator.leaveNode(currentNode);
|
1195
|
+
case STEP_KIND_CALL: {
|
1196
|
+
emitter.emit(step.target, ...step.args);
|
1197
|
+
break;
|
1149
1198
|
}
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1199
|
+
|
1200
|
+
default:
|
1201
|
+
throw new Error(`Invalid traversal step found: "${step.type}".`);
|
1153
1202
|
}
|
1154
|
-
|
1203
|
+
|
1204
|
+
}
|
1155
1205
|
|
1156
1206
|
return lintingProblems;
|
1157
1207
|
}
|
@@ -1237,7 +1287,6 @@ function assertEslintrcConfig(linter) {
|
|
1237
1287
|
}
|
1238
1288
|
}
|
1239
1289
|
|
1240
|
-
|
1241
1290
|
//------------------------------------------------------------------------------
|
1242
1291
|
// Public Interface
|
1243
1292
|
//------------------------------------------------------------------------------
|
@@ -1343,12 +1392,25 @@ class Linter {
|
|
1343
1392
|
});
|
1344
1393
|
|
1345
1394
|
if (!slots.lastSourceCode) {
|
1395
|
+
let t;
|
1396
|
+
|
1397
|
+
if (options.stats) {
|
1398
|
+
t = startTime();
|
1399
|
+
}
|
1400
|
+
|
1346
1401
|
const parseResult = parse(
|
1347
1402
|
text,
|
1348
1403
|
languageOptions,
|
1349
1404
|
options.filename
|
1350
1405
|
);
|
1351
1406
|
|
1407
|
+
if (options.stats) {
|
1408
|
+
const time = endTime(t);
|
1409
|
+
const timeOpts = { type: "parse" };
|
1410
|
+
|
1411
|
+
storeTime(time, timeOpts, slots);
|
1412
|
+
}
|
1413
|
+
|
1352
1414
|
if (!parseResult.success) {
|
1353
1415
|
return [parseResult.error];
|
1354
1416
|
}
|
@@ -1399,7 +1461,9 @@ class Linter {
|
|
1399
1461
|
options.disableFixes,
|
1400
1462
|
slots.cwd,
|
1401
1463
|
providedOptions.physicalFilename,
|
1402
|
-
null
|
1464
|
+
null,
|
1465
|
+
options.stats,
|
1466
|
+
slots
|
1403
1467
|
);
|
1404
1468
|
} catch (err) {
|
1405
1469
|
err.message += `\nOccurred while linting ${options.filename}`;
|
@@ -1627,12 +1691,24 @@ class Linter {
|
|
1627
1691
|
const settings = config.settings || {};
|
1628
1692
|
|
1629
1693
|
if (!slots.lastSourceCode) {
|
1694
|
+
let t;
|
1695
|
+
|
1696
|
+
if (options.stats) {
|
1697
|
+
t = startTime();
|
1698
|
+
}
|
1699
|
+
|
1630
1700
|
const parseResult = parse(
|
1631
1701
|
text,
|
1632
1702
|
languageOptions,
|
1633
1703
|
options.filename
|
1634
1704
|
);
|
1635
1705
|
|
1706
|
+
if (options.stats) {
|
1707
|
+
const time = endTime(t);
|
1708
|
+
|
1709
|
+
storeTime(time, { type: "parse" }, slots);
|
1710
|
+
}
|
1711
|
+
|
1636
1712
|
if (!parseResult.success) {
|
1637
1713
|
return [parseResult.error];
|
1638
1714
|
}
|
@@ -1842,7 +1918,9 @@ class Linter {
|
|
1842
1918
|
options.disableFixes,
|
1843
1919
|
slots.cwd,
|
1844
1920
|
providedOptions.physicalFilename,
|
1845
|
-
options.ruleFilter
|
1921
|
+
options.ruleFilter,
|
1922
|
+
options.stats,
|
1923
|
+
slots
|
1846
1924
|
);
|
1847
1925
|
} catch (err) {
|
1848
1926
|
err.message += `\nOccurred while linting ${options.filename}`;
|
@@ -2082,6 +2160,22 @@ class Linter {
|
|
2082
2160
|
return internalSlotsMap.get(this).lastSourceCode;
|
2083
2161
|
}
|
2084
2162
|
|
2163
|
+
/**
|
2164
|
+
* Gets the times spent on (parsing, fixing, linting) a file.
|
2165
|
+
* @returns {LintTimes} The times.
|
2166
|
+
*/
|
2167
|
+
getTimes() {
|
2168
|
+
return internalSlotsMap.get(this).times ?? { passes: [] };
|
2169
|
+
}
|
2170
|
+
|
2171
|
+
/**
|
2172
|
+
* Gets the number of autofix passes that were made in the last run.
|
2173
|
+
* @returns {number} The number of autofix passes.
|
2174
|
+
*/
|
2175
|
+
getFixPassCount() {
|
2176
|
+
return internalSlotsMap.get(this).fixPasses ?? 0;
|
2177
|
+
}
|
2178
|
+
|
2085
2179
|
/**
|
2086
2180
|
* Gets the list of SuppressedLintMessage produced in the last running.
|
2087
2181
|
* @returns {SuppressedLintMessage[]} The list of SuppressedLintMessage
|
@@ -2158,6 +2252,7 @@ class Linter {
|
|
2158
2252
|
currentText = text;
|
2159
2253
|
const debugTextDescription = options && options.filename || `${text.slice(0, 10)}...`;
|
2160
2254
|
const shouldFix = options && typeof options.fix !== "undefined" ? options.fix : true;
|
2255
|
+
const stats = options?.stats;
|
2161
2256
|
|
2162
2257
|
/**
|
2163
2258
|
* This loop continues until one of the following is true:
|
@@ -2168,15 +2263,46 @@ class Linter {
|
|
2168
2263
|
* That means anytime a fix is successfully applied, there will be another pass.
|
2169
2264
|
* Essentially, guaranteeing a minimum of two passes.
|
2170
2265
|
*/
|
2266
|
+
const slots = internalSlotsMap.get(this);
|
2267
|
+
|
2268
|
+
// Remove lint times from the last run.
|
2269
|
+
if (stats) {
|
2270
|
+
delete slots.times;
|
2271
|
+
slots.fixPasses = 0;
|
2272
|
+
}
|
2273
|
+
|
2171
2274
|
do {
|
2172
2275
|
passNumber++;
|
2276
|
+
let tTotal;
|
2277
|
+
|
2278
|
+
if (stats) {
|
2279
|
+
tTotal = startTime();
|
2280
|
+
}
|
2173
2281
|
|
2174
2282
|
debug(`Linting code for ${debugTextDescription} (pass ${passNumber})`);
|
2175
2283
|
messages = this.verify(currentText, config, options);
|
2176
2284
|
|
2177
2285
|
debug(`Generating fixed text for ${debugTextDescription} (pass ${passNumber})`);
|
2286
|
+
let t;
|
2287
|
+
|
2288
|
+
if (stats) {
|
2289
|
+
t = startTime();
|
2290
|
+
}
|
2291
|
+
|
2178
2292
|
fixedResult = SourceCodeFixer.applyFixes(currentText, messages, shouldFix);
|
2179
2293
|
|
2294
|
+
if (stats) {
|
2295
|
+
|
2296
|
+
if (fixedResult.fixed) {
|
2297
|
+
const time = endTime(t);
|
2298
|
+
|
2299
|
+
storeTime(time, { type: "fix" }, slots);
|
2300
|
+
slots.fixPasses++;
|
2301
|
+
} else {
|
2302
|
+
storeTime(0, { type: "fix" }, slots);
|
2303
|
+
}
|
2304
|
+
}
|
2305
|
+
|
2180
2306
|
/*
|
2181
2307
|
* stop if there are any syntax errors.
|
2182
2308
|
* 'fixedResult.output' is a empty string.
|
@@ -2191,6 +2317,13 @@ class Linter {
|
|
2191
2317
|
// update to use the fixed output instead of the original text
|
2192
2318
|
currentText = fixedResult.output;
|
2193
2319
|
|
2320
|
+
if (stats) {
|
2321
|
+
tTotal = endTime(tTotal);
|
2322
|
+
const passIndex = slots.times.passes.length - 1;
|
2323
|
+
|
2324
|
+
slots.times.passes[passIndex].total = tTotal;
|
2325
|
+
}
|
2326
|
+
|
2194
2327
|
} while (
|
2195
2328
|
fixedResult.fixed &&
|
2196
2329
|
passNumber < MAX_AUTOFIX_PASSES
|
@@ -2201,7 +2334,18 @@ class Linter {
|
|
2201
2334
|
* the most up-to-date information.
|
2202
2335
|
*/
|
2203
2336
|
if (fixedResult.fixed) {
|
2337
|
+
let tTotal;
|
2338
|
+
|
2339
|
+
if (stats) {
|
2340
|
+
tTotal = startTime();
|
2341
|
+
}
|
2342
|
+
|
2204
2343
|
fixedResult.messages = this.verify(currentText, config, options);
|
2344
|
+
|
2345
|
+
if (stats) {
|
2346
|
+
storeTime(0, { type: "fix" }, slots);
|
2347
|
+
slots.times.passes.at(-1).total = endTime(tTotal);
|
2348
|
+
}
|
2205
2349
|
}
|
2206
2350
|
|
2207
2351
|
// 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
|
|
package/lib/options.js
CHANGED
@@ -60,6 +60,7 @@ const optionator = require("optionator");
|
|
60
60
|
* @property {boolean} [passOnNoPatterns=false] When set to true, missing patterns cause
|
61
61
|
* the linting operation to short circuit and not report any failures.
|
62
62
|
* @property {string[]} _ Positional filenames or patterns
|
63
|
+
* @property {boolean} [stats] Report additional statistics
|
63
64
|
*/
|
64
65
|
|
65
66
|
//------------------------------------------------------------------------------
|
@@ -103,6 +104,16 @@ module.exports = function(usingFlatConfig) {
|
|
103
104
|
};
|
104
105
|
}
|
105
106
|
|
107
|
+
let inspectConfigFlag;
|
108
|
+
|
109
|
+
if (usingFlatConfig) {
|
110
|
+
inspectConfigFlag = {
|
111
|
+
option: "inspect-config",
|
112
|
+
type: "Boolean",
|
113
|
+
description: "Open the config inspector with the current configuration"
|
114
|
+
};
|
115
|
+
}
|
116
|
+
|
106
117
|
let extFlag;
|
107
118
|
|
108
119
|
if (!usingFlatConfig) {
|
@@ -143,6 +154,17 @@ module.exports = function(usingFlatConfig) {
|
|
143
154
|
};
|
144
155
|
}
|
145
156
|
|
157
|
+
let statsFlag;
|
158
|
+
|
159
|
+
if (usingFlatConfig) {
|
160
|
+
statsFlag = {
|
161
|
+
option: "stats",
|
162
|
+
type: "Boolean",
|
163
|
+
default: "false",
|
164
|
+
description: "Add statistics to the lint report"
|
165
|
+
};
|
166
|
+
}
|
167
|
+
|
146
168
|
let warnIgnoredFlag;
|
147
169
|
|
148
170
|
if (usingFlatConfig) {
|
@@ -173,6 +195,7 @@ module.exports = function(usingFlatConfig) {
|
|
173
195
|
? "Use this configuration instead of eslint.config.js, eslint.config.mjs, or eslint.config.cjs"
|
174
196
|
: "Use this configuration, overriding .eslintrc.* config options if present"
|
175
197
|
},
|
198
|
+
inspectConfigFlag,
|
176
199
|
envFlag,
|
177
200
|
extFlag,
|
178
201
|
{
|
@@ -400,7 +423,8 @@ module.exports = function(usingFlatConfig) {
|
|
400
423
|
option: "print-config",
|
401
424
|
type: "path::String",
|
402
425
|
description: "Print the configuration for the given file"
|
403
|
-
}
|
426
|
+
},
|
427
|
+
statsFlag
|
404
428
|
].filter(value => !!value)
|
405
429
|
});
|
406
430
|
};
|
package/lib/rule-tester/index.js
CHANGED
@@ -136,6 +136,15 @@ const suggestionObjectParameters = new Set([
|
|
136
136
|
]);
|
137
137
|
const friendlySuggestionObjectParameterList = `[${[...suggestionObjectParameters].map(key => `'${key}'`).join(", ")}]`;
|
138
138
|
|
139
|
+
/*
|
140
|
+
* Ignored test case properties when checking for test case duplicates.
|
141
|
+
*/
|
142
|
+
const duplicationIgnoredParameters = new Set([
|
143
|
+
"name",
|
144
|
+
"errors",
|
145
|
+
"output"
|
146
|
+
]);
|
147
|
+
|
139
148
|
const forbiddenMethods = [
|
140
149
|
"applyInlineConfig",
|
141
150
|
"applyLanguageOptions",
|
@@ -848,7 +857,7 @@ class RuleTester {
|
|
848
857
|
|
849
858
|
/**
|
850
859
|
* Check if this test case is a duplicate of one we have seen before.
|
851
|
-
* @param {Object} item test case object
|
860
|
+
* @param {string|Object} item test case object
|
852
861
|
* @param {Set<string>} seenTestCases set of serialized test cases we have seen so far (managed by this function)
|
853
862
|
* @returns {void}
|
854
863
|
* @private
|
@@ -863,7 +872,14 @@ class RuleTester {
|
|
863
872
|
return;
|
864
873
|
}
|
865
874
|
|
866
|
-
const
|
875
|
+
const normalizedItem = typeof item === "string" ? { code: item } : item;
|
876
|
+
const serializedTestCase = stringify(normalizedItem, {
|
877
|
+
replacer(key, value) {
|
878
|
+
|
879
|
+
// "this" is the currently stringified object --> only ignore top-level properties
|
880
|
+
return (normalizedItem !== this || !duplicationIgnoredParameters.has(key)) ? value : void 0;
|
881
|
+
}
|
882
|
+
});
|
867
883
|
|
868
884
|
assert(
|
869
885
|
!seenTestCases.has(serializedTestCase),
|