eslint 7.0.0-alpha.1 → 7.0.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 +329 -0
- package/README.md +7 -7
- package/bin/eslint.js +115 -77
- package/conf/category-list.json +2 -3
- package/conf/environments.js +2 -1
- package/conf/eslint-recommended.js +3 -0
- package/lib/api.js +2 -0
- package/lib/cli-engine/cascading-config-array-factory.js +16 -2
- package/lib/cli-engine/cli-engine.js +53 -47
- package/lib/cli-engine/config-array/config-array.js +30 -1
- package/lib/cli-engine/config-array/ignore-pattern.js +7 -1
- package/lib/cli-engine/config-array-factory.js +244 -235
- package/lib/cli.js +181 -95
- package/lib/eslint/eslint.js +656 -0
- package/lib/eslint/index.js +7 -0
- package/lib/init/autoconfig.js +4 -4
- package/lib/init/config-file.js +2 -2
- package/lib/init/config-initializer.js +2 -4
- package/lib/init/source-code-utils.js +2 -2
- package/lib/linter/node-event-generator.js +2 -2
- package/lib/options.js +0 -1
- package/lib/rule-tester/rule-tester.js +178 -23
- package/lib/rules/accessor-pairs.js +2 -2
- package/lib/rules/array-callback-return.js +3 -18
- package/lib/rules/arrow-body-style.js +26 -15
- package/lib/rules/callback-return.js +4 -0
- package/lib/rules/camelcase.js +38 -1
- package/lib/rules/comma-style.js +3 -8
- package/lib/rules/computed-property-spacing.js +2 -2
- package/lib/rules/curly.js +124 -40
- package/lib/rules/func-call-spacing.js +4 -3
- package/lib/rules/func-names.js +31 -24
- package/lib/rules/getter-return.js +2 -12
- package/lib/rules/global-require.js +4 -0
- package/lib/rules/handle-callback-err.js +4 -0
- package/lib/rules/id-blacklist.js +140 -64
- package/lib/rules/id-length.js +14 -4
- package/lib/rules/indent-legacy.js +0 -16
- package/lib/rules/key-spacing.js +1 -1
- package/lib/rules/new-cap.js +1 -1
- package/lib/rules/newline-per-chained-call.js +6 -3
- package/lib/rules/no-alert.js +5 -3
- package/lib/rules/no-buffer-constructor.js +4 -0
- package/lib/rules/no-dupe-else-if.js +1 -1
- package/lib/rules/no-empty-function.js +4 -2
- package/lib/rules/no-eval.js +3 -2
- package/lib/rules/no-extra-bind.js +1 -1
- package/lib/rules/no-extra-boolean-cast.js +168 -38
- package/lib/rules/no-extra-parens.js +9 -5
- package/lib/rules/no-implied-eval.js +83 -101
- package/lib/rules/no-import-assign.js +1 -1
- package/lib/rules/no-inner-declarations.js +31 -39
- package/lib/rules/no-lone-blocks.js +1 -1
- package/lib/rules/no-magic-numbers.js +72 -37
- package/lib/rules/no-mixed-requires.js +4 -0
- package/lib/rules/no-new-object.js +15 -3
- package/lib/rules/no-new-require.js +4 -0
- package/lib/rules/no-new-wrappers.js +1 -1
- package/lib/rules/no-obj-calls.js +24 -5
- package/lib/rules/no-path-concat.js +4 -0
- package/lib/rules/no-plusplus.js +39 -3
- package/lib/rules/no-process-env.js +4 -0
- package/lib/rules/no-process-exit.js +4 -0
- package/lib/rules/no-prototype-builtins.js +1 -1
- package/lib/rules/no-restricted-modules.js +52 -18
- package/lib/rules/no-setter-return.js +1 -1
- package/lib/rules/no-sync.js +4 -0
- package/lib/rules/no-underscore-dangle.js +1 -1
- package/lib/rules/no-unexpected-multiline.js +22 -12
- package/lib/rules/no-useless-concat.js +1 -1
- package/lib/rules/operator-assignment.js +3 -3
- package/lib/rules/operator-linebreak.js +4 -16
- package/lib/rules/prefer-numeric-literals.js +3 -3
- package/lib/rules/prefer-object-spread.js +2 -2
- package/lib/rules/require-await.js +1 -1
- package/lib/rules/space-before-function-paren.js +5 -2
- package/lib/rules/template-curly-spacing.js +59 -42
- package/lib/rules/utils/ast-utils.js +65 -4
- package/lib/rules/wrap-iife.js +54 -17
- package/lib/rules/yoda.js +101 -51
- package/lib/shared/relative-module-resolver.js +1 -0
- package/lib/shared/types.js +9 -2
- package/messages/plugin-conflict.txt +7 -0
- package/package.json +27 -26
package/lib/init/config-file.js
CHANGED
@@ -45,7 +45,7 @@ function sortByKey(a, b) {
|
|
45
45
|
function writeJSONConfigFile(config, filePath) {
|
46
46
|
debug(`Writing JSON config file: ${filePath}`);
|
47
47
|
|
48
|
-
const content = stringify(config, { cmp: sortByKey, space: 4 })
|
48
|
+
const content = `${stringify(config, { cmp: sortByKey, space: 4 })}\n`;
|
49
49
|
|
50
50
|
fs.writeFileSync(filePath, content, "utf8");
|
51
51
|
}
|
@@ -80,7 +80,7 @@ function writeJSConfigFile(config, filePath) {
|
|
80
80
|
debug(`Writing JS config file: ${filePath}`);
|
81
81
|
|
82
82
|
let contentToWrite;
|
83
|
-
const stringifiedContent = `module.exports = ${stringify(config, { cmp: sortByKey, space: 4 })}
|
83
|
+
const stringifiedContent = `module.exports = ${stringify(config, { cmp: sortByKey, space: 4 })};\n`;
|
84
84
|
|
85
85
|
try {
|
86
86
|
const { CLIEngine } = require("../cli-engine");
|
@@ -15,6 +15,7 @@ const util = require("util"),
|
|
15
15
|
inquirer = require("inquirer"),
|
16
16
|
ProgressBar = require("progress"),
|
17
17
|
semver = require("semver"),
|
18
|
+
espree = require("espree"),
|
18
19
|
recConfig = require("../../conf/eslint-recommended"),
|
19
20
|
ConfigOps = require("../shared/config-ops"),
|
20
21
|
log = require("../shared/logging"),
|
@@ -31,8 +32,6 @@ const debug = require("debug")("eslint:config-initializer");
|
|
31
32
|
// Private
|
32
33
|
//------------------------------------------------------------------------------
|
33
34
|
|
34
|
-
const DEFAULT_ECMA_VERSION = 2018;
|
35
|
-
|
36
35
|
/* istanbul ignore next: hard to test fs function */
|
37
36
|
/**
|
38
37
|
* Create .eslintrc file in the current working directory
|
@@ -265,8 +264,7 @@ function processAnswers(answers) {
|
|
265
264
|
extends: []
|
266
265
|
};
|
267
266
|
|
268
|
-
|
269
|
-
config.parserOptions.ecmaVersion = DEFAULT_ECMA_VERSION;
|
267
|
+
config.parserOptions.ecmaVersion = espree.latestEcmaVersion;
|
270
268
|
config.env.es6 = true;
|
271
269
|
config.globals = {
|
272
270
|
Atomics: "readonly",
|
@@ -23,7 +23,7 @@ const { CLIEngine } = require("../cli-engine");
|
|
23
23
|
* TODO1: Expose the API that enumerates target files.
|
24
24
|
* TODO2: Extract the creation logic of `SourceCode` from `Linter` class.
|
25
25
|
*/
|
26
|
-
const { getCLIEngineInternalSlots } = require("../cli-engine/cli-engine"); // eslint-disable-line no-restricted-
|
26
|
+
const { getCLIEngineInternalSlots } = require("../cli-engine/cli-engine"); // eslint-disable-line node/no-restricted-require
|
27
27
|
|
28
28
|
const debug = require("debug")("eslint:source-code-utils");
|
29
29
|
|
@@ -97,7 +97,7 @@ function getSourceCodeOfFiles(patterns, options, callback) {
|
|
97
97
|
sourceCodes[filename] = sourceCode;
|
98
98
|
}
|
99
99
|
if (callback) {
|
100
|
-
callback(filenames.length); // eslint-disable-line callback-return
|
100
|
+
callback(filenames.length); // eslint-disable-line node/callback-return
|
101
101
|
}
|
102
102
|
});
|
103
103
|
|
@@ -159,8 +159,8 @@ function tryParseSelector(rawSelector) {
|
|
159
159
|
try {
|
160
160
|
return esquery.parse(rawSelector.replace(/:exit$/u, ""));
|
161
161
|
} catch (err) {
|
162
|
-
if (typeof err.offset === "number") {
|
163
|
-
throw new SyntaxError(`Syntax error in selector "${rawSelector}" at position ${err.offset}: ${err.message}`);
|
162
|
+
if (err.location && err.location.start && typeof err.location.start.offset === "number") {
|
163
|
+
throw new SyntaxError(`Syntax error in selector "${rawSelector}" at position ${err.location.start.offset}: ${err.message}`);
|
164
164
|
}
|
165
165
|
throw err;
|
166
166
|
}
|
package/lib/options.js
CHANGED
@@ -45,16 +45,20 @@ const
|
|
45
45
|
path = require("path"),
|
46
46
|
util = require("util"),
|
47
47
|
lodash = require("lodash"),
|
48
|
+
Traverser = require("../../lib/shared/traverser"),
|
48
49
|
{ getRuleOptionsSchema, validate } = require("../shared/config-validator"),
|
49
50
|
{ Linter, SourceCodeFixer, interpolate } = require("../linter");
|
50
51
|
|
51
52
|
const ajv = require("../shared/ajv")({ strictDefaults: true });
|
52
53
|
|
54
|
+
const espreePath = require.resolve("espree");
|
53
55
|
|
54
56
|
//------------------------------------------------------------------------------
|
55
57
|
// Typedefs
|
56
58
|
//------------------------------------------------------------------------------
|
57
59
|
|
60
|
+
/** @typedef {import("../shared/types").Parser} Parser */
|
61
|
+
|
58
62
|
/**
|
59
63
|
* A test case that is expected to pass lint.
|
60
64
|
* @typedef {Object} ValidTestCase
|
@@ -119,6 +123,33 @@ const RuleTesterParameters = [
|
|
119
123
|
"output"
|
120
124
|
];
|
121
125
|
|
126
|
+
/*
|
127
|
+
* All allowed property names in error objects.
|
128
|
+
*/
|
129
|
+
const errorObjectParameters = new Set([
|
130
|
+
"message",
|
131
|
+
"messageId",
|
132
|
+
"data",
|
133
|
+
"type",
|
134
|
+
"line",
|
135
|
+
"column",
|
136
|
+
"endLine",
|
137
|
+
"endColumn",
|
138
|
+
"suggestions"
|
139
|
+
]);
|
140
|
+
const friendlyErrorObjectParameterList = `[${[...errorObjectParameters].map(key => `'${key}'`).join(", ")}]`;
|
141
|
+
|
142
|
+
/*
|
143
|
+
* All allowed property names in suggestion objects.
|
144
|
+
*/
|
145
|
+
const suggestionObjectParameters = new Set([
|
146
|
+
"desc",
|
147
|
+
"messageId",
|
148
|
+
"data",
|
149
|
+
"output"
|
150
|
+
]);
|
151
|
+
const friendlySuggestionObjectParameterList = `[${[...suggestionObjectParameters].map(key => `'${key}'`).join(", ")}]`;
|
152
|
+
|
122
153
|
const hasOwnProperty = Function.call.bind(Object.hasOwnProperty);
|
123
154
|
|
124
155
|
/**
|
@@ -179,6 +210,70 @@ function sanitize(text) {
|
|
179
210
|
);
|
180
211
|
}
|
181
212
|
|
213
|
+
/**
|
214
|
+
* Define `start`/`end` properties as throwing error.
|
215
|
+
* @param {string} objName Object name used for error messages.
|
216
|
+
* @param {ASTNode} node The node to define.
|
217
|
+
* @returns {void}
|
218
|
+
*/
|
219
|
+
function defineStartEndAsError(objName, node) {
|
220
|
+
Object.defineProperties(node, {
|
221
|
+
start: {
|
222
|
+
get() {
|
223
|
+
throw new Error(`Use ${objName}.range[0] instead of ${objName}.start`);
|
224
|
+
},
|
225
|
+
configurable: true,
|
226
|
+
enumerable: false
|
227
|
+
},
|
228
|
+
end: {
|
229
|
+
get() {
|
230
|
+
throw new Error(`Use ${objName}.range[1] instead of ${objName}.end`);
|
231
|
+
},
|
232
|
+
configurable: true,
|
233
|
+
enumerable: false
|
234
|
+
}
|
235
|
+
});
|
236
|
+
}
|
237
|
+
|
238
|
+
/**
|
239
|
+
* Define `start`/`end` properties of all nodes of the given AST as throwing error.
|
240
|
+
* @param {ASTNode} ast The root node to errorize `start`/`end` properties.
|
241
|
+
* @param {Object} [visitorKeys] Visitor keys to be used for traversing the given ast.
|
242
|
+
* @returns {void}
|
243
|
+
*/
|
244
|
+
function defineStartEndAsErrorInTree(ast, visitorKeys) {
|
245
|
+
Traverser.traverse(ast, { visitorKeys, enter: defineStartEndAsError.bind(null, "node") });
|
246
|
+
ast.tokens.forEach(defineStartEndAsError.bind(null, "token"));
|
247
|
+
ast.comments.forEach(defineStartEndAsError.bind(null, "token"));
|
248
|
+
}
|
249
|
+
|
250
|
+
/**
|
251
|
+
* Wraps the given parser in order to intercept and modify return values from the `parse` and `parseForESLint` methods, for test purposes.
|
252
|
+
* In particular, to modify ast nodes, tokens and comments to throw on access to their `start` and `end` properties.
|
253
|
+
* @param {Parser} parser Parser object.
|
254
|
+
* @returns {Parser} Wrapped parser object.
|
255
|
+
*/
|
256
|
+
function wrapParser(parser) {
|
257
|
+
if (typeof parser.parseForESLint === "function") {
|
258
|
+
return {
|
259
|
+
parseForESLint(...args) {
|
260
|
+
const ret = parser.parseForESLint(...args);
|
261
|
+
|
262
|
+
defineStartEndAsErrorInTree(ret.ast, ret.visitorKeys);
|
263
|
+
return ret;
|
264
|
+
}
|
265
|
+
};
|
266
|
+
}
|
267
|
+
return {
|
268
|
+
parse(...args) {
|
269
|
+
const ast = parser.parse(...args);
|
270
|
+
|
271
|
+
defineStartEndAsErrorInTree(ast);
|
272
|
+
return ast;
|
273
|
+
}
|
274
|
+
};
|
275
|
+
}
|
276
|
+
|
182
277
|
//------------------------------------------------------------------------------
|
183
278
|
// Public Interface
|
184
279
|
//------------------------------------------------------------------------------
|
@@ -423,9 +518,12 @@ class RuleTester {
|
|
423
518
|
|
424
519
|
if (typeof config.parser === "string") {
|
425
520
|
assert(path.isAbsolute(config.parser), "Parsers provided as strings to RuleTester must be absolute paths");
|
426
|
-
|
521
|
+
} else {
|
522
|
+
config.parser = espreePath;
|
427
523
|
}
|
428
524
|
|
525
|
+
linter.defineParser(config.parser, wrapParser(require(config.parser)));
|
526
|
+
|
429
527
|
if (schema) {
|
430
528
|
ajv.validateSchema(schema);
|
431
529
|
|
@@ -456,20 +554,21 @@ class RuleTester {
|
|
456
554
|
|
457
555
|
// Verify the code.
|
458
556
|
const messages = linter.verify(code, config, filename);
|
557
|
+
const fatalErrorMessage = messages.find(m => m.fatal);
|
459
558
|
|
460
|
-
|
461
|
-
if (typeof item.errors !== "number") {
|
462
|
-
const errorMessage = messages.find(m => m.fatal);
|
463
|
-
|
464
|
-
assert(!errorMessage, `A fatal parsing error occurred: ${errorMessage && errorMessage.message}`);
|
465
|
-
}
|
559
|
+
assert(!fatalErrorMessage, `A fatal parsing error occurred: ${fatalErrorMessage && fatalErrorMessage.message}`);
|
466
560
|
|
467
561
|
// Verify if autofix makes a syntax error or not.
|
468
562
|
if (messages.some(m => m.fix)) {
|
469
563
|
output = SourceCodeFixer.applyFixes(code, messages).output;
|
470
564
|
const errorMessageInFix = linter.verify(output, config, filename).find(m => m.fatal);
|
471
565
|
|
472
|
-
assert(!errorMessageInFix,
|
566
|
+
assert(!errorMessageInFix, [
|
567
|
+
"A fatal parsing error occurred in autofix.",
|
568
|
+
`Error: ${errorMessageInFix && errorMessageInFix.message}`,
|
569
|
+
"Autofix output:",
|
570
|
+
output
|
571
|
+
].join("\n"));
|
473
572
|
} else {
|
474
573
|
output = code;
|
475
574
|
}
|
@@ -545,10 +644,12 @@ class RuleTester {
|
|
545
644
|
assert.ok(item.errors || item.errors === 0,
|
546
645
|
`Did not specify errors for an invalid test of ${ruleName}`);
|
547
646
|
|
647
|
+
const ruleHasMetaMessages = hasOwnProperty(rule, "meta") && hasOwnProperty(rule.meta, "messages");
|
648
|
+
const friendlyIDList = ruleHasMetaMessages ? `[${Object.keys(rule.meta.messages).map(key => `'${key}'`).join(", ")}]` : null;
|
649
|
+
|
548
650
|
const result = runRuleForItem(item);
|
549
651
|
const messages = result.messages;
|
550
652
|
|
551
|
-
|
552
653
|
if (typeof item.errors === "number") {
|
553
654
|
assert.strictEqual(messages.length, item.errors, util.format("Should have %d error%s but had %d: %s",
|
554
655
|
item.errors, item.errors === 1 ? "" : "s", messages.length, util.inspect(messages)));
|
@@ -573,25 +674,31 @@ class RuleTester {
|
|
573
674
|
|
574
675
|
// Just an error message.
|
575
676
|
assertMessageMatches(message.message, error);
|
576
|
-
} else if (typeof error === "object") {
|
677
|
+
} else if (typeof error === "object" && error !== null) {
|
577
678
|
|
578
679
|
/*
|
579
680
|
* Error object.
|
580
681
|
* This may have a message, messageId, data, node type, line, and/or
|
581
682
|
* column.
|
582
683
|
*/
|
684
|
+
|
685
|
+
Object.keys(error).forEach(propertyName => {
|
686
|
+
assert.ok(
|
687
|
+
errorObjectParameters.has(propertyName),
|
688
|
+
`Invalid error property name '${propertyName}'. Expected one of ${friendlyErrorObjectParameterList}.`
|
689
|
+
);
|
690
|
+
});
|
691
|
+
|
583
692
|
if (hasOwnProperty(error, "message")) {
|
584
693
|
assert.ok(!hasOwnProperty(error, "messageId"), "Error should not specify both 'message' and a 'messageId'.");
|
585
694
|
assert.ok(!hasOwnProperty(error, "data"), "Error should not specify both 'data' and 'message'.");
|
586
695
|
assertMessageMatches(message.message, error.message);
|
587
696
|
} else if (hasOwnProperty(error, "messageId")) {
|
588
697
|
assert.ok(
|
589
|
-
|
698
|
+
ruleHasMetaMessages,
|
590
699
|
"Error can not use 'messageId' if rule under test doesn't define 'meta.messages'."
|
591
700
|
);
|
592
701
|
if (!hasOwnProperty(rule.meta.messages, error.messageId)) {
|
593
|
-
const friendlyIDList = `[${Object.keys(rule.meta.messages).map(key => `'${key}'`).join(", ")}]`;
|
594
|
-
|
595
702
|
assert(false, `Invalid messageId '${error.messageId}'. Expected one of ${friendlyIDList}.`);
|
596
703
|
}
|
597
704
|
assert.strictEqual(
|
@@ -654,20 +761,62 @@ class RuleTester {
|
|
654
761
|
assert.strictEqual(message.suggestions.length, error.suggestions.length, `Error should have ${error.suggestions.length} suggestions. Instead found ${message.suggestions.length} suggestions`);
|
655
762
|
|
656
763
|
error.suggestions.forEach((expectedSuggestion, index) => {
|
764
|
+
assert.ok(
|
765
|
+
typeof expectedSuggestion === "object" && expectedSuggestion !== null,
|
766
|
+
"Test suggestion in 'suggestions' array must be an object."
|
767
|
+
);
|
768
|
+
Object.keys(expectedSuggestion).forEach(propertyName => {
|
769
|
+
assert.ok(
|
770
|
+
suggestionObjectParameters.has(propertyName),
|
771
|
+
`Invalid suggestion property name '${propertyName}'. Expected one of ${friendlySuggestionObjectParameterList}.`
|
772
|
+
);
|
773
|
+
});
|
774
|
+
|
657
775
|
const actualSuggestion = message.suggestions[index];
|
776
|
+
const suggestionPrefix = `Error Suggestion at index ${index} :`;
|
777
|
+
|
778
|
+
if (hasOwnProperty(expectedSuggestion, "desc")) {
|
779
|
+
assert.ok(
|
780
|
+
!hasOwnProperty(expectedSuggestion, "data"),
|
781
|
+
`${suggestionPrefix} Test should not specify both 'desc' and 'data'.`
|
782
|
+
);
|
783
|
+
assert.strictEqual(
|
784
|
+
actualSuggestion.desc,
|
785
|
+
expectedSuggestion.desc,
|
786
|
+
`${suggestionPrefix} desc should be "${expectedSuggestion.desc}" but got "${actualSuggestion.desc}" instead.`
|
787
|
+
);
|
788
|
+
}
|
658
789
|
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
790
|
+
if (hasOwnProperty(expectedSuggestion, "messageId")) {
|
791
|
+
assert.ok(
|
792
|
+
ruleHasMetaMessages,
|
793
|
+
`${suggestionPrefix} Test can not use 'messageId' if rule under test doesn't define 'meta.messages'.`
|
794
|
+
);
|
795
|
+
assert.ok(
|
796
|
+
hasOwnProperty(rule.meta.messages, expectedSuggestion.messageId),
|
797
|
+
`${suggestionPrefix} Test has invalid messageId '${expectedSuggestion.messageId}', the rule under test allows only one of ${friendlyIDList}.`
|
798
|
+
);
|
799
|
+
assert.strictEqual(
|
800
|
+
actualSuggestion.messageId,
|
801
|
+
expectedSuggestion.messageId,
|
802
|
+
`${suggestionPrefix} messageId should be '${expectedSuggestion.messageId}' but got '${actualSuggestion.messageId}' instead.`
|
803
|
+
);
|
804
|
+
if (hasOwnProperty(expectedSuggestion, "data")) {
|
805
|
+
const unformattedMetaMessage = rule.meta.messages[expectedSuggestion.messageId];
|
806
|
+
const rehydratedDesc = interpolate(unformattedMetaMessage, expectedSuggestion.data);
|
807
|
+
|
808
|
+
assert.strictEqual(
|
809
|
+
actualSuggestion.desc,
|
810
|
+
rehydratedDesc,
|
811
|
+
`${suggestionPrefix} Hydrated test desc "${rehydratedDesc}" does not match received desc "${actualSuggestion.desc}".`
|
812
|
+
);
|
667
813
|
}
|
814
|
+
} else {
|
815
|
+
assert.ok(
|
816
|
+
!hasOwnProperty(expectedSuggestion, "data"),
|
817
|
+
`${suggestionPrefix} Test must specify 'messageId' if 'data' is used.`
|
818
|
+
);
|
668
819
|
}
|
669
|
-
assertSuggestionKeyEquals("desc");
|
670
|
-
assertSuggestionKeyEquals("messageId");
|
671
820
|
|
672
821
|
if (hasOwnProperty(expectedSuggestion, "output")) {
|
673
822
|
const codeWithAppliedSuggestion = SourceCodeFixer.applyFixes(item.code, [actualSuggestion]).output;
|
@@ -695,6 +844,12 @@ class RuleTester {
|
|
695
844
|
} else {
|
696
845
|
assert.strictEqual(result.output, item.output, "Output is incorrect.");
|
697
846
|
}
|
847
|
+
} else {
|
848
|
+
assert.strictEqual(
|
849
|
+
result.output,
|
850
|
+
item.code,
|
851
|
+
"The rule fixed the code. Please add 'output' property."
|
852
|
+
);
|
698
853
|
}
|
699
854
|
|
700
855
|
assertASTDidntChange(result.beforeAST, result.afterAST);
|
@@ -171,7 +171,7 @@ module.exports = {
|
|
171
171
|
},
|
172
172
|
enforceForClassMembers: {
|
173
173
|
type: "boolean",
|
174
|
-
default:
|
174
|
+
default: true
|
175
175
|
}
|
176
176
|
},
|
177
177
|
additionalProperties: false
|
@@ -190,7 +190,7 @@ module.exports = {
|
|
190
190
|
const config = context.options[0] || {};
|
191
191
|
const checkGetWithoutSet = config.getWithoutSet === true;
|
192
192
|
const checkSetWithoutGet = config.setWithoutGet !== false;
|
193
|
-
const enforceForClassMembers = config.enforceForClassMembers
|
193
|
+
const enforceForClassMembers = config.enforceForClassMembers !== false;
|
194
194
|
const sourceCode = context.getSourceCode();
|
195
195
|
|
196
196
|
/**
|
@@ -29,22 +29,6 @@ function isReachable(segment) {
|
|
29
29
|
return segment.reachable;
|
30
30
|
}
|
31
31
|
|
32
|
-
/**
|
33
|
-
* Gets a readable location.
|
34
|
-
*
|
35
|
-
* - FunctionExpression -> the function name or `function` keyword.
|
36
|
-
* - ArrowFunctionExpression -> `=>` token.
|
37
|
-
* @param {ASTNode} node A function node to get.
|
38
|
-
* @param {SourceCode} sourceCode A source code to get tokens.
|
39
|
-
* @returns {ASTNode|Token} The node or the token of a location.
|
40
|
-
*/
|
41
|
-
function getLocation(node, sourceCode) {
|
42
|
-
if (node.type === "ArrowFunctionExpression") {
|
43
|
-
return sourceCode.getTokenBefore(node.body);
|
44
|
-
}
|
45
|
-
return node.id || node;
|
46
|
-
}
|
47
|
-
|
48
32
|
/**
|
49
33
|
* Checks a given node is a MemberExpression node which has the specified name's
|
50
34
|
* property.
|
@@ -179,6 +163,7 @@ module.exports = {
|
|
179
163
|
create(context) {
|
180
164
|
|
181
165
|
const options = context.options[0] || { allowImplicit: false, checkForEach: false };
|
166
|
+
const sourceCode = context.getSourceCode();
|
182
167
|
|
183
168
|
let funcInfo = {
|
184
169
|
arrayMethodName: null,
|
@@ -217,12 +202,12 @@ module.exports = {
|
|
217
202
|
}
|
218
203
|
|
219
204
|
if (messageId) {
|
220
|
-
let name = astUtils.getFunctionNameWithKind(
|
205
|
+
let name = astUtils.getFunctionNameWithKind(node);
|
221
206
|
|
222
207
|
name = messageId === "expectedNoReturnValue" ? lodash.upperFirst(name) : name;
|
223
208
|
context.report({
|
224
209
|
node,
|
225
|
-
loc:
|
210
|
+
loc: astUtils.getFunctionHeadLoc(node, sourceCode),
|
226
211
|
messageId,
|
227
212
|
data: { name }
|
228
213
|
});
|
@@ -91,7 +91,7 @@ module.exports = {
|
|
91
91
|
* @returns {Token} The found closing parenthesis token.
|
92
92
|
*/
|
93
93
|
function findClosingParen(token) {
|
94
|
-
let node = sourceCode.getNodeByRangeIndex(token.range[
|
94
|
+
let node = sourceCode.getNodeByRangeIndex(token.range[0]);
|
95
95
|
|
96
96
|
while (!astUtils.isParenthesised(sourceCode, node)) {
|
97
97
|
node = node.parent;
|
@@ -206,24 +206,35 @@ module.exports = {
|
|
206
206
|
fix(fixer) {
|
207
207
|
const fixes = [];
|
208
208
|
const arrowToken = sourceCode.getTokenBefore(arrowBody, astUtils.isArrowToken);
|
209
|
-
const
|
210
|
-
const
|
209
|
+
const [firstTokenAfterArrow, secondTokenAfterArrow] = sourceCode.getTokensAfter(arrowToken, { count: 2 });
|
210
|
+
const lastToken = sourceCode.getLastToken(node);
|
211
211
|
const isParenthesisedObjectLiteral =
|
212
|
-
astUtils.isOpeningParenToken(
|
213
|
-
astUtils.isOpeningBraceToken(
|
214
|
-
|
215
|
-
// Wrap the value by a block and a return statement.
|
216
|
-
fixes.push(
|
217
|
-
fixer.insertTextBefore(firstBodyToken, "{return "),
|
218
|
-
fixer.insertTextAfter(lastBodyToken, "}")
|
219
|
-
);
|
212
|
+
astUtils.isOpeningParenToken(firstTokenAfterArrow) &&
|
213
|
+
astUtils.isOpeningBraceToken(secondTokenAfterArrow);
|
220
214
|
|
221
215
|
// If the value is object literal, remove parentheses which were forced by syntax.
|
222
216
|
if (isParenthesisedObjectLiteral) {
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
)
|
217
|
+
const openingParenToken = firstTokenAfterArrow;
|
218
|
+
const openingBraceToken = secondTokenAfterArrow;
|
219
|
+
|
220
|
+
if (astUtils.isTokenOnSameLine(openingParenToken, openingBraceToken)) {
|
221
|
+
fixes.push(fixer.replaceText(openingParenToken, "{return "));
|
222
|
+
} else {
|
223
|
+
|
224
|
+
// Avoid ASI
|
225
|
+
fixes.push(
|
226
|
+
fixer.replaceText(openingParenToken, "{"),
|
227
|
+
fixer.insertTextBefore(openingBraceToken, "return ")
|
228
|
+
);
|
229
|
+
}
|
230
|
+
|
231
|
+
// Closing paren for the object doesn't have to be lastToken, e.g.: () => ({}).foo()
|
232
|
+
fixes.push(fixer.remove(findClosingParen(openingBraceToken)));
|
233
|
+
fixes.push(fixer.insertTextAfter(lastToken, "}"));
|
234
|
+
|
235
|
+
} else {
|
236
|
+
fixes.push(fixer.insertTextBefore(firstTokenAfterArrow, "{return "));
|
237
|
+
fixes.push(fixer.insertTextAfter(lastToken, "}"));
|
227
238
|
}
|
228
239
|
|
229
240
|
return fixes;
|
package/lib/rules/camelcase.js
CHANGED
@@ -125,6 +125,40 @@ module.exports = {
|
|
125
125
|
return false;
|
126
126
|
}
|
127
127
|
|
128
|
+
/**
|
129
|
+
* Checks whether the given node represents assignment target property in destructuring.
|
130
|
+
*
|
131
|
+
* For examples:
|
132
|
+
* ({a: b.foo} = c); // => true for `foo`
|
133
|
+
* ([a.foo] = b); // => true for `foo`
|
134
|
+
* ([a.foo = 1] = b); // => true for `foo`
|
135
|
+
* ({...a.foo} = b); // => true for `foo`
|
136
|
+
* @param {ASTNode} node An Identifier node to check
|
137
|
+
* @returns {boolean} True if the node is an assignment target property in destructuring.
|
138
|
+
*/
|
139
|
+
function isAssignmentTargetPropertyInDestructuring(node) {
|
140
|
+
if (
|
141
|
+
node.parent.type === "MemberExpression" &&
|
142
|
+
node.parent.property === node &&
|
143
|
+
!node.parent.computed
|
144
|
+
) {
|
145
|
+
const effectiveParent = node.parent.parent;
|
146
|
+
|
147
|
+
return (
|
148
|
+
effectiveParent.type === "Property" &&
|
149
|
+
effectiveParent.value === node.parent &&
|
150
|
+
effectiveParent.parent.type === "ObjectPattern" ||
|
151
|
+
effectiveParent.type === "ArrayPattern" ||
|
152
|
+
effectiveParent.type === "RestElement" ||
|
153
|
+
(
|
154
|
+
effectiveParent.type === "AssignmentPattern" &&
|
155
|
+
effectiveParent.left === node.parent
|
156
|
+
)
|
157
|
+
);
|
158
|
+
}
|
159
|
+
return false;
|
160
|
+
}
|
161
|
+
|
128
162
|
/**
|
129
163
|
* Reports an AST node as a rule violation.
|
130
164
|
* @param {ASTNode} node The node to report.
|
@@ -170,6 +204,9 @@ module.exports = {
|
|
170
204
|
// Report AssignmentExpressions only if they are the left side of the assignment
|
171
205
|
} else if (effectiveParent.type === "AssignmentExpression" && nameIsUnderscored && (effectiveParent.right.type !== "MemberExpression" || effectiveParent.left.type === "MemberExpression" && effectiveParent.left.property.name === node.name)) {
|
172
206
|
report(node);
|
207
|
+
|
208
|
+
} else if (isAssignmentTargetPropertyInDestructuring(node) && nameIsUnderscored) {
|
209
|
+
report(node);
|
173
210
|
}
|
174
211
|
|
175
212
|
/*
|
@@ -186,7 +223,7 @@ module.exports = {
|
|
186
223
|
|
187
224
|
const assignmentKeyEqualsValue = node.parent.key.name === node.parent.value.name;
|
188
225
|
|
189
|
-
if (
|
226
|
+
if (nameIsUnderscored && node.parent.computed) {
|
190
227
|
report(node);
|
191
228
|
}
|
192
229
|
|
package/lib/rules/comma-style.js
CHANGED
@@ -146,10 +146,7 @@ module.exports = {
|
|
146
146
|
// lone comma
|
147
147
|
context.report({
|
148
148
|
node: reportItem,
|
149
|
-
loc:
|
150
|
-
line: commaToken.loc.end.line,
|
151
|
-
column: commaToken.loc.start.column
|
152
|
-
},
|
149
|
+
loc: commaToken.loc,
|
153
150
|
messageId: "unexpectedLineBeforeAndAfterComma",
|
154
151
|
fix: getFixerFunction(styleType, previousItemToken, commaToken, currentItemToken)
|
155
152
|
});
|
@@ -158,6 +155,7 @@ module.exports = {
|
|
158
155
|
|
159
156
|
context.report({
|
160
157
|
node: reportItem,
|
158
|
+
loc: commaToken.loc,
|
161
159
|
messageId: "expectedCommaFirst",
|
162
160
|
fix: getFixerFunction(style, previousItemToken, commaToken, currentItemToken)
|
163
161
|
});
|
@@ -166,10 +164,7 @@ module.exports = {
|
|
166
164
|
|
167
165
|
context.report({
|
168
166
|
node: reportItem,
|
169
|
-
loc:
|
170
|
-
line: commaToken.loc.end.line,
|
171
|
-
column: commaToken.loc.end.column
|
172
|
-
},
|
167
|
+
loc: commaToken.loc,
|
173
168
|
messageId: "expectedCommaLast",
|
174
169
|
fix: getFixerFunction(style, previousItemToken, commaToken, currentItemToken)
|
175
170
|
});
|
@@ -32,7 +32,7 @@ module.exports = {
|
|
32
32
|
properties: {
|
33
33
|
enforceForClassMembers: {
|
34
34
|
type: "boolean",
|
35
|
-
default:
|
35
|
+
default: true
|
36
36
|
}
|
37
37
|
},
|
38
38
|
additionalProperties: false
|
@@ -51,7 +51,7 @@ module.exports = {
|
|
51
51
|
create(context) {
|
52
52
|
const sourceCode = context.getSourceCode();
|
53
53
|
const propertyNameMustBeSpaced = context.options[0] === "always"; // default is "never"
|
54
|
-
const enforceForClassMembers = context.options[1]
|
54
|
+
const enforceForClassMembers = !context.options[1] || context.options[1].enforceForClassMembers;
|
55
55
|
|
56
56
|
//--------------------------------------------------------------------------
|
57
57
|
// Helpers
|