eslint 6.7.2 → 6.8.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 +30 -0
- package/README.md +1 -2
- package/conf/config-schema.js +1 -0
- package/conf/eslint-recommended.js +0 -1
- package/lib/cli-engine/cli-engine.js +1 -0
- package/lib/cli-engine/config-array-factory.js +2 -0
- package/lib/cli-engine/file-enumerator.js +13 -9
- package/lib/cli-engine/lint-result-cache.js +2 -1
- package/lib/cli.js +2 -1
- package/lib/linter/config-comment-parser.js +1 -1
- package/lib/options.js +6 -0
- package/lib/rule-tester/rule-tester.js +4 -2
- package/lib/rules/array-bracket-spacing.js +8 -8
- package/lib/rules/key-spacing.js +34 -15
- package/lib/rules/lines-between-class-members.js +42 -53
- package/lib/rules/no-multiple-empty-lines.js +1 -1
- package/lib/rules/no-restricted-imports.js +2 -2
- package/lib/rules/no-unsafe-negation.js +30 -5
- package/lib/rules/prefer-numeric-literals.js +29 -28
- package/lib/rules/require-atomic-updates.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,33 @@
|
|
1
|
+
v6.8.0 - December 20, 2019
|
2
|
+
|
3
|
+
* [`c5c7086`](https://github.com/eslint/eslint/commit/c5c708666b450fb69522a55aa375626f9297dc6f) Fix: ignore aligning single line in key-spacing (fixes #11414) (#12652) (YeonJuan)
|
4
|
+
* [`9986d9e`](https://github.com/eslint/eslint/commit/9986d9e0baed0d3586bbee472fe2fae2ed625f5d) Chore: add object option test cases in yield-star-spacing (#12679) (YeonJuan)
|
5
|
+
* [`1713d07`](https://github.com/eslint/eslint/commit/1713d0758b083f3840d724505f997a7cb20ff384) New: Add no-error-on-unmatched-pattern flag (fixes #10587) (#12377) (ncraley)
|
6
|
+
* [`5c25a26`](https://github.com/eslint/eslint/commit/5c25a26608fbd9a1d0127c9a3653609aa4b63e86) Update: autofix bug in lines-between-class-members (fixes #12391) (#12632) (YeonJuan)
|
7
|
+
* [`4b3cc5c`](https://github.com/eslint/eslint/commit/4b3cc5cd2459f04eae149faea0651785d7f9db0b) Chore: enable prefer-regex-literals in eslint codebase (#12268) (薛定谔的猫)
|
8
|
+
* [`05faebb`](https://github.com/eslint/eslint/commit/05faebb943456ad2b20117f3c8b3eccbe2e2fb03) Update: improve suggestion testing experience (#12602) (Brad Zacher)
|
9
|
+
* [`05f7dd5`](https://github.com/eslint/eslint/commit/05f7dd53ed91a6e3be9eb40825fb6d2207f82209) Update: Add suggestions for no-unsafe-negation (fixes #12591) (#12609) (Milos Djermanovic)
|
10
|
+
* [`d3e43f1`](https://github.com/eslint/eslint/commit/d3e43f1c10c5e19f40e7b3d3944b87f1b0c9c075) Docs: Update no-multi-assign explanation (#12615) (Yuping Zuo)
|
11
|
+
* [`272e4db`](https://github.com/eslint/eslint/commit/272e4db6074283bc01cc6ec72c9e396bb3c110e6) Fix: no-multiple-empty-lines: Adjust reported `loc` (#12594) (Tobias Bieniek)
|
12
|
+
* [`a258039`](https://github.com/eslint/eslint/commit/a258039e556075d7d1f955a79d094ea103ec165a) Fix: no-restricted-imports schema allows multiple paths/patterns objects (#12639) (Milos Djermanovic)
|
13
|
+
* [`51f9620`](https://github.com/eslint/eslint/commit/51f9620cc55cc091fe38dbe68e4633de06297b8c) Fix: improve report location for array-bracket-spacing (#12653) (Milos Djermanovic)
|
14
|
+
* [`45364af`](https://github.com/eslint/eslint/commit/45364afc9c7f0251348cd1a7a13656c3816435d7) Fix: prefer-numeric-literals doesn't check types of literal arguments (#12655) (Milos Djermanovic)
|
15
|
+
* [`e3c570e`](https://github.com/eslint/eslint/commit/e3c570eaf3d1d44fb57bf42f1870887856e4c5a0) Docs: Add example for expression option (#12694) (Arnaud Barré)
|
16
|
+
* [`6b774ef`](https://github.com/eslint/eslint/commit/6b774ef0d849ccf5c1127b25e1fe7c3e438d586b) Docs: Add spacing in comments for no-console rule (#12696) (Nikki Nikkhoui)
|
17
|
+
* [`7171fca`](https://github.com/eslint/eslint/commit/7171fca6ef4e0e8f267658fc7d8f603f00eddd84) Chore: refactor regex in config comment parser (#12662) (Milos Djermanovic)
|
18
|
+
* [`1600648`](https://github.com/eslint/eslint/commit/1600648d2880ffb1e9e414b31ff0f66ead7167f9) Update: Allow $schema in config (#12612) (Yordis Prieto)
|
19
|
+
* [`acc0e47`](https://github.com/eslint/eslint/commit/acc0e47572a9390292b4e313b4a4bf360d236358) Update: support .eslintrc.cjs (refs eslint/rfcs#43) (#12321) (Evan Plaice)
|
20
|
+
* [`49c1658`](https://github.com/eslint/eslint/commit/49c1658544ace24b9aaaa301af0fc07a2ef3bf30) Chore: remove bundling of ESLint during release (#12676) (Kai Cataldo)
|
21
|
+
* [`257f3d6`](https://github.com/eslint/eslint/commit/257f3d67905a52bf8602a5a5707c893cc90d7ca7) Chore: complete to move to GitHub Actions (#12625) (Toru Nagashima)
|
22
|
+
* [`ab912f0`](https://github.com/eslint/eslint/commit/ab912f0ef709a916ab9a27ea09d9d7adf046fb2d) Docs: 1tbs with allowSingleLine edge cases (refs #12284) (#12314) (Ari Kardasis)
|
23
|
+
* [`dd1c30e`](https://github.com/eslint/eslint/commit/dd1c30e35f05ed332e2abbd3d4d53635efde74b8) Sponsors: Sync README with website (ESLint Jenkins)
|
24
|
+
* [`a230f84`](https://github.com/eslint/eslint/commit/a230f8404e4f2423dd79378b065d24c12776775b) Update: include node version in cache (#12582) (Eric Wang)
|
25
|
+
* [`8b65f17`](https://github.com/eslint/eslint/commit/8b65f175dfb4fac11ed7184537be400ed14996fb) Chore: remove references to parser demo (#12644) (Kai Cataldo)
|
26
|
+
* [`e9cef99`](https://github.com/eslint/eslint/commit/e9cef99e6ebec1faefdb576ca597e81ae4f04afd) Docs: wrap {{}} in raw liquid tags to prevent interpolation (#12643) (Kai Cataldo)
|
27
|
+
* [`e707453`](https://github.com/eslint/eslint/commit/e70745325ff9e085acc6843dd8bfae5550645d4f) Docs: Fix configuration example in no-restricted-imports (fixes #11717) (#12638) (Milos Djermanovic)
|
28
|
+
* [`19194ce`](https://github.com/eslint/eslint/commit/19194cec724e016df02376bbeae31171be6f0bdf) Chore: Add tests to cover default object options in comma-dangle (#12627) (YeonJuan)
|
29
|
+
* [`6e36d12`](https://github.com/eslint/eslint/commit/6e36d12d95e76022172fd0ec8a5e85c22fde6a8a) Update: do not recommend require-atomic-updates (refs #11899) (#12599) (Kai Cataldo)
|
30
|
+
|
1
31
|
v6.7.2 - November 30, 2019
|
2
32
|
|
3
33
|
* [`bc435a9`](https://github.com/eslint/eslint/commit/bc435a93afd6ba4def1b53993ef7cf8220f3f070) Fix: isSpaceBetweenTokens() recognizes spaces in JSXText (fixes #12614) (#12616) (Toru Nagashima)
|
package/README.md
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
[![NPM version][npm-image]][npm-url]
|
2
|
-
[](https://dev.azure.com/eslint/eslint/_build/latest?definitionId=1&branchName=master)
|
3
2
|
[](https://github.com/eslint/eslint/actions)
|
4
3
|
[![Downloads][downloads-image]][downloads-url]
|
5
4
|
[](https://www.bountysource.com/trackers/282608-eslint?utm_source=282608&utm_medium=shield&utm_campaign=TRACKER_BADGE)
|
@@ -265,7 +264,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
|
|
265
264
|
<h3>Gold Sponsors</h3>
|
266
265
|
<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://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/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/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://opensource.facebook.com"><img src="https://images.opencollective.com/fbopensource/fbb8a5b/logo.png" alt="Facebook Open Source" height="96"></a></p><h3>Silver Sponsors</h3>
|
267
266
|
<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>
|
268
|
-
<p><a href="https://uxplanet.org/top-ui-ux-design-agencies-user-experience-firms-8c54697e290"><img src="https://images.opencollective.com/ui-ux-design-agencies/cae5dfe/logo.png" alt="UI UX Design Agencies" height="32"></a> <a href="https://
|
267
|
+
<p><a href="https://uxplanet.org/top-ui-ux-design-agencies-user-experience-firms-8c54697e290"><img src="https://images.opencollective.com/ui-ux-design-agencies/cae5dfe/logo.png" alt="UI UX Design Agencies" height="32"></a> <a href="https://medium.com/@niksundin/best-web-design-companies-1872e445775f"><img src="https://images.opencollective.com/top-web-design-agencies/d92d747/logo.png" alt="Top Web Design Agencies" height="32"></a> <a href="https://www.bugsnag.com/platforms?utm_source=Open Collective&utm_medium=Website&utm_content=open-source&utm_campaign=2019-community&utm_term="><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://www.crosswordsolver.com"><img src="https://images.opencollective.com/crosswordsolver/d4481d6/logo.png" alt="Crosswordsolver" height="32"></a> <a href="https://www.codacy.com/?utm_source=eslint&utm_medium=cpm&utm_campaign=eslint-sponsorship"><img src="https://images.opencollective.com/codacy/ed22716/logo.png" alt="Codacy" 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/0b37d14/logo.png" alt="Free Icons by Icons8" height="32"></a> <a href="https://edubirdie.com/"><img src="https://images.opencollective.com/edubirdie2/b1d51ab/logo.png" alt="EduBirdie" height="32"></a> <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/d5592fe/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></p>
|
269
268
|
<!--sponsorsend-->
|
270
269
|
|
271
270
|
## <a name="technology-sponsors"></a>Technology Sponsors
|
package/conf/config-schema.js
CHANGED
@@ -62,6 +62,7 @@ const eslintRecommendedPath = path.resolve(__dirname, "../../conf/eslint-recomme
|
|
62
62
|
const eslintAllPath = path.resolve(__dirname, "../../conf/eslint-all.js");
|
63
63
|
const configFilenames = [
|
64
64
|
".eslintrc.js",
|
65
|
+
".eslintrc.cjs",
|
65
66
|
".eslintrc.yaml",
|
66
67
|
".eslintrc.yml",
|
67
68
|
".eslintrc.json",
|
@@ -279,6 +280,7 @@ function configMissingError(configName, importerName) {
|
|
279
280
|
function loadConfigFile(filePath) {
|
280
281
|
switch (path.extname(filePath)) {
|
281
282
|
case ".js":
|
283
|
+
case ".cjs":
|
282
284
|
return loadJSConfigFile(filePath);
|
283
285
|
|
284
286
|
case ".json":
|
@@ -190,6 +190,7 @@ class FileEnumerator {
|
|
190
190
|
configArrayFactory = new CascadingConfigArrayFactory({ cwd }),
|
191
191
|
extensions = [".js"],
|
192
192
|
globInputPaths = true,
|
193
|
+
errorOnUnmatchedPattern = true,
|
193
194
|
ignore = true
|
194
195
|
} = {}) {
|
195
196
|
internalSlotsMap.set(this, {
|
@@ -208,6 +209,7 @@ class FileEnumerator {
|
|
208
209
|
"u"
|
209
210
|
),
|
210
211
|
globInputPaths,
|
212
|
+
errorOnUnmatchedPattern,
|
211
213
|
ignoreFlag: ignore
|
212
214
|
});
|
213
215
|
}
|
@@ -226,7 +228,7 @@ class FileEnumerator {
|
|
226
228
|
* @returns {IterableIterator<FileAndConfig>} The found files.
|
227
229
|
*/
|
228
230
|
*iterateFiles(patternOrPatterns) {
|
229
|
-
const { globInputPaths } = internalSlotsMap.get(this);
|
231
|
+
const { globInputPaths, errorOnUnmatchedPattern } = internalSlotsMap.get(this);
|
230
232
|
const patterns = Array.isArray(patternOrPatterns)
|
231
233
|
? patternOrPatterns
|
232
234
|
: [patternOrPatterns];
|
@@ -265,14 +267,16 @@ class FileEnumerator {
|
|
265
267
|
}
|
266
268
|
|
267
269
|
// Raise an error if any files were not found.
|
268
|
-
if (
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
270
|
+
if (errorOnUnmatchedPattern) {
|
271
|
+
if (!foundRegardlessOfIgnored) {
|
272
|
+
throw new NoFilesFoundError(
|
273
|
+
pattern,
|
274
|
+
!globInputPaths && isGlob(pattern)
|
275
|
+
);
|
276
|
+
}
|
277
|
+
if (!found) {
|
278
|
+
throw new AllFilesIgnoredError(pattern);
|
279
|
+
}
|
276
280
|
}
|
277
281
|
}
|
278
282
|
|
@@ -20,6 +20,7 @@ const hash = require("./hash");
|
|
20
20
|
//-----------------------------------------------------------------------------
|
21
21
|
|
22
22
|
const configHashCache = new WeakMap();
|
23
|
+
const nodeVersion = process && process.version;
|
23
24
|
|
24
25
|
/**
|
25
26
|
* Calculates the hash of the config
|
@@ -28,7 +29,7 @@ const configHashCache = new WeakMap();
|
|
28
29
|
*/
|
29
30
|
function hashOfConfigFor(config) {
|
30
31
|
if (!configHashCache.has(config)) {
|
31
|
-
configHashCache.set(config, hash(`${pkg.version}_${stringify(config)}`));
|
32
|
+
configHashCache.set(config, hash(`${pkg.version}_${nodeVersion}_${stringify(config)}`));
|
32
33
|
}
|
33
34
|
|
34
35
|
return configHashCache.get(config);
|
package/lib/cli.js
CHANGED
@@ -68,7 +68,8 @@ function translateOptions(cliOptions) {
|
|
68
68
|
fixTypes: cliOptions.fixType,
|
69
69
|
allowInlineConfig: cliOptions.inlineConfig,
|
70
70
|
reportUnusedDisableDirectives: cliOptions.reportUnusedDisableDirectives,
|
71
|
-
resolvePluginsRelativeTo: cliOptions.resolvePluginsRelativeTo
|
71
|
+
resolvePluginsRelativeTo: cliOptions.resolvePluginsRelativeTo,
|
72
|
+
errorOnUnmatchedPattern: cliOptions.errorOnUnmatchedPattern
|
72
73
|
};
|
73
74
|
}
|
74
75
|
|
@@ -90,7 +90,7 @@ module.exports = class ConfigCommentParser {
|
|
90
90
|
* But we are supporting that. So this is a fallback for that.
|
91
91
|
*/
|
92
92
|
items = {};
|
93
|
-
const normalizedString = string.replace(/([a-zA-Z0-9
|
93
|
+
const normalizedString = string.replace(/([-a-zA-Z0-9/]+):/gu, "\"$1\":").replace(/(\]|[0-9])\s+(?=")/u, "$1,");
|
94
94
|
|
95
95
|
try {
|
96
96
|
items = JSON.parse(`{${normalizedString}}`);
|
package/lib/options.js
CHANGED
@@ -230,6 +230,12 @@ module.exports = optionator({
|
|
230
230
|
default: "false",
|
231
231
|
description: "Output execution environment information"
|
232
232
|
},
|
233
|
+
{
|
234
|
+
option: "error-on-unmatched-pattern",
|
235
|
+
type: "Boolean",
|
236
|
+
default: "true",
|
237
|
+
description: "Prevent errors when pattern is unmatched"
|
238
|
+
},
|
233
239
|
{
|
234
240
|
option: "debug",
|
235
241
|
type: "Boolean",
|
@@ -596,8 +596,10 @@ class RuleTester {
|
|
596
596
|
if (hasOwnProperty(error, "suggestions")) {
|
597
597
|
|
598
598
|
// Support asserting there are no suggestions
|
599
|
-
if (!error.suggestions) {
|
600
|
-
|
599
|
+
if (!error.suggestions || (Array.isArray(error.suggestions) && error.suggestions.length === 0)) {
|
600
|
+
if (Array.isArray(message.suggestions) && message.suggestions.length > 0) {
|
601
|
+
assert.fail(`Error should have no suggestions on error with message: "${message.message}"`);
|
602
|
+
}
|
601
603
|
} else {
|
602
604
|
assert.strictEqual(Array.isArray(message.suggestions), true, `Error should have an array of suggestions. Instead received "${message.suggestions}" on error with message: "${message.message}"`);
|
603
605
|
assert.strictEqual(message.suggestions.length, error.suggestions.length, `Error should have ${error.suggestions.length} suggestions. Instead found ${message.suggestions.length} suggestions`);
|
@@ -84,16 +84,16 @@ module.exports = {
|
|
84
84
|
* @returns {void}
|
85
85
|
*/
|
86
86
|
function reportNoBeginningSpace(node, token) {
|
87
|
+
const nextToken = sourceCode.getTokenAfter(token);
|
88
|
+
|
87
89
|
context.report({
|
88
90
|
node,
|
89
|
-
loc: token.loc.start,
|
91
|
+
loc: { start: token.loc.end, end: nextToken.loc.start },
|
90
92
|
messageId: "unexpectedSpaceAfter",
|
91
93
|
data: {
|
92
94
|
tokenValue: token.value
|
93
95
|
},
|
94
96
|
fix(fixer) {
|
95
|
-
const nextToken = sourceCode.getTokenAfter(token);
|
96
|
-
|
97
97
|
return fixer.removeRange([token.range[1], nextToken.range[0]]);
|
98
98
|
}
|
99
99
|
});
|
@@ -106,16 +106,16 @@ module.exports = {
|
|
106
106
|
* @returns {void}
|
107
107
|
*/
|
108
108
|
function reportNoEndingSpace(node, token) {
|
109
|
+
const previousToken = sourceCode.getTokenBefore(token);
|
110
|
+
|
109
111
|
context.report({
|
110
112
|
node,
|
111
|
-
loc: token.loc.start,
|
113
|
+
loc: { start: previousToken.loc.end, end: token.loc.start },
|
112
114
|
messageId: "unexpectedSpaceBefore",
|
113
115
|
data: {
|
114
116
|
tokenValue: token.value
|
115
117
|
},
|
116
118
|
fix(fixer) {
|
117
|
-
const previousToken = sourceCode.getTokenBefore(token);
|
118
|
-
|
119
119
|
return fixer.removeRange([previousToken.range[1], token.range[0]]);
|
120
120
|
}
|
121
121
|
});
|
@@ -130,7 +130,7 @@ module.exports = {
|
|
130
130
|
function reportRequiredBeginningSpace(node, token) {
|
131
131
|
context.report({
|
132
132
|
node,
|
133
|
-
loc: token.loc
|
133
|
+
loc: token.loc,
|
134
134
|
messageId: "missingSpaceAfter",
|
135
135
|
data: {
|
136
136
|
tokenValue: token.value
|
@@ -150,7 +150,7 @@ module.exports = {
|
|
150
150
|
function reportRequiredEndingSpace(node, token) {
|
151
151
|
context.report({
|
152
152
|
node,
|
153
|
-
loc: token.loc
|
153
|
+
loc: token.loc,
|
154
154
|
messageId: "missingSpaceBefore",
|
155
155
|
data: {
|
156
156
|
tokenValue: token.value
|
package/lib/rules/key-spacing.js
CHANGED
@@ -42,6 +42,18 @@ function isSingleLine(node) {
|
|
42
42
|
return (node.loc.end.line === node.loc.start.line);
|
43
43
|
}
|
44
44
|
|
45
|
+
/**
|
46
|
+
* Checks whether the properties on a single line.
|
47
|
+
* @param {ASTNode[]} properties List of Property AST nodes.
|
48
|
+
* @returns {boolean} True if all properies is on a single line.
|
49
|
+
*/
|
50
|
+
function isSingleLineProperties(properties) {
|
51
|
+
const [firstProp] = properties,
|
52
|
+
lastProp = last(properties);
|
53
|
+
|
54
|
+
return firstProp.loc.start.line === lastProp.loc.end.line;
|
55
|
+
}
|
56
|
+
|
45
57
|
/**
|
46
58
|
* Initializes a single option property from the configuration with defaults for undefined values
|
47
59
|
* @param {Object} toOptions Object to be initialized
|
@@ -583,17 +595,6 @@ module.exports = {
|
|
583
595
|
}
|
584
596
|
}
|
585
597
|
|
586
|
-
/**
|
587
|
-
* Verifies vertical alignment, taking into account groups of properties.
|
588
|
-
* @param {ASTNode} node ObjectExpression node being evaluated.
|
589
|
-
* @returns {void}
|
590
|
-
*/
|
591
|
-
function verifyAlignment(node) {
|
592
|
-
createGroups(node).forEach(group => {
|
593
|
-
verifyGroupAlignment(group.filter(isKeyValueProperty));
|
594
|
-
});
|
595
|
-
}
|
596
|
-
|
597
598
|
/**
|
598
599
|
* Verifies spacing of property conforms to specified options.
|
599
600
|
* @param {ASTNode} node Property node being evaluated.
|
@@ -611,17 +612,35 @@ module.exports = {
|
|
611
612
|
|
612
613
|
/**
|
613
614
|
* Verifies spacing of each property in a list.
|
614
|
-
* @param
|
615
|
+
* @param {ASTNode[]} properties List of Property AST nodes.
|
616
|
+
* @param {Object} lineOptions Configured singleLine or multiLine options
|
615
617
|
* @returns {void}
|
616
618
|
*/
|
617
|
-
function verifyListSpacing(properties) {
|
619
|
+
function verifyListSpacing(properties, lineOptions) {
|
618
620
|
const length = properties.length;
|
619
621
|
|
620
622
|
for (let i = 0; i < length; i++) {
|
621
|
-
verifySpacing(properties[i],
|
623
|
+
verifySpacing(properties[i], lineOptions);
|
622
624
|
}
|
623
625
|
}
|
624
626
|
|
627
|
+
/**
|
628
|
+
* Verifies vertical alignment, taking into account groups of properties.
|
629
|
+
* @param {ASTNode} node ObjectExpression node being evaluated.
|
630
|
+
* @returns {void}
|
631
|
+
*/
|
632
|
+
function verifyAlignment(node) {
|
633
|
+
createGroups(node).forEach(group => {
|
634
|
+
const properties = group.filter(isKeyValueProperty);
|
635
|
+
|
636
|
+
if (properties.length > 0 && isSingleLineProperties(properties)) {
|
637
|
+
verifyListSpacing(properties, multiLineOptions);
|
638
|
+
} else {
|
639
|
+
verifyGroupAlignment(properties);
|
640
|
+
}
|
641
|
+
});
|
642
|
+
}
|
643
|
+
|
625
644
|
//--------------------------------------------------------------------------
|
626
645
|
// Public API
|
627
646
|
//--------------------------------------------------------------------------
|
@@ -631,7 +650,7 @@ module.exports = {
|
|
631
650
|
return {
|
632
651
|
ObjectExpression(node) {
|
633
652
|
if (isSingleLine(node)) {
|
634
|
-
verifyListSpacing(node.properties.filter(isKeyValueProperty));
|
653
|
+
verifyListSpacing(node.properties.filter(isKeyValueProperty), singleLineOptions);
|
635
654
|
} else {
|
636
655
|
verifyAlignment(node);
|
637
656
|
}
|
@@ -54,62 +54,45 @@ module.exports = {
|
|
54
54
|
const sourceCode = context.getSourceCode();
|
55
55
|
|
56
56
|
/**
|
57
|
-
*
|
58
|
-
* @param {Token}
|
59
|
-
* @param {Token}
|
60
|
-
* @
|
57
|
+
* Return the last token among the consecutive tokens that have no exceed max line difference in between, before the first token in the next member.
|
58
|
+
* @param {Token} prevLastToken The last token in the previous member node.
|
59
|
+
* @param {Token} nextFirstToken The first token in the next member node.
|
60
|
+
* @param {number} maxLine The maximum number of allowed line difference between consecutive tokens.
|
61
|
+
* @returns {Token} The last token among the consecutive tokens.
|
61
62
|
*/
|
62
|
-
function
|
63
|
-
const
|
64
|
-
const len = comments.length;
|
63
|
+
function findLastConsecutiveTokenAfter(prevLastToken, nextFirstToken, maxLine) {
|
64
|
+
const after = sourceCode.getTokenAfter(prevLastToken, { includeComments: true });
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
const linesBetweenFstAndSnd = second.loc.start.line - first.loc.end.line - 1;
|
69
|
-
|
70
|
-
return linesBetweenFstAndSnd >= 1;
|
71
|
-
}
|
72
|
-
|
73
|
-
|
74
|
-
// If there are comments
|
75
|
-
let sumOfCommentLines = 0; // the numbers of lines of comments
|
76
|
-
let prevCommentLineNum = -1; // line number of the end of the previous comment
|
77
|
-
|
78
|
-
for (let i = 0; i < len; i++) {
|
79
|
-
const commentLinesOfThisComment = comments[i].loc.end.line - comments[i].loc.start.line + 1;
|
80
|
-
|
81
|
-
sumOfCommentLines += commentLinesOfThisComment;
|
82
|
-
|
83
|
-
/*
|
84
|
-
* If this comment and the previous comment are in the same line,
|
85
|
-
* the count of comment lines is duplicated. So decrement sumOfCommentLines.
|
86
|
-
*/
|
87
|
-
if (prevCommentLineNum === comments[i].loc.start.line) {
|
88
|
-
sumOfCommentLines -= 1;
|
89
|
-
}
|
90
|
-
|
91
|
-
prevCommentLineNum = comments[i].loc.end.line;
|
66
|
+
if (after !== nextFirstToken && after.loc.start.line - prevLastToken.loc.end.line <= maxLine) {
|
67
|
+
return findLastConsecutiveTokenAfter(after, nextFirstToken, maxLine);
|
92
68
|
}
|
69
|
+
return prevLastToken;
|
70
|
+
}
|
93
71
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
72
|
+
/**
|
73
|
+
* Return the first token among the consecutive tokens that have no exceed max line difference in between, after the last token in the previous member.
|
74
|
+
* @param {Token} nextFirstToken The first token in the next member node.
|
75
|
+
* @param {Token} prevLastToken The last token in the previous member node.
|
76
|
+
* @param {number} maxLine The maximum number of allowed line difference between consecutive tokens.
|
77
|
+
* @returns {Token} The first token among the consecutive tokens.
|
78
|
+
*/
|
79
|
+
function findFirstConsecutiveTokenBefore(nextFirstToken, prevLastToken, maxLine) {
|
80
|
+
const before = sourceCode.getTokenBefore(nextFirstToken, { includeComments: true });
|
101
81
|
|
102
|
-
|
103
|
-
|
104
|
-
* the count of comment lines is duplicated. So decrement sumOfCommentLines.
|
105
|
-
*/
|
106
|
-
if (comments[len - 1].loc.end.line === second.loc.start.line) {
|
107
|
-
sumOfCommentLines -= 1;
|
82
|
+
if (before !== prevLastToken && nextFirstToken.loc.start.line - before.loc.end.line <= maxLine) {
|
83
|
+
return findFirstConsecutiveTokenBefore(before, prevLastToken, maxLine);
|
108
84
|
}
|
85
|
+
return nextFirstToken;
|
86
|
+
}
|
109
87
|
|
110
|
-
|
111
|
-
|
112
|
-
|
88
|
+
/**
|
89
|
+
* Checks if there is a token or comment between two tokens.
|
90
|
+
* @param {Token} before The token before.
|
91
|
+
* @param {Token} after The token after.
|
92
|
+
* @returns {boolean} True if there is a token or comment between two tokens.
|
93
|
+
*/
|
94
|
+
function hasTokenOrCommentBetween(before, after) {
|
95
|
+
return sourceCode.getTokensBetween(before, after, { includeComments: true }).length !== 0;
|
113
96
|
}
|
114
97
|
|
115
98
|
return {
|
@@ -120,10 +103,13 @@ module.exports = {
|
|
120
103
|
const curFirst = sourceCode.getFirstToken(body[i]);
|
121
104
|
const curLast = sourceCode.getLastToken(body[i]);
|
122
105
|
const nextFirst = sourceCode.getFirstToken(body[i + 1]);
|
123
|
-
const isPadded = isPaddingBetweenTokens(curLast, nextFirst);
|
124
106
|
const isMulti = !astUtils.isTokenOnSameLine(curFirst, curLast);
|
125
107
|
const skip = !isMulti && options[1].exceptAfterSingleLine;
|
126
|
-
|
108
|
+
const beforePadding = findLastConsecutiveTokenAfter(curLast, nextFirst, 1);
|
109
|
+
const afterPadding = findFirstConsecutiveTokenBefore(nextFirst, curLast, 1);
|
110
|
+
const isPadded = afterPadding.loc.start.line - beforePadding.loc.end.line > 1;
|
111
|
+
const hasTokenInPadding = hasTokenOrCommentBetween(beforePadding, afterPadding);
|
112
|
+
const curLineLastToken = findLastConsecutiveTokenAfter(curLast, nextFirst, 0);
|
127
113
|
|
128
114
|
if ((options[0] === "always" && !skip && !isPadded) ||
|
129
115
|
(options[0] === "never" && isPadded)) {
|
@@ -131,9 +117,12 @@ module.exports = {
|
|
131
117
|
node: body[i + 1],
|
132
118
|
messageId: isPadded ? "never" : "always",
|
133
119
|
fix(fixer) {
|
120
|
+
if (hasTokenInPadding) {
|
121
|
+
return null;
|
122
|
+
}
|
134
123
|
return isPadded
|
135
|
-
? fixer.replaceTextRange([
|
136
|
-
: fixer.insertTextAfter(
|
124
|
+
? fixer.replaceTextRange([beforePadding.range[1], afterPadding.range[0]], "\n")
|
125
|
+
: fixer.insertTextAfter(curLineLastToken, "\n");
|
137
126
|
}
|
138
127
|
});
|
139
128
|
}
|
@@ -110,7 +110,7 @@ module.exports = {
|
|
110
110
|
if (lineNumber - lastLineNumber - 1 > maxAllowed) {
|
111
111
|
context.report({
|
112
112
|
node,
|
113
|
-
loc: { start: { line: lastLineNumber + 1, column: 0 }, end: { line: lineNumber, column: 0 } },
|
113
|
+
loc: { start: { line: lastLineNumber + maxAllowed + 1, column: 0 }, end: { line: lineNumber, column: 0 } },
|
114
114
|
message,
|
115
115
|
data: { max: maxAllowed, pluralizedLines: maxAllowed === 1 ? "line" : "lines" },
|
116
116
|
fix(fixer) {
|
@@ -72,14 +72,14 @@ module.exports = {
|
|
72
72
|
arrayOfStringsOrObjects,
|
73
73
|
{
|
74
74
|
type: "array",
|
75
|
-
items: {
|
75
|
+
items: [{
|
76
76
|
type: "object",
|
77
77
|
properties: {
|
78
78
|
paths: arrayOfStringsOrObjects,
|
79
79
|
patterns: arrayOfStrings
|
80
80
|
},
|
81
81
|
additionalProperties: false
|
82
|
-
},
|
82
|
+
}],
|
83
83
|
additionalItems: false
|
84
84
|
}
|
85
85
|
]
|
@@ -54,7 +54,8 @@ module.exports = {
|
|
54
54
|
description: "disallow negating the left operand of relational operators",
|
55
55
|
category: "Possible Errors",
|
56
56
|
recommended: true,
|
57
|
-
url: "https://eslint.org/docs/rules/no-unsafe-negation"
|
57
|
+
url: "https://eslint.org/docs/rules/no-unsafe-negation",
|
58
|
+
suggestion: true
|
58
59
|
},
|
59
60
|
|
60
61
|
schema: [
|
@@ -69,9 +70,13 @@ module.exports = {
|
|
69
70
|
additionalProperties: false
|
70
71
|
}
|
71
72
|
],
|
73
|
+
|
72
74
|
fixable: null,
|
75
|
+
|
73
76
|
messages: {
|
74
|
-
unexpected: "Unexpected negating the left operand of '{{operator}}' operator."
|
77
|
+
unexpected: "Unexpected negating the left operand of '{{operator}}' operator.",
|
78
|
+
suggestNegatedExpression: "Negate '{{operator}}' expression instead of its left operand. This changes the current behavior.",
|
79
|
+
suggestParenthesisedNegation: "Wrap negation in '()' to make the intention explicit. This preserves the current behavior."
|
75
80
|
}
|
76
81
|
},
|
77
82
|
|
@@ -82,10 +87,11 @@ module.exports = {
|
|
82
87
|
|
83
88
|
return {
|
84
89
|
BinaryExpression(node) {
|
85
|
-
const
|
90
|
+
const operator = node.operator;
|
91
|
+
const orderingRelationRuleApplies = enforceForOrderingRelations && isOrderingRelationalOperator(operator);
|
86
92
|
|
87
93
|
if (
|
88
|
-
(isInOrInstanceOfOperator(
|
94
|
+
(isInOrInstanceOfOperator(operator) || orderingRelationRuleApplies) &&
|
89
95
|
isNegation(node.left) &&
|
90
96
|
!astUtils.isParenthesised(sourceCode, node.left)
|
91
97
|
) {
|
@@ -93,7 +99,26 @@ module.exports = {
|
|
93
99
|
node,
|
94
100
|
loc: node.left.loc,
|
95
101
|
messageId: "unexpected",
|
96
|
-
data: { operator
|
102
|
+
data: { operator },
|
103
|
+
suggest: [
|
104
|
+
{
|
105
|
+
messageId: "suggestNegatedExpression",
|
106
|
+
data: { operator },
|
107
|
+
fix(fixer) {
|
108
|
+
const negationToken = sourceCode.getFirstToken(node.left);
|
109
|
+
const fixRange = [negationToken.range[1], node.range[1]];
|
110
|
+
const text = sourceCode.text.slice(fixRange[0], fixRange[1]);
|
111
|
+
|
112
|
+
return fixer.replaceTextRange(fixRange, `(${text})`);
|
113
|
+
}
|
114
|
+
},
|
115
|
+
{
|
116
|
+
messageId: "suggestParenthesisedNegation",
|
117
|
+
fix(fixer) {
|
118
|
+
return fixer.replaceText(node.left, `(${sourceCode.getText(node.left)})`);
|
119
|
+
}
|
120
|
+
}
|
121
|
+
]
|
97
122
|
});
|
98
123
|
}
|
99
124
|
}
|
@@ -15,6 +15,12 @@ const astUtils = require("./utils/ast-utils");
|
|
15
15
|
// Helpers
|
16
16
|
//------------------------------------------------------------------------------
|
17
17
|
|
18
|
+
const radixMap = new Map([
|
19
|
+
[2, { system: "binary", literalPrefix: "0b" }],
|
20
|
+
[8, { system: "octal", literalPrefix: "0o" }],
|
21
|
+
[16, { system: "hexadecimal", literalPrefix: "0x" }]
|
22
|
+
]);
|
23
|
+
|
18
24
|
/**
|
19
25
|
* Checks to see if a CallExpression's callee node is `parseInt` or
|
20
26
|
* `Number.parseInt`.
|
@@ -54,49 +60,44 @@ module.exports = {
|
|
54
60
|
},
|
55
61
|
|
56
62
|
schema: [],
|
63
|
+
|
64
|
+
messages: {
|
65
|
+
useLiteral: "Use {{system}} literals instead of {{functionName}}()."
|
66
|
+
},
|
67
|
+
|
57
68
|
fixable: "code"
|
58
69
|
},
|
59
70
|
|
60
71
|
create(context) {
|
61
72
|
const sourceCode = context.getSourceCode();
|
62
73
|
|
63
|
-
const radixMap = {
|
64
|
-
2: "binary",
|
65
|
-
8: "octal",
|
66
|
-
16: "hexadecimal"
|
67
|
-
};
|
68
|
-
|
69
|
-
const prefixMap = {
|
70
|
-
2: "0b",
|
71
|
-
8: "0o",
|
72
|
-
16: "0x"
|
73
|
-
};
|
74
|
-
|
75
74
|
//----------------------------------------------------------------------
|
76
75
|
// Public
|
77
76
|
//----------------------------------------------------------------------
|
78
77
|
|
79
78
|
return {
|
80
79
|
|
81
|
-
CallExpression(node) {
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
80
|
+
"CallExpression[arguments.length=2]"(node) {
|
81
|
+
const [strNode, radixNode] = node.arguments,
|
82
|
+
str = strNode.value,
|
83
|
+
radix = radixNode.value;
|
84
|
+
|
85
|
+
if (
|
86
|
+
strNode.type === "Literal" &&
|
87
|
+
radixNode.type === "Literal" &&
|
88
|
+
typeof str === "string" &&
|
89
|
+
typeof radix === "number" &&
|
90
|
+
radixMap.has(radix) &&
|
91
|
+
isParseInt(node.callee)
|
92
|
+
) {
|
87
93
|
|
88
|
-
|
89
|
-
const radixName = radixMap[node.arguments[1].value];
|
94
|
+
const { system, literalPrefix } = radixMap.get(radix);
|
90
95
|
|
91
|
-
if (isParseInt(node.callee) &&
|
92
|
-
radixName &&
|
93
|
-
node.arguments[0].type === "Literal"
|
94
|
-
) {
|
95
96
|
context.report({
|
96
97
|
node,
|
97
|
-
|
98
|
+
messageId: "useLiteral",
|
98
99
|
data: {
|
99
|
-
|
100
|
+
system,
|
100
101
|
functionName: sourceCode.getText(node.callee)
|
101
102
|
},
|
102
103
|
fix(fixer) {
|
@@ -104,9 +105,9 @@ module.exports = {
|
|
104
105
|
return null;
|
105
106
|
}
|
106
107
|
|
107
|
-
const replacement = `${
|
108
|
+
const replacement = `${literalPrefix}${str}`;
|
108
109
|
|
109
|
-
if (+replacement !== parseInt(
|
110
|
+
if (+replacement !== parseInt(str, radix)) {
|
110
111
|
|
111
112
|
/*
|
112
113
|
* If the newly-produced literal would be invalid, (e.g. 0b1234),
|
@@ -162,7 +162,7 @@ module.exports = {
|
|
162
162
|
docs: {
|
163
163
|
description: "disallow assignments that can lead to race conditions due to usage of `await` or `yield`",
|
164
164
|
category: "Possible Errors",
|
165
|
-
recommended:
|
165
|
+
recommended: false,
|
166
166
|
url: "https://eslint.org/docs/rules/require-atomic-updates"
|
167
167
|
},
|
168
168
|
|