@@ -13,11 +13,26 @@ namespace ts {
13
13
referenced : boolean ;
14
14
}
15
15
16
- export function getModuleInstanceState ( node : ModuleDeclaration ) : ModuleInstanceState {
17
- return node . body ? getModuleInstanceStateWorker ( node . body ) : ModuleInstanceState . Instantiated ;
16
+ export function getModuleInstanceState ( node : ModuleDeclaration , visited ?: Map < ModuleInstanceState | undefined > ) : ModuleInstanceState {
17
+ if ( node . body && ! node . body . parent ) {
18
+ // getModuleInstanceStateForAliasTarget needs to walk up the parent chain, so parent pointers must be set on this tree already
19
+ setParentPointers ( node , node . body ) ;
20
+ }
21
+ return node . body ? getModuleInstanceStateCached ( node . body , visited ) : ModuleInstanceState . Instantiated ;
18
22
}
19
23
20
- function getModuleInstanceStateWorker ( node : Node ) : ModuleInstanceState {
24
+ function getModuleInstanceStateCached ( node : Node , visited = createMap < ModuleInstanceState | undefined > ( ) ) {
25
+ const nodeId = "" + getNodeId ( node ) ;
26
+ if ( visited . has ( nodeId ) ) {
27
+ return visited . get ( nodeId ) || ModuleInstanceState . NonInstantiated ;
28
+ }
29
+ visited . set ( nodeId , undefined ) ;
30
+ const result = getModuleInstanceStateWorker ( node , visited ) ;
31
+ visited . set ( nodeId , result ) ;
32
+ return result ;
33
+ }
34
+
35
+ function getModuleInstanceStateWorker ( node : Node , visited : Map < ModuleInstanceState | undefined > ) : ModuleInstanceState {
21
36
// A module is uninstantiated if it contains only
22
37
switch ( node . kind ) {
23
38
// 1. interface declarations, type alias declarations
@@ -37,11 +52,27 @@ namespace ts {
37
52
return ModuleInstanceState . NonInstantiated ;
38
53
}
39
54
break ;
40
- // 4. other uninstantiated module declarations.
55
+ // 4. Export alias declarations pointing at only uninstantiated modules or things uninstantiated modules contain
56
+ case SyntaxKind . ExportDeclaration :
57
+ if ( ! ( node as ExportDeclaration ) . moduleSpecifier && ! ! ( node as ExportDeclaration ) . exportClause ) {
58
+ let state = ModuleInstanceState . NonInstantiated ;
59
+ for ( const specifier of ( node as ExportDeclaration ) . exportClause ! . elements ) {
60
+ const specifierState = getModuleInstanceStateForAliasTarget ( specifier , visited ) ;
61
+ if ( specifierState > state ) {
62
+ state = specifierState ;
63
+ }
64
+ if ( state === ModuleInstanceState . Instantiated ) {
65
+ return state ;
66
+ }
67
+ }
68
+ return state ;
69
+ }
70
+ break ;
71
+ // 5. other uninstantiated module declarations.
41
72
case SyntaxKind . ModuleBlock : {
42
73
let state = ModuleInstanceState . NonInstantiated ;
43
74
forEachChild ( node , n => {
44
- const childState = getModuleInstanceStateWorker ( n ) ;
75
+ const childState = getModuleInstanceStateCached ( n , visited ) ;
45
76
switch ( childState ) {
46
77
case ModuleInstanceState . NonInstantiated :
47
78
// child is non-instantiated - continue searching
@@ -61,7 +92,7 @@ namespace ts {
61
92
return state ;
62
93
}
63
94
case SyntaxKind . ModuleDeclaration :
64
- return getModuleInstanceState ( node as ModuleDeclaration ) ;
95
+ return getModuleInstanceState ( node as ModuleDeclaration , visited ) ;
65
96
case SyntaxKind . Identifier :
66
97
// Only jsdoc typedef definition can exist in jsdoc namespace, and it should
67
98
// be considered the same as type alias
@@ -72,6 +103,36 @@ namespace ts {
72
103
return ModuleInstanceState . Instantiated ;
73
104
}
74
105
106
+ function getModuleInstanceStateForAliasTarget ( specifier : ExportSpecifier , visited : Map < ModuleInstanceState | undefined > ) {
107
+ const name = specifier . propertyName || specifier . name ;
108
+ let p : Node | undefined = specifier . parent ;
109
+ while ( p ) {
110
+ if ( isBlock ( p ) || isModuleBlock ( p ) || isSourceFile ( p ) ) {
111
+ const statements = p . statements ;
112
+ let found : ModuleInstanceState | undefined ;
113
+ for ( const statement of statements ) {
114
+ if ( nodeHasName ( statement , name ) ) {
115
+ if ( ! statement . parent ) {
116
+ setParentPointers ( p , statement ) ;
117
+ }
118
+ const state = getModuleInstanceStateCached ( statement , visited ) ;
119
+ if ( found === undefined || state > found ) {
120
+ found = state ;
121
+ }
122
+ if ( found === ModuleInstanceState . Instantiated ) {
123
+ return found ;
124
+ }
125
+ }
126
+ }
127
+ if ( found !== undefined ) {
128
+ return found ;
129
+ }
130
+ }
131
+ p = p . parent ;
132
+ }
133
+ return ModuleInstanceState . Instantiated ; // Couldn't locate, assume could refer to a value
134
+ }
135
+
75
136
const enum ContainerFlags {
76
137
// The current node is not a container, and no container manipulation should happen before
77
138
// recursing into it.
@@ -2561,7 +2622,7 @@ namespace ts {
2561
2622
// Declare a 'member' if the container is an ES5 class or ES6 constructor
2562
2623
constructorSymbol . members = constructorSymbol . members || createSymbolTable ( ) ;
2563
2624
// It's acceptable for multiple 'this' assignments of the same identifier to occur
2564
- declareSymbol ( constructorSymbol . members , constructorSymbol , node , SymbolFlags . Property , SymbolFlags . PropertyExcludes & ~ SymbolFlags . Property ) ;
2625
+ declareSymbol ( constructorSymbol . members , constructorSymbol , node , SymbolFlags . Property | SymbolFlags . Assignment , SymbolFlags . PropertyExcludes & ~ SymbolFlags . Property ) ;
2565
2626
addDeclarationToSymbol ( constructorSymbol , constructorSymbol . valueDeclaration , SymbolFlags . Class ) ;
2566
2627
}
2567
2628
break ;
@@ -2575,7 +2636,7 @@ namespace ts {
2575
2636
// Bind this property to the containing class
2576
2637
const containingClass = thisContainer . parent ;
2577
2638
const symbolTable = hasModifier ( thisContainer , ModifierFlags . Static ) ? containingClass . symbol . exports ! : containingClass . symbol . members ! ;
2578
- declareSymbol ( symbolTable , containingClass . symbol , node , SymbolFlags . Property , SymbolFlags . None , /*isReplaceableByMethod*/ true ) ;
2639
+ declareSymbol ( symbolTable , containingClass . symbol , node , SymbolFlags . Property | SymbolFlags . Assignment , SymbolFlags . None , /*isReplaceableByMethod*/ true ) ;
2579
2640
break ;
2580
2641
case SyntaxKind . SourceFile :
2581
2642
// this.property = assignment in a source file -- declare symbol in exports for a module, in locals for a script
0 commit comments