eslint 8.0.0-beta.2 → 8.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 +4 -4
- package/lib/cli-engine/cli-engine.js +10 -21
- package/lib/config/rule-validator.js +24 -7
- package/lib/eslint/eslint.js +2 -15
- package/lib/linter/apply-disable-directives.js +80 -11
- package/lib/linter/config-comment-parser.js +1 -1
- package/lib/linter/linter.js +27 -4
- package/lib/linter/node-event-generator.js +7 -0
- package/lib/rule-tester/rule-tester.js +5 -2
- package/lib/rules/index.js +1 -0
- package/lib/rules/keyword-spacing.js +14 -1
- package/lib/rules/no-new-func.js +34 -6
- package/lib/rules/no-unused-private-class-members.js +194 -0
- package/lib/rules/require-atomic-updates.js +21 -8
- package/lib/rules/space-before-blocks.js +14 -2
- package/lib/rules/switch-colon-spacing.js +1 -13
- package/lib/rules/utils/ast-utils.js +15 -1
- package/lib/shared/types.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
@@ -242,7 +242,7 @@ Toru Nagashima
|
|
242
242
|
</td><td align="center" valign="top" width="11%">
|
243
243
|
<a href="https://github.com/aladdin-add">
|
244
244
|
<img src="https://github.com/aladdin-add.png?s=75" width="75" height="75"><br />
|
245
|
-
|
245
|
+
唯然
|
246
246
|
</a>
|
247
247
|
</td></tr></tbody></table>
|
248
248
|
|
@@ -296,9 +296,9 @@ The following companies, organizations, and individuals support ESLint's ongoing
|
|
296
296
|
<!--sponsorsstart-->
|
297
297
|
<h3>Platinum Sponsors</h3>
|
298
298
|
<p><a href="https://automattic.com"><img src="https://images.opencollective.com/photomatt/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
|
299
|
-
<p><a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="96"></a> <a href="https://google.com/chrome"><img src="https://images.opencollective.com/chrome/dc55bd4/logo.png" alt="Chrome's Web Framework & Tools Performance Fund" height="96"></a> <a href="https://www.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> <a href="https://coinbase.com"><img src="https://avatars.githubusercontent.com/u/1885080?v=4" alt="Coinbase" height="96"></a> <a href="https://substack.com/"><img src="https://avatars.githubusercontent.com/u/53023767?v=4" alt="Substack" height="96"></a></p><h3>Silver Sponsors</h3>
|
300
|
-
<p><a href="https://
|
301
|
-
<p><a href="https://troypoint.com"><img src="https://images.opencollective.com/troypoint/080f96f/avatar.png" alt="TROYPOINT" height="32"></a> <a href="https://mobilen.nu"><img src="https://images.opencollective.com/mobilen/e19860d/logo.png" alt="Mobilen" 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="null"><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" 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://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a> <a href="https://www.practiceignition.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Practice Ignition" height="32"></a></p>
|
299
|
+
<p><a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="96"></a> <a href="https://google.com/chrome"><img src="https://images.opencollective.com/chrome/dc55bd4/logo.png" alt="Chrome's Web Framework & Tools Performance Fund" height="96"></a> <a href="https://www.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> <a href="https://coinbase.com"><img src="https://avatars.githubusercontent.com/u/1885080?v=4" alt="Coinbase" height="96"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="96"></a> <a href="https://substack.com/"><img src="https://avatars.githubusercontent.com/u/53023767?v=4" alt="Substack" height="96"></a></p><h3>Silver Sponsors</h3>
|
300
|
+
<p><a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a></p><h3>Bronze Sponsors</h3>
|
301
|
+
<p><a href="https://launchdarkly.com"><img src="https://images.opencollective.com/launchdarkly/574bb9e/logo.png" alt="launchdarkly" height="32"></a> <a href="https://troypoint.com"><img src="https://images.opencollective.com/troypoint/080f96f/avatar.png" alt="TROYPOINT" height="32"></a> <a href="https://mobilen.nu"><img src="https://images.opencollective.com/mobilen/e19860d/logo.png" alt="Mobilen" 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="null"><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" 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://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a> <a href="https://www.practiceignition.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Practice Ignition" height="32"></a></p>
|
302
302
|
<!--sponsorsend-->
|
303
303
|
|
304
304
|
## <a name="technology-sponsors"></a>Technology Sponsors
|
@@ -570,8 +570,10 @@ class CLIEngine {
|
|
570
570
|
/**
|
571
571
|
* Creates a new instance of the core CLI engine.
|
572
572
|
* @param {CLIEngineOptions} providedOptions The options for this instance.
|
573
|
+
* @param {Object} [additionalData] Additional settings that are not CLIEngineOptions.
|
574
|
+
* @param {Record<string,Plugin>|null} [additionalData.preloadedPlugins] Preloaded plugins.
|
573
575
|
*/
|
574
|
-
constructor(providedOptions) {
|
576
|
+
constructor(providedOptions, { preloadedPlugins } = {}) {
|
575
577
|
const options = Object.assign(
|
576
578
|
Object.create(null),
|
577
579
|
defaultOptions,
|
@@ -584,6 +586,13 @@ class CLIEngine {
|
|
584
586
|
}
|
585
587
|
|
586
588
|
const additionalPluginPool = new Map();
|
589
|
+
|
590
|
+
if (preloadedPlugins) {
|
591
|
+
for (const [id, plugin] of Object.entries(preloadedPlugins)) {
|
592
|
+
additionalPluginPool.set(id, plugin);
|
593
|
+
}
|
594
|
+
}
|
595
|
+
|
587
596
|
const cacheFilePath = getCacheFile(
|
588
597
|
options.cacheLocation || options.cacheFile,
|
589
598
|
options.cwd
|
@@ -698,26 +707,6 @@ class CLIEngine {
|
|
698
707
|
});
|
699
708
|
}
|
700
709
|
|
701
|
-
|
702
|
-
/**
|
703
|
-
* Add a plugin by passing its configuration
|
704
|
-
* @param {string} name Name of the plugin.
|
705
|
-
* @param {Plugin} pluginObject Plugin configuration object.
|
706
|
-
* @returns {void}
|
707
|
-
*/
|
708
|
-
addPlugin(name, pluginObject) {
|
709
|
-
const {
|
710
|
-
additionalPluginPool,
|
711
|
-
configArrayFactory,
|
712
|
-
lastConfigArrays
|
713
|
-
} = internalSlotsMap.get(this);
|
714
|
-
|
715
|
-
additionalPluginPool.set(name, pluginObject);
|
716
|
-
configArrayFactory.clearCache();
|
717
|
-
lastConfigArrays.length = 1;
|
718
|
-
lastConfigArrays[0] = configArrayFactory.getConfigArrayForFile();
|
719
|
-
}
|
720
|
-
|
721
710
|
/**
|
722
711
|
* Resolves the patterns passed into executeOnFiles() into glob-based patterns
|
723
712
|
* for easier handling.
|
@@ -35,16 +35,33 @@ function findRuleDefinition(ruleId, config) {
|
|
35
35
|
pluginName = ruleIdParts.join("/");
|
36
36
|
}
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
}
|
38
|
+
const errorMessageHeader = `Key "rules": Key "${ruleId}"`;
|
39
|
+
let errorMessage = `${errorMessageHeader}: Could not find plugin "${pluginName}".`;
|
41
40
|
|
42
|
-
if
|
43
|
-
|
44
|
-
|
41
|
+
// if the plugin exists then we need to check if the rule exists
|
42
|
+
if (config.plugins && config.plugins[pluginName]) {
|
43
|
+
|
44
|
+
const plugin = config.plugins[pluginName];
|
45
|
+
|
46
|
+
// first check for exact rule match
|
47
|
+
if (plugin.rules && plugin.rules[ruleName]) {
|
48
|
+
return config.plugins[pluginName].rules[ruleName];
|
49
|
+
}
|
45
50
|
|
46
|
-
|
51
|
+
errorMessage = `${errorMessageHeader}: Could not find "${ruleName}" in plugin "${pluginName}".`;
|
52
|
+
|
53
|
+
// otherwise, let's see if we can find the rule name elsewhere
|
54
|
+
for (const [otherPluginName, otherPlugin] of Object.entries(config.plugins)) {
|
55
|
+
if (otherPlugin.rules && otherPlugin.rules[ruleName]) {
|
56
|
+
errorMessage += ` Did you mean "${otherPluginName}/${ruleName}"?`;
|
57
|
+
break;
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
// falls through to throw error
|
62
|
+
}
|
47
63
|
|
64
|
+
throw new TypeError(errorMessage);
|
48
65
|
}
|
49
66
|
|
50
67
|
/**
|
package/lib/eslint/eslint.js
CHANGED
@@ -54,7 +54,7 @@ const { version } = require("../../package.json");
|
|
54
54
|
* @property {string} [ignorePath] The ignore file to use instead of .eslintignore.
|
55
55
|
* @property {ConfigData} [overrideConfig] Override config object, overrides all configs used with this instance
|
56
56
|
* @property {string} [overrideConfigFile] The configuration file to use.
|
57
|
-
* @property {Record<string,Plugin
|
57
|
+
* @property {Record<string,Plugin>|null} [plugins] Preloaded plugins. This is a map-like object, keys are plugin IDs and each value is implementation.
|
58
58
|
* @property {"error" | "warn" | "off"} [reportUnusedDisableDirectives] the severity to report unused eslint-disable directives.
|
59
59
|
* @property {string} [resolvePluginsRelativeTo] The folder where plugins should be resolved from, defaulting to the CWD.
|
60
60
|
* @property {string[]} [rulePaths] An array of directories to load custom rules from.
|
@@ -433,26 +433,13 @@ class ESLint {
|
|
433
433
|
*/
|
434
434
|
constructor(options = {}) {
|
435
435
|
const processedOptions = processOptions(options);
|
436
|
-
const cliEngine = new CLIEngine(processedOptions);
|
436
|
+
const cliEngine = new CLIEngine(processedOptions, { preloadedPlugins: options.plugins });
|
437
437
|
const {
|
438
|
-
additionalPluginPool,
|
439
438
|
configArrayFactory,
|
440
439
|
lastConfigArrays
|
441
440
|
} = getCLIEngineInternalSlots(cliEngine);
|
442
441
|
let updated = false;
|
443
442
|
|
444
|
-
/*
|
445
|
-
* Address `plugins` to add plugin implementations.
|
446
|
-
* Operate the `additionalPluginPool` internal slot directly to avoid
|
447
|
-
* using `addPlugin(id, plugin)` method that resets cache everytime.
|
448
|
-
*/
|
449
|
-
if (options.plugins) {
|
450
|
-
for (const [id, plugin] of Object.entries(options.plugins)) {
|
451
|
-
additionalPluginPool.set(id, plugin);
|
452
|
-
updated = true;
|
453
|
-
}
|
454
|
-
}
|
455
|
-
|
456
443
|
/*
|
457
444
|
* Address `overrideConfig` to set override config.
|
458
445
|
* Operate the `configArrayFactory` internal slot directly because this
|
@@ -46,26 +46,95 @@ function groupByParentComment(directives) {
|
|
46
46
|
* @returns {{ description, fix, position }[]} Details for later creation of output Problems.
|
47
47
|
*/
|
48
48
|
function createIndividualDirectivesRemoval(directives, commentToken) {
|
49
|
-
|
49
|
+
|
50
|
+
/*
|
51
|
+
* `commentToken.value` starts right after `//` or `/*`.
|
52
|
+
* All calculated offsets will be relative to this index.
|
53
|
+
*/
|
54
|
+
const commentValueStart = commentToken.range[0] + "//".length;
|
55
|
+
|
56
|
+
// Find where the list of rules starts. `\S+` matches with the directive name (e.g. `eslint-disable-line`)
|
57
|
+
const listStartOffset = /^\s*\S+\s+/u.exec(commentToken.value)[0].length;
|
58
|
+
|
59
|
+
/*
|
60
|
+
* Get the list text without any surrounding whitespace. In order to preserve the original
|
61
|
+
* formatting, we don't want to change that whitespace.
|
62
|
+
*
|
63
|
+
* // eslint-disable-line rule-one , rule-two , rule-three -- comment
|
64
|
+
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
65
|
+
*/
|
50
66
|
const listText = commentToken.value
|
51
|
-
.slice(
|
52
|
-
.split(/\s-{2,}\s/u)[0] // remove
|
53
|
-
.trimRight();
|
54
|
-
|
67
|
+
.slice(listStartOffset) // remove directive name and all whitespace before the list
|
68
|
+
.split(/\s-{2,}\s/u)[0] // remove `-- comment`, if it exists
|
69
|
+
.trimRight(); // remove all whitespace after the list
|
70
|
+
|
71
|
+
/*
|
72
|
+
* We can assume that `listText` contains multiple elements.
|
73
|
+
* Otherwise, this function wouldn't be called - if there is
|
74
|
+
* only one rule in the list, then the whole comment must be removed.
|
75
|
+
*/
|
55
76
|
|
56
77
|
return directives.map(directive => {
|
57
78
|
const { ruleId } = directive;
|
58
|
-
|
59
|
-
const
|
60
|
-
const
|
61
|
-
const
|
79
|
+
|
80
|
+
const regex = new RegExp(String.raw`(?:^|\s*,\s*)${escapeRegExp(ruleId)}(?:\s*,\s*|$)`, "u");
|
81
|
+
const match = regex.exec(listText);
|
82
|
+
const matchedText = match[0];
|
83
|
+
const matchStartOffset = listStartOffset + match.index;
|
84
|
+
const matchEndOffset = matchStartOffset + matchedText.length;
|
85
|
+
|
86
|
+
const firstIndexOfComma = matchedText.indexOf(",");
|
87
|
+
const lastIndexOfComma = matchedText.lastIndexOf(",");
|
88
|
+
|
89
|
+
let removalStartOffset, removalEndOffset;
|
90
|
+
|
91
|
+
if (firstIndexOfComma !== lastIndexOfComma) {
|
92
|
+
|
93
|
+
/*
|
94
|
+
* Since there are two commas, this must one of the elements in the middle of the list.
|
95
|
+
* Matched range starts where the previous rule name ends, and ends where the next rule name starts.
|
96
|
+
*
|
97
|
+
* // eslint-disable-line rule-one , rule-two , rule-three -- comment
|
98
|
+
* ^^^^^^^^^^^^^^
|
99
|
+
*
|
100
|
+
* We want to remove only the content between the two commas, and also one of the commas.
|
101
|
+
*
|
102
|
+
* // eslint-disable-line rule-one , rule-two , rule-three -- comment
|
103
|
+
* ^^^^^^^^^^^
|
104
|
+
*/
|
105
|
+
removalStartOffset = matchStartOffset + firstIndexOfComma;
|
106
|
+
removalEndOffset = matchStartOffset + lastIndexOfComma;
|
107
|
+
|
108
|
+
} else {
|
109
|
+
|
110
|
+
/*
|
111
|
+
* This is either the first element or the last element.
|
112
|
+
*
|
113
|
+
* If this is the first element, matched range starts where the first rule name starts
|
114
|
+
* and ends where the second rule name starts. This is exactly the range we want
|
115
|
+
* to remove so that the second rule name will start where the first one was starting
|
116
|
+
* and thus preserve the original formatting.
|
117
|
+
*
|
118
|
+
* // eslint-disable-line rule-one , rule-two , rule-three -- comment
|
119
|
+
* ^^^^^^^^^^^
|
120
|
+
*
|
121
|
+
* Similarly, if this is the last element, we've already matched the range we want to
|
122
|
+
* remove. The previous rule name will end where the last one was ending, relative
|
123
|
+
* to the content on the right side.
|
124
|
+
*
|
125
|
+
* // eslint-disable-line rule-one , rule-two , rule-three -- comment
|
126
|
+
* ^^^^^^^^^^^^^
|
127
|
+
*/
|
128
|
+
removalStartOffset = matchStartOffset;
|
129
|
+
removalEndOffset = matchEndOffset;
|
130
|
+
}
|
62
131
|
|
63
132
|
return {
|
64
133
|
description: `'${ruleId}'`,
|
65
134
|
fix: {
|
66
135
|
range: [
|
67
|
-
|
68
|
-
|
136
|
+
commentValueStart + removalStartOffset,
|
137
|
+
commentValueStart + removalEndOffset
|
69
138
|
],
|
70
139
|
text: ""
|
71
140
|
},
|
@@ -15,7 +15,7 @@ const levn = require("levn"),
|
|
15
15
|
Legacy: {
|
16
16
|
ConfigOps
|
17
17
|
}
|
18
|
-
} = require("@eslint/eslintrc/universal");
|
18
|
+
} = require("@eslint/eslintrc/universal");
|
19
19
|
|
20
20
|
const debug = require("debug")("eslint:config-comment-parser");
|
21
21
|
|
package/lib/linter/linter.js
CHANGED
@@ -24,7 +24,7 @@ const
|
|
24
24
|
ConfigValidator,
|
25
25
|
environments: BuiltInEnvironments
|
26
26
|
}
|
27
|
-
} = require("@eslint/eslintrc/universal"),
|
27
|
+
} = require("@eslint/eslintrc/universal"),
|
28
28
|
Traverser = require("../shared/traverser"),
|
29
29
|
{ SourceCode } = require("../source-code"),
|
30
30
|
CodePathAnalyzer = require("./code-path-analysis/code-path-analyzer"),
|
@@ -955,13 +955,31 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parser
|
|
955
955
|
|
956
956
|
const ruleListeners = createRuleListeners(rule, ruleContext);
|
957
957
|
|
958
|
+
/**
|
959
|
+
* Include `ruleId` in error logs
|
960
|
+
* @param {Function} ruleListener A rule method that listens for a node.
|
961
|
+
* @returns {Function} ruleListener wrapped in error handler
|
962
|
+
*/
|
963
|
+
function addRuleErrorHandler(ruleListener) {
|
964
|
+
return function ruleErrorHandler(...listenerArgs) {
|
965
|
+
try {
|
966
|
+
return ruleListener(...listenerArgs);
|
967
|
+
} catch (e) {
|
968
|
+
e.ruleId = ruleId;
|
969
|
+
throw e;
|
970
|
+
}
|
971
|
+
};
|
972
|
+
}
|
973
|
+
|
958
974
|
// add all the selectors from the rule as listeners
|
959
975
|
Object.keys(ruleListeners).forEach(selector => {
|
976
|
+
const ruleListener = timing.enabled
|
977
|
+
? timing.time(ruleId, ruleListeners[selector])
|
978
|
+
: ruleListeners[selector];
|
979
|
+
|
960
980
|
emitter.on(
|
961
981
|
selector,
|
962
|
-
|
963
|
-
? timing.time(ruleId, ruleListeners[selector])
|
964
|
-
: ruleListeners[selector]
|
982
|
+
addRuleErrorHandler(ruleListener)
|
965
983
|
);
|
966
984
|
});
|
967
985
|
});
|
@@ -1223,6 +1241,11 @@ class Linter {
|
|
1223
1241
|
debug("Parser Options:", parserOptions);
|
1224
1242
|
debug("Parser Path:", parserName);
|
1225
1243
|
debug("Settings:", settings);
|
1244
|
+
|
1245
|
+
if (err.ruleId) {
|
1246
|
+
err.message += `\nRule: "${err.ruleId}"`;
|
1247
|
+
}
|
1248
|
+
|
1226
1249
|
throw err;
|
1227
1250
|
}
|
1228
1251
|
|
@@ -98,6 +98,13 @@ function getPossibleTypes(parsedSelector) {
|
|
98
98
|
case "adjacent":
|
99
99
|
return getPossibleTypes(parsedSelector.right);
|
100
100
|
|
101
|
+
case "class":
|
102
|
+
if (parsedSelector.name === "function") {
|
103
|
+
return ["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"];
|
104
|
+
}
|
105
|
+
|
106
|
+
return null;
|
107
|
+
|
101
108
|
default:
|
102
109
|
return null;
|
103
110
|
|
@@ -67,6 +67,7 @@ const { SourceCode } = require("../source-code");
|
|
67
67
|
/**
|
68
68
|
* A test case that is expected to pass lint.
|
69
69
|
* @typedef {Object} ValidTestCase
|
70
|
+
* @property {string} [name] Name for the test case.
|
70
71
|
* @property {string} code Code for the test case.
|
71
72
|
* @property {any[]} [options] Options for the test case.
|
72
73
|
* @property {{ [name: string]: any }} [settings] Settings for the test case.
|
@@ -81,6 +82,7 @@ const { SourceCode } = require("../source-code");
|
|
81
82
|
/**
|
82
83
|
* A test case that is expected to fail lint.
|
83
84
|
* @typedef {Object} InvalidTestCase
|
85
|
+
* @property {string} [name] Name for the test case.
|
84
86
|
* @property {string} code Code for the test case.
|
85
87
|
* @property {number | Array<TestCaseError | string | RegExp>} errors Expected errors.
|
86
88
|
* @property {string | null} [output] The expected code after autofixes are applied. If set to `null`, the test runner will assert that no autofix is suggested.
|
@@ -124,6 +126,7 @@ let defaultConfig = { rules: {} };
|
|
124
126
|
* configuration
|
125
127
|
*/
|
126
128
|
const RuleTesterParameters = [
|
129
|
+
"name",
|
127
130
|
"code",
|
128
131
|
"filename",
|
129
132
|
"options",
|
@@ -964,7 +967,7 @@ class RuleTester {
|
|
964
967
|
RuleTester.describe("valid", () => {
|
965
968
|
test.valid.forEach(valid => {
|
966
969
|
RuleTester[valid.only ? "itOnly" : "it"](
|
967
|
-
sanitize(typeof valid === "object" ? valid.code : valid),
|
970
|
+
sanitize(typeof valid === "object" ? valid.name || valid.code : valid),
|
968
971
|
() => {
|
969
972
|
testValidTemplate(valid);
|
970
973
|
}
|
@@ -975,7 +978,7 @@ class RuleTester {
|
|
975
978
|
RuleTester.describe("invalid", () => {
|
976
979
|
test.invalid.forEach(invalid => {
|
977
980
|
RuleTester[invalid.only ? "itOnly" : "it"](
|
978
|
-
sanitize(invalid.code),
|
981
|
+
sanitize(invalid.name || invalid.code),
|
979
982
|
() => {
|
980
983
|
testInvalidTemplate(invalid);
|
981
984
|
}
|
package/lib/rules/index.js
CHANGED
@@ -221,6 +221,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
|
|
221
221
|
"no-unsafe-optional-chaining": () => require("./no-unsafe-optional-chaining"),
|
222
222
|
"no-unused-expressions": () => require("./no-unused-expressions"),
|
223
223
|
"no-unused-labels": () => require("./no-unused-labels"),
|
224
|
+
"no-unused-private-class-members": () => require("./no-unused-private-class-members"),
|
224
225
|
"no-unused-vars": () => require("./no-unused-vars"),
|
225
226
|
"no-use-before-define": () => require("./no-use-before-define"),
|
226
227
|
"no-useless-backreference": () => require("./no-useless-backreference"),
|
@@ -109,6 +109,8 @@ module.exports = {
|
|
109
109
|
create(context) {
|
110
110
|
const sourceCode = context.getSourceCode();
|
111
111
|
|
112
|
+
const tokensToIgnore = new WeakSet();
|
113
|
+
|
112
114
|
/**
|
113
115
|
* Reports a given token if there are not space(s) before the token.
|
114
116
|
* @param {Token} token A token to report.
|
@@ -121,6 +123,7 @@ module.exports = {
|
|
121
123
|
if (prevToken &&
|
122
124
|
(CHECK_TYPE.test(prevToken.type) || pattern.test(prevToken.value)) &&
|
123
125
|
!isOpenParenOfTemplate(prevToken) &&
|
126
|
+
!tokensToIgnore.has(prevToken) &&
|
124
127
|
astUtils.isTokenOnSameLine(prevToken, token) &&
|
125
128
|
!sourceCode.isSpaceBetweenTokens(prevToken, token)
|
126
129
|
) {
|
@@ -147,6 +150,7 @@ module.exports = {
|
|
147
150
|
if (prevToken &&
|
148
151
|
(CHECK_TYPE.test(prevToken.type) || pattern.test(prevToken.value)) &&
|
149
152
|
!isOpenParenOfTemplate(prevToken) &&
|
153
|
+
!tokensToIgnore.has(prevToken) &&
|
150
154
|
astUtils.isTokenOnSameLine(prevToken, token) &&
|
151
155
|
sourceCode.isSpaceBetweenTokens(prevToken, token)
|
152
156
|
) {
|
@@ -173,6 +177,7 @@ module.exports = {
|
|
173
177
|
if (nextToken &&
|
174
178
|
(CHECK_TYPE.test(nextToken.type) || pattern.test(nextToken.value)) &&
|
175
179
|
!isCloseParenOfTemplate(nextToken) &&
|
180
|
+
!tokensToIgnore.has(nextToken) &&
|
176
181
|
astUtils.isTokenOnSameLine(token, nextToken) &&
|
177
182
|
!sourceCode.isSpaceBetweenTokens(token, nextToken)
|
178
183
|
) {
|
@@ -199,6 +204,7 @@ module.exports = {
|
|
199
204
|
if (nextToken &&
|
200
205
|
(CHECK_TYPE.test(nextToken.type) || pattern.test(nextToken.value)) &&
|
201
206
|
!isCloseParenOfTemplate(nextToken) &&
|
207
|
+
!tokensToIgnore.has(nextToken) &&
|
202
208
|
astUtils.isTokenOnSameLine(token, nextToken) &&
|
203
209
|
sourceCode.isSpaceBetweenTokens(token, nextToken)
|
204
210
|
) {
|
@@ -584,7 +590,14 @@ module.exports = {
|
|
584
590
|
ImportNamespaceSpecifier: checkSpacingForImportNamespaceSpecifier,
|
585
591
|
MethodDefinition: checkSpacingForProperty,
|
586
592
|
PropertyDefinition: checkSpacingForProperty,
|
587
|
-
Property: checkSpacingForProperty
|
593
|
+
Property: checkSpacingForProperty,
|
594
|
+
|
595
|
+
// To avoid conflicts with `space-infix-ops`, e.g. `a > this.b`
|
596
|
+
"BinaryExpression[operator='>']"(node) {
|
597
|
+
const operatorToken = sourceCode.getTokenBefore(node.right, astUtils.isNotOpeningParenToken);
|
598
|
+
|
599
|
+
tokensToIgnore.add(operatorToken);
|
600
|
+
}
|
588
601
|
};
|
589
602
|
}
|
590
603
|
};
|
package/lib/rules/no-new-func.js
CHANGED
@@ -5,6 +5,18 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Requirements
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
const astUtils = require("./utils/ast-utils");
|
13
|
+
|
14
|
+
//------------------------------------------------------------------------------
|
15
|
+
// Helpers
|
16
|
+
//------------------------------------------------------------------------------
|
17
|
+
|
18
|
+
const callMethods = new Set(["apply", "bind", "call"]);
|
19
|
+
|
8
20
|
//------------------------------------------------------------------------------
|
9
21
|
// Rule Definition
|
10
22
|
//------------------------------------------------------------------------------
|
@@ -37,14 +49,30 @@ module.exports = {
|
|
37
49
|
variable.references.forEach(ref => {
|
38
50
|
const node = ref.identifier;
|
39
51
|
const { parent } = node;
|
52
|
+
let evalNode;
|
53
|
+
|
54
|
+
if (parent) {
|
55
|
+
if (node === parent.callee && (
|
56
|
+
parent.type === "NewExpression" ||
|
57
|
+
parent.type === "CallExpression"
|
58
|
+
)) {
|
59
|
+
evalNode = parent;
|
60
|
+
} else if (
|
61
|
+
parent.type === "MemberExpression" &&
|
62
|
+
node === parent.object &&
|
63
|
+
callMethods.has(astUtils.getStaticPropertyName(parent))
|
64
|
+
) {
|
65
|
+
const maybeCallee = parent.parent.type === "ChainExpression" ? parent.parent : parent;
|
66
|
+
|
67
|
+
if (maybeCallee.parent.type === "CallExpression" && maybeCallee.parent.callee === maybeCallee) {
|
68
|
+
evalNode = maybeCallee.parent;
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}
|
40
72
|
|
41
|
-
if (
|
42
|
-
parent &&
|
43
|
-
(parent.type === "NewExpression" || parent.type === "CallExpression") &&
|
44
|
-
node === parent.callee
|
45
|
-
) {
|
73
|
+
if (evalNode) {
|
46
74
|
context.report({
|
47
|
-
node:
|
75
|
+
node: evalNode,
|
48
76
|
messageId: "noFunctionConstructor"
|
49
77
|
});
|
50
78
|
}
|
@@ -0,0 +1,194 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Rule to flag declared but unused private class members
|
3
|
+
* @author Tim van der Lippe
|
4
|
+
*/
|
5
|
+
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Rule Definition
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
module.exports = {
|
13
|
+
meta: {
|
14
|
+
type: "problem",
|
15
|
+
|
16
|
+
docs: {
|
17
|
+
description: "disallow unused private class members",
|
18
|
+
recommended: false,
|
19
|
+
url: "https://eslint.org/docs/rules/no-unused-private-class-members"
|
20
|
+
},
|
21
|
+
|
22
|
+
schema: [],
|
23
|
+
|
24
|
+
messages: {
|
25
|
+
unusedPrivateClassMember: "'{{classMemberName}}' is defined but never used."
|
26
|
+
}
|
27
|
+
},
|
28
|
+
|
29
|
+
create(context) {
|
30
|
+
const trackedClasses = [];
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Check whether the current node is in a write only assignment.
|
34
|
+
* @param {ASTNode} privateIdentifierNode Node referring to a private identifier
|
35
|
+
* @returns {boolean} Whether the node is in a write only assignment
|
36
|
+
* @private
|
37
|
+
*/
|
38
|
+
function isWriteOnlyAssignment(privateIdentifierNode) {
|
39
|
+
const parentStatement = privateIdentifierNode.parent.parent;
|
40
|
+
const isAssignmentExpression = parentStatement.type === "AssignmentExpression";
|
41
|
+
|
42
|
+
if (!isAssignmentExpression &&
|
43
|
+
parentStatement.type !== "ForInStatement" &&
|
44
|
+
parentStatement.type !== "ForOfStatement" &&
|
45
|
+
parentStatement.type !== "AssignmentPattern") {
|
46
|
+
return false;
|
47
|
+
}
|
48
|
+
|
49
|
+
// It is a write-only usage, since we still allow usages on the right for reads
|
50
|
+
if (parentStatement.left !== privateIdentifierNode.parent) {
|
51
|
+
return false;
|
52
|
+
}
|
53
|
+
|
54
|
+
// For any other operator (such as '+=') we still consider it a read operation
|
55
|
+
if (isAssignmentExpression && parentStatement.operator !== "=") {
|
56
|
+
|
57
|
+
/*
|
58
|
+
* However, if the read operation is "discarded" in an empty statement, then
|
59
|
+
* we consider it write only.
|
60
|
+
*/
|
61
|
+
return parentStatement.parent.type === "ExpressionStatement";
|
62
|
+
}
|
63
|
+
|
64
|
+
return true;
|
65
|
+
}
|
66
|
+
|
67
|
+
//--------------------------------------------------------------------------
|
68
|
+
// Public
|
69
|
+
//--------------------------------------------------------------------------
|
70
|
+
|
71
|
+
return {
|
72
|
+
|
73
|
+
// Collect all declared members up front and assume they are all unused
|
74
|
+
ClassBody(classBodyNode) {
|
75
|
+
const privateMembers = new Map();
|
76
|
+
|
77
|
+
trackedClasses.unshift(privateMembers);
|
78
|
+
for (const bodyMember of classBodyNode.body) {
|
79
|
+
if (bodyMember.type === "PropertyDefinition" || bodyMember.type === "MethodDefinition") {
|
80
|
+
if (bodyMember.key.type === "PrivateIdentifier") {
|
81
|
+
privateMembers.set(bodyMember.key.name, {
|
82
|
+
declaredNode: bodyMember,
|
83
|
+
isAccessor: bodyMember.type === "MethodDefinition" &&
|
84
|
+
(bodyMember.kind === "set" || bodyMember.kind === "get")
|
85
|
+
});
|
86
|
+
}
|
87
|
+
}
|
88
|
+
}
|
89
|
+
},
|
90
|
+
|
91
|
+
/*
|
92
|
+
* Process all usages of the private identifier and remove a member from
|
93
|
+
* `declaredAndUnusedPrivateMembers` if we deem it used.
|
94
|
+
*/
|
95
|
+
PrivateIdentifier(privateIdentifierNode) {
|
96
|
+
const classBody = trackedClasses.find(classProperties => classProperties.has(privateIdentifierNode.name));
|
97
|
+
|
98
|
+
// Can't happen, as it is a parser to have a missing class body, but let's code defensively here.
|
99
|
+
if (!classBody) {
|
100
|
+
return;
|
101
|
+
}
|
102
|
+
|
103
|
+
// In case any other usage was already detected, we can short circuit the logic here.
|
104
|
+
const memberDefinition = classBody.get(privateIdentifierNode.name);
|
105
|
+
|
106
|
+
if (memberDefinition.isUsed) {
|
107
|
+
return;
|
108
|
+
}
|
109
|
+
|
110
|
+
// The definition of the class member itself
|
111
|
+
if (privateIdentifierNode.parent.type === "PropertyDefinition" ||
|
112
|
+
privateIdentifierNode.parent.type === "MethodDefinition") {
|
113
|
+
return;
|
114
|
+
}
|
115
|
+
|
116
|
+
/*
|
117
|
+
* Any usage of an accessor is considered a read, as the getter/setter can have
|
118
|
+
* side-effects in its definition.
|
119
|
+
*/
|
120
|
+
if (memberDefinition.isAccessor) {
|
121
|
+
memberDefinition.isUsed = true;
|
122
|
+
return;
|
123
|
+
}
|
124
|
+
|
125
|
+
// Any assignments to this member, except for assignments that also read
|
126
|
+
if (isWriteOnlyAssignment(privateIdentifierNode)) {
|
127
|
+
return;
|
128
|
+
}
|
129
|
+
|
130
|
+
const wrappingExpressionType = privateIdentifierNode.parent.parent.type;
|
131
|
+
const parentOfWrappingExpressionType = privateIdentifierNode.parent.parent.parent.type;
|
132
|
+
|
133
|
+
// A statement which only increments (`this.#x++;`)
|
134
|
+
if (wrappingExpressionType === "UpdateExpression" &&
|
135
|
+
parentOfWrappingExpressionType === "ExpressionStatement") {
|
136
|
+
return;
|
137
|
+
}
|
138
|
+
|
139
|
+
/*
|
140
|
+
* ({ x: this.#usedInDestructuring } = bar);
|
141
|
+
*
|
142
|
+
* But should treat the following as a read:
|
143
|
+
* ({ [this.#x]: a } = foo);
|
144
|
+
*/
|
145
|
+
if (wrappingExpressionType === "Property" &&
|
146
|
+
parentOfWrappingExpressionType === "ObjectPattern" &&
|
147
|
+
privateIdentifierNode.parent.parent.value === privateIdentifierNode.parent) {
|
148
|
+
return;
|
149
|
+
}
|
150
|
+
|
151
|
+
// [...this.#unusedInRestPattern] = bar;
|
152
|
+
if (wrappingExpressionType === "RestElement") {
|
153
|
+
return;
|
154
|
+
}
|
155
|
+
|
156
|
+
// [this.#unusedInAssignmentPattern] = bar;
|
157
|
+
if (wrappingExpressionType === "ArrayPattern") {
|
158
|
+
return;
|
159
|
+
}
|
160
|
+
|
161
|
+
/*
|
162
|
+
* We can't delete the memberDefinition, as we need to keep track of which member we are marking as used.
|
163
|
+
* In the case of nested classes, we only mark the first member we encounter as used. If you were to delete
|
164
|
+
* the member, then any subsequent usage could incorrectly mark the member of an encapsulating parent class
|
165
|
+
* as used, which is incorrect.
|
166
|
+
*/
|
167
|
+
memberDefinition.isUsed = true;
|
168
|
+
},
|
169
|
+
|
170
|
+
/*
|
171
|
+
* Post-process the class members and report any remaining members.
|
172
|
+
* Since private members can only be accessed in the current class context,
|
173
|
+
* we can safely assume that all usages are within the current class body.
|
174
|
+
*/
|
175
|
+
"ClassBody:exit"() {
|
176
|
+
const unusedPrivateMembers = trackedClasses.shift();
|
177
|
+
|
178
|
+
for (const [classMemberName, { declaredNode, isUsed }] of unusedPrivateMembers.entries()) {
|
179
|
+
if (isUsed) {
|
180
|
+
continue;
|
181
|
+
}
|
182
|
+
context.report({
|
183
|
+
node: declaredNode,
|
184
|
+
loc: declaredNode.key.loc,
|
185
|
+
messageId: "unusedPrivateClassMember",
|
186
|
+
data: {
|
187
|
+
classMemberName: `#${classMemberName}`
|
188
|
+
}
|
189
|
+
});
|
190
|
+
}
|
191
|
+
}
|
192
|
+
};
|
193
|
+
}
|
194
|
+
};
|
@@ -179,7 +179,8 @@ module.exports = {
|
|
179
179
|
schema: [],
|
180
180
|
|
181
181
|
messages: {
|
182
|
-
nonAtomicUpdate: "Possible race condition: `{{value}}` might be reassigned based on an outdated value of `{{value}}`."
|
182
|
+
nonAtomicUpdate: "Possible race condition: `{{value}}` might be reassigned based on an outdated value of `{{value}}`.",
|
183
|
+
nonAtomicObjectUpdate: "Possible race condition: `{{value}}` might be assigned based on an outdated state of `{{object}}`."
|
183
184
|
}
|
184
185
|
},
|
185
186
|
|
@@ -275,13 +276,25 @@ module.exports = {
|
|
275
276
|
const variable = reference.resolved;
|
276
277
|
|
277
278
|
if (segmentInfo.isOutdated(codePath.currentSegments, variable)) {
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
279
|
+
if (node.parent.left === reference.identifier) {
|
280
|
+
context.report({
|
281
|
+
node: node.parent,
|
282
|
+
messageId: "nonAtomicUpdate",
|
283
|
+
data: {
|
284
|
+
value: variable.name
|
285
|
+
}
|
286
|
+
});
|
287
|
+
} else {
|
288
|
+
context.report({
|
289
|
+
node: node.parent,
|
290
|
+
messageId: "nonAtomicObjectUpdate",
|
291
|
+
data: {
|
292
|
+
value: sourceCode.getText(node.parent.left),
|
293
|
+
object: variable.name
|
294
|
+
}
|
295
|
+
});
|
296
|
+
}
|
297
|
+
|
285
298
|
}
|
286
299
|
}
|
287
300
|
}
|
@@ -107,13 +107,25 @@ module.exports = {
|
|
107
107
|
* Checks whether the spacing before the given block is already controlled by another rule:
|
108
108
|
* - `arrow-spacing` checks spaces after `=>`.
|
109
109
|
* - `keyword-spacing` checks spaces after keywords in certain contexts.
|
110
|
+
* - `switch-colon-spacing` checks spaces after `:` of switch cases.
|
110
111
|
* @param {Token} precedingToken first token before the block.
|
111
112
|
* @param {ASTNode|Token} node `BlockStatement` node or `{` token of a `SwitchStatement` node.
|
112
113
|
* @returns {boolean} `true` if requiring or disallowing spaces before the given block could produce conflicts with other rules.
|
113
114
|
*/
|
114
115
|
function isConflicted(precedingToken, node) {
|
115
|
-
return
|
116
|
-
astUtils.
|
116
|
+
return (
|
117
|
+
astUtils.isArrowToken(precedingToken) ||
|
118
|
+
(
|
119
|
+
astUtils.isKeywordToken(precedingToken) &&
|
120
|
+
!isFunctionBody(node)
|
121
|
+
) ||
|
122
|
+
(
|
123
|
+
astUtils.isColonToken(precedingToken) &&
|
124
|
+
node.parent &&
|
125
|
+
node.parent.type === "SwitchCase" &&
|
126
|
+
precedingToken === astUtils.getSwitchCaseColonToken(node.parent, sourceCode)
|
127
|
+
)
|
128
|
+
);
|
117
129
|
}
|
118
130
|
|
119
131
|
/**
|
@@ -50,18 +50,6 @@ module.exports = {
|
|
50
50
|
const beforeSpacing = options.before === true; // false by default
|
51
51
|
const afterSpacing = options.after !== false; // true by default
|
52
52
|
|
53
|
-
/**
|
54
|
-
* Get the colon token of the given SwitchCase node.
|
55
|
-
* @param {ASTNode} node The SwitchCase node to get.
|
56
|
-
* @returns {Token} The colon token of the node.
|
57
|
-
*/
|
58
|
-
function getColonToken(node) {
|
59
|
-
if (node.test) {
|
60
|
-
return sourceCode.getTokenAfter(node.test, astUtils.isColonToken);
|
61
|
-
}
|
62
|
-
return sourceCode.getFirstToken(node, 1);
|
63
|
-
}
|
64
|
-
|
65
53
|
/**
|
66
54
|
* Check whether the spacing between the given 2 tokens is valid or not.
|
67
55
|
* @param {Token} left The left token to check.
|
@@ -114,7 +102,7 @@ module.exports = {
|
|
114
102
|
|
115
103
|
return {
|
116
104
|
SwitchCase(node) {
|
117
|
-
const colonToken =
|
105
|
+
const colonToken = astUtils.getSwitchCaseColonToken(node, sourceCode);
|
118
106
|
const beforeToken = sourceCode.getTokenBefore(colonToken);
|
119
107
|
const afterToken = sourceCode.getTokenAfter(colonToken);
|
120
108
|
|
@@ -756,6 +756,19 @@ function isLogicalAssignmentOperator(operator) {
|
|
756
756
|
return LOGICAL_ASSIGNMENT_OPERATORS.has(operator);
|
757
757
|
}
|
758
758
|
|
759
|
+
/**
|
760
|
+
* Get the colon token of the given SwitchCase node.
|
761
|
+
* @param {ASTNode} node The SwitchCase node to get.
|
762
|
+
* @param {SourceCode} sourceCode The source code object to get tokens.
|
763
|
+
* @returns {Token} The colon token of the node.
|
764
|
+
*/
|
765
|
+
function getSwitchCaseColonToken(node, sourceCode) {
|
766
|
+
if (node.test) {
|
767
|
+
return sourceCode.getTokenAfter(node.test, isColonToken);
|
768
|
+
}
|
769
|
+
return sourceCode.getFirstToken(node, 1);
|
770
|
+
}
|
771
|
+
|
759
772
|
//------------------------------------------------------------------------------
|
760
773
|
// Public Interface
|
761
774
|
//------------------------------------------------------------------------------
|
@@ -1872,5 +1885,6 @@ module.exports = {
|
|
1872
1885
|
isSpecificMemberAccess,
|
1873
1886
|
equalLiteralValue,
|
1874
1887
|
isSameReference,
|
1875
|
-
isLogicalAssignmentOperator
|
1888
|
+
isLogicalAssignmentOperator,
|
1889
|
+
getSwitchCaseColonToken
|
1876
1890
|
};
|
package/lib/shared/types.js
CHANGED
@@ -21,7 +21,7 @@ module.exports = {};
|
|
21
21
|
/**
|
22
22
|
* @typedef {Object} ParserOptions
|
23
23
|
* @property {EcmaFeatures} [ecmaFeatures] The optional features.
|
24
|
-
* @property {3|5|6|7|8|9|10|11|12|2015|2016|2017|2018|2019|2020|2021} [ecmaVersion] The ECMAScript version (or revision number).
|
24
|
+
* @property {3|5|6|7|8|9|10|11|12|13|2015|2016|2017|2018|2019|2020|2021|2022} [ecmaVersion] The ECMAScript version (or revision number).
|
25
25
|
* @property {"script"|"module"} [sourceType] The source code type.
|
26
26
|
*/
|
27
27
|
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "eslint",
|
3
|
-
"version": "8.
|
3
|
+
"version": "8.1.0",
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
6
6
|
"bin": {
|
@@ -47,7 +47,7 @@
|
|
47
47
|
"homepage": "https://eslint.org",
|
48
48
|
"bugs": "https://github.com/eslint/eslint/issues/",
|
49
49
|
"dependencies": {
|
50
|
-
"@eslint/eslintrc": "^1.0.
|
50
|
+
"@eslint/eslintrc": "^1.0.3",
|
51
51
|
"@humanwhocodes/config-array": "^0.6.0",
|
52
52
|
"ajv": "^6.10.0",
|
53
53
|
"chalk": "^4.0.0",
|
@@ -99,7 +99,7 @@
|
|
99
99
|
"eslint": "file:.",
|
100
100
|
"eslint-config-eslint": "file:packages/eslint-config-eslint",
|
101
101
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
102
|
-
"eslint-plugin-eslint-plugin": "^
|
102
|
+
"eslint-plugin-eslint-plugin": "^4.0.1",
|
103
103
|
"eslint-plugin-internal-rules": "file:tools/internal-rules",
|
104
104
|
"eslint-plugin-jsdoc": "^36.0.6",
|
105
105
|
"eslint-plugin-node": "^11.1.0",
|