9
9
// Requirements
10
10
//------------------------------------------------------------------------------
11
11
12
- let esutils = require ( "esutils" ) ;
12
+ const esutils = require ( "esutils" ) ;
13
13
14
14
//------------------------------------------------------------------------------
15
15
// Helpers
16
16
//------------------------------------------------------------------------------
17
17
18
- let anyFunctionPattern = / ^ (?: F u n c t i o n (?: D e c l a r a t i o n | E x p r e s s i o n ) | A r r o w F u n c t i o n E x p r e s s i o n ) $ / ;
19
- let anyLoopPattern = / ^ (?: D o W h i l e | F o r | F o r I n | F o r O f | W h i l e ) S t a t e m e n t $ / ;
20
- let arrayOrTypedArrayPattern = / A r r a y $ / ;
21
- let arrayMethodPattern = / ^ (?: e v e r y | f i l t e r | f i n d | f i n d I n d e x | f o r E a c h | m a p | s o m e ) $ / ;
22
- let bindOrCallOrApplyPattern = / ^ (?: b i n d | c a l l | a p p l y ) $ / ;
23
- let breakableTypePattern = / ^ (?: (?: D o ) ? W h i l e | F o r (?: I n | O f ) ? | S w i t c h ) S t a t e m e n t $ / ;
24
- let thisTagPattern = / ^ [ \s \* ] * @ t h i s / m;
18
+ const anyFunctionPattern = / ^ (?: F u n c t i o n (?: D e c l a r a t i o n | E x p r e s s i o n ) | A r r o w F u n c t i o n E x p r e s s i o n ) $ / ;
19
+ const anyLoopPattern = / ^ (?: D o W h i l e | F o r | F o r I n | F o r O f | W h i l e ) S t a t e m e n t $ / ;
20
+ const arrayOrTypedArrayPattern = / A r r a y $ / ;
21
+ const arrayMethodPattern = / ^ (?: e v e r y | f i l t e r | f i n d | f i n d I n d e x | f o r E a c h | m a p | s o m e ) $ / ;
22
+ const bindOrCallOrApplyPattern = / ^ (?: b i n d | c a l l | a p p l y ) $ / ;
23
+ const breakableTypePattern = / ^ (?: (?: D o ) ? W h i l e | F o r (?: I n | O f ) ? | S w i t c h ) S t a t e m e n t $ / ;
24
+ const thisTagPattern = / ^ [ \s \* ] * @ t h i s / m;
25
25
26
26
/**
27
27
* Checks reference if is non initializer and writable.
@@ -32,15 +32,14 @@ let thisTagPattern = /^[\s\*]*@this/m;
32
32
* @private
33
33
*/
34
34
function isModifyingReference ( reference , index , references ) {
35
- let identifier = reference . identifier ,
36
- modifyingDifferentIdentifier ;
35
+ const identifier = reference . identifier ;
37
36
38
37
/*
39
38
* Destructuring assignments can have multiple default value, so
40
39
* possibly there are multiple writeable references for the same
41
40
* identifier.
42
41
*/
43
- modifyingDifferentIdentifier = index === 0 ||
42
+ const modifyingDifferentIdentifier = index === 0 ||
44
43
references [ index - 1 ] . identifier !== identifier ;
45
44
46
45
return ( identifier &&
@@ -50,16 +49,23 @@ function isModifyingReference(reference, index, references) {
50
49
) ;
51
50
}
52
51
52
+ /**
53
+ * Checks whether the given string starts with uppercase or not.
54
+ *
55
+ * @param {string } s - The string to check.
56
+ * @returns {boolean } `true` if the string starts with uppercase.
57
+ */
58
+ function startsWithUpperCase ( s ) {
59
+ return s [ 0 ] !== s [ 0 ] . toLocaleLowerCase ( ) ;
60
+ }
61
+
53
62
/**
54
63
* Checks whether or not a node is a constructor.
55
64
* @param {ASTNode } node - A function node to check.
56
65
* @returns {boolean } Wehether or not a node is a constructor.
57
66
*/
58
67
function isES5Constructor ( node ) {
59
- return (
60
- node . id &&
61
- node . id . name [ 0 ] !== node . id . name [ 0 ] . toLocaleLowerCase ( )
62
- ) ;
68
+ return ( node . id && startsWithUpperCase ( node . id . name ) ) ;
63
69
}
64
70
65
71
/**
@@ -160,7 +166,7 @@ function isMethodWhichHasThisArg(node) {
160
166
* @returns {boolean } Whether or not the node has a `@this` tag in its comments.
161
167
*/
162
168
function hasJSDocThisTag ( node , sourceCode ) {
163
- let jsdocComment = sourceCode . getJSDocComment ( node ) ;
169
+ const jsdocComment = sourceCode . getJSDocComment ( node ) ;
164
170
165
171
if ( jsdocComment && thisTagPattern . test ( jsdocComment . value ) ) {
166
172
return true ;
@@ -183,7 +189,7 @@ function hasJSDocThisTag(node, sourceCode) {
183
189
* @private
184
190
*/
185
191
function isParenthesised ( sourceCode , node ) {
186
- let previousToken = sourceCode . getTokenBefore ( node ) ,
192
+ const previousToken = sourceCode . getTokenBefore ( node ) ,
187
193
nextToken = sourceCode . getTokenAfter ( node ) ;
188
194
189
195
return Boolean ( previousToken && nextToken ) &&
@@ -285,7 +291,7 @@ module.exports = {
285
291
* @returns {boolean } `true` if the node is an ESLint directive comment
286
292
*/
287
293
isDirectiveComment : function ( node ) {
288
- let comment = node . value . trim ( ) ;
294
+ const comment = node . value . trim ( ) ;
289
295
290
296
return (
291
297
node . type === "Line" && comment . indexOf ( "eslint-" ) === 0 ||
@@ -321,7 +327,7 @@ module.exports = {
321
327
let scope = initScope ;
322
328
323
329
while ( scope ) {
324
- let variable = scope . set . get ( name ) ;
330
+ const variable = scope . set . get ( name ) ;
325
331
326
332
if ( variable ) {
327
333
return variable ;
@@ -345,9 +351,9 @@ module.exports = {
345
351
* If the location is below, this judges `this` is valid.
346
352
*
347
353
* - The location is not on an object literal.
348
- * - The location does not assign to a property .
354
+ * - The location is not assigned to a variable which starts with an uppercase letter .
349
355
* - The location is not on an ES2015 class.
350
- * - The location does not call its `bind`/`call`/`apply` method directly.
356
+ * - Its `bind`/`call`/`apply` method is not called directly.
351
357
* - The function is not a callback of array methods (such as `.forEach()`) if `thisArg` is given.
352
358
*
353
359
* @param {ASTNode } node - A function node to check.
@@ -358,9 +364,10 @@ module.exports = {
358
364
if ( isES5Constructor ( node ) || hasJSDocThisTag ( node , sourceCode ) ) {
359
365
return false ;
360
366
}
367
+ const isAnonymous = node . id === null ;
361
368
362
369
while ( node ) {
363
- let parent = node . parent ;
370
+ const parent = node . parent ;
364
371
365
372
switch ( parent . type ) {
366
373
@@ -392,25 +399,44 @@ module.exports = {
392
399
// e.g.
393
400
// var obj = { foo() { ... } };
394
401
// var obj = { foo: function() { ... } };
395
- case "Property" :
396
- return false ;
397
-
398
- // e.g.
399
- // obj.foo = foo() { ... };
400
- case "AssignmentExpression" :
401
- return (
402
- parent . right !== node ||
403
- parent . left . type !== "MemberExpression"
404
- ) ;
405
-
406
- // e.g.
407
402
// class A { constructor() { ... } }
408
403
// class A { foo() { ... } }
409
404
// class A { get foo() { ... } }
410
405
// class A { set foo() { ... } }
411
406
// class A { static foo() { ... } }
407
+ case "Property" :
412
408
case "MethodDefinition" :
413
- return false ;
409
+ return parent . value !== node ;
410
+
411
+ // e.g.
412
+ // obj.foo = function foo() { ... };
413
+ // Foo = function() { ... };
414
+ // [obj.foo = function foo() { ... }] = a;
415
+ // [Foo = function() { ... }] = a;
416
+ case "AssignmentExpression" :
417
+ case "AssignmentPattern" :
418
+ if ( parent . right === node ) {
419
+ if ( parent . left . type === "MemberExpression" ) {
420
+ return false ;
421
+ }
422
+ if ( isAnonymous &&
423
+ parent . left . type === "Identifier" &&
424
+ startsWithUpperCase ( parent . left . name )
425
+ ) {
426
+ return false ;
427
+ }
428
+ }
429
+ return true ;
430
+
431
+ // e.g.
432
+ // var Foo = function() { ... };
433
+ case "VariableDeclarator" :
434
+ return ! (
435
+ isAnonymous &&
436
+ parent . init === node &&
437
+ parent . id . type === "Identifier" &&
438
+ startsWithUpperCase ( parent . id . name )
439
+ ) ;
414
440
415
441
// e.g.
416
442
// var foo = function foo() { ... }.bind(obj);
@@ -585,5 +611,74 @@ module.exports = {
585
611
*/
586
612
isFunction : function ( node ) {
587
613
return Boolean ( node && anyFunctionPattern . test ( node . type ) ) ;
614
+ } ,
615
+
616
+ /**
617
+ * Gets the property name of a given node.
618
+ * The node can be a MemberExpression, a Property, or a MethodDefinition.
619
+ *
620
+ * If the name is dynamic, this returns `null`.
621
+ *
622
+ * For examples:
623
+ *
624
+ * a.b // => "b"
625
+ * a["b"] // => "b"
626
+ * a['b'] // => "b"
627
+ * a[`b`] // => "b"
628
+ * a[100] // => "100"
629
+ * a[b] // => null
630
+ * a["a" + "b"] // => null
631
+ * a[tag`b`] // => null
632
+ * a[`${b}`] // => null
633
+ *
634
+ * let a = {b: 1} // => "b"
635
+ * let a = {["b"]: 1} // => "b"
636
+ * let a = {['b']: 1} // => "b"
637
+ * let a = {[`b`]: 1} // => "b"
638
+ * let a = {[100]: 1} // => "100"
639
+ * let a = {[b]: 1} // => null
640
+ * let a = {["a" + "b"]: 1} // => null
641
+ * let a = {[tag`b`]: 1} // => null
642
+ * let a = {[`${b}`]: 1 } // => null
643
+ *
644
+ * @param {ASTNode } node - The node to get.
645
+ * @returns {string|null } The property name if static. Otherwise, null.
646
+ */
647
+ getStaticPropertyName ( node ) {
648
+ let prop ;
649
+
650
+ switch ( node && node . type ) {
651
+ case "Property" :
652
+ case "MethodDefinition" :
653
+ prop = node . key ;
654
+ break ;
655
+
656
+ case "MemberExpression" :
657
+ prop = node . property ;
658
+ break ;
659
+
660
+ // no default
661
+ }
662
+
663
+ switch ( prop && prop . type ) {
664
+ case "Literal" :
665
+ return String ( prop . value ) ;
666
+
667
+ case "TemplateLiteral" :
668
+ if ( prop . expressions . length === 0 && prop . quasis . length === 1 ) {
669
+ return prop . quasis [ 0 ] . value . cooked ;
670
+ }
671
+ break ;
672
+
673
+ case "Identifier" :
674
+ if ( ! node . computed ) {
675
+ return prop . name ;
676
+ }
677
+ break ;
678
+
679
+ // no default
680
+ }
681
+
682
+ return null ;
588
683
}
589
684
} ;
0 commit comments