eslint 7.4.0 → 7.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +86 -0
- package/README.md +29 -17
- package/conf/config-schema.js +12 -0
- package/lib/cli-engine/cascading-config-array-factory.js +12 -0
- package/lib/cli-engine/cli-engine.js +2 -2
- package/lib/cli-engine/config-array/config-array.js +12 -0
- package/lib/cli-engine/config-array/config-dependency.js +12 -0
- package/lib/cli-engine/config-array/extracted-config.js +12 -0
- package/lib/cli-engine/config-array/ignore-pattern.js +12 -0
- package/lib/cli-engine/config-array/index.js +12 -0
- package/lib/cli-engine/config-array/override-tester.js +12 -0
- package/lib/cli-engine/config-array-factory.js +13 -1
- package/lib/cli-engine/formatters/checkstyle.js +2 -2
- package/lib/eslint/eslint.js +7 -1
- package/lib/init/autoconfig.js +1 -1
- package/lib/init/config-initializer.js +3 -3
- package/lib/linter/code-path-analysis/code-path-analyzer.js +76 -0
- package/lib/linter/code-path-analysis/code-path-segment.js +0 -1
- package/lib/linter/code-path-analysis/code-path-state.js +61 -2
- package/lib/linter/code-path-analysis/debug-helpers.js +26 -19
- package/lib/linter/config-comment-parser.js +1 -1
- package/lib/linter/linter.js +6 -3
- package/lib/rule-tester/rule-tester.js +10 -0
- 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/comma-dangle.js +1 -2
- package/lib/rules/consistent-return.js +1 -12
- package/lib/rules/constructor-super.js +18 -1
- 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/id-length.js +19 -1
- package/lib/rules/indent.js +23 -3
- 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-duplicate-case.js +23 -4
- 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-loss-of-precision.js +10 -2
- package/lib/rules/no-magic-numbers.js +24 -11
- 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-underscore-dangle.js +66 -21
- 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-warning-comments.js +40 -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 +4 -43
- 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 +14 -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 +363 -165
- package/lib/rules/wrap-iife.js +9 -2
- package/lib/rules/yoda.js +2 -55
- package/lib/shared/config-validator.js +14 -2
- package/lib/shared/relative-module-resolver.js +12 -0
- package/lib/shared/types.js +1 -1
- package/messages/extend-config-missing.txt +1 -1
- package/messages/no-config-found.txt +1 -1
- package/messages/plugin-conflict.txt +1 -1
- package/messages/plugin-missing.txt +1 -1
- package/messages/whitespace-found.txt +1 -1
- package/package.json +7 -7
- package/conf/environments.js +0 -168
- package/lib/shared/config-ops.js +0 -130
- package/lib/shared/naming.js +0 -97
@@ -37,9 +37,11 @@ const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
|
|
37
37
|
// A set of node types that can contain a list of statements
|
38
38
|
const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]);
|
39
39
|
|
40
|
-
const DECIMAL_INTEGER_PATTERN = /^(0|[1-9]
|
40
|
+
const DECIMAL_INTEGER_PATTERN = /^(0|[1-9](?:_?\d)*)$/u;
|
41
41
|
const OCTAL_ESCAPE_PATTERN = /^(?:[^\\]|\\[^0-7]|\\0(?![0-9]))*\\(?:[1-7]|0[0-9])/u;
|
42
42
|
|
43
|
+
const LOGICAL_ASSIGNMENT_OPERATORS = new Set(["&&=", "||=", "??="]);
|
44
|
+
|
43
45
|
/**
|
44
46
|
* Checks reference if is non initializer and writable.
|
45
47
|
* @param {Reference} reference A reference to check.
|
@@ -143,6 +145,23 @@ function isInLoop(node) {
|
|
143
145
|
return false;
|
144
146
|
}
|
145
147
|
|
148
|
+
/**
|
149
|
+
* Determines whether the given node is a `null` literal.
|
150
|
+
* @param {ASTNode} node The node to check
|
151
|
+
* @returns {boolean} `true` if the node is a `null` literal
|
152
|
+
*/
|
153
|
+
function isNullLiteral(node) {
|
154
|
+
|
155
|
+
/*
|
156
|
+
* Checking `node.value === null` does not guarantee that a literal is a null literal.
|
157
|
+
* When parsing values that cannot be represented in the current environment (e.g. unicode
|
158
|
+
* regexes in Node 4), `node.value` is set to `null` because it wouldn't be possible to
|
159
|
+
* set `node.value` to a unicode regex. To make sure a literal is actually `null`, check
|
160
|
+
* `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020
|
161
|
+
*/
|
162
|
+
return node.type === "Literal" && node.value === null && !node.regex && !node.bigint;
|
163
|
+
}
|
164
|
+
|
146
165
|
/**
|
147
166
|
* Checks whether or not a node is `null` or `undefined`.
|
148
167
|
* @param {ASTNode} node A node to check.
|
@@ -151,7 +170,7 @@ function isInLoop(node) {
|
|
151
170
|
*/
|
152
171
|
function isNullOrUndefined(node) {
|
153
172
|
return (
|
154
|
-
|
173
|
+
isNullLiteral(node) ||
|
155
174
|
(node.type === "Identifier" && node.name === "undefined") ||
|
156
175
|
(node.type === "UnaryExpression" && node.operator === "void")
|
157
176
|
);
|
@@ -166,20 +185,270 @@ function isCallee(node) {
|
|
166
185
|
return node.parent.type === "CallExpression" && node.parent.callee === node;
|
167
186
|
}
|
168
187
|
|
188
|
+
/**
|
189
|
+
* Returns the result of the string conversion applied to the evaluated value of the given expression node,
|
190
|
+
* if it can be determined statically.
|
191
|
+
*
|
192
|
+
* This function returns a `string` value for all `Literal` nodes and simple `TemplateLiteral` nodes only.
|
193
|
+
* In all other cases, this function returns `null`.
|
194
|
+
* @param {ASTNode} node Expression node.
|
195
|
+
* @returns {string|null} String value if it can be determined. Otherwise, `null`.
|
196
|
+
*/
|
197
|
+
function getStaticStringValue(node) {
|
198
|
+
switch (node.type) {
|
199
|
+
case "Literal":
|
200
|
+
if (node.value === null) {
|
201
|
+
if (isNullLiteral(node)) {
|
202
|
+
return String(node.value); // "null"
|
203
|
+
}
|
204
|
+
if (node.regex) {
|
205
|
+
return `/${node.regex.pattern}/${node.regex.flags}`;
|
206
|
+
}
|
207
|
+
if (node.bigint) {
|
208
|
+
return node.bigint;
|
209
|
+
}
|
210
|
+
|
211
|
+
// Otherwise, this is an unknown literal. The function will return null.
|
212
|
+
|
213
|
+
} else {
|
214
|
+
return String(node.value);
|
215
|
+
}
|
216
|
+
break;
|
217
|
+
case "TemplateLiteral":
|
218
|
+
if (node.expressions.length === 0 && node.quasis.length === 1) {
|
219
|
+
return node.quasis[0].value.cooked;
|
220
|
+
}
|
221
|
+
break;
|
222
|
+
|
223
|
+
// no default
|
224
|
+
}
|
225
|
+
|
226
|
+
return null;
|
227
|
+
}
|
228
|
+
|
229
|
+
/**
|
230
|
+
* Gets the property name of a given node.
|
231
|
+
* The node can be a MemberExpression, a Property, or a MethodDefinition.
|
232
|
+
*
|
233
|
+
* If the name is dynamic, this returns `null`.
|
234
|
+
*
|
235
|
+
* For examples:
|
236
|
+
*
|
237
|
+
* a.b // => "b"
|
238
|
+
* a["b"] // => "b"
|
239
|
+
* a['b'] // => "b"
|
240
|
+
* a[`b`] // => "b"
|
241
|
+
* a[100] // => "100"
|
242
|
+
* a[b] // => null
|
243
|
+
* a["a" + "b"] // => null
|
244
|
+
* a[tag`b`] // => null
|
245
|
+
* a[`${b}`] // => null
|
246
|
+
*
|
247
|
+
* let a = {b: 1} // => "b"
|
248
|
+
* let a = {["b"]: 1} // => "b"
|
249
|
+
* let a = {['b']: 1} // => "b"
|
250
|
+
* let a = {[`b`]: 1} // => "b"
|
251
|
+
* let a = {[100]: 1} // => "100"
|
252
|
+
* let a = {[b]: 1} // => null
|
253
|
+
* let a = {["a" + "b"]: 1} // => null
|
254
|
+
* let a = {[tag`b`]: 1} // => null
|
255
|
+
* let a = {[`${b}`]: 1} // => null
|
256
|
+
* @param {ASTNode} node The node to get.
|
257
|
+
* @returns {string|null} The property name if static. Otherwise, null.
|
258
|
+
*/
|
259
|
+
function getStaticPropertyName(node) {
|
260
|
+
let prop;
|
261
|
+
|
262
|
+
switch (node && node.type) {
|
263
|
+
case "ChainExpression":
|
264
|
+
return getStaticPropertyName(node.expression);
|
265
|
+
|
266
|
+
case "Property":
|
267
|
+
case "MethodDefinition":
|
268
|
+
prop = node.key;
|
269
|
+
break;
|
270
|
+
|
271
|
+
case "MemberExpression":
|
272
|
+
prop = node.property;
|
273
|
+
break;
|
274
|
+
|
275
|
+
// no default
|
276
|
+
}
|
277
|
+
|
278
|
+
if (prop) {
|
279
|
+
if (prop.type === "Identifier" && !node.computed) {
|
280
|
+
return prop.name;
|
281
|
+
}
|
282
|
+
|
283
|
+
return getStaticStringValue(prop);
|
284
|
+
}
|
285
|
+
|
286
|
+
return null;
|
287
|
+
}
|
288
|
+
|
289
|
+
/**
|
290
|
+
* Retrieve `ChainExpression#expression` value if the given node a `ChainExpression` node. Otherwise, pass through it.
|
291
|
+
* @param {ASTNode} node The node to address.
|
292
|
+
* @returns {ASTNode} The `ChainExpression#expression` value if the node is a `ChainExpression` node. Otherwise, the node.
|
293
|
+
*/
|
294
|
+
function skipChainExpression(node) {
|
295
|
+
return node && node.type === "ChainExpression" ? node.expression : node;
|
296
|
+
}
|
297
|
+
|
298
|
+
/**
|
299
|
+
* Check if the `actual` is an expected value.
|
300
|
+
* @param {string} actual The string value to check.
|
301
|
+
* @param {string | RegExp} expected The expected string value or pattern.
|
302
|
+
* @returns {boolean} `true` if the `actual` is an expected value.
|
303
|
+
*/
|
304
|
+
function checkText(actual, expected) {
|
305
|
+
return typeof expected === "string"
|
306
|
+
? actual === expected
|
307
|
+
: expected.test(actual);
|
308
|
+
}
|
309
|
+
|
310
|
+
/**
|
311
|
+
* Check if a given node is an Identifier node with a given name.
|
312
|
+
* @param {ASTNode} node The node to check.
|
313
|
+
* @param {string | RegExp} name The expected name or the expected pattern of the object name.
|
314
|
+
* @returns {boolean} `true` if the node is an Identifier node with the name.
|
315
|
+
*/
|
316
|
+
function isSpecificId(node, name) {
|
317
|
+
return node.type === "Identifier" && checkText(node.name, name);
|
318
|
+
}
|
319
|
+
|
320
|
+
/**
|
321
|
+
* Check if a given node is member access with a given object name and property name pair.
|
322
|
+
* This is regardless of optional or not.
|
323
|
+
* @param {ASTNode} node The node to check.
|
324
|
+
* @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.
|
325
|
+
* @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.
|
326
|
+
* @returns {boolean} `true` if the node is member access with the object name and property name pair.
|
327
|
+
* The node is a `MemberExpression` or `ChainExpression`.
|
328
|
+
*/
|
329
|
+
function isSpecificMemberAccess(node, objectName, propertyName) {
|
330
|
+
const checkNode = skipChainExpression(node);
|
331
|
+
|
332
|
+
if (checkNode.type !== "MemberExpression") {
|
333
|
+
return false;
|
334
|
+
}
|
335
|
+
|
336
|
+
if (objectName && !isSpecificId(checkNode.object, objectName)) {
|
337
|
+
return false;
|
338
|
+
}
|
339
|
+
|
340
|
+
if (propertyName) {
|
341
|
+
const actualPropertyName = getStaticPropertyName(checkNode);
|
342
|
+
|
343
|
+
if (typeof actualPropertyName !== "string" || !checkText(actualPropertyName, propertyName)) {
|
344
|
+
return false;
|
345
|
+
}
|
346
|
+
}
|
347
|
+
|
348
|
+
return true;
|
349
|
+
}
|
350
|
+
|
351
|
+
/**
|
352
|
+
* Check if two literal nodes are the same value.
|
353
|
+
* @param {ASTNode} left The Literal node to compare.
|
354
|
+
* @param {ASTNode} right The other Literal node to compare.
|
355
|
+
* @returns {boolean} `true` if the two literal nodes are the same value.
|
356
|
+
*/
|
357
|
+
function equalLiteralValue(left, right) {
|
358
|
+
|
359
|
+
// RegExp literal.
|
360
|
+
if (left.regex || right.regex) {
|
361
|
+
return Boolean(
|
362
|
+
left.regex &&
|
363
|
+
right.regex &&
|
364
|
+
left.regex.pattern === right.regex.pattern &&
|
365
|
+
left.regex.flags === right.regex.flags
|
366
|
+
);
|
367
|
+
}
|
368
|
+
|
369
|
+
// BigInt literal.
|
370
|
+
if (left.bigint || right.bigint) {
|
371
|
+
return left.bigint === right.bigint;
|
372
|
+
}
|
373
|
+
|
374
|
+
return left.value === right.value;
|
375
|
+
}
|
376
|
+
|
377
|
+
/**
|
378
|
+
* Check if two expressions reference the same value. For example:
|
379
|
+
* a = a
|
380
|
+
* a.b = a.b
|
381
|
+
* a[0] = a[0]
|
382
|
+
* a['b'] = a['b']
|
383
|
+
* @param {ASTNode} left The left side of the comparison.
|
384
|
+
* @param {ASTNode} right The right side of the comparison.
|
385
|
+
* @param {boolean} [disableStaticComputedKey] Don't address `a.b` and `a["b"]` are the same if `true`. For backward compatibility.
|
386
|
+
* @returns {boolean} `true` if both sides match and reference the same value.
|
387
|
+
*/
|
388
|
+
function isSameReference(left, right, disableStaticComputedKey = false) {
|
389
|
+
if (left.type !== right.type) {
|
390
|
+
|
391
|
+
// Handle `a.b` and `a?.b` are samely.
|
392
|
+
if (left.type === "ChainExpression") {
|
393
|
+
return isSameReference(left.expression, right, disableStaticComputedKey);
|
394
|
+
}
|
395
|
+
if (right.type === "ChainExpression") {
|
396
|
+
return isSameReference(left, right.expression, disableStaticComputedKey);
|
397
|
+
}
|
398
|
+
|
399
|
+
return false;
|
400
|
+
}
|
401
|
+
|
402
|
+
switch (left.type) {
|
403
|
+
case "Super":
|
404
|
+
case "ThisExpression":
|
405
|
+
return true;
|
406
|
+
|
407
|
+
case "Identifier":
|
408
|
+
return left.name === right.name;
|
409
|
+
case "Literal":
|
410
|
+
return equalLiteralValue(left, right);
|
411
|
+
|
412
|
+
case "ChainExpression":
|
413
|
+
return isSameReference(left.expression, right.expression, disableStaticComputedKey);
|
414
|
+
|
415
|
+
case "MemberExpression": {
|
416
|
+
if (!disableStaticComputedKey) {
|
417
|
+
const nameA = getStaticPropertyName(left);
|
418
|
+
|
419
|
+
// x.y = x["y"]
|
420
|
+
if (nameA !== null) {
|
421
|
+
return (
|
422
|
+
isSameReference(left.object, right.object, disableStaticComputedKey) &&
|
423
|
+
nameA === getStaticPropertyName(right)
|
424
|
+
);
|
425
|
+
}
|
426
|
+
}
|
427
|
+
|
428
|
+
/*
|
429
|
+
* x[0] = x[0]
|
430
|
+
* x[y] = x[y]
|
431
|
+
* x.y = x.y
|
432
|
+
*/
|
433
|
+
return (
|
434
|
+
left.computed === right.computed &&
|
435
|
+
isSameReference(left.object, right.object, disableStaticComputedKey) &&
|
436
|
+
isSameReference(left.property, right.property, disableStaticComputedKey)
|
437
|
+
);
|
438
|
+
}
|
439
|
+
|
440
|
+
default:
|
441
|
+
return false;
|
442
|
+
}
|
443
|
+
}
|
444
|
+
|
169
445
|
/**
|
170
446
|
* Checks whether or not a node is `Reflect.apply`.
|
171
447
|
* @param {ASTNode} node A node to check.
|
172
448
|
* @returns {boolean} Whether or not the node is a `Reflect.apply`.
|
173
449
|
*/
|
174
450
|
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
|
-
);
|
451
|
+
return isSpecificMemberAccess(node, "Reflect", "apply");
|
183
452
|
}
|
184
453
|
|
185
454
|
/**
|
@@ -188,14 +457,7 @@ function isReflectApply(node) {
|
|
188
457
|
* @returns {boolean} Whether or not the node is a `Array.from`.
|
189
458
|
*/
|
190
459
|
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
|
-
);
|
460
|
+
return isSpecificMemberAccess(node, arrayOrTypedArrayPattern, "from");
|
199
461
|
}
|
200
462
|
|
201
463
|
/**
|
@@ -204,17 +466,7 @@ function isArrayFromMethod(node) {
|
|
204
466
|
* @returns {boolean} Whether or not the node is a method which has `thisArg`.
|
205
467
|
*/
|
206
468
|
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;
|
469
|
+
return isSpecificMemberAccess(node, null, arrayMethodPattern);
|
218
470
|
}
|
219
471
|
|
220
472
|
/**
|
@@ -289,6 +541,15 @@ function isDotToken(token) {
|
|
289
541
|
return token.value === "." && token.type === "Punctuator";
|
290
542
|
}
|
291
543
|
|
544
|
+
/**
|
545
|
+
* Checks if the given token is a `?.` token or not.
|
546
|
+
* @param {Token} token The token to check.
|
547
|
+
* @returns {boolean} `true` if the token is a `?.` token.
|
548
|
+
*/
|
549
|
+
function isQuestionDotToken(token) {
|
550
|
+
return token.value === "?." && token.type === "Punctuator";
|
551
|
+
}
|
552
|
+
|
292
553
|
/**
|
293
554
|
* Checks if the given token is a semicolon token or not.
|
294
555
|
* @param {Token} token The token to check.
|
@@ -463,6 +724,15 @@ function isMixedLogicalAndCoalesceExpressions(left, right) {
|
|
463
724
|
);
|
464
725
|
}
|
465
726
|
|
727
|
+
/**
|
728
|
+
* Checks if the given operator is a logical assignment operator.
|
729
|
+
* @param {string} operator The operator to check.
|
730
|
+
* @returns {boolean} `true` if the operator is a logical assignment operator.
|
731
|
+
*/
|
732
|
+
function isLogicalAssignmentOperator(operator) {
|
733
|
+
return LOGICAL_ASSIGNMENT_OPERATORS.has(operator);
|
734
|
+
}
|
735
|
+
|
466
736
|
//------------------------------------------------------------------------------
|
467
737
|
// Public Interface
|
468
738
|
//------------------------------------------------------------------------------
|
@@ -505,6 +775,7 @@ module.exports = {
|
|
505
775
|
isCommaToken,
|
506
776
|
isCommentToken,
|
507
777
|
isDotToken,
|
778
|
+
isQuestionDotToken,
|
508
779
|
isKeywordToken,
|
509
780
|
isNotClosingBraceToken: negate(isClosingBraceToken),
|
510
781
|
isNotClosingBracketToken: negate(isClosingBracketToken),
|
@@ -512,6 +783,7 @@ module.exports = {
|
|
512
783
|
isNotColonToken: negate(isColonToken),
|
513
784
|
isNotCommaToken: negate(isCommaToken),
|
514
785
|
isNotDotToken: negate(isDotToken),
|
786
|
+
isNotQuestionDotToken: negate(isQuestionDotToken),
|
515
787
|
isNotOpeningBraceToken: negate(isOpeningBraceToken),
|
516
788
|
isNotOpeningBracketToken: negate(isOpeningBracketToken),
|
517
789
|
isNotOpeningParenToken: negate(isOpeningParenToken),
|
@@ -669,6 +941,7 @@ module.exports = {
|
|
669
941
|
*/
|
670
942
|
case "LogicalExpression":
|
671
943
|
case "ConditionalExpression":
|
944
|
+
case "ChainExpression":
|
672
945
|
currentNode = parent;
|
673
946
|
break;
|
674
947
|
|
@@ -755,14 +1028,21 @@ module.exports = {
|
|
755
1028
|
* (function foo() { ... }).apply(obj, []);
|
756
1029
|
*/
|
757
1030
|
case "MemberExpression":
|
758
|
-
|
759
|
-
parent.object
|
760
|
-
parent
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
1031
|
+
if (
|
1032
|
+
parent.object === currentNode &&
|
1033
|
+
isSpecificMemberAccess(parent, null, bindOrCallOrApplyPattern)
|
1034
|
+
) {
|
1035
|
+
const maybeCalleeNode = parent.parent.type === "ChainExpression"
|
1036
|
+
? parent.parent
|
1037
|
+
: parent;
|
1038
|
+
|
1039
|
+
return !(
|
1040
|
+
isCallee(maybeCalleeNode) &&
|
1041
|
+
maybeCalleeNode.parent.arguments.length >= 1 &&
|
1042
|
+
!isNullOrUndefined(maybeCalleeNode.parent.arguments[0])
|
1043
|
+
);
|
1044
|
+
}
|
1045
|
+
return true;
|
766
1046
|
|
767
1047
|
/*
|
768
1048
|
* e.g.
|
@@ -884,6 +1164,7 @@ module.exports = {
|
|
884
1164
|
return 17;
|
885
1165
|
|
886
1166
|
case "CallExpression":
|
1167
|
+
case "ChainExpression":
|
887
1168
|
case "ImportExpression":
|
888
1169
|
return 18;
|
889
1170
|
|
@@ -913,104 +1194,6 @@ module.exports = {
|
|
913
1194
|
return isFunction(node) && module.exports.isEmptyBlock(node.body);
|
914
1195
|
},
|
915
1196
|
|
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
1197
|
/**
|
1015
1198
|
* Get directives from directive prologue of a Program or Function node.
|
1016
1199
|
* @param {ASTNode} node The node to check.
|
@@ -1056,16 +1239,25 @@ module.exports = {
|
|
1056
1239
|
* @returns {boolean} `true` if this node is a decimal integer.
|
1057
1240
|
* @example
|
1058
1241
|
*
|
1059
|
-
*
|
1060
|
-
* 5
|
1061
|
-
*
|
1062
|
-
*
|
1063
|
-
*
|
1064
|
-
*
|
1065
|
-
*
|
1066
|
-
*
|
1067
|
-
*
|
1068
|
-
*
|
1242
|
+
* 0 // true
|
1243
|
+
* 5 // true
|
1244
|
+
* 50 // true
|
1245
|
+
* 5_000 // true
|
1246
|
+
* 1_234_56 // true
|
1247
|
+
* 5. // false
|
1248
|
+
* .5 // false
|
1249
|
+
* 5.0 // false
|
1250
|
+
* 5.00_00 // false
|
1251
|
+
* 05 // false
|
1252
|
+
* 0x5 // false
|
1253
|
+
* 0b101 // false
|
1254
|
+
* 0b11_01 // false
|
1255
|
+
* 0o5 // false
|
1256
|
+
* 5e0 // false
|
1257
|
+
* 5e1_000 // false
|
1258
|
+
* 5n // false
|
1259
|
+
* 1_000n // false
|
1260
|
+
* '5' // false
|
1069
1261
|
*/
|
1070
1262
|
isDecimalInteger(node) {
|
1071
1263
|
return node.type === "Literal" && typeof node.value === "number" &&
|
@@ -1164,7 +1356,7 @@ module.exports = {
|
|
1164
1356
|
if (node.id) {
|
1165
1357
|
tokens.push(`'${node.id.name}'`);
|
1166
1358
|
} else {
|
1167
|
-
const name =
|
1359
|
+
const name = getStaticPropertyName(parent);
|
1168
1360
|
|
1169
1361
|
if (name !== null) {
|
1170
1362
|
tokens.push(`'${name}'`);
|
@@ -1391,10 +1583,24 @@ module.exports = {
|
|
1391
1583
|
case "TaggedTemplateExpression":
|
1392
1584
|
case "YieldExpression":
|
1393
1585
|
case "AwaitExpression":
|
1586
|
+
case "ChainExpression":
|
1394
1587
|
return true; // possibly an error object.
|
1395
1588
|
|
1396
1589
|
case "AssignmentExpression":
|
1397
|
-
|
1590
|
+
if (["=", "&&="].includes(node.operator)) {
|
1591
|
+
return module.exports.couldBeError(node.right);
|
1592
|
+
}
|
1593
|
+
|
1594
|
+
if (["||=", "??="].includes(node.operator)) {
|
1595
|
+
return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right);
|
1596
|
+
}
|
1597
|
+
|
1598
|
+
/**
|
1599
|
+
* All other assignment operators are mathematical assignment operators (arithmetic or bitwise).
|
1600
|
+
* An assignment expression with a mathematical operator can either evaluate to a primitive value,
|
1601
|
+
* or throw, depending on the operands. Thus, it cannot evaluate to an `Error` object.
|
1602
|
+
*/
|
1603
|
+
return false;
|
1398
1604
|
|
1399
1605
|
case "SequenceExpression": {
|
1400
1606
|
const exprs = node.expressions;
|
@@ -1413,23 +1619,6 @@ module.exports = {
|
|
1413
1619
|
}
|
1414
1620
|
},
|
1415
1621
|
|
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
1622
|
/**
|
1434
1623
|
* Check if a given node is a numeric literal or not.
|
1435
1624
|
* @param {ASTNode} node The node to check.
|
@@ -1590,5 +1779,14 @@ module.exports = {
|
|
1590
1779
|
|
1591
1780
|
isLogicalExpression,
|
1592
1781
|
isCoalesceExpression,
|
1593
|
-
isMixedLogicalAndCoalesceExpressions
|
1782
|
+
isMixedLogicalAndCoalesceExpressions,
|
1783
|
+
isNullLiteral,
|
1784
|
+
getStaticStringValue,
|
1785
|
+
getStaticPropertyName,
|
1786
|
+
skipChainExpression,
|
1787
|
+
isSpecificId,
|
1788
|
+
isSpecificMemberAccess,
|
1789
|
+
equalLiteralValue,
|
1790
|
+
isSameReference,
|
1791
|
+
isLogicalAssignmentOperator
|
1594
1792
|
};
|
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;
|