eslint 9.0.0-beta.2 → 9.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/bin/eslint.js +2 -1
- package/lib/cli.js +28 -11
- package/lib/config/flat-config-schema.js +1 -2
- package/lib/eslint/eslint-helpers.js +0 -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 +30 -31
- package/lib/rule-tester/index.js +3 -1
- package/lib/rules/constructor-super.js +53 -23
- package/lib/rules/no-fallthrough.js +41 -16
- package/lib/rules/no-this-before-super.js +28 -9
- package/lib/rules/no-useless-return.js +7 -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/source-code/index.js +3 -1
- package/lib/source-code/source-code.js +165 -1
- package/package.json +9 -6
- package/lib/cli-engine/xml-escape.js +0 -34
- package/lib/shared/deprecation-warnings.js +0 -58
package/README.md
CHANGED
@@ -303,8 +303,8 @@ The following companies, organizations, and individuals support ESLint's ongoing
|
|
303
303
|
<!-- NOTE: This section is autogenerated. Do not manually edit.-->
|
304
304
|
<!--sponsorsstart-->
|
305
305
|
<h3>Platinum Sponsors</h3>
|
306
|
-
<p><a href="
|
307
|
-
<p><a href="https://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a></p><h3>Silver Sponsors</h3>
|
306
|
+
<p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
|
307
|
+
<p><a href="https://bitwarden.com"><img src="https://avatars.githubusercontent.com/u/15990069?v=4" alt="Bitwarden" height="96"></a> <a href="https://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a></p><h3>Silver Sponsors</h3>
|
308
308
|
<p><a href="https://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/eb04ddc/logo.png" alt="JetBrains" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a> <a href="https://www.workleap.com"><img src="https://avatars.githubusercontent.com/u/53535748?u=d1e55d7661d724bf2281c1bfd33cb8f99fe2465f&v=4" alt="Workleap" height="64"></a></p><h3>Bronze Sponsors</h3>
|
309
309
|
<p><a href="https://www.notion.so"><img src="https://images.opencollective.com/notion/bf3b117/logo.png" alt="notion" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://transloadit.com/"><img src="https://avatars.githubusercontent.com/u/125754?v=4" alt="Transloadit" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104?v=4" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://usenextbase.com"><img src="https://avatars.githubusercontent.com/u/145838380?v=4" alt="Nextbase Starter Kit" height="32"></a></p>
|
310
310
|
<!--sponsorsend-->
|
package/bin/eslint.js
CHANGED
@@ -149,7 +149,8 @@ ${getErrorMessage(error)}`;
|
|
149
149
|
}
|
150
150
|
|
151
151
|
// Otherwise, call the CLI.
|
152
|
-
const
|
152
|
+
const cli = require("../lib/cli");
|
153
|
+
const exitCode = await cli.execute(
|
153
154
|
process.argv,
|
154
155
|
process.argv.includes("--stdin") ? await readStdin() : null,
|
155
156
|
true
|
package/lib/cli.js
CHANGED
@@ -37,6 +37,7 @@ const debug = require("debug")("eslint:cli");
|
|
37
37
|
/** @typedef {import("./eslint/eslint").LintMessage} LintMessage */
|
38
38
|
/** @typedef {import("./eslint/eslint").LintResult} LintResult */
|
39
39
|
/** @typedef {import("./options").ParsedCLIOptions} ParsedCLIOptions */
|
40
|
+
/** @typedef {import("./shared/types").Plugin} Plugin */
|
40
41
|
/** @typedef {import("./shared/types").ResultsMeta} ResultsMeta */
|
41
42
|
|
42
43
|
//------------------------------------------------------------------------------
|
@@ -47,6 +48,32 @@ const mkdir = promisify(fs.mkdir);
|
|
47
48
|
const stat = promisify(fs.stat);
|
48
49
|
const writeFile = promisify(fs.writeFile);
|
49
50
|
|
51
|
+
/**
|
52
|
+
* Loads plugins with the specified names.
|
53
|
+
* @param {{ "import": (name: string) => Promise<any> }} importer An object with an `import` method called once for each plugin.
|
54
|
+
* @param {string[]} pluginNames The names of the plugins to be loaded, with or without the "eslint-plugin-" prefix.
|
55
|
+
* @returns {Promise<Record<string, Plugin>>} A mapping of plugin short names to implementations.
|
56
|
+
*/
|
57
|
+
async function loadPlugins(importer, pluginNames) {
|
58
|
+
const plugins = {};
|
59
|
+
|
60
|
+
await Promise.all(pluginNames.map(async pluginName => {
|
61
|
+
|
62
|
+
const longName = naming.normalizePackageName(pluginName, "eslint-plugin");
|
63
|
+
const module = await importer.import(longName);
|
64
|
+
|
65
|
+
if (!("default" in module)) {
|
66
|
+
throw new Error(`"${longName}" cannot be used with the \`--plugin\` option because its default module does not provide a \`default\` export`);
|
67
|
+
}
|
68
|
+
|
69
|
+
const shortName = naming.getShorthandName(pluginName, "eslint-plugin");
|
70
|
+
|
71
|
+
plugins[shortName] = module.default;
|
72
|
+
}));
|
73
|
+
|
74
|
+
return plugins;
|
75
|
+
}
|
76
|
+
|
50
77
|
/**
|
51
78
|
* Predicate function for whether or not to apply fixes in quiet mode.
|
52
79
|
* If a message is a warning, do not apply a fix.
|
@@ -152,17 +179,7 @@ async function translateOptions({
|
|
152
179
|
}
|
153
180
|
|
154
181
|
if (plugin) {
|
155
|
-
|
156
|
-
|
157
|
-
for (const pluginName of plugin) {
|
158
|
-
|
159
|
-
const shortName = naming.getShorthandName(pluginName, "eslint-plugin");
|
160
|
-
const longName = naming.normalizePackageName(pluginName, "eslint-plugin");
|
161
|
-
|
162
|
-
plugins[shortName] = await importer.import(longName);
|
163
|
-
}
|
164
|
-
|
165
|
-
overrideConfig[0].plugins = plugins;
|
182
|
+
overrideConfig[0].plugins = await loadPlugins(importer, plugin);
|
166
183
|
}
|
167
184
|
|
168
185
|
} else {
|
package/lib/linter/index.js
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
"use strict";
|
2
2
|
|
3
3
|
const { Linter } = require("./linter");
|
4
|
-
const { interpolate } = require("./interpolate");
|
5
4
|
const SourceCodeFixer = require("./source-code-fixer");
|
6
5
|
|
7
6
|
module.exports = {
|
8
7
|
Linter,
|
9
8
|
|
10
9
|
// For testers.
|
11
|
-
SourceCodeFixer
|
12
|
-
interpolate
|
10
|
+
SourceCodeFixer
|
13
11
|
};
|
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"),
|
@@ -53,13 +52,13 @@ const commentParser = new ConfigCommentParser();
|
|
53
52
|
const DEFAULT_ERROR_LOC = { start: { line: 1, column: 0 }, end: { line: 1, column: 1 } };
|
54
53
|
const parserSymbol = Symbol.for("eslint.RuleTester.parser");
|
55
54
|
const { LATEST_ECMA_VERSION } = require("../../conf/ecma-version");
|
55
|
+
const STEP_KIND_VISIT = 1;
|
56
|
+
const STEP_KIND_CALL = 2;
|
56
57
|
|
57
58
|
//------------------------------------------------------------------------------
|
58
59
|
// Typedefs
|
59
60
|
//------------------------------------------------------------------------------
|
60
61
|
|
61
|
-
/** @typedef {InstanceType<import("../cli-engine/config-array").ConfigArray>} ConfigArray */
|
62
|
-
/** @typedef {InstanceType<import("../cli-engine/config-array").ExtractedConfig>} ExtractedConfig */
|
63
62
|
/** @typedef {import("../shared/types").ConfigData} ConfigData */
|
64
63
|
/** @typedef {import("../shared/types").Environment} Environment */
|
65
64
|
/** @typedef {import("../shared/types").GlobalConf} GlobalConf */
|
@@ -988,22 +987,13 @@ function createRuleListeners(rule, ruleContext) {
|
|
988
987
|
* @param {string} physicalFilename The full path of the file on disk without any code block information
|
989
988
|
* @param {Function} ruleFilter A predicate function to filter which rules should be executed.
|
990
989
|
* @returns {LintMessage[]} An array of reported problems
|
990
|
+
* @throws {Error} If traversal into a node fails.
|
991
991
|
*/
|
992
992
|
function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageOptions, settings, filename, disableFixes, cwd, physicalFilename, ruleFilter) {
|
993
993
|
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
|
-
});
|
994
|
+
|
995
|
+
// must happen first to assign all node.parent properties
|
996
|
+
const eventQueue = sourceCode.traverse();
|
1007
997
|
|
1008
998
|
/*
|
1009
999
|
* Create a frozen object with the ruleContext properties and methods that are shared by all rules.
|
@@ -1133,25 +1123,34 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageO
|
|
1133
1123
|
});
|
1134
1124
|
});
|
1135
1125
|
|
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 });
|
1126
|
+
const eventGenerator = new NodeEventGenerator(emitter, { visitorKeys: sourceCode.visitorKeys, fallback: Traverser.getKeys });
|
1140
1127
|
|
1141
|
-
|
1142
|
-
|
1128
|
+
for (const step of eventQueue) {
|
1129
|
+
switch (step.kind) {
|
1130
|
+
case STEP_KIND_VISIT: {
|
1131
|
+
try {
|
1132
|
+
if (step.phase === 1) {
|
1133
|
+
eventGenerator.enterNode(step.target);
|
1134
|
+
} else {
|
1135
|
+
eventGenerator.leaveNode(step.target);
|
1136
|
+
}
|
1137
|
+
} catch (err) {
|
1138
|
+
err.currentNode = step.target;
|
1139
|
+
throw err;
|
1140
|
+
}
|
1141
|
+
break;
|
1142
|
+
}
|
1143
1143
|
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
} else {
|
1148
|
-
eventGenerator.leaveNode(currentNode);
|
1144
|
+
case STEP_KIND_CALL: {
|
1145
|
+
emitter.emit(step.target, ...step.args);
|
1146
|
+
break;
|
1149
1147
|
}
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1148
|
+
|
1149
|
+
default:
|
1150
|
+
throw new Error(`Invalid traversal step found: "${step.type}".`);
|
1153
1151
|
}
|
1154
|
-
|
1152
|
+
|
1153
|
+
}
|
1155
1154
|
|
1156
1155
|
return lintingProblems;
|
1157
1156
|
}
|
package/lib/rule-tester/index.js
CHANGED
@@ -119,6 +119,30 @@ function isPossibleConstructor(node) {
|
|
119
119
|
}
|
120
120
|
}
|
121
121
|
|
122
|
+
/**
|
123
|
+
* A class to store information about a code path segment.
|
124
|
+
*/
|
125
|
+
class SegmentInfo {
|
126
|
+
|
127
|
+
/**
|
128
|
+
* Indicates if super() is called in all code paths.
|
129
|
+
* @type {boolean}
|
130
|
+
*/
|
131
|
+
calledInEveryPaths = false;
|
132
|
+
|
133
|
+
/**
|
134
|
+
* Indicates if super() is called in any code paths.
|
135
|
+
* @type {boolean}
|
136
|
+
*/
|
137
|
+
calledInSomePaths = false;
|
138
|
+
|
139
|
+
/**
|
140
|
+
* The nodes which have been validated and don't need to be reconsidered.
|
141
|
+
* @type {ASTNode[]}
|
142
|
+
*/
|
143
|
+
validNodes = [];
|
144
|
+
}
|
145
|
+
|
122
146
|
//------------------------------------------------------------------------------
|
123
147
|
// Rule Definition
|
124
148
|
//------------------------------------------------------------------------------
|
@@ -159,12 +183,8 @@ module.exports = {
|
|
159
183
|
*/
|
160
184
|
let funcInfo = null;
|
161
185
|
|
162
|
-
|
163
|
-
* {
|
164
|
-
* Information for each code path segment.
|
165
|
-
* - calledInSomePaths: A flag of be called `super()` in some code paths.
|
166
|
-
* - calledInEveryPaths: A flag of be called `super()` in all code paths.
|
167
|
-
* - validNodes:
|
186
|
+
/**
|
187
|
+
* @type {Record<string, SegmentInfo>}
|
168
188
|
*/
|
169
189
|
let segInfoMap = Object.create(null);
|
170
190
|
|
@@ -174,7 +194,16 @@ module.exports = {
|
|
174
194
|
* @returns {boolean} The flag which shows `super()` is called in some paths
|
175
195
|
*/
|
176
196
|
function isCalledInSomePath(segment) {
|
177
|
-
return segment.reachable && segInfoMap[segment.id]
|
197
|
+
return segment.reachable && segInfoMap[segment.id]?.calledInSomePaths;
|
198
|
+
}
|
199
|
+
|
200
|
+
/**
|
201
|
+
* Determines if a segment has been seen in the traversal.
|
202
|
+
* @param {CodePathSegment} segment A code path segment to check.
|
203
|
+
* @returns {boolean} `true` if the segment has been seen.
|
204
|
+
*/
|
205
|
+
function hasSegmentBeenSeen(segment) {
|
206
|
+
return !!segInfoMap[segment.id];
|
178
207
|
}
|
179
208
|
|
180
209
|
/**
|
@@ -190,10 +219,10 @@ module.exports = {
|
|
190
219
|
* If not skipped, this never becomes true after a loop.
|
191
220
|
*/
|
192
221
|
if (segment.nextSegments.length === 1 &&
|
193
|
-
segment.nextSegments[0]
|
194
|
-
) {
|
222
|
+
segment.nextSegments[0]?.isLoopedPrevSegment(segment)) {
|
195
223
|
return true;
|
196
224
|
}
|
225
|
+
|
197
226
|
return segment.reachable && segInfoMap[segment.id].calledInEveryPaths;
|
198
227
|
}
|
199
228
|
|
@@ -250,9 +279,9 @@ module.exports = {
|
|
250
279
|
}
|
251
280
|
|
252
281
|
// Reports if `super()` lacked.
|
253
|
-
const
|
254
|
-
const calledInEveryPaths =
|
255
|
-
const calledInSomePaths =
|
282
|
+
const seenSegments = codePath.returnedSegments.filter(hasSegmentBeenSeen);
|
283
|
+
const calledInEveryPaths = seenSegments.every(isCalledInEveryPath);
|
284
|
+
const calledInSomePaths = seenSegments.some(isCalledInSomePath);
|
256
285
|
|
257
286
|
if (!calledInEveryPaths) {
|
258
287
|
context.report({
|
@@ -278,18 +307,16 @@ module.exports = {
|
|
278
307
|
}
|
279
308
|
|
280
309
|
// Initialize info.
|
281
|
-
const info = segInfoMap[segment.id] =
|
282
|
-
calledInSomePaths: false,
|
283
|
-
calledInEveryPaths: false,
|
284
|
-
validNodes: []
|
285
|
-
};
|
310
|
+
const info = segInfoMap[segment.id] = new SegmentInfo();
|
286
311
|
|
287
312
|
// When there are previous segments, aggregates these.
|
288
313
|
const prevSegments = segment.prevSegments;
|
289
314
|
|
290
315
|
if (prevSegments.length > 0) {
|
291
|
-
|
292
|
-
|
316
|
+
const seenPrevSegments = prevSegments.filter(hasSegmentBeenSeen);
|
317
|
+
|
318
|
+
info.calledInSomePaths = seenPrevSegments.some(isCalledInSomePath);
|
319
|
+
info.calledInEveryPaths = seenPrevSegments.every(isCalledInEveryPath);
|
293
320
|
}
|
294
321
|
},
|
295
322
|
|
@@ -326,12 +353,12 @@ module.exports = {
|
|
326
353
|
funcInfo.codePath.traverseSegments(
|
327
354
|
{ first: toSegment, last: fromSegment },
|
328
355
|
segment => {
|
329
|
-
const info = segInfoMap[segment.id];
|
330
|
-
const
|
356
|
+
const info = segInfoMap[segment.id] ?? new SegmentInfo();
|
357
|
+
const seenPrevSegments = segment.prevSegments.filter(hasSegmentBeenSeen);
|
331
358
|
|
332
359
|
// Updates flags.
|
333
|
-
info.calledInSomePaths =
|
334
|
-
info.calledInEveryPaths =
|
360
|
+
info.calledInSomePaths = seenPrevSegments.some(isCalledInSomePath);
|
361
|
+
info.calledInEveryPaths = seenPrevSegments.every(isCalledInEveryPath);
|
335
362
|
|
336
363
|
// If flags become true anew, reports the valid nodes.
|
337
364
|
if (info.calledInSomePaths || isRealLoop) {
|
@@ -348,6 +375,9 @@ module.exports = {
|
|
348
375
|
});
|
349
376
|
}
|
350
377
|
}
|
378
|
+
|
379
|
+
// save just in case we created a new SegmentInfo object
|
380
|
+
segInfoMap[segment.id] = info;
|
351
381
|
}
|
352
382
|
);
|
353
383
|
},
|
@@ -48,9 +48,9 @@ function isFallThroughComment(comment, fallthroughCommentPattern) {
|
|
48
48
|
* @param {ASTNode} subsequentCase The case after caseWhichFallsThrough.
|
49
49
|
* @param {RuleContext} context A rule context which stores comments.
|
50
50
|
* @param {RegExp} fallthroughCommentPattern A pattern to match comment to.
|
51
|
-
* @returns {
|
51
|
+
* @returns {null | object} the comment if the case has a valid fallthrough comment, otherwise null
|
52
52
|
*/
|
53
|
-
function
|
53
|
+
function getFallthroughComment(caseWhichFallsThrough, subsequentCase, context, fallthroughCommentPattern) {
|
54
54
|
const sourceCode = context.sourceCode;
|
55
55
|
|
56
56
|
if (caseWhichFallsThrough.consequent.length === 1 && caseWhichFallsThrough.consequent[0].type === "BlockStatement") {
|
@@ -58,13 +58,17 @@ function hasFallthroughComment(caseWhichFallsThrough, subsequentCase, context, f
|
|
58
58
|
const commentInBlock = sourceCode.getCommentsBefore(trailingCloseBrace).pop();
|
59
59
|
|
60
60
|
if (commentInBlock && isFallThroughComment(commentInBlock.value, fallthroughCommentPattern)) {
|
61
|
-
return
|
61
|
+
return commentInBlock;
|
62
62
|
}
|
63
63
|
}
|
64
64
|
|
65
65
|
const comment = sourceCode.getCommentsBefore(subsequentCase).pop();
|
66
66
|
|
67
|
-
|
67
|
+
if (comment && isFallThroughComment(comment.value, fallthroughCommentPattern)) {
|
68
|
+
return comment;
|
69
|
+
}
|
70
|
+
|
71
|
+
return null;
|
68
72
|
}
|
69
73
|
|
70
74
|
/**
|
@@ -103,12 +107,17 @@ module.exports = {
|
|
103
107
|
allowEmptyCase: {
|
104
108
|
type: "boolean",
|
105
109
|
default: false
|
110
|
+
},
|
111
|
+
reportUnusedFallthroughComment: {
|
112
|
+
type: "boolean",
|
113
|
+
default: false
|
106
114
|
}
|
107
115
|
},
|
108
116
|
additionalProperties: false
|
109
117
|
}
|
110
118
|
],
|
111
119
|
messages: {
|
120
|
+
unusedFallthroughComment: "Found a comment that would permit fallthrough, but case cannot fall through.",
|
112
121
|
case: "Expected a 'break' statement before 'case'.",
|
113
122
|
default: "Expected a 'break' statement before 'default'."
|
114
123
|
}
|
@@ -120,12 +129,13 @@ module.exports = {
|
|
120
129
|
let currentCodePathSegments = new Set();
|
121
130
|
const sourceCode = context.sourceCode;
|
122
131
|
const allowEmptyCase = options.allowEmptyCase || false;
|
132
|
+
const reportUnusedFallthroughComment = options.reportUnusedFallthroughComment || false;
|
123
133
|
|
124
134
|
/*
|
125
135
|
* We need to use leading comments of the next SwitchCase node because
|
126
136
|
* trailing comments is wrong if semicolons are omitted.
|
127
137
|
*/
|
128
|
-
let
|
138
|
+
let previousCase = null;
|
129
139
|
let fallthroughCommentPattern = null;
|
130
140
|
|
131
141
|
if (options.commentPattern) {
|
@@ -168,13 +178,23 @@ module.exports = {
|
|
168
178
|
* And reports the previous fallthrough node if that does not exist.
|
169
179
|
*/
|
170
180
|
|
171
|
-
if (
|
172
|
-
context
|
173
|
-
|
174
|
-
|
175
|
-
|
181
|
+
if (previousCase && previousCase.node.parent === node.parent) {
|
182
|
+
const previousCaseFallthroughComment = getFallthroughComment(previousCase.node, node, context, fallthroughCommentPattern);
|
183
|
+
|
184
|
+
if (previousCase.isFallthrough && !(previousCaseFallthroughComment)) {
|
185
|
+
context.report({
|
186
|
+
messageId: node.test ? "case" : "default",
|
187
|
+
node
|
188
|
+
});
|
189
|
+
} else if (reportUnusedFallthroughComment && !previousCase.isSwitchExitReachable && previousCaseFallthroughComment) {
|
190
|
+
context.report({
|
191
|
+
messageId: "unusedFallthroughComment",
|
192
|
+
node: previousCaseFallthroughComment
|
193
|
+
});
|
194
|
+
}
|
195
|
+
|
176
196
|
}
|
177
|
-
|
197
|
+
previousCase = null;
|
178
198
|
},
|
179
199
|
|
180
200
|
"SwitchCase:exit"(node) {
|
@@ -185,11 +205,16 @@ module.exports = {
|
|
185
205
|
* `break`, `return`, or `throw` are unreachable.
|
186
206
|
* And allows empty cases and the last case.
|
187
207
|
*/
|
188
|
-
|
189
|
-
|
190
|
-
node.parent.cases.at(-1) !== node
|
191
|
-
|
192
|
-
|
208
|
+
const isSwitchExitReachable = isAnySegmentReachable(currentCodePathSegments);
|
209
|
+
const isFallthrough = isSwitchExitReachable && (node.consequent.length > 0 || (!allowEmptyCase && hasBlankLinesBetween(node, nextToken))) &&
|
210
|
+
node.parent.cases.at(-1) !== node;
|
211
|
+
|
212
|
+
previousCase = {
|
213
|
+
node,
|
214
|
+
isSwitchExitReachable,
|
215
|
+
isFallthrough
|
216
|
+
};
|
217
|
+
|
193
218
|
}
|
194
219
|
};
|
195
220
|
}
|
@@ -30,6 +30,29 @@ function isConstructorFunction(node) {
|
|
30
30
|
);
|
31
31
|
}
|
32
32
|
|
33
|
+
/*
|
34
|
+
* Information for each code path segment.
|
35
|
+
* - superCalled: The flag which shows `super()` called in all code paths.
|
36
|
+
* - invalidNodes: The array of invalid ThisExpression and Super nodes.
|
37
|
+
*/
|
38
|
+
/**
|
39
|
+
*
|
40
|
+
*/
|
41
|
+
class SegmentInfo {
|
42
|
+
|
43
|
+
/**
|
44
|
+
* Indicates whether `super()` is called in all code paths.
|
45
|
+
* @type {boolean}
|
46
|
+
*/
|
47
|
+
superCalled = false;
|
48
|
+
|
49
|
+
/**
|
50
|
+
* The array of invalid ThisExpression and Super nodes.
|
51
|
+
* @type {ASTNode[]}
|
52
|
+
*/
|
53
|
+
invalidNodes = [];
|
54
|
+
}
|
55
|
+
|
33
56
|
//------------------------------------------------------------------------------
|
34
57
|
// Rule Definition
|
35
58
|
//------------------------------------------------------------------------------
|
@@ -64,13 +87,7 @@ module.exports = {
|
|
64
87
|
*/
|
65
88
|
let funcInfo = null;
|
66
89
|
|
67
|
-
|
68
|
-
* Information for each code path segment.
|
69
|
-
* Each key is the id of a code path segment.
|
70
|
-
* Each value is an object:
|
71
|
-
* - superCalled: The flag which shows `super()` called in all code paths.
|
72
|
-
* - invalidNodes: The array of invalid ThisExpression and Super nodes.
|
73
|
-
*/
|
90
|
+
/** @type {Record<string, SegmentInfo>} */
|
74
91
|
let segInfoMap = Object.create(null);
|
75
92
|
|
76
93
|
/**
|
@@ -79,7 +96,7 @@ module.exports = {
|
|
79
96
|
* @returns {boolean} `true` if `super()` is called.
|
80
97
|
*/
|
81
98
|
function isCalled(segment) {
|
82
|
-
return !segment.reachable || segInfoMap[segment.id]
|
99
|
+
return !segment.reachable || segInfoMap[segment.id]?.superCalled;
|
83
100
|
}
|
84
101
|
|
85
102
|
/**
|
@@ -285,7 +302,7 @@ module.exports = {
|
|
285
302
|
funcInfo.codePath.traverseSegments(
|
286
303
|
{ first: toSegment, last: fromSegment },
|
287
304
|
(segment, controller) => {
|
288
|
-
const info = segInfoMap[segment.id];
|
305
|
+
const info = segInfoMap[segment.id] ?? new SegmentInfo();
|
289
306
|
|
290
307
|
if (info.superCalled) {
|
291
308
|
controller.skip();
|
@@ -295,6 +312,8 @@ module.exports = {
|
|
295
312
|
) {
|
296
313
|
info.superCalled = true;
|
297
314
|
}
|
315
|
+
|
316
|
+
segInfoMap[segment.id] = info;
|
298
317
|
}
|
299
318
|
);
|
300
319
|
},
|
@@ -146,7 +146,9 @@ module.exports = {
|
|
146
146
|
continue;
|
147
147
|
}
|
148
148
|
|
149
|
-
|
149
|
+
if (segmentInfoMap.has(segment)) {
|
150
|
+
uselessReturns.push(...segmentInfoMap.get(segment).uselessReturns);
|
151
|
+
}
|
150
152
|
}
|
151
153
|
|
152
154
|
return uselessReturns;
|
@@ -182,6 +184,10 @@ module.exports = {
|
|
182
184
|
|
183
185
|
const info = segmentInfoMap.get(segment);
|
184
186
|
|
187
|
+
if (!info) {
|
188
|
+
return;
|
189
|
+
}
|
190
|
+
|
185
191
|
info.uselessReturns = info.uselessReturns.filter(node => {
|
186
192
|
if (scopeInfo.traversedTryBlockStatements && scopeInfo.traversedTryBlockStatements.length > 0) {
|
187
193
|
const returnInitialRange = node.range[0];
|
@@ -275,7 +281,6 @@ module.exports = {
|
|
275
281
|
* NOTE: This event is notified for only reachable segments.
|
276
282
|
*/
|
277
283
|
onCodePathSegmentStart(segment) {
|
278
|
-
|
279
284
|
scopeInfo.currentSegments.add(segment);
|
280
285
|
|
281
286
|
const info = {
|
@@ -3,9 +3,14 @@
|
|
3
3
|
*/
|
4
4
|
"use strict";
|
5
5
|
|
6
|
+
const isCombiningCharacter = require("./is-combining-character");
|
7
|
+
const isEmojiModifier = require("./is-emoji-modifier");
|
8
|
+
const isRegionalIndicatorSymbol = require("./is-regional-indicator-symbol");
|
9
|
+
const isSurrogatePair = require("./is-surrogate-pair");
|
10
|
+
|
6
11
|
module.exports = {
|
7
|
-
isCombiningCharacter
|
8
|
-
isEmojiModifier
|
9
|
-
isRegionalIndicatorSymbol
|
10
|
-
isSurrogatePair
|
12
|
+
isCombiningCharacter,
|
13
|
+
isEmojiModifier,
|
14
|
+
isRegionalIndicatorSymbol,
|
15
|
+
isSurrogatePair
|
11
16
|
};
|
@@ -162,6 +162,7 @@ function version() {
|
|
162
162
|
//------------------------------------------------------------------------------
|
163
163
|
|
164
164
|
module.exports = {
|
165
|
+
__esModule: true, // Indicate intent for imports, remove ambiguity for Knip (see: https://github.com/eslint/eslint/pull/18005#discussion_r1484422616)
|
165
166
|
environment,
|
166
167
|
version
|
167
168
|
};
|
package/lib/source-code/index.js
CHANGED
@@ -18,8 +18,12 @@ const
|
|
18
18
|
directivesPattern
|
19
19
|
} = require("../shared/directives"),
|
20
20
|
|
21
|
-
/* eslint-disable
|
21
|
+
/* eslint-disable n/no-restricted-require -- Should eventually be moved into SourceCode. */
|
22
|
+
CodePathAnalyzer = require("../linter/code-path-analysis/code-path-analyzer"),
|
23
|
+
createEmitter = require("../linter/safe-emitter"),
|
22
24
|
ConfigCommentParser = require("../linter/config-comment-parser"),
|
25
|
+
/* eslint-enable n/no-restricted-require -- Should eventually be moved into SourceCode. */
|
26
|
+
|
23
27
|
eslintScope = require("eslint-scope");
|
24
28
|
|
25
29
|
//------------------------------------------------------------------------------
|
@@ -34,6 +38,16 @@ const
|
|
34
38
|
|
35
39
|
const commentParser = new ConfigCommentParser();
|
36
40
|
|
41
|
+
const CODE_PATH_EVENTS = [
|
42
|
+
"onCodePathStart",
|
43
|
+
"onCodePathEnd",
|
44
|
+
"onCodePathSegmentStart",
|
45
|
+
"onCodePathSegmentEnd",
|
46
|
+
"onCodePathSegmentLoop",
|
47
|
+
"onUnreachableCodePathSegmentStart",
|
48
|
+
"onUnreachableCodePathSegmentEnd"
|
49
|
+
];
|
50
|
+
|
37
51
|
/**
|
38
52
|
* Validates that the given AST has the required information.
|
39
53
|
* @param {ASTNode} ast The Program node of the AST to check.
|
@@ -300,6 +314,65 @@ function markExportedVariables(globalScope, variables) {
|
|
300
314
|
|
301
315
|
}
|
302
316
|
|
317
|
+
const STEP_KIND = {
|
318
|
+
visit: 1,
|
319
|
+
call: 2
|
320
|
+
};
|
321
|
+
|
322
|
+
/**
|
323
|
+
* A class to represent a step in the traversal process.
|
324
|
+
*/
|
325
|
+
class TraversalStep {
|
326
|
+
|
327
|
+
/**
|
328
|
+
* The type of the step.
|
329
|
+
* @type {string}
|
330
|
+
*/
|
331
|
+
type;
|
332
|
+
|
333
|
+
/**
|
334
|
+
* The kind of the step. Represents the same data as the `type` property
|
335
|
+
* but it's a number for performance.
|
336
|
+
* @type {number}
|
337
|
+
*/
|
338
|
+
kind;
|
339
|
+
|
340
|
+
/**
|
341
|
+
* The target of the step.
|
342
|
+
* @type {ASTNode|string}
|
343
|
+
*/
|
344
|
+
target;
|
345
|
+
|
346
|
+
/**
|
347
|
+
* The phase of the step.
|
348
|
+
* @type {number|undefined}
|
349
|
+
*/
|
350
|
+
phase;
|
351
|
+
|
352
|
+
/**
|
353
|
+
* The arguments of the step.
|
354
|
+
* @type {Array<any>}
|
355
|
+
*/
|
356
|
+
args;
|
357
|
+
|
358
|
+
/**
|
359
|
+
* Creates a new instance.
|
360
|
+
* @param {Object} options The options for the step.
|
361
|
+
* @param {string} options.type The type of the step.
|
362
|
+
* @param {ASTNode|string} options.target The target of the step.
|
363
|
+
* @param {number|undefined} [options.phase] The phase of the step.
|
364
|
+
* @param {Array<any>} options.args The arguments of the step.
|
365
|
+
* @returns {void}
|
366
|
+
*/
|
367
|
+
constructor({ type, target, phase, args }) {
|
368
|
+
this.type = type;
|
369
|
+
this.kind = STEP_KIND[type];
|
370
|
+
this.target = target;
|
371
|
+
this.phase = phase;
|
372
|
+
this.args = args;
|
373
|
+
}
|
374
|
+
}
|
375
|
+
|
303
376
|
//------------------------------------------------------------------------------
|
304
377
|
// Public Interface
|
305
378
|
//------------------------------------------------------------------------------
|
@@ -311,6 +384,12 @@ const caches = Symbol("caches");
|
|
311
384
|
*/
|
312
385
|
class SourceCode extends TokenStore {
|
313
386
|
|
387
|
+
/**
|
388
|
+
* The cache of steps that were taken while traversing the source code.
|
389
|
+
* @type {Array<TraversalStep>}
|
390
|
+
*/
|
391
|
+
#steps;
|
392
|
+
|
314
393
|
/**
|
315
394
|
* @param {string|Object} textOrConfig The source code text or config object.
|
316
395
|
* @param {string} textOrConfig.text The source code text.
|
@@ -972,6 +1051,91 @@ class SourceCode extends TokenStore {
|
|
972
1051
|
|
973
1052
|
}
|
974
1053
|
|
1054
|
+
/**
|
1055
|
+
* Traverse the source code and return the steps that were taken.
|
1056
|
+
* @returns {Array<TraversalStep>} The steps that were taken while traversing the source code.
|
1057
|
+
*/
|
1058
|
+
traverse() {
|
1059
|
+
|
1060
|
+
// Because the AST doesn't mutate, we can cache the steps
|
1061
|
+
if (this.#steps) {
|
1062
|
+
return this.#steps;
|
1063
|
+
}
|
1064
|
+
|
1065
|
+
const steps = this.#steps = [];
|
1066
|
+
|
1067
|
+
/*
|
1068
|
+
* This logic works for any AST, not just ESTree. Because ESLint has allowed
|
1069
|
+
* custom parsers to return any AST, we need to ensure that the traversal
|
1070
|
+
* logic works for any AST.
|
1071
|
+
*/
|
1072
|
+
const emitter = createEmitter();
|
1073
|
+
let analyzer = {
|
1074
|
+
enterNode(node) {
|
1075
|
+
steps.push(new TraversalStep({
|
1076
|
+
type: "visit",
|
1077
|
+
target: node,
|
1078
|
+
phase: 1,
|
1079
|
+
args: [node, node.parent]
|
1080
|
+
}));
|
1081
|
+
},
|
1082
|
+
leaveNode(node) {
|
1083
|
+
steps.push(new TraversalStep({
|
1084
|
+
type: "visit",
|
1085
|
+
target: node,
|
1086
|
+
phase: 2,
|
1087
|
+
args: [node, node.parent]
|
1088
|
+
}));
|
1089
|
+
},
|
1090
|
+
emitter
|
1091
|
+
};
|
1092
|
+
|
1093
|
+
/*
|
1094
|
+
* We do code path analysis for ESTree only. Code path analysis is not
|
1095
|
+
* necessary for other ASTs, and it's also not possible to do for other
|
1096
|
+
* ASTs because the necessary information is not available.
|
1097
|
+
*
|
1098
|
+
* Generally speaking, we can tell that the AST is an ESTree if it has a
|
1099
|
+
* Program node at the top level. This is not a perfect heuristic, but it
|
1100
|
+
* is good enough for now.
|
1101
|
+
*/
|
1102
|
+
const isESTree = this.ast.type === "Program";
|
1103
|
+
|
1104
|
+
if (isESTree) {
|
1105
|
+
analyzer = new CodePathAnalyzer(analyzer);
|
1106
|
+
|
1107
|
+
CODE_PATH_EVENTS.forEach(eventName => {
|
1108
|
+
emitter.on(eventName, (...args) => {
|
1109
|
+
steps.push(new TraversalStep({
|
1110
|
+
type: "call",
|
1111
|
+
target: eventName,
|
1112
|
+
args
|
1113
|
+
}));
|
1114
|
+
});
|
1115
|
+
});
|
1116
|
+
}
|
1117
|
+
|
1118
|
+
/*
|
1119
|
+
* The actual AST traversal is done by the `Traverser` class. This class
|
1120
|
+
* is responsible for walking the AST and calling the appropriate methods
|
1121
|
+
* on the `analyzer` object, which is appropriate for the given AST.
|
1122
|
+
*/
|
1123
|
+
Traverser.traverse(this.ast, {
|
1124
|
+
enter(node, parent) {
|
1125
|
+
|
1126
|
+
// save the parent node on a property for backwards compatibility
|
1127
|
+
node.parent = parent;
|
1128
|
+
|
1129
|
+
analyzer.enterNode(node);
|
1130
|
+
},
|
1131
|
+
leave(node) {
|
1132
|
+
analyzer.leaveNode(node);
|
1133
|
+
},
|
1134
|
+
visitorKeys: this.visitorKeys
|
1135
|
+
});
|
1136
|
+
|
1137
|
+
return steps;
|
1138
|
+
}
|
975
1139
|
}
|
976
1140
|
|
977
1141
|
module.exports = SourceCode;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "eslint",
|
3
|
-
"version": "9.0.0-
|
3
|
+
"version": "9.0.0-rc.0",
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
6
6
|
"bin": {
|
@@ -23,6 +23,7 @@
|
|
23
23
|
"lint:docs:rule-examples": "node Makefile.js checkRuleExamples",
|
24
24
|
"lint:fix": "node Makefile.js lint -- fix",
|
25
25
|
"lint:fix:docs:js": "node Makefile.js lintDocsJS -- fix",
|
26
|
+
"lint:unused": "knip",
|
26
27
|
"release:generate:alpha": "node Makefile.js generatePrerelease -- alpha",
|
27
28
|
"release:generate:beta": "node Makefile.js generatePrerelease -- beta",
|
28
29
|
"release:generate:latest": "node Makefile.js generateRelease",
|
@@ -48,7 +49,7 @@
|
|
48
49
|
"node tools/fetch-docs-links.js",
|
49
50
|
"git add docs/src/_data/further_reading_links.json"
|
50
51
|
],
|
51
|
-
"docs/**/*.svg": "npx svgo -r --multipass"
|
52
|
+
"docs/**/*.svg": "npx -y svgo -r --multipass"
|
52
53
|
},
|
53
54
|
"files": [
|
54
55
|
"LICENSE",
|
@@ -66,7 +67,7 @@
|
|
66
67
|
"@eslint-community/eslint-utils": "^4.2.0",
|
67
68
|
"@eslint-community/regexpp": "^4.6.1",
|
68
69
|
"@eslint/eslintrc": "^3.0.2",
|
69
|
-
"@eslint/js": "9.0.0-
|
70
|
+
"@eslint/js": "9.0.0-rc.0",
|
70
71
|
"@humanwhocodes/config-array": "^0.11.14",
|
71
72
|
"@humanwhocodes/module-importer": "^1.0.1",
|
72
73
|
"@nodelib/fs.walk": "^1.2.8",
|
@@ -75,7 +76,7 @@
|
|
75
76
|
"cross-spawn": "^7.0.2",
|
76
77
|
"debug": "^4.3.2",
|
77
78
|
"escape-string-regexp": "^4.0.0",
|
78
|
-
"eslint-scope": "^8.0.
|
79
|
+
"eslint-scope": "^8.0.1",
|
79
80
|
"eslint-visitor-keys": "^4.0.0",
|
80
81
|
"espree": "^10.0.1",
|
81
82
|
"esquery": "^1.4.2",
|
@@ -101,6 +102,8 @@
|
|
101
102
|
"devDependencies": {
|
102
103
|
"@babel/core": "^7.4.3",
|
103
104
|
"@babel/preset-env": "^7.4.3",
|
105
|
+
"@types/estree": "^1.0.5",
|
106
|
+
"@types/node": "^20.11.5",
|
104
107
|
"@wdio/browser-runner": "^8.14.6",
|
105
108
|
"@wdio/cli": "^8.14.6",
|
106
109
|
"@wdio/concise-reporter": "^8.14.0",
|
@@ -131,6 +134,7 @@
|
|
131
134
|
"got": "^11.8.3",
|
132
135
|
"gray-matter": "^4.0.3",
|
133
136
|
"js-yaml": "^4.1.0",
|
137
|
+
"knip": "^5.0.1",
|
134
138
|
"lint-staged": "^11.0.0",
|
135
139
|
"load-perf": "^0.2.0",
|
136
140
|
"markdown-it": "^12.2.0",
|
@@ -138,7 +142,6 @@
|
|
138
142
|
"markdownlint": "^0.33.0",
|
139
143
|
"markdownlint-cli": "^0.39.0",
|
140
144
|
"marked": "^4.0.8",
|
141
|
-
"memfs": "^3.0.1",
|
142
145
|
"metascraper": "^5.25.7",
|
143
146
|
"metascraper-description": "^5.25.7",
|
144
147
|
"metascraper-image": "^5.29.3",
|
@@ -157,8 +160,8 @@
|
|
157
160
|
"semver": "^7.5.3",
|
158
161
|
"shelljs": "^0.8.5",
|
159
162
|
"sinon": "^11.0.0",
|
163
|
+
"typescript": "^5.3.3",
|
160
164
|
"vite-plugin-commonjs": "^0.10.0",
|
161
|
-
"webdriverio": "^8.14.6",
|
162
165
|
"webpack": "^5.23.0",
|
163
166
|
"webpack-cli": "^4.5.0",
|
164
167
|
"yorkie": "^2.0.0"
|
@@ -1,34 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* @fileoverview XML character escaper
|
3
|
-
* @author George Chung
|
4
|
-
*/
|
5
|
-
"use strict";
|
6
|
-
|
7
|
-
//------------------------------------------------------------------------------
|
8
|
-
// Public Interface
|
9
|
-
//------------------------------------------------------------------------------
|
10
|
-
|
11
|
-
/**
|
12
|
-
* Returns the escaped value for a character
|
13
|
-
* @param {string} s string to examine
|
14
|
-
* @returns {string} severity level
|
15
|
-
* @private
|
16
|
-
*/
|
17
|
-
module.exports = function(s) {
|
18
|
-
return (`${s}`).replace(/[<>&"'\x00-\x1F\x7F\u0080-\uFFFF]/gu, c => { // eslint-disable-line no-control-regex -- Converting controls to entities
|
19
|
-
switch (c) {
|
20
|
-
case "<":
|
21
|
-
return "<";
|
22
|
-
case ">":
|
23
|
-
return ">";
|
24
|
-
case "&":
|
25
|
-
return "&";
|
26
|
-
case "\"":
|
27
|
-
return """;
|
28
|
-
case "'":
|
29
|
-
return "'";
|
30
|
-
default:
|
31
|
-
return `&#${c.charCodeAt(0)};`;
|
32
|
-
}
|
33
|
-
});
|
34
|
-
};
|
@@ -1,58 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* @fileoverview Provide the function that emits deprecation warnings.
|
3
|
-
* @author Toru Nagashima <http://github.com/mysticatea>
|
4
|
-
*/
|
5
|
-
"use strict";
|
6
|
-
|
7
|
-
//------------------------------------------------------------------------------
|
8
|
-
// Requirements
|
9
|
-
//------------------------------------------------------------------------------
|
10
|
-
|
11
|
-
const path = require("path");
|
12
|
-
|
13
|
-
//------------------------------------------------------------------------------
|
14
|
-
// Private
|
15
|
-
//------------------------------------------------------------------------------
|
16
|
-
|
17
|
-
// Definitions for deprecation warnings.
|
18
|
-
const deprecationWarningMessages = {
|
19
|
-
ESLINT_LEGACY_ECMAFEATURES:
|
20
|
-
"The 'ecmaFeatures' config file property is deprecated and has no effect."
|
21
|
-
};
|
22
|
-
|
23
|
-
const sourceFileErrorCache = new Set();
|
24
|
-
|
25
|
-
/**
|
26
|
-
* Emits a deprecation warning containing a given filepath. A new deprecation warning is emitted
|
27
|
-
* for each unique file path, but repeated invocations with the same file path have no effect.
|
28
|
-
* No warnings are emitted if the `--no-deprecation` or `--no-warnings` Node runtime flags are active.
|
29
|
-
* @param {string} source The name of the configuration source to report the warning for.
|
30
|
-
* @param {string} errorCode The warning message to show.
|
31
|
-
* @returns {void}
|
32
|
-
*/
|
33
|
-
function emitDeprecationWarning(source, errorCode) {
|
34
|
-
const cacheKey = JSON.stringify({ source, errorCode });
|
35
|
-
|
36
|
-
if (sourceFileErrorCache.has(cacheKey)) {
|
37
|
-
return;
|
38
|
-
}
|
39
|
-
|
40
|
-
sourceFileErrorCache.add(cacheKey);
|
41
|
-
|
42
|
-
const rel = path.relative(process.cwd(), source);
|
43
|
-
const message = deprecationWarningMessages[errorCode];
|
44
|
-
|
45
|
-
process.emitWarning(
|
46
|
-
`${message} (found in "${rel}")`,
|
47
|
-
"DeprecationWarning",
|
48
|
-
errorCode
|
49
|
-
);
|
50
|
-
}
|
51
|
-
|
52
|
-
//------------------------------------------------------------------------------
|
53
|
-
// Public Interface
|
54
|
-
//------------------------------------------------------------------------------
|
55
|
-
|
56
|
-
module.exports = {
|
57
|
-
emitDeprecationWarning
|
58
|
-
};
|