eslint 6.0.0-rc.0 → 6.2.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 +90 -0
- package/README.md +4 -13
- package/bin/eslint.js +4 -1
- package/conf/config-schema.js +1 -0
- package/conf/environments.js +72 -15
- package/lib/cli-engine/cascading-config-array-factory.js +15 -3
- package/lib/cli-engine/cli-engine.js +13 -3
- package/lib/cli-engine/config-array/config-array.js +8 -2
- package/lib/cli-engine/config-array/extracted-config.js +16 -1
- package/lib/cli-engine/config-array-factory.js +9 -6
- package/lib/cli-engine/file-enumerator.js +5 -13
- package/lib/init/config-initializer.js +19 -9
- package/lib/init/npm-utils.js +2 -2
- package/lib/linter/code-path-analysis/code-path-analyzer.js +1 -0
- package/lib/linter/linter.js +49 -16
- package/lib/rule-tester/rule-tester.js +15 -3
- package/lib/rules/accessor-pairs.js +195 -35
- package/lib/rules/arrow-body-style.js +2 -2
- package/lib/rules/class-methods-use-this.js +10 -3
- package/lib/rules/dot-location.js +21 -17
- package/lib/rules/dot-notation.js +6 -2
- package/lib/rules/func-call-spacing.js +30 -20
- package/lib/rules/func-names.js +4 -0
- package/lib/rules/function-call-argument-newline.js +120 -0
- package/lib/rules/function-paren-newline.js +34 -22
- package/lib/rules/indent.js +13 -2
- package/lib/rules/index.js +1 -0
- package/lib/rules/max-len.js +7 -0
- package/lib/rules/multiline-comment-style.js +2 -1
- package/lib/rules/new-cap.js +2 -1
- package/lib/rules/no-dupe-keys.js +1 -1
- package/lib/rules/no-duplicate-case.js +10 -8
- package/lib/rules/no-else-return.js +127 -0
- package/lib/rules/no-extra-bind.js +1 -0
- package/lib/rules/no-extra-boolean-cast.js +44 -5
- package/lib/rules/no-extra-parens.js +295 -39
- package/lib/rules/no-mixed-operators.js +48 -13
- package/lib/rules/no-param-reassign.js +12 -1
- package/lib/rules/no-restricted-syntax.js +2 -2
- package/lib/rules/no-unused-vars.js +1 -1
- package/lib/rules/no-var.js +14 -1
- package/lib/rules/prefer-const.js +9 -3
- package/lib/rules/prefer-template.js +1 -10
- package/lib/rules/require-atomic-updates.js +63 -84
- package/lib/rules/sort-keys.js +11 -3
- package/lib/rules/utils/ast-utils.js +45 -3
- package/lib/rules/yoda.js +1 -1
- package/lib/{cli-engine → shared}/naming.js +0 -0
- package/lib/shared/types.js +2 -0
- package/messages/extend-config-missing.txt +2 -0
- package/messages/print-config-with-directory-path.txt +2 -0
- package/package.json +27 -30
package/lib/linter/linter.js
CHANGED
@@ -198,14 +198,20 @@ function createMissingRuleMessage(ruleId) {
|
|
198
198
|
/**
|
199
199
|
* creates a linting problem
|
200
200
|
* @param {Object} options to create linting error
|
201
|
-
* @param {string} options.ruleId the ruleId to report
|
202
|
-
* @param {Object} options.loc the loc to report
|
203
|
-
* @param {string} options.message the error message to report
|
204
|
-
* @
|
201
|
+
* @param {string} [options.ruleId] the ruleId to report
|
202
|
+
* @param {Object} [options.loc] the loc to report
|
203
|
+
* @param {string} [options.message] the error message to report
|
204
|
+
* @param {string} [options.severity] the error message to report
|
205
|
+
* @returns {LintMessage} created problem, returns a missing-rule problem if only provided ruleId.
|
205
206
|
* @private
|
206
207
|
*/
|
207
208
|
function createLintingProblem(options) {
|
208
|
-
const {
|
209
|
+
const {
|
210
|
+
ruleId = null,
|
211
|
+
loc = DEFAULT_ERROR_LOC,
|
212
|
+
message = createMissingRuleMessage(options.ruleId),
|
213
|
+
severity = 2
|
214
|
+
} = options;
|
209
215
|
|
210
216
|
return {
|
211
217
|
ruleId,
|
@@ -214,7 +220,7 @@ function createLintingProblem(options) {
|
|
214
220
|
column: loc.start.column + 1,
|
215
221
|
endLine: loc.end.line,
|
216
222
|
endColumn: loc.end.column + 1,
|
217
|
-
severity
|
223
|
+
severity,
|
218
224
|
nodeType: null
|
219
225
|
};
|
220
226
|
}
|
@@ -257,28 +263,42 @@ function createDisableDirectives(options) {
|
|
257
263
|
* @param {string} filename The file being checked.
|
258
264
|
* @param {ASTNode} ast The top node of the AST.
|
259
265
|
* @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
|
266
|
+
* @param {string|null} warnInlineConfig If a string then it should warn directive comments as disabled. The string value is the config name what the setting came from.
|
260
267
|
* @returns {{configuredRules: Object, enabledGlobals: {value:string,comment:Token}[], exportedVariables: Object, problems: Problem[], disableDirectives: DisableDirective[]}}
|
261
268
|
* A collection of the directive comments that were found, along with any problems that occurred when parsing
|
262
269
|
*/
|
263
|
-
function getDirectiveComments(filename, ast, ruleMapper) {
|
270
|
+
function getDirectiveComments(filename, ast, ruleMapper, warnInlineConfig) {
|
264
271
|
const configuredRules = {};
|
265
|
-
const enabledGlobals =
|
272
|
+
const enabledGlobals = Object.create(null);
|
266
273
|
const exportedVariables = {};
|
267
274
|
const problems = [];
|
268
275
|
const disableDirectives = [];
|
269
276
|
|
270
277
|
ast.comments.filter(token => token.type !== "Shebang").forEach(comment => {
|
271
278
|
const trimmedCommentText = comment.value.trim();
|
272
|
-
const match = /^(eslint(
|
279
|
+
const match = /^(eslint(?:-env|-enable|-disable(?:(?:-next)?-line)?)?|exported|globals?)(?:\s|$)/u.exec(trimmedCommentText);
|
273
280
|
|
274
281
|
if (!match) {
|
275
282
|
return;
|
276
283
|
}
|
284
|
+
const lineCommentSupported = /^eslint-disable-(next-)?line$/u.test(match[1]);
|
285
|
+
|
286
|
+
if (warnInlineConfig && (lineCommentSupported || comment.type === "Block")) {
|
287
|
+
const kind = comment.type === "Block" ? `/*${match[1]}*/` : `//${match[1]}`;
|
288
|
+
|
289
|
+
problems.push(createLintingProblem({
|
290
|
+
ruleId: null,
|
291
|
+
message: `'${kind}' has no effect because you have 'noInlineConfig' setting in ${warnInlineConfig}.`,
|
292
|
+
loc: comment.loc,
|
293
|
+
severity: 1
|
294
|
+
}));
|
295
|
+
return;
|
296
|
+
}
|
277
297
|
|
278
298
|
const directiveValue = trimmedCommentText.slice(match.index + match[1].length);
|
279
299
|
let directiveType = "";
|
280
300
|
|
281
|
-
if (
|
301
|
+
if (lineCommentSupported) {
|
282
302
|
if (comment.loc.start.line === comment.loc.end.line) {
|
283
303
|
directiveType = match[1].slice("eslint-".length);
|
284
304
|
} else {
|
@@ -441,16 +461,27 @@ function normalizeFilename(filename) {
|
|
441
461
|
return index === -1 ? filename : parts.slice(index).join(path.sep);
|
442
462
|
}
|
443
463
|
|
464
|
+
// eslint-disable-next-line valid-jsdoc
|
444
465
|
/**
|
445
466
|
* Normalizes the possible options for `linter.verify` and `linter.verifyAndFix` to a
|
446
467
|
* consistent shape.
|
447
468
|
* @param {VerifyOptions} providedOptions Options
|
448
|
-
* @
|
469
|
+
* @param {ConfigData} config Config.
|
470
|
+
* @returns {Required<VerifyOptions> & { warnInlineConfig: string|null }} Normalized options
|
449
471
|
*/
|
450
|
-
function normalizeVerifyOptions(providedOptions) {
|
472
|
+
function normalizeVerifyOptions(providedOptions, config) {
|
473
|
+
const disableInlineConfig = config.noInlineConfig === true;
|
474
|
+
const ignoreInlineConfig = providedOptions.allowInlineConfig === false;
|
475
|
+
const configNameOfNoInlineConfig = config.configNameOfNoInlineConfig
|
476
|
+
? ` (${config.configNameOfNoInlineConfig})`
|
477
|
+
: "";
|
478
|
+
|
451
479
|
return {
|
452
480
|
filename: normalizeFilename(providedOptions.filename || "<input>"),
|
453
|
-
allowInlineConfig:
|
481
|
+
allowInlineConfig: !ignoreInlineConfig,
|
482
|
+
warnInlineConfig: disableInlineConfig && !ignoreInlineConfig
|
483
|
+
? `your config${configNameOfNoInlineConfig}`
|
484
|
+
: null,
|
454
485
|
reportUnusedDisableDirectives: Boolean(providedOptions.reportUnusedDisableDirectives),
|
455
486
|
disableFixes: Boolean(providedOptions.disableFixes)
|
456
487
|
};
|
@@ -984,7 +1015,7 @@ class Linter {
|
|
984
1015
|
_verifyWithoutProcessors(textOrSourceCode, providedConfig, providedOptions) {
|
985
1016
|
const slots = internalSlotsMap.get(this);
|
986
1017
|
const config = providedConfig || {};
|
987
|
-
const options = normalizeVerifyOptions(providedOptions);
|
1018
|
+
const options = normalizeVerifyOptions(providedOptions, config);
|
988
1019
|
let text;
|
989
1020
|
|
990
1021
|
// evaluate arguments
|
@@ -1019,7 +1050,9 @@ class Linter {
|
|
1019
1050
|
}
|
1020
1051
|
|
1021
1052
|
// search and apply "eslint-env *".
|
1022
|
-
const envInFile =
|
1053
|
+
const envInFile = options.allowInlineConfig && !options.warnInlineConfig
|
1054
|
+
? findEslintEnv(text)
|
1055
|
+
: {};
|
1023
1056
|
const resolvedEnvConfig = Object.assign({ builtin: true }, config.env, envInFile);
|
1024
1057
|
const enabledEnvs = Object.keys(resolvedEnvConfig)
|
1025
1058
|
.filter(envName => resolvedEnvConfig[envName])
|
@@ -1062,7 +1095,7 @@ class Linter {
|
|
1062
1095
|
|
1063
1096
|
const sourceCode = slots.lastSourceCode;
|
1064
1097
|
const commentDirectives = options.allowInlineConfig
|
1065
|
-
? getDirectiveComments(options.filename, sourceCode.ast, ruleId => getRule(slots, ruleId))
|
1098
|
+
? getDirectiveComments(options.filename, sourceCode.ast, ruleId => getRule(slots, ruleId), options.warnInlineConfig)
|
1066
1099
|
: { configuredRules: {}, enabledGlobals: {}, exportedVariables: {}, problems: [], disableDirectives: [] };
|
1067
1100
|
|
1068
1101
|
// augment global scope with declared global variables
|
@@ -123,6 +123,18 @@ function freezeDeeply(x) {
|
|
123
123
|
}
|
124
124
|
}
|
125
125
|
|
126
|
+
/**
|
127
|
+
* Replace control characters by `\u00xx` form.
|
128
|
+
* @param {string} text The text to sanitize.
|
129
|
+
* @returns {string} The sanitized text.
|
130
|
+
*/
|
131
|
+
function sanitize(text) {
|
132
|
+
return text.replace(
|
133
|
+
/[\u0000-\u001f]/gu, // eslint-disable-line no-control-regex
|
134
|
+
c => `\\u${c.codePointAt(0).toString(16).padStart(4, "0")}`
|
135
|
+
);
|
136
|
+
}
|
137
|
+
|
126
138
|
//------------------------------------------------------------------------------
|
127
139
|
// Public Interface
|
128
140
|
//------------------------------------------------------------------------------
|
@@ -537,8 +549,8 @@ class RuleTester {
|
|
537
549
|
assert(false, `Invalid messageId '${error.messageId}'. Expected one of ${friendlyIDList}.`);
|
538
550
|
}
|
539
551
|
assert.strictEqual(
|
540
|
-
error.messageId,
|
541
552
|
message.messageId,
|
553
|
+
error.messageId,
|
542
554
|
`messageId '${message.messageId}' does not match expected messageId '${error.messageId}'.`
|
543
555
|
);
|
544
556
|
if (hasOwnProperty(error, "data")) {
|
@@ -613,7 +625,7 @@ class RuleTester {
|
|
613
625
|
RuleTester.describe(ruleName, () => {
|
614
626
|
RuleTester.describe("valid", () => {
|
615
627
|
test.valid.forEach(valid => {
|
616
|
-
RuleTester.it(typeof valid === "object" ? valid.code : valid, () => {
|
628
|
+
RuleTester.it(sanitize(typeof valid === "object" ? valid.code : valid), () => {
|
617
629
|
testValidTemplate(valid);
|
618
630
|
});
|
619
631
|
});
|
@@ -621,7 +633,7 @@ class RuleTester {
|
|
621
633
|
|
622
634
|
RuleTester.describe("invalid", () => {
|
623
635
|
test.invalid.forEach(invalid => {
|
624
|
-
RuleTester.it(invalid.code, () => {
|
636
|
+
RuleTester.it(sanitize(invalid.code), () => {
|
625
637
|
testInvalidTemplate(invalid);
|
626
638
|
});
|
627
639
|
});
|
@@ -5,10 +5,87 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Requirements
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
const astUtils = require("./utils/ast-utils");
|
13
|
+
|
14
|
+
//------------------------------------------------------------------------------
|
15
|
+
// Typedefs
|
16
|
+
//------------------------------------------------------------------------------
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Property name if it can be computed statically, otherwise the list of the tokens of the key node.
|
20
|
+
* @typedef {string|Token[]} Key
|
21
|
+
*/
|
22
|
+
|
23
|
+
/**
|
24
|
+
* Accessor nodes with the same key.
|
25
|
+
* @typedef {Object} AccessorData
|
26
|
+
* @property {Key} key Accessor's key
|
27
|
+
* @property {ASTNode[]} getters List of getter nodes.
|
28
|
+
* @property {ASTNode[]} setters List of setter nodes.
|
29
|
+
*/
|
30
|
+
|
8
31
|
//------------------------------------------------------------------------------
|
9
32
|
// Helpers
|
10
33
|
//------------------------------------------------------------------------------
|
11
34
|
|
35
|
+
/**
|
36
|
+
* Checks whether or not the given lists represent the equal tokens in the same order.
|
37
|
+
* Tokens are compared by their properties, not by instance.
|
38
|
+
* @param {Token[]} left First list of tokens.
|
39
|
+
* @param {Token[]} right Second list of tokens.
|
40
|
+
* @returns {boolean} `true` if the lists have same tokens.
|
41
|
+
*/
|
42
|
+
function areEqualTokenLists(left, right) {
|
43
|
+
if (left.length !== right.length) {
|
44
|
+
return false;
|
45
|
+
}
|
46
|
+
|
47
|
+
for (let i = 0; i < left.length; i++) {
|
48
|
+
const leftToken = left[i],
|
49
|
+
rightToken = right[i];
|
50
|
+
|
51
|
+
if (leftToken.type !== rightToken.type || leftToken.value !== rightToken.value) {
|
52
|
+
return false;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
return true;
|
57
|
+
}
|
58
|
+
|
59
|
+
/**
|
60
|
+
* Checks whether or not the given keys are equal.
|
61
|
+
* @param {Key} left First key.
|
62
|
+
* @param {Key} right Second key.
|
63
|
+
* @returns {boolean} `true` if the keys are equal.
|
64
|
+
*/
|
65
|
+
function areEqualKeys(left, right) {
|
66
|
+
if (typeof left === "string" && typeof right === "string") {
|
67
|
+
|
68
|
+
// Statically computed names.
|
69
|
+
return left === right;
|
70
|
+
}
|
71
|
+
if (Array.isArray(left) && Array.isArray(right)) {
|
72
|
+
|
73
|
+
// Token lists.
|
74
|
+
return areEqualTokenLists(left, right);
|
75
|
+
}
|
76
|
+
|
77
|
+
return false;
|
78
|
+
}
|
79
|
+
|
80
|
+
/**
|
81
|
+
* Checks whether or not a given node is of an accessor kind ('get' or 'set').
|
82
|
+
* @param {ASTNode} node - A node to check.
|
83
|
+
* @returns {boolean} `true` if the node is of an accessor kind.
|
84
|
+
*/
|
85
|
+
function isAccessorKind(node) {
|
86
|
+
return node.kind === "get" || node.kind === "set";
|
87
|
+
}
|
88
|
+
|
12
89
|
/**
|
13
90
|
* Checks whether or not a given node is an `Identifier` node which was named a given name.
|
14
91
|
* @param {ASTNode} node - A node to check.
|
@@ -97,69 +174,152 @@ module.exports = {
|
|
97
174
|
}],
|
98
175
|
|
99
176
|
messages: {
|
100
|
-
|
101
|
-
|
177
|
+
missingGetterInPropertyDescriptor: "Getter is not present in property descriptor.",
|
178
|
+
missingSetterInPropertyDescriptor: "Setter is not present in property descriptor.",
|
179
|
+
missingGetterInObjectLiteral: "Getter is not present for {{ name }}.",
|
180
|
+
missingSetterInObjectLiteral: "Setter is not present for {{ name }}."
|
102
181
|
}
|
103
182
|
},
|
104
183
|
create(context) {
|
105
184
|
const config = context.options[0] || {};
|
106
185
|
const checkGetWithoutSet = config.getWithoutSet === true;
|
107
186
|
const checkSetWithoutGet = config.setWithoutGet !== false;
|
187
|
+
const sourceCode = context.getSourceCode();
|
108
188
|
|
109
189
|
/**
|
110
|
-
*
|
111
|
-
* @param {ASTNode} node The node to
|
190
|
+
* Reports the given node.
|
191
|
+
* @param {ASTNode} node The node to report.
|
192
|
+
* @param {string} messageKind "missingGetter" or "missingSetter".
|
112
193
|
* @returns {void}
|
113
194
|
* @private
|
114
195
|
*/
|
115
|
-
function
|
116
|
-
|
117
|
-
|
118
|
-
|
196
|
+
function report(node, messageKind) {
|
197
|
+
if (node.type === "Property") {
|
198
|
+
context.report({
|
199
|
+
node,
|
200
|
+
messageId: `${messageKind}InObjectLiteral`,
|
201
|
+
loc: astUtils.getFunctionHeadLoc(node.value, sourceCode),
|
202
|
+
data: { name: astUtils.getFunctionNameWithKind(node.value) }
|
203
|
+
});
|
204
|
+
} else {
|
205
|
+
context.report({
|
206
|
+
node,
|
207
|
+
messageId: `${messageKind}InPropertyDescriptor`
|
208
|
+
});
|
209
|
+
}
|
210
|
+
}
|
119
211
|
|
120
|
-
|
121
|
-
|
212
|
+
/**
|
213
|
+
* Reports each of the nodes in the given list using the same messageId.
|
214
|
+
* @param {ASTNode[]} nodes Nodes to report.
|
215
|
+
* @param {string} messageKind "missingGetter" or "missingSetter".
|
216
|
+
* @returns {void}
|
217
|
+
* @private
|
218
|
+
*/
|
219
|
+
function reportList(nodes, messageKind) {
|
220
|
+
for (const node of nodes) {
|
221
|
+
report(node, messageKind);
|
222
|
+
}
|
223
|
+
}
|
122
224
|
|
123
|
-
|
225
|
+
/**
|
226
|
+
* Creates a new `AccessorData` object for the given getter or setter node.
|
227
|
+
* @param {ASTNode} node A getter or setter node.
|
228
|
+
* @returns {AccessorData} New `AccessorData` object that contains the given node.
|
229
|
+
* @private
|
230
|
+
*/
|
231
|
+
function createAccessorData(node) {
|
232
|
+
const name = astUtils.getStaticPropertyName(node);
|
233
|
+
const key = (name !== null) ? name : sourceCode.getTokens(node.key);
|
124
234
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
}
|
235
|
+
return {
|
236
|
+
key,
|
237
|
+
getters: node.kind === "get" ? [node] : [],
|
238
|
+
setters: node.kind === "set" ? [node] : []
|
239
|
+
};
|
240
|
+
}
|
132
241
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
242
|
+
/**
|
243
|
+
* Merges the given `AccessorData` object into the given accessors list.
|
244
|
+
* @param {AccessorData[]} accessors The list to merge into.
|
245
|
+
* @param {AccessorData} accessorData The object to merge.
|
246
|
+
* @returns {AccessorData[]} The same instance with the merged object.
|
247
|
+
* @private
|
248
|
+
*/
|
249
|
+
function mergeAccessorData(accessors, accessorData) {
|
250
|
+
const equalKeyElement = accessors.find(a => areEqualKeys(a.key, accessorData.key));
|
137
251
|
|
138
|
-
|
139
|
-
|
140
|
-
|
252
|
+
if (equalKeyElement) {
|
253
|
+
equalKeyElement.getters.push(...accessorData.getters);
|
254
|
+
equalKeyElement.setters.push(...accessorData.setters);
|
255
|
+
} else {
|
256
|
+
accessors.push(accessorData);
|
257
|
+
}
|
141
258
|
|
142
|
-
|
259
|
+
return accessors;
|
260
|
+
}
|
143
261
|
|
144
|
-
|
145
|
-
|
262
|
+
/**
|
263
|
+
* Checks accessor pairs in the given list of nodes.
|
264
|
+
* @param {ASTNode[]} nodes The list to check.
|
265
|
+
* @returns {void}
|
266
|
+
* @private
|
267
|
+
*/
|
268
|
+
function checkList(nodes) {
|
269
|
+
const accessors = nodes
|
270
|
+
.filter(isAccessorKind)
|
271
|
+
.map(createAccessorData)
|
272
|
+
.reduce(mergeAccessorData, []);
|
146
273
|
|
147
|
-
|
148
|
-
|
274
|
+
for (const { getters, setters } of accessors) {
|
275
|
+
if (checkSetWithoutGet && setters.length && !getters.length) {
|
276
|
+
reportList(setters, "missingGetter");
|
277
|
+
}
|
278
|
+
if (checkGetWithoutSet && getters.length && !setters.length) {
|
279
|
+
reportList(getters, "missingSetter");
|
149
280
|
}
|
150
281
|
}
|
282
|
+
}
|
151
283
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
284
|
+
/**
|
285
|
+
* Checks accessor pairs in an object literal.
|
286
|
+
* @param {ASTNode} node `ObjectExpression` node to check.
|
287
|
+
* @returns {void}
|
288
|
+
* @private
|
289
|
+
*/
|
290
|
+
function checkObjectLiteral(node) {
|
291
|
+
checkList(node.properties.filter(p => p.type === "Property"));
|
292
|
+
}
|
293
|
+
|
294
|
+
/**
|
295
|
+
* Checks accessor pairs in a property descriptor.
|
296
|
+
* @param {ASTNode} node Property descriptor `ObjectExpression` node to check.
|
297
|
+
* @returns {void}
|
298
|
+
* @private
|
299
|
+
*/
|
300
|
+
function checkPropertyDescriptor(node) {
|
301
|
+
const namesToCheck = node.properties
|
302
|
+
.filter(p => p.type === "Property" && p.kind === "init" && !p.computed)
|
303
|
+
.map(({ key }) => key.name);
|
304
|
+
|
305
|
+
const hasGetter = namesToCheck.includes("get");
|
306
|
+
const hasSetter = namesToCheck.includes("set");
|
307
|
+
|
308
|
+
if (checkSetWithoutGet && hasSetter && !hasGetter) {
|
309
|
+
report(node, "missingGetter");
|
310
|
+
}
|
311
|
+
if (checkGetWithoutSet && hasGetter && !hasSetter) {
|
312
|
+
report(node, "missingSetter");
|
156
313
|
}
|
157
314
|
}
|
158
315
|
|
159
316
|
return {
|
160
317
|
ObjectExpression(node) {
|
161
318
|
if (checkSetWithoutGet || checkGetWithoutSet) {
|
162
|
-
|
319
|
+
checkObjectLiteral(node);
|
320
|
+
if (isPropertyDescriptor(node)) {
|
321
|
+
checkPropertyDescriptor(node);
|
322
|
+
}
|
163
323
|
}
|
164
324
|
}
|
165
325
|
};
|
@@ -175,10 +175,10 @@ module.exports = {
|
|
175
175
|
}
|
176
176
|
|
177
177
|
/*
|
178
|
-
* If the first token of the reutrn value is `{
|
178
|
+
* If the first token of the reutrn value is `{` or the return value is a sequence expression,
|
179
179
|
* enclose the return value by parentheses to avoid syntax error.
|
180
180
|
*/
|
181
|
-
if (astUtils.isOpeningBraceToken(firstValueToken)) {
|
181
|
+
if (astUtils.isOpeningBraceToken(firstValueToken) || blockBody[0].argument.type === "SequenceExpression") {
|
182
182
|
fixes.push(
|
183
183
|
fixer.insertTextBefore(firstValueToken, "("),
|
184
184
|
fixer.insertTextAfter(lastValueToken, ")")
|
@@ -5,6 +5,12 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Requirements
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
const astUtils = require("./utils/ast-utils");
|
13
|
+
|
8
14
|
//------------------------------------------------------------------------------
|
9
15
|
// Rule Definition
|
10
16
|
//------------------------------------------------------------------------------
|
@@ -34,7 +40,7 @@ module.exports = {
|
|
34
40
|
}],
|
35
41
|
|
36
42
|
messages: {
|
37
|
-
missingThis: "Expected 'this' to be used by class
|
43
|
+
missingThis: "Expected 'this' to be used by class {{name}}."
|
38
44
|
}
|
39
45
|
},
|
40
46
|
create(context) {
|
@@ -70,7 +76,8 @@ module.exports = {
|
|
70
76
|
* @private
|
71
77
|
*/
|
72
78
|
function isIncludedInstanceMethod(node) {
|
73
|
-
return isInstanceMethod(node) &&
|
79
|
+
return isInstanceMethod(node) &&
|
80
|
+
(node.computed || !exceptMethods.has(node.key.name));
|
74
81
|
}
|
75
82
|
|
76
83
|
/**
|
@@ -89,7 +96,7 @@ module.exports = {
|
|
89
96
|
node,
|
90
97
|
messageId: "missingThis",
|
91
98
|
data: {
|
92
|
-
name: node
|
99
|
+
name: astUtils.getFunctionNameWithKind(node)
|
93
100
|
}
|
94
101
|
});
|
95
102
|
}
|
@@ -54,29 +54,31 @@ module.exports = {
|
|
54
54
|
*/
|
55
55
|
function checkDotLocation(obj, prop, node) {
|
56
56
|
const dot = sourceCode.getTokenBefore(prop);
|
57
|
-
|
57
|
+
|
58
|
+
// `obj` expression can be parenthesized, but those paren tokens are not a part of the `obj` node.
|
59
|
+
const tokenBeforeDot = sourceCode.getTokenBefore(dot);
|
60
|
+
|
61
|
+
const textBeforeDot = sourceCode.getText().slice(tokenBeforeDot.range[1], dot.range[0]);
|
58
62
|
const textAfterDot = sourceCode.getText().slice(dot.range[1], prop.range[0]);
|
59
63
|
|
60
|
-
if (
|
61
|
-
if (
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
context.report({
|
66
|
-
node,
|
67
|
-
loc: dot.loc.start,
|
68
|
-
messageId: "expectedDotAfterObject",
|
69
|
-
fix: fixer => fixer.replaceTextRange([obj.range[1], prop.range[0]], `${neededTextAfterObj}.${textBeforeDot}${textAfterDot}`)
|
70
|
-
});
|
71
|
-
}
|
72
|
-
} else if (!astUtils.isTokenOnSameLine(dot, prop)) {
|
64
|
+
if (onObject) {
|
65
|
+
if (!astUtils.isTokenOnSameLine(tokenBeforeDot, dot)) {
|
66
|
+
const neededTextAfterToken = astUtils.isDecimalIntegerNumericToken(tokenBeforeDot) ? " " : "";
|
67
|
+
|
73
68
|
context.report({
|
74
69
|
node,
|
75
70
|
loc: dot.loc.start,
|
76
|
-
messageId: "
|
77
|
-
fix: fixer => fixer.replaceTextRange([
|
71
|
+
messageId: "expectedDotAfterObject",
|
72
|
+
fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], prop.range[0]], `${neededTextAfterToken}.${textBeforeDot}${textAfterDot}`)
|
78
73
|
});
|
79
74
|
}
|
75
|
+
} else if (!astUtils.isTokenOnSameLine(dot, prop)) {
|
76
|
+
context.report({
|
77
|
+
node,
|
78
|
+
loc: dot.loc.start,
|
79
|
+
messageId: "expectedDotBeforeProperty",
|
80
|
+
fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], prop.range[0]], `${textBeforeDot}${textAfterDot}.`)
|
81
|
+
});
|
80
82
|
}
|
81
83
|
}
|
82
84
|
|
@@ -86,7 +88,9 @@ module.exports = {
|
|
86
88
|
* @returns {void}
|
87
89
|
*/
|
88
90
|
function checkNode(node) {
|
89
|
-
|
91
|
+
if (!node.computed) {
|
92
|
+
checkDotLocation(node.object, node.property, node);
|
93
|
+
}
|
90
94
|
}
|
91
95
|
|
92
96
|
return {
|
@@ -9,13 +9,16 @@
|
|
9
9
|
//------------------------------------------------------------------------------
|
10
10
|
|
11
11
|
const astUtils = require("./utils/ast-utils");
|
12
|
+
const keywords = require("./utils/keywords");
|
12
13
|
|
13
14
|
//------------------------------------------------------------------------------
|
14
15
|
// Rule Definition
|
15
16
|
//------------------------------------------------------------------------------
|
16
17
|
|
17
18
|
const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/u;
|
18
|
-
|
19
|
+
|
20
|
+
// `null` literal must be handled separately.
|
21
|
+
const literalTypesToCheck = new Set(["string", "boolean"]);
|
19
22
|
|
20
23
|
module.exports = {
|
21
24
|
meta: {
|
@@ -115,7 +118,8 @@ module.exports = {
|
|
115
118
|
MemberExpression(node) {
|
116
119
|
if (
|
117
120
|
node.computed &&
|
118
|
-
node.property.type === "Literal"
|
121
|
+
node.property.type === "Literal" &&
|
122
|
+
(literalTypesToCheck.has(typeof node.property.value) || astUtils.isNullLiteral(node.property))
|
119
123
|
) {
|
120
124
|
checkComputedProperty(node, node.property.value);
|
121
125
|
}
|