@@ -2,112 +2,72 @@ import { readFileSync } from 'fs-extra';
2
2
import { extname , normalize , resolve } from 'path' ;
3
3
4
4
import 'reflect-metadata' ;
5
- import { CompilerOptions , createProgram , ParsedCommandLine , Program , transpileModule , TranspileOptions , TranspileOutput } from 'typescript' ;
6
- import { NgcCliOptions } from '@angular/compiler-cli' ;
7
- import { tsc } from '@angular/tsc-wrapped/src/tsc' ;
8
- import AngularCompilerOptions from '@angular/tsc-wrapped/src/options' ;
5
+ import { CompilerHost , CompilerOptions , ParsedCommandLine , Program , transpileModule , TranspileOptions , TranspileOutput , createProgram } from 'typescript' ;
9
6
10
7
import { HybridFileSystem } from '../util/hybrid-file-system' ;
11
8
import { getInstance as getHybridFileSystem } from '../util/hybrid-file-system-factory' ;
12
9
import { getInMemoryCompilerHostInstance } from './compiler-host-factory' ;
13
10
import { InMemoryCompilerHost } from './compiler-host' ;
14
- import { getFallbackMainContent , replaceBootstrap } from './utils' ;
11
+ import { getFallbackMainContent , replaceBootstrapImpl } from './utils' ;
15
12
import { Logger } from '../logger/logger' ;
16
13
import { printDiagnostics , clearDiagnostics , DiagnosticsType } from '../logger/logger-diagnostics' ;
17
14
import { runTypeScriptDiagnostics } from '../logger/logger-typescript' ;
15
+ import { getTsConfig , TsConfig } from '../transpile' ;
18
16
import { BuildError } from '../util/errors' ;
19
- import { changeExtension } from '../util/helpers' ;
20
- import { BuildContext } from '../util/interfaces' ;
17
+ import { changeExtension , readFileAsync } from '../util/helpers' ;
18
+ import { BuildContext , CodegenOptions , File , SemverVersion } from '../util/interfaces' ;
21
19
22
- import { doCodegen } from './codegen' ;
20
+ export async function runAot ( context : BuildContext , options : AotOptions ) {
21
+ const tsConfig = getTsConfig ( context ) ;
23
22
24
- export class AotCompiler {
23
+ const angularCompilerOptions = Object . assign ( { } , {
24
+ basePath : options . rootDir ,
25
+ entryPoint : options . entryPoint
26
+ } ) ;
25
27
26
- private tsConfig : ParsedTsConfig ;
27
- private angularCompilerOptions : AngularCompilerOptions ;
28
- private program : Program ;
29
- private compilerHost : InMemoryCompilerHost ;
30
- private fileSystem : HybridFileSystem ;
31
- private lazyLoadedModuleDictionary : any ;
28
+ const aggregateCompilerOption = Object . assign ( tsConfig . options , angularCompilerOptions ) ;
32
29
33
- constructor ( private context : BuildContext , private options : AotOptions ) {
34
- this . tsConfig = getNgcConfig ( this . context , this . options . tsConfigPath ) ;
30
+ const fileSystem = getHybridFileSystem ( false ) ;
31
+ const compilerHost = getInMemoryCompilerHostInstance ( tsConfig . options ) ;
32
+ // todo, consider refactoring at some point
33
+ const tsProgram = createProgram ( tsConfig . fileNames , tsConfig . options , compilerHost ) ;
35
34
36
- this . angularCompilerOptions = Object . assign ( { } , this . tsConfig . ngOptions , {
37
- basePath : this . options . rootDir ,
38
- entryPoint : this . options . entryPoint
39
- } ) ;
40
-
41
- this . fileSystem = getHybridFileSystem ( false ) ;
42
- this . compilerHost = getInMemoryCompilerHostInstance ( this . tsConfig . parsed . options ) ;
43
- this . program = createProgram ( this . tsConfig . parsed . fileNames , this . tsConfig . parsed . options , this . compilerHost ) ;
44
- }
35
+ clearDiagnostics ( context , DiagnosticsType . TypeScript ) ;
45
36
46
- compile ( ) : Promise < any > {
47
- return Promise . resolve ( ) . then ( ( ) => {
48
- } ) . then ( ( ) => {
49
- clearDiagnostics ( this . context , DiagnosticsType . TypeScript ) ;
50
- const i18nOptions : NgcCliOptions = {
37
+ if ( isNg5 ( context . angularVersion ) ) {
38
+ await runNg5Aot ( tsConfig , aggregateCompilerOption , compilerHost ) ;
39
+ } else {
40
+ await runNg4Aot ( {
41
+ angularCompilerOptions : angularCompilerOptions ,
42
+ cliOptions : {
51
43
i18nFile : undefined ,
52
44
i18nFormat : undefined ,
53
45
locale : undefined ,
54
- basePath : this . options . rootDir ,
46
+ basePath : options . rootDir ,
55
47
missingTranslation : null
56
- } ;
57
-
58
- Logger . debug ( '[AotCompiler] compile: starting codegen ... ' ) ;
59
- return doCodegen ( {
60
- angularCompilerOptions : this . angularCompilerOptions ,
61
- cliOptions : i18nOptions ,
62
- program : this . program ,
63
- compilerHost : this . compilerHost ,
64
- compilerOptions : this . tsConfig . parsed . options
65
- } ) ;
66
- } ) . then ( ( ) => {
67
- Logger . debug ( '[AotCompiler] compile: starting codegen ... DONE' ) ;
68
- Logger . debug ( '[AotCompiler] compile: Creating and validating new TypeScript Program ...' ) ;
69
- this . program = errorCheckProgram ( this . context , this . tsConfig , this . compilerHost , this . program ) ;
70
- Logger . debug ( '[AotCompiler] compile: Creating and validating new TypeScript Program ... DONE' ) ;
71
- } ) . then ( ( ) => {
72
- Logger . debug ( '[AotCompiler] compile: Starting to process and modify entry point ...' ) ;
73
- const mainFile = this . context . fileCache . get ( this . options . entryPoint ) ;
74
- if ( ! mainFile ) {
75
- throw new BuildError ( new Error ( `Could not find entry point (bootstrap file) ${ this . options . entryPoint } ` ) ) ;
76
- }
77
- Logger . debug ( '[AotCompiler] compile: Resolving NgModule from entry point' ) ;
78
- let modifiedFileContent : string = null ;
79
- try {
80
- Logger . debug ( '[AotCompiler] compile: Dynamically changing entry point content to AOT mode content' ) ;
81
- modifiedFileContent = replaceBootstrap ( mainFile . path , mainFile . content , this . options . appNgModulePath , this . options . appNgModuleClass ) ;
82
- } catch ( ex ) {
83
- Logger . debug ( `Failed to parse bootstrap: ` , ex . message ) ;
84
- Logger . warn ( `Failed to parse and update ${ this . options . entryPoint } content for AoT compilation.
85
- For now, the default fallback content will be used instead.
86
- Please consider updating ${ this . options . entryPoint } with the content from the following link:
87
- https://github.com/ionic-team/ionic2-app-base/tree/master/src/app/main.ts` ) ;
88
- modifiedFileContent = getFallbackMainContent ( ) ;
89
- }
90
-
91
- Logger . debug ( `[AotCompiler] compile: Modified File Content: ${ modifiedFileContent } ` ) ;
92
- this . context . fileCache . set ( this . options . entryPoint , { path : this . options . entryPoint , content : modifiedFileContent } ) ;
93
- Logger . debug ( '[AotCompiler] compile: Starting to process and modify entry point ... DONE' ) ;
94
- } )
95
- . then ( ( ) => {
96
- Logger . debug ( '[AotCompiler] compile: Transpiling files ...' ) ;
97
- transpileFiles ( this . context , this . tsConfig , this . fileSystem ) ;
98
- Logger . debug ( '[AotCompiler] compile: Transpiling files ... DONE' ) ;
99
- } ) . then ( ( ) => {
100
- return {
101
- lazyLoadedModuleDictionary : this . lazyLoadedModuleDictionary
102
- } ;
48
+ } ,
49
+ program : tsProgram ,
50
+ compilerHost : compilerHost ,
51
+ compilerOptions : tsConfig . options
103
52
} ) ;
104
53
}
54
+
55
+ errorCheckProgram ( context , tsConfig , compilerHost , tsProgram ) ;
56
+
57
+ // update bootstrap in main.ts
58
+ const mainFile = context . fileCache . get ( changeExtension ( options . entryPoint , '.js' ) ) ;
59
+ const modifiedBootstrapContent = replaceBootstrap ( mainFile , options . appNgModulePath , options . appNgModuleClass , options ) ;
60
+ mainFile . content = modifiedBootstrapContent ;
61
+
62
+ if ( isTranspileRequired ( context . angularVersion ) ) {
63
+ transpileFiles ( context , tsConfig , fileSystem ) ;
64
+ }
105
65
}
106
66
107
- function errorCheckProgram ( context : BuildContext , tsConfig : ParsedTsConfig , compilerHost : InMemoryCompilerHost , cachedProgram : Program ) {
67
+ function errorCheckProgram ( context : BuildContext , tsConfig : TsConfig , compilerHost : InMemoryCompilerHost , cachedProgram : Program ) {
108
68
// Create a new Program, based on the old one. This will trigger a resolution of all
109
69
// transitive modules, which include files that might just have been generated.
110
- const program = createProgram ( tsConfig . parsed . fileNames , tsConfig . parsed . options , compilerHost , cachedProgram ) ;
70
+ const program = createProgram ( tsConfig . fileNames , tsConfig . options , compilerHost , cachedProgram ) ;
111
71
const globalDiagnostics = program . getGlobalDiagnostics ( ) ;
112
72
const tsDiagnostics = program . getSyntacticDiagnostics ( )
113
73
. concat ( program . getSemanticDiagnostics ( ) )
@@ -126,11 +86,34 @@ function errorCheckProgram(context: BuildContext, tsConfig: ParsedTsConfig, comp
126
86
return program ;
127
87
}
128
88
129
- function transpileFiles ( context : BuildContext , tsConfig : ParsedTsConfig , fileSystem : HybridFileSystem ) {
89
+ function replaceBootstrap ( mainFile : File , appNgModulePath : string , appNgModuleClass : string , options : AotOptions ) {
90
+ if ( ! mainFile ) {
91
+ throw new BuildError ( new Error ( `Could not find entry point (bootstrap file) ${ options . entryPoint } ` ) ) ;
92
+ }
93
+ let modifiedFileContent : string = null ;
94
+ try {
95
+ Logger . debug ( '[AotCompiler] compile: Dynamically changing entry point content to AOT mode content' ) ;
96
+ modifiedFileContent = replaceBootstrapImpl ( mainFile . path , mainFile . content , appNgModulePath , appNgModuleClass ) ;
97
+ } catch ( ex ) {
98
+ Logger . debug ( `Failed to parse bootstrap: ` , ex . message ) ;
99
+ Logger . warn ( `Failed to parse and update ${ options . entryPoint } content for AoT compilation.
100
+ For now, the default fallback content will be used instead.
101
+ Please consider updating ${ options . entryPoint } with the content from the following link:
102
+ https://github.com/ionic-team/ionic2-app-base/tree/master/src/app/main.ts` ) ;
103
+ modifiedFileContent = getFallbackMainContent ( ) ;
104
+ }
105
+ return modifiedFileContent ;
106
+ }
107
+
108
+ export function isTranspileRequired ( angularVersion : SemverVersion ) {
109
+ return angularVersion . major <= 4 ;
110
+ }
111
+
112
+ export function transpileFiles ( context : BuildContext , tsConfig : TsConfig , fileSystem : HybridFileSystem ) {
130
113
const tsFiles = context . fileCache . getAll ( ) . filter ( file => extname ( file . path ) === '.ts' && file . path . indexOf ( '.d.ts' ) === - 1 ) ;
131
114
for ( const tsFile of tsFiles ) {
132
115
Logger . debug ( `[AotCompiler] transpileFiles: Transpiling file ${ tsFile . path } ...` ) ;
133
- const transpileOutput = transpileFileContent ( tsFile . path , tsFile . content , tsConfig . parsed . options ) ;
116
+ const transpileOutput = transpileFileContent ( tsFile . path , tsFile . content , tsConfig . options ) ;
134
117
const diagnostics = runTypeScriptDiagnostics ( context , transpileOutput . diagnostics ) ;
135
118
if ( diagnostics . length ) {
136
119
// darn, we've got some things wrong, transpile failed :(
@@ -156,26 +139,52 @@ function transpileFileContent(fileName: string, sourceText: string, options: Com
156
139
return transpileModule ( sourceText , transpileOptions ) ;
157
140
}
158
141
159
- export interface AotOptions {
160
- tsConfigPath : string ;
161
- rootDir : string ;
162
- entryPoint : string ;
163
- appNgModulePath : string ;
164
- appNgModuleClass : string ;
142
+ export function isNg5 ( version : SemverVersion ) {
143
+ return version . major >= 5 ;
144
+ }
145
+
146
+ export async function runNg4Aot ( options : CodegenOptions ) {
147
+ const module = await import ( '@angular/compiler-cli' ) ;
148
+ return await module . __NGTOOLS_PRIVATE_API_2 . codeGen ( {
149
+ angularCompilerOptions : options . angularCompilerOptions ,
150
+ basePath : options . cliOptions . basePath ,
151
+ program : options . program ,
152
+ host : options . compilerHost ,
153
+ compilerOptions : options . compilerOptions ,
154
+ i18nFile : options . cliOptions . i18nFile ,
155
+ i18nFormat : options . cliOptions . i18nFormat ,
156
+ locale : options . cliOptions . locale ,
157
+ readResource : ( fileName : string ) => {
158
+ return readFileAsync ( fileName ) ;
159
+ }
160
+ } ) ;
165
161
}
166
162
167
- export function getNgcConfig ( context : BuildContext , tsConfigPath ?: string ) : ParsedTsConfig {
163
+ export async function runNg5Aot ( tsConfig : TsConfig , aggregateCompilerOptions : CompilerOptions , compilerHost : CompilerHost ) {
164
+ const ngTools2 = await import ( '@angular/compiler-cli/ngtools2' ) ;
165
+ const angularCompilerHost = ngTools2 . createCompilerHost ( { options : aggregateCompilerOptions , tsHost : compilerHost } ) ;
166
+ const program = ngTools2 . createProgram ( {
167
+ rootNames : tsConfig . fileNames ,
168
+ options : aggregateCompilerOptions ,
169
+ host : angularCompilerHost ,
170
+ oldProgram : null
171
+ } ) ;
168
172
169
- const tsConfigFile = tsc . readConfiguration ( tsConfigPath , process . cwd ( ) ) ;
170
- if ( ! tsConfigFile ) {
171
- throw new BuildError ( `tsconfig: invalid tsconfig file, "${ tsConfigPath } "` ) ;
173
+ await program . loadNgStructureAsync ( ) ;
172
174
173
- }
174
- return tsConfigFile ;
175
- }
175
+ const transformations : any [ ] = [ ] ;
176
+
177
+ const transformers = {
178
+ beforeTs : transformations
179
+ } ;
176
180
177
- export interface ParsedTsConfig {
178
- parsed : ParsedCommandLine ;
179
- ngOptions : AngularCompilerOptions ;
181
+ program . emit ( { emitFlags : ngTools2 . EmitFlags . Default , customTransformers : transformers } ) ;
180
182
}
181
183
184
+ export interface AotOptions {
185
+ tsConfigPath : string ;
186
+ rootDir : string ;
187
+ entryPoint : string ;
188
+ appNgModulePath : string ;
189
+ appNgModuleClass : string ;
190
+ }
0 commit comments