@@ -15,7 +15,7 @@ import {Logger} from '../logging/logger';
1515import { BundleProgram } from '../packages/bundle_program' ;
1616import { findAll , getNameText , hasNameIdentifier , isDefined , stripDollarSuffix } from '../utils' ;
1717
18- import { ModuleWithProvidersFunction , NgccClassSymbol , NgccReflectionHost , PRE_R3_MARKER , SwitchableVariableDeclaration , isSwitchableVariableDeclaration } from './ngcc_host' ;
18+ import { ClassSymbol , ModuleWithProvidersFunction , NgccClassSymbol , NgccReflectionHost , PRE_R3_MARKER , SwitchableVariableDeclaration , isSwitchableVariableDeclaration } from './ngcc_host' ;
1919
2020export const DECORATORS = 'decorators' as ts . __String ;
2121export const PROP_DECORATORS = 'propDecorators' as ts . __String ;
@@ -91,7 +91,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
9191 }
9292
9393 /**
94- * Find the declaration of a node that we think is a class.
94+ * Find a symbol for a node that we think is a class.
9595 * Classes should have a `name` identifier, because they may need to be referenced in other parts
9696 * of the program.
9797 *
@@ -104,22 +104,111 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
104104 * Here, the intermediate `MyClass_1` assignment is optional. In the above example, the
105105 * `class MyClass {}` node is returned as declaration of `MyClass`.
106106 *
107- * @param node the node that represents the class whose declaration we are finding.
108- * @returns the declaration of the class or `undefined` if it is not a "class".
107+ * @param declaration the declaration node whose symbol we are finding.
108+ * @returns the symbol for the node or `undefined` if it is not a "class" or has no symbol .
109109 */
110- getClassDeclaration ( node : ts . Node ) : ClassDeclaration | undefined {
111- return getInnerClassDeclaration ( node ) || undefined ;
110+ getClassSymbol ( declaration : ts . Node ) : NgccClassSymbol | undefined {
111+ const symbol = this . getClassSymbolFromOuterDeclaration ( declaration ) ;
112+ if ( symbol !== undefined ) {
113+ return symbol ;
114+ }
115+
116+ return this . getClassSymbolFromInnerDeclaration ( declaration ) ;
112117 }
113118
114119 /**
115- * Find a symbol for a node that we think is a class.
116- * @param node the node whose symbol we are finding.
117- * @returns the symbol for the node or `undefined` if it is not a "class" or has no symbol.
120+ * In ES2015, a class may be declared using a variable declaration of the following structure:
121+ *
122+ * ```
123+ * var MyClass = MyClass_1 = class MyClass {};
124+ * ```
125+ *
126+ * This method extracts the `NgccClassSymbol` for `MyClass` when provided with the `var MyClass`
127+ * declaration node. When the `class MyClass {}` node or any other node is given, this method will
128+ * return undefined instead.
129+ *
130+ * @param declaration the declaration whose symbol we are finding.
131+ * @returns the symbol for the node or `undefined` if it does not represent an outer declaration
132+ * of a class.
118133 */
119- getClassSymbol ( declaration : ts . Node ) : NgccClassSymbol | undefined {
120- const classDeclaration = this . getClassDeclaration ( declaration ) ;
121- return classDeclaration &&
122- this . checker . getSymbolAtLocation ( classDeclaration . name ) as NgccClassSymbol ;
134+ protected getClassSymbolFromOuterDeclaration ( declaration : ts . Node ) : NgccClassSymbol | undefined {
135+ // Create a symbol without inner declaration if the declaration is a regular class declaration.
136+ if ( ts . isClassDeclaration ( declaration ) && hasNameIdentifier ( declaration ) ) {
137+ return this . createClassSymbol ( declaration , null ) ;
138+ }
139+
140+ // Otherwise, the declaration may be a variable declaration, in which case it must be
141+ // initialized using a class expression as inner declaration.
142+ if ( ts . isVariableDeclaration ( declaration ) && hasNameIdentifier ( declaration ) ) {
143+ const innerDeclaration = getInnerClassDeclaration ( declaration ) ;
144+ if ( innerDeclaration !== null ) {
145+ return this . createClassSymbol ( declaration , innerDeclaration ) ;
146+ }
147+ }
148+
149+ return undefined ;
150+ }
151+
152+ /**
153+ * In ES2015, a class may be declared using a variable declaration of the following structure:
154+ *
155+ * ```
156+ * var MyClass = MyClass_1 = class MyClass {};
157+ * ```
158+ *
159+ * This method extracts the `NgccClassSymbol` for `MyClass` when provided with the
160+ * `class MyClass {}` declaration node. When the `var MyClass` node or any other node is given,
161+ * this method will return undefined instead.
162+ *
163+ * @param declaration the declaration whose symbol we are finding.
164+ * @returns the symbol for the node or `undefined` if it does not represent an inner declaration
165+ * of a class.
166+ */
167+ protected getClassSymbolFromInnerDeclaration ( declaration : ts . Node ) : NgccClassSymbol | undefined {
168+ if ( ! ts . isClassExpression ( declaration ) || ! hasNameIdentifier ( declaration ) ) {
169+ return undefined ;
170+ }
171+
172+ const outerDeclaration = getVariableDeclarationOfDeclaration ( declaration ) ;
173+ if ( outerDeclaration === undefined || ! hasNameIdentifier ( outerDeclaration ) ) {
174+ return undefined ;
175+ }
176+
177+ return this . createClassSymbol ( outerDeclaration , declaration ) ;
178+ }
179+
180+ /**
181+ * Creates an `NgccClassSymbol` from an outer and inner declaration. If a class only has an outer
182+ * declaration, the "implementation" symbol of the created `NgccClassSymbol` will be set equal to
183+ * the "declaration" symbol.
184+ *
185+ * @param outerDeclaration The outer declaration node of the class.
186+ * @param innerDeclaration The inner declaration node of the class, or undefined if no inner
187+ * declaration is present.
188+ * @returns the `NgccClassSymbol` representing the class, or undefined if a `ts.Symbol` for any of
189+ * the declarations could not be resolved.
190+ */
191+ protected createClassSymbol (
192+ outerDeclaration : ClassDeclaration , innerDeclaration : ClassDeclaration | null ) : NgccClassSymbol
193+ | undefined {
194+ const declarationSymbol =
195+ this . checker . getSymbolAtLocation ( outerDeclaration . name ) as ClassSymbol | undefined ;
196+ if ( declarationSymbol === undefined ) {
197+ return undefined ;
198+ }
199+
200+ const implementationSymbol = innerDeclaration !== null ?
201+ this . checker . getSymbolAtLocation ( innerDeclaration . name ) :
202+ declarationSymbol ;
203+ if ( implementationSymbol === undefined ) {
204+ return undefined ;
205+ }
206+
207+ return {
208+ name : declarationSymbol . name ,
209+ declaration : declarationSymbol ,
210+ implementation : implementationSymbol ,
211+ } ;
123212 }
124213
125214 /**
@@ -221,7 +310,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
221310 * Check whether the given node actually represents a class.
222311 */
223312 isClass ( node : ts . Node ) : node is ClassDeclaration {
224- return super . isClass ( node ) || ! ! this . getClassDeclaration ( node ) ;
313+ return super . isClass ( node ) || this . getClassSymbol ( node ) !== undefined ;
225314 }
226315
227316 /**
@@ -549,7 +638,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
549638 */
550639 protected getStaticProperty ( symbol : NgccClassSymbol , propertyName : ts . __String ) : ts . Symbol
551640 | undefined {
552- return symbol . exports && symbol . exports . get ( propertyName ) ;
641+ return symbol . implementation . exports && symbol . implementation . exports . get ( propertyName ) ;
553642 }
554643
555644 /**
@@ -561,8 +650,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
561650 * @returns all information of the decorators on the class.
562651 */
563652 protected acquireDecoratorInfo ( classSymbol : NgccClassSymbol ) : DecoratorInfo {
564- if ( this . decoratorCache . has ( classSymbol . valueDeclaration ) ) {
565- return this . decoratorCache . get ( classSymbol . valueDeclaration ) ! ;
653+ const decl = classSymbol . declaration . valueDeclaration ;
654+ if ( this . decoratorCache . has ( decl ) ) {
655+ return this . decoratorCache . get ( decl ) ! ;
566656 }
567657
568658 // First attempt extracting decorators from static properties.
@@ -572,7 +662,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
572662 decoratorInfo = this . computeDecoratorInfoFromHelperCalls ( classSymbol ) ;
573663 }
574664
575- this . decoratorCache . set ( classSymbol . valueDeclaration , decoratorInfo ) ;
665+ this . decoratorCache . set ( decl , decoratorInfo ) ;
576666 return decoratorInfo ;
577667 }
578668
@@ -665,8 +755,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
665755
666756 // The member map contains all the method (instance and static); and any instance properties
667757 // that are initialized in the class.
668- if ( symbol . members ) {
669- symbol . members . forEach ( ( value , key ) => {
758+ if ( symbol . implementation . members ) {
759+ symbol . implementation . members . forEach ( ( value , key ) => {
670760 const decorators = decoratorsMap . get ( key as string ) ;
671761 const reflectedMembers = this . reflectMembers ( value , decorators ) ;
672762 if ( reflectedMembers ) {
@@ -677,8 +767,8 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
677767 }
678768
679769 // The static property map contains all the static properties
680- if ( symbol . exports ) {
681- symbol . exports . forEach ( ( value , key ) => {
770+ if ( symbol . implementation . exports ) {
771+ symbol . implementation . exports . forEach ( ( value , key ) => {
682772 const decorators = decoratorsMap . get ( key as string ) ;
683773 const reflectedMembers = this . reflectMembers ( value , decorators , true ) ;
684774 if ( reflectedMembers ) {
@@ -697,11 +787,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
697787 // }
698788 // MyClass.staticProperty = ...;
699789 // ```
700- const variableDeclaration = getVariableDeclarationOfDeclaration ( symbol . valueDeclaration ) ;
701- if ( variableDeclaration !== undefined ) {
702- const variableSymbol = this . checker . getSymbolAtLocation ( variableDeclaration . name ) ;
703- if ( variableSymbol && variableSymbol . exports ) {
704- variableSymbol . exports . forEach ( ( value , key ) => {
790+ if ( ts . isVariableDeclaration ( symbol . declaration . valueDeclaration ) ) {
791+ if ( symbol . declaration . exports ) {
792+ symbol . declaration . exports . forEach ( ( value , key ) => {
705793 const decorators = decoratorsMap . get ( key as string ) ;
706794 const reflectedMembers = this . reflectMembers ( value , decorators , true ) ;
707795 if ( reflectedMembers ) {
@@ -1182,8 +1270,9 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
11821270 */
11831271 protected getConstructorParameterDeclarations ( classSymbol : NgccClassSymbol ) :
11841272 ts . ParameterDeclaration [ ] | null {
1185- if ( classSymbol . members && classSymbol . members . has ( CONSTRUCTOR ) ) {
1186- const constructorSymbol = classSymbol . members . get ( CONSTRUCTOR ) ! ;
1273+ const members = classSymbol . implementation . members ;
1274+ if ( members && members . has ( CONSTRUCTOR ) ) {
1275+ const constructorSymbol = members . get ( CONSTRUCTOR ) ! ;
11871276 // For some reason the constructor does not have a `valueDeclaration` ?!?
11881277 const constructor = constructorSymbol . declarations &&
11891278 constructorSymbol . declarations [ 0 ] as ts . ConstructorDeclaration | undefined ;
@@ -1314,7 +1403,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
13141403 * @returns an array of statements that may contain helper calls.
13151404 */
13161405 protected getStatementsForClass ( classSymbol : NgccClassSymbol ) : ts . Statement [ ] {
1317- return Array . from ( classSymbol . valueDeclaration . getSourceFile ( ) . statements ) ;
1406+ return Array . from ( classSymbol . declaration . valueDeclaration . getSourceFile ( ) . statements ) ;
13181407 }
13191408
13201409 /**
@@ -1637,28 +1726,29 @@ function getCalleeName(call: ts.CallExpression): string|null {
16371726 * ```
16381727 *
16391728 * Here, the intermediate `MyClass_1` assignment is optional. In the above example, the
1640- * `class MyClass {}` expression is returned as declaration of `MyClass`. Note that if `node`
1641- * represents a regular class declaration, it will be returned as-is .
1729+ * `class MyClass {}` expression is returned as declaration of `var MyClass`. If the variable
1730+ * is not initialized using a class expression, null is returned.
16421731 *
16431732 * @param node the node that represents the class whose declaration we are finding.
16441733 * @returns the declaration of the class or `null` if it is not a "class".
16451734 */
1646- function getInnerClassDeclaration ( node : ts . Node ) :
1647- ClassDeclaration < ts . ClassDeclaration | ts . ClassExpression > | null {
1735+ function getInnerClassDeclaration ( node : ts . Node ) : ClassDeclaration < ts . ClassExpression > | null {
1736+ if ( ! ts . isVariableDeclaration ( node ) || node . initializer === undefined ) {
1737+ return null ;
1738+ }
1739+
16481740 // Recognize a variable declaration of the form `var MyClass = class MyClass {}` or
16491741 // `var MyClass = MyClass_1 = class MyClass {};`
1650- if ( ts . isVariableDeclaration ( node ) && node . initializer !== undefined ) {
1651- node = node . initializer ;
1652- while ( isAssignment ( node ) ) {
1653- node = node . right ;
1654- }
1742+ let expression = node . initializer ;
1743+ while ( isAssignment ( expression ) ) {
1744+ expression = expression . right ;
16551745 }
16561746
1657- if ( ! ts . isClassDeclaration ( node ) && ! ts . isClassExpression ( node ) ) {
1747+ if ( ! ts . isClassExpression ( expression ) || ! hasNameIdentifier ( expression ) ) {
16581748 return null ;
16591749 }
16601750
1661- return hasNameIdentifier ( node ) ? node : null ;
1751+ return expression ;
16621752}
16631753
16641754function getDecoratorArgs ( node : ts . ObjectLiteralExpression ) : ts . Expression [ ] {
0 commit comments