eslint 7.0.0-alpha.2 → 7.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 +332 -0
- package/README.md +9 -10
- package/bin/eslint.js +115 -77
- package/conf/category-list.json +0 -1
- package/conf/environments.js +2 -1
- 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 +3 -4
- package/lib/init/source-code-utils.js +2 -2
- package/lib/linter/linter.js +2 -1
- package/lib/linter/node-event-generator.js +2 -2
- package/lib/options.js +0 -1
- package/lib/rule-tester/rule-tester.js +132 -22
- package/lib/rules/accessor-pairs.js +1 -1
- package/lib/rules/array-callback-return.js +3 -18
- package/lib/rules/arrow-parens.js +19 -3
- package/lib/rules/block-spacing.js +19 -2
- 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/func-call-spacing.js +4 -3
- 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 +138 -102
- package/lib/rules/index.js +1 -0
- package/lib/rules/key-spacing.js +1 -1
- package/lib/rules/linebreak-style.js +8 -2
- package/lib/rules/max-lines-per-function.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-empty-function.js +4 -2
- package/lib/rules/no-eval.js +2 -1
- package/lib/rules/no-extra-bind.js +1 -1
- package/lib/rules/no-extra-boolean-cast.js +102 -23
- package/lib/rules/no-extra-parens.js +9 -5
- package/lib/rules/no-implied-eval.js +83 -101
- package/lib/rules/no-inner-declarations.js +31 -39
- package/lib/rules/no-lone-blocks.js +1 -1
- package/lib/rules/no-loss-of-precision.js +198 -0
- package/lib/rules/no-magic-numbers.js +72 -37
- package/lib/rules/no-mixed-requires.js +4 -0
- package/lib/rules/no-new-func.js +22 -19
- package/lib/rules/no-new-object.js +15 -3
- package/lib/rules/no-new-require.js +4 -0
- package/lib/rules/no-new-symbol.js +2 -1
- 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 +4 -0
- package/lib/rules/no-sync.js +4 -0
- package/lib/rules/no-unexpected-multiline.js +22 -12
- package/lib/rules/no-useless-concat.js +1 -1
- package/lib/rules/one-var-declaration-per-line.js +1 -1
- package/lib/rules/operator-assignment.js +3 -3
- package/lib/rules/operator-linebreak.js +4 -16
- package/lib/rules/padded-blocks.js +17 -4
- 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/rest-spread-spacing.js +3 -6
- package/lib/rules/semi-spacing.js +32 -8
- 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 +116 -10
- package/lib/rules/yoda.js +101 -51
- package/lib/shared/relative-module-resolver.js +1 -0
- package/lib/shared/types.js +9 -2
- package/lib/source-code/source-code.js +1 -0
- package/messages/extend-config-missing.txt +1 -1
- package/messages/no-config-found.txt +1 -1
- package/messages/plugin-conflict.txt +7 -0
- package/messages/plugin-missing.txt +1 -1
- package/messages/whitespace-found.txt +1 -1
- 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",
|
@@ -328,6 +326,7 @@ function processAnswers(answers) {
|
|
328
326
|
}
|
329
327
|
if (answers.typescript && config.extends.includes("eslint:recommended")) {
|
330
328
|
config.extends.push("plugin:@typescript-eslint/eslint-recommended");
|
329
|
+
config.extends.push("plugin:@typescript-eslint/recommended");
|
331
330
|
}
|
332
331
|
|
333
332
|
// normalize extends
|
@@ -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
|
|
package/lib/linter/linter.js
CHANGED
@@ -938,7 +938,8 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parser
|
|
938
938
|
});
|
939
939
|
});
|
940
940
|
|
941
|
-
|
941
|
+
// only run code path analyzer if the top level node is "Program", skip otherwise
|
942
|
+
const eventGenerator = nodeQueue[0].node.type === "Program" ? new CodePathAnalyzer(new NodeEventGenerator(emitter)) : new NodeEventGenerator(emitter);
|
942
943
|
|
943
944
|
nodeQueue.forEach(traversalInfo => {
|
944
945
|
currentNode = traversalInfo.node;
|
@@ -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
|
@@ -141,6 +145,7 @@ const friendlyErrorObjectParameterList = `[${[...errorObjectParameters].map(key
|
|
141
145
|
const suggestionObjectParameters = new Set([
|
142
146
|
"desc",
|
143
147
|
"messageId",
|
148
|
+
"data",
|
144
149
|
"output"
|
145
150
|
]);
|
146
151
|
const friendlySuggestionObjectParameterList = `[${[...suggestionObjectParameters].map(key => `'${key}'`).join(", ")}]`;
|
@@ -205,6 +210,70 @@ function sanitize(text) {
|
|
205
210
|
);
|
206
211
|
}
|
207
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
|
+
|
208
277
|
//------------------------------------------------------------------------------
|
209
278
|
// Public Interface
|
210
279
|
//------------------------------------------------------------------------------
|
@@ -449,9 +518,12 @@ class RuleTester {
|
|
449
518
|
|
450
519
|
if (typeof config.parser === "string") {
|
451
520
|
assert(path.isAbsolute(config.parser), "Parsers provided as strings to RuleTester must be absolute paths");
|
452
|
-
|
521
|
+
} else {
|
522
|
+
config.parser = espreePath;
|
453
523
|
}
|
454
524
|
|
525
|
+
linter.defineParser(config.parser, wrapParser(require(config.parser)));
|
526
|
+
|
455
527
|
if (schema) {
|
456
528
|
ajv.validateSchema(schema);
|
457
529
|
|
@@ -482,20 +554,21 @@ class RuleTester {
|
|
482
554
|
|
483
555
|
// Verify the code.
|
484
556
|
const messages = linter.verify(code, config, filename);
|
557
|
+
const fatalErrorMessage = messages.find(m => m.fatal);
|
485
558
|
|
486
|
-
|
487
|
-
if (typeof item.errors !== "number") {
|
488
|
-
const errorMessage = messages.find(m => m.fatal);
|
489
|
-
|
490
|
-
assert(!errorMessage, `A fatal parsing error occurred: ${errorMessage && errorMessage.message}`);
|
491
|
-
}
|
559
|
+
assert(!fatalErrorMessage, `A fatal parsing error occurred: ${fatalErrorMessage && fatalErrorMessage.message}`);
|
492
560
|
|
493
561
|
// Verify if autofix makes a syntax error or not.
|
494
562
|
if (messages.some(m => m.fix)) {
|
495
563
|
output = SourceCodeFixer.applyFixes(code, messages).output;
|
496
564
|
const errorMessageInFix = linter.verify(output, config, filename).find(m => m.fatal);
|
497
565
|
|
498
|
-
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"));
|
499
572
|
} else {
|
500
573
|
output = code;
|
501
574
|
}
|
@@ -571,10 +644,12 @@ class RuleTester {
|
|
571
644
|
assert.ok(item.errors || item.errors === 0,
|
572
645
|
`Did not specify errors for an invalid test of ${ruleName}`);
|
573
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
|
+
|
574
650
|
const result = runRuleForItem(item);
|
575
651
|
const messages = result.messages;
|
576
652
|
|
577
|
-
|
578
653
|
if (typeof item.errors === "number") {
|
579
654
|
assert.strictEqual(messages.length, item.errors, util.format("Should have %d error%s but had %d: %s",
|
580
655
|
item.errors, item.errors === 1 ? "" : "s", messages.length, util.inspect(messages)));
|
@@ -620,12 +695,10 @@ class RuleTester {
|
|
620
695
|
assertMessageMatches(message.message, error.message);
|
621
696
|
} else if (hasOwnProperty(error, "messageId")) {
|
622
697
|
assert.ok(
|
623
|
-
|
698
|
+
ruleHasMetaMessages,
|
624
699
|
"Error can not use 'messageId' if rule under test doesn't define 'meta.messages'."
|
625
700
|
);
|
626
701
|
if (!hasOwnProperty(rule.meta.messages, error.messageId)) {
|
627
|
-
const friendlyIDList = `[${Object.keys(rule.meta.messages).map(key => `'${key}'`).join(", ")}]`;
|
628
|
-
|
629
702
|
assert(false, `Invalid messageId '${error.messageId}'. Expected one of ${friendlyIDList}.`);
|
630
703
|
}
|
631
704
|
assert.strictEqual(
|
@@ -700,19 +773,50 @@ class RuleTester {
|
|
700
773
|
});
|
701
774
|
|
702
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
|
+
}
|
703
789
|
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
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
|
+
);
|
712
813
|
}
|
814
|
+
} else {
|
815
|
+
assert.ok(
|
816
|
+
!hasOwnProperty(expectedSuggestion, "data"),
|
817
|
+
`${suggestionPrefix} Test must specify 'messageId' if 'data' is used.`
|
818
|
+
);
|
713
819
|
}
|
714
|
-
assertSuggestionKeyEquals("desc");
|
715
|
-
assertSuggestionKeyEquals("messageId");
|
716
820
|
|
717
821
|
if (hasOwnProperty(expectedSuggestion, "output")) {
|
718
822
|
const codeWithAppliedSuggestion = SourceCodeFixer.applyFixes(item.code, [actualSuggestion]).output;
|
@@ -740,6 +844,12 @@ class RuleTester {
|
|
740
844
|
} else {
|
741
845
|
assert.strictEqual(result.output, item.output, "Output is incorrect.");
|
742
846
|
}
|
847
|
+
} else {
|
848
|
+
assert.strictEqual(
|
849
|
+
result.output,
|
850
|
+
item.code,
|
851
|
+
"The rule fixed the code. Please add 'output' property."
|
852
|
+
);
|
743
853
|
}
|
744
854
|
|
745
855
|
assertASTDidntChange(result.beforeAST, result.afterAST);
|
@@ -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
|
});
|
@@ -105,10 +105,27 @@ module.exports = {
|
|
105
105
|
], `${shouldAddSpaceForAsync ? " " : ""}${paramToken.value}`);
|
106
106
|
}
|
107
107
|
|
108
|
+
/**
|
109
|
+
* Checks whether there are comments inside the params or not.
|
110
|
+
* @returns {boolean} `true` if there are comments inside of parens, else `false`
|
111
|
+
*/
|
112
|
+
function hasCommentsInParens() {
|
113
|
+
if (astUtils.isOpeningParenToken(firstTokenOfParam)) {
|
114
|
+
const closingParenToken = sourceCode.getTokenAfter(node.params[0], astUtils.isClosingParenToken);
|
115
|
+
|
116
|
+
return closingParenToken && sourceCode.commentsExistBetween(firstTokenOfParam, closingParenToken);
|
117
|
+
}
|
118
|
+
return false;
|
119
|
+
|
120
|
+
}
|
121
|
+
|
122
|
+
if (hasCommentsInParens()) {
|
123
|
+
return;
|
124
|
+
}
|
125
|
+
|
108
126
|
// "as-needed", { "requireForBlockBody": true }: x => x
|
109
127
|
if (
|
110
128
|
requireForBlockBody &&
|
111
|
-
node.params.length === 1 &&
|
112
129
|
node.params[0].type === "Identifier" &&
|
113
130
|
!node.params[0].typeAnnotation &&
|
114
131
|
node.body.type !== "BlockStatement" &&
|
@@ -144,7 +161,6 @@ module.exports = {
|
|
144
161
|
|
145
162
|
// "as-needed": x => x
|
146
163
|
if (asNeeded &&
|
147
|
-
node.params.length === 1 &&
|
148
164
|
node.params[0].type === "Identifier" &&
|
149
165
|
!node.params[0].typeAnnotation &&
|
150
166
|
!node.returnType
|
@@ -178,7 +194,7 @@ module.exports = {
|
|
178
194
|
}
|
179
195
|
|
180
196
|
return {
|
181
|
-
ArrowFunctionExpression: parens
|
197
|
+
"ArrowFunctionExpression[params.length=1]": parens
|
182
198
|
};
|
183
199
|
}
|
184
200
|
};
|
@@ -102,9 +102,18 @@ module.exports = {
|
|
102
102
|
|
103
103
|
// Check.
|
104
104
|
if (!isValid(openBrace, firstToken)) {
|
105
|
+
let loc = openBrace.loc;
|
106
|
+
|
107
|
+
if (messageId === "extra") {
|
108
|
+
loc = {
|
109
|
+
start: openBrace.loc.end,
|
110
|
+
end: firstToken.loc.start
|
111
|
+
};
|
112
|
+
}
|
113
|
+
|
105
114
|
context.report({
|
106
115
|
node,
|
107
|
-
loc
|
116
|
+
loc,
|
108
117
|
messageId,
|
109
118
|
data: {
|
110
119
|
location: "after",
|
@@ -120,9 +129,17 @@ module.exports = {
|
|
120
129
|
});
|
121
130
|
}
|
122
131
|
if (!isValid(lastToken, closeBrace)) {
|
132
|
+
let loc = closeBrace.loc;
|
133
|
+
|
134
|
+
if (messageId === "extra") {
|
135
|
+
loc = {
|
136
|
+
start: lastToken.loc.end,
|
137
|
+
end: closeBrace.loc.start
|
138
|
+
};
|
139
|
+
}
|
123
140
|
context.report({
|
124
141
|
node,
|
125
|
-
loc
|
142
|
+
loc,
|
126
143
|
messageId,
|
127
144
|
data: {
|
128
145
|
location: "before",
|
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
|
});
|
@@ -63,7 +63,8 @@ module.exports = {
|
|
63
63
|
},
|
64
64
|
|
65
65
|
messages: {
|
66
|
-
|
66
|
+
unexpectedWhitespace: "Unexpected whitespace between function name and paren.",
|
67
|
+
unexpectedNewline: "Unexpected newline between function name and paren.",
|
67
68
|
missing: "Missing space between function name and paren."
|
68
69
|
}
|
69
70
|
},
|
@@ -116,7 +117,7 @@ module.exports = {
|
|
116
117
|
context.report({
|
117
118
|
node,
|
118
119
|
loc: leftToken.loc.start,
|
119
|
-
messageId: "
|
120
|
+
messageId: "unexpectedWhitespace",
|
120
121
|
fix(fixer) {
|
121
122
|
|
122
123
|
/*
|
@@ -143,7 +144,7 @@ module.exports = {
|
|
143
144
|
context.report({
|
144
145
|
node,
|
145
146
|
loc: leftToken.loc.start,
|
146
|
-
messageId: "
|
147
|
+
messageId: "unexpectedNewline",
|
147
148
|
fix(fixer) {
|
148
149
|
return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " ");
|
149
150
|
}
|
@@ -25,17 +25,6 @@ function isReachable(segment) {
|
|
25
25
|
return segment.reachable;
|
26
26
|
}
|
27
27
|
|
28
|
-
/**
|
29
|
-
* Gets a readable location.
|
30
|
-
*
|
31
|
-
* - FunctionExpression -> the function name or `function` keyword.
|
32
|
-
* @param {ASTNode} node A function node to get.
|
33
|
-
* @returns {ASTNode|Token} The node or the token of a location.
|
34
|
-
*/
|
35
|
-
function getId(node) {
|
36
|
-
return node.id || node;
|
37
|
-
}
|
38
|
-
|
39
28
|
//------------------------------------------------------------------------------
|
40
29
|
// Rule Definition
|
41
30
|
//------------------------------------------------------------------------------
|
@@ -75,6 +64,7 @@ module.exports = {
|
|
75
64
|
create(context) {
|
76
65
|
|
77
66
|
const options = context.options[0] || { allowImplicit: false };
|
67
|
+
const sourceCode = context.getSourceCode();
|
78
68
|
|
79
69
|
let funcInfo = {
|
80
70
|
upper: null,
|
@@ -99,7 +89,7 @@ module.exports = {
|
|
99
89
|
) {
|
100
90
|
context.report({
|
101
91
|
node,
|
102
|
-
loc:
|
92
|
+
loc: astUtils.getFunctionHeadLoc(node, sourceCode),
|
103
93
|
messageId: funcInfo.hasReturn ? "expectedAlways" : "expected",
|
104
94
|
data: {
|
105
95
|
name: astUtils.getFunctionNameWithKind(funcInfo.node)
|