refactor(compiler): emit OutputAst and not sources (#16832)

This is in preparation for creating typescript nodes
directly from `OutputAst` nodes.
This commit is contained in:
Tobias Bosch 2017-05-17 11:21:08 -07:00 committed by Chuck Jazdzewski
parent 6123b9c0c6
commit de8d7c65f2
8 changed files with 102 additions and 70 deletions

View File

@ -44,8 +44,8 @@ export class CodeGenerator {
generatedModules.forEach(generatedModule => {
const sourceFile = this.program.getSourceFile(generatedModule.srcFileUrl);
const emitPath = this.ngCompilerHost.calculateEmitPath(generatedModule.genFileUrl);
const source = GENERATED_META_FILES.test(emitPath) ? generatedModule.source :
generatedModule.source;
const source =
generatedModule.source || compiler.toTypeScript(generatedModule, PREAMBLE);
this.host.writeFile(emitPath, source, false, () => {}, [sourceFile]);
});
});
@ -91,7 +91,6 @@ export class CodeGenerator {
i18nFormat: cliOptions.i18nFormat,
locale: cliOptions.locale, missingTranslation,
enableLegacyTemplate: options.enableLegacyTemplate !== false,
genFilePreamble: PREAMBLE,
});
return new CodeGenerator(options, program, tsCompilerHost, aotCompiler, ngCompilerHost);
}

View File

@ -33,8 +33,7 @@ export class AotCompiler {
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
private _summaryResolver: SummaryResolver<StaticSymbol>, private _localeId: string|null,
private _translationFormat: string|null, private _genFilePreamble: string|null,
private _symbolResolver: StaticSymbolResolver) {}
private _translationFormat: string|null, private _symbolResolver: StaticSymbolResolver) {}
clearCache() { this._metadataResolver.clearCache(); }
@ -273,10 +272,7 @@ export class AotCompiler {
}
private _codegenSourceModule(srcFileUrl: string, ctx: OutputContext): GeneratedFile {
return new GeneratedFile(
srcFileUrl, ctx.genFilePath,
this._outputEmitter.emitStatements(
sourceUrl(srcFileUrl), ctx.genFilePath, ctx.statements, this._genFilePreamble));
return new GeneratedFile(srcFileUrl, ctx.genFilePath, ctx.statements);
}
}

View File

@ -72,6 +72,6 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
const compiler = new AotCompiler(
config, compilerHost, resolver, tmplParser, new StyleCompiler(urlResolver), viewCompiler,
new NgModuleCompiler(), new TypeScriptEmitter(), summaryResolver, options.locale || null,
options.i18nFormat || null, options.genFilePreamble || null, symbolResolver);
options.i18nFormat || null, symbolResolver);
return {compiler, reflector: staticReflector};
}

View File

@ -14,6 +14,4 @@ export interface AotCompilerOptions {
translations?: string;
missingTranslation?: MissingTranslationStrategy;
enableLegacyTemplate?: boolean;
/** preamble for all generated source files */
genFilePreamble?: string;
}

View File

