eslint 6.0.1 → 6.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/CHANGELOG.md +29 -0
- package/README.md +3 -3
- package/bin/eslint.js +3 -0
- package/lib/cli-engine/cli-engine.js +5 -2
- package/lib/cli-engine/config-array-factory.js +1 -1
- package/lib/cli-engine/file-enumerator.js +5 -13
- package/lib/init/config-initializer.js +19 -9
- package/lib/linter/linter.js +1 -1
- package/lib/rule-tester/rule-tester.js +1 -1
- package/lib/rules/arrow-body-style.js +2 -2
- package/lib/rules/dot-location.js +21 -17
- package/lib/rules/no-extra-parens.js +241 -17
- package/lib/rules/no-param-reassign.js +12 -1
- package/lib/rules/prefer-const.js +9 -3
- package/lib/rules/utils/ast-utils.js +26 -1
- package/lib/{cli-engine → shared}/naming.js +0 -0
- package/package.json +18 -17
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,32 @@
|
|
1
|
+
v6.1.0 - July 20, 2019
|
2
|
+
|
3
|
+
* [`8f86cca`](https://github.com/eslint/eslint/commit/8f86ccaa89daf10123370868c5dcb48c1fcbef7d) Upgrade: eslint-scope@^5.0.0 (#12011) (Kevin Partington)
|
4
|
+
* [`d08683e`](https://github.com/eslint/eslint/commit/d08683e3c807f92daf266894093c70f8d5ac6afa) Fix: glob processing (fixes #11940) (#11986) (Toru Nagashima)
|
5
|
+
* [`bfcf8b2`](https://github.com/eslint/eslint/commit/bfcf8b21011466b570b536ca31ec10fd228b3dca) Fix: dot-location errors with parenthesized objects (fixes #11868) (#11933) (Milos Djermanovic)
|
6
|
+
* [`79e8d09`](https://github.com/eslint/eslint/commit/79e8d099bbbebfa4d804484eeeeea9c074ede870) Fix: add parens for sequence expr in arrow-body-style (fixes #11917) (#11918) (Pig Fang)
|
7
|
+
* [`105c098`](https://github.com/eslint/eslint/commit/105c098f3cece8b83ab8d1566b8ea41dd94a60b9) Docs: update docs for object-curly-spacing (fixes #11634) (#12009) (Chiawen Chen)
|
8
|
+
* [`c90a12c`](https://github.com/eslint/eslint/commit/c90a12c283698befcafd2c86f8bd8942428fe80b) Chore: update release script for new website repo (#12006) (Kai Cataldo)
|
9
|
+
* [`e2c08a9`](https://github.com/eslint/eslint/commit/e2c08a9c8d86238955ecc8fd5a626584ee91eba5) Sponsors: Sync README with website (ESLint Jenkins)
|
10
|
+
* [`b974fcb`](https://github.com/eslint/eslint/commit/b974fcbd3321ab382a914520018d4c051b2e5c62) Update: Check computed property keys in no-extra-parens (#11952) (Milos Djermanovic)
|
11
|
+
* [`222d27c`](https://github.com/eslint/eslint/commit/222d27c32a6d6d8828233b3b99e93ecefa94c603) Update: Add for-in and for-of checks for props in no-param-reassign (#11941) (Milos Djermanovic)
|
12
|
+
* [`e4c450f`](https://github.com/eslint/eslint/commit/e4c450febc9bd77b33f6473667afa9f955be6b71) Fix: no-extra-parens autofix with `in` in a for-loop init (fixes #11706) (#11848) (Milos Djermanovic)
|
13
|
+
* [`2dafe2d`](https://github.com/eslint/eslint/commit/2dafe2d288d1e0d353bb938d12a5da888091cfdb) Fix: prefer-const produces invalid autofix (fixes #11699) (#11827) (Milos Djermanovic)
|
14
|
+
* [`cb475fd`](https://github.com/eslint/eslint/commit/cb475fd8c0bbfcb00340459966b6780f39ea87a7) Fix: Cache file error handling on read-only file system. (fixes #11945) (#11946) (Cuki)
|
15
|
+
* [`89412c3`](https://github.com/eslint/eslint/commit/89412c3cbc52e556dba590fa94e10bf40faf1fdf) Docs: Fixed a typo (fixes #11999) (#12000) (Eddie Olson)
|
16
|
+
* [`6669f78`](https://github.com/eslint/eslint/commit/6669f78a3dd305aef6191e7eea24fae2ae4fd2e8) Fix: --init with Vue.js failed (fixes #11970) (#11985) (Toru Nagashima)
|
17
|
+
* [`93633c2`](https://github.com/eslint/eslint/commit/93633c2b3716b17816bcb3dc221c49b75db41317) Upgrade: Upgrade lodash dependency (fixes #11992) (#11994) (Cyd La Luz)
|
18
|
+
* [`776dae7`](https://github.com/eslint/eslint/commit/776dae71f2f5c7b5f0650ea3c277eca26e324e41) Docs: fix wrong Node.js version in getting started (#11993) (Toru Nagashima)
|
19
|
+
* [`4448261`](https://github.com/eslint/eslint/commit/4448261f5d217d8a06eb0ef898401928b54a34e3) Docs: some typos and optimization points (#11960) (Jason Lee)
|
20
|
+
* [`2a10856`](https://github.com/eslint/eslint/commit/2a10856d1ed5880a09a5ba452bd80d49c1be4e6c) Chore: Add temporary test files to .gitignore (#11978) (Milos Djermanovic)
|
21
|
+
* [`d83b233`](https://github.com/eslint/eslint/commit/d83b23382de3b80056a7e6330ed5846316c94147) Chore: update path for release bundles (#11977) (Kai Cataldo)
|
22
|
+
* [`1fb3620`](https://github.com/eslint/eslint/commit/1fb362093a65b99456a11029967d9ee0c31fd697) Fix: creating of enabledGlobals object without prototype (fixes #11929) (#11935) (finico)
|
23
|
+
* [`c2f2db9`](https://github.com/eslint/eslint/commit/c2f2db97c6d6a415b78ee7b3e8924853d465e757) Docs: Replace global true and false with writable and readonly in rules (#11956) (Milos Djermanovic)
|
24
|
+
* [`19335b8`](https://github.com/eslint/eslint/commit/19335b8f47029b2f742d5507ba39484eaf68d07b) Fix: actual messageId and expected messageId are switched in rule tester (#11928) (Milos Djermanovic)
|
25
|
+
* [`8b216e0`](https://github.com/eslint/eslint/commit/8b216e04fb0dd0a1a4d3730ebe4b24780020b09c) Docs: Fix incorrect example comments for unicode-bom rule (fixes #11937) (#11938) (Brandon Yeager)
|
26
|
+
* [`cc3885b`](https://github.com/eslint/eslint/commit/cc3885b028e29ebc575c900f43af81cb0dabffb6) Chore: add v8-compile-cache to speed up instantiation time (#11921) (薛定谔的猫)
|
27
|
+
* [`d8f2688`](https://github.com/eslint/eslint/commit/d8f26886f19a17f2e1cdcb91e2db84fc7ba3fdfb) Upgrade: deps (#11904) (薛定谔的猫)
|
28
|
+
* [`e5f1ccc`](https://github.com/eslint/eslint/commit/e5f1ccc9e2d07ad0acf149027ffc382021d54da1) Docs: add 'stricter rule config validating' in migrating docs (#11905) (薛定谔的猫)
|
29
|
+
|
1
30
|
v6.0.1 - June 24, 2019
|
2
31
|
|
3
32
|
* [`b5bde06`](https://github.com/eslint/eslint/commit/b5bde0669bd6a7a6b8e38cdf204d8d4b932cea63) Fix: --rulesdir option didn't work (fixes #11888) (#11890) (Toru Nagashima)
|
package/README.md
CHANGED
@@ -258,9 +258,9 @@ The following companies, organizations, and individuals support ESLint's ongoing
|
|
258
258
|
<!-- NOTE: This section is autogenerated. Do not manually edit.-->
|
259
259
|
<!--sponsorsstart-->
|
260
260
|
<h3>Gold Sponsors</h3>
|
261
|
-
<p><a href="https://www.shopify.com"><img src="https://images.opencollective.com/shopify/logo.png" alt="Shopify" height="96"></a> <a href="http://salesforce.com"><img src="https://images.opencollective.com/salesforce/logo.png" alt="Salesforce" height="96"></a> <a href="https://badoo.com/team?utm_source=eslint"><img src="https://images.opencollective.com/badoo/logo.png" alt="Badoo" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/logo.png" alt="Airbnb" height="96"></a> <a href="https://code.facebook.com/projects/"><img src="https://images.opencollective.com/fbopensource/logo.png" alt="Facebook Open Source" height="96"></a></p><h3>Silver Sponsors</h3>
|
262
|
-
<p><a href="https://www.ampproject.org/"><img src="https://images.opencollective.com/amp/logo.png" alt="AMP Project" height="64"></a></p><h3>Bronze Sponsors</h3>
|
263
|
-
<p><a href="https://clay.global"><img src="https://images.opencollective.com/clayglobal/logo.png" alt="clay" height="32"></a> <a href="https://discordapp.com"><img src="https://images.opencollective.com/discordapp/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://tekhattan.com"><img src="https://images.opencollective.com/tekhattan/logo.png" alt="TekHattan" height="32"></a> <a href="https://www.marfeel.com/"><img src="https://images.opencollective.com/marfeel/logo.png" alt="Marfeel" height="32"></a> <a href="http://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/logo.png" alt="Fire Stick Tricks" height="32"></a> <a href="https://jsheroes.io/"><img src="https://images.opencollective.com/jsheroes1/logo.png" alt="JSHeroes " height="32"></a> <a href="https://faithlife.com/ref/about"><img src="https://images.opencollective.com/faithlife/logo.png" alt="Faithlife" height="32"></a></p>
|
261
|
+
<p><a href="https://www.shopify.com"><img src="https://images.opencollective.com/shopify/eeb91aa/logo.png" alt="Shopify" height="96"></a> <a href="http://salesforce.com"><img src="https://images.opencollective.com/salesforce/853ecef/logo.png" alt="Salesforce" height="96"></a> <a href="https://badoo.com/team?utm_source=eslint"><img src="https://images.opencollective.com/badoo/2826a3b/logo.png" alt="Badoo" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/001a341/logo.png" alt="Airbnb" height="96"></a> <a href="https://code.facebook.com/projects/"><img src="https://images.opencollective.com/fbopensource/fbb8a5b/logo.png" alt="Facebook Open Source" height="96"></a></p><h3>Silver Sponsors</h3>
|
262
|
+
<p><a href="https://www.ampproject.org/"><img src="https://images.opencollective.com/amp/c8a3b25/logo.png" alt="AMP Project" height="64"></a></p><h3>Bronze Sponsors</h3>
|
263
|
+
<p><a href="https://clay.global"><img src="https://images.opencollective.com/clayglobal/2468f34/logo.png" alt="clay" height="32"></a> <a href="https://discordapp.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://tekhattan.com"><img src="https://images.opencollective.com/tekhattan/bc73c28/logo.png" alt="TekHattan" height="32"></a> <a href="https://www.marfeel.com/"><img src="https://images.opencollective.com/marfeel/4b88e30/logo.png" alt="Marfeel" height="32"></a> <a href="http://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://jsheroes.io/"><img src="https://images.opencollective.com/jsheroes1/9fedf0b/logo.png" alt="JSHeroes " height="32"></a> <a href="https://faithlife.com/ref/about"><img src="https://images.opencollective.com/faithlife/534b832/logo.png" alt="Faithlife" height="32"></a></p>
|
264
264
|
<!--sponsorsend-->
|
265
265
|
|
266
266
|
## <a name="technology-sponsors"></a>Technology Sponsors
|
package/bin/eslint.js
CHANGED
@@ -9,6 +9,9 @@
|
|
9
9
|
|
10
10
|
"use strict";
|
11
11
|
|
12
|
+
// to use V8's code cache to speed up instantiation time
|
13
|
+
require("v8-compile-cache");
|
14
|
+
|
12
15
|
//------------------------------------------------------------------------------
|
13
16
|
// Helpers
|
14
17
|
//------------------------------------------------------------------------------
|
@@ -20,6 +20,7 @@ const path = require("path");
|
|
20
20
|
const defaultOptions = require("../../conf/default-cli-options");
|
21
21
|
const pkg = require("../../package.json");
|
22
22
|
const ConfigOps = require("../shared/config-ops");
|
23
|
+
const naming = require("../shared/naming");
|
23
24
|
const ModuleResolver = require("../shared/relative-module-resolver");
|
24
25
|
const { Linter } = require("../linter");
|
25
26
|
const builtInRules = require("../rules");
|
@@ -29,7 +30,6 @@ const { FileEnumerator } = require("./file-enumerator");
|
|
29
30
|
const hash = require("./hash");
|
30
31
|
const { IgnoredPaths } = require("./ignored-paths");
|
31
32
|
const LintResultCache = require("./lint-result-cache");
|
32
|
-
const naming = require("./naming");
|
33
33
|
|
34
34
|
const debug = require("debug")("eslint:cli-engine");
|
35
35
|
const validFixTypes = new Set(["problem", "suggestion", "layout"]);
|
@@ -734,7 +734,10 @@ class CLIEngine {
|
|
734
734
|
try {
|
735
735
|
fs.unlinkSync(cacheFilePath);
|
736
736
|
} catch (error) {
|
737
|
-
|
737
|
+
const errorCode = error && error.code;
|
738
|
+
|
739
|
+
// Ignore errors when no such file exists or file system is read only (and cache file does not exist)
|
740
|
+
if (errorCode !== "ENOENT" && !(errorCode === "EROFS" && !fs.existsSync(cacheFilePath))) {
|
738
741
|
throw error;
|
739
742
|
}
|
740
743
|
}
|
@@ -38,9 +38,9 @@ const path = require("path");
|
|
38
38
|
const importFresh = require("import-fresh");
|
39
39
|
const stripComments = require("strip-json-comments");
|
40
40
|
const { validateConfigSchema } = require("../shared/config-validator");
|
41
|
+
const naming = require("../shared/naming");
|
41
42
|
const ModuleResolver = require("../shared/relative-module-resolver");
|
42
43
|
const { ConfigArray, ConfigDependency, OverrideTester } = require("./config-array");
|
43
|
-
const naming = require("./naming");
|
44
44
|
const debug = require("debug")("eslint:config-array-factory");
|
45
45
|
|
46
46
|
//------------------------------------------------------------------------------
|
@@ -292,26 +292,18 @@ class FileEnumerator {
|
|
292
292
|
_iterateFiles(pattern) {
|
293
293
|
const { cwd, globInputPaths } = internalSlotsMap.get(this);
|
294
294
|
const absolutePath = path.resolve(cwd, pattern);
|
295
|
-
|
296
|
-
if (globInputPaths && isGlobPattern(pattern)) {
|
297
|
-
return this._iterateFilesWithGlob(
|
298
|
-
absolutePath,
|
299
|
-
dotfilesPattern.test(pattern)
|
300
|
-
);
|
301
|
-
}
|
302
|
-
|
295
|
+
const isDot = dotfilesPattern.test(pattern);
|
303
296
|
const stat = statSafeSync(absolutePath);
|
304
297
|
|
305
298
|
if (stat && stat.isDirectory()) {
|
306
|
-
return this._iterateFilesWithDirectory(
|
307
|
-
absolutePath,
|
308
|
-
dotfilesPattern.test(pattern)
|
309
|
-
);
|
299
|
+
return this._iterateFilesWithDirectory(absolutePath, isDot);
|
310
300
|
}
|
311
|
-
|
312
301
|
if (stat && stat.isFile()) {
|
313
302
|
return this._iterateFilesWithFile(absolutePath);
|
314
303
|
}
|
304
|
+
if (globInputPaths && isGlobPattern(pattern)) {
|
305
|
+
return this._iterateFilesWithGlob(absolutePath, isDot);
|
306
|
+
}
|
315
307
|
|
316
308
|
return [];
|
317
309
|
}
|
@@ -18,6 +18,7 @@ const util = require("util"),
|
|
18
18
|
recConfig = require("../../conf/eslint-recommended"),
|
19
19
|
ConfigOps = require("../shared/config-ops"),
|
20
20
|
log = require("../shared/logging"),
|
21
|
+
naming = require("../shared/naming"),
|
21
22
|
ModuleResolver = require("../shared/relative-module-resolver"),
|
22
23
|
autoconfig = require("./autoconfig.js"),
|
23
24
|
ConfigFile = require("./config-file"),
|
@@ -97,17 +98,26 @@ function getModulesList(config, installESLint) {
|
|
97
98
|
// Create a list of modules which should be installed based on config
|
98
99
|
if (config.plugins) {
|
99
100
|
for (const plugin of config.plugins) {
|
100
|
-
|
101
|
+
const moduleName = naming.normalizePackageName(plugin, "eslint-plugin");
|
102
|
+
|
103
|
+
modules[moduleName] = "latest";
|
101
104
|
}
|
102
105
|
}
|
103
|
-
if (config.extends
|
104
|
-
const
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
106
|
+
if (config.extends) {
|
107
|
+
const extendList = Array.isArray(config.extends) ? config.extends : [config.extends];
|
108
|
+
|
109
|
+
for (const extend of extendList) {
|
110
|
+
if (extend.startsWith("eslint:") || extend.startsWith("plugin:")) {
|
111
|
+
continue;
|
112
|
+
}
|
113
|
+
const moduleName = naming.normalizePackageName(extend, "eslint-config");
|
114
|
+
|
115
|
+
modules[moduleName] = "latest";
|
116
|
+
Object.assign(
|
117
|
+
modules,
|
118
|
+
getPeerDependencies(`${moduleName}@latest`)
|
119
|
+
);
|
120
|
+
}
|
111
121
|
}
|
112
122
|
|
113
123
|
if (installESLint === false) {
|
package/lib/linter/linter.js
CHANGED
@@ -262,7 +262,7 @@ function createDisableDirectives(options) {
|
|
262
262
|
*/
|
263
263
|
function getDirectiveComments(filename, ast, ruleMapper) {
|
264
264
|
const configuredRules = {};
|
265
|
-
const enabledGlobals =
|
265
|
+
const enabledGlobals = Object.create(null);
|
266
266
|
const exportedVariables = {};
|
267
267
|
const problems = [];
|
268
268
|
const disableDirectives = [];
|
@@ -549,8 +549,8 @@ class RuleTester {
|
|
549
549
|
assert(false, `Invalid messageId '${error.messageId}'. Expected one of ${friendlyIDList}.`);
|
550
550
|
}
|
551
551
|
assert.strictEqual(
|
552
|
-
error.messageId,
|
553
552
|
message.messageId,
|
553
|
+
error.messageId,
|
554
554
|
`messageId '${message.messageId}' does not match expected messageId '${error.messageId}'.`
|
555
555
|
);
|
556
556
|
if (hasOwnProperty(error, "data")) {
|
@@ -175,10 +175,10 @@ module.exports = {
|
|
175
175
|
}
|
176
176
|
|
177
177
|
/*
|
178
|
-
* If the first token of the reutrn value is `{
|
178
|
+
* If the first token of the reutrn value is `{` or the return value is a sequence expression,
|
179
179
|
* enclose the return value by parentheses to avoid syntax error.
|
180
180
|
*/
|
181
|
-
if (astUtils.isOpeningBraceToken(firstValueToken)) {
|
181
|
+
if (astUtils.isOpeningBraceToken(firstValueToken) || blockBody[0].argument.type === "SequenceExpression") {
|
182
182
|
fixes.push(
|
183
183
|
fixer.insertTextBefore(firstValueToken, "("),
|
184
184
|
fixer.insertTextAfter(lastValueToken, ")")
|
@@ -54,29 +54,31 @@ module.exports = {
|
|
54
54
|
*/
|
55
55
|
function checkDotLocation(obj, prop, node) {
|
56
56
|
const dot = sourceCode.getTokenBefore(prop);
|
57
|
-
|
57
|
+
|
58
|
+
// `obj` expression can be parenthesized, but those paren tokens are not a part of the `obj` node.
|
59
|
+
const tokenBeforeDot = sourceCode.getTokenBefore(dot);
|
60
|
+
|
61
|
+
const textBeforeDot = sourceCode.getText().slice(tokenBeforeDot.range[1], dot.range[0]);
|
58
62
|
const textAfterDot = sourceCode.getText().slice(dot.range[1], prop.range[0]);
|
59
63
|
|
60
|
-
if (
|
61
|
-
if (
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
context.report({
|
66
|
-
node,
|
67
|
-
loc: dot.loc.start,
|
68
|
-
messageId: "expectedDotAfterObject",
|
69
|
-
fix: fixer => fixer.replaceTextRange([obj.range[1], prop.range[0]], `${neededTextAfterObj}.${textBeforeDot}${textAfterDot}`)
|
70
|
-
});
|
71
|
-
}
|
72
|
-
} else if (!astUtils.isTokenOnSameLine(dot, prop)) {
|
64
|
+
if (onObject) {
|
65
|
+
if (!astUtils.isTokenOnSameLine(tokenBeforeDot, dot)) {
|
66
|
+
const neededTextAfterToken = astUtils.isDecimalIntegerNumericToken(tokenBeforeDot) ? " " : "";
|
67
|
+
|
73
68
|
context.report({
|
74
69
|
node,
|
75
70
|
loc: dot.loc.start,
|
76
|
-
messageId: "
|
77
|
-
fix: fixer => fixer.replaceTextRange([
|
71
|
+
messageId: "expectedDotAfterObject",
|
72
|
+
fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], prop.range[0]], `${neededTextAfterToken}.${textBeforeDot}${textAfterDot}`)
|
78
73
|
});
|
79
74
|
}
|
75
|
+
} else if (!astUtils.isTokenOnSameLine(dot, prop)) {
|
76
|
+
context.report({
|
77
|
+
node,
|
78
|
+
loc: dot.loc.start,
|
79
|
+
messageId: "expectedDotBeforeProperty",
|
80
|
+
fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], prop.range[0]], `${textBeforeDot}${textAfterDot}.`)
|
81
|
+
});
|
80
82
|
}
|
81
83
|
}
|
82
84
|
|
@@ -86,7 +88,9 @@ module.exports = {
|
|
86
88
|
* @returns {void}
|
87
89
|
*/
|
88
90
|
function checkNode(node) {
|
89
|
-
|
91
|
+
if (!node.computed) {
|
92
|
+
checkDotLocation(node.object, node.property, node);
|
93
|
+
}
|
90
94
|
}
|
91
95
|
|
92
96
|
return {
|
@@ -81,6 +81,8 @@ module.exports = {
|
|
81
81
|
const PRECEDENCE_OF_ASSIGNMENT_EXPR = precedence({ type: "AssignmentExpression" });
|
82
82
|
const PRECEDENCE_OF_UPDATE_EXPR = precedence({ type: "UpdateExpression" });
|
83
83
|
|
84
|
+
let reportsBuffer;
|
85
|
+
|
84
86
|
/**
|
85
87
|
* Determines if this rule should be enforced for a node given the current configuration.
|
86
88
|
* @param {ASTNode} node - The node to be checked.
|
@@ -316,19 +318,33 @@ module.exports = {
|
|
316
318
|
}
|
317
319
|
}
|
318
320
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
321
|
+
/**
|
322
|
+
* Finishes reporting
|
323
|
+
* @returns {void}
|
324
|
+
* @private
|
325
|
+
*/
|
326
|
+
function finishReport() {
|
327
|
+
context.report({
|
328
|
+
node,
|
329
|
+
loc: leftParenToken.loc.start,
|
330
|
+
messageId: "unexpected",
|
331
|
+
fix(fixer) {
|
332
|
+
const parenthesizedSource = sourceCode.text.slice(leftParenToken.range[1], rightParenToken.range[0]);
|
333
|
+
|
334
|
+
return fixer.replaceTextRange([
|
335
|
+
leftParenToken.range[0],
|
336
|
+
rightParenToken.range[1]
|
337
|
+
], (requiresLeadingSpace(node) ? " " : "") + parenthesizedSource + (requiresTrailingSpace(node) ? " " : ""));
|
338
|
+
}
|
339
|
+
});
|
340
|
+
}
|
325
341
|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
342
|
+
if (reportsBuffer) {
|
343
|
+
reportsBuffer.reports.push({ node, finishReport });
|
344
|
+
return;
|
345
|
+
}
|
346
|
+
|
347
|
+
finishReport();
|
332
348
|
}
|
333
349
|
|
334
350
|
/**
|
@@ -498,6 +514,126 @@ module.exports = {
|
|
498
514
|
}
|
499
515
|
}
|
500
516
|
|
517
|
+
/**
|
518
|
+
* Finds the path from the given node to the specified ancestor.
|
519
|
+
* @param {ASTNode} node First node in the path.
|
520
|
+
* @param {ASTNode} ancestor Last node in the path.
|
521
|
+
* @returns {ASTNode[]} Path, including both nodes.
|
522
|
+
* @throws {Error} If the given node does not have the specified ancestor.
|
523
|
+
*/
|
524
|
+
function pathToAncestor(node, ancestor) {
|
525
|
+
const path = [node];
|
526
|
+
let currentNode = node;
|
527
|
+
|
528
|
+
while (currentNode !== ancestor) {
|
529
|
+
|
530
|
+
currentNode = currentNode.parent;
|
531
|
+
|
532
|
+
/* istanbul ignore if */
|
533
|
+
if (currentNode === null) {
|
534
|
+
throw new Error("Nodes are not in the ancestor-descendant relationship.");
|
535
|
+
}
|
536
|
+
|
537
|
+
path.push(currentNode);
|
538
|
+
}
|
539
|
+
|
540
|
+
return path;
|
541
|
+
}
|
542
|
+
|
543
|
+
/**
|
544
|
+
* Finds the path from the given node to the specified descendant.
|
545
|
+
* @param {ASTNode} node First node in the path.
|
546
|
+
* @param {ASTNode} descendant Last node in the path.
|
547
|
+
* @returns {ASTNode[]} Path, including both nodes.
|
548
|
+
* @throws {Error} If the given node does not have the specified descendant.
|
549
|
+
*/
|
550
|
+
function pathToDescendant(node, descendant) {
|
551
|
+
return pathToAncestor(descendant, node).reverse();
|
552
|
+
}
|
553
|
+
|
554
|
+
/**
|
555
|
+
* Checks whether the syntax of the given ancestor of an 'in' expression inside a for-loop initializer
|
556
|
+
* is preventing the 'in' keyword from being interpreted as a part of an ill-formed for-in loop.
|
557
|
+
*
|
558
|
+
* @param {ASTNode} node Ancestor of an 'in' expression.
|
559
|
+
* @param {ASTNode} child Child of the node, ancestor of the same 'in' expression or the 'in' expression itself.
|
560
|
+
* @returns {boolean} True if the keyword 'in' would be interpreted as the 'in' operator, without any parenthesis.
|
561
|
+
*/
|
562
|
+
function isSafelyEnclosingInExpression(node, child) {
|
563
|
+
switch (node.type) {
|
564
|
+
case "ArrayExpression":
|
565
|
+
case "ArrayPattern":
|
566
|
+
case "BlockStatement":
|
567
|
+
case "ObjectExpression":
|
568
|
+
case "ObjectPattern":
|
569
|
+
case "TemplateLiteral":
|
570
|
+
return true;
|
571
|
+
case "ArrowFunctionExpression":
|
572
|
+
case "FunctionExpression":
|
573
|
+
return node.params.includes(child);
|
574
|
+
case "CallExpression":
|
575
|
+
case "NewExpression":
|
576
|
+
return node.arguments.includes(child);
|
577
|
+
case "MemberExpression":
|
578
|
+
return node.computed && node.property === child;
|
579
|
+
case "ConditionalExpression":
|
580
|
+
return node.consequent === child;
|
581
|
+
default:
|
582
|
+
return false;
|
583
|
+
}
|
584
|
+
}
|
585
|
+
|
586
|
+
/**
|
587
|
+
* Starts a new reports buffering. Warnings will be stored in a buffer instead of being reported immediately.
|
588
|
+
* An additional logic that requires multiple nodes (e.g. a whole subtree) may dismiss some of the stored warnings.
|
589
|
+
*
|
590
|
+
* @returns {void}
|
591
|
+
*/
|
592
|
+
function startNewReportsBuffering() {
|
593
|
+
reportsBuffer = {
|
594
|
+
upper: reportsBuffer,
|
595
|
+
inExpressionNodes: [],
|
596
|
+
reports: []
|
597
|
+
};
|
598
|
+
}
|
599
|
+
|
600
|
+
/**
|
601
|
+
* Ends the current reports buffering.
|
602
|
+
* @returns {void}
|
603
|
+
*/
|
604
|
+
function endCurrentReportsBuffering() {
|
605
|
+
const { upper, inExpressionNodes, reports } = reportsBuffer;
|
606
|
+
|
607
|
+
if (upper) {
|
608
|
+
upper.inExpressionNodes.push(...inExpressionNodes);
|
609
|
+
upper.reports.push(...reports);
|
610
|
+
} else {
|
611
|
+
|
612
|
+
// flush remaining reports
|
613
|
+
reports.forEach(({ finishReport }) => finishReport());
|
614
|
+
}
|
615
|
+
|
616
|
+
reportsBuffer = upper;
|
617
|
+
}
|
618
|
+
|
619
|
+
/**
|
620
|
+
* Checks whether the given node is in the current reports buffer.
|
621
|
+
* @param {ASTNode} node Node to check.
|
622
|
+
* @returns {boolean} True if the node is in the current buffer, false otherwise.
|
623
|
+
*/
|
624
|
+
function isInCurrentReportsBuffer(node) {
|
625
|
+
return reportsBuffer.reports.some(r => r.node === node);
|
626
|
+
}
|
627
|
+
|
628
|
+
/**
|
629
|
+
* Removes the given node from the current reports buffer.
|
630
|
+
* @param {ASTNode} node Node to remove.
|
631
|
+
* @returns {void}
|
632
|
+
*/
|
633
|
+
function removeFromCurrentReportsBuffer(node) {
|
634
|
+
reportsBuffer.reports = reportsBuffer.reports.filter(r => r.node !== node);
|
635
|
+
}
|
636
|
+
|
501
637
|
return {
|
502
638
|
ArrayExpression(node) {
|
503
639
|
node.elements
|
@@ -540,7 +676,14 @@ module.exports = {
|
|
540
676
|
}
|
541
677
|
},
|
542
678
|
|
543
|
-
BinaryExpression
|
679
|
+
BinaryExpression(node) {
|
680
|
+
if (reportsBuffer && node.operator === "in") {
|
681
|
+
reportsBuffer.inExpressionNodes.push(node);
|
682
|
+
}
|
683
|
+
|
684
|
+
checkBinaryLogical(node);
|
685
|
+
},
|
686
|
+
|
544
687
|
CallExpression: checkCallNew,
|
545
688
|
|
546
689
|
ConditionalExpression(node) {
|
@@ -602,10 +745,6 @@ module.exports = {
|
|
602
745
|
},
|
603
746
|
|
604
747
|
ForStatement(node) {
|
605
|
-
if (node.init && hasExcessParens(node.init)) {
|
606
|
-
report(node.init);
|
607
|
-
}
|
608
|
-
|
609
748
|
if (node.test && hasExcessParens(node.test) && !isCondAssignException(node)) {
|
610
749
|
report(node.test);
|
611
750
|
}
|
@@ -613,6 +752,81 @@ module.exports = {
|
|
613
752
|
if (node.update && hasExcessParens(node.update)) {
|
614
753
|
report(node.update);
|
615
754
|
}
|
755
|
+
|
756
|
+
if (node.init) {
|
757
|
+
startNewReportsBuffering();
|
758
|
+
|
759
|
+
if (hasExcessParens(node.init)) {
|
760
|
+
report(node.init);
|
761
|
+
}
|
762
|
+
}
|
763
|
+
},
|
764
|
+
|
765
|
+
"ForStatement > *.init:exit"(node) {
|
766
|
+
|
767
|
+
/*
|
768
|
+
* Removing parentheses around `in` expressions might change semantics and cause errors.
|
769
|
+
*
|
770
|
+
* For example, this valid for loop:
|
771
|
+
* for (let a = (b in c); ;);
|
772
|
+
* after removing parentheses would be treated as an invalid for-in loop:
|
773
|
+
* for (let a = b in c; ;);
|
774
|
+
*/
|
775
|
+
|
776
|
+
if (reportsBuffer.reports.length) {
|
777
|
+
reportsBuffer.inExpressionNodes.forEach(inExpressionNode => {
|
778
|
+
const path = pathToDescendant(node, inExpressionNode);
|
779
|
+
let nodeToExclude;
|
780
|
+
|
781
|
+
for (let i = 0; i < path.length; i++) {
|
782
|
+
const pathNode = path[i];
|
783
|
+
|
784
|
+
if (i < path.length - 1) {
|
785
|
+
const nextPathNode = path[i + 1];
|
786
|
+
|
787
|
+
if (isSafelyEnclosingInExpression(pathNode, nextPathNode)) {
|
788
|
+
|
789
|
+
// The 'in' expression in safely enclosed by the syntax of its ancestor nodes (e.g. by '{}' or '[]').
|
790
|
+
return;
|
791
|
+
}
|
792
|
+
}
|
793
|
+
|
794
|
+
if (isParenthesised(pathNode)) {
|
795
|
+
if (isInCurrentReportsBuffer(pathNode)) {
|
796
|
+
|
797
|
+
// This node was supposed to be reported, but parentheses might be necessary.
|
798
|
+
|
799
|
+
if (isParenthesisedTwice(pathNode)) {
|
800
|
+
|
801
|
+
/*
|
802
|
+
* This node is parenthesised twice, it certainly has at least one pair of `extra` parentheses.
|
803
|
+
* If the --fix option is on, the current fixing iteration will remove only one pair of parentheses.
|
804
|
+
* The remaining pair is safely enclosing the 'in' expression.
|
805
|
+
*/
|
806
|
+
return;
|
807
|
+
}
|
808
|
+
|
809
|
+
// Exclude the outermost node only.
|
810
|
+
if (!nodeToExclude) {
|
811
|
+
nodeToExclude = pathNode;
|
812
|
+
}
|
813
|
+
|
814
|
+
// Don't break the loop here, there might be some safe nodes or parentheses that will stay inside.
|
815
|
+
|
816
|
+
} else {
|
817
|
+
|
818
|
+
// This node will stay parenthesised, the 'in' expression in safely enclosed by '()'.
|
819
|
+
return;
|
820
|
+
}
|
821
|
+
}
|
822
|
+
}
|
823
|
+
|
824
|
+
// Exclude the node from the list (i.e. treat parentheses as necessary)
|
825
|
+
removeFromCurrentReportsBuffer(nodeToExclude);
|
826
|
+
});
|
827
|
+
}
|
828
|
+
|
829
|
+
endCurrentReportsBuffering();
|
616
830
|
},
|
617
831
|
|
618
832
|
IfStatement(node) {
|
@@ -664,6 +878,16 @@ module.exports = {
|
|
664
878
|
}).forEach(property => report(property.value));
|
665
879
|
},
|
666
880
|
|
881
|
+
Property(node) {
|
882
|
+
if (node.computed) {
|
883
|
+
const { key } = node;
|
884
|
+
|
885
|
+
if (key && hasExcessParens(key) && precedence(key) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
|
886
|
+
report(key);
|
887
|
+
}
|
888
|
+
}
|
889
|
+
},
|
890
|
+
|
667
891
|
ReturnStatement(node) {
|
668
892
|
const returnToken = sourceCode.getFirstToken(node);
|
669
893
|
|
@@ -67,7 +67,8 @@ module.exports = {
|
|
67
67
|
let node = reference.identifier;
|
68
68
|
let parent = node.parent;
|
69
69
|
|
70
|
-
while (parent && !stopNodePattern.test(parent.type)
|
70
|
+
while (parent && (!stopNodePattern.test(parent.type) ||
|
71
|
+
parent.type === "ForInStatement" || parent.type === "ForOfStatement")) {
|
71
72
|
switch (parent.type) {
|
72
73
|
|
73
74
|
// e.g. foo.a = 0;
|
@@ -85,6 +86,16 @@ module.exports = {
|
|
85
86
|
}
|
86
87
|
break;
|
87
88
|
|
89
|
+
// e.g. for (foo.a in b) {}
|
90
|
+
case "ForInStatement":
|
91
|
+
case "ForOfStatement":
|
92
|
+
if (parent.left === node) {
|
93
|
+
return true;
|
94
|
+
}
|
95
|
+
|
96
|
+
// this is a stop node for parent.right and parent.body
|
97
|
+
return false;
|
98
|
+
|
88
99
|
// EXCLUDES: e.g. cache.get(foo.a).b = 0;
|
89
100
|
case "CallExpression":
|
90
101
|
if (parent.callee !== node) {
|
@@ -420,8 +420,9 @@ module.exports = {
|
|
420
420
|
|
421
421
|
let shouldFix = varDeclParent &&
|
422
422
|
|
423
|
-
// Don't do a fix unless the
|
424
|
-
(varDeclParent.parent.type === "ForInStatement" || varDeclParent.parent.type === "ForOfStatement" ||
|
423
|
+
// Don't do a fix unless all variables in the declarations are initialized (or it's in a for-in or for-of loop)
|
424
|
+
(varDeclParent.parent.type === "ForInStatement" || varDeclParent.parent.type === "ForOfStatement" ||
|
425
|
+
varDeclParent.declarations.every(declaration => declaration.init)) &&
|
425
426
|
|
426
427
|
/*
|
427
428
|
* If options.destructuring is "all", then this warning will not occur unless
|
@@ -450,7 +451,12 @@ module.exports = {
|
|
450
451
|
node,
|
451
452
|
messageId: "useConst",
|
452
453
|
data: node,
|
453
|
-
fix: shouldFix
|
454
|
+
fix: shouldFix
|
455
|
+
? fixer => fixer.replaceText(
|
456
|
+
sourceCode.getFirstToken(varDeclParent, t => t.value === varDeclParent.kind),
|
457
|
+
"const"
|
458
|
+
)
|
459
|
+
: null
|
454
460
|
});
|
455
461
|
});
|
456
462
|
}
|
@@ -37,6 +37,8 @@ const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
|
|
37
37
|
// A set of node types that can contain a list of statements
|
38
38
|
const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]);
|
39
39
|
|
40
|
+
const DECIMAL_INTEGER_PATTERN = /^(0|[1-9]\d*)$/u;
|
41
|
+
|
40
42
|
/**
|
41
43
|
* Checks reference if is non initializer and writable.
|
42
44
|
* @param {Reference} reference - A reference to check.
|
@@ -283,6 +285,16 @@ function isCommaToken(token) {
|
|
283
285
|
return token.value === "," && token.type === "Punctuator";
|
284
286
|
}
|
285
287
|
|
288
|
+
/**
|
289
|
+
* Checks if the given token is a dot token or not.
|
290
|
+
*
|
291
|
+
* @param {Token} token - The token to check.
|
292
|
+
* @returns {boolean} `true` if the token is a dot token.
|
293
|
+
*/
|
294
|
+
function isDotToken(token) {
|
295
|
+
return token.value === "." && token.type === "Punctuator";
|
296
|
+
}
|
297
|
+
|
286
298
|
/**
|
287
299
|
* Checks if the given token is a semicolon token or not.
|
288
300
|
*
|
@@ -462,12 +474,14 @@ module.exports = {
|
|
462
474
|
isColonToken,
|
463
475
|
isCommaToken,
|
464
476
|
isCommentToken,
|
477
|
+
isDotToken,
|
465
478
|
isKeywordToken,
|
466
479
|
isNotClosingBraceToken: negate(isClosingBraceToken),
|
467
480
|
isNotClosingBracketToken: negate(isClosingBracketToken),
|
468
481
|
isNotClosingParenToken: negate(isClosingParenToken),
|
469
482
|
isNotColonToken: negate(isColonToken),
|
470
483
|
isNotCommaToken: negate(isCommaToken),
|
484
|
+
isNotDotToken: negate(isDotToken),
|
471
485
|
isNotOpeningBraceToken: negate(isOpeningBraceToken),
|
472
486
|
isNotOpeningBracketToken: negate(isOpeningBracketToken),
|
473
487
|
isNotOpeningParenToken: negate(isOpeningParenToken),
|
@@ -988,7 +1002,18 @@ module.exports = {
|
|
988
1002
|
* '5' // false
|
989
1003
|
*/
|
990
1004
|
isDecimalInteger(node) {
|
991
|
-
return node.type === "Literal" && typeof node.value === "number" &&
|
1005
|
+
return node.type === "Literal" && typeof node.value === "number" &&
|
1006
|
+
DECIMAL_INTEGER_PATTERN.test(node.raw);
|
1007
|
+
},
|
1008
|
+
|
1009
|
+
/**
|
1010
|
+
* Determines whether this token is a decimal integer numeric token.
|
1011
|
+
* This is similar to isDecimalInteger(), but for tokens.
|
1012
|
+
* @param {Token} token - The token to check.
|
1013
|
+
* @returns {boolean} `true` if this token is a decimal integer.
|
1014
|
+
*/
|
1015
|
+
isDecimalIntegerNumericToken(token) {
|
1016
|
+
return token.type === "Numeric" && DECIMAL_INTEGER_PATTERN.test(token.value);
|
992
1017
|
},
|
993
1018
|
|
994
1019
|
/**
|
File without changes
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "eslint",
|
3
|
-
"version": "6.0
|
3
|
+
"version": "6.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": {
|
@@ -49,7 +49,7 @@
|
|
49
49
|
"cross-spawn": "^6.0.5",
|
50
50
|
"debug": "^4.0.1",
|
51
51
|
"doctrine": "^3.0.0",
|
52
|
-
"eslint-scope": "^
|
52
|
+
"eslint-scope": "^5.0.0",
|
53
53
|
"eslint-utils": "^1.3.1",
|
54
54
|
"eslint-visitor-keys": "^1.0.0",
|
55
55
|
"espree": "^6.0.0",
|
@@ -57,28 +57,29 @@
|
|
57
57
|
"esutils": "^2.0.2",
|
58
58
|
"file-entry-cache": "^5.0.1",
|
59
59
|
"functional-red-black-tree": "^1.0.1",
|
60
|
-
"glob-parent": "^
|
60
|
+
"glob-parent": "^5.0.0",
|
61
61
|
"globals": "^11.7.0",
|
62
62
|
"ignore": "^4.0.6",
|
63
63
|
"import-fresh": "^3.0.0",
|
64
64
|
"imurmurhash": "^0.1.4",
|
65
|
-
"inquirer": "^6.
|
65
|
+
"inquirer": "^6.4.1",
|
66
66
|
"is-glob": "^4.0.0",
|
67
67
|
"js-yaml": "^3.13.1",
|
68
68
|
"json-stable-stringify-without-jsonify": "^1.0.1",
|
69
69
|
"levn": "^0.3.0",
|
70
|
-
"lodash": "^4.17.
|
70
|
+
"lodash": "^4.17.14",
|
71
71
|
"minimatch": "^3.0.4",
|
72
72
|
"mkdirp": "^0.5.1",
|
73
73
|
"natural-compare": "^1.4.0",
|
74
74
|
"optionator": "^0.8.2",
|
75
75
|
"progress": "^2.0.0",
|
76
76
|
"regexpp": "^2.0.1",
|
77
|
-
"semver": "^
|
78
|
-
"strip-ansi": "^
|
79
|
-
"strip-json-comments": "^
|
77
|
+
"semver": "^6.1.2",
|
78
|
+
"strip-ansi": "^5.2.0",
|
79
|
+
"strip-json-comments": "^3.0.1",
|
80
80
|
"table": "^5.2.3",
|
81
|
-
"text-table": "^0.2.0"
|
81
|
+
"text-table": "^0.2.0",
|
82
|
+
"v8-compile-cache": "^2.0.3"
|
82
83
|
},
|
83
84
|
"devDependencies": {
|
84
85
|
"@babel/core": "^7.4.3",
|
@@ -109,22 +110,22 @@
|
|
109
110
|
"leche": "^2.2.3",
|
110
111
|
"lint-staged": "^8.1.5",
|
111
112
|
"load-perf": "^0.2.0",
|
112
|
-
"markdownlint": "^0.
|
113
|
-
"markdownlint-cli": "^0.
|
114
|
-
"metro-memory-fs": "^0.
|
113
|
+
"markdownlint": "^0.15.0",
|
114
|
+
"markdownlint-cli": "^0.17.0",
|
115
|
+
"metro-memory-fs": "^0.54.1",
|
115
116
|
"mocha": "^6.1.2",
|
116
117
|
"mocha-junit-reporter": "^1.23.0",
|
117
118
|
"npm-license": "^0.3.3",
|
118
|
-
"nyc": "^
|
119
|
+
"nyc": "^14.1.1",
|
119
120
|
"proxyquire": "^2.0.1",
|
120
|
-
"puppeteer": "^1.
|
121
|
-
"recast": "^0.
|
121
|
+
"puppeteer": "^1.18.0",
|
122
|
+
"recast": "^0.18.1",
|
122
123
|
"regenerator-runtime": "^0.13.2",
|
123
124
|
"shelljs": "^0.8.2",
|
124
125
|
"sinon": "^7.3.2",
|
125
126
|
"temp": "^0.9.0",
|
126
|
-
"webpack": "^4.
|
127
|
-
"webpack-cli": "^3.3.
|
127
|
+
"webpack": "^4.35.0",
|
128
|
+
"webpack-cli": "^3.3.5",
|
128
129
|
"yorkie": "^2.0.0"
|
129
130
|
},
|
130
131
|
"keywords": [
|