diff --git a/packages/compiler-cli/src/codegen.ts b/packages/compiler-cli/src/codegen.ts index 569ee37bd3..8d19416dd8 100644 --- a/packages/compiler-cli/src/codegen.ts +++ b/packages/compiler-cli/src/codegen.ts @@ -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); } diff --git a/packages/compiler/src/aot/compiler.ts b/packages/compiler/src/aot/compiler.ts index 988a6f7870..86a471a99d 100644 --- a/packages/compiler/src/aot/compiler.ts +++ b/packages/compiler/src/aot/compiler.ts @@ -33,8 +33,7 @@ export class AotCompiler { private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler, private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter, private _summaryResolver: SummaryResolver, 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); } } diff --git a/packages/compiler/src/aot/compiler_factory.ts b/packages/compiler/src/aot/compiler_factory.ts index 0ed14b0443..8e7be5b1ed 100644 --- a/packages/compiler/src/aot/compiler_factory.ts +++ b/packages/compiler/src/aot/compiler_factory.ts @@ -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}; } diff --git a/packages/compiler/src/aot/compiler_options.ts b/packages/compiler/src/aot/compiler_options.ts index 8dd59f6d3f..84b899fc11 100644 --- a/packages/compiler/src/aot/compiler_options.ts +++ b/packages/compiler/src/aot/compiler_options.ts @@ -14,6 +14,4 @@ export interface AotCompilerOptions { translations?: string; missingTranslation?: MissingTranslationStrategy; enableLegacyTemplate?: boolean; - /** preamble for all generated source files */ - genFilePreamble?: string; } diff --git a/packages/compiler/src/aot/generated_file.ts b/packages/compiler/src/aot/generated_file.ts index b698397e84..226126f4f4 100644 --- a/packages/compiler/src/aot/generated_file.ts +++ b/packages/compiler/src/aot/generated_file.ts @@ -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); } diff --git a/packages/compiler/test/aot/compiler_spec.ts b/packages/compiler/test/aot/compiler_spec.ts index 1c02ea4350..7859691886 100644 --- a/packages/compiler/test/aot/compiler_spec.ts +++ b/packages/compiler/test/aot/compiler_spec.ts @@ -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]`); }); })); diff --git a/packages/compiler/test/aot/jit_summaries_spec.ts b/packages/compiler/test/aot/jit_summaries_spec.ts index 934e37257d..671a537edd 100644 --- a/packages/compiler/test/aot/jit_summaries_spec.ts +++ b/packages/compiler/test/aot/jit_summaries_spec.ts @@ -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,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(() => { @@ -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 ' + @@ -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'`); })); diff --git a/packages/compiler/test/aot/test_util.ts b/packages/compiler/test/aot/test_util.ts index c53ac27acd..d51c05efc9 100644 --- a/packages/compiler/test/aot/test_util.ts +++ b/packages/compiler/test/aot/test_util.ts @@ -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);