eslint 8.4.0 → 8.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/bin/eslint.js +7 -1
- package/lib/cli-engine/cli-engine.js +4 -1
- package/lib/eslint/eslint.js +1 -0
- package/lib/linter/apply-disable-directives.js +20 -16
- package/lib/linter/linter.js +8 -6
- package/lib/rule-tester/rule-tester.js +19 -5
- package/lib/rules/camelcase.js +7 -1
- package/lib/rules/id-match.js +35 -1
- package/lib/rules/index.js +1 -0
- package/lib/rules/keyword-spacing.js +32 -0
- package/lib/rules/max-lines-per-function.js +2 -20
- package/lib/rules/no-constant-condition.js +23 -4
- package/lib/rules/no-invalid-this.js +50 -53
- package/lib/rules/no-restricted-exports.js +9 -3
- package/lib/rules/no-restricted-imports.js +24 -7
- package/lib/rules/no-restricted-modules.js +2 -1
- package/lib/rules/no-self-assign.js +1 -1
- package/lib/rules/no-useless-rename.js +8 -4
- package/lib/rules/prefer-object-has-own.js +112 -0
- package/lib/rules/prefer-regex-literals.js +217 -1
- package/lib/rules/prefer-template.js +1 -1
- package/lib/rules/quotes.js +12 -1
- package/lib/rules/utils/ast-utils.js +21 -1
- package/messages/no-config-found.js +1 -1
- package/package.json +7 -7
- package/lib/init/autoconfig.js +0 -351
- package/lib/init/config-file.js +0 -144
- package/lib/init/config-initializer.js +0 -709
- package/lib/init/config-rule.js +0 -316
- package/lib/init/npm-utils.js +0 -179
- package/lib/init/source-code-utils.js +0 -110
package/README.md
CHANGED
@@ -54,7 +54,7 @@ $ npm install eslint --save-dev
|
|
54
54
|
You should then set up a configuration file:
|
55
55
|
|
56
56
|
```sh
|
57
|
-
$
|
57
|
+
$ npm init @eslint/config
|
58
58
|
```
|
59
59
|
|
60
60
|
After that, you can run ESLint on any file or directory like this:
|
@@ -65,7 +65,7 @@ $ ./node_modules/.bin/eslint yourfile.js
|
|
65
65
|
|
66
66
|
## <a name="configuration"></a>Configuration
|
67
67
|
|
68
|
-
After running `
|
68
|
+
After running `npm init @eslint/config`, you'll have a `.eslintrc` file in your directory. In it, you'll see some rules configured like this:
|
69
69
|
|
70
70
|
```json
|
71
71
|
{
|
@@ -294,7 +294,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
|
|
294
294
|
<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>
|
295
295
|
<p><a href="https://contra.com"><img src="https://images.opencollective.com/contra1/c70f93f/logo.png" alt="Contra" height="96"></a> <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>
|
296
296
|
<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>
|
297
|
-
<p><a href="https://
|
297
|
+
<p><a href="https://launchdarkly.com"><img src="https://images.opencollective.com/launchdarkly/574bb9e/logo.png" alt="launchdarkly" 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://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.practiceignition.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Practice Ignition" height="32"></a></p>
|
298
298
|
<!--sponsorsend-->
|
299
299
|
|
300
300
|
## <a name="technology-sponsors"></a>Technology Sponsors
|
package/bin/eslint.js
CHANGED
@@ -124,7 +124,13 @@ ${message}`);
|
|
124
124
|
|
125
125
|
// Call the config initializer if `--init` is present.
|
126
126
|
if (process.argv.includes("--init")) {
|
127
|
-
|
127
|
+
|
128
|
+
// `eslint --init` has been moved to `@eslint/create-config`
|
129
|
+
console.warn("You can also run this command directly using 'npm init @eslint/config'.");
|
130
|
+
|
131
|
+
const spawn = require("cross-spawn");
|
132
|
+
|
133
|
+
spawn.sync("npm", ["init", "@eslint/config"], { encoding: "utf8", stdio: "inherit" });
|
128
134
|
return;
|
129
135
|
}
|
130
136
|
|
@@ -92,6 +92,7 @@ const validFixTypes = new Set(["directive", "problem", "suggestion", "layout"]);
|
|
92
92
|
* @property {string} filePath The path to the file that was linted.
|
93
93
|
* @property {LintMessage[]} messages All of the messages for the result.
|
94
94
|
* @property {number} errorCount Number of errors for the result.
|
95
|
+
* @property {number} fatalErrorCount Number of fatal errors for the result.
|
95
96
|
* @property {number} warningCount Number of warnings for the result.
|
96
97
|
* @property {number} fixableErrorCount Number of fixable errors for the result.
|
97
98
|
* @property {number} fixableWarningCount Number of fixable warnings for the result.
|
@@ -104,6 +105,7 @@ const validFixTypes = new Set(["directive", "problem", "suggestion", "layout"]);
|
|
104
105
|
* @typedef {Object} LintReport
|
105
106
|
* @property {LintResult[]} results All of the result.
|
106
107
|
* @property {number} errorCount Number of errors for the result.
|
108
|
+
* @property {number} fatalErrorCount Number of fatal errors for the result.
|
107
109
|
* @property {number} warningCount Number of warnings for the result.
|
108
110
|
* @property {number} fixableErrorCount Number of fixable errors for the result.
|
109
111
|
* @property {number} fixableWarningCount Number of fixable warnings for the result.
|
@@ -308,6 +310,7 @@ function createIgnoreResult(filePath, baseDir) {
|
|
308
310
|
}
|
309
311
|
],
|
310
312
|
errorCount: 0,
|
313
|
+
fatalErrorCount: 0,
|
311
314
|
warningCount: 1,
|
312
315
|
fixableErrorCount: 0,
|
313
316
|
fixableWarningCount: 0
|
@@ -408,7 +411,7 @@ function isErrorMessage(message) {
|
|
408
411
|
* a directory or looks like a directory (ends in `path.sep`), in which case the file
|
409
412
|
* name will be the `cacheFile/.cache_hashOfCWD`
|
410
413
|
*
|
411
|
-
* if cacheFile points to a file or looks like a file then
|
414
|
+
* if cacheFile points to a file or looks like a file then it will just use that file
|
412
415
|
* @param {string} cacheFile The name of file to be used to store the cache
|
413
416
|
* @param {string} cwd Current working directory
|
414
417
|
* @returns {string} the resolved path to the cache file
|
package/lib/eslint/eslint.js
CHANGED
@@ -79,6 +79,7 @@ const { version } = require("../../package.json");
|
|
79
79
|
* @property {string} filePath The path to the file that was linted.
|
80
80
|
* @property {LintMessage[]} messages All of the messages for the result.
|
81
81
|
* @property {number} errorCount Number of errors for the result.
|
82
|
+
* @property {number} fatalErrorCount Number of fatal errors for the result.
|
82
83
|
* @property {number} warningCount Number of warnings for the result.
|
83
84
|
* @property {number} fixableErrorCount Number of fixable errors for the result.
|
84
85
|
* @property {number} fixableWarningCount Number of fixable warnings for the result.
|
@@ -43,7 +43,7 @@ function groupByParentComment(directives) {
|
|
43
43
|
* Creates removal details for a set of directives within the same comment.
|
44
44
|
* @param {Directive[]} directives Unused directives to be removed.
|
45
45
|
* @param {Token} commentToken The backing Comment token.
|
46
|
-
* @returns {{ description, fix,
|
46
|
+
* @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
|
47
47
|
*/
|
48
48
|
function createIndividualDirectivesRemoval(directives, commentToken) {
|
49
49
|
|
@@ -138,7 +138,7 @@ function createIndividualDirectivesRemoval(directives, commentToken) {
|
|
138
138
|
],
|
139
139
|
text: ""
|
140
140
|
},
|
141
|
-
|
141
|
+
unprocessedDirective: directive.unprocessedDirective
|
142
142
|
};
|
143
143
|
});
|
144
144
|
}
|
@@ -147,7 +147,7 @@ function createIndividualDirectivesRemoval(directives, commentToken) {
|
|
147
147
|
* Creates a description of deleting an entire unused disable comment.
|
148
148
|
* @param {Directive[]} directives Unused directives to be removed.
|
149
149
|
* @param {Token} commentToken The backing Comment token.
|
150
|
-
* @returns {{ description, fix,
|
150
|
+
* @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output Problem.
|
151
151
|
*/
|
152
152
|
function createCommentRemoval(directives, commentToken) {
|
153
153
|
const { range } = commentToken;
|
@@ -161,14 +161,14 @@ function createCommentRemoval(directives, commentToken) {
|
|
161
161
|
range,
|
162
162
|
text: " "
|
163
163
|
},
|
164
|
-
|
164
|
+
unprocessedDirective: directives[0].unprocessedDirective
|
165
165
|
};
|
166
166
|
}
|
167
167
|
|
168
168
|
/**
|
169
169
|
* Parses details from directives to create output Problems.
|
170
170
|
* @param {Directive[]} allDirectives Unused directives to be removed.
|
171
|
-
* @returns {{ description, fix,
|
171
|
+
* @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
|
172
172
|
*/
|
173
173
|
function processUnusedDisableDirectives(allDirectives) {
|
174
174
|
const directiveGroups = groupByParentComment(allDirectives);
|
@@ -261,17 +261,21 @@ function applyDirectives(options) {
|
|
261
261
|
const processed = processUnusedDisableDirectives(unusedDisableDirectivesToReport);
|
262
262
|
|
263
263
|
const unusedDisableDirectives = processed
|
264
|
-
.map(({ description, fix,
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
:
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
264
|
+
.map(({ description, fix, unprocessedDirective }) => {
|
265
|
+
const { parentComment, type, line, column } = unprocessedDirective;
|
266
|
+
|
267
|
+
return {
|
268
|
+
ruleId: null,
|
269
|
+
message: description
|
270
|
+
? `Unused eslint-disable directive (no problems were reported from ${description}).`
|
271
|
+
: "Unused eslint-disable directive (no problems were reported).",
|
272
|
+
line: type === "disable-next-line" ? parentComment.commentToken.loc.start.line : line,
|
273
|
+
column: type === "disable-next-line" ? parentComment.commentToken.loc.start.column + 1 : column,
|
274
|
+
severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2,
|
275
|
+
nodeType: null,
|
276
|
+
...options.disableFixes ? {} : { fix }
|
277
|
+
};
|
278
|
+
});
|
275
279
|
|
276
280
|
return { problems, unusedDisableDirectives };
|
277
281
|
}
|
package/lib/linter/linter.js
CHANGED
@@ -305,7 +305,11 @@ function createDisableDirectives(options) {
|
|
305
305
|
|
306
306
|
// push to directives, if the rule is defined(including null, e.g. /*eslint enable*/)
|
307
307
|
if (ruleId === null || !!ruleMapper(ruleId)) {
|
308
|
-
|
308
|
+
if (type === "disable-next-line") {
|
309
|
+
result.directives.push({ parentComment, type, line: commentToken.loc.end.line, column: commentToken.loc.end.column + 1, ruleId });
|
310
|
+
} else {
|
311
|
+
result.directives.push({ parentComment, type, line: commentToken.loc.start.line, column: commentToken.loc.start.column + 1, ruleId });
|
312
|
+
}
|
309
313
|
} else {
|
310
314
|
result.directiveProblems.push(createLintingProblem({ ruleId, loc: commentToken.loc }));
|
311
315
|
}
|
@@ -326,14 +330,13 @@ function stripDirectiveComment(value) {
|
|
326
330
|
* Parses comments in file to extract file-specific config of rules, globals
|
327
331
|
* and environments and merges them with global config; also code blocks
|
328
332
|
* where reporting is disabled or enabled and merges them with reporting config.
|
329
|
-
* @param {string} filename The file being checked.
|
330
333
|
* @param {ASTNode} ast The top node of the AST.
|
331
334
|
* @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
|
332
335
|
* @param {string|null} warnInlineConfig If a string then it should warn directive comments as disabled. The string value is the config name what the setting came from.
|
333
336
|
* @returns {{configuredRules: Object, enabledGlobals: {value:string,comment:Token}[], exportedVariables: Object, problems: Problem[], disableDirectives: DisableDirective[]}}
|
334
337
|
* A collection of the directive comments that were found, along with any problems that occurred when parsing
|
335
338
|
*/
|
336
|
-
function getDirectiveComments(
|
339
|
+
function getDirectiveComments(ast, ruleMapper, warnInlineConfig) {
|
337
340
|
const configuredRules = {};
|
338
341
|
const enabledGlobals = Object.create(null);
|
339
342
|
const exportedVariables = {};
|
@@ -369,7 +372,7 @@ function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
|
|
369
372
|
return;
|
370
373
|
}
|
371
374
|
|
372
|
-
if (
|
375
|
+
if (directiveText === "eslint-disable-line" && comment.loc.start.line !== comment.loc.end.line) {
|
373
376
|
const message = `${directiveText} comment should not span multiple lines.`;
|
374
377
|
|
375
378
|
problems.push(createLintingProblem({
|
@@ -1332,7 +1335,7 @@ class Linter {
|
|
1332
1335
|
|
1333
1336
|
const sourceCode = slots.lastSourceCode;
|
1334
1337
|
const commentDirectives = options.allowInlineConfig
|
1335
|
-
? getDirectiveComments(
|
1338
|
+
? getDirectiveComments(sourceCode.ast, ruleId => getRule(slots, ruleId), options.warnInlineConfig)
|
1336
1339
|
: { configuredRules: {}, enabledGlobals: {}, exportedVariables: {}, problems: [], disableDirectives: [] };
|
1337
1340
|
|
1338
1341
|
// augment global scope with declared global variables
|
@@ -1593,7 +1596,6 @@ class Linter {
|
|
1593
1596
|
const sourceCode = slots.lastSourceCode;
|
1594
1597
|
const commentDirectives = options.allowInlineConfig
|
1595
1598
|
? getDirectiveComments(
|
1596
|
-
options.filename,
|
1597
1599
|
sourceCode.ast,
|
1598
1600
|
ruleId => getRuleFromConfig(ruleId, config),
|
1599
1601
|
options.warnInlineConfig
|
@@ -216,6 +216,9 @@ function freezeDeeply(x) {
|
|
216
216
|
* @returns {string} The sanitized text.
|
217
217
|
*/
|
218
218
|
function sanitize(text) {
|
219
|
+
if (typeof text !== "string") {
|
220
|
+
return "";
|
221
|
+
}
|
219
222
|
return text.replace(
|
220
223
|
/[\u0000-\u0009\u000b-\u001a]/gu, // eslint-disable-line no-control-regex -- Escaping controls
|
221
224
|
c => `\\u${c.codePointAt(0).toString(16).padStart(4, "0")}`
|
@@ -691,6 +694,13 @@ class RuleTester {
|
|
691
694
|
* @private
|
692
695
|
*/
|
693
696
|
function testValidTemplate(item) {
|
697
|
+
const code = typeof item === "object" ? item.code : item;
|
698
|
+
|
699
|
+
assert.ok(typeof code === "string", "Test case must specify a string value for 'code'");
|
700
|
+
if (item.name) {
|
701
|
+
assert.ok(typeof item.name === "string", "Optional test case property 'name' must be a string");
|
702
|
+
}
|
703
|
+
|
694
704
|
const result = runRuleForItem(item);
|
695
705
|
const messages = result.messages;
|
696
706
|
|
@@ -731,6 +741,10 @@ class RuleTester {
|
|
731
741
|
* @private
|
732
742
|
*/
|
733
743
|
function testInvalidTemplate(item) {
|
744
|
+
assert.ok(typeof item.code === "string", "Test case must specify a string value for 'code'");
|
745
|
+
if (item.name) {
|
746
|
+
assert.ok(typeof item.name === "string", "Optional test case property 'name' must be a string");
|
747
|
+
}
|
734
748
|
assert.ok(item.errors || item.errors === 0,
|
735
749
|
`Did not specify errors for an invalid test of ${ruleName}`);
|
736
750
|
|
@@ -963,10 +977,10 @@ class RuleTester {
|
|
963
977
|
* This creates a mocha test suite and pipes all supplied info through
|
964
978
|
* one of the templates above.
|
965
979
|
*/
|
966
|
-
|
967
|
-
|
980
|
+
this.constructor.describe(ruleName, () => {
|
981
|
+
this.constructor.describe("valid", () => {
|
968
982
|
test.valid.forEach(valid => {
|
969
|
-
|
983
|
+
this.constructor[valid.only ? "itOnly" : "it"](
|
970
984
|
sanitize(typeof valid === "object" ? valid.name || valid.code : valid),
|
971
985
|
() => {
|
972
986
|
testValidTemplate(valid);
|
@@ -975,9 +989,9 @@ class RuleTester {
|
|
975
989
|
});
|
976
990
|
});
|
977
991
|
|
978
|
-
|
992
|
+
this.constructor.describe("invalid", () => {
|
979
993
|
test.invalid.forEach(invalid => {
|
980
|
-
|
994
|
+
this.constructor[invalid.only ? "itOnly" : "it"](
|
981
995
|
sanitize(invalid.name || invalid.code),
|
982
996
|
() => {
|
983
997
|
testInvalidTemplate(invalid);
|
package/lib/rules/camelcase.js
CHANGED
@@ -5,6 +5,12 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Requirements
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
const astUtils = require("./utils/ast-utils");
|
13
|
+
|
8
14
|
//------------------------------------------------------------------------------
|
9
15
|
// Rule Definition
|
10
16
|
//------------------------------------------------------------------------------
|
@@ -165,7 +171,7 @@ module.exports = {
|
|
165
171
|
case "ImportSpecifier":
|
166
172
|
return (
|
167
173
|
parent.local === node &&
|
168
|
-
parent.imported
|
174
|
+
astUtils.getModuleExportName(parent.imported) === localName
|
169
175
|
);
|
170
176
|
|
171
177
|
default:
|
package/lib/rules/id-match.js
CHANGED
@@ -67,6 +67,8 @@ module.exports = {
|
|
67
67
|
onlyDeclarations = !!options.onlyDeclarations,
|
68
68
|
ignoreDestructuring = !!options.ignoreDestructuring;
|
69
69
|
|
70
|
+
let globalScope;
|
71
|
+
|
70
72
|
//--------------------------------------------------------------------------
|
71
73
|
// Helpers
|
72
74
|
//--------------------------------------------------------------------------
|
@@ -77,6 +79,19 @@ module.exports = {
|
|
77
79
|
const DECLARATION_TYPES = new Set(["FunctionDeclaration", "VariableDeclarator"]);
|
78
80
|
const IMPORT_TYPES = new Set(["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"]);
|
79
81
|
|
82
|
+
/**
|
83
|
+
* Checks whether the given node represents a reference to a global variable that is not declared in the source code.
|
84
|
+
* These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
|
85
|
+
* @param {ASTNode} node `Identifier` node to check.
|
86
|
+
* @returns {boolean} `true` if the node is a reference to a global variable.
|
87
|
+
*/
|
88
|
+
function isReferenceToGlobalVariable(node) {
|
89
|
+
const variable = globalScope.set.get(node.name);
|
90
|
+
|
91
|
+
return variable && variable.defs.length === 0 &&
|
92
|
+
variable.references.some(ref => ref.identifier === node);
|
93
|
+
}
|
94
|
+
|
80
95
|
/**
|
81
96
|
* Checks if a string matches the provided pattern
|
82
97
|
* @param {string} name The string to check.
|
@@ -155,11 +170,19 @@ module.exports = {
|
|
155
170
|
|
156
171
|
return {
|
157
172
|
|
173
|
+
Program() {
|
174
|
+
globalScope = context.getScope();
|
175
|
+
},
|
176
|
+
|
158
177
|
Identifier(node) {
|
159
178
|
const name = node.name,
|
160
179
|
parent = node.parent,
|
161
180
|
effectiveParent = (parent.type === "MemberExpression") ? parent.parent : parent;
|
162
181
|
|
182
|
+
if (isReferenceToGlobalVariable(node)) {
|
183
|
+
return;
|
184
|
+
}
|
185
|
+
|
163
186
|
if (parent.type === "MemberExpression") {
|
164
187
|
|
165
188
|
if (!checkProperties) {
|
@@ -188,6 +211,17 @@ module.exports = {
|
|
188
211
|
}
|
189
212
|
}
|
190
213
|
|
214
|
+
// For https://github.com/eslint/eslint/issues/15123
|
215
|
+
} else if (
|
216
|
+
parent.type === "Property" &&
|
217
|
+
parent.parent.type === "ObjectExpression" &&
|
218
|
+
parent.key === node &&
|
219
|
+
!parent.computed
|
220
|
+
) {
|
221
|
+
if (checkProperties && isInvalid(name)) {
|
222
|
+
report(node);
|
223
|
+
}
|
224
|
+
|
191
225
|
/*
|
192
226
|
* Properties have their own rules, and
|
193
227
|
* AssignmentPattern nodes can be treated like Properties:
|
@@ -216,7 +250,7 @@ module.exports = {
|
|
216
250
|
}
|
217
251
|
|
218
252
|
// never check properties or always ignore destructuring
|
219
|
-
if (!checkProperties || (ignoreDestructuring && isInsideObjectPattern(node))) {
|
253
|
+
if ((!checkProperties && !parent.computed) || (ignoreDestructuring && isInsideObjectPattern(node))) {
|
220
254
|
return;
|
221
255
|
}
|
222
256
|
|
package/lib/rules/index.js
CHANGED
@@ -255,6 +255,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
|
|
255
255
|
"prefer-exponentiation-operator": () => require("./prefer-exponentiation-operator"),
|
256
256
|
"prefer-named-capture-group": () => require("./prefer-named-capture-group"),
|
257
257
|
"prefer-numeric-literals": () => require("./prefer-numeric-literals"),
|
258
|
+
"prefer-object-has-own": () => require("./prefer-object-has-own"),
|
258
259
|
"prefer-object-spread": () => require("./prefer-object-spread"),
|
259
260
|
"prefer-promise-reject-errors": () => require("./prefer-promise-reject-errors"),
|
260
261
|
"prefer-reflect": () => require("./prefer-reflect"),
|
@@ -469,6 +469,7 @@ module.exports = {
|
|
469
469
|
const asToken = sourceCode.getTokenBefore(node.exported);
|
470
470
|
|
471
471
|
checkSpacingBefore(asToken, PREV_TOKEN_M);
|
472
|
+
checkSpacingAfter(asToken, NEXT_TOKEN_M);
|
472
473
|
}
|
473
474
|
|
474
475
|
if (node.source) {
|
@@ -479,6 +480,35 @@ module.exports = {
|
|
479
480
|
}
|
480
481
|
}
|
481
482
|
|
483
|
+
/**
|
484
|
+
* Reports `as` keyword of a given node if usage of spacing around this
|
485
|
+
* keyword is invalid.
|
486
|
+
* @param {ASTNode} node An `ImportSpecifier` node to check.
|
487
|
+
* @returns {void}
|
488
|
+
*/
|
489
|
+
function checkSpacingForImportSpecifier(node) {
|
490
|
+
if (node.imported.range[0] !== node.local.range[0]) {
|
491
|
+
const asToken = sourceCode.getTokenBefore(node.local);
|
492
|
+
|
493
|
+
checkSpacingBefore(asToken, PREV_TOKEN_M);
|
494
|
+
}
|
495
|
+
}
|
496
|
+
|
497
|
+
/**
|
498
|
+
* Reports `as` keyword of a given node if usage of spacing around this
|
499
|
+
* keyword is invalid.
|
500
|
+
* @param {ASTNode} node An `ExportSpecifier` node to check.
|
501
|
+
* @returns {void}
|
502
|
+
*/
|
503
|
+
function checkSpacingForExportSpecifier(node) {
|
504
|
+
if (node.local.range[0] !== node.exported.range[0]) {
|
505
|
+
const asToken = sourceCode.getTokenBefore(node.exported);
|
506
|
+
|
507
|
+
checkSpacingBefore(asToken, PREV_TOKEN_M);
|
508
|
+
checkSpacingAfter(asToken, NEXT_TOKEN_M);
|
509
|
+
}
|
510
|
+
}
|
511
|
+
|
482
512
|
/**
|
483
513
|
* Reports `as` keyword of a given node if usage of spacing around this
|
484
514
|
* keyword is invalid.
|
@@ -588,6 +618,8 @@ module.exports = {
|
|
588
618
|
YieldExpression: checkSpacingBeforeFirstToken,
|
589
619
|
|
590
620
|
// Others
|
621
|
+
ImportSpecifier: checkSpacingForImportSpecifier,
|
622
|
+
ExportSpecifier: checkSpacingForExportSpecifier,
|
591
623
|
ImportNamespaceSpecifier: checkSpacingForImportNamespaceSpecifier,
|
592
624
|
MethodDefinition: checkSpacingForProperty,
|
593
625
|
PropertyDefinition: checkSpacingForProperty,
|
@@ -80,7 +80,7 @@ module.exports = {
|
|
80
80
|
OPTIONS_OR_INTEGER_SCHEMA
|
81
81
|
],
|
82
82
|
messages: {
|
83
|
-
exceed: "{{name}} has
|
83
|
+
exceed: "{{name}} has too many lines ({{lineCount}}). Maximum allowed is {{maxLines}}."
|
84
84
|
}
|
85
85
|
},
|
86
86
|
|
@@ -170,26 +170,18 @@ module.exports = {
|
|
170
170
|
return;
|
171
171
|
}
|
172
172
|
let lineCount = 0;
|
173
|
-
let comments = 0;
|
174
|
-
let blankLines = 0;
|
175
173
|
|
176
174
|
for (let i = node.loc.start.line - 1; i < node.loc.end.line; ++i) {
|
177
175
|
const line = lines[i];
|
178
176
|
|
179
177
|
if (skipComments) {
|
180
178
|
if (commentLineNumbers.has(i + 1) && isFullLineComment(line, i + 1, commentLineNumbers.get(i + 1))) {
|
181
|
-
if (lineCount <= maxLines) {
|
182
|
-
comments++;
|
183
|
-
}
|
184
179
|
continue;
|
185
180
|
}
|
186
181
|
}
|
187
182
|
|
188
183
|
if (skipBlankLines) {
|
189
184
|
if (line.match(/^\s*$/u)) {
|
190
|
-
if (lineCount <= maxLines) {
|
191
|
-
blankLines++;
|
192
|
-
}
|
193
185
|
continue;
|
194
186
|
}
|
195
187
|
}
|
@@ -199,21 +191,11 @@ module.exports = {
|
|
199
191
|
|
200
192
|
if (lineCount > maxLines) {
|
201
193
|
const name = upperCaseFirst(astUtils.getFunctionNameWithKind(funcNode));
|
202
|
-
const linesExceed = lineCount - maxLines;
|
203
|
-
|
204
|
-
const loc = {
|
205
|
-
start: {
|
206
|
-
line: node.loc.start.line + maxLines + (comments + blankLines),
|
207
|
-
column: 0
|
208
|
-
},
|
209
|
-
end: node.loc.end
|
210
|
-
};
|
211
194
|
|
212
195
|
context.report({
|
213
196
|
node,
|
214
|
-
loc,
|
215
197
|
messageId: "exceed",
|
216
|
-
data: { name,
|
198
|
+
data: { name, lineCount, maxLines }
|
217
199
|
});
|
218
200
|
}
|
219
201
|
}
|
@@ -124,7 +124,8 @@ module.exports = {
|
|
124
124
|
* Checks if a node has a constant truthiness value.
|
125
125
|
* @param {ASTNode} node The AST node to check.
|
126
126
|
* @param {boolean} inBooleanPosition `false` if checking branch of a condition.
|
127
|
-
* `true` in all other cases
|
127
|
+
* `true` in all other cases. When `false`, checks if -- for both string and
|
128
|
+
* number -- if coerced to that type, the value will be constant.
|
128
129
|
* @returns {Bool} true when node's truthiness is constant
|
129
130
|
* @private
|
130
131
|
*/
|
@@ -138,15 +139,31 @@ module.exports = {
|
|
138
139
|
case "Literal":
|
139
140
|
case "ArrowFunctionExpression":
|
140
141
|
case "FunctionExpression":
|
141
|
-
|
142
|
+
return true;
|
142
143
|
case "ClassExpression":
|
144
|
+
case "ObjectExpression":
|
145
|
+
|
146
|
+
/**
|
147
|
+
* In theory objects like:
|
148
|
+
*
|
149
|
+
* `{toString: () => a}`
|
150
|
+
* `{valueOf: () => a}`
|
151
|
+
*
|
152
|
+
* Or a classes like:
|
153
|
+
*
|
154
|
+
* `class { static toString() { return a } }`
|
155
|
+
* `class { static valueOf() { return a } }`
|
156
|
+
*
|
157
|
+
* Are not constant verifiably when `inBooleanPosition` is
|
158
|
+
* false, but it's an edge case we've opted not to handle.
|
159
|
+
*/
|
143
160
|
return true;
|
144
161
|
case "TemplateLiteral":
|
145
162
|
return (inBooleanPosition && node.quasis.some(quasi => quasi.value.cooked.length)) ||
|
146
|
-
node.expressions.every(exp => isConstant(exp,
|
163
|
+
node.expressions.every(exp => isConstant(exp, false));
|
147
164
|
|
148
165
|
case "ArrayExpression": {
|
149
|
-
if (
|
166
|
+
if (!inBooleanPosition) {
|
150
167
|
return node.elements.every(element => isConstant(element, false));
|
151
168
|
}
|
152
169
|
return true;
|
@@ -196,6 +213,8 @@ module.exports = {
|
|
196
213
|
|
197
214
|
case "SequenceExpression":
|
198
215
|
return isConstant(node.expressions[node.expressions.length - 1], inBooleanPosition);
|
216
|
+
case "SpreadElement":
|
217
|
+
return isConstant(node.argument, inBooleanPosition);
|
199
218
|
|
200
219
|
// no default
|
201
220
|
}
|