99// Requirements
1010//------------------------------------------------------------------------------
1111
12- let esutils = require ( "esutils" ) ;
12+ const esutils = require ( "esutils" ) ;
1313
1414//------------------------------------------------------------------------------
1515// Helpers
1616//------------------------------------------------------------------------------
1717
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;
2525
2626/**
2727 * Checks reference if is non initializer and writable.
@@ -32,15 +32,14 @@ let thisTagPattern = /^[\s\*]*@this/m;
3232 * @private
3333 */
3434function isModifyingReference ( reference , index , references ) {
35- let identifier = reference . identifier ,
36- modifyingDifferentIdentifier ;
35+ const identifier = reference . identifier ;
3736
3837 /*
3938 * Destructuring assignments can have multiple default value, so
4039 * possibly there are multiple writeable references for the same
4140 * identifier.
4241 */
43- modifyingDifferentIdentifier = index === 0 ||
42+ const modifyingDifferentIdentifier = index === 0 ||
4443 references [ index - 1 ] . identifier !== identifier ;
4544
4645 return ( identifier &&
@@ -50,16 +49,23 @@ function isModifyingReference(reference, index, references) {
5049 ) ;
5150}
5251
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+
5362/**
5463 * Checks whether or not a node is a constructor.
5564 * @param {ASTNode } node - A function node to check.
5665 * @returns {boolean } Wehether or not a node is a constructor.
5766 */
5867function 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 ) ) ;
6369}
6470
6571/**
@@ -160,7 +166,7 @@ function isMethodWhichHasThisArg(node) {
160166 * @returns {boolean } Whether or not the node has a `@this` tag in its comments.
161167 */
162168function hasJSDocThisTag ( node , sourceCode ) {
163- let jsdocComment = sourceCode . getJSDocComment ( node ) ;
169+ const jsdocComment = sourceCode . getJSDocComment ( node ) ;
164170
165171 if ( jsdocComment && thisTagPattern . test ( jsdocComment . value ) ) {
166172 return true ;
@@ -183,7 +189,7 @@ function hasJSDocThisTag(node, sourceCode) {
183189 * @private
184190 */
185191function isParenthesised ( sourceCode , node ) {
186- let previousToken = sourceCode . getTokenBefore ( node ) ,
192+ const previousToken = sourceCode . getTokenBefore ( node ) ,
187193 nextToken = sourceCode . getTokenAfter ( node ) ;
188194
189195 return Boolean ( previousToken && nextToken ) &&
@@ -285,7 +291,7 @@ module.exports = {
285291 * @returns {boolean } `true` if the node is an ESLint directive comment
286292 */
287293 isDirectiveComment : function ( node ) {
288- let comment = node . value . trim ( ) ;
294+ const comment = node . value . trim ( ) ;
289295
290296 return (
291297 node . type === "Line" && comment . indexOf ( "eslint-" ) === 0 ||
@@ -321,7 +327,7 @@ module.exports = {
321327 let scope = initScope ;
322328
323329 while ( scope ) {
324- let variable = scope . set . get ( name ) ;
330+ const variable = scope . set . get ( name ) ;
325331
326332 if ( variable ) {
327333 return variable ;
@@ -345,9 +351,9 @@ module.exports = {
345351 * If the location is below, this judges `this` is valid.
346352 *
347353 * - 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 .
349355 * - 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.
351357 * - The function is not a callback of array methods (such as `.forEach()`) if `thisArg` is given.
352358 *
353359 * @param {ASTNode } node - A function node to check.
@@ -358,9 +364,10 @@ module.exports = {
358364 if ( isES5Constructor ( node ) || hasJSDocThisTag ( node , sourceCode ) ) {
359365 return false ;
360366 }
367+ const isAnonymous = node . id === null ;
361368
362369 while ( node ) {
363- let parent = node . parent ;
370+ const parent = node . parent ;
364371
365372 switch ( parent . type ) {
366373
@@ -392,25 +399,44 @@ module.exports = {
392399 // e.g.
393400 // var obj = { foo() { ... } };
394401 // 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.
407402 // class A { constructor() { ... } }
408403 // class A { foo() { ... } }
409404 // class A { get foo() { ... } }
410405 // class A { set foo() { ... } }
411406 // class A { static foo() { ... } }
407+ case "Property" :
412408 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+ ) ;
414440
415441 // e.g.
416442 // var foo = function foo() { ... }.bind(obj);
@@ -585,5 +611,74 @@ module.exports = {
585611 */
586612 isFunction : function ( node ) {
587613 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 ;
588683 }
589684} ;
0 commit comments