Skip to content
This repository was archived by the owner on May 1, 2020. It is now read-only.

Commit e240b2b

Browse files
committed
feature(ngc): add support for ng5
1 parent 484d90d commit e240b2b

20 files changed

+451
-424
lines changed

package-lock.json

Lines changed: 172 additions & 114 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
},
3232
"main": "dist/index.js",
3333
"dependencies": {
34-
"@angular-devkit/build-optimizer": "^0.0.31",
34+
"@angular-devkit/build-optimizer": "0.0.32",
3535
"autoprefixer": "^7.1.6",
3636
"chalk": "^2.3.0",
3737
"chokidar": "^1.7.0",
@@ -59,17 +59,16 @@
5959
"xml2js": "^0.4.19"
6060
},
6161
"devDependencies": {
62-
"@angular/animations": "4.4.3",
63-
"@angular/common": "4.4.3",
64-
"@angular/compiler": "4.4.3",
65-
"@angular/compiler-cli": "4.4.3",
66-
"@angular/core": "4.4.3",
67-
"@angular/forms": "4.4.3",
68-
"@angular/http": "4.4.3",
69-
"@angular/platform-browser": "4.4.3",
70-
"@angular/platform-browser-dynamic": "4.4.3",
71-
"@angular/platform-server": "4.4.3",
72-
"@angular/tsc-wrapped": "4.4.3",
62+
"@angular/animations": "5.0.0",
63+
"@angular/common": "5.0.0",
64+
"@angular/compiler": "5.0.0",
65+
"@angular/compiler-cli": "5.0.0",
66+
"@angular/core": "5.0.0",
67+
"@angular/forms": "5.0.0",
68+
"@angular/http": "5.0.0",
69+
"@angular/platform-browser": "5.0.0",
70+
"@angular/platform-browser-dynamic": "5.0.0",
71+
"@angular/platform-server": "5.0.0",
7372
"@types/chokidar": "^1.7.3",
7473
"@types/clean-css": "^3.4.29",
7574
"@types/express": "^4.0.39",
@@ -91,8 +90,8 @@
9190
"rimraf": "^2.6.1",
9291
"rxjs": "^5.5.2",
9392
"sw-toolbox": "^3.6.0",
94-
"tslint-ionic-rules": "^0.0.11",
95-
"typescript": "~2.3.4",
93+
"tslint-ionic-rules": "0.0.8",
94+
"typescript": "~2.4.2",
9695
"zone.js": "^0.8.17"
9796
},
9897
"repository": {

src/aot/aot-compiler.ts

Lines changed: 108 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -2,112 +2,72 @@ import { readFileSync } from 'fs-extra';
22
import { extname, normalize, resolve } from 'path';
33

44
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';
96

107
import { HybridFileSystem } from '../util/hybrid-file-system';
118
import { getInstance as getHybridFileSystem } from '../util/hybrid-file-system-factory';
129
import { getInMemoryCompilerHostInstance } from './compiler-host-factory';
1310
import { InMemoryCompilerHost } from './compiler-host';
14-
import { getFallbackMainContent, replaceBootstrap } from './utils';
11+
import { getFallbackMainContent, replaceBootstrapImpl } from './utils';
1512
import { Logger } from '../logger/logger';
1613
import { printDiagnostics, clearDiagnostics, DiagnosticsType } from '../logger/logger-diagnostics';
1714
import { runTypeScriptDiagnostics } from '../logger/logger-typescript';
15+
import { getTsConfig, TsConfig } from '../transpile';
1816
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';
2119

22-
import { doCodegen } from './codegen';
20+
export async function runAot(context: BuildContext, options: AotOptions) {
21+
const tsConfig = getTsConfig(context);
2322

24-
export class AotCompiler {
23+
const angularCompilerOptions = Object.assign({}, {
24+
basePath: options.rootDir,
25+
entryPoint: options.entryPoint
26+
});
2527

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);
3229

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);
3534

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);
4536

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: {
5143
i18nFile: undefined,
5244
i18nFormat: undefined,
5345
locale: undefined,
54-
basePath: this.options.rootDir,
46+
basePath: options.rootDir,
5547
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
10352
});
10453
}
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+
}
10565
}
10666

107-
function errorCheckProgram(context: BuildContext, tsConfig: ParsedTsConfig, compilerHost: InMemoryCompilerHost, cachedProgram: Program) {
67+
function errorCheckProgram(context: BuildContext, tsConfig: TsConfig, compilerHost: InMemoryCompilerHost, cachedProgram: Program) {
10868
// Create a new Program, based on the old one. This will trigger a resolution of all
10969
// 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);
11171
const globalDiagnostics = program.getGlobalDiagnostics();
11272
const tsDiagnostics = program.getSyntacticDiagnostics()
11373
.concat(program.getSemanticDiagnostics())
@@ -126,11 +86,34 @@ function errorCheckProgram(context: BuildContext, tsConfig: ParsedTsConfig, comp
12686
return program;
12787
}
12888

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) {
130113
const tsFiles = context.fileCache.getAll().filter(file => extname(file.path) === '.ts' && file.path.indexOf('.d.ts') === -1);
131114
for (const tsFile of tsFiles) {
132115
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);
134117
const diagnostics = runTypeScriptDiagnostics(context, transpileOutput.diagnostics);
135118
if (diagnostics.length) {
136119
// darn, we've got some things wrong, transpile failed :(
@@ -156,26 +139,52 @@ function transpileFileContent(fileName: string, sourceText: string, options: Com
156139
return transpileModule(sourceText, transpileOptions);
157140
}
158141

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+
});
165161
}
166162

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+
});
168172

169-
const tsConfigFile = tsc.readConfiguration(tsConfigPath, process.cwd());
170-
if (!tsConfigFile) {
171-
throw new BuildError(`tsconfig: invalid tsconfig file, "${tsConfigPath}"`);
173+
await program.loadNgStructureAsync();
172174

173-
}
174-
return tsConfigFile;
175-
}
175+
const transformations: any[] = [];
176+
177+
const transformers = {
178+
beforeTs: transformations
179+
};
176180

177-
export interface ParsedTsConfig {
178-
parsed: ParsedCommandLine;
179-
ngOptions: AngularCompilerOptions;
181+
program.emit({ emitFlags: ngTools2.EmitFlags.Default, customTransformers: transformers });
180182
}
181183

184+
export interface AotOptions {
185+
tsConfigPath: string;
186+
rootDir: string;
187+
entryPoint: string;
188+
appNgModulePath: string;
189+
appNgModuleClass: string;
190+
}

src/aot/codegen.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/aot/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ function importAndEnableProdMode(filePath: string, fileContent: string) {
114114
return modifiedFileContent;
115115
}
116116

117-
export function replaceBootstrap(filePath: string, fileContent: string, appNgModulePath: string, appNgModuleClassName: string) {
117+
export function replaceBootstrapImpl(filePath: string, fileContent: string, appNgModulePath: string, appNgModuleClassName: string) {
118118
if (!fileContent.match(/\bbootstrapModule\b/)) {
119119
throw new Error(`Could not find bootstrapModule in ${filePath}`);
120120
}

0 commit comments

Comments
 (0)