1
1
import * as fs from 'fs' ;
2
- import { dirname , join , resolve } from 'path' ;
2
+ import { join } from 'path' ;
3
3
import * as ts from 'typescript' ;
4
4
5
+ import { TypeScriptFileRefactor } from './refactor' ;
5
6
6
- function _createSource ( path : string ) : ts . SourceFile {
7
- return ts . createSourceFile ( path , fs . readFileSync ( path , 'utf-8' ) , ts . ScriptTarget . Latest ) ;
8
- }
9
-
10
- function _findNodes ( sourceFile : ts . SourceFile , node : ts . Node , kind : ts . SyntaxKind ,
11
- keepGoing = false ) : ts . Node [ ] {
12
- if ( node . kind == kind && ! keepGoing ) {
13
- return [ node ] ;
14
- }
15
7
16
- return node . getChildren ( sourceFile ) . reduce ( ( result , n ) => {
17
- return result . concat ( _findNodes ( sourceFile , n , kind , keepGoing ) ) ;
18
- } , node . kind == kind ? [ node ] : [ ] ) ;
19
- }
20
-
21
- function _recursiveSymbolExportLookup ( sourcePath : string ,
22
- sourceFile : ts . SourceFile ,
23
- symbolName : string ) : string | null {
8
+ function _recursiveSymbolExportLookup ( refactor : TypeScriptFileRefactor ,
9
+ symbolName : string ,
10
+ host : ts . CompilerHost ,
11
+ program : ts . Program ) : string | null {
24
12
// Check this file.
25
- const hasSymbol = _findNodes ( sourceFile , sourceFile , ts . SyntaxKind . ClassDeclaration )
13
+ const hasSymbol = refactor . findAstNodes ( null , ts . SyntaxKind . ClassDeclaration )
26
14
. some ( ( cd : ts . ClassDeclaration ) => {
27
15
return cd . name && cd . name . text == symbolName ;
28
16
} ) ;
29
17
if ( hasSymbol ) {
30
- return sourcePath ;
18
+ return refactor . fileName ;
31
19
}
32
20
33
21
// We found the bootstrap variable, now we just need to get where it's imported.
34
- const exports = _findNodes ( sourceFile , sourceFile , ts . SyntaxKind . ExportDeclaration , false )
22
+ const exports = refactor . findAstNodes ( null , ts . SyntaxKind . ExportDeclaration )
35
23
. map ( node => node as ts . ExportDeclaration ) ;
36
24
37
25
for ( const decl of exports ) {
38
26
if ( ! decl . moduleSpecifier || decl . moduleSpecifier . kind !== ts . SyntaxKind . StringLiteral ) {
39
27
continue ;
40
28
}
41
29
42
- const module = resolve ( dirname ( sourcePath ) , ( decl . moduleSpecifier as ts . StringLiteral ) . text ) ;
30
+ const modulePath = ( decl . moduleSpecifier as ts . StringLiteral ) . text ;
31
+ const resolvedModule = ts . resolveModuleName (
32
+ modulePath , refactor . fileName , program . getCompilerOptions ( ) , host ) ;
33
+ if ( ! resolvedModule . resolvedModule || ! resolvedModule . resolvedModule . resolvedFileName ) {
34
+ return null ;
35
+ }
36
+
37
+ const module = resolvedModule . resolvedModule . resolvedFileName ;
43
38
if ( ! decl . exportClause ) {
44
- const moduleTs = module + '.ts' ;
45
- if ( fs . existsSync ( moduleTs ) ) {
46
- const moduleSource = _createSource ( moduleTs ) ;
47
- const maybeModule = _recursiveSymbolExportLookup ( module , moduleSource , symbolName ) ;
48
- if ( maybeModule ) {
49
- return maybeModule ;
50
- }
39
+ const moduleRefactor = new TypeScriptFileRefactor ( module , host , program ) ;
40
+ const maybeModule = _recursiveSymbolExportLookup ( moduleRefactor , symbolName , host , program ) ;
41
+ if ( maybeModule ) {
42
+ return maybeModule ;
51
43
}
52
44
continue ;
53
45
}
@@ -59,25 +51,24 @@ function _recursiveSymbolExportLookup(sourcePath: string,
59
51
if ( fs . statSync ( module ) . isDirectory ( ) ) {
60
52
const indexModule = join ( module , 'index.ts' ) ;
61
53
if ( fs . existsSync ( indexModule ) ) {
54
+ const indexRefactor = new TypeScriptFileRefactor ( indexModule , host , program ) ;
62
55
const maybeModule = _recursiveSymbolExportLookup (
63
- indexModule , _createSource ( indexModule ) , symbolName ) ;
56
+ indexRefactor , symbolName , host , program ) ;
64
57
if ( maybeModule ) {
65
58
return maybeModule ;
66
59
}
67
60
}
68
61
}
69
62
70
63
// Create the source and verify that the symbol is at least a class.
71
- const source = _createSource ( module ) ;
72
- const hasSymbol = _findNodes ( source , source , ts . SyntaxKind . ClassDeclaration )
64
+ const source = new TypeScriptFileRefactor ( module , host , program ) ;
65
+ const hasSymbol = source . findAstNodes ( null , ts . SyntaxKind . ClassDeclaration )
73
66
. some ( ( cd : ts . ClassDeclaration ) => {
74
67
return cd . name && cd . name . text == symbolName ;
75
68
} ) ;
76
69
77
70
if ( hasSymbol ) {
78
71
return module ;
79
- } else {
80
- return null ;
81
72
}
82
73
}
83
74
}
@@ -86,11 +77,12 @@ function _recursiveSymbolExportLookup(sourcePath: string,
86
77
return null ;
87
78
}
88
79
89
- function _symbolImportLookup ( sourcePath : string ,
90
- sourceFile : ts . SourceFile ,
91
- symbolName : string ) : string | null {
80
+ function _symbolImportLookup ( refactor : TypeScriptFileRefactor ,
81
+ symbolName : string ,
82
+ host : ts . CompilerHost ,
83
+ program : ts . Program ) : string | null {
92
84
// We found the bootstrap variable, now we just need to get where it's imported.
93
- const imports = _findNodes ( sourceFile , sourceFile , ts . SyntaxKind . ImportDeclaration , false )
85
+ const imports = refactor . findAstNodes ( null , ts . SyntaxKind . ImportDeclaration )
94
86
. map ( node => node as ts . ImportDeclaration ) ;
95
87
96
88
for ( const decl of imports ) {
@@ -101,8 +93,14 @@ function _symbolImportLookup(sourcePath: string,
101
93
continue ;
102
94
}
103
95
104
- const module = resolve ( dirname ( sourcePath ) , ( decl . moduleSpecifier as ts . StringLiteral ) . text ) ;
96
+ const resolvedModule = ts . resolveModuleName (
97
+ ( decl . moduleSpecifier as ts . StringLiteral ) . text ,
98
+ refactor . fileName , program . getCompilerOptions ( ) , host ) ;
99
+ if ( ! resolvedModule . resolvedModule || ! resolvedModule . resolvedModule . resolvedFileName ) {
100
+ return null ;
101
+ }
105
102
103
+ const module = resolvedModule . resolvedModule . resolvedFileName ;
106
104
if ( decl . importClause . namedBindings . kind == ts . SyntaxKind . NamespaceImport ) {
107
105
const binding = decl . importClause . namedBindings as ts . NamespaceImport ;
108
106
if ( binding . name . text == symbolName ) {
@@ -113,29 +111,11 @@ function _symbolImportLookup(sourcePath: string,
113
111
const binding = decl . importClause . namedBindings as ts . NamedImports ;
114
112
for ( const specifier of binding . elements ) {
115
113
if ( specifier . name . text == symbolName ) {
116
- // If it's a directory, load its index and recursively lookup.
117
- if ( fs . statSync ( module ) . isDirectory ( ) ) {
118
- const indexModule = join ( module , 'index.ts' ) ;
119
- if ( fs . existsSync ( indexModule ) ) {
120
- const maybeModule = _recursiveSymbolExportLookup (
121
- indexModule , _createSource ( indexModule ) , symbolName ) ;
122
- if ( maybeModule ) {
123
- return maybeModule ;
124
- }
125
- }
126
- }
127
-
128
- // Create the source and verify that the symbol is at least a class.
129
- const source = _createSource ( module ) ;
130
- const hasSymbol = _findNodes ( source , source , ts . SyntaxKind . ClassDeclaration )
131
- . some ( ( cd : ts . ClassDeclaration ) => {
132
- return cd . name && cd . name . text == symbolName ;
133
- } ) ;
134
-
135
- if ( hasSymbol ) {
136
- return module ;
137
- } else {
138
- return null ;
114
+ // Create the source and recursively lookup the import.
115
+ const source = new TypeScriptFileRefactor ( module , host , program ) ;
116
+ const maybeModule = _recursiveSymbolExportLookup ( source , symbolName , host , program ) ;
117
+ if ( maybeModule ) {
118
+ return maybeModule ;
139
119
}
140
120
}
141
121
}
@@ -145,30 +125,32 @@ function _symbolImportLookup(sourcePath: string,
145
125
}
146
126
147
127
148
- export function resolveEntryModuleFromMain ( mainPath : string ) {
149
- const source = _createSource ( mainPath ) ;
128
+ export function resolveEntryModuleFromMain ( mainPath : string ,
129
+ host : ts . CompilerHost ,
130
+ program : ts . Program ) {
131
+ const source = new TypeScriptFileRefactor ( mainPath , host , program ) ;
150
132
151
- const bootstrap = _findNodes ( source , source , ts . SyntaxKind . CallExpression , false )
133
+ const bootstrap = source . findAstNodes ( source . sourceFile , ts . SyntaxKind . CallExpression , false )
152
134
. map ( node => node as ts . CallExpression )
153
135
. filter ( call => {
154
136
const access = call . expression as ts . PropertyAccessExpression ;
155
137
return access . kind == ts . SyntaxKind . PropertyAccessExpression
156
138
&& access . name . kind == ts . SyntaxKind . Identifier
157
139
&& ( access . name . text == 'bootstrapModule'
158
140
|| access . name . text == 'bootstrapModuleFactory' ) ;
159
- } ) ;
141
+ } )
142
+ . map ( node => node . arguments [ 0 ] as ts . Identifier )
143
+ . filter ( node => node . kind == ts . SyntaxKind . Identifier ) ;
160
144
161
- if ( bootstrap . length != 1
162
- || bootstrap [ 0 ] . arguments [ 0 ] . kind !== ts . SyntaxKind . Identifier ) {
145
+ if ( bootstrap . length != 1 ) {
163
146
throw new Error ( 'Tried to find bootstrap code, but could not. Specify either '
164
147
+ 'statically analyzable bootstrap code or pass in an entryModule '
165
148
+ 'to the plugins options.' ) ;
166
149
}
167
-
168
- const bootstrapSymbolName = ( bootstrap [ 0 ] . arguments [ 0 ] as ts . Identifier ) . text ;
169
- const module = _symbolImportLookup ( mainPath , source , bootstrapSymbolName ) ;
150
+ const bootstrapSymbolName = bootstrap [ 0 ] . text ;
151
+ const module = _symbolImportLookup ( source , bootstrapSymbolName , host , program ) ;
170
152
if ( module ) {
171
- return `${ resolve ( dirname ( mainPath ) , module ) } #${ bootstrapSymbolName } ` ;
153
+ return `${ module . replace ( / \. t s $ / , '' ) } #${ bootstrapSymbolName } ` ;
172
154
}
173
155
174
156
// shrug... something bad happened and we couldn't find the import statement.
0 commit comments