1
- import { readFileSync } from 'fs' ;
2
- import { extname , normalize , resolve } from 'path' ;
1
+ import { mkdirpSync , readFileSync , writeFileSync } from 'fs-extra ' ;
2
+ import { basename , dirname , extname , join , normalize , relative , resolve } from 'path' ;
3
3
4
4
import 'reflect-metadata' ;
5
- import { CompilerOptions , createProgram , ParsedCommandLine , Program , ScriptTarget , transpileModule , TranspileOptions , TranspileOutput } from 'typescript' ;
5
+ import { CallExpression , CompilerOptions , createProgram , createSourceFile , Decorator , Identifier , ParsedCommandLine , Program , ScriptTarget , SyntaxKind , transpileModule , TranspileOptions , TranspileOutput } from 'typescript' ;
6
6
import { CodeGenerator , NgcCliOptions , NodeReflectorHostContext , ReflectorHost , StaticReflector } from '@angular/compiler-cli' ;
7
7
import { tsc } from '@angular/tsc-wrapped/src/tsc' ;
8
8
import AngularCompilerOptions from '@angular/tsc-wrapped/src/options' ;
@@ -11,13 +11,14 @@ import { HybridFileSystem } from '../util/hybrid-file-system';
11
11
import { getInstance as getHybridFileSystem } from '../util/hybrid-file-system-factory' ;
12
12
import { getInstance } from './compiler-host-factory' ;
13
13
import { NgcCompilerHost } from './compiler-host' ;
14
- import { resolveAppNgModuleFromMain } from './app-module-resolver' ;
15
14
import { patchReflectorHost } from './reflector-host' ;
16
- import { getTypescriptSourceFile , removeDecorators } from '../util/typescript-utils' ;
15
+ import { findNodes , getNodeStringContent , getTypescriptSourceFile , removeDecorators } from '../util/typescript-utils' ;
17
16
import { getFallbackMainContent , replaceBootstrap } from './utils' ;
18
17
import { Logger } from '../logger/logger' ;
19
18
import { printDiagnostics , clearDiagnostics , DiagnosticsType } from '../logger/logger-diagnostics' ;
20
19
import { runTypeScriptDiagnostics } from '../logger/logger-typescript' ;
20
+ import { isDebugMode } from '../util/config' ;
21
+ import * as Constants from '../util/constants' ;
21
22
import { BuildError } from '../util/errors' ;
22
23
import { changeExtension } from '../util/helpers' ;
23
24
import { BuildContext } from '../util/interfaces' ;
@@ -31,6 +32,8 @@ export class AotCompiler {
31
32
private reflectorHost : ReflectorHost ;
32
33
private compilerHost : NgcCompilerHost ;
33
34
private fileSystem : HybridFileSystem ;
35
+ private appLevelNgModuleFilePath : string ;
36
+ private lazyLoadedModuleDictionary : any ;
34
37
35
38
constructor ( private context : BuildContext , private options : AotOptions ) {
36
39
this . tsConfig = getNgcConfig ( this . context , this . options . tsConfigPath ) ;
@@ -47,7 +50,7 @@ export class AotCompiler {
47
50
this . reflector = new StaticReflector ( this . reflectorHost ) ;
48
51
}
49
52
50
- compile ( ) {
53
+ compile ( ) : Promise < void > {
51
54
return Promise . resolve ( ) . then ( ( ) => {
52
55
} ) . then ( ( ) => {
53
56
clearDiagnostics ( this . context , DiagnosticsType . TypeScript ) ;
@@ -75,50 +78,31 @@ export class AotCompiler {
75
78
} ) . then ( ( ) => {
76
79
Logger . debug ( '[AotCompiler] compile: starting codegen ... DONE' ) ;
77
80
Logger . debug ( '[AotCompiler] compile: Creating and validating new TypeScript Program ...' ) ;
78
- // Create a new Program, based on the old one. This will trigger a resolution of all
79
- // transitive modules, which include files that might just have been generated.
80
- this . program = createProgram ( this . tsConfig . parsed . fileNames , this . tsConfig . parsed . options , this . compilerHost , this . program ) ;
81
- const globalDiagnostics = this . program . getGlobalDiagnostics ( ) ;
82
- const tsDiagnostics = this . program . getSyntacticDiagnostics ( )
83
- . concat ( this . program . getSemanticDiagnostics ( ) )
84
- . concat ( this . program . getOptionsDiagnostics ( ) ) ;
85
-
86
- if ( globalDiagnostics . length ) {
87
- const diagnostics = runTypeScriptDiagnostics ( this . context , globalDiagnostics ) ;
88
- printDiagnostics ( this . context , DiagnosticsType . TypeScript , diagnostics , true , false ) ;
89
- throw new BuildError ( new Error ( 'Failed to transpile TypeScript' ) ) ;
90
- }
91
- if ( tsDiagnostics . length ) {
92
- const diagnostics = runTypeScriptDiagnostics ( this . context , tsDiagnostics ) ;
93
- printDiagnostics ( this . context , DiagnosticsType . TypeScript , diagnostics , true , false ) ;
94
- throw new BuildError ( new Error ( 'Failed to transpile TypeScript' ) ) ;
95
- }
81
+ this . program = errorCheckProgram ( this . context , this . tsConfig , this . compilerHost , this . program ) ;
82
+ Logger . debug ( '[AotCompiler] compile: Creating and validating new TypeScript Program ... DONE' ) ;
96
83
} )
97
84
. then ( ( ) => {
98
- Logger . debug ( '[AotCompiler] compile: Creating and validating new TypeScript Program ... DONE' ) ;
85
+
99
86
Logger . debug ( '[AotCompiler] compile: The following files are included in the program: ' ) ;
100
87
for ( const fileName of this . tsConfig . parsed . fileNames ) {
101
88
Logger . debug ( `[AotCompiler] compile: ${ fileName } ` ) ;
102
89
const cleanedFileName = normalize ( resolve ( fileName ) ) ;
103
90
const content = readFileSync ( cleanedFileName ) . toString ( ) ;
104
91
this . context . fileCache . set ( cleanedFileName , { path : cleanedFileName , content : content } ) ;
105
92
}
106
- } )
107
- . then ( ( ) => {
93
+ } ) . then ( ( ) => {
108
94
Logger . debug ( '[AotCompiler] compile: Starting to process and modify entry point ...' ) ;
109
95
const mainFile = this . context . fileCache . get ( this . options . entryPoint ) ;
110
96
if ( ! mainFile ) {
111
97
throw new BuildError ( new Error ( `Could not find entry point (bootstrap file) ${ this . options . entryPoint } ` ) ) ;
112
98
}
113
99
const mainSourceFile = getTypescriptSourceFile ( mainFile . path , mainFile . content , ScriptTarget . Latest , false ) ;
114
100
Logger . debug ( '[AotCompiler] compile: Resolving NgModule from entry point' ) ;
115
- const AppNgModuleStringAndClassName = resolveAppNgModuleFromMain ( mainSourceFile , this . context . fileCache , this . compilerHost , this . program ) ;
116
- const AppNgModuleTokens = AppNgModuleStringAndClassName . split ( '#' ) ;
117
-
101
+ this . appLevelNgModuleFilePath = this . options . appNgModulePath
118
102
let modifiedFileContent : string = null ;
119
103
try {
120
104
Logger . debug ( '[AotCompiler] compile: Dynamically changing entry point content to AOT mode content' ) ;
121
- modifiedFileContent = replaceBootstrap ( mainFile . path , mainFile . content , AppNgModuleTokens [ 0 ] , AppNgModuleTokens [ 1 ] ) ;
105
+ modifiedFileContent = replaceBootstrap ( mainFile . path , mainFile . content , this . options . appNgModulePath , this . options . appNgModuleClass ) ;
122
106
} catch ( ex ) {
123
107
Logger . debug ( `Failed to parse bootstrap: ` , ex . message ) ;
124
108
Logger . warn ( `Failed to parse and update ${ this . options . entryPoint } content for AoT compilation.
@@ -128,36 +112,68 @@ export class AotCompiler {
128
112
modifiedFileContent = getFallbackMainContent ( ) ;
129
113
}
130
114
115
+
131
116
Logger . debug ( `[AotCompiler] compile: Modified File Content: ${ modifiedFileContent } ` ) ;
132
117
this . context . fileCache . set ( this . options . entryPoint , { path : this . options . entryPoint , content : modifiedFileContent } ) ;
118
+ Logger . debug ( '[AotCompiler] compile: Starting to process and modify entry point ... DONE' ) ;
133
119
} )
134
120
. then ( ( ) => {
135
- Logger . debug ( '[AotCompiler] compile: Starting to process and modify entry point ... DONE' ) ;
136
121
Logger . debug ( '[AotCompiler] compile: Removing decorators from program files ...' ) ;
137
- const tsFiles = this . context . fileCache . getAll ( ) . filter ( file => extname ( file . path ) === '.ts' && file . path . indexOf ( '.d.ts' ) === - 1 ) ;
138
- for ( const tsFile of tsFiles ) {
139
- // Temporary fix to keep custom decorators until a
140
- // proper resolution can be found.
141
- /*const cleanedFileContent = removeDecorators(tsFile.path, tsFile.content);
142
- tsFile.content = cleanedFileContent;*/
143
- const cleanedFileContent = tsFile . content ;
144
- const transpileOutput = this . transpileFileContent ( tsFile . path , cleanedFileContent , this . tsConfig . parsed . options ) ;
145
- const diagnostics = runTypeScriptDiagnostics ( this . context , transpileOutput . diagnostics ) ;
146
- if ( diagnostics . length ) {
147
- // darn, we've got some things wrong, transpile failed :(
148
- printDiagnostics ( this . context , DiagnosticsType . TypeScript , diagnostics , true , true ) ;
149
- throw new BuildError ( ) ;
150
- }
151
-
152
- const jsFilePath = changeExtension ( tsFile . path , '.js' ) ;
153
- this . fileSystem . addVirtualFile ( jsFilePath , transpileOutput . outputText ) ;
154
- this . fileSystem . addVirtualFile ( jsFilePath + '.map' , transpileOutput . sourceMapText ) ;
155
- }
122
+ transpileFiles ( this . context , this . tsConfig , this . fileSystem ) ;
156
123
Logger . debug ( '[AotCompiler] compile: Removing decorators from program files ... DONE' ) ;
124
+ } ) . then ( ( ) => {
125
+ return {
126
+ lazyLoadedModuleDictionary : this . lazyLoadedModuleDictionary
127
+ } ;
157
128
} ) ;
158
129
}
130
+ }
159
131
160
- transpileFileContent ( fileName : string , sourceText : string , options : CompilerOptions ) : TranspileOutput {
132
+ function errorCheckProgram ( context : BuildContext , tsConfig : ParsedTsConfig , compilerHost : NgcCompilerHost , cachedProgram : Program ) {
133
+ // Create a new Program, based on the old one. This will trigger a resolution of all
134
+ // transitive modules, which include files that might just have been generated.
135
+ const program = createProgram ( tsConfig . parsed . fileNames , tsConfig . parsed . options , compilerHost , cachedProgram ) ;
136
+ const globalDiagnostics = program . getGlobalDiagnostics ( ) ;
137
+ const tsDiagnostics = program . getSyntacticDiagnostics ( )
138
+ . concat ( program . getSemanticDiagnostics ( ) )
139
+ . concat ( program . getOptionsDiagnostics ( ) ) ;
140
+
141
+ if ( globalDiagnostics . length ) {
142
+ const diagnostics = runTypeScriptDiagnostics ( context , globalDiagnostics ) ;
143
+ printDiagnostics ( context , DiagnosticsType . TypeScript , diagnostics , true , false ) ;
144
+ throw new BuildError ( new Error ( 'Failed to transpile TypeScript' ) ) ;
145
+ }
146
+ if ( tsDiagnostics . length ) {
147
+ const diagnostics = runTypeScriptDiagnostics ( context , tsDiagnostics ) ;
148
+ printDiagnostics ( context , DiagnosticsType . TypeScript , diagnostics , true , false ) ;
149
+ throw new BuildError ( new Error ( 'Failed to transpile TypeScript' ) ) ;
150
+ }
151
+ return program ;
152
+ }
153
+
154
+ function transpileFiles ( context : BuildContext , tsConfig : ParsedTsConfig , fileSystem : HybridFileSystem ) {
155
+ const tsFiles = context . fileCache . getAll ( ) . filter ( file => extname ( file . path ) === '.ts' && file . path . indexOf ( '.d.ts' ) === - 1 ) ;
156
+ for ( const tsFile of tsFiles ) {
157
+ const transpileOutput = transpileFileContent ( tsFile . path , tsFile . content , tsConfig . parsed . options ) ;
158
+ const diagnostics = runTypeScriptDiagnostics ( context , transpileOutput . diagnostics ) ;
159
+ if ( diagnostics . length ) {
160
+ // darn, we've got some things wrong, transpile failed :(
161
+ printDiagnostics ( context , DiagnosticsType . TypeScript , diagnostics , true , true ) ;
162
+ throw new BuildError ( ) ;
163
+ }
164
+
165
+ const jsFilePath = changeExtension ( tsFile . path , '.js' ) ;
166
+ fileSystem . addVirtualFile ( jsFilePath , transpileOutput . outputText ) ;
167
+ fileSystem . addVirtualFile ( jsFilePath + '.map' , transpileOutput . sourceMapText ) ;
168
+
169
+ // write files to disk here if debug is enabled
170
+ if ( isDebugMode ( ) || true ) {
171
+ writeNgcFilesToDisk ( context , tsFile . path , tsFile . content , transpileOutput . outputText , transpileOutput . sourceMapText ) ;
172
+ }
173
+ }
174
+ }
175
+
176
+ function transpileFileContent ( fileName : string , sourceText : string , options : CompilerOptions ) : TranspileOutput {
161
177
const transpileOptions : TranspileOptions = {
162
178
compilerOptions : options ,
163
179
fileName : fileName ,
@@ -166,12 +182,27 @@ export class AotCompiler {
166
182
167
183
return transpileModule ( sourceText , transpileOptions ) ;
168
184
}
169
- }
185
+
186
+ function writeNgcFilesToDisk ( context : BuildContext , typescriptFilePath : string , typescriptFileContent : string , transpiledFileContent : string , sourcemapContent : string ) {
187
+ const dirName = dirname ( typescriptFilePath ) ;
188
+ const relativePath = relative ( process . cwd ( ) , dirName ) ;
189
+ const tmpPath = join ( context . tmpDir , relativePath ) ;
190
+ const fileName = basename ( typescriptFilePath ) ;
191
+ const fileToWrite = join ( tmpPath , fileName ) ;
192
+ const jsFileToWrite = changeExtension ( fileToWrite , '.js' ) ;
193
+
194
+ mkdirpSync ( tmpPath ) ;
195
+ writeFileSync ( fileToWrite , typescriptFileContent ) ;
196
+ writeFileSync ( jsFileToWrite , transpiledFileContent ) ;
197
+ writeFileSync ( jsFileToWrite + '.map' , sourcemapContent ) ;
198
+ }
170
199
171
200
export interface AotOptions {
172
201
tsConfigPath : string ;
173
202
rootDir : string ;
174
203
entryPoint : string ;
204
+ appNgModulePath : string ;
205
+ appNgModuleClass : string ;
175
206
}
176
207
177
208
export function getNgcConfig ( context : BuildContext , tsConfigPath ?: string ) : ParsedTsConfig {
@@ -188,3 +219,4 @@ export interface ParsedTsConfig {
188
219
parsed : ParsedCommandLine ;
189
220
ngOptions : AngularCompilerOptions ;
190
221
}
222
+
0 commit comments