Skip to content

Commit

Permalink
refactor(compiler): emit OutputAst and not sources
Browse files Browse the repository at this point in the history
This is in preparation for creating typescript nodes
directly from `OutputAst` nodes.
  • Loading branch information
tbosch committed May 17, 2017
1 parent 56777ed commit 9ff389c
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 70 deletions.
5 changes: 2 additions & 3 deletions packages/compiler-cli/src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
});
});
Expand Down Expand Up @@ -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);
}
Expand Down
8 changes: 2 additions & 6 deletions packages/compiler/src/aot/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(); }

Expand Down Expand Up @@ -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);
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/compiler/src/aot/compiler_factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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};
}
2 changes: 0 additions & 2 deletions packages/compiler/src/aot/compiler_options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,4 @@ export interface AotCompilerOptions {
translations?: string;
missingTranslation?: MissingTranslationStrategy;
enableLegacyTemplate?: boolean;
/** preamble for all generated source files */
genFilePreamble?: string;
}
26 changes: 25 additions & 1 deletion packages/compiler/src/aot/generated_file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
40 changes: 23 additions & 17 deletions packages/compiler/test/aot/compiler_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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
Expand All @@ -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});
});
}));
Expand All @@ -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});
});
}));
Expand All @@ -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});
});
}));
Expand All @@ -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});
});
Expand Down Expand Up @@ -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);
});

}));
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(');
});

}));
Expand Down Expand Up @@ -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]`);
});
}));
Expand Down Expand Up @@ -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]`);
});
}));
Expand Down
79 changes: 43 additions & 36 deletions packages/compiler/test/aot/jit_summaries_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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(() => {
Expand All @@ -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(() => {
Expand All @@ -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(() => {
Expand All @@ -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(() => {
Expand Down Expand Up @@ -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(() => {
Expand Down Expand Up @@ -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',
Expand All @@ -223,9 +229,9 @@ describe('aot summaries for jit', () => {
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');

expect(genFile.source)
.toMatch(
/export function MyModuleNgSummary()[^;]*,\s*MyDirectiveNgSummary,\s*MyPipeNgSummary\s*\]\s*;/);
const genSource = toTypeScript(genFile);
expect(genSource).toMatch(
/export function MyModuleNgSummary()[^;]*,\s*MyDirectiveNgSummary,\s*MyPipeNgSummary\s*\]\s*;/);
}));

it('should reference imported @NgModule summaries in @NgModule summaries', fakeAsync(() => {
Expand All @@ -245,9 +251,9 @@ describe('aot summaries for jit', () => {
const genFile =
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');

expect(genFile.source)
.toMatch(
/export function MyModuleNgSummary()[^;]*,\s*MyImportedModuleNgSummary\s*\]\s*;/);
const genSource = toTypeScript(genFile);
expect(genSource).toMatch(
/export function MyModuleNgSummary()[^;]*,\s*MyImportedModuleNgSummary\s*\]\s*;/);
}));

it('should create and use reexports for imported NgModules ' +
Expand Down Expand Up @@ -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'`);

Expand All @@ -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'`);
}));
Expand Down

0 comments on commit 9ff389c

Please sign in to comment.