@ -6,6 +6,30 @@
* found in the LICENSE file at https://angular.io/license
*/
import {sourceUrl} from '../compile_metadata';
import {Statement} from '../output/output_ast';
import {TypeScriptEmitter} from '../output/ts_emitter';
export class GeneratedFile {
constructor(public srcFileUrl: string, public genFileUrl: string, public source: string) {}
public source: string|null;
public stmts: Statement[]|null;
constructor(
public srcFileUrl: string, public genFileUrl: string, sourceOrStmts: string|Statement[]) {
if (typeof sourceOrStmts === 'string') {
this.source = sourceOrStmts;
this.stmts = null;
} else {
this.source = null;
this.stmts = sourceOrStmts;
}
}
}
export function toTypeScript(file: GeneratedFile, preamble: string = ''): string {
if (!file.stmts) {
throw new Error(`Illegal state: No stmts present on GeneratedFile ${file.genFileUrl}`);
}
return new TypeScriptEmitter().emitStatements(
sourceUrl(file.srcFileUrl), file.genFileUrl, file.stmts, preamble);
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {GeneratedFile} from '@angular/compiler';
import {GeneratedFile, toTypeScript} from '@angular/compiler';
import {NodeFlags} from '@angular/core/src/view/index';
import {async} from '@angular/core/testing';
import {MetadataBundler, MetadataCollector, ModuleMetadata, privateEntriesToIndex} from '@angular/tsc-wrapped';
@ -128,7 +128,8 @@ describe('compiler (unbundled Angular)', () => {
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
compileApp().then((genFile) => {
const sourceMap = extractSourceMap(genFile.source) !;
const genSource = toTypeScript(genFile);
const sourceMap = extractSourceMap(genSource) !;
expect(sourceMap.file).toEqual(genFile.genFileUrl);
// the generated file contains code that is not mapped to
@ -149,8 +150,9 @@ describe('compiler (unbundled Angular)', () => {
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
compileApp().then((genFile) => {
const sourceMap = extractSourceMap(genFile.source) !;
expect(originalPositionFor(sourceMap, findLineAndColumn(genFile.source, `'span'`)))
const genSource = toTypeScript(genFile);
const sourceMap = extractSourceMap(genSource) !;
expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `'span'`)))
.toEqual({line: 2, column: 3, source: ngUrl});
});
}));
@ -161,9 +163,9 @@ describe('compiler (unbundled Angular)', () => {
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
compileApp().then((genFile) => {
const sourceMap = extractSourceMap(genFile.source) !;
expect(
originalPositionFor(sourceMap, findLineAndColumn(genFile.source, `someMethod()`)))
const genSource = toTypeScript(genFile);
const sourceMap = extractSourceMap(genSource) !;
expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `someMethod()`)))
.toEqual({line: 2, column: 9, source: ngUrl});
});
}));
@ -174,9 +176,9 @@ describe('compiler (unbundled Angular)', () => {
appDir['app.component.ts'] = createComponentSource(templateDecorator(template));
compileApp().then((genFile) => {
const sourceMap = extractSourceMap(genFile.source) !;
expect(
originalPositionFor(sourceMap, findLineAndColumn(genFile.source, `someMethod()`)))
const genSource = toTypeScript(genFile);
const sourceMap = extractSourceMap(genSource) !;
expect(originalPositionFor(sourceMap, findLineAndColumn(genSource, `someMethod()`)))
.toEqual({line: 2, column: 9, source: ngUrl});
});
}));
@ -185,7 +187,8 @@ describe('compiler (unbundled Angular)', () => {
appDir['app.component.ts'] = createComponentSource(templateDecorator('Hello World!'));
compileApp().then((genFile) => {
const sourceMap = extractSourceMap(genFile.source) !;
const genSource = toTypeScript(genFile);
const sourceMap = extractSourceMap(genSource) !;
expect(originalPositionFor(sourceMap, {line: 1, column: 0}))
.toEqual({line: 1, column: 0, source: ngComponentPath});
});
@ -258,10 +261,11 @@ describe('compiler (unbundled Angular)', () => {
}
};
const genFilePreamble = '/* Hello world! */';
compile([FILES, angularFiles], {genFilePreamble}).then(({genFiles}) => {
compile([FILES, angularFiles]).then(({genFiles}) => {
const genFile =
genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
expect(genFile.source.startsWith(genFilePreamble)).toBe(true);
const genSource = toTypeScript(genFile, genFilePreamble);
expect(genSource.startsWith(genFilePreamble)).toBe(true);
});
}));
@ -295,8 +299,9 @@ describe('compiler (unbundled Angular)', () => {
};
compile([FILES, angularFiles]).then(({genFiles}) => {
const genFile = genFiles.find(genFile => genFile.srcFileUrl === '/app/app.ts');
const genSource = toTypeScript(genFile);
const createComponentFactoryCall =
/ɵccf\([^)]*\)/m.exec(genFile.source) ![0].replace(/\s*/g, '');
/ɵccf\([^)]*\)/m.exec(genSource) ![0].replace(/\s*/g, '');
// selector
expect(createComponentFactoryCall).toContain('my-comp');
// inputs
@ -328,7 +333,8 @@ describe('compiler (unbundled Angular)', () => {
compile([FILES, angularFiles]).then(({genFiles}) => {
const genFile = genFiles.find(
gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
expect(genFile.source).not.toContain('check(');
const genSource = toTypeScript(genFile);
expect(genSource).not.toContain('check(');
});
}));
@ -411,7 +417,7 @@ describe('compiler (unbundled Angular)', () => {
.then(({genFiles}) => {
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
expect(mainNgFactory.source)
expect(toTypeScript(mainNgFactory))
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam]`);
});
}));
@ -463,7 +469,7 @@ describe('compiler (unbundled Angular)', () => {
.then(({genFiles}) => {
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
expect(mainNgFactory.source)
expect(toTypeScript(mainNgFactory))
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam_2]`);
});
}));

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AotCompiler, AotCompilerHost, AotCompilerOptions, CompileSummaryKind, GeneratedFile, createAotCompiler} from '@angular/compiler';
import {AotCompiler, AotCompilerHost, AotCompilerOptions, CompileSummaryKind, GeneratedFile, createAotCompiler, toTypeScript} from '@angular/compiler';
import {fakeAsync, tick} from '@angular/core/testing';
import {MockDirectory, compile, setup} from './test_util';
@ -44,11 +44,12 @@ describe('aot summaries for jit', () => {
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
expect(genFile.source).toContain(`import * as i0 from '/app/app.module'`);
expect(genFile.source).toContain('export function MyServiceNgSummary()');
const genSource = toTypeScript(genFile);
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
expect(genSource).toContain('export function MyServiceNgSummary()');
// Note: CompileSummaryKind.Injectable = 3
expect(genFile.source).toMatch(/summaryKind:3,\s*type:\{\s*reference:i0.MyService/);
expect(genFile.source).toContain('token:{identifier:{reference:i0.Dep}}');
expect(genSource).toMatch(/summaryKind:3,\s*type:\{\s*reference:i0.MyService/);
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
}));
it('should create @Pipe summaries', fakeAsync(() => {
@ -72,11 +73,12 @@ describe('aot summaries for jit', () => {
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
expect(genFile.source).toContain(`import * as i0 from '/app/app.module'`);
expect(genFile.source).toContain('export function MyPipeNgSummary()');
const genSource = toTypeScript(genFile);
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
expect(genSource).toContain('export function MyPipeNgSummary()');
// Note: CompileSummaryKind.Pipe = 1
expect(genFile.source).toMatch(/summaryKind:0,\s*type:\{\s*reference:i0.MyPipe/);
expect(genFile.source).toContain('token:{identifier:{reference:i0.Dep}}');
expect(genSource).toMatch(/summaryKind:0,\s*type:\{\s*reference:i0.MyPipe/);
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
}));
it('should create @Directive summaries', fakeAsync(() => {
@ -100,11 +102,12 @@ describe('aot summaries for jit', () => {
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
expect(genFile.source).toContain(`import * as i0 from '/app/app.module'`);
expect(genFile.source).toContain('export function MyDirectiveNgSummary()');
const genSource = toTypeScript(genFile);
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
expect(genSource).toContain('export function MyDirectiveNgSummary()');
// Note: CompileSummaryKind.Directive = 1
expect(genFile.source).toMatch(/summaryKind:1,\s*type:\{\s*reference:i0.MyDirective/);
expect(genFile.source).toContain('token:{identifier:{reference:i0.Dep}}');
expect(genSource).toMatch(/summaryKind:1,\s*type:\{\s*reference:i0.MyDirective/);
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
}));
it('should create @NgModule summaries', fakeAsync(() => {
@ -125,11 +128,12 @@ describe('aot summaries for jit', () => {
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
expect(genFile.source).toContain(`import * as i0 from '/app/app.module'`);
expect(genFile.source).toContain('export function MyModuleNgSummary()');
const genSource = toTypeScript(genFile);
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
expect(genSource).toContain('export function MyModuleNgSummary()');
// Note: CompileSummaryKind.NgModule = 2
expect(genFile.source).toMatch(/summaryKind:2,\s*type:\{\s*reference:i0.MyModule/);
expect(genFile.source).toContain('token:{identifier:{reference:i0.Dep}}');
expect(genSource).toMatch(/summaryKind:2,\s*type:\{\s*reference:i0.MyModule/);
expect(genSource).toContain('token:{identifier:{reference:i0.Dep}}');
}));
it('should embed useClass provider summaries in @Directive summaries', fakeAsync(() => {
@ -163,10 +167,11 @@ describe('aot summaries for jit', () => {
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
expect(genFile.source).toMatch(/useClass:\{\s*reference:i1.MyService/);
const genSource = toTypeScript(genFile);
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
// Note: CompileSummaryKind.Injectable = 3
expect(genFile.source).toMatch(/summaryKind:3,\s*type:\{\s*reference:i1.MyService/);
expect(genFile.source).toContain('token:{identifier:{reference:i1.Dep}}');
expect(genSource).toMatch(/summaryKind:3,\s*type:\{\s*reference:i1.MyService/);
expect(genSource).toContain('token:{identifier:{reference:i1.Dep}}');
}));
it('should embed useClass provider summaries into @NgModule summaries', fakeAsync(() => {
@ -196,10 +201,11 @@ describe('aot summaries for jit', () => {
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
expect(genFile.source).toMatch(/useClass:\{\s*reference:i1.MyService/);
const genSource = toTypeScript(genFile);
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
// Note: CompileSummaryKind.Injectable = 3
expect(genFile.source).toMatch(/summaryKind:3,\s*type:\{\s*reference:i1.MyService/);
expect(genFile.source).toContain('token:{identifier:{reference:i1.Dep}}');
expect(genSource).toMatch(/summaryKind:3,\s*type:\{\s*reference:i1.MyService/);
expect(genSource).toContain('token:{identifier:{reference:i1.Dep}}');
}));
it('should reference declared @Directive and @Pipe summaries in @NgModule summaries',
@ -223,8 +229,8 @@ describe('aot summaries for jit', () => {
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
expect(genFile.source)
.toMatch(
const genSource = toTypeScript(genFile);
expect(genSource).toMatch(
/export function MyModuleNgSummary()[^;]*,\s*MyDirectiveNgSummary,\s*MyPipeNgSummary\s*\]\s*;/);
}));
@ -245,8 +251,8 @@ describe('aot summaries for jit', () => {
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
expect(genFile.source)
.toMatch(
const genSource = toTypeScript(genFile);
expect(genSource).toMatch(
/export function MyModuleNgSummary()[^;]*,\s*MyImportedModuleNgSummary\s*\]\s*;/);
}));
@ -295,11 +301,11 @@ describe('aot summaries for jit', () => {
lib2Gen.find(f => f.genFileUrl === '/lib2/reexport.ngsummary.ts');
// ngsummaries should add reexports for imported NgModules from a direct dependency
expect(lib2ModuleNgSummary.source)
expect(toTypeScript(lib2ModuleNgSummary))
.toContain(
`export {Lib1ModuleNgSummary as Lib1Module_1NgSummary} from '/lib1/module.ngsummary'`);
// ngsummaries should add reexports for reexported values from a direct dependency
expect(lib2ReexportNgSummary.source)
expect(toTypeScript(lib2ReexportNgSummary))
.toContain(
`export {ReexportModuleNgSummary as ReexportModule_2NgSummary} from '/lib1/reexport.ngsummary'`);
@ -326,19 +332,20 @@ describe('aot summaries for jit', () => {
lib3Gen.find(f => f.genFileUrl === '/lib3/reexport.ngsummary.ts');
// ngsummary.ts files should use the reexported values from direct and deep deps
expect(lib3ModuleNgSummary.source).toContain(`import * as i4 from '/lib2/module.ngsummary'`);
expect(lib3ModuleNgSummary.source)
const lib3ModuleNgSummarySource = toTypeScript(lib3ModuleNgSummary);
expect(lib3ModuleNgSummarySource).toContain(`import * as i4 from '/lib2/module.ngsummary'`);
expect(lib3ModuleNgSummarySource)
.toContain(`import * as i5 from '/lib2/reexport.ngsummary'`);
expect(lib3ModuleNgSummary.source)
expect(lib3ModuleNgSummarySource)
.toMatch(
/export function Lib3ModuleNgSummary()[^;]*,\s*i4.Lib1Module_1NgSummary,\s*i4.Lib2ModuleNgSummary,\s*i5.ReexportModule_2NgSummary\s*\]\s*;/);
// ngsummaries should add reexports for imported NgModules from a deep dependency
expect(lib3ModuleNgSummary.source)
expect(lib3ModuleNgSummarySource)
.toContain(
`export {Lib1Module_1NgSummary as Lib1Module_1NgSummary,Lib2ModuleNgSummary as Lib2Module_2NgSummary} from '/lib2/module.ngsummary'`);
// ngsummaries should add reexports for reexported values from a deep dependency
expect(lib3ReexportNgSummary.source)
expect(toTypeScript(lib3ReexportNgSummary))
.toContain(
`export {ReexportModule_2NgSummary as ReexportModule_3NgSummary} from '/lib2/reexport.ngsummary'`);
}));

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AotCompilerHost, AotCompilerOptions, GeneratedFile, createAotCompiler} from '@angular/compiler';
import {AotCompilerHost, AotCompilerOptions, GeneratedFile, createAotCompiler, toTypeScript} from '@angular/compiler';
import {ɵReflectionCapabilities as ReflectionCapabilities, ɵreflector as reflector} from '@angular/core';
import {MetadataBundlerHost, MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped';
import * as fs from 'fs';
@ -630,9 +630,11 @@ export function compile(
if (preCompile) preCompile(program);
const {compiler, reflector} = createAotCompiler(aotHost, options);
return compiler.compileAll(program.getSourceFiles().map(sf => sf.fileName)).then(genFiles => {
genFiles.forEach(
file => isSource(file.genFileUrl) ? host.addScript(file.genFileUrl, file.source) :
host.override(file.genFileUrl, file.source));
genFiles.forEach((file) => {
const source = file.source || toTypeScript(file);
isSource(file.genFileUrl) ? host.addScript(file.genFileUrl, source) :
host.override(file.genFileUrl, source);
});
const scripts = host.scriptNames.slice(0);
const newProgram = ts.createProgram(scripts, tsSettings, host);
if (postCompile) postCompile(newProgram);