eslint 7.4.0 → 7.5.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 +28 -0
- package/README.md +3 -1
- package/lib/linter/code-path-analysis/code-path-analyzer.js +38 -0
- package/lib/linter/code-path-analysis/code-path-segment.js +0 -1
- package/lib/linter/code-path-analysis/code-path-state.js +59 -0
- package/lib/linter/code-path-analysis/debug-helpers.js +26 -19
- package/lib/rules/accessor-pairs.js +1 -14
- package/lib/rules/array-callback-return.js +5 -7
- package/lib/rules/arrow-body-style.js +41 -6
- package/lib/rules/consistent-return.js +1 -12
- package/lib/rules/constructor-super.js +1 -0
- package/lib/rules/dot-location.js +20 -14
- package/lib/rules/dot-notation.js +36 -33
- package/lib/rules/func-call-spacing.js +42 -6
- package/lib/rules/func-name-matching.js +1 -4
- package/lib/rules/global-require.js +2 -1
- package/lib/rules/id-blacklist.js +233 -0
- package/lib/rules/indent.js +19 -0
- package/lib/rules/index.js +1 -3
- package/lib/rules/keyword-spacing.js +2 -2
- package/lib/rules/max-len.js +13 -2
- package/lib/rules/new-cap.js +10 -14
- package/lib/rules/newline-per-chained-call.js +15 -5
- package/lib/rules/no-alert.js +10 -3
- package/lib/rules/no-eval.js +8 -38
- package/lib/rules/no-extend-native.js +37 -40
- package/lib/rules/no-extra-bind.js +57 -17
- package/lib/rules/no-extra-boolean-cast.js +7 -0
- package/lib/rules/no-extra-parens.js +27 -7
- package/lib/rules/no-implicit-coercion.js +11 -6
- package/lib/rules/no-implied-eval.js +7 -28
- package/lib/rules/no-import-assign.js +33 -32
- package/lib/rules/no-irregular-whitespace.js +22 -12
- package/lib/rules/no-magic-numbers.js +4 -8
- package/lib/rules/no-obj-calls.js +7 -4
- package/lib/rules/no-prototype-builtins.js +13 -3
- package/lib/rules/no-self-assign.js +3 -53
- package/lib/rules/no-setter-return.js +5 -8
- package/lib/rules/no-unexpected-multiline.js +2 -2
- package/lib/rules/no-unneeded-ternary.js +0 -2
- package/lib/rules/no-unused-expressions.js +55 -23
- package/lib/rules/no-useless-call.js +10 -7
- package/lib/rules/no-whitespace-before-property.js +16 -4
- package/lib/rules/object-curly-newline.js +4 -4
- package/lib/rules/operator-assignment.js +3 -42
- package/lib/rules/padding-line-between-statements.js +2 -2
- package/lib/rules/prefer-arrow-callback.js +90 -25
- package/lib/rules/prefer-exponentiation-operator.js +1 -1
- package/lib/rules/prefer-numeric-literals.js +4 -13
- package/lib/rules/prefer-promise-reject-errors.js +1 -3
- package/lib/rules/prefer-regex-literals.js +2 -5
- package/lib/rules/prefer-spread.js +2 -6
- package/lib/rules/radix.js +5 -2
- package/lib/rules/sort-imports.js +28 -0
- package/lib/rules/use-isnan.js +1 -1
- package/lib/rules/utils/ast-utils.js +317 -153
- package/lib/rules/wrap-iife.js +9 -2
- package/lib/rules/yoda.js +2 -55
- package/package.json +6 -6
@@ -143,6 +143,23 @@ function isInLoop(node) {
|
|
143
143
|
return false;
|
144
144
|
}
|
145
145
|
|
146
|
+
/**
|
147
|
+
* Determines whether the given node is a `null` literal.
|
148
|
+
* @param {ASTNode} node The node to check
|
149
|
+
* @returns {boolean} `true` if the node is a `null` literal
|
150
|
+
*/
|
151
|
+
function isNullLiteral(node) {
|
152
|
+
|
153
|
+
/*
|
154
|
+
* Checking `node.value === null` does not guarantee that a literal is a null literal.
|
155
|
+
* When parsing values that cannot be represented in the current environment (e.g. unicode
|
156
|
+
* regexes in Node 4), `node.value` is set to `null` because it wouldn't be possible to
|
157
|
+
* set `node.value` to a unicode regex. To make sure a literal is actually `null`, check
|
158
|
+
* `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020
|
159
|
+
*/
|
160
|
+
return node.type === "Literal" && node.value === null && !node.regex && !node.bigint;
|
161
|
+
}
|
162
|
+
|
146
163
|
/**
|
147
164
|
* Checks whether or not a node is `null` or `undefined`.
|
148
165
|
* @param {ASTNode} node A node to check.
|
@@ -151,7 +168,7 @@ function isInLoop(node) {
|
|
151
168
|
*/
|
152
169
|
function isNullOrUndefined(node) {
|
153
170
|
return (
|
154
|
-
|
171
|
+
isNullLiteral(node) ||
|
155
172
|
(node.type === "Identifier" && node.name === "undefined") ||
|
156
173
|
(node.type === "UnaryExpression" && node.operator === "void")
|
157
174
|
);
|
@@ -166,20 +183,270 @@ function isCallee(node) {
|
|
166
183
|
return node.parent.type === "CallExpression" && node.parent.callee === node;
|
167
184
|
}
|
168
185
|
|
186
|
+
/**
|
187
|
+
* Returns the result of the string conversion applied to the evaluated value of the given expression node,
|
188
|
+
* if it can be determined statically.
|
189
|
+
*
|
190
|
+
* This function returns a `string` value for all `Literal` nodes and simple `TemplateLiteral` nodes only.
|
191
|
+
* In all other cases, this function returns `null`.
|
192
|
+
* @param {ASTNode} node Expression node.
|
193
|
+
* @returns {string|null} String value if it can be determined. Otherwise, `null`.
|
194
|
+
*/
|
195
|
+
function getStaticStringValue(node) {
|
196
|
+
switch (node.type) {
|
197
|
+
case "Literal":
|
198
|
+
if (node.value === null) {
|
199
|
+
if (isNullLiteral(node)) {
|
200
|
+
return String(node.value); // "null"
|
201
|
+
}
|
202
|
+
if (node.regex) {
|
203
|
+
return `/${node.regex.pattern}/${node.regex.flags}`;
|
204
|
+
}
|
205
|
+
if (node.bigint) {
|
206
|
+
return node.bigint;
|
207
|
+
}
|
208
|
+
|
209
|
+
// Otherwise, this is an unknown literal. The function will return null.
|
210
|
+
|
211
|
+
} else {
|
212
|
+
return String(node.value);
|
213
|
+
}
|
214
|
+
break;
|
215
|
+
case "TemplateLiteral":
|
216
|
+
if (node.expressions.length === 0 && node.quasis.length === 1) {
|
217
|
+
return node.quasis[0].value.cooked;
|
218
|
+
}
|
219
|
+
break;
|
220
|
+
|
221
|
+
// no default
|
222
|
+
}
|
223
|
+
|
224
|
+
return null;
|
225
|
+
}
|
226
|
+
|
227
|
+
/**
|
228
|
+
* Gets the property name of a given node.
|
229
|
+
* The node can be a MemberExpression, a Property, or a MethodDefinition.
|
230
|
+
*
|
231
|
+
* If the name is dynamic, this returns `null`.
|
232
|
+
*
|
233
|
+
* For examples:
|
234
|
+
*
|
235
|
+
* a.b // => "b"
|
236
|
+
* a["b"] // => "b"
|
237
|
+
* a['b'] // => "b"
|
238
|
+
* a[`b`] // => "b"
|
239
|
+
* a[100] // => "100"
|
240
|
+
* a[b] // => null
|
241
|
+
* a["a" + "b"] // => null
|
242
|
+
* a[tag`b`] // => null
|
243
|
+
* a[`${b}`] // => null
|
244
|
+
*
|
245
|
+
* let a = {b: 1} // => "b"
|
246
|
+
* let a = {["b"]: 1} // => "b"
|
247
|
+
* let a = {['b']: 1} // => "b"
|
248
|
+
* let a = {[`b`]: 1} // => "b"
|
249
|
+
* let a = {[100]: 1} // => "100"
|
250
|
+
* let a = {[b]: 1} // => null
|
251
|
+
* let a = {["a" + "b"]: 1} // => null
|
252
|
+
* let a = {[tag`b`]: 1} // => null
|
253
|
+
* let a = {[`${b}`]: 1} // => null
|
254
|
+
* @param {ASTNode} node The node to get.
|
255
|
+
* @returns {string|null} The property name if static. Otherwise, null.
|
256
|
+
*/
|
257
|
+
function getStaticPropertyName(node) {
|
258
|
+
let prop;
|
259
|
+
|
260
|
+
switch (node && node.type) {
|
261
|
+
case "ChainExpression":
|
262
|
+
return getStaticPropertyName(node.expression);
|
263
|
+
|
264
|
+
case "Property":
|
265
|
+
case "MethodDefinition":
|
266
|
+
prop = node.key;
|
267
|
+
break;
|
268
|
+
|
269
|
+
case "MemberExpression":
|
270
|
+
prop = node.property;
|
271
|
+
break;
|
272
|
+
|
273
|
+
// no default
|
274
|
+
}
|
275
|
+
|
276
|
+
if (prop) {
|
277
|
+
if (prop.type === "Identifier" && !node.computed) {
|
278
|
+
return prop.name;
|
279
|
+
}
|
280
|
+
|
281
|
+
return getStaticStringValue(prop);
|
282
|
+
}
|
283
|
+
|
284
|
+
return null;
|
285
|
+
}
|
286
|
+
|
287
|
+
/**
|
288
|
+
* Retrieve `ChainExpression#expression` value if the given node a `ChainExpression` node. Otherwise, pass through it.
|
289
|
+
* @param {ASTNode} node The node to address.
|
290
|
+
* @returns {ASTNode} The `ChainExpression#expression` value if the node is a `ChainExpression` node. Otherwise, the node.
|
291
|
+
*/
|
292
|
+
function skipChainExpression(node) {
|
293
|
+
return node && node.type === "ChainExpression" ? node.expression : node;
|
294
|
+
}
|
295
|
+
|
296
|
+
/**
|
297
|
+
* Check if the `actual` is an expected value.
|
298
|
+
* @param {string} actual The string value to check.
|
299
|
+
* @param {string | RegExp} expected The expected string value or pattern.
|
300
|
+
* @returns {boolean} `true` if the `actual` is an expected value.
|
301
|
+
*/
|
302
|
+
function checkText(actual, expected) {
|
303
|
+
return typeof expected === "string"
|
304
|
+
? actual === expected
|
305
|
+
: expected.test(actual);
|
306
|
+
}
|
307
|
+
|
308
|
+
/**
|
309
|
+
* Check if a given node is an Identifier node with a given name.
|
310
|
+
* @param {ASTNode} node The node to check.
|
311
|
+
* @param {string | RegExp} name The expected name or the expected pattern of the object name.
|
312
|
+
* @returns {boolean} `true` if the node is an Identifier node with the name.
|
313
|
+
*/
|
314
|
+
function isSpecificId(node, name) {
|
315
|
+
return node.type === "Identifier" && checkText(node.name, name);
|
316
|
+
}
|
317
|
+
|
318
|
+
/**
|
319
|
+
* Check if a given node is member access with a given object name and property name pair.
|
320
|
+
* This is regardless of optional or not.
|
321
|
+
* @param {ASTNode} node The node to check.
|
322
|
+
* @param {string | RegExp | null} objectName The expected name or the expected pattern of the object name. If this is nullish, this method doesn't check object.
|
323
|
+
* @param {string | RegExp | null} propertyName The expected name or the expected pattern of the property name. If this is nullish, this method doesn't check property.
|
324
|
+
* @returns {boolean} `true` if the node is member access with the object name and property name pair.
|
325
|
+
* The node is a `MemberExpression` or `ChainExpression`.
|
326
|
+
*/
|
327
|
+
function isSpecificMemberAccess(node, objectName, propertyName) {
|
328
|
+
const checkNode = skipChainExpression(node);
|
329
|
+
|
330
|
+
if (checkNode.type !== "MemberExpression") {
|
331
|
+
return false;
|
332
|
+
}
|
333
|
+
|
334
|
+
if (objectName && !isSpecificId(checkNode.object, objectName)) {
|
335
|
+
return false;
|
336
|
+
}
|
337
|
+
|
338
|
+
if (propertyName) {
|
339
|
+
const actualPropertyName = getStaticPropertyName(checkNode);
|
340
|
+
|
341
|
+
if (typeof actualPropertyName !== "string" || !checkText(actualPropertyName, propertyName)) {
|
342
|
+
return false;
|
343
|
+
}
|
344
|
+
}
|
345
|
+
|
346
|
+
return true;
|
347
|
+
}
|
348
|
+
|
349
|
+
/**
|
350
|
+
* Check if two literal nodes are the same value.
|
351
|
+
* @param {ASTNode} left The Literal node to compare.
|
352
|
+
* @param {ASTNode} right The other Literal node to compare.
|
353
|
+
* @returns {boolean} `true` if the two literal nodes are the same value.
|
354
|
+
*/
|
355
|
+
function equalLiteralValue(left, right) {
|
356
|
+
|
357
|
+
// RegExp literal.
|
358
|
+
if (left.regex || right.regex) {
|
359
|
+
return Boolean(
|
360
|
+
left.regex &&
|
361
|
+
right.regex &&
|
362
|
+
left.regex.pattern === right.regex.pattern &&
|
363
|
+
left.regex.flags === right.regex.flags
|
364
|
+
);
|
365
|
+
}
|
366
|
+
|
367
|
+
// BigInt literal.
|
368
|
+
if (left.bigint || right.bigint) {
|
369
|
+
return left.bigint === right.bigint;
|
370
|
+
}
|
371
|
+
|
372
|
+
return left.value === right.value;
|
373
|
+
}
|
374
|
+
|
375
|
+
/**
|
376
|
+
* Check if two expressions reference the same value. For example:
|
377
|
+
* a = a
|
378
|
+
* a.b = a.b
|
379
|
+
* a[0] = a[0]
|
380
|
+
* a['b'] = a['b']
|
381
|
+
* @param {ASTNode} left The left side of the comparison.
|
382
|
+
* @param {ASTNode} right The right side of the comparison.
|
383
|
+
* @param {boolean} [disableStaticComputedKey] Don't address `a.b` and `a["b"]` are the same if `true`. For backward compatibility.
|
384
|
+
* @returns {boolean} `true` if both sides match and reference the same value.
|
385
|
+
*/
|
386
|
+
function isSameReference(left, right, disableStaticComputedKey = false) {
|
387
|
+
if (left.type !== right.type) {
|
388
|
+
|
389
|
+
// Handle `a.b` and `a?.b` are samely.
|
390
|
+
if (left.type === "ChainExpression") {
|
391
|
+
return isSameReference(left.expression, right, disableStaticComputedKey);
|
392
|
+
}
|
393
|
+
if (right.type === "ChainExpression") {
|
394
|
+
return isSameReference(left, right.expression, disableStaticComputedKey);
|
395
|
+
}
|
396
|
+
|
397
|
+
return false;
|
398
|
+
}
|
399
|
+
|
400
|
+
switch (left.type) {
|
401
|
+
case "Super":
|
402
|
+
case "ThisExpression":
|
403
|
+
return true;
|
404
|
+
|
405
|
+
case "Identifier":
|
406
|
+
return left.name === right.name;
|
407
|
+
case "Literal":
|
408
|
+
return equalLiteralValue(left, right);
|
409
|
+
|
410
|
+
case "ChainExpression":
|
411
|
+
return isSameReference(left.expression, right.expression, disableStaticComputedKey);
|
412
|
+
|
413
|
+
case "MemberExpression": {
|
414
|
+
if (!disableStaticComputedKey) {
|
415
|
+
const nameA = getStaticPropertyName(left);
|
416
|
+
|
417
|
+
// x.y = x["y"]
|
418
|
+
if (nameA !== null) {
|
419
|
+
return (
|
420
|
+
isSameReference(left.object, right.object, disableStaticComputedKey) &&
|
421
|
+
nameA === getStaticPropertyName(right)
|
422
|
+
);
|
423
|
+
}
|
424
|
+
}
|
425
|
+
|
426
|
+
/*
|
427
|
+
* x[0] = x[0]
|
428
|
+
* x[y] = x[y]
|
429
|
+
* x.y = x.y
|
430
|
+
*/
|
431
|
+
return (
|
432
|
+
left.computed === right.computed &&
|
433
|
+
isSameReference(left.object, right.object, disableStaticComputedKey) &&
|
434
|
+
isSameReference(left.property, right.property, disableStaticComputedKey)
|
435
|
+
);
|
436
|
+
}
|
437
|
+
|
438
|
+
default:
|
439
|
+
return false;
|
440
|
+
}
|
441
|
+
}
|
442
|
+
|
169
443
|
/**
|
170
444
|
* Checks whether or not a node is `Reflect.apply`.
|
171
445
|
* @param {ASTNode} node A node to check.
|
172
446
|
* @returns {boolean} Whether or not the node is a `Reflect.apply`.
|
173
447
|
*/
|
174
448
|
function isReflectApply(node) {
|
175
|
-
return (
|
176
|
-
node.type === "MemberExpression" &&
|
177
|
-
node.object.type === "Identifier" &&
|
178
|
-
node.object.name === "Reflect" &&
|
179
|
-
node.property.type === "Identifier" &&
|
180
|
-
node.property.name === "apply" &&
|
181
|
-
node.computed === false
|
182
|
-
);
|
449
|
+
return isSpecificMemberAccess(node, "Reflect", "apply");
|
183
450
|
}
|
184
451
|
|
185
452
|
/**
|
@@ -188,14 +455,7 @@ function isReflectApply(node) {
|
|
188
455
|
* @returns {boolean} Whether or not the node is a `Array.from`.
|
189
456
|
*/
|
190
457
|
function isArrayFromMethod(node) {
|
191
|
-
return (
|
192
|
-
node.type === "MemberExpression" &&
|
193
|
-
node.object.type === "Identifier" &&
|
194
|
-
arrayOrTypedArrayPattern.test(node.object.name) &&
|
195
|
-
node.property.type === "Identifier" &&
|
196
|
-
node.property.name === "from" &&
|
197
|
-
node.computed === false
|
198
|
-
);
|
458
|
+
return isSpecificMemberAccess(node, arrayOrTypedArrayPattern, "from");
|
199
459
|
}
|
200
460
|
|
201
461
|
/**
|
@@ -204,17 +464,7 @@ function isArrayFromMethod(node) {
|
|
204
464
|
* @returns {boolean} Whether or not the node is a method which has `thisArg`.
|
205
465
|
*/
|
206
466
|
function isMethodWhichHasThisArg(node) {
|
207
|
-
|
208
|
-
let currentNode = node;
|
209
|
-
currentNode.type === "MemberExpression" && !currentNode.computed;
|
210
|
-
currentNode = currentNode.property
|
211
|
-
) {
|
212
|
-
if (currentNode.property.type === "Identifier") {
|
213
|
-
return arrayMethodPattern.test(currentNode.property.name);
|
214
|
-
}
|
215
|
-
}
|
216
|
-
|
217
|
-
return false;
|
467
|
+
return isSpecificMemberAccess(node, null, arrayMethodPattern);
|
218
468
|
}
|
219
469
|
|
220
470
|
/**
|
@@ -289,6 +539,15 @@ function isDotToken(token) {
|
|
289
539
|
return token.value === "." && token.type === "Punctuator";
|
290
540
|
}
|
291
541
|
|
542
|
+
/**
|
543
|
+
* Checks if the given token is a `?.` token or not.
|
544
|
+
* @param {Token} token The token to check.
|
545
|
+
* @returns {boolean} `true` if the token is a `?.` token.
|
546
|
+
*/
|
547
|
+
function isQuestionDotToken(token) {
|
548
|
+
return token.value === "?." && token.type === "Punctuator";
|
549
|
+
}
|
550
|
+
|
292
551
|
/**
|
293
552
|
* Checks if the given token is a semicolon token or not.
|
294
553
|
* @param {Token} token The token to check.
|
@@ -505,6 +764,7 @@ module.exports = {
|
|
505
764
|
isCommaToken,
|
506
765
|
isCommentToken,
|
507
766
|
isDotToken,
|
767
|
+
isQuestionDotToken,
|
508
768
|
isKeywordToken,
|
509
769
|
isNotClosingBraceToken: negate(isClosingBraceToken),
|
510
770
|
isNotClosingBracketToken: negate(isClosingBracketToken),
|
@@ -512,6 +772,7 @@ module.exports = {
|
|
512
772
|
isNotColonToken: negate(isColonToken),
|
513
773
|
isNotCommaToken: negate(isCommaToken),
|
514
774
|
isNotDotToken: negate(isDotToken),
|
775
|
+
isNotQuestionDotToken: negate(isQuestionDotToken),
|
515
776
|
isNotOpeningBraceToken: negate(isOpeningBraceToken),
|
516
777
|
isNotOpeningBracketToken: negate(isOpeningBracketToken),
|
517
778
|
isNotOpeningParenToken: negate(isOpeningParenToken),
|
@@ -669,6 +930,7 @@ module.exports = {
|
|
669
930
|
*/
|
670
931
|
case "LogicalExpression":
|
671
932
|
case "ConditionalExpression":
|
933
|
+
case "ChainExpression":
|
672
934
|
currentNode = parent;
|
673
935
|
break;
|
674
936
|
|
@@ -755,14 +1017,21 @@ module.exports = {
|
|
755
1017
|
* (function foo() { ... }).apply(obj, []);
|
756
1018
|
*/
|
757
1019
|
case "MemberExpression":
|
758
|
-
|
759
|
-
parent.object
|
760
|
-
parent
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
1020
|
+
if (
|
1021
|
+
parent.object === currentNode &&
|
1022
|
+
isSpecificMemberAccess(parent, null, bindOrCallOrApplyPattern)
|
1023
|
+
) {
|
1024
|
+
const maybeCalleeNode = parent.parent.type === "ChainExpression"
|
1025
|
+
? parent.parent
|
1026
|
+
: parent;
|
1027
|
+
|
1028
|
+
return !(
|
1029
|
+
isCallee(maybeCalleeNode) &&
|
1030
|
+
maybeCalleeNode.parent.arguments.length >= 1 &&
|
1031
|
+
!isNullOrUndefined(maybeCalleeNode.parent.arguments[0])
|
1032
|
+
);
|
1033
|
+
}
|
1034
|
+
return true;
|
766
1035
|
|
767
1036
|
/*
|
768
1037
|
* e.g.
|
@@ -884,6 +1153,7 @@ module.exports = {
|
|
884
1153
|
return 17;
|
885
1154
|
|
886
1155
|
case "CallExpression":
|
1156
|
+
case "ChainExpression":
|
887
1157
|
case "ImportExpression":
|
888
1158
|
return 18;
|
889
1159
|
|
@@ -913,104 +1183,6 @@ module.exports = {
|
|
913
1183
|
return isFunction(node) && module.exports.isEmptyBlock(node.body);
|
914
1184
|
},
|
915
1185
|
|
916
|
-
/**
|
917
|
-
* Returns the result of the string conversion applied to the evaluated value of the given expression node,
|
918
|
-
* if it can be determined statically.
|
919
|
-
*
|
920
|
-
* This function returns a `string` value for all `Literal` nodes and simple `TemplateLiteral` nodes only.
|
921
|
-
* In all other cases, this function returns `null`.
|
922
|
-
* @param {ASTNode} node Expression node.
|
923
|
-
* @returns {string|null} String value if it can be determined. Otherwise, `null`.
|
924
|
-
*/
|
925
|
-
getStaticStringValue(node) {
|
926
|
-
switch (node.type) {
|
927
|
-
case "Literal":
|
928
|
-
if (node.value === null) {
|
929
|
-
if (module.exports.isNullLiteral(node)) {
|
930
|
-
return String(node.value); // "null"
|
931
|
-
}
|
932
|
-
if (node.regex) {
|
933
|
-
return `/${node.regex.pattern}/${node.regex.flags}`;
|
934
|
-
}
|
935
|
-
if (node.bigint) {
|
936
|
-
return node.bigint;
|
937
|
-
}
|
938
|
-
|
939
|
-
// Otherwise, this is an unknown literal. The function will return null.
|
940
|
-
|
941
|
-
} else {
|
942
|
-
return String(node.value);
|
943
|
-
}
|
944
|
-
break;
|
945
|
-
case "TemplateLiteral":
|
946
|
-
if (node.expressions.length === 0 && node.quasis.length === 1) {
|
947
|
-
return node.quasis[0].value.cooked;
|
948
|
-
}
|
949
|
-
break;
|
950
|
-
|
951
|
-
// no default
|
952
|
-
}
|
953
|
-
|
954
|
-
return null;
|
955
|
-
},
|
956
|
-
|
957
|
-
/**
|
958
|
-
* Gets the property name of a given node.
|
959
|
-
* The node can be a MemberExpression, a Property, or a MethodDefinition.
|
960
|
-
*
|
961
|
-
* If the name is dynamic, this returns `null`.
|
962
|
-
*
|
963
|
-
* For examples:
|
964
|
-
*
|
965
|
-
* a.b // => "b"
|
966
|
-
* a["b"] // => "b"
|
967
|
-
* a['b'] // => "b"
|
968
|
-
* a[`b`] // => "b"
|
969
|
-
* a[100] // => "100"
|
970
|
-
* a[b] // => null
|
971
|
-
* a["a" + "b"] // => null
|
972
|
-
* a[tag`b`] // => null
|
973
|
-
* a[`${b}`] // => null
|
974
|
-
*
|
975
|
-
* let a = {b: 1} // => "b"
|
976
|
-
* let a = {["b"]: 1} // => "b"
|
977
|
-
* let a = {['b']: 1} // => "b"
|
978
|
-
* let a = {[`b`]: 1} // => "b"
|
979
|
-
* let a = {[100]: 1} // => "100"
|
980
|
-
* let a = {[b]: 1} // => null
|
981
|
-
* let a = {["a" + "b"]: 1} // => null
|
982
|
-
* let a = {[tag`b`]: 1} // => null
|
983
|
-
* let a = {[`${b}`]: 1} // => null
|
984
|
-
* @param {ASTNode} node The node to get.
|
985
|
-
* @returns {string|null} The property name if static. Otherwise, null.
|
986
|
-
*/
|
987
|
-
getStaticPropertyName(node) {
|
988
|
-
let prop;
|
989
|
-
|
990
|
-
switch (node && node.type) {
|
991
|
-
case "Property":
|
992
|
-
case "MethodDefinition":
|
993
|
-
prop = node.key;
|
994
|
-
break;
|
995
|
-
|
996
|
-
case "MemberExpression":
|
997
|
-
prop = node.property;
|
998
|
-
break;
|
999
|
-
|
1000
|
-
// no default
|
1001
|
-
}
|
1002
|
-
|
1003
|
-
if (prop) {
|
1004
|
-
if (prop.type === "Identifier" && !node.computed) {
|
1005
|
-
return prop.name;
|
1006
|
-
}
|
1007
|
-
|
1008
|
-
return module.exports.getStaticStringValue(prop);
|
1009
|
-
}
|
1010
|
-
|
1011
|
-
return null;
|
1012
|
-
},
|
1013
|
-
|
1014
1186
|
/**
|
1015
1187
|
* Get directives from directive prologue of a Program or Function node.
|
1016
1188
|
* @param {ASTNode} node The node to check.
|
@@ -1164,7 +1336,7 @@ module.exports = {
|
|
1164
1336
|
if (node.id) {
|
1165
1337
|
tokens.push(`'${node.id.name}'`);
|
1166
1338
|
} else {
|
1167
|
-
const name =
|
1339
|
+
const name = getStaticPropertyName(parent);
|
1168
1340
|
|
1169
1341
|
if (name !== null) {
|
1170
1342
|
tokens.push(`'${name}'`);
|
@@ -1391,6 +1563,7 @@ module.exports = {
|
|
1391
1563
|
case "TaggedTemplateExpression":
|
1392
1564
|
case "YieldExpression":
|
1393
1565
|
case "AwaitExpression":
|
1566
|
+
case "ChainExpression":
|
1394
1567
|
return true; // possibly an error object.
|
1395
1568
|
|
1396
1569
|
case "AssignmentExpression":
|
@@ -1413,23 +1586,6 @@ module.exports = {
|
|
1413
1586
|
}
|
1414
1587
|
},
|
1415
1588
|
|
1416
|
-
/**
|
1417
|
-
* Determines whether the given node is a `null` literal.
|
1418
|
-
* @param {ASTNode} node The node to check
|
1419
|
-
* @returns {boolean} `true` if the node is a `null` literal
|
1420
|
-
*/
|
1421
|
-
isNullLiteral(node) {
|
1422
|
-
|
1423
|
-
/*
|
1424
|
-
* Checking `node.value === null` does not guarantee that a literal is a null literal.
|
1425
|
-
* When parsing values that cannot be represented in the current environment (e.g. unicode
|
1426
|
-
* regexes in Node 4), `node.value` is set to `null` because it wouldn't be possible to
|
1427
|
-
* set `node.value` to a unicode regex. To make sure a literal is actually `null`, check
|
1428
|
-
* `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020
|
1429
|
-
*/
|
1430
|
-
return node.type === "Literal" && node.value === null && !node.regex && !node.bigint;
|
1431
|
-
},
|
1432
|
-
|
1433
1589
|
/**
|
1434
1590
|
* Check if a given node is a numeric literal or not.
|
1435
1591
|
* @param {ASTNode} node The node to check.
|
@@ -1590,5 +1746,13 @@ module.exports = {
|
|
1590
1746
|
|
1591
1747
|
isLogicalExpression,
|
1592
1748
|
isCoalesceExpression,
|
1593
|
-
isMixedLogicalAndCoalesceExpressions
|
1749
|
+
isMixedLogicalAndCoalesceExpressions,
|
1750
|
+
isNullLiteral,
|
1751
|
+
getStaticStringValue,
|
1752
|
+
getStaticPropertyName,
|
1753
|
+
skipChainExpression,
|
1754
|
+
isSpecificId,
|
1755
|
+
isSpecificMemberAccess,
|
1756
|
+
equalLiteralValue,
|
1757
|
+
isSameReference
|
1594
1758
|
};
|
package/lib/rules/wrap-iife.js
CHANGED
@@ -23,7 +23,14 @@ const eslintUtils = require("eslint-utils");
|
|
23
23
|
* @private
|
24
24
|
*/
|
25
25
|
function isCalleeOfNewExpression(node) {
|
26
|
-
|
26
|
+
const maybeCallee = node.parent.type === "ChainExpression"
|
27
|
+
? node.parent
|
28
|
+
: node;
|
29
|
+
|
30
|
+
return (
|
31
|
+
maybeCallee.parent.type === "NewExpression" &&
|
32
|
+
maybeCallee.parent.callee === maybeCallee
|
33
|
+
);
|
27
34
|
}
|
28
35
|
|
29
36
|
//------------------------------------------------------------------------------
|
@@ -98,7 +105,7 @@ module.exports = {
|
|
98
105
|
* @returns {ASTNode} node that is the function expression of the given IIFE, or null if none exist
|
99
106
|
*/
|
100
107
|
function getFunctionNodeFromIIFE(node) {
|
101
|
-
const callee = node.callee;
|
108
|
+
const callee = astUtils.skipChainExpression(node.callee);
|
102
109
|
|
103
110
|
if (callee.type === "FunctionExpression") {
|
104
111
|
return callee;
|
package/lib/rules/yoda.js
CHANGED
@@ -111,59 +111,6 @@ function getNormalizedLiteral(node) {
|
|
111
111
|
return null;
|
112
112
|
}
|
113
113
|
|
114
|
-
/**
|
115
|
-
* Checks whether two expressions reference the same value. For example:
|
116
|
-
* a = a
|
117
|
-
* a.b = a.b
|
118
|
-
* a[0] = a[0]
|
119
|
-
* a['b'] = a['b']
|
120
|
-
* @param {ASTNode} a Left side of the comparison.
|
121
|
-
* @param {ASTNode} b Right side of the comparison.
|
122
|
-
* @returns {boolean} True if both sides match and reference the same value.
|
123
|
-
*/
|
124
|
-
function same(a, b) {
|
125
|
-
if (a.type !== b.type) {
|
126
|
-
return false;
|
127
|
-
}
|
128
|
-
|
129
|
-
switch (a.type) {
|
130
|
-
case "Identifier":
|
131
|
-
return a.name === b.name;
|
132
|
-
|
133
|
-
case "Literal":
|
134
|
-
return a.value === b.value;
|
135
|
-
|
136
|
-
case "MemberExpression": {
|
137
|
-
const nameA = astUtils.getStaticPropertyName(a);
|
138
|
-
|
139
|
-
// x.y = x["y"]
|
140
|
-
if (nameA !== null) {
|
141
|
-
return (
|
142
|
-
same(a.object, b.object) &&
|
143
|
-
nameA === astUtils.getStaticPropertyName(b)
|
144
|
-
);
|
145
|
-
}
|
146
|
-
|
147
|
-
/*
|
148
|
-
* x[0] = x[0]
|
149
|
-
* x[y] = x[y]
|
150
|
-
* x.y = x.y
|
151
|
-
*/
|
152
|
-
return (
|
153
|
-
a.computed === b.computed &&
|
154
|
-
same(a.object, b.object) &&
|
155
|
-
same(a.property, b.property)
|
156
|
-
);
|
157
|
-
}
|
158
|
-
|
159
|
-
case "ThisExpression":
|
160
|
-
return true;
|
161
|
-
|
162
|
-
default:
|
163
|
-
return false;
|
164
|
-
}
|
165
|
-
}
|
166
|
-
|
167
114
|
//------------------------------------------------------------------------------
|
168
115
|
// Rule Definition
|
169
116
|
//------------------------------------------------------------------------------
|
@@ -236,7 +183,7 @@ module.exports = {
|
|
236
183
|
* @returns {boolean} Whether node is a "between" range test.
|
237
184
|
*/
|
238
185
|
function isBetweenTest() {
|
239
|
-
if (node.operator === "&&" &&
|
186
|
+
if (node.operator === "&&" && astUtils.isSameReference(left.right, right.left)) {
|
240
187
|
const leftLiteral = getNormalizedLiteral(left.left);
|
241
188
|
const rightLiteral = getNormalizedLiteral(right.right);
|
242
189
|
|
@@ -260,7 +207,7 @@ module.exports = {
|
|
260
207
|
* @returns {boolean} Whether node is an "outside" range test.
|
261
208
|
*/
|
262
209
|
function isOutsideTest() {
|
263
|
-
if (node.operator === "||" &&
|
210
|
+
if (node.operator === "||" && astUtils.isSameReference(left.left, right.right)) {
|
264
211
|
const leftLiteral = getNormalizedLiteral(left.right);
|
265
212
|
const rightLiteral = getNormalizedLiteral(right.left);
|
266
213
|
|