@@ -16,23 +16,29 @@ const isCommentToken = (token) => {
1616
1717const decoratorMetaTokens = new Map ( [ [ ')' , '(' ] , [ '>' , '<' ] ] ) ;
1818
19+ // eslint-disable-next-line complexity
1920const getDecorator = ( token , sourceCode ) => {
2021 if ( ! token ) {
2122 return false ;
2223 }
24+
2325 if ( token . type === 'Punctuator' ) {
2426 const tokenClose = token . value ;
2527 const tokenOpen = decoratorMetaTokens . get ( tokenClose ) ;
2628 if ( tokenOpen ) {
2729 let nested = 0 ;
2830 let tokenBefore = token ;
31+ let exitEarlyUnlessTyped = false ;
2932 do {
3033 tokenBefore = sourceCode . getTokenBefore ( tokenBefore , { includeComments : true } ) ;
3134 // istanbul ignore if
3235 if ( tokenBefore && tokenBefore . type === 'Punctuator' ) {
3336 if ( tokenBefore . value === tokenClose ) {
3437 nested ++ ;
3538 } else if ( tokenBefore . value === tokenOpen ) {
39+ if ( tokenOpen === '(' ) {
40+ exitEarlyUnlessTyped = true ;
41+ }
3642 if ( nested ) {
3743 nested -- ;
3844 } else {
@@ -42,7 +48,33 @@ const getDecorator = (token, sourceCode) => {
4248 }
4349 } while ( tokenBefore ) ;
4450
45- return getDecorator ( tokenBefore , sourceCode ) ;
51+ // Because our token retrieval doesn't give us as much info as AST, and
52+ // because decorators can be nested and fairly complex, besides finding
53+ // any decorators, we also want to avoid checking backwards indefinitely
54+ // in a potentially large document, so we exit early if parentheses are
55+ // not preceded by a type where we have to keep checking backward for
56+ // now (such as a regular call expression) or if a decorator is found.
57+ if ( exitEarlyUnlessTyped ) {
58+ // Check for `@someDecorator(`
59+ const identifier = sourceCode . getTokenBefore ( tokenBefore , { includeComments : true } ) ;
60+ if ( identifier && identifier . type === 'Identifier' ) {
61+ const before = sourceCode . getTokenBefore ( identifier , { includeComments : true } ) ;
62+ if ( before && before . type === 'Punctuator' && before . value === '@' ) {
63+ // Decorator found
64+ return before ;
65+ }
66+ }
67+
68+ // If decorators may have `:`, we might need to check for those as with typed.
69+ if ( ! identifier || identifier . type !== 'Punctuator' || identifier . value !== '>' ) {
70+ // Should not be a decorator
71+ return false ;
72+ }
73+
74+ // Could be a typed decorator, so keep checking for one
75+ }
76+
77+ return getDecorator ( tokenBefore , sourceCode , true ) ;
4678 }
4779 if ( token . value === '@' ) {
4880 return token ;
0 commit comments