diff --git a/integration/ngcc/test.sh b/integration/ngcc/test.sh index d46ad3201d..efa8901f7a 100755 --- a/integration/ngcc/test.sh +++ b/integration/ngcc/test.sh @@ -33,6 +33,12 @@ ivy-ngcc grep "ApplicationModule.ngModuleDef = ɵngcc0.defineNgModule" node_modules/@angular/core/esm5/src/application_module.js if [[ $? != 0 ]]; then exit 1; fi +# Did it transform @angular/core typing files correctly? + grep "import [*] as ɵngcc0 from './r3_symbols';" node_modules/@angular/core/src/application_module.d.ts + if [[ $? != 0 ]]; then exit 1; fi + grep "static ngInjectorDef: ɵngcc0.InjectorDef;" node_modules/@angular/core/src/application_module.d.ts + if [[ $? != 0 ]]; then exit 1; fi + # Can it be safely run again (as a noop)? ivy-ngcc diff --git a/packages/compiler-cli/src/ngcc/src/packages/bundle.ts b/packages/compiler-cli/src/ngcc/src/packages/bundle.ts new file mode 100644 index 0000000000..872e8ad213 --- /dev/null +++ b/packages/compiler-cli/src/ngcc/src/packages/bundle.ts @@ -0,0 +1,31 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as ts from 'typescript'; + +/** + * A bundle represents the currently compiled entry point format, containing + * information that is necessary for compiling @angular/core with ngcc. + */ +export interface BundleInfo { + isCore: boolean; + isFlat: boolean; + rewriteCoreImportsTo: ts.SourceFile|null; + rewriteCoreDtsImportsTo: ts.SourceFile|null; +} + +export function createBundleInfo( + isCore: boolean, rewriteCoreImportsTo: ts.SourceFile | null, + rewriteCoreDtsImportsTo: ts.SourceFile | null): BundleInfo { + return { + isCore, + isFlat: rewriteCoreImportsTo === null, + rewriteCoreImportsTo: rewriteCoreImportsTo, + rewriteCoreDtsImportsTo: rewriteCoreDtsImportsTo, + }; +} diff --git a/packages/compiler-cli/src/ngcc/src/packages/transformer.ts b/packages/compiler-cli/src/ngcc/src/packages/transformer.ts index 53d2a18def..0b13e8a832 100644 --- a/packages/compiler-cli/src/ngcc/src/packages/transformer.ts +++ b/packages/compiler-cli/src/ngcc/src/packages/transformer.ts @@ -20,6 +20,7 @@ import {EsmRenderer} from '../rendering/esm_renderer'; import {FileInfo, Renderer} from '../rendering/renderer'; import {checkMarkerFile, writeMarkerFile} from './build_marker'; +import {BundleInfo, createBundleInfo} from './bundle'; import {EntryPoint, EntryPointFormat} from './entry_point'; /** @@ -70,27 +71,40 @@ export class Transformer { const host = ts.createCompilerHost(options); const rootDirs = this.getRootDirs(host, options); const isCore = entryPoint.name === '@angular/core'; - const r3SymbolsPath = isCore ? this.findR3SymbolsPath(dirname(entryPointFilePath)) : null; + const r3SymbolsPath = + isCore ? this.findR3SymbolsPath(dirname(entryPointFilePath), 'r3_symbols.js') : null; const rootPaths = r3SymbolsPath ? [entryPointFilePath, r3SymbolsPath] : [entryPointFilePath]; const packageProgram = ts.createProgram(rootPaths, options, host); - console.time(entryPoint.name + '(dtsmappper creation)'); - const dtsFilePath = entryPoint.typings; - const dtsProgram = transformDts ? ts.createProgram([entryPoint.typings], options, host) : null; - console.timeEnd(entryPoint.name + '(dtsmappper creation)'); - const reflectionHost = this.getHost(isCore, format, packageProgram, dtsFilePath, dtsProgram); const r3SymbolsFile = r3SymbolsPath && packageProgram.getSourceFile(r3SymbolsPath) || null; + // Create the program for processing DTS files if enabled for this format. + const dtsFilePath = entryPoint.typings; + let dtsProgram: ts.Program|null = null; + let r3SymbolsDtsFile: ts.SourceFile|null = null; + if (transformDts) { + console.time(`${entryPoint.name} (dtsMapper creation)`); + const r3SymbolsDtsPath = + isCore ? this.findR3SymbolsPath(dirname(dtsFilePath), 'r3_symbols.d.ts') : null; + const rootDtsPaths = r3SymbolsDtsPath ? [dtsFilePath, r3SymbolsDtsPath] : [dtsFilePath]; + + dtsProgram = ts.createProgram(rootDtsPaths, options, host); + r3SymbolsDtsFile = r3SymbolsDtsPath && dtsProgram.getSourceFile(r3SymbolsDtsPath) || null; + console.timeEnd(`${entryPoint.name} (dtsMapper creation)`); + } + + const bundle = createBundleInfo(isCore, r3SymbolsFile, r3SymbolsDtsFile); + const reflectionHost = this.getHost(isCore, format, packageProgram, dtsFilePath, dtsProgram); + // Parse and analyze the files. const {decorationAnalyses, switchMarkerAnalyses} = this.analyzeProgram(packageProgram, reflectionHost, rootDirs, isCore); - console.time(entryPoint.name + '(rendering)'); + console.time(`${entryPoint.name} (rendering)`); // Transform the source files and source maps. - const renderer = this.getRenderer( - format, packageProgram, reflectionHost, isCore, r3SymbolsFile, transformDts); + const renderer = this.getRenderer(format, packageProgram, reflectionHost, bundle, transformDts); const renderedFiles = renderer.renderProgram(packageProgram, decorationAnalyses, switchMarkerAnalyses); - console.timeEnd(entryPoint.name + '(rendering)'); + console.timeEnd(`${entryPoint.name} (rendering)`); // Write out all the transformed files. renderedFiles.forEach(file => this.writeFile(file)); @@ -125,17 +139,15 @@ export class Transformer { } getRenderer( - format: string, program: ts.Program, host: NgccReflectionHost, isCore: boolean, - rewriteCoreImportsTo: ts.SourceFile|null, transformDts: boolean): Renderer { + format: string, program: ts.Program, host: NgccReflectionHost, bundle: BundleInfo, + transformDts: boolean): Renderer { switch (format) { case 'esm2015': case 'fesm2015': - return new EsmRenderer( - host, isCore, rewriteCoreImportsTo, this.sourcePath, this.targetPath, transformDts); + return new EsmRenderer(host, bundle, this.sourcePath, this.targetPath, transformDts); case 'esm5': case 'fesm5': - return new Esm5Renderer( - host, isCore, rewriteCoreImportsTo, this.sourcePath, this.targetPath, transformDts); + return new Esm5Renderer(host, bundle, this.sourcePath, this.targetPath, transformDts); default: throw new Error(`Renderer for "${format}" not yet implemented.`); } @@ -162,8 +174,8 @@ export class Transformer { writeFileSync(file.path, file.contents, 'utf8'); } - findR3SymbolsPath(directory: string): string|null { - const r3SymbolsFilePath = resolve(directory, 'r3_symbols.js'); + findR3SymbolsPath(directory: string, fileName: string): string|null { + const r3SymbolsFilePath = resolve(directory, fileName); if (existsSync(r3SymbolsFilePath)) { return r3SymbolsFilePath; } @@ -181,7 +193,7 @@ export class Transformer { }); for (const subDirectory of subDirectories) { - const r3SymbolsFilePath = this.findR3SymbolsPath(resolve(directory, subDirectory)); + const r3SymbolsFilePath = this.findR3SymbolsPath(resolve(directory, subDirectory), fileName); if (r3SymbolsFilePath) { return r3SymbolsFilePath; } diff --git a/packages/compiler-cli/src/ngcc/src/rendering/esm5_renderer.ts b/packages/compiler-cli/src/ngcc/src/rendering/esm5_renderer.ts index 3d8c70e396..0cdf9b2bea 100644 --- a/packages/compiler-cli/src/ngcc/src/rendering/esm5_renderer.ts +++ b/packages/compiler-cli/src/ngcc/src/rendering/esm5_renderer.ts @@ -9,14 +9,14 @@ import * as ts from 'typescript'; import MagicString from 'magic-string'; import {NgccReflectionHost} from '../host/ngcc_host'; import {CompiledClass} from '../analysis/decoration_analyzer'; +import {BundleInfo} from '../packages/bundle'; import {EsmRenderer} from './esm_renderer'; export class Esm5Renderer extends EsmRenderer { constructor( - protected host: NgccReflectionHost, protected isCore: boolean, - protected rewriteCoreImportsTo: ts.SourceFile|null, protected sourcePath: string, - protected targetPath: string, transformDts: boolean) { - super(host, isCore, rewriteCoreImportsTo, sourcePath, targetPath, transformDts); + protected host: NgccReflectionHost, protected bundle: BundleInfo, + protected sourcePath: string, protected targetPath: string, transformDts: boolean) { + super(host, bundle, sourcePath, targetPath, transformDts); } /** diff --git a/packages/compiler-cli/src/ngcc/src/rendering/esm_renderer.ts b/packages/compiler-cli/src/ngcc/src/rendering/esm_renderer.ts index aa59ab1640..657665da21 100644 --- a/packages/compiler-cli/src/ngcc/src/rendering/esm_renderer.ts +++ b/packages/compiler-cli/src/ngcc/src/rendering/esm_renderer.ts @@ -9,14 +9,14 @@ import * as ts from 'typescript'; import MagicString from 'magic-string'; import {NgccReflectionHost, POST_R3_MARKER, PRE_R3_MARKER, SwitchableVariableDeclaration} from '../host/ngcc_host'; import {CompiledClass} from '../analysis/decoration_analyzer'; +import {BundleInfo} from '../packages/bundle'; import {Renderer} from './renderer'; export class EsmRenderer extends Renderer { constructor( - protected host: NgccReflectionHost, protected isCore: boolean, - protected rewriteCoreImportsTo: ts.SourceFile|null, protected sourcePath: string, - protected targetPath: string, transformDts: boolean) { - super(host, isCore, rewriteCoreImportsTo, sourcePath, targetPath, transformDts); + protected host: NgccReflectionHost, protected bundle: BundleInfo, + protected sourcePath: string, protected targetPath: string, transformDts: boolean) { + super(host, bundle, sourcePath, targetPath, transformDts); } /** diff --git a/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts b/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts index 54f2cacdd1..7e8c7ee85f 100644 --- a/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts +++ b/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts @@ -19,6 +19,7 @@ import {translateStatement, translateType} from '../../../ngtsc/translator'; import {NgccImportManager} from './ngcc_import_manager'; import {CompiledClass, CompiledFile, DecorationAnalyses} from '../analysis/decoration_analyzer'; import {SwitchMarkerAnalyses, SwitchMarkerAnalysis} from '../analysis/switch_marker_analyzer'; +import {BundleInfo} from '../packages/bundle'; import {IMPORT_PREFIX} from '../constants'; import {NgccReflectionHost, SwitchableVariableDeclaration} from '../host/ngcc_host'; @@ -55,9 +56,9 @@ interface DtsClassInfo { */ export abstract class Renderer { constructor( - protected host: NgccReflectionHost, protected isCore: boolean, - protected rewriteCoreImportsTo: ts.SourceFile|null, protected sourcePath: string, - protected targetPath: string, protected transformDts: boolean) {} + protected host: NgccReflectionHost, protected bundle: BundleInfo, + protected sourcePath: string, protected targetPath: string, protected transformDts: boolean) { + } renderProgram( program: ts.Program, decorationAnalyses: DecorationAnalyses, @@ -101,7 +102,7 @@ export abstract class Renderer { if (compiledFile) { const importManager = - new NgccImportManager(!this.rewriteCoreImportsTo, this.isCore, IMPORT_PREFIX); + new NgccImportManager(this.bundle.isFlat, this.bundle.isCore, IMPORT_PREFIX); const decoratorsToRemove = new Map(); compiledFile.compiledClasses.forEach(clazz => { @@ -116,8 +117,8 @@ export abstract class Renderer { compiledFile.sourceFile); this.addImports( - outputText, - importManager.getAllImports(compiledFile.sourceFile.fileName, this.rewriteCoreImportsTo)); + outputText, importManager.getAllImports( + compiledFile.sourceFile.fileName, this.bundle.rewriteCoreImportsTo)); // TODO: remove contructor param metadata and property decorators (we need info from the // handlers to do this) @@ -130,7 +131,7 @@ export abstract class Renderer { renderDtsFile(dtsFile: ts.SourceFile, dtsClasses: DtsClassInfo[]): FileInfo[] { const input = this.extractSourceMap(dtsFile); const outputText = new MagicString(input.source); - const importManager = new NgccImportManager(false, this.isCore, IMPORT_PREFIX); + const importManager = new NgccImportManager(false, this.bundle.isCore, IMPORT_PREFIX); dtsClasses.forEach(dtsClass => { const endOfClass = dtsClass.dtsDeclaration.getEnd(); @@ -142,7 +143,8 @@ export abstract class Renderer { }); this.addImports( - outputText, importManager.getAllImports(dtsFile.fileName, this.rewriteCoreImportsTo)); + outputText, + importManager.getAllImports(dtsFile.fileName, this.bundle.rewriteCoreDtsImportsTo)); return this.renderSourceAndMap(dtsFile, input, outputText); } diff --git a/packages/compiler-cli/src/ngcc/test/rendering/esm2015_renderer_spec.ts b/packages/compiler-cli/src/ngcc/test/rendering/esm2015_renderer_spec.ts index d1ba2c9f68..7d51ce172e 100644 --- a/packages/compiler-cli/src/ngcc/test/rendering/esm2015_renderer_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/rendering/esm2015_renderer_spec.ts @@ -12,6 +12,7 @@ import MagicString from 'magic-string'; import {makeProgram} from '../helpers/utils'; import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer'; +import {createBundleInfo} from '../../src/packages/bundle'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; import {EsmRenderer} from '../../src/rendering/esm_renderer'; @@ -23,7 +24,8 @@ function setup(file: {name: string, contents: string}, transformDts: boolean = f const decorationAnalyses = new DecorationAnalyzer(program.getTypeChecker(), host, [''], false).analyzeProgram(program); const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(program); - const renderer = new EsmRenderer(host, false, null, dir, dir, false); + const bundle = createBundleInfo(false, null, null); + const renderer = new EsmRenderer(host, bundle, dir, dir, false); return {host, program, sourceFile, renderer, decorationAnalyses, switchMarkerAnalyses}; } diff --git a/packages/compiler-cli/src/ngcc/test/rendering/esm5_renderer_spec.ts b/packages/compiler-cli/src/ngcc/test/rendering/esm5_renderer_spec.ts index 3cad53c8fc..c04bbee610 100644 --- a/packages/compiler-cli/src/ngcc/test/rendering/esm5_renderer_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/rendering/esm5_renderer_spec.ts @@ -10,6 +10,7 @@ import MagicString from 'magic-string'; import {makeProgram, getDeclaration} from '../helpers/utils'; import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer'; +import {createBundleInfo} from '../../src/packages/bundle'; import {Esm5ReflectionHost} from '../../src/host/esm5_host'; import {Esm5Renderer} from '../../src/rendering/esm5_renderer'; @@ -20,7 +21,8 @@ function setup(file: {name: string, contents: string}) { const decorationAnalyses = new DecorationAnalyzer(program.getTypeChecker(), host, [''], false).analyzeProgram(program); const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(program); - const renderer = new Esm5Renderer(host, false, null, '', '', false); + const bundle = createBundleInfo(false, null, null); + const renderer = new Esm5Renderer(host, bundle, '', '', false); return {host, program, sourceFile, renderer, decorationAnalyses, switchMarkerAnalyses}; } diff --git a/packages/compiler-cli/src/ngcc/test/rendering/renderer_spec.ts b/packages/compiler-cli/src/ngcc/test/rendering/renderer_spec.ts index 1fc8c6da8e..e63fa81f89 100644 --- a/packages/compiler-cli/src/ngcc/test/rendering/renderer_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/rendering/renderer_spec.ts @@ -13,13 +13,13 @@ import {fromObject, generateMapFileComment} from 'convert-source-map'; import {makeProgram} from '../helpers/utils'; import {CompiledClass, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer'; +import {BundleInfo, createBundleInfo} from '../../src/packages/bundle'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; import {Renderer} from '../../src/rendering/renderer'; class TestRenderer extends Renderer { - constructor( - host: Esm2015ReflectionHost, isCore: boolean, rewriteCoreImportsTo: ts.SourceFile|null) { - super(host, isCore, rewriteCoreImportsTo, '/src', '/dist', false); + constructor(host: Esm2015ReflectionHost, bundle: BundleInfo) { + super(host, bundle, '/src', '/dist', false); } addImports(output: MagicString, imports: {name: string, as: string}[]) { output.prepend('\n// ADD IMPORTS\n'); @@ -42,14 +42,15 @@ function createTestRenderer( files: {name: string, contents: string}[], options: {isCore?: boolean, rewriteCoreImportsTo?: string} = {}) { const program = makeProgram(...files); - const host = new Esm2015ReflectionHost(options.isCore || false, program.getTypeChecker()); - const decorationAnalyses = - new DecorationAnalyzer(program.getTypeChecker(), host, [''], options.isCore || false) - .analyzeProgram(program); - const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(program); const rewriteCoreImportsTo = options.rewriteCoreImportsTo ? program.getSourceFile(options.rewriteCoreImportsTo) ! : null; - const renderer = new TestRenderer(host, options.isCore || false, rewriteCoreImportsTo); + const bundle = createBundleInfo(options.isCore || false, rewriteCoreImportsTo, null); + const host = new Esm2015ReflectionHost(bundle.isCore, program.getTypeChecker()); + const decorationAnalyses = + new DecorationAnalyzer(program.getTypeChecker(), host, [''], bundle.isCore) + .analyzeProgram(program); + const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(program); + const renderer = new TestRenderer(host, bundle); spyOn(renderer, 'addImports').and.callThrough(); spyOn(renderer, 'addDefinitions').and.callThrough(); spyOn(renderer, 'removeDecorators').and.callThrough();