From e842d1858133ab1a902dd4200c07ab26ff630b00 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 21 Nov 2018 20:52:27 +0000 Subject: [PATCH 01/13] fix(ivy): ngcc - support typings (d.ts) classes that are not publicly exported If a decorated class is not publicly exported via an entry-point then the previous approach to finding the associated typings file failed. Now we ensure that we extract all the class declarations from the dtsTypings program, even if they are not exported from the entry-point. This is achieved by also parsing statements of each source file, rather than just parsing classes that are exported from the entry-point. Because we now look at all the files, it is possible for there to be multiple class declarations with the same local name. In this case, only the first declaration with a given name is added to the map; subsequent classes are ignored. We are most interested in classes that are publicly exported from the entry-point, so these are added to the map first, to ensure that they are not ignored. --- .../src/ngcc/src/host/esm2015_host.ts | 78 ++++++++++++------- .../src/ngcc/test/host/esm2015_host_spec.ts | 62 ++++++++++++--- 2 files changed, 103 insertions(+), 37 deletions(-) diff --git a/packages/compiler-cli/src/ngcc/src/host/esm2015_host.ts b/packages/compiler-cli/src/ngcc/src/host/esm2015_host.ts index a59610ee4b18..4ef22cb3b3ad 100644 --- a/packages/compiler-cli/src/ngcc/src/host/esm2015_host.ts +++ b/packages/compiler-cli/src/ngcc/src/host/esm2015_host.ts @@ -356,12 +356,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N throw new Error( `Cannot get the dts file for a class declaration that has no indetifier: ${declaration.getText()} in ${declaration.getSourceFile().fileName}`); } - const dtsDeclaration = this.dtsClassMap.get(declaration.name.text); - if (!dtsDeclaration) { - throw new Error( - `Unable to find matching typings (.d.ts) declaration for ${declaration.name.text} in ${declaration.getSourceFile().fileName}`); - } - return dtsDeclaration; + return this.dtsClassMap.get(declaration.name.text) || null; } } return null; @@ -993,31 +988,37 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N } } + /** + * Extract all the class declarations from the dtsTypings program, storing them in a map + * where the key is the declared name of the class and the value is the declaration itself. + * + * It is possible for there to be multiple class declarations with the same local name. + * Only the first declaration with a given name is added to the map; subsequent classes will be + * ignored. + * + * We are most interested in classes that are publicly exported from the entry point, so these are + * added to the map first, to ensure that they are not ignored. + * + * @param dtsRootFileName The filename of the entry-point to the `dtsTypings` program. + * @param dtsProgram The program containing all the typings files. + * @returns a map of class names to class declarations. + */ protected computeDtsClassMap(dtsRootFileName: string, dtsProgram: ts.Program): Map { const dtsClassMap = new Map(); const checker = dtsProgram.getTypeChecker(); - const dtsRootFile = dtsProgram.getSourceFile(dtsRootFileName); - const rootModule = dtsRootFile && checker.getSymbolAtLocation(dtsRootFile); - const moduleExports = rootModule && checker.getExportsOfModule(rootModule); - if (moduleExports) { - moduleExports.forEach(exportedSymbol => { - if (exportedSymbol.flags & ts.SymbolFlags.Alias) { - exportedSymbol = checker.getAliasedSymbol(exportedSymbol); - } - const declaration = exportedSymbol.declarations[0]; - if (declaration && ts.isClassDeclaration(declaration)) { - const name = exportedSymbol.name; - const previousDeclaration = dtsClassMap.get(name); - if (previousDeclaration && previousDeclaration !== declaration) { - console.warn( - `Ambiguous class name ${name} in typings files: ${previousDeclaration.getSourceFile().fileName} and ${declaration.getSourceFile().fileName}`); - } else { - dtsClassMap.set(name, declaration); - } - } - }); + + // First add all the classes that are publicly exported from the entry-point + const rootFile = dtsProgram.getSourceFile(dtsRootFileName); + if (!rootFile) { + throw new Error(`The given file ${dtsRootFileName} is not part of the typings program.`); } + collectExportedClasses(checker, dtsClassMap, rootFile); + + // Now add any additional classes that are exported from individual dts files, + // but are not publicly exported from the entry-point. + dtsProgram.getSourceFiles().forEach( + sourceFile => { collectExportedClasses(checker, dtsClassMap, sourceFile); }); return dtsClassMap; } } @@ -1151,3 +1152,28 @@ function getFarLeftIdentifier(propertyAccess: ts.PropertyAccessExpression): ts.I } return ts.isIdentifier(propertyAccess.expression) ? propertyAccess.expression : null; } + +/** + * Search a source file for exported classes, storing them in the provided `dtsClassMap`. + * @param checker The typechecker for the source program. + * @param dtsClassMap The map in which to store the collected exported classes. + * @param srcFile The source file to search for exported classes. + */ +function collectExportedClasses( + checker: ts.TypeChecker, dtsClassMap: Map, + srcFile: ts.SourceFile): void { + const srcModule = srcFile && checker.getSymbolAtLocation(srcFile); + const moduleExports = srcModule && checker.getExportsOfModule(srcModule); + if (moduleExports) { + moduleExports.forEach(exportedSymbol => { + if (exportedSymbol.flags & ts.SymbolFlags.Alias) { + exportedSymbol = checker.getAliasedSymbol(exportedSymbol); + } + const declaration = exportedSymbol.valueDeclaration; + const name = exportedSymbol.name; + if (declaration && ts.isClassDeclaration(declaration) && !dtsClassMap.has(name)) { + dtsClassMap.set(name, declaration); + } + }); + } +} diff --git a/packages/compiler-cli/src/ngcc/test/host/esm2015_host_spec.ts b/packages/compiler-cli/src/ngcc/test/host/esm2015_host_spec.ts index e9493946b04c..2878872ec2c4 100644 --- a/packages/compiler-cli/src/ngcc/test/host/esm2015_host_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/host/esm2015_host_spec.ts @@ -445,9 +445,14 @@ const ARITY_CLASSES = [ ]; const TYPINGS_SRC_FILES = [ - {name: '/src/index.js', contents: `export * from './class1'; export * from './class2';`}, + { + name: '/src/index.js', + contents: + `import {InternalClass} from './internal'; export * from './class1'; export * from './class2';` + }, {name: '/src/class1.js', contents: 'export class Class1 {}\nexport class MissingClass1 {}'}, {name: '/src/class2.js', contents: 'export class Class2 {}'}, + {name: '/src/internal.js', contents: 'export class InternalClass {}\nexport class Class2 {}'}, {name: '/src/missing-class.js', contents: 'export class MissingClass2 {}'}, { name: '/src/flat-file.js', contents: @@ -456,7 +461,11 @@ const TYPINGS_SRC_FILES = [ ]; const TYPINGS_DTS_FILES = [ - {name: '/typings/index.d.ts', contents: `export * from './class1'; export * from './class2';`}, + { + name: '/typings/index.d.ts', + contents: + `import {InternalClass} from './internal'; export * from './class1'; export * from './class2';` + }, { name: '/typings/class1.d.ts', contents: `export declare class Class1 {}\nexport declare class OtherClass {}` @@ -466,6 +475,10 @@ const TYPINGS_DTS_FILES = [ contents: `export declare class Class2 {}\nexport declare interface SomeInterface {}\nexport {Class3 as xClass3} from './class3';` }, + { + name: '/typings/internal.d.ts', + contents: `export declare class InternalClass {}\nexport declare class Class2 {}` + }, {name: '/typings/class3.d.ts', contents: `export declare class Class3 {}`}, ]; @@ -1277,7 +1290,7 @@ describe('Fesm2015ReflectionHost', () => { expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts'); }); - it('should throw an error if there is no matching class in the matching dts file', () => { + it('should return null if there is no matching class in the matching dts file', () => { const srcProgram = makeProgram(...TYPINGS_SRC_FILES); const dtsProgram = makeProgram(...TYPINGS_DTS_FILES); const missingClass = @@ -1285,12 +1298,10 @@ describe('Fesm2015ReflectionHost', () => { const host = new Esm2015ReflectionHost( false, srcProgram.getTypeChecker(), TYPINGS_DTS_FILES[0].name, dtsProgram); - expect(() => host.getDtsDeclarationOfClass(missingClass)) - .toThrowError( - 'Unable to find matching typings (.d.ts) declaration for MissingClass1 in /src/class1.js'); + expect(host.getDtsDeclarationOfClass(missingClass)).toBe(null); }); - it('should throw an error if there is no matching dts file', () => { + it('should return null if there is no matching dts file', () => { const srcProgram = makeProgram(...TYPINGS_SRC_FILES); const dtsProgram = makeProgram(...TYPINGS_DTS_FILES); const missingClass = getDeclaration( @@ -1298,9 +1309,7 @@ describe('Fesm2015ReflectionHost', () => { const host = new Esm2015ReflectionHost( false, srcProgram.getTypeChecker(), TYPINGS_DTS_FILES[0].name, dtsProgram); - expect(() => host.getDtsDeclarationOfClass(missingClass)) - .toThrowError( - 'Unable to find matching typings (.d.ts) declaration for MissingClass2 in /src/missing-class.js'); + expect(host.getDtsDeclarationOfClass(missingClass)).toBe(null); }); it('should find the dts file that contains a matching class declaration, even if the source files do not match', @@ -1327,6 +1336,37 @@ describe('Fesm2015ReflectionHost', () => { const dtsDeclaration = host.getDtsDeclarationOfClass(class3); expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class3.d.ts'); }); - }); + it('should find the dts file that contains a matching class declaration, even if the class is not publicly exported', + () => { + const srcProgram = makeProgram(...TYPINGS_SRC_FILES); + const dtsProgram = makeProgram(...TYPINGS_DTS_FILES); + const internalClass = + getDeclaration(srcProgram, '/src/internal.js', 'InternalClass', ts.isClassDeclaration); + const host = new Esm2015ReflectionHost( + false, srcProgram.getTypeChecker(), TYPINGS_DTS_FILES[0].name, dtsProgram); + + const dtsDeclaration = host.getDtsDeclarationOfClass(internalClass); + expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/internal.d.ts'); + }); + + it('should prefer the publicly exported class if there are multiple classes with the same name', + () => { + const srcProgram = makeProgram(...TYPINGS_SRC_FILES); + const dtsProgram = makeProgram(...TYPINGS_DTS_FILES); + const class2 = + getDeclaration(srcProgram, '/src/class2.js', 'Class2', ts.isClassDeclaration); + const internalClass2 = + getDeclaration(srcProgram, '/src/internal.js', 'Class2', ts.isClassDeclaration); + const host = new Esm2015ReflectionHost( + false, srcProgram.getTypeChecker(), TYPINGS_DTS_FILES[0].name, dtsProgram); + + const class2DtsDeclaration = host.getDtsDeclarationOfClass(class2); + expect(class2DtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class2.d.ts'); + + const internalClass2DtsDeclaration = host.getDtsDeclarationOfClass(internalClass2); + expect(internalClass2DtsDeclaration !.getSourceFile().fileName) + .toEqual('/typings/class2.d.ts'); + }); + }); }); From 9766024ae62baf06f0646e93f2a1db6c734ae0d2 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Tue, 13 Nov 2018 09:02:13 +0000 Subject: [PATCH 02/13] refactor(ivy): ngcc - make `EntryPoint` an interface rather than a type By inverting the relationship between `EntryPointPaths` and `EntryPointFormat` we can have interfaces rather than types. Thanks to @gkalpak for this idea. --- .../src/ngcc/src/packages/entry_point.ts | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/packages/compiler-cli/src/ngcc/src/packages/entry_point.ts b/packages/compiler-cli/src/ngcc/src/packages/entry_point.ts index 59742bfb314b..74c8091f12b3 100644 --- a/packages/compiler-cli/src/ngcc/src/packages/entry_point.ts +++ b/packages/compiler-cli/src/ngcc/src/packages/entry_point.ts @@ -11,22 +11,26 @@ import * as fs from 'fs'; /** - * The possible values for the format of an entry-point. + * An object containing paths to the entry-points for each format. */ -export type EntryPointFormat = 'esm5' | 'fesm5' | 'esm2015' | 'fesm2015' | 'umd'; +export interface EntryPointPaths { + esm5?: string; + fesm5?: string; + esm2015?: string; + fesm2015?: string; + umd?: string; +} /** - * An object containing paths to the entry-points for each format. + * The possible values for the format of an entry-point. */ -export type EntryPointPaths = { - [Format in EntryPointFormat]?: string; -}; +export type EntryPointFormat = keyof(EntryPointPaths); /** * An object containing information about an entry-point, including paths * to each of the possible entry-point formats. */ -export type EntryPoint = EntryPointPaths & { +export interface EntryPoint extends EntryPointPaths { /** The name of the package (e.g. `@angular/core`). */ name: string; /** The path to the package that contains this entry-point. */ @@ -35,8 +39,11 @@ export type EntryPoint = EntryPointPaths & { path: string; /** The path to a typings (.d.ts) file for this entry-point. */ typings: string; -}; +} +/** + * The properties that may be loaded from the `package.json` file. + */ interface EntryPointPackageJson { name: string; fesm2015?: string; @@ -66,13 +73,13 @@ function loadEntryPointPackage(packageJsonPath: string): {[key: string]: any}|nu } /** - * Try to get entry point info from the given path. - * @param pkgPath the absolute path to the containing npm package - * @param entryPoint the absolute path to the potential entry point. + * Try to get an entry point from the given path. + * @param packagePath the absolute path to the containing npm package + * @param entryPointPath the absolute path to the potential entry point. * @returns Info about the entry point if it is valid, `null` otherwise. */ -export function getEntryPointInfo(pkgPath: string, entryPoint: string): EntryPoint|null { - const packageJsonPath = path.resolve(entryPoint, 'package.json'); +export function getEntryPointInfo(packagePath: string, entryPointPath: string): EntryPoint|null { + const packageJsonPath = path.resolve(entryPointPath, 'package.json'); if (!fs.existsSync(packageJsonPath)) { return null; } @@ -88,7 +95,7 @@ export function getEntryPointInfo(pkgPath: string, entryPoint: string): EntryPoi name, module: modulePath, types, - typings = types, // synonymous + typings = types, // synonymous es2015, fesm2015 = es2015, // synonymous fesm5 = modulePath, // synonymous @@ -101,33 +108,33 @@ export function getEntryPointInfo(pkgPath: string, entryPoint: string): EntryPoi return null; } - // Also we need to have a metadata.json file - const metadataPath = path.resolve(entryPoint, typings.replace(/\.d\.ts$/, '') + '.metadata.json'); + // Also there must exist a `metadata.json` file next to the typings entry-point. + const metadataPath = path.resolve(entryPointPath, typings.replace(/\.d\.ts$/, '') + '.metadata.json'); if (!fs.existsSync(metadataPath)) { return null; } const entryPointInfo: EntryPoint = { name, - package: pkgPath, - path: entryPoint, - typings: path.resolve(entryPoint, typings), + package: packagePath, + path: entryPointPath, + typings: path.resolve(entryPointPath, typings), }; if (esm2015) { - entryPointInfo.esm2015 = path.resolve(entryPoint, esm2015); + entryPointInfo.esm2015 = path.resolve(entryPointPath, esm2015); } if (fesm2015) { - entryPointInfo.fesm2015 = path.resolve(entryPoint, fesm2015); + entryPointInfo.fesm2015 = path.resolve(entryPointPath, fesm2015); } if (fesm5) { - entryPointInfo.fesm5 = path.resolve(entryPoint, fesm5); + entryPointInfo.fesm5 = path.resolve(entryPointPath, fesm5); } if (esm5) { - entryPointInfo.esm5 = path.resolve(entryPoint, esm5); + entryPointInfo.esm5 = path.resolve(entryPointPath, esm5); } if (main) { - entryPointInfo.umd = path.resolve(entryPoint, main); + entryPointInfo.umd = path.resolve(entryPointPath, main); } return entryPointInfo; From fc6a58e7375e1e65c4094547d418627b1e2c8166 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Tue, 13 Nov 2018 14:40:54 +0000 Subject: [PATCH 03/13] style(ivy): ngcc - fix misspelled method --- .../src/ngcc/src/packages/dependency_host.ts | 4 +-- .../test/packages/dependency_host_spec.ts | 25 ++++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/compiler-cli/src/ngcc/src/packages/dependency_host.ts b/packages/compiler-cli/src/ngcc/src/packages/dependency_host.ts index 9255fd5de01c..5c26e34a8bff 100644 --- a/packages/compiler-cli/src/ngcc/src/packages/dependency_host.ts +++ b/packages/compiler-cli/src/ngcc/src/packages/dependency_host.ts @@ -28,7 +28,7 @@ export class DependencyHost { from: string, resolved: Set, missing: Set, deepImports: Set, internal: Set = new Set()): void { const fromContents = fs.readFileSync(from, 'utf8'); - if (!this.hasImportOrReeportStatements(fromContents)) { + if (!this.hasImportOrReexportStatements(fromContents)) { return; } @@ -142,7 +142,7 @@ export class DependencyHost { * @returns false if there are definitely no import or re-export statements * in this file, true otherwise. */ - hasImportOrReeportStatements(source: string): boolean { + hasImportOrReexportStatements(source: string): boolean { return /(import|export)\s.+from/.test(source); } } diff --git a/packages/compiler-cli/src/ngcc/test/packages/dependency_host_spec.ts b/packages/compiler-cli/src/ngcc/test/packages/dependency_host_spec.ts index 1542cfea9ec6..fc19dd07e42b 100644 --- a/packages/compiler-cli/src/ngcc/test/packages/dependency_host_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/packages/dependency_host_spec.ts @@ -222,29 +222,30 @@ describe('DependencyHost', () => { } }); - describe('hasImportOrReeportStatements', () => { + describe('hasImportOrReexportStatements', () => { it('should return true if there is an import statement', () => { - expect(host.hasImportOrReeportStatements('import {X} from "some/x";')).toBe(true); - expect(host.hasImportOrReeportStatements('import * as X from "some/x";')).toBe(true); + expect(host.hasImportOrReexportStatements('import {X} from "some/x";')).toBe(true); + expect(host.hasImportOrReexportStatements('import * as X from "some/x";')).toBe(true); expect( - host.hasImportOrReeportStatements('blah blah\n\n import {X} from "some/x";\nblah blah')) + host.hasImportOrReexportStatements('blah blah\n\n import {X} from "some/x";\nblah blah')) .toBe(true); - expect(host.hasImportOrReeportStatements('\t\timport {X} from "some/x";')).toBe(true); + expect(host.hasImportOrReexportStatements('\t\timport {X} from "some/x";')).toBe(true); }); it('should return true if there is a re-export statement', () => { - expect(host.hasImportOrReeportStatements('export {X} from "some/x";')).toBe(true); + expect(host.hasImportOrReexportStatements('export {X} from "some/x";')).toBe(true); expect( - host.hasImportOrReeportStatements('blah blah\n\n export {X} from "some/x";\nblah blah')) + host.hasImportOrReexportStatements('blah blah\n\n export {X} from "some/x";\nblah blah')) .toBe(true); - expect(host.hasImportOrReeportStatements('\t\texport {X} from "some/x";')).toBe(true); - expect(host.hasImportOrReeportStatements( + expect(host.hasImportOrReexportStatements('\t\texport {X} from "some/x";')).toBe(true); + expect(host.hasImportOrReexportStatements( 'blah blah\n\n export * from "@angular/core;\nblah blah')) .toBe(true); }); it('should return false if there is no import nor re-export statement', () => { - expect(host.hasImportOrReeportStatements('blah blah')).toBe(false); - expect(host.hasImportOrReeportStatements('export function moo() {}')).toBe(false); - expect(host.hasImportOrReeportStatements('Some text that happens to include the word import')) + expect(host.hasImportOrReexportStatements('blah blah')).toBe(false); + expect(host.hasImportOrReexportStatements('export function moo() {}')).toBe(false); + expect( + host.hasImportOrReexportStatements('Some text that happens to include the word import')) .toBe(false); }); }); From c918dc3cdc6a967b7a61ca6f92d09e701bca91e8 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Tue, 13 Nov 2018 14:40:54 +0000 Subject: [PATCH 04/13] feat(ivy): register references from NgModule annotations The `NgModuleDecoratorHandler` can now register all the references that it finds in the `NgModule` metadata, such as `declarations`, `imports`, `exports` etc. This information can then be used by ngcc to work out if any of these references are internal only and need to be manually exported from a library's entry-point. --- .../ngcc/src/analysis/decoration_analyzer.ts | 9 ++-- .../src/analysis/ngcc_references_registry.ts | 49 +++++++++++++++++ .../src/ngcc/src/packages/entry_point.ts | 7 +-- .../src/ngcc/src/packages/transformer.ts | 5 +- packages/compiler-cli/src/ngcc/src/utils.ts | 10 ++++ .../test/analysis/decoration_analyzer_spec.ts | 20 ++++--- .../test/analysis/references_registry_spec.ts | 52 +++++++++++++++++++ .../src/ngcc/test/rendering/renderer_spec.ts | 5 +- .../src/ngtsc/annotations/index.ts | 1 + .../src/ngtsc/annotations/src/ng_module.ts | 8 ++- .../annotations/src/references_registry.ts | 39 ++++++++++++++ packages/compiler-cli/src/ngtsc/program.ts | 6 ++- 12 files changed, 191 insertions(+), 20 deletions(-) create mode 100644 packages/compiler-cli/src/ngcc/src/analysis/ngcc_references_registry.ts create mode 100644 packages/compiler-cli/src/ngcc/test/analysis/references_registry_spec.ts create mode 100644 packages/compiler-cli/src/ngtsc/annotations/src/references_registry.ts diff --git a/packages/compiler-cli/src/ngcc/src/analysis/decoration_analyzer.ts b/packages/compiler-cli/src/ngcc/src/analysis/decoration_analyzer.ts index 75fb58bfc52a..87c0f4174f89 100644 --- a/packages/compiler-cli/src/ngcc/src/analysis/decoration_analyzer.ts +++ b/packages/compiler-cli/src/ngcc/src/analysis/decoration_analyzer.ts @@ -9,9 +9,8 @@ import {ConstantPool} from '@angular/compiler'; import * as fs from 'fs'; import * as ts from 'typescript'; -import {BaseDefDecoratorHandler, ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ResourceLoader, SelectorScopeRegistry} from '../../../ngtsc/annotations'; +import {BaseDefDecoratorHandler, ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ReferencesRegistry, ResourceLoader, SelectorScopeRegistry} from '../../../ngtsc/annotations'; import {CompileResult, DecoratorHandler} from '../../../ngtsc/transform'; - import {DecoratedClass} from '../host/decorated_class'; import {NgccReflectionHost} from '../host/ngcc_host'; import {isDefined} from '../utils'; @@ -63,13 +62,15 @@ export class DecorationAnalyzer { this.rootDirs), new DirectiveDecoratorHandler(this.typeChecker, this.host, this.scopeRegistry, this.isCore), new InjectableDecoratorHandler(this.host, this.isCore), - new NgModuleDecoratorHandler(this.typeChecker, this.host, this.scopeRegistry, this.isCore), + new NgModuleDecoratorHandler( + this.typeChecker, this.host, this.scopeRegistry, this.referencesRegistry, this.isCore), new PipeDecoratorHandler(this.typeChecker, this.host, this.scopeRegistry, this.isCore), ]; constructor( private typeChecker: ts.TypeChecker, private host: NgccReflectionHost, - private rootDirs: string[], private isCore: boolean) {} + private referencesRegistry: ReferencesRegistry, private rootDirs: string[], + private isCore: boolean) {} /** * Analyze a program to find all the decorated files should be transformed. diff --git a/packages/compiler-cli/src/ngcc/src/analysis/ngcc_references_registry.ts b/packages/compiler-cli/src/ngcc/src/analysis/ngcc_references_registry.ts new file mode 100644 index 000000000000..f4779456b605 --- /dev/null +++ b/packages/compiler-cli/src/ngcc/src/analysis/ngcc_references_registry.ts @@ -0,0 +1,49 @@ +/** + * @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'; +import {ReferencesRegistry} from '../../../ngtsc/annotations'; +import {Declaration, ReflectionHost} from '../../../ngtsc/host'; +import {Reference, ResolvedReference} from '../../../ngtsc/metadata'; +import {hasNameIdentifier} from '../utils'; + +/** + * This is a place for DecoratorHandlers to register references that they + * find in their analysis of the code. + * + * This registry is used to ensure that these references are publicly exported + * from libraries that are compiled by ngcc. + */ +export class NgccReferencesRegistry implements ReferencesRegistry { + private map = new Map(); + + constructor(private host: ReflectionHost) {} + + /** + * Register one or more references in the registry. + * Only `ResolveReference` references are stored. Other types are ignored. + * @param references A collection of references to register. + */ + add(...references: Reference[]): void { + references.forEach(ref => { + // Only store resolved references. We are not interested in literals. + if (ref instanceof ResolvedReference && hasNameIdentifier(ref.node)) { + const declaration = this.host.getDeclarationOfIdentifier(ref.node.name); + if (declaration && hasNameIdentifier(declaration.node)) { + this.map.set(declaration.node.name, declaration); + } + } + }); + } + + /** + * Create and return a mapping for the registered resolved references. + * @returns A map of reference identifiers to reference declarations. + */ + getDeclarationMap(): Map { return this.map; } +} diff --git a/packages/compiler-cli/src/ngcc/src/packages/entry_point.ts b/packages/compiler-cli/src/ngcc/src/packages/entry_point.ts index 74c8091f12b3..d2fe854c0294 100644 --- a/packages/compiler-cli/src/ngcc/src/packages/entry_point.ts +++ b/packages/compiler-cli/src/ngcc/src/packages/entry_point.ts @@ -62,7 +62,7 @@ interface EntryPointPackageJson { * @param packageJsonPath the absolute path to the package.json file. * @returns JSON from the package.json file if it is valid, `null` otherwise. */ -function loadEntryPointPackage(packageJsonPath: string): {[key: string]: any}|null { +function loadEntryPointPackage(packageJsonPath: string): EntryPointPackageJson|null { try { return JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); } catch (e) { @@ -95,7 +95,7 @@ export function getEntryPointInfo(packagePath: string, entryPointPath: string): name, module: modulePath, types, - typings = types, // synonymous + typings = types, // synonymous es2015, fesm2015 = es2015, // synonymous fesm5 = modulePath, // synonymous @@ -109,7 +109,8 @@ export function getEntryPointInfo(packagePath: string, entryPointPath: string): } // Also there must exist a `metadata.json` file next to the typings entry-point. - const metadataPath = path.resolve(entryPointPath, typings.replace(/\.d\.ts$/, '') + '.metadata.json'); + const metadataPath = + path.resolve(entryPointPath, typings.replace(/\.d\.ts$/, '') + '.metadata.json'); if (!fs.existsSync(metadataPath)) { return null; } diff --git a/packages/compiler-cli/src/ngcc/src/packages/transformer.ts b/packages/compiler-cli/src/ngcc/src/packages/transformer.ts index 0b13e8a83201..8bc909203434 100644 --- a/packages/compiler-cli/src/ngcc/src/packages/transformer.ts +++ b/packages/compiler-cli/src/ngcc/src/packages/transformer.ts @@ -11,6 +11,7 @@ import {mkdir, mv} from 'shelljs'; import * as ts from 'typescript'; import {DecorationAnalyzer} from '../analysis/decoration_analyzer'; +import {NgccReferencesRegistry} from '../analysis/ngcc_references_registry'; import {SwitchMarkerAnalyzer} from '../analysis/switch_marker_analyzer'; import {Esm2015ReflectionHost} from '../host/esm2015_host'; import {Esm5ReflectionHost} from '../host/esm5_host'; @@ -156,8 +157,10 @@ export class Transformer { analyzeProgram( program: ts.Program, reflectionHost: NgccReflectionHost, rootDirs: string[], isCore: boolean) { + const typeChecker = bundle.program.getTypeChecker(); + const referencesRegistry = new NgccReferencesRegistry(reflectionHost); const decorationAnalyzer = - new DecorationAnalyzer(program.getTypeChecker(), reflectionHost, rootDirs, isCore); + new DecorationAnalyzer(typeChecker, reflectionHost, referencesRegistry, rootDirs, isCore); const switchMarkerAnalyzer = new SwitchMarkerAnalyzer(reflectionHost); return { decorationAnalyses: decorationAnalyzer.analyzeProgram(program), diff --git a/packages/compiler-cli/src/ngcc/src/utils.ts b/packages/compiler-cli/src/ngcc/src/utils.ts index 94682bd8b6c4..ceef984d94c0 100644 --- a/packages/compiler-cli/src/ngcc/src/utils.ts +++ b/packages/compiler-cli/src/ngcc/src/utils.ts @@ -40,3 +40,13 @@ export function findAll(node: ts.Node, test: (node: ts.Node) => node is ts.No } } } + +/** + * Does the given declaration have a name which is an identifier? + * @param declaration The declaration to test. + * @returns true if the declaration has an identifer for a name. + */ +export function hasNameIdentifier(declaration: ts.Declaration): declaration is ts.Declaration& + {name: ts.Identifier} { + return ts.isIdentifier((declaration as any).name); +} diff --git a/packages/compiler-cli/src/ngcc/test/analysis/decoration_analyzer_spec.ts b/packages/compiler-cli/src/ngcc/test/analysis/decoration_analyzer_spec.ts index 2d435ca63891..89868d3b6283 100644 --- a/packages/compiler-cli/src/ngcc/test/analysis/decoration_analyzer_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/analysis/decoration_analyzer_spec.ts @@ -10,6 +10,7 @@ import * as ts from 'typescript'; import {Decorator} from '../../../ngtsc/host'; import {DecoratorHandler} from '../../../ngtsc/transform'; import {DecorationAnalyses, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; +import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; import {makeProgram} from '../helpers/utils'; @@ -84,9 +85,10 @@ describe('DecorationAnalyzer', () => { beforeEach(() => { program = makeProgram(TEST_PROGRAM); - const analyzer = new DecorationAnalyzer( - program.getTypeChecker(), new Esm2015ReflectionHost(false, program.getTypeChecker()), - [''], false); + const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); + const referencesRegistry = new NgccReferencesRegistry(host); + const analyzer = + new DecorationAnalyzer(program.getTypeChecker(), host, referencesRegistry, [''], false); testHandler = createTestHandler(); analyzer.handlers = [testHandler]; result = analyzer.analyzeProgram(program); @@ -126,9 +128,10 @@ describe('DecorationAnalyzer', () => { it('should analyze an internally imported component, which is not publicly exported from the entry-point', () => { const program = makeProgram(...INTERNAL_COMPONENT_PROGRAM); + const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); + const referencesRegistry = new NgccReferencesRegistry(host); const analyzer = new DecorationAnalyzer( - program.getTypeChecker(), new Esm2015ReflectionHost(false, program.getTypeChecker()), - [''], false); + program.getTypeChecker(), host, referencesRegistry, [''], false); const testHandler = createTestHandler(); analyzer.handlers = [testHandler]; const result = analyzer.analyzeProgram(program); @@ -142,9 +145,10 @@ describe('DecorationAnalyzer', () => { it('should analyze an internally defined component, which is not exported at all', () => { const program = makeProgram(...INTERNAL_COMPONENT_PROGRAM); - const analyzer = new DecorationAnalyzer( - program.getTypeChecker(), new Esm2015ReflectionHost(false, program.getTypeChecker()), - [''], false); + const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); + const referencesRegistry = new NgccReferencesRegistry(host); + const analyzer = + new DecorationAnalyzer(program.getTypeChecker(), host, referencesRegistry, [''], false); const testHandler = createTestHandler(); analyzer.handlers = [testHandler]; const result = analyzer.analyzeProgram(program); diff --git a/packages/compiler-cli/src/ngcc/test/analysis/references_registry_spec.ts b/packages/compiler-cli/src/ngcc/test/analysis/references_registry_spec.ts new file mode 100644 index 000000000000..b2f9ce27aaea --- /dev/null +++ b/packages/compiler-cli/src/ngcc/test/analysis/references_registry_spec.ts @@ -0,0 +1,52 @@ +/** + * @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'; + +import {Reference, TypeScriptReflectionHost, staticallyResolve} from '../../../ngtsc/metadata'; +import {getDeclaration, makeProgram} from '../../../ngtsc/testing/in_memory_typescript'; +import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; + +describe('NgccReferencesRegistry', () => { + it('should return a mapping from resolved reference identifiers to their declarations', () => { + const {program} = makeProgram([{ + name: 'index.ts', + contents: ` + export class SomeClass {} + export function someFunction() {} + export const someVariable = 42; + + export const testArray = [SomeClass, someFunction, someVariable]; + ` + }]); + + const checker = program.getTypeChecker(); + + const testArrayDeclaration = + getDeclaration(program, 'index.ts', 'testArray', ts.isVariableDeclaration); + const someClassDecl = getDeclaration(program, 'index.ts', 'SomeClass', ts.isClassDeclaration); + const someFunctionDecl = + getDeclaration(program, 'index.ts', 'someFunction', ts.isFunctionDeclaration); + const someVariableDecl = + getDeclaration(program, 'index.ts', 'someVariable', ts.isVariableDeclaration); + const testArrayExpression = testArrayDeclaration.initializer !; + + const host = new TypeScriptReflectionHost(checker); + const registry = new NgccReferencesRegistry(host); + + const references = + staticallyResolve(testArrayExpression, host, checker) as Reference[]; + registry.add(...references); + + const map = registry.getDeclarationMap(); + expect(map.size).toEqual(2); + expect(map.get(someClassDecl.name !) !.node).toBe(someClassDecl); + expect(map.get(someFunctionDecl.name !) !.node).toBe(someFunctionDecl); + expect(map.has(someVariableDecl.name as ts.Identifier)).toBe(false); + }); +}); 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 e63fa81f8931..56806524ae93 100644 --- a/packages/compiler-cli/src/ngcc/test/rendering/renderer_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/rendering/renderer_spec.ts @@ -12,6 +12,7 @@ import MagicString from 'magic-string'; import {fromObject, generateMapFileComment} from 'convert-source-map'; import {makeProgram} from '../helpers/utils'; import {CompiledClass, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; +import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer'; import {BundleInfo, createBundleInfo} from '../../src/packages/bundle'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; @@ -46,8 +47,10 @@ function createTestRenderer( options.rewriteCoreImportsTo ? program.getSourceFile(options.rewriteCoreImportsTo) ! : null; const bundle = createBundleInfo(options.isCore || false, rewriteCoreImportsTo, null); const host = new Esm2015ReflectionHost(bundle.isCore, program.getTypeChecker()); + const referencesRegistry = new NgccReferencesRegistry(host); const decorationAnalyses = - new DecorationAnalyzer(program.getTypeChecker(), host, [''], bundle.isCore) + new DecorationAnalyzer( + program.getTypeChecker(), host, referencesRegistry, [''], bundle.isCore) .analyzeProgram(program); const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(program); const renderer = new TestRenderer(host, bundle); diff --git a/packages/compiler-cli/src/ngtsc/annotations/index.ts b/packages/compiler-cli/src/ngtsc/annotations/index.ts index 8902f606e895..ac736ce26450 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/index.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/index.ts @@ -15,4 +15,5 @@ export {DirectiveDecoratorHandler} from './src/directive'; export {InjectableDecoratorHandler} from './src/injectable'; export {NgModuleDecoratorHandler} from './src/ng_module'; export {PipeDecoratorHandler} from './src/pipe'; +export {NoopReferencesRegistry, ReferencesRegistry} from './src/references_registry'; export {CompilationScope, SelectorScopeRegistry} from './src/selector_scope'; diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts index 23bd83bf31f0..5544ddeca827 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts @@ -15,6 +15,7 @@ import {Reference, ResolvedReference, ResolvedValue, reflectObjectLiteral, stati import {AnalysisOutput, CompileResult, DecoratorHandler} from '../../transform'; import {generateSetClassMetadataCall} from './metadata'; +import {ReferencesRegistry} from './references_registry'; import {SelectorScopeRegistry} from './selector_scope'; import {getConstructorDependencies, isAngularCore, toR3Reference, unwrapExpression} from './util'; @@ -32,7 +33,8 @@ export interface NgModuleAnalysis { export class NgModuleDecoratorHandler implements DecoratorHandler { constructor( private checker: ts.TypeChecker, private reflector: ReflectionHost, - private scopeRegistry: SelectorScopeRegistry, private isCore: boolean) {} + private scopeRegistry: SelectorScopeRegistry, private referencesRegistry: ReferencesRegistry, + private isCore: boolean) {} detect(node: ts.Declaration, decorators: Decorator[]|null): Decorator|undefined { if (!decorators) { @@ -72,6 +74,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler[] = []; if (ngModule.has('imports')) { @@ -80,6 +83,7 @@ export class NgModuleDecoratorHandler implements DecoratorHandler this._extractModuleFromModuleWithProvidersFn(ref.node)); imports = this.resolveTypeList(expr, importsMeta, 'imports'); + this.referencesRegistry.add(...imports); } let exports: Reference[] = []; if (ngModule.has('exports')) { @@ -88,12 +92,14 @@ export class NgModuleDecoratorHandler implements DecoratorHandler this._extractModuleFromModuleWithProvidersFn(ref.node)); exports = this.resolveTypeList(expr, exportsMeta, 'exports'); + this.referencesRegistry.add(...exports); } let bootstrap: Reference[] = []; if (ngModule.has('bootstrap')) { const expr = ngModule.get('bootstrap') !; const bootstrapMeta = staticallyResolve(expr, this.reflector, this.checker); bootstrap = this.resolveTypeList(expr, bootstrapMeta, 'bootstrap'); + this.referencesRegistry.add(...bootstrap); } // Register this module's information with the SelectorScopeRegistry. This ensures that during diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/references_registry.ts b/packages/compiler-cli/src/ngtsc/annotations/src/references_registry.ts new file mode 100644 index 000000000000..a45ce6ad354e --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/annotations/src/references_registry.ts @@ -0,0 +1,39 @@ +/** + * @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'; +import {Declaration} from '../../host'; +import {Reference} from '../../metadata'; + +/** + * Implement this interface if you want DecoratorHandlers to register + * references that they find in their analysis of the code. + */ +export interface ReferencesRegistry { + /** + * Register one or more references in the registry. + * Only `ResolveReference` references are stored. Other types are ignored. + * @param references A collection of references to register. + */ + add(...references: Reference[]): void; + /** + * Create and return a mapping for the registered resolved references. + * @returns A map of reference identifiers to reference declarations. + */ + getDeclarationMap(): Map; +} + +/** + * This registry does nothing, since ngtsc does not currently need + * this functionality. + * The ngcc tool implements a working version for its purposes. + */ +export class NoopReferencesRegistry implements ReferencesRegistry { + add(...references: Reference[]): void {} + getDeclarationMap(): Map { return new Map(); } +} \ No newline at end of file diff --git a/packages/compiler-cli/src/ngtsc/program.ts b/packages/compiler-cli/src/ngtsc/program.ts index c925718dca2d..943566dcb444 100644 --- a/packages/compiler-cli/src/ngtsc/program.ts +++ b/packages/compiler-cli/src/ngtsc/program.ts @@ -13,7 +13,7 @@ import * as ts from 'typescript'; import * as api from '../transformers/api'; import {nocollapseHack} from '../transformers/nocollapse_hack'; -import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ResourceLoader, SelectorScopeRegistry} from './annotations'; +import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, NoopReferencesRegistry, PipeDecoratorHandler, ResourceLoader, SelectorScopeRegistry} from './annotations'; import {BaseDefDecoratorHandler} from './annotations/src/base_def'; import {TypeScriptReflectionHost} from './metadata'; import {FileResourceLoader, HostResourceLoader} from './resource_loader'; @@ -214,6 +214,7 @@ export class NgtscProgram implements api.Program { private makeCompilation(): IvyCompilation { const checker = this.tsProgram.getTypeChecker(); const scopeRegistry = new SelectorScopeRegistry(checker, this.reflector); + const referencesRegistry = new NoopReferencesRegistry(); // Set up the IvyCompilation, which manages state for the Ivy transformer. const handlers = [ @@ -222,7 +223,8 @@ export class NgtscProgram implements api.Program { checker, this.reflector, scopeRegistry, this.isCore, this.resourceLoader, this.rootDirs), new DirectiveDecoratorHandler(checker, this.reflector, scopeRegistry, this.isCore), new InjectableDecoratorHandler(this.reflector, this.isCore), - new NgModuleDecoratorHandler(checker, this.reflector, scopeRegistry, this.isCore), + new NgModuleDecoratorHandler( + checker, this.reflector, scopeRegistry, referencesRegistry, this.isCore), new PipeDecoratorHandler(checker, this.reflector, scopeRegistry, this.isCore), ]; From 1eaa2d819df395355f2fa5fbc33bed27246e1f4f Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sun, 25 Nov 2018 14:13:59 +0000 Subject: [PATCH 05/13] style(ivy): ngcc - fix typos --- packages/compiler-cli/src/ngcc/src/host/esm2015_host.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/compiler-cli/src/ngcc/src/host/esm2015_host.ts b/packages/compiler-cli/src/ngcc/src/host/esm2015_host.ts index 4ef22cb3b3ad..4e05b3b0746f 100644 --- a/packages/compiler-cli/src/ngcc/src/host/esm2015_host.ts +++ b/packages/compiler-cli/src/ngcc/src/host/esm2015_host.ts @@ -848,7 +848,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N } /** - * Get the parmeter type and decorators for a class where the information is stored on + * Get the parameter type and decorators for a class where the information is stored on * in calls to `__decorate` helpers. * * Reflect over the helpers to find the decorators and types about each of @@ -973,7 +973,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N * Test whether a decorator was imported from `@angular/core`. * * Is the decorator: - * * externally imported from `@angulare/core`? + * * externally imported from `@angular/core`? * * the current hosted program is actually `@angular/core` and * - relatively internally imported; or * - not imported, from the current file. From 171dad22cdad8482ff9f01fd64bea65045f7d87a Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sun, 25 Nov 2018 14:14:32 +0000 Subject: [PATCH 06/13] refactor(ivy): ngcc - remove unused code --- .../src/ngcc/test/host/esm5_host_import_helper_spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/compiler-cli/src/ngcc/test/host/esm5_host_import_helper_spec.ts b/packages/compiler-cli/src/ngcc/test/host/esm5_host_import_helper_spec.ts index 604425af9b46..60aca577a37c 100644 --- a/packages/compiler-cli/src/ngcc/test/host/esm5_host_import_helper_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/host/esm5_host_import_helper_spec.ts @@ -136,7 +136,6 @@ describe('Esm5ReflectionHost [import helper style]', () => { }); it('should use `getImportOfIdentifier()` to retrieve import info', () => { - const mockImportInfo = {} as Import; const spy = spyOn(Esm5ReflectionHost.prototype, 'getImportOfIdentifier') .and.callFake( (identifier: ts.Identifier) => identifier.getText() === 'Directive' ? From 289684faf9ca5e430b513fd6ab37bb736cd40a4f Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sun, 25 Nov 2018 14:15:37 +0000 Subject: [PATCH 07/13] refactor(ivy): ngcc - subclasses do not need to declare `protected` variables These variables are already declared as protected in the super class, and so it is redundant to do it again in the subclasses. --- packages/compiler-cli/src/ngcc/src/rendering/esm5_renderer.ts | 4 ++-- packages/compiler-cli/src/ngcc/src/rendering/esm_renderer.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 0cdf9b2bea8c..44e6ae2c4960 100644 --- a/packages/compiler-cli/src/ngcc/src/rendering/esm5_renderer.ts +++ b/packages/compiler-cli/src/ngcc/src/rendering/esm5_renderer.ts @@ -14,8 +14,8 @@ import {EsmRenderer} from './esm_renderer'; export class Esm5Renderer extends EsmRenderer { constructor( - protected host: NgccReflectionHost, protected bundle: BundleInfo, - protected sourcePath: string, protected targetPath: string, transformDts: boolean) { + host: NgccReflectionHost, bundle: BundleInfo, sourcePath: string, 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 657665da212f..fb9c527c8c3d 100644 --- a/packages/compiler-cli/src/ngcc/src/rendering/esm_renderer.ts +++ b/packages/compiler-cli/src/ngcc/src/rendering/esm_renderer.ts @@ -14,8 +14,8 @@ import {Renderer} from './renderer'; export class EsmRenderer extends Renderer { constructor( - protected host: NgccReflectionHost, protected bundle: BundleInfo, - protected sourcePath: string, protected targetPath: string, transformDts: boolean) { + host: NgccReflectionHost, bundle: BundleInfo, sourcePath: string, targetPath: string, + transformDts: boolean) { super(host, bundle, sourcePath, targetPath, transformDts); } From fc12c0a88d5a2752efa20696fd04ef00692478dd Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sun, 25 Nov 2018 21:40:25 +0000 Subject: [PATCH 08/13] refactor(ivy): ngcc - encapsulate variables into "bundles" There are a number of variables that need to be passed around the program, in particular to the renderers, which benefit from being stored in well defined objects. The new `EntryPointBundle` structure is a specific format of an entry-point and contains the compiled `BundleProgram` objects for the source and typings, if appropriate. This change helps with future refactoring, where we may need to add new properties to this object. It allows us to maintain more stable APIs between the constituent parts of ngcc, rather than passing lots of primitive values around throughout the program. --- .../src/ngcc/src/host/esm2015_host.ts | 11 +- packages/compiler-cli/src/ngcc/src/main.ts | 20 +- .../src/ngcc/src/packages/bundle.ts | 31 --- .../src/ngcc/src/packages/bundle_program.ts | 72 +++++ .../ngcc/src/packages/entry_point_bundle.ts | 58 +++++ .../src/ngcc/src/packages/transformer.ts | 160 +++--------- .../src/ngcc/src/rendering/esm5_renderer.ts | 8 +- .../src/ngcc/src/rendering/esm_renderer.ts | 10 +- .../src/ngcc/src/rendering/renderer.ts | 43 +-- .../test/analysis/decoration_analyzer_spec.ts | 8 +- .../analysis/switch_marker_analyzer_spec.ts | 4 +- .../src/ngcc/test/helpers/utils.ts | 42 ++- .../host/esm2015_host_import_helper_spec.ts | 34 +-- .../src/ngcc/test/host/esm2015_host_spec.ts | 162 ++++++------ .../test/host/esm5_host_import_helper_spec.ts | 34 +-- .../src/ngcc/test/host/esm5_host_spec.ts | 100 +++---- .../test/rendering/esm2015_renderer_spec.ts | 33 ++- .../ngcc/test/rendering/esm5_renderer_spec.ts | 34 ++- .../src/ngcc/test/rendering/renderer_spec.ts | 245 ++++++++++-------- 19 files changed, 609 insertions(+), 500 deletions(-) delete mode 100644 packages/compiler-cli/src/ngcc/src/packages/bundle.ts create mode 100644 packages/compiler-cli/src/ngcc/src/packages/bundle_program.ts create mode 100644 packages/compiler-cli/src/ngcc/src/packages/entry_point_bundle.ts diff --git a/packages/compiler-cli/src/ngcc/src/host/esm2015_host.ts b/packages/compiler-cli/src/ngcc/src/host/esm2015_host.ts index 4e05b3b0746f..a4d12aa42042 100644 --- a/packages/compiler-cli/src/ngcc/src/host/esm2015_host.ts +++ b/packages/compiler-cli/src/ngcc/src/host/esm2015_host.ts @@ -10,7 +10,8 @@ import * as ts from 'typescript'; import {ClassMember, ClassMemberKind, CtorParameter, Decorator, Import} from '../../../ngtsc/host'; import {TypeScriptReflectionHost, reflectObjectLiteral} from '../../../ngtsc/metadata'; -import {findAll, getNameText, getOriginalSymbol, isDefined} from '../utils'; +import {BundleProgram} from '../packages/bundle_program'; +import {findAll, getNameText, isDefined} from '../utils'; import {DecoratedClass} from './decorated_class'; import {NgccReflectionHost, PRE_R3_MARKER, SwitchableVariableDeclaration, isSwitchableVariableDeclaration} from './ngcc_host'; @@ -49,13 +50,9 @@ export const CONSTRUCTOR_PARAMS = 'ctorParameters' as ts.__String; */ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements NgccReflectionHost { protected dtsClassMap: Map|null; - constructor( - protected isCore: boolean, checker: ts.TypeChecker, dtsRootFileName?: string, - dtsProgram?: ts.Program|null) { + constructor(protected isCore: boolean, checker: ts.TypeChecker, dts?: BundleProgram|null) { super(checker); - this.dtsClassMap = (dtsRootFileName && dtsProgram) ? - this.computeDtsClassMap(dtsRootFileName, dtsProgram) : - null; + this.dtsClassMap = dts && this.computeDtsClassMap(dts.path, dts.program) || null; } /** diff --git a/packages/compiler-cli/src/ngcc/src/main.ts b/packages/compiler-cli/src/ngcc/src/main.ts index 35f003c2f5a0..924fc42232d3 100644 --- a/packages/compiler-cli/src/ngcc/src/main.ts +++ b/packages/compiler-cli/src/ngcc/src/main.ts @@ -11,6 +11,7 @@ import * as yargs from 'yargs'; import {DependencyHost} from './packages/dependency_host'; import {DependencyResolver} from './packages/dependency_resolver'; import {EntryPointFormat} from './packages/entry_point'; +import {makeEntryPointBundle} from './packages/entry_point_bundle'; import {EntryPointFinder} from './packages/entry_point_finder'; import {Transformer} from './packages/transformer'; @@ -48,12 +49,25 @@ export function mainNgcc(args: string[]): number { try { const {entryPoints} = finder.findEntryPoints(sourcePath); entryPoints.forEach(entryPoint => { + + // Are we compiling the Angular core? + const isCore = entryPoint.name === '@angular/core'; + // We transform the d.ts typings files while transforming one of the formats. // This variable decides with which of the available formats to do this transform. // It is marginally faster to process via the flat file if available. - const dtsTranformFormat: EntryPointFormat = entryPoint.fesm2015 ? 'fesm2015' : 'esm2015'; - formats.forEach( - format => transformer.transform(entryPoint, format, format === dtsTranformFormat)); + const dtsTransformFormat: EntryPointFormat = entryPoint.fesm2015 ? 'fesm2015' : 'esm2015'; + + formats.forEach(format => { + const bundle = + makeEntryPointBundle(entryPoint, isCore, format, format === dtsTransformFormat); + if (bundle === null) { + console.warn( + `Skipping ${entryPoint.name} : ${format} (no entry point file for this format).`); + } else { + transformer.transform(entryPoint, isCore, bundle); + } + }); }); } catch (e) { console.error(e.stack); diff --git a/packages/compiler-cli/src/ngcc/src/packages/bundle.ts b/packages/compiler-cli/src/ngcc/src/packages/bundle.ts deleted file mode 100644 index 872e8ad213f1..000000000000 --- a/packages/compiler-cli/src/ngcc/src/packages/bundle.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @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/bundle_program.ts b/packages/compiler-cli/src/ngcc/src/packages/bundle_program.ts new file mode 100644 index 000000000000..9bbcd8abb88c --- /dev/null +++ b/packages/compiler-cli/src/ngcc/src/packages/bundle_program.ts @@ -0,0 +1,72 @@ +/** + * @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 {dirname, resolve} from 'canonical-path'; +import {existsSync, lstatSync, readdirSync} from 'fs'; +import * as ts from 'typescript'; + +/** +* An entry point bundle contains one or two programs, e.g. `src` and `dts`, +* that are compiled via TypeScript. +* +* To aid with processing the program, this interface exposes the program itself, +* as well as path and TS file of the entry-point to the program and the r3Symbols +* file, if appropriate. +*/ +export interface BundleProgram { + program: ts.Program; + path: string; + file: ts.SourceFile; + r3SymbolsPath: string|null; + r3SymbolsFile: ts.SourceFile|null; +} + +/** + * Create a bundle program. + */ +export function makeBundleProgram( + isCore: boolean, path: string, r3FileName: string, options: ts.CompilerOptions, + host: ts.CompilerHost): BundleProgram { + const r3SymbolsPath = isCore ? findR3SymbolsPath(dirname(path), r3FileName) : null; + const rootPaths = r3SymbolsPath ? [path, r3SymbolsPath] : [path]; + const program = ts.createProgram(rootPaths, options, host); + const file = program.getSourceFile(path) !; + const r3SymbolsFile = r3SymbolsPath && program.getSourceFile(r3SymbolsPath) || null; + + return {program, path, file, r3SymbolsPath, r3SymbolsFile}; +} + +/** + * Search the given directory hierarchy to find the path to the `r3_symbols` file. + */ +export function findR3SymbolsPath(directory: string, filename: string): string|null { + const r3SymbolsFilePath = resolve(directory, filename); + if (existsSync(r3SymbolsFilePath)) { + return r3SymbolsFilePath; + } + + const subDirectories = + readdirSync(directory) + // Not interested in hidden files + .filter(p => !p.startsWith('.')) + // Ignore node_modules + .filter(p => p !== 'node_modules') + // Only interested in directories (and only those that are not symlinks) + .filter(p => { + const stat = lstatSync(resolve(directory, p)); + return stat.isDirectory() && !stat.isSymbolicLink(); + }); + + for (const subDirectory of subDirectories) { + const r3SymbolsFilePath = findR3SymbolsPath(resolve(directory, subDirectory, ), filename); + if (r3SymbolsFilePath) { + return r3SymbolsFilePath; + } + } + + return null; +} diff --git a/packages/compiler-cli/src/ngcc/src/packages/entry_point_bundle.ts b/packages/compiler-cli/src/ngcc/src/packages/entry_point_bundle.ts new file mode 100644 index 000000000000..3dbbaee26242 --- /dev/null +++ b/packages/compiler-cli/src/ngcc/src/packages/entry_point_bundle.ts @@ -0,0 +1,58 @@ +/** + * @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'; + +import {BundleProgram, makeBundleProgram} from './bundle_program'; +import {EntryPoint, EntryPointFormat} from './entry_point'; + + +/** + * A bundle of files and paths (and TS programs) that correspond to a particular + * format of a package entry-point. + */ +export interface EntryPointBundle { + format: EntryPointFormat; + isFlat: boolean; + rootDirs: string[]; + src: BundleProgram; + dts: BundleProgram|null; +} + +/** + * Get an object that describes a formatted bundle for an entry-point. + * @param entryPoint The entry-point that contains the bundle. + * @param format The format of the bundle. + * @param transformDts True if processing this bundle should also process its `.d.ts` files. + */ +export function makeEntryPointBundle( + entryPoint: EntryPoint, isCore: boolean, format: EntryPointFormat, + transformDts: boolean): EntryPointBundle|null { + // Bail out if the entry-point does not have this format. + const path = entryPoint[format]; + if (!path) { + return null; + } + + // Create the TS program and necessary helpers. + const options: ts.CompilerOptions = { + allowJs: true, + maxNodeModuleJsDepth: Infinity, + rootDir: entryPoint.path, + }; + const host = ts.createCompilerHost(options); + const rootDirs = [entryPoint.path]; + + // Create the bundle programs, as necessary. + const src = makeBundleProgram(isCore, path, 'r3_symbols.js', options, host); + const dts = transformDts ? + makeBundleProgram(isCore, entryPoint.typings, 'r3_symbols.d.ts', options, host) : + null; + const isFlat = src.r3SymbolsFile === null; + + return {format, rootDirs, isFlat, src, dts}; +} diff --git a/packages/compiler-cli/src/ngcc/src/packages/transformer.ts b/packages/compiler-cli/src/ngcc/src/packages/transformer.ts index 8bc909203434..3de2e644fb46 100644 --- a/packages/compiler-cli/src/ngcc/src/packages/transformer.ts +++ b/packages/compiler-cli/src/ngcc/src/packages/transformer.ts @@ -5,10 +5,9 @@ * 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 {dirname, resolve} from 'canonical-path'; -import {existsSync, lstatSync, readdirSync, writeFileSync} from 'fs'; +import {dirname} from 'canonical-path'; +import {existsSync, writeFileSync} from 'fs'; import {mkdir, mv} from 'shelljs'; -import * as ts from 'typescript'; import {DecorationAnalyzer} from '../analysis/decoration_analyzer'; import {NgccReferencesRegistry} from '../analysis/ngcc_references_registry'; @@ -21,8 +20,8 @@ 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'; +import {EntryPoint} from './entry_point'; +import {EntryPointBundle} from './entry_point_bundle'; /** * A Package is stored in a directory on disk and that directory can contain one or more package @@ -32,9 +31,11 @@ import {EntryPoint, EntryPointFormat} from './entry_point'; * parsed to identify the decorated exported classes that need to be analyzed and compiled by one or * more `DecoratorHandler` objects. * - * Each entry point to a package is identified by a `SourceFile` that can be parsed and analyzed to + * Each entry point to a package is identified by a `package.json` which contains properties that + * indicate what formatted bundles are accessible via this end-point. + * + * Each bundle is identified by a root `SourceFile` that can be parsed and analyzed to * identify classes that need to be transformed; and then finally rendered and written to disk. - * The actual file which needs to be transformed depends upon the package format. * * Along with the source files, the corresponding source maps (either inline or external) and * `.d.ts` files are transformed accordingly. @@ -46,126 +47,71 @@ import {EntryPoint, EntryPointFormat} from './entry_point'; export class Transformer { constructor(private sourcePath: string, private targetPath: string) {} - transform(entryPoint: EntryPoint, format: EntryPointFormat, transformDts: boolean): void { - if (checkMarkerFile(entryPoint, format)) { - console.warn(`Skipping ${entryPoint.name} : ${format} (already built).`); + /** + * Transform the source (and typings) files of a bundle. + * @param bundle the bundle to transform. + */ + transform(entryPoint: EntryPoint, isCore: boolean, bundle: EntryPointBundle): void { + if (checkMarkerFile(entryPoint, bundle.format)) { + console.warn(`Skipping ${entryPoint.name} : ${bundle.format} (already built).`); return; } - const entryPointFilePath = entryPoint[format]; - if (!entryPointFilePath) { - console.warn( - `Skipping ${entryPoint.name} : ${format} (no entry point file for this format).`); - return; - } - - console.warn(`Compiling ${entryPoint.name} - ${format}`); - - const options: ts.CompilerOptions = { - allowJs: true, - maxNodeModuleJsDepth: Infinity, - rootDir: entryPoint.path, - }; + console.warn(`Compiling ${entryPoint.name} - ${bundle.format}`); - // Create the TS program and necessary helpers. - // TODO : create a custom compiler host that reads from .bak files if available. - const host = ts.createCompilerHost(options); - const rootDirs = this.getRootDirs(host, options); - const isCore = entryPoint.name === '@angular/core'; - const r3SymbolsPath = - isCore ? this.findR3SymbolsPath(dirname(entryPointFilePath), 'r3_symbols.js') : null; - const rootPaths = r3SymbolsPath ? [entryPointFilePath, r3SymbolsPath] : [entryPointFilePath]; - const packageProgram = ts.createProgram(rootPaths, options, host); - 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); + const reflectionHost = this.getHost(isCore, bundle); // Parse and analyze the files. const {decorationAnalyses, switchMarkerAnalyses} = - this.analyzeProgram(packageProgram, reflectionHost, rootDirs, isCore); + this.analyzeProgram(reflectionHost, isCore, bundle); - console.time(`${entryPoint.name} (rendering)`); // Transform the source files and source maps. - const renderer = this.getRenderer(format, packageProgram, reflectionHost, bundle, transformDts); - const renderedFiles = - renderer.renderProgram(packageProgram, decorationAnalyses, switchMarkerAnalyses); - console.timeEnd(`${entryPoint.name} (rendering)`); + const renderer = this.getRenderer(reflectionHost, isCore, bundle); + const renderedFiles = renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); // Write out all the transformed files. renderedFiles.forEach(file => this.writeFile(file)); // Write the built-with-ngcc marker - writeMarkerFile(entryPoint, format); + writeMarkerFile(entryPoint, bundle.format); } - getRootDirs(host: ts.CompilerHost, options: ts.CompilerOptions) { - if (options.rootDirs !== undefined) { - return options.rootDirs; - } else if (options.rootDir !== undefined) { - return [options.rootDir]; - } else { - return [host.getCurrentDirectory()]; - } - } - - getHost( - isCore: boolean, format: string, program: ts.Program, dtsFilePath: string, - dtsProgram: ts.Program|null): NgccReflectionHost { - switch (format) { + getHost(isCore: boolean, bundle: EntryPointBundle): NgccReflectionHost { + const typeChecker = bundle.src.program.getTypeChecker(); + switch (bundle.format) { case 'esm2015': case 'fesm2015': - return new Esm2015ReflectionHost(isCore, program.getTypeChecker(), dtsFilePath, dtsProgram); + return new Esm2015ReflectionHost(isCore, typeChecker, bundle.dts); case 'esm5': case 'fesm5': - return new Esm5ReflectionHost(isCore, program.getTypeChecker()); + return new Esm5ReflectionHost(isCore, typeChecker); default: - throw new Error(`Relection host for "${format}" not yet implemented.`); + throw new Error(`Reflection host for "${bundle.format}" not yet implemented.`); } } - getRenderer( - format: string, program: ts.Program, host: NgccReflectionHost, bundle: BundleInfo, - transformDts: boolean): Renderer { - switch (format) { + getRenderer(host: NgccReflectionHost, isCore: boolean, bundle: EntryPointBundle): Renderer { + switch (bundle.format) { case 'esm2015': case 'fesm2015': - return new EsmRenderer(host, bundle, this.sourcePath, this.targetPath, transformDts); + return new EsmRenderer(host, isCore, bundle, this.sourcePath, this.targetPath); case 'esm5': case 'fesm5': - return new Esm5Renderer(host, bundle, this.sourcePath, this.targetPath, transformDts); + return new Esm5Renderer(host, isCore, bundle, this.sourcePath, this.targetPath); default: - throw new Error(`Renderer for "${format}" not yet implemented.`); + throw new Error(`Renderer for "${bundle.format}" not yet implemented.`); } } - analyzeProgram( - program: ts.Program, reflectionHost: NgccReflectionHost, rootDirs: string[], - isCore: boolean) { - const typeChecker = bundle.program.getTypeChecker(); + analyzeProgram(reflectionHost: NgccReflectionHost, isCore: boolean, bundle: EntryPointBundle) { + const typeChecker = bundle.src.program.getTypeChecker(); const referencesRegistry = new NgccReferencesRegistry(reflectionHost); - const decorationAnalyzer = - new DecorationAnalyzer(typeChecker, reflectionHost, referencesRegistry, rootDirs, isCore); + const decorationAnalyzer = new DecorationAnalyzer( + typeChecker, reflectionHost, referencesRegistry, bundle.rootDirs, isCore); const switchMarkerAnalyzer = new SwitchMarkerAnalyzer(reflectionHost); - return { - decorationAnalyses: decorationAnalyzer.analyzeProgram(program), - switchMarkerAnalyses: switchMarkerAnalyzer.analyzeProgram(program), - }; + const decorationAnalyses = decorationAnalyzer.analyzeProgram(bundle.src.program); + const switchMarkerAnalyses = switchMarkerAnalyzer.analyzeProgram(bundle.src.program); + return {decorationAnalyses, switchMarkerAnalyses}; } writeFile(file: FileInfo): void { @@ -176,32 +122,4 @@ export class Transformer { } writeFileSync(file.path, file.contents, 'utf8'); } - - findR3SymbolsPath(directory: string, fileName: string): string|null { - const r3SymbolsFilePath = resolve(directory, fileName); - if (existsSync(r3SymbolsFilePath)) { - return r3SymbolsFilePath; - } - - const subDirectories = - readdirSync(directory) - // Not interested in hidden files - .filter(p => !p.startsWith('.')) - // Ignore node_modules - .filter(p => p !== 'node_modules') - // Only interested in directories (and only those that are not symlinks) - .filter(p => { - const stat = lstatSync(resolve(directory, p)); - return stat.isDirectory() && !stat.isSymbolicLink(); - }); - - for (const subDirectory of subDirectories) { - const r3SymbolsFilePath = this.findR3SymbolsPath(resolve(directory, subDirectory), fileName); - if (r3SymbolsFilePath) { - return r3SymbolsFilePath; - } - } - - return null; - } } 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 44e6ae2c4960..21a31996fbe7 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'; +import {EntryPointBundle} from '../packages/entry_point_bundle'; export class Esm5Renderer extends EsmRenderer { constructor( - host: NgccReflectionHost, bundle: BundleInfo, sourcePath: string, targetPath: string, - transformDts: boolean) { - super(host, bundle, sourcePath, targetPath, transformDts); + protected host: NgccReflectionHost, isCore: boolean, bundle: EntryPointBundle, + sourcePath: string, targetPath: string) { + super(host, isCore, bundle, sourcePath, targetPath); } /** 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 fb9c527c8c3d..60482ab43eda 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'; +import {Renderer, stripExtension} from './renderer'; +import {EntryPointBundle} from '../packages/entry_point_bundle'; export class EsmRenderer extends Renderer { constructor( - host: NgccReflectionHost, bundle: BundleInfo, sourcePath: string, targetPath: string, - transformDts: boolean) { - super(host, bundle, sourcePath, targetPath, transformDts); + host: NgccReflectionHost, isCore: boolean, bundle: EntryPointBundle, sourcePath: string, + targetPath: string) { + super(host, isCore, bundle, sourcePath, targetPath); } /** diff --git a/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts b/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts index 7e8c7ee85f7d..4d1552b4523a 100644 --- a/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts +++ b/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts @@ -19,9 +19,9 @@ 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'; +import {EntryPointBundle} from '../packages/entry_point_bundle'; interface SourceMapInfo { source: string; @@ -56,28 +56,33 @@ interface DtsClassInfo { */ export abstract class Renderer { constructor( - protected host: NgccReflectionHost, protected bundle: BundleInfo, - protected sourcePath: string, protected targetPath: string, protected transformDts: boolean) { - } + protected host: NgccReflectionHost, protected isCore: boolean, + protected bundle: EntryPointBundle, protected sourcePath: string, + protected targetPath: string) {} - renderProgram( - program: ts.Program, decorationAnalyses: DecorationAnalyses, - switchMarkerAnalyses: SwitchMarkerAnalyses): FileInfo[] { + renderProgram(decorationAnalyses: DecorationAnalyses, switchMarkerAnalyses: SwitchMarkerAnalyses): + FileInfo[] { const renderedFiles: FileInfo[] = []; // Transform the source files. - program.getSourceFiles().map(sourceFile => { + this.bundle.src.program.getSourceFiles().map(sourceFile => { const compiledFile = decorationAnalyses.get(sourceFile); const switchMarkerAnalysis = switchMarkerAnalyses.get(sourceFile); - if (compiledFile || switchMarkerAnalysis) { + if (compiledFile || switchMarkerAnalysis || sourceFile === this.bundle.src.file) { renderedFiles.push(...this.renderFile(sourceFile, compiledFile, switchMarkerAnalysis)); } }); - if (this.transformDts) { - // Transform the .d.ts files + // Transform the .d.ts files + if (this.bundle.dts) { const dtsFiles = this.getTypingsFilesToRender(decorationAnalyses); + + // If the dts entry-point is not already there (it did not have compiled classes) + // then add it now, to ensure it gets its extra exports rendered. + if (!dtsFiles.has(this.bundle.dts.file)) { + dtsFiles.set(this.bundle.dts.file, []); + } dtsFiles.forEach((classes, file) => renderedFiles.push(...this.renderDtsFile(file, classes))); } @@ -101,8 +106,7 @@ export abstract class Renderer { } if (compiledFile) { - const importManager = - new NgccImportManager(this.bundle.isFlat, this.bundle.isCore, IMPORT_PREFIX); + const importManager = new NgccImportManager(this.bundle.isFlat, this.isCore, IMPORT_PREFIX); const decoratorsToRemove = new Map(); compiledFile.compiledClasses.forEach(clazz => { @@ -118,9 +122,9 @@ export abstract class Renderer { this.addImports( outputText, importManager.getAllImports( - compiledFile.sourceFile.fileName, this.bundle.rewriteCoreImportsTo)); + compiledFile.sourceFile.fileName, this.bundle.src.r3SymbolsFile)); - // TODO: remove contructor param metadata and property decorators (we need info from the + // TODO: remove constructor param metadata and property decorators (we need info from the // handlers to do this) this.removeDecorators(outputText, decoratorsToRemove); } @@ -131,7 +135,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.bundle.isCore, IMPORT_PREFIX); + const importManager = new NgccImportManager(false, this.isCore, IMPORT_PREFIX); dtsClasses.forEach(dtsClass => { const endOfClass = dtsClass.dtsDeclaration.getEnd(); @@ -143,8 +147,7 @@ export abstract class Renderer { }); this.addImports( - outputText, - importManager.getAllImports(dtsFile.fileName, this.bundle.rewriteCoreDtsImportsTo)); + outputText, importManager.getAllImports(dtsFile.fileName, this.bundle.dts !.r3SymbolsFile)); return this.renderSourceAndMap(dtsFile, input, outputText); } @@ -337,6 +340,10 @@ export function renderDefinitions( return definitions; } +export function stripExtension(filePath: string): string { + return filePath.replace(/\.(js|d\.ts$)/, ''); +} + /** * Create an Angular AST statement node that contains the assignment of the * compiled decorator to be applied to the class. diff --git a/packages/compiler-cli/src/ngcc/test/analysis/decoration_analyzer_spec.ts b/packages/compiler-cli/src/ngcc/test/analysis/decoration_analyzer_spec.ts index 89868d3b6283..1978ba02c82f 100644 --- a/packages/compiler-cli/src/ngcc/test/analysis/decoration_analyzer_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/analysis/decoration_analyzer_spec.ts @@ -13,7 +13,7 @@ import {DecorationAnalyses, DecorationAnalyzer} from '../../src/analysis/decorat import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; -import {makeProgram} from '../helpers/utils'; +import {makeTestProgram} from '../helpers/utils'; const TEST_PROGRAM = { name: 'test.js', @@ -84,7 +84,7 @@ describe('DecorationAnalyzer', () => { let result: DecorationAnalyses; beforeEach(() => { - program = makeProgram(TEST_PROGRAM); + program = makeTestProgram(TEST_PROGRAM); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const referencesRegistry = new NgccReferencesRegistry(host); const analyzer = @@ -127,7 +127,7 @@ describe('DecorationAnalyzer', () => { // is not yet solved. it('should analyze an internally imported component, which is not publicly exported from the entry-point', () => { - const program = makeProgram(...INTERNAL_COMPONENT_PROGRAM); + const program = makeTestProgram(...INTERNAL_COMPONENT_PROGRAM); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const referencesRegistry = new NgccReferencesRegistry(host); const analyzer = new DecorationAnalyzer( @@ -144,7 +144,7 @@ describe('DecorationAnalyzer', () => { }); it('should analyze an internally defined component, which is not exported at all', () => { - const program = makeProgram(...INTERNAL_COMPONENT_PROGRAM); + const program = makeTestProgram(...INTERNAL_COMPONENT_PROGRAM); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const referencesRegistry = new NgccReferencesRegistry(host); const analyzer = diff --git a/packages/compiler-cli/src/ngcc/test/analysis/switch_marker_analyzer_spec.ts b/packages/compiler-cli/src/ngcc/test/analysis/switch_marker_analyzer_spec.ts index b97daa07eeb6..ff27fd9d1ca0 100644 --- a/packages/compiler-cli/src/ngcc/test/analysis/switch_marker_analyzer_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/analysis/switch_marker_analyzer_spec.ts @@ -9,7 +9,7 @@ import * as ts from 'typescript'; import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; -import {makeProgram} from '../helpers/utils'; +import {makeTestProgram} from '../helpers/utils'; const TEST_PROGRAM = [ { @@ -46,7 +46,7 @@ const TEST_PROGRAM = [ describe('SwitchMarkerAnalyzer', () => { describe('analyzeProgram()', () => { it('should check for switchable markers in all the files of the program', () => { - const program = makeProgram(...TEST_PROGRAM); + const program = makeTestProgram(...TEST_PROGRAM); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const analyzer = new SwitchMarkerAnalyzer(host); const analysis = analyzer.analyzeProgram(program); diff --git a/packages/compiler-cli/src/ngcc/test/helpers/utils.ts b/packages/compiler-cli/src/ngcc/test/helpers/utils.ts index 705f2e26697d..1d466683ab2e 100644 --- a/packages/compiler-cli/src/ngcc/test/helpers/utils.ts +++ b/packages/compiler-cli/src/ngcc/test/helpers/utils.ts @@ -6,12 +6,48 @@ * found in the LICENSE file at https://angular.io/license */ import * as ts from 'typescript'; -import {makeProgram as _makeProgram} from '../../../ngtsc/testing/in_memory_typescript'; + +import {makeProgram} from '../../../ngtsc/testing/in_memory_typescript'; +import {BundleProgram} from '../../src/packages/bundle_program'; +import {EntryPointFormat} from '../../src/packages/entry_point'; +import {EntryPointBundle} from '../../src/packages/entry_point_bundle'; export {getDeclaration} from '../../../ngtsc/testing/in_memory_typescript'; -export function makeProgram(...files: {name: string, contents: string}[]): ts.Program { - return _makeProgram([getFakeCore(), getFakeTslib(), ...files], {allowJs: true, checkJs: false}) + +/** + * + * @param format The format of the bundle. + * @param files The source files to include in the bundle. + * @param dtsFiles The typings files to include the bundle. + */ +export function makeTestEntryPointBundle( + format: EntryPointFormat, files: {name: string, contents: string, isRoot?: boolean}[], + dtsFiles?: {name: string, contents: string, isRoot?: boolean}[]): EntryPointBundle { + const src = makeTestBundleProgram(files); + const dts = dtsFiles ? makeTestBundleProgram(dtsFiles) : null; + const isFlat = src.r3SymbolsFile === null; + return {format, rootDirs: ['/'], src, dts, isFlat}; +} + +/** + * Create a bundle program for testing. + * @param files The source files of the bundle program. + */ +export function makeTestBundleProgram(files: {name: string, contents: string}[]): BundleProgram { + const program = makeTestProgram(...files); + const path = files[0].name; + const file = program.getSourceFile(path) !; + const r3SymbolsInfo = files.find(file => file.name.indexOf('r3_symbols') !== -1) || null; + const r3SymbolsPath = r3SymbolsInfo && r3SymbolsInfo.name; + const r3SymbolsFile = r3SymbolsPath && program.getSourceFile(r3SymbolsPath) || null; + return {program, path, file, r3SymbolsPath, r3SymbolsFile}; +} + + +export function makeTestProgram( + ...files: {name: string, contents: string, isRoot?: boolean | undefined}[]): ts.Program { + return makeProgram([getFakeCore(), getFakeTslib(), ...files], {allowJs: true, checkJs: false}) .program; } diff --git a/packages/compiler-cli/src/ngcc/test/host/esm2015_host_import_helper_spec.ts b/packages/compiler-cli/src/ngcc/test/host/esm2015_host_import_helper_spec.ts index 51b2120b8fbd..1e38b6eb12c3 100644 --- a/packages/compiler-cli/src/ngcc/test/host/esm2015_host_import_helper_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/host/esm2015_host_import_helper_spec.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {ClassMemberKind, Import} from '../../../ngtsc/host'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; -import {convertToDirectTsLibImport, getDeclaration, makeProgram} from '../helpers/utils'; +import {convertToDirectTsLibImport, getDeclaration, makeTestProgram} from '../helpers/utils'; const FILES = [ { @@ -103,7 +103,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => { describe('getDecoratorsOfDeclaration()', () => { it('should find the decorators on a class', () => { - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -127,7 +127,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => { {from: '@angular/core', name: 'Directive'} : {}); - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -142,7 +142,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => { }); it('should support decorators being used inside @angular/core', () => { - const program = makeProgram(fileSystem.files[1]); + const program = makeTestProgram(fileSystem.files[1]); const host = new Esm2015ReflectionHost(true, program.getTypeChecker()); const classNode = getDeclaration( program, '/node_modules/@angular/core/some_directive.js', 'SomeDirective', @@ -163,7 +163,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => { describe('getMembersOfClass()', () => { it('should find decorated members on a class', () => { - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -181,7 +181,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => { }); it('should find non decorated properties on a class', () => { - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -195,7 +195,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => { }); it('should find static methods on a class', () => { - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -208,7 +208,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => { }); it('should find static properties on a class', () => { - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -225,7 +225,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => { const spy = spyOn(Esm2015ReflectionHost.prototype, 'getImportOfIdentifier').and.returnValue({}); - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -236,7 +236,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => { }); it('should support decorators being used inside @angular/core', () => { - const program = makeProgram(fileSystem.files[1]); + const program = makeTestProgram(fileSystem.files[1]); const host = new Esm2015ReflectionHost(true, program.getTypeChecker()); const classNode = getDeclaration( program, '/node_modules/@angular/core/some_directive.js', 'SomeDirective', @@ -252,7 +252,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => { describe('getConstructorParameters', () => { it('should find the decorated constructor parameters', () => { - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -273,7 +273,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => { const spy = spyOn(Esm2015ReflectionHost.prototype, 'getImportOfIdentifier') .and.returnValue(mockImportInfo); - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -291,7 +291,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => { describe('getDeclarationOfIdentifier', () => { it('should return the declaration of a locally defined identifier', () => { - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -307,7 +307,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => { }); it('should return the declaration of an externally defined identifier', () => { - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -330,7 +330,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => { describe('getVariableValue', () => { it('should find the "actual" declaration of an aliased variable identifier', () => { - const program = makeProgram(fileSystem.files[2]); + const program = makeTestProgram(fileSystem.files[2]); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const ngModuleRef = findVariableDeclaration( program.getSourceFile(fileSystem.files[2].name) !, 'HttpClientXsrfModule_1'); @@ -345,7 +345,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => { }); it('should return null if the variable has no assignment', () => { - const program = makeProgram(fileSystem.files[2]); + const program = makeTestProgram(fileSystem.files[2]); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const missingValue = findVariableDeclaration( program.getSourceFile(fileSystem.files[2].name) !, 'missingValue'); @@ -354,7 +354,7 @@ describe('Fesm2015ReflectionHost [import helper style]', () => { }); it('should return null if the variable is not assigned from a call to __decorate', () => { - const program = makeProgram(fileSystem.files[2]); + const program = makeTestProgram(fileSystem.files[2]); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const nonDecoratedVar = findVariableDeclaration( program.getSourceFile(fileSystem.files[2].name) !, 'nonDecoratedVar'); diff --git a/packages/compiler-cli/src/ngcc/test/host/esm2015_host_spec.ts b/packages/compiler-cli/src/ngcc/test/host/esm2015_host_spec.ts index 2878872ec2c4..790d610cd9de 100644 --- a/packages/compiler-cli/src/ngcc/test/host/esm2015_host_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/host/esm2015_host_spec.ts @@ -7,9 +7,10 @@ */ import * as ts from 'typescript'; + import {ClassMemberKind, Import} from '../../../ngtsc/host'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; -import {getDeclaration, makeProgram} from '../helpers/utils'; +import {getDeclaration, makeTestBundleProgram, makeTestProgram} from '../helpers/utils'; const SOME_DIRECTIVE_FILE = { name: '/some_directive.js', @@ -486,7 +487,7 @@ describe('Fesm2015ReflectionHost', () => { describe('getDecoratorsOfDeclaration()', () => { it('should find the decorators on a class', () => { - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration); @@ -504,7 +505,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should return null if the symbol is not a class', () => { - const program = makeProgram(FOO_FUNCTION_FILE); + const program = makeTestProgram(FOO_FUNCTION_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const functionNode = getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration); @@ -513,7 +514,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should return null if there are no decorators', () => { - const program = makeProgram(SIMPLE_CLASS_FILE); + const program = makeTestProgram(SIMPLE_CLASS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isClassDeclaration); @@ -522,7 +523,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should ignore `decorators` if it is not an array literal', () => { - const program = makeProgram(INVALID_DECORATORS_FILE); + const program = makeTestProgram(INVALID_DECORATORS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', ts.isClassDeclaration); @@ -531,7 +532,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should ignore decorator elements that are not object literals', () => { - const program = makeProgram(INVALID_DECORATORS_FILE); + const program = makeTestProgram(INVALID_DECORATORS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', ts.isClassDeclaration); @@ -542,7 +543,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should ignore decorator elements that have no `type` property', () => { - const program = makeProgram(INVALID_DECORATORS_FILE); + const program = makeTestProgram(INVALID_DECORATORS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', ts.isClassDeclaration); @@ -553,7 +554,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should ignore decorator elements whose `type` value is not an identifier', () => { - const program = makeProgram(INVALID_DECORATORS_FILE); + const program = makeTestProgram(INVALID_DECORATORS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', ts.isClassDeclaration); @@ -568,7 +569,7 @@ describe('Fesm2015ReflectionHost', () => { const spy = spyOn(Esm2015ReflectionHost.prototype, 'getImportOfIdentifier') .and.returnValue(mockImportInfo); - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration); @@ -583,7 +584,7 @@ describe('Fesm2015ReflectionHost', () => { describe('(returned decorators `args`)', () => { it('should be an empty array if decorator has no `args` property', () => { - const program = makeProgram(INVALID_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_DECORATOR_ARGS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', ts.isClassDeclaration); @@ -595,7 +596,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should be an empty array if decorator\'s `args` has no property assignment', () => { - const program = makeProgram(INVALID_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_DECORATOR_ARGS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', @@ -608,7 +609,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should be an empty array if `args` property value is not an array literal', () => { - const program = makeProgram(INVALID_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_DECORATOR_ARGS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', ts.isClassDeclaration); @@ -623,7 +624,7 @@ describe('Fesm2015ReflectionHost', () => { describe('getMembersOfClass()', () => { it('should find decorated properties on a class', () => { - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration); @@ -641,7 +642,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should find non decorated properties on a class', () => { - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration); @@ -655,7 +656,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should find static methods on a class', () => { - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration); @@ -668,7 +669,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should find static properties on a class', () => { - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration); @@ -682,7 +683,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should throw if the symbol is not a class', () => { - const program = makeProgram(FOO_FUNCTION_FILE); + const program = makeTestProgram(FOO_FUNCTION_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const functionNode = getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration); @@ -692,7 +693,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should return an empty array if there are no prop decorators', () => { - const program = makeProgram(SIMPLE_CLASS_FILE); + const program = makeTestProgram(SIMPLE_CLASS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isClassDeclaration); @@ -703,7 +704,7 @@ describe('Fesm2015ReflectionHost', () => { it('should not process decorated properties in `propDecorators` if it is not an object literal', () => { - const program = makeProgram(INVALID_PROP_DECORATORS_FILE); + const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteral', ts.isClassDeclaration); @@ -713,7 +714,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should ignore prop decorator elements that are not object literals', () => { - const program = makeProgram(INVALID_PROP_DECORATORS_FILE); + const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteralProp', @@ -727,7 +728,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should ignore prop decorator elements that have no `type` property', () => { - const program = makeProgram(INVALID_PROP_DECORATORS_FILE); + const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', ts.isClassDeclaration); @@ -740,7 +741,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should ignore prop decorator elements whose `type` value is not an identifier', () => { - const program = makeProgram(INVALID_PROP_DECORATORS_FILE); + const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', ts.isClassDeclaration); @@ -760,7 +761,7 @@ describe('Fesm2015ReflectionHost', () => { return {name: `name${callCount}`, from: '@angular/core'}; }); - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration); @@ -782,7 +783,7 @@ describe('Fesm2015ReflectionHost', () => { describe('(returned prop decorators `args`)', () => { it('should be an empty array if prop decorator has no `args` property', () => { - const program = makeProgram(INVALID_PROP_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_PROP_DECORATOR_ARGS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', @@ -797,7 +798,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should be an empty array if prop decorator\'s `args` has no property assignment', () => { - const program = makeProgram(INVALID_PROP_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_PROP_DECORATOR_ARGS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', @@ -812,7 +813,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should be an empty array if `args` property value is not an array literal', () => { - const program = makeProgram(INVALID_PROP_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_PROP_DECORATOR_ARGS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', @@ -830,7 +831,7 @@ describe('Fesm2015ReflectionHost', () => { describe('getConstructorParameters()', () => { it('should find the decorated constructor parameters', () => { - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration); @@ -846,7 +847,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should throw if the symbol is not a class', () => { - const program = makeProgram(FOO_FUNCTION_FILE); + const program = makeTestProgram(FOO_FUNCTION_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const functionNode = getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration); @@ -856,7 +857,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should return `null` if there is no constructor', () => { - const program = makeProgram(SIMPLE_CLASS_FILE); + const program = makeTestProgram(SIMPLE_CLASS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isClassDeclaration); @@ -865,7 +866,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should return an array even if there are no decorators', () => { - const program = makeProgram(SIMPLE_CLASS_FILE); + const program = makeTestProgram(SIMPLE_CLASS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', ts.isClassDeclaration); @@ -878,7 +879,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should return an empty array if there are no constructor parameters', () => { - const program = makeProgram(INVALID_CTOR_DECORATORS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', ts.isClassDeclaration); @@ -888,7 +889,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should ignore decorators that are not imported from core', () => { - const program = makeProgram(INVALID_CTOR_DECORATORS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATORS_FILE.name, 'NotFromCore', ts.isClassDeclaration); @@ -902,7 +903,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should ignore `ctorParameters` if it is not an arrow function', () => { - const program = makeProgram(INVALID_CTOR_DECORATORS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrowFunction', ts.isClassDeclaration); @@ -916,7 +917,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should ignore `ctorParameters` if it does not return an array literal', () => { - const program = makeProgram(INVALID_CTOR_DECORATORS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', ts.isClassDeclaration); @@ -931,7 +932,7 @@ describe('Fesm2015ReflectionHost', () => { describe('(returned parameters `decorators`)', () => { it('should ignore param decorator elements that are not object literals', () => { - const program = makeProgram(INVALID_CTOR_DECORATORS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATORS_FILE.name, 'NotObjectLiteral', ts.isClassDeclaration); @@ -949,7 +950,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should ignore param decorator elements that have no `type` property', () => { - const program = makeProgram(INVALID_CTOR_DECORATORS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty', ts.isClassDeclaration); @@ -961,7 +962,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should ignore param decorator elements whose `type` value is not an identifier', () => { - const program = makeProgram(INVALID_CTOR_DECORATORS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier', ts.isClassDeclaration); @@ -977,7 +978,7 @@ describe('Fesm2015ReflectionHost', () => { const spy = spyOn(Esm2015ReflectionHost.prototype, 'getImportOfIdentifier') .and.returnValue(mockImportInfo); - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration); @@ -994,7 +995,7 @@ describe('Fesm2015ReflectionHost', () => { describe('(returned parameters `decorators.args`)', () => { it('should be an empty array if param decorator has no `args` property', () => { - const program = makeProgram(INVALID_CTOR_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATOR_ARGS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', @@ -1009,7 +1010,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should be an empty array if param decorator\'s `args` has no property assignment', () => { - const program = makeProgram(INVALID_CTOR_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATOR_ARGS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', @@ -1023,7 +1024,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should be an empty array if `args` property value is not an array literal', () => { - const program = makeProgram(INVALID_CTOR_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATOR_ARGS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', @@ -1040,7 +1041,7 @@ describe('Fesm2015ReflectionHost', () => { describe('getDefinitionOfFunction()', () => { it('should return an object describing the function declaration passed as an argument', () => { - const program = makeProgram(FUNCTION_BODY_FILE); + const program = makeTestProgram(FUNCTION_BODY_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const fooNode = @@ -1102,7 +1103,7 @@ describe('Fesm2015ReflectionHost', () => { describe('getImportOfIdentifier()', () => { it('should find the import of an identifier', () => { - const program = makeProgram(...IMPORTS_FILES); + const program = makeTestProgram(...IMPORTS_FILES); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const variableNode = getDeclaration(program, IMPORTS_FILES[1].name, 'b', ts.isVariableDeclaration); @@ -1112,7 +1113,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should find the name by which the identifier was exported, not imported', () => { - const program = makeProgram(...IMPORTS_FILES); + const program = makeTestProgram(...IMPORTS_FILES); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const variableNode = getDeclaration(program, IMPORTS_FILES[1].name, 'c', ts.isVariableDeclaration); @@ -1122,7 +1123,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should return null if the identifier was not imported', () => { - const program = makeProgram(...IMPORTS_FILES); + const program = makeTestProgram(...IMPORTS_FILES); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const variableNode = getDeclaration(program, IMPORTS_FILES[1].name, 'd', ts.isVariableDeclaration); @@ -1134,7 +1135,7 @@ describe('Fesm2015ReflectionHost', () => { describe('getDeclarationOfIdentifier()', () => { it('should return the declaration of a locally defined identifier', () => { - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration); @@ -1150,7 +1151,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should return the declaration of an externally defined identifier', () => { - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration(program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isClassDeclaration); @@ -1170,7 +1171,7 @@ describe('Fesm2015ReflectionHost', () => { describe('getExportsOfModule()', () => { it('should return a map of all the exports from a given module', () => { - const program = makeProgram(...EXPORTS_FILES); + const program = makeTestProgram(...EXPORTS_FILES); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const file = program.getSourceFile(EXPORTS_FILES[1].name) !; const exportDeclarations = host.getExportsOfModule(file); @@ -1205,7 +1206,7 @@ describe('Fesm2015ReflectionHost', () => { describe('isClass()', () => { it('should return true if a given node is a TS class declaration', () => { - const program = makeProgram(SIMPLE_CLASS_FILE); + const program = makeTestProgram(SIMPLE_CLASS_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const node = getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isClassDeclaration); @@ -1213,7 +1214,7 @@ describe('Fesm2015ReflectionHost', () => { }); it('should return false if a given node is a TS function declaration', () => { - const program = makeProgram(FOO_FUNCTION_FILE); + const program = makeTestProgram(FOO_FUNCTION_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const node = getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration); expect(host.isClass(node)).toBe(false); @@ -1222,10 +1223,10 @@ describe('Fesm2015ReflectionHost', () => { describe('getGenericArityOfClass()', () => { it('should properly count type parameters', () => { - const dtsProgram = makeProgram(ARITY_CLASSES[1]); - const program = makeProgram(ARITY_CLASSES[0]); - const host = new Esm2015ReflectionHost( - false, program.getTypeChecker(), ARITY_CLASSES[1].name, dtsProgram); + const program = makeTestProgram(ARITY_CLASSES[0]); + const dtsProgram = makeTestProgram(ARITY_CLASSES[1]); + const dts = makeTestBundleProgram([ARITY_CLASSES[1]]); + const host = new Esm2015ReflectionHost(false, program.getTypeChecker(), dts); const noTypeParamClass = getDeclaration(program, '/src/class.js', 'NoTypeParam', ts.isClassDeclaration); expect(host.getGenericArityOfClass(noTypeParamClass)).toBe(0); @@ -1241,7 +1242,7 @@ describe('Fesm2015ReflectionHost', () => { describe('getSwitchableDeclarations()', () => { it('should return a collection of all the switchable variable declarations in the given module', () => { - const program = makeProgram(MARKER_FILE); + const program = makeTestProgram(MARKER_FILE); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const file = program.getSourceFile(MARKER_FILE.name) !; const declarations = host.getSwitchableDeclarations(file); @@ -1253,7 +1254,7 @@ describe('Fesm2015ReflectionHost', () => { describe('findDecoratedClasses()', () => { it('should return an array of all decorated classes in the given source file', () => { - const program = makeProgram(...DECORATED_FILES); + const program = makeTestProgram(...DECORATED_FILES); const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); const primaryFile = program.getSourceFile(DECORATED_FILES[0].name) !; const secondaryFile = program.getSourceFile(DECORATED_FILES[1].name) !; @@ -1280,58 +1281,53 @@ describe('Fesm2015ReflectionHost', () => { describe('getDtsDeclarationsOfClass()', () => { it('should find the dts declaration that has the same relative path to the source file', () => { - const srcProgram = makeProgram(...TYPINGS_SRC_FILES); - const dtsProgram = makeProgram(...TYPINGS_DTS_FILES); + const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES); + const dts = makeTestBundleProgram(TYPINGS_DTS_FILES); const class1 = getDeclaration(srcProgram, '/src/class1.js', 'Class1', ts.isClassDeclaration); - const host = new Esm2015ReflectionHost( - false, srcProgram.getTypeChecker(), TYPINGS_DTS_FILES[0].name, dtsProgram); + const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts); const dtsDeclaration = host.getDtsDeclarationOfClass(class1); expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts'); }); it('should return null if there is no matching class in the matching dts file', () => { - const srcProgram = makeProgram(...TYPINGS_SRC_FILES); - const dtsProgram = makeProgram(...TYPINGS_DTS_FILES); + const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES); + const dts = makeTestBundleProgram(TYPINGS_DTS_FILES); const missingClass = getDeclaration(srcProgram, '/src/class1.js', 'MissingClass1', ts.isClassDeclaration); - const host = new Esm2015ReflectionHost( - false, srcProgram.getTypeChecker(), TYPINGS_DTS_FILES[0].name, dtsProgram); + const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts); expect(host.getDtsDeclarationOfClass(missingClass)).toBe(null); }); it('should return null if there is no matching dts file', () => { - const srcProgram = makeProgram(...TYPINGS_SRC_FILES); - const dtsProgram = makeProgram(...TYPINGS_DTS_FILES); + const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES); + const dts = makeTestBundleProgram(TYPINGS_DTS_FILES); const missingClass = getDeclaration( srcProgram, '/src/missing-class.js', 'MissingClass2', ts.isClassDeclaration); - const host = new Esm2015ReflectionHost( - false, srcProgram.getTypeChecker(), TYPINGS_DTS_FILES[0].name, dtsProgram); + const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts); expect(host.getDtsDeclarationOfClass(missingClass)).toBe(null); }); it('should find the dts file that contains a matching class declaration, even if the source files do not match', () => { - const srcProgram = makeProgram(...TYPINGS_SRC_FILES); - const dtsProgram = makeProgram(...TYPINGS_DTS_FILES); + const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES); + const dts = makeTestBundleProgram(TYPINGS_DTS_FILES); const class1 = getDeclaration(srcProgram, '/src/flat-file.js', 'Class1', ts.isClassDeclaration); - const host = new Esm2015ReflectionHost( - false, srcProgram.getTypeChecker(), TYPINGS_DTS_FILES[0].name, dtsProgram); + const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts); const dtsDeclaration = host.getDtsDeclarationOfClass(class1); expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts'); }); it('should find aliased exports', () => { - const srcProgram = makeProgram(...TYPINGS_SRC_FILES); - const dtsProgram = makeProgram(...TYPINGS_DTS_FILES); + const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES); + const dts = makeTestBundleProgram(TYPINGS_DTS_FILES); const class3 = getDeclaration(srcProgram, '/src/flat-file.js', 'Class3', ts.isClassDeclaration); - const host = new Esm2015ReflectionHost( - false, srcProgram.getTypeChecker(), TYPINGS_DTS_FILES[0].name, dtsProgram); + const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts); const dtsDeclaration = host.getDtsDeclarationOfClass(class3); expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class3.d.ts'); @@ -1339,12 +1335,11 @@ describe('Fesm2015ReflectionHost', () => { it('should find the dts file that contains a matching class declaration, even if the class is not publicly exported', () => { - const srcProgram = makeProgram(...TYPINGS_SRC_FILES); - const dtsProgram = makeProgram(...TYPINGS_DTS_FILES); + const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES); + const dts = makeTestBundleProgram(TYPINGS_DTS_FILES); const internalClass = getDeclaration(srcProgram, '/src/internal.js', 'InternalClass', ts.isClassDeclaration); - const host = new Esm2015ReflectionHost( - false, srcProgram.getTypeChecker(), TYPINGS_DTS_FILES[0].name, dtsProgram); + const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts); const dtsDeclaration = host.getDtsDeclarationOfClass(internalClass); expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/internal.d.ts'); @@ -1352,14 +1347,13 @@ describe('Fesm2015ReflectionHost', () => { it('should prefer the publicly exported class if there are multiple classes with the same name', () => { - const srcProgram = makeProgram(...TYPINGS_SRC_FILES); - const dtsProgram = makeProgram(...TYPINGS_DTS_FILES); + const srcProgram = makeTestProgram(...TYPINGS_SRC_FILES); + const dts = makeTestBundleProgram(TYPINGS_DTS_FILES); const class2 = getDeclaration(srcProgram, '/src/class2.js', 'Class2', ts.isClassDeclaration); const internalClass2 = getDeclaration(srcProgram, '/src/internal.js', 'Class2', ts.isClassDeclaration); - const host = new Esm2015ReflectionHost( - false, srcProgram.getTypeChecker(), TYPINGS_DTS_FILES[0].name, dtsProgram); + const host = new Esm2015ReflectionHost(false, srcProgram.getTypeChecker(), dts); const class2DtsDeclaration = host.getDtsDeclarationOfClass(class2); expect(class2DtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class2.d.ts'); diff --git a/packages/compiler-cli/src/ngcc/test/host/esm5_host_import_helper_spec.ts b/packages/compiler-cli/src/ngcc/test/host/esm5_host_import_helper_spec.ts index 60aca577a37c..e696d5675f28 100644 --- a/packages/compiler-cli/src/ngcc/test/host/esm5_host_import_helper_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/host/esm5_host_import_helper_spec.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {ClassMemberKind, Import} from '../../../ngtsc/host'; import {Esm5ReflectionHost} from '../../src/host/esm5_host'; -import {convertToDirectTsLibImport, getDeclaration, makeProgram} from '../helpers/utils'; +import {convertToDirectTsLibImport, getDeclaration, makeTestProgram} from '../helpers/utils'; const FILES = [ { @@ -118,7 +118,7 @@ describe('Esm5ReflectionHost [import helper style]', () => { describe('getDecoratorsOfDeclaration()', () => { it('should find the decorators on a class', () => { - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -142,7 +142,7 @@ describe('Esm5ReflectionHost [import helper style]', () => { {from: '@angular/core', name: 'Directive'} : {}); - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -157,7 +157,7 @@ describe('Esm5ReflectionHost [import helper style]', () => { }); it('should support decorators being used inside @angular/core', () => { - const program = makeProgram(fileSystem.files[1]); + const program = makeTestProgram(fileSystem.files[1]); const host = new Esm5ReflectionHost(true, program.getTypeChecker()); const classNode = getDeclaration( program, '/node_modules/@angular/core/some_directive.js', 'SomeDirective', @@ -178,7 +178,7 @@ describe('Esm5ReflectionHost [import helper style]', () => { describe('getMembersOfClass()', () => { it('should find decorated members on a class', () => { - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -196,7 +196,7 @@ describe('Esm5ReflectionHost [import helper style]', () => { }); it('should find non decorated properties on a class', () => { - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -210,7 +210,7 @@ describe('Esm5ReflectionHost [import helper style]', () => { }); it('should find static methods on a class', () => { - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -223,7 +223,7 @@ describe('Esm5ReflectionHost [import helper style]', () => { }); it('should find static properties on a class', () => { - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -240,7 +240,7 @@ describe('Esm5ReflectionHost [import helper style]', () => { const spy = spyOn(Esm5ReflectionHost.prototype, 'getImportOfIdentifier').and.returnValue({}); - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -251,7 +251,7 @@ describe('Esm5ReflectionHost [import helper style]', () => { }); it('should support decorators being used inside @angular/core', () => { - const program = makeProgram(fileSystem.files[1]); + const program = makeTestProgram(fileSystem.files[1]); const host = new Esm5ReflectionHost(true, program.getTypeChecker()); const classNode = getDeclaration( program, '/node_modules/@angular/core/some_directive.js', 'SomeDirective', @@ -267,7 +267,7 @@ describe('Esm5ReflectionHost [import helper style]', () => { describe('getConstructorParameters', () => { it('should find the decorated constructor parameters', () => { - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -288,7 +288,7 @@ describe('Esm5ReflectionHost [import helper style]', () => { const spy = spyOn(Esm5ReflectionHost.prototype, 'getImportOfIdentifier') .and.returnValue(mockImportInfo); - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -306,7 +306,7 @@ describe('Esm5ReflectionHost [import helper style]', () => { describe('getDeclarationOfIdentifier', () => { it('should return the declaration of a locally defined identifier', () => { - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -322,7 +322,7 @@ describe('Esm5ReflectionHost [import helper style]', () => { }); it('should return the declaration of an externally defined identifier', () => { - const program = makeProgram(fileSystem.files[0]); + const program = makeTestProgram(fileSystem.files[0]); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, '/some_directive.js', 'SomeDirective', ts.isVariableDeclaration); @@ -347,7 +347,7 @@ describe('Esm5ReflectionHost [import helper style]', () => { describe('getVariableValue', () => { it('should find the "actual" declaration of an aliased variable identifier', () => { - const program = makeProgram(fileSystem.files[2]); + const program = makeTestProgram(fileSystem.files[2]); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const ngModuleRef = findVariableDeclaration( program.getSourceFile(fileSystem.files[2].name) !, 'HttpClientXsrfModule_1'); @@ -362,7 +362,7 @@ describe('Esm5ReflectionHost [import helper style]', () => { }); it('should return undefined if the variable has no assignment', () => { - const program = makeProgram(fileSystem.files[2]); + const program = makeTestProgram(fileSystem.files[2]); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const missingValue = findVariableDeclaration( program.getSourceFile(fileSystem.files[2].name) !, 'missingValue'); @@ -371,7 +371,7 @@ describe('Esm5ReflectionHost [import helper style]', () => { }); it('should return null if the variable is not assigned from a call to __decorate', () => { - const program = makeProgram(fileSystem.files[2]); + const program = makeTestProgram(fileSystem.files[2]); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const nonDecoratedVar = findVariableDeclaration( program.getSourceFile(fileSystem.files[2].name) !, 'nonDecoratedVar'); diff --git a/packages/compiler-cli/src/ngcc/test/host/esm5_host_spec.ts b/packages/compiler-cli/src/ngcc/test/host/esm5_host_spec.ts index 4c6ddd00144d..6479edecd456 100644 --- a/packages/compiler-cli/src/ngcc/test/host/esm5_host_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/host/esm5_host_spec.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {ClassMemberKind, Import} from '../../../ngtsc/host'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; import {Esm5ReflectionHost} from '../../src/host/esm5_host'; -import {getDeclaration, makeProgram} from '../helpers/utils'; +import {getDeclaration, makeTestProgram} from '../helpers/utils'; const SOME_DIRECTIVE_FILE = { name: '/some_directive.js', @@ -476,7 +476,7 @@ describe('Esm5ReflectionHost', () => { describe('getDecoratorsOfDeclaration()', () => { it('should find the decorators on a class', () => { - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration); @@ -494,7 +494,7 @@ describe('Esm5ReflectionHost', () => { }); it('should return null if the symbol is not a class', () => { - const program = makeProgram(FOO_FUNCTION_FILE); + const program = makeTestProgram(FOO_FUNCTION_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const functionNode = getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration); @@ -503,7 +503,7 @@ describe('Esm5ReflectionHost', () => { }); it('should return null if there are no decorators', () => { - const program = makeProgram(SIMPLE_CLASS_FILE); + const program = makeTestProgram(SIMPLE_CLASS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration); @@ -512,7 +512,7 @@ describe('Esm5ReflectionHost', () => { }); it('should ignore `decorators` if it is not an array literal', () => { - const program = makeProgram(INVALID_DECORATORS_FILE); + const program = makeTestProgram(INVALID_DECORATORS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_DECORATORS_FILE.name, 'NotArrayLiteral', ts.isVariableDeclaration); @@ -521,7 +521,7 @@ describe('Esm5ReflectionHost', () => { }); it('should ignore decorator elements that are not object literals', () => { - const program = makeProgram(INVALID_DECORATORS_FILE); + const program = makeTestProgram(INVALID_DECORATORS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_DECORATORS_FILE.name, 'NotObjectLiteral', ts.isVariableDeclaration); @@ -532,7 +532,7 @@ describe('Esm5ReflectionHost', () => { }); it('should ignore decorator elements that have no `type` property', () => { - const program = makeProgram(INVALID_DECORATORS_FILE); + const program = makeTestProgram(INVALID_DECORATORS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_DECORATORS_FILE.name, 'NoTypeProperty', ts.isVariableDeclaration); @@ -543,7 +543,7 @@ describe('Esm5ReflectionHost', () => { }); it('should ignore decorator elements whose `type` value is not an identifier', () => { - const program = makeProgram(INVALID_DECORATORS_FILE); + const program = makeTestProgram(INVALID_DECORATORS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_DECORATORS_FILE.name, 'NotIdentifier', ts.isVariableDeclaration); @@ -558,7 +558,7 @@ describe('Esm5ReflectionHost', () => { const spy = spyOn(Esm5ReflectionHost.prototype, 'getImportOfIdentifier') .and.returnValue(mockImportInfo); - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration); @@ -573,7 +573,7 @@ describe('Esm5ReflectionHost', () => { describe('(returned decorators `args`)', () => { it('should be an empty array if decorator has no `args` property', () => { - const program = makeProgram(INVALID_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_DECORATOR_ARGS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', ts.isVariableDeclaration); @@ -585,7 +585,7 @@ describe('Esm5ReflectionHost', () => { }); it('should be an empty array if decorator\'s `args` has no property assignment', () => { - const program = makeProgram(INVALID_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_DECORATOR_ARGS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', @@ -598,7 +598,7 @@ describe('Esm5ReflectionHost', () => { }); it('should be an empty array if `args` property value is not an array literal', () => { - const program = makeProgram(INVALID_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_DECORATOR_ARGS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', ts.isVariableDeclaration); @@ -613,7 +613,7 @@ describe('Esm5ReflectionHost', () => { describe('getMembersOfClass()', () => { it('should find decorated members on a class', () => { - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration); @@ -631,7 +631,7 @@ describe('Esm5ReflectionHost', () => { }); it('should find non decorated properties on a class', () => { - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration); @@ -645,7 +645,7 @@ describe('Esm5ReflectionHost', () => { }); it('should find static methods on a class', () => { - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration); @@ -658,7 +658,7 @@ describe('Esm5ReflectionHost', () => { }); it('should find static properties on a class', () => { - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration); @@ -672,7 +672,7 @@ describe('Esm5ReflectionHost', () => { }); it('should throw if the symbol is not a class', () => { - const program = makeProgram(FOO_FUNCTION_FILE); + const program = makeTestProgram(FOO_FUNCTION_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const functionNode = getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration); @@ -682,7 +682,7 @@ describe('Esm5ReflectionHost', () => { }); it('should return an empty array if there are no prop decorators', () => { - const program = makeProgram(SIMPLE_CLASS_FILE); + const program = makeTestProgram(SIMPLE_CLASS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration); @@ -693,7 +693,7 @@ describe('Esm5ReflectionHost', () => { it('should not process decorated properties in `propDecorators` if it is not an object literal', () => { - const program = makeProgram(INVALID_PROP_DECORATORS_FILE); + const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteral', @@ -704,7 +704,7 @@ describe('Esm5ReflectionHost', () => { }); it('should ignore prop decorator elements that are not object literals', () => { - const program = makeProgram(INVALID_PROP_DECORATORS_FILE); + const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_PROP_DECORATORS_FILE.name, 'NotObjectLiteralProp', @@ -718,7 +718,7 @@ describe('Esm5ReflectionHost', () => { }); it('should ignore prop decorator elements that have no `type` property', () => { - const program = makeProgram(INVALID_PROP_DECORATORS_FILE); + const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_PROP_DECORATORS_FILE.name, 'NoTypeProperty', ts.isVariableDeclaration); @@ -731,7 +731,7 @@ describe('Esm5ReflectionHost', () => { }); it('should ignore prop decorator elements whose `type` value is not an identifier', () => { - const program = makeProgram(INVALID_PROP_DECORATORS_FILE); + const program = makeTestProgram(INVALID_PROP_DECORATORS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_PROP_DECORATORS_FILE.name, 'NotIdentifier', ts.isVariableDeclaration); @@ -750,7 +750,7 @@ describe('Esm5ReflectionHost', () => { return {name: `name${callCount}`, from: `@angular/core`}; }); - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration); @@ -766,7 +766,7 @@ describe('Esm5ReflectionHost', () => { describe('(returned prop decorators `args`)', () => { it('should be an empty array if prop decorator has no `args` property', () => { - const program = makeProgram(INVALID_PROP_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_PROP_DECORATOR_ARGS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', @@ -781,7 +781,7 @@ describe('Esm5ReflectionHost', () => { }); it('should be an empty array if prop decorator\'s `args` has no property assignment', () => { - const program = makeProgram(INVALID_PROP_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_PROP_DECORATOR_ARGS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', @@ -796,7 +796,7 @@ describe('Esm5ReflectionHost', () => { }); it('should be an empty array if `args` property value is not an array literal', () => { - const program = makeProgram(INVALID_PROP_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_PROP_DECORATOR_ARGS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_PROP_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', @@ -814,7 +814,7 @@ describe('Esm5ReflectionHost', () => { describe('getConstructorParameters', () => { it('should find the decorated constructor parameters', () => { - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration); @@ -830,7 +830,7 @@ describe('Esm5ReflectionHost', () => { }); it('should throw if the symbol is not a class', () => { - const program = makeProgram(FOO_FUNCTION_FILE); + const program = makeTestProgram(FOO_FUNCTION_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const functionNode = getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration); @@ -843,7 +843,7 @@ describe('Esm5ReflectionHost', () => { // it('should return `null` if there is no constructor', () => { }); it('should return an array even if there are no decorators', () => { - const program = makeProgram(SIMPLE_CLASS_FILE); + const program = makeTestProgram(SIMPLE_CLASS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, SIMPLE_CLASS_FILE.name, 'NoDecoratorConstructorClass', ts.isVariableDeclaration); @@ -856,7 +856,7 @@ describe('Esm5ReflectionHost', () => { }); it('should return an empty array if there are no constructor parameters', () => { - const program = makeProgram(INVALID_CTOR_DECORATORS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATORS_FILE.name, 'NoParameters', ts.isVariableDeclaration); @@ -869,7 +869,7 @@ describe('Esm5ReflectionHost', () => { // it('should ignore `ctorParameters` if it is an arrow function', () => { }); it('should ignore `ctorParameters` if it does not return an array literal', () => { - const program = makeProgram(INVALID_CTOR_DECORATORS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATORS_FILE.name, 'NotArrayLiteral', ts.isVariableDeclaration); @@ -884,7 +884,7 @@ describe('Esm5ReflectionHost', () => { describe('(returned parameters `decorators`)', () => { it('should ignore param decorator elements that are not object literals', () => { - const program = makeProgram(INVALID_CTOR_DECORATORS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATORS_FILE.name, 'NotObjectLiteral', @@ -903,7 +903,7 @@ describe('Esm5ReflectionHost', () => { }); it('should ignore param decorator elements that have no `type` property', () => { - const program = makeProgram(INVALID_CTOR_DECORATORS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATORS_FILE.name, 'NoTypeProperty', ts.isVariableDeclaration); @@ -915,7 +915,7 @@ describe('Esm5ReflectionHost', () => { }); it('should ignore param decorator elements whose `type` value is not an identifier', () => { - const program = makeProgram(INVALID_CTOR_DECORATORS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATORS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATORS_FILE.name, 'NotIdentifier', ts.isVariableDeclaration); @@ -931,7 +931,7 @@ describe('Esm5ReflectionHost', () => { const spy = spyOn(Esm5ReflectionHost.prototype, 'getImportOfIdentifier') .and.returnValue(mockImportInfo); - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration); @@ -948,7 +948,7 @@ describe('Esm5ReflectionHost', () => { describe('(returned parameters `decorators.args`)', () => { it('should be an empty array if param decorator has no `args` property', () => { - const program = makeProgram(INVALID_CTOR_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATOR_ARGS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoArgsProperty', @@ -963,7 +963,7 @@ describe('Esm5ReflectionHost', () => { }); it('should be an empty array if param decorator\'s `args` has no property assignment', () => { - const program = makeProgram(INVALID_CTOR_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATOR_ARGS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NoPropertyAssignment', @@ -977,7 +977,7 @@ describe('Esm5ReflectionHost', () => { }); it('should be an empty array if `args` property value is not an array literal', () => { - const program = makeProgram(INVALID_CTOR_DECORATOR_ARGS_FILE); + const program = makeTestProgram(INVALID_CTOR_DECORATOR_ARGS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, INVALID_CTOR_DECORATOR_ARGS_FILE.name, 'NotArrayLiteral', @@ -994,7 +994,7 @@ describe('Esm5ReflectionHost', () => { describe('getDefinitionOfFunction()', () => { it('should return an object describing the function declaration passed as an argument', () => { - const program = makeProgram(FUNCTION_BODY_FILE); + const program = makeTestProgram(FUNCTION_BODY_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const fooNode = @@ -1042,7 +1042,7 @@ describe('Esm5ReflectionHost', () => { describe('getImportOfIdentifier', () => { it('should find the import of an identifier', () => { - const program = makeProgram(...IMPORTS_FILES); + const program = makeTestProgram(...IMPORTS_FILES); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const variableNode = getDeclaration(program, IMPORTS_FILES[1].name, 'b', ts.isVariableDeclaration); @@ -1052,7 +1052,7 @@ describe('Esm5ReflectionHost', () => { }); it('should find the name by which the identifier was exported, not imported', () => { - const program = makeProgram(...IMPORTS_FILES); + const program = makeTestProgram(...IMPORTS_FILES); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const variableNode = getDeclaration(program, IMPORTS_FILES[1].name, 'c', ts.isVariableDeclaration); @@ -1062,7 +1062,7 @@ describe('Esm5ReflectionHost', () => { }); it('should return null if the identifier was not imported', () => { - const program = makeProgram(...IMPORTS_FILES); + const program = makeTestProgram(...IMPORTS_FILES); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const variableNode = getDeclaration(program, IMPORTS_FILES[1].name, 'd', ts.isVariableDeclaration); @@ -1074,7 +1074,7 @@ describe('Esm5ReflectionHost', () => { describe('getDeclarationOfIdentifier', () => { it('should return the declaration of a locally defined identifier', () => { - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration); @@ -1090,7 +1090,7 @@ describe('Esm5ReflectionHost', () => { }); it('should return the declaration of an externally defined identifier', () => { - const program = makeProgram(SOME_DIRECTIVE_FILE); + const program = makeTestProgram(SOME_DIRECTIVE_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const classNode = getDeclaration( program, SOME_DIRECTIVE_FILE.name, 'SomeDirective', ts.isVariableDeclaration); @@ -1110,7 +1110,7 @@ describe('Esm5ReflectionHost', () => { describe('getExportsOfModule()', () => { it('should return a map of all the exports from a given module', () => { - const program = makeProgram(...EXPORTS_FILES); + const program = makeTestProgram(...EXPORTS_FILES); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const file = program.getSourceFile(EXPORTS_FILES[1].name) !; const exportDeclarations = host.getExportsOfModule(file); @@ -1168,7 +1168,7 @@ describe('Esm5ReflectionHost', () => { }); it('should return the class symbol for an ES5 class (outer variable declaration)', () => { - const program = makeProgram(SIMPLE_CLASS_FILE); + const program = makeTestProgram(SIMPLE_CLASS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const node = getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration); @@ -1176,7 +1176,7 @@ describe('Esm5ReflectionHost', () => { }); it('should return the class symbol for an ES5 class (inner function declaration)', () => { - const program = makeProgram(SIMPLE_CLASS_FILE); + const program = makeTestProgram(SIMPLE_CLASS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const outerNode = getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration); @@ -1190,7 +1190,7 @@ describe('Esm5ReflectionHost', () => { it('should return the same class symbol (of the inner declaration) for outer and inner declarations', () => { - const program = makeProgram(SIMPLE_CLASS_FILE); + const program = makeTestProgram(SIMPLE_CLASS_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const outerNode = getDeclaration( program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration); @@ -1204,7 +1204,7 @@ describe('Esm5ReflectionHost', () => { }); it('should return undefined if node is not an ES5 class', () => { - const program = makeProgram(FOO_FUNCTION_FILE); + const program = makeTestProgram(FOO_FUNCTION_FILE); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const node = getDeclaration(program, FOO_FUNCTION_FILE.name, 'foo', ts.isFunctionDeclaration); expect(host.getClassSymbol(node)).toBeUndefined(); @@ -1254,7 +1254,7 @@ describe('Esm5ReflectionHost', () => { describe('findDecoratedClasses()', () => { it('should return an array of all decorated classes in the given source file', () => { - const program = makeProgram(...DECORATED_FILES); + const program = makeTestProgram(...DECORATED_FILES); const host = new Esm5ReflectionHost(false, program.getTypeChecker()); const primary = program.getSourceFile(DECORATED_FILES[0].name) !; 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 7d51ce172eea..5e874c7c7897 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 @@ -7,30 +7,35 @@ */ import {dirname} from 'canonical-path'; import * as ts from 'typescript'; - import MagicString from 'magic-string'; -import {makeProgram} from '../helpers/utils'; + +import {makeTestEntryPointBundle} from '../helpers/utils'; import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; +import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; 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'; -function setup(file: {name: string, contents: string}, transformDts: boolean = false) { +function setup(file: {name: string, contents: string}) { const dir = dirname(file.name); - const program = makeProgram(file); - const sourceFile = program.getSourceFile(file.name) !; - const host = new Esm2015ReflectionHost(false, program.getTypeChecker()); + const bundle = makeTestEntryPointBundle('esm2015', [file]) !; + const typeChecker = bundle.src.program.getTypeChecker(); + const host = new Esm2015ReflectionHost(false, typeChecker); + const referencesRegistry = new NgccReferencesRegistry(host); const decorationAnalyses = - new DecorationAnalyzer(program.getTypeChecker(), host, [''], false).analyzeProgram(program); - const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(program); - const bundle = createBundleInfo(false, null, null); - const renderer = new EsmRenderer(host, bundle, dir, dir, false); - return {host, program, sourceFile, renderer, decorationAnalyses, switchMarkerAnalyses}; + new DecorationAnalyzer(typeChecker, host, referencesRegistry, [''], false) + .analyzeProgram(bundle.src.program); + const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(bundle.src.program); + const renderer = new EsmRenderer(host, false, bundle, dir, dir); + return { + host, + program: bundle.src.program, + sourceFile: bundle.src.file, renderer, decorationAnalyses, switchMarkerAnalyses + }; } const PROGRAM = { - name: 'some/file.js', + name: '/some/file.js', contents: ` /* A copyright notice */ import {Directive} from '@angular/core'; @@ -65,7 +70,7 @@ function compileNgModuleFactory__POST_R3__(injector, options, moduleType) { }; const PROGRAM_DECORATE_HELPER = { - name: 'some/file.js', + name: '/some/file.js', contents: ` import * as tslib_1 from "tslib"; var D_1; 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 c04bbee6106a..f36c17d649cb 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 @@ -5,29 +5,37 @@ * 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'; +import {dirname} from 'canonical-path'; import MagicString from 'magic-string'; -import {makeProgram, getDeclaration} from '../helpers/utils'; +import * as ts from 'typescript'; + +import {makeTestEntryPointBundle, getDeclaration} from '../helpers/utils'; import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; +import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; 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'; function setup(file: {name: string, contents: string}) { - const program = makeProgram(file); - const sourceFile = program.getSourceFile(file.name) !; - const host = new Esm5ReflectionHost(false, program.getTypeChecker()); + const dir = dirname(file.name); + const bundle = makeTestEntryPointBundle('esm5', [file]); + const typeChecker = bundle.src.program.getTypeChecker(); + const host = new Esm5ReflectionHost(false, typeChecker); + const referencesRegistry = new NgccReferencesRegistry(host); const decorationAnalyses = - new DecorationAnalyzer(program.getTypeChecker(), host, [''], false).analyzeProgram(program); - const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(program); - const bundle = createBundleInfo(false, null, null); - const renderer = new Esm5Renderer(host, bundle, '', '', false); - return {host, program, sourceFile, renderer, decorationAnalyses, switchMarkerAnalyses}; + new DecorationAnalyzer(typeChecker, host, referencesRegistry, [''], false) + .analyzeProgram(bundle.src.program); + const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(bundle.src.program); + const renderer = new Esm5Renderer(host, false, bundle, dir, dir); + return { + host, + program: bundle.src.program, + sourceFile: bundle.src.file, renderer, decorationAnalyses, switchMarkerAnalyses + }; } const PROGRAM = { - name: 'some/file.js', + name: '/some/file.js', contents: ` /* A copyright notice */ import {Directive} from '@angular/core'; @@ -86,7 +94,7 @@ export {A, B, C, NoIife, BadIife};` }; const PROGRAM_DECORATE_HELPER = { - name: 'some/file.js', + name: '/some/file.js', contents: ` import * as tslib_1 from "tslib"; /* A copyright notice */ 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 56806524ae93..c1682e78f56e 100644 --- a/packages/compiler-cli/src/ngcc/test/rendering/renderer_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/rendering/renderer_spec.ts @@ -6,21 +6,21 @@ * found in the LICENSE file at https://angular.io/license */ import * as fs from 'fs'; -import * as ts from 'typescript'; - import MagicString from 'magic-string'; +import * as ts from 'typescript'; import {fromObject, generateMapFileComment} from 'convert-source-map'; -import {makeProgram} from '../helpers/utils'; import {CompiledClass, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; 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'; +import {EntryPoint} from '../../src/packages/entry_point'; +import {EntryPointBundle} from '../../src/packages/entry_point_bundle'; +import {makeTestEntryPointBundle} from '../helpers/utils'; class TestRenderer extends Renderer { - constructor(host: Esm2015ReflectionHost, bundle: BundleInfo) { - super(host, bundle, '/src', '/dist', false); + constructor(host: Esm2015ReflectionHost, isCore: boolean, bundle: EntryPointBundle) { + super(host, isCore, bundle, '/src', '/dist'); } addImports(output: MagicString, imports: {name: string, as: string}[]) { output.prepend('\n// ADD IMPORTS\n'); @@ -40,25 +40,23 @@ class TestRenderer extends Renderer { } function createTestRenderer( - files: {name: string, contents: string}[], - options: {isCore?: boolean, rewriteCoreImportsTo?: string} = {}) { - const program = makeProgram(...files); - const rewriteCoreImportsTo = - options.rewriteCoreImportsTo ? program.getSourceFile(options.rewriteCoreImportsTo) ! : null; - const bundle = createBundleInfo(options.isCore || false, rewriteCoreImportsTo, null); - const host = new Esm2015ReflectionHost(bundle.isCore, program.getTypeChecker()); + packageName: string, files: {name: string, contents: string}[], + dtsFile?: {name: string, contents: string}) { + const isCore = packageName === '@angular/core'; + const bundle = makeTestEntryPointBundle('esm2015', files, dtsFile && [dtsFile]); + const typeChecker = bundle.src.program.getTypeChecker(); + const host = new Esm2015ReflectionHost(isCore, typeChecker, bundle.dts); const referencesRegistry = new NgccReferencesRegistry(host); const decorationAnalyses = - new DecorationAnalyzer( - program.getTypeChecker(), host, referencesRegistry, [''], bundle.isCore) - .analyzeProgram(program); - const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(program); - const renderer = new TestRenderer(host, bundle); + new DecorationAnalyzer(typeChecker, host, referencesRegistry, bundle.rootDirs, isCore) + .analyzeProgram(bundle.src.program); + const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(bundle.src.program); + const renderer = new TestRenderer(host, isCore, bundle); spyOn(renderer, 'addImports').and.callThrough(); spyOn(renderer, 'addDefinitions').and.callThrough(); spyOn(renderer, 'removeDecorators').and.callThrough(); - return {renderer, program, decorationAnalyses, switchMarkerAnalyses}; + return {renderer, decorationAnalyses, switchMarkerAnalyses}; } @@ -68,6 +66,10 @@ describe('Renderer', () => { contents: `import { Directive } from '@angular/core';\nexport class A {\n foo(x) {\n return x;\n }\n}\nA.decorators = [\n { type: Directive, args: [{ selector: '[a]' }] }\n];\n` }; + const INPUT_DTS_PROGRAM = { + name: '/typings/file.d.ts', + contents: `export declare class A {\nfoo(x: number): number;\n}\n` + }; const INPUT_PROGRAM_MAP = fromObject({ 'version': 3, @@ -105,9 +107,9 @@ describe('Renderer', () => { describe('renderProgram()', () => { it('should render the modified contents; and a new map file, if the original provided no map file.', () => { - const {renderer, program, decorationAnalyses, switchMarkerAnalyses} = - createTestRenderer([INPUT_PROGRAM]); - const result = renderer.renderProgram(program, decorationAnalyses, switchMarkerAnalyses); + const {renderer, decorationAnalyses, switchMarkerAnalyses} = + createTestRenderer('test-package', [INPUT_PROGRAM]); + const result = renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); expect(result[0].path).toEqual('/dist/file.js'); expect(result[0].contents) .toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('/dist/file.js.map')); @@ -115,104 +117,111 @@ describe('Renderer', () => { expect(result[1].contents).toEqual(OUTPUT_PROGRAM_MAP.toJSON()); }); - it('should call addImports with the source code and info about the core Angular library.', - () => { - const {decorationAnalyses, program, renderer, switchMarkerAnalyses} = - createTestRenderer([INPUT_PROGRAM]); - renderer.renderProgram(program, decorationAnalyses, switchMarkerAnalyses); - const addImportsSpy = renderer.addImports as jasmine.Spy; - expect(addImportsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS); - expect(addImportsSpy.calls.first().args[1]).toEqual([ - {name: '@angular/core', as: 'ɵngcc0'} - ]); - }); + describe('calling abstract methods', () => { + it('should call addImports with the source code and info about the core Angular library.', + () => { + const {decorationAnalyses, renderer, switchMarkerAnalyses} = + createTestRenderer('test-package', [INPUT_PROGRAM]); + renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); + const addImportsSpy = renderer.addImports as jasmine.Spy; + expect(addImportsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS); + expect(addImportsSpy.calls.first().args[1]).toEqual([ + {name: '@angular/core', as: 'ɵngcc0'} + ]); + }); - it('should call addDefinitions with the source code, the analyzed class and the renderered definitions.', - () => { - const {decorationAnalyses, program, renderer, switchMarkerAnalyses} = - createTestRenderer([INPUT_PROGRAM]); - renderer.renderProgram(program, decorationAnalyses, switchMarkerAnalyses); - const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy; - expect(addDefinitionsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS); - expect(addDefinitionsSpy.calls.first().args[1]).toEqual(jasmine.objectContaining({ - name: 'A', - decorators: [jasmine.objectContaining({name: 'Directive'})], - })); - expect(addDefinitionsSpy.calls.first().args[2]) - .toEqual(`/*@__PURE__*/ ɵngcc0.ɵsetClassMetadata(A, [{ + it('should call addDefinitions with the source code, the analyzed class and the rendered definitions.', + () => { + const {decorationAnalyses, renderer, switchMarkerAnalyses} = + createTestRenderer('test-package', [INPUT_PROGRAM]); + renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); + const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy; + expect(addDefinitionsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS); + expect(addDefinitionsSpy.calls.first().args[1]).toEqual(jasmine.objectContaining({ + name: 'A', + decorators: [jasmine.objectContaining({name: 'Directive'})], + })); + expect(addDefinitionsSpy.calls.first().args[2]) + .toEqual(`/*@__PURE__*/ ɵngcc0.ɵsetClassMetadata(A, [{ type: Directive, args: [{ selector: '[a]' }] }], null, { foo: [] }); A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""]], factory: function A_Factory(t) { return new (t || A)(); } });`); - }); + }); - it('should call removeDecorators with the source code, a map of class decorators that have been analyzed', - () => { - const {decorationAnalyses, program, renderer, switchMarkerAnalyses} = - createTestRenderer([INPUT_PROGRAM]); - renderer.renderProgram(program, decorationAnalyses, switchMarkerAnalyses); - const removeDecoratorsSpy = renderer.removeDecorators as jasmine.Spy; - expect(removeDecoratorsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS); - - // Each map key is the TS node of the decorator container - // Each map value is an array of TS nodes that are the decorators to remove - const map = removeDecoratorsSpy.calls.first().args[1] as Map; - const keys = Array.from(map.keys()); - expect(keys.length).toEqual(1); - expect(keys[0].getText()) - .toEqual(`[\n { type: Directive, args: [{ selector: '[a]' }] }\n]`); - const values = Array.from(map.values()); - expect(values.length).toEqual(1); - expect(values[0].length).toEqual(1); - expect(values[0][0].getText()).toEqual(`{ type: Directive, args: [{ selector: '[a]' }] }`); - }); + it('should call removeDecorators with the source code, a map of class decorators that have been analyzed', + () => { + const {decorationAnalyses, renderer, switchMarkerAnalyses} = + createTestRenderer('test-package', [INPUT_PROGRAM]); + renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); + const removeDecoratorsSpy = renderer.removeDecorators as jasmine.Spy; + expect(removeDecoratorsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS); - it('should merge any inline source map from the original file and write the output as an inline source map', - () => { - const {decorationAnalyses, program, renderer, switchMarkerAnalyses} = createTestRenderer([{ - ...INPUT_PROGRAM, - contents: INPUT_PROGRAM.contents + '\n' + INPUT_PROGRAM_MAP.toComment() - }]); - const result = renderer.renderProgram(program, decorationAnalyses, switchMarkerAnalyses); - expect(result[0].path).toEqual('/dist/file.js'); - expect(result[0].contents) - .toEqual(RENDERED_CONTENTS + '\n' + MERGED_OUTPUT_PROGRAM_MAP.toComment()); - expect(result[1]).toBeUndefined(); - }); + // Each map key is the TS node of the decorator container + // Each map value is an array of TS nodes that are the decorators to remove + const map = removeDecoratorsSpy.calls.first().args[1] as Map; + const keys = Array.from(map.keys()); + expect(keys.length).toEqual(1); + expect(keys[0].getText()) + .toEqual(`[\n { type: Directive, args: [{ selector: '[a]' }] }\n]`); + const values = Array.from(map.values()); + expect(values.length).toEqual(1); + expect(values[0].length).toEqual(1); + expect(values[0][0].getText()) + .toEqual(`{ type: Directive, args: [{ selector: '[a]' }] }`); + }); + }); - it('should merge any external source map from the original file and write the output to an external source map', - () => { - // Mock out reading the map file from disk - spyOn(fs, 'readFileSync').and.returnValue(INPUT_PROGRAM_MAP.toJSON()); - const {decorationAnalyses, program, renderer, switchMarkerAnalyses} = createTestRenderer([{ - ...INPUT_PROGRAM, - contents: INPUT_PROGRAM.contents + '\n//# sourceMappingURL=file.js.map' - }]); - const result = renderer.renderProgram(program, decorationAnalyses, switchMarkerAnalyses); - expect(result[0].path).toEqual('/dist/file.js'); - expect(result[0].contents) - .toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('/dist/file.js.map')); - expect(result[1].path).toEqual('/dist/file.js.map'); - expect(result[1].contents).toEqual(MERGED_OUTPUT_PROGRAM_MAP.toJSON()); - }); + describe('source map merging', () => { + it('should merge any inline source map from the original file and write the output as an inline source map', + () => { + const {decorationAnalyses, renderer, switchMarkerAnalyses} = createTestRenderer( + 'test-package', [{ + ...INPUT_PROGRAM, + contents: INPUT_PROGRAM.contents + '\n' + INPUT_PROGRAM_MAP.toComment() + }]); + const result = renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); + expect(result[0].path).toEqual('/dist/file.js'); + expect(result[0].contents) + .toEqual(RENDERED_CONTENTS + '\n' + MERGED_OUTPUT_PROGRAM_MAP.toComment()); + expect(result[1]).toBeUndefined(); + }); - describe('@angular/core support', () => { + it('should merge any external source map from the original file and write the output to an external source map', + () => { + // Mock out reading the map file from disk + spyOn(fs, 'readFileSync').and.returnValue(INPUT_PROGRAM_MAP.toJSON()); + const {decorationAnalyses, renderer, switchMarkerAnalyses} = createTestRenderer( + 'test-package', [{ + ...INPUT_PROGRAM, + contents: INPUT_PROGRAM.contents + '\n//# sourceMappingURL=file.js.map' + }]); + const result = renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); + expect(result[0].path).toEqual('/dist/file.js'); + expect(result[0].contents) + .toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('/dist/file.js.map')); + expect(result[1].path).toEqual('/dist/file.js.map'); + expect(result[1].contents).toEqual(MERGED_OUTPUT_PROGRAM_MAP.toJSON()); + }); + }); + describe('@angular/core support', () => { it('should render relative imports in ESM bundles', () => { - const R3_SYMBOLS_FILE = { - name: '/src/r3_symbols.js', - contents: `export const NgModule = () => null;` - }; const CORE_FILE = { name: '/src/core.js', contents: `import { NgModule } from './ng_module';\nexport class MyModule {}\nMyModule.decorators = [\n { type: NgModule, args: [] }\n];\n` }; - - const {decorationAnalyses, program, renderer, switchMarkerAnalyses} = createTestRenderer( - [R3_SYMBOLS_FILE, CORE_FILE], - {isCore: true, rewriteCoreImportsTo: R3_SYMBOLS_FILE.name}); - renderer.renderProgram(program, decorationAnalyses, switchMarkerAnalyses); + const R3_SYMBOLS_FILE = { + // r3_symbols in the file name indicates that this is the path to rewrite core imports to + name: '/src/r3_symbols.js', + contents: `export const NgModule = () => null;` + }; + // The package name of `@angular/core` indicates that we are compiling the core library. + const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} = + createTestRenderer('@angular/core', [CORE_FILE, R3_SYMBOLS_FILE]); + renderer.renderProgram( + decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy; expect(addDefinitionsSpy.calls.first().args[2]) .toContain(`/*@__PURE__*/ ɵngcc0.setClassMetadata(`); @@ -227,16 +236,38 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", "" export class MyModule {}\nMyModule.decorators = [\n { type: NgModule, args: [] }\n];\n` }; - const {decorationAnalyses, program, renderer, switchMarkerAnalyses} = - createTestRenderer([CORE_FILE], {isCore: true}); - renderer.renderProgram(program, decorationAnalyses, switchMarkerAnalyses); + const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} = + createTestRenderer('@angular/core', [CORE_FILE]); + renderer.renderProgram( + decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy; expect(addDefinitionsSpy.calls.first().args[2]) .toContain(`/*@__PURE__*/ setClassMetadata(`); const addImportsSpy = renderer.addImports as jasmine.Spy; expect(addImportsSpy.calls.first().args[1]).toEqual([]); }); + }); + + describe('rendering typings', () => { + it('should render extract types into typings files', () => { + const {renderer, decorationAnalyses, switchMarkerAnalyses} = + createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM); + const result = renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); + const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !; + expect(typingsFile.contents) + .toContain( + 'foo(x: number): number;\n static ngDirectiveDef: ɵngcc0.ɵDirectiveDefWithMeta'); + }); + + it('should render imports into typings files', () => { + const {renderer, decorationAnalyses, switchMarkerAnalyses} = + createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM); + const result = renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); + + const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !; + expect(typingsFile.contents).toContain(`// ADD IMPORTS\nexport declare class A`); + }); }); }); }); From 8111d4b9f6a3ac4b4d59b7a8d16a70b07c07c9de Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 21 Nov 2018 09:05:27 +0000 Subject: [PATCH 09/13] feat(ivy): ngcc - add `PrivateDeclarationsAnalyzer` This analyzer searches the source for declared classes that are not exported publicly from the entry-point. --- .../analysis/private_declarations_analyzer.ts | 60 +++++++ .../private_declarations_analyzer_spec.ts | 158 ++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 packages/compiler-cli/src/ngcc/src/analysis/private_declarations_analyzer.ts create mode 100644 packages/compiler-cli/src/ngcc/test/analysis/private_declarations_analyzer_spec.ts diff --git a/packages/compiler-cli/src/ngcc/src/analysis/private_declarations_analyzer.ts b/packages/compiler-cli/src/ngcc/src/analysis/private_declarations_analyzer.ts new file mode 100644 index 000000000000..26d3f839a0a8 --- /dev/null +++ b/packages/compiler-cli/src/ngcc/src/analysis/private_declarations_analyzer.ts @@ -0,0 +1,60 @@ +/** + * @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'; + +import {ReferencesRegistry} from '../../../ngtsc/annotations'; +import {Declaration} from '../../../ngtsc/host'; +import {NgccReflectionHost} from '../host/ngcc_host'; +import {hasNameIdentifier, isDefined} from '../utils'; + +export interface ExportInfo { + identifier: string; + from: string; + dtsFrom: string|null; +} +export type PrivateDeclarationsAnalyses = ExportInfo[]; + +/** + * This class will analyze a program to find all the declared classes + * (i.e. on an NgModule) that are not publicly exported via an entry-point. + */ +export class PrivateDeclarationsAnalyzer { + constructor(private host: NgccReflectionHost, private referencesRegistry: ReferencesRegistry) {} + + analyzeProgram(program: ts.Program): PrivateDeclarationsAnalyses { + const rootFiles = this.getRootFiles(program); + return this.getPrivateDeclarations(rootFiles, this.referencesRegistry.getDeclarationMap()); + } + + private getRootFiles(program: ts.Program): ts.SourceFile[] { + return program.getRootFileNames().map(f => program.getSourceFile(f)).filter(isDefined); + } + + private getPrivateDeclarations( + rootFiles: ts.SourceFile[], + declarations: Map): PrivateDeclarationsAnalyses { + const privateDeclarations: Map = new Map(declarations); + rootFiles.forEach(f => { + const exports = this.host.getExportsOfModule(f); + if (exports) { + exports.forEach((declaration, exportedName) => { + if (hasNameIdentifier(declaration.node) && declaration.node.name.text === exportedName) { + privateDeclarations.delete(declaration.node.name); + } + }); + } + }); + return Array.from(privateDeclarations.keys()).map(id => { + const from = id.getSourceFile().fileName; + const declaration = privateDeclarations.get(id) !; + const dtsDeclaration = this.host.getDtsDeclarationOfClass(declaration.node); + const dtsFrom = dtsDeclaration && dtsDeclaration.getSourceFile().fileName; + return {identifier: id.text, from, dtsFrom}; + }); + } +} diff --git a/packages/compiler-cli/src/ngcc/test/analysis/private_declarations_analyzer_spec.ts b/packages/compiler-cli/src/ngcc/test/analysis/private_declarations_analyzer_spec.ts new file mode 100644 index 000000000000..9188a5344b61 --- /dev/null +++ b/packages/compiler-cli/src/ngcc/test/analysis/private_declarations_analyzer_spec.ts @@ -0,0 +1,158 @@ +/** + * @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 {ResolvedReference} from '@angular/compiler-cli/src/ngtsc/metadata'; +import * as ts from 'typescript'; + +import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; +import {PrivateDeclarationsAnalyzer} from '../../src/analysis/private_declarations_analyzer'; +import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; +import {getDeclaration, makeTestBundleProgram, makeTestProgram} from '../helpers/utils'; + +const TEST_PROGRAM = [ + { + name: '/src/entry_point.js', + isRoot: true, + contents: ` + export {PublicComponent} from './a'; + export {ModuleA} from './mod'; + export {ModuleB} from './b'; + ` + }, + { + name: '/src/a.js', + isRoot: false, + contents: ` + import {Component} from '@angular/core'; + export class PublicComponent {} + PublicComponent.decorators = [ + {type: Component, args: [{selectors: 'a', template: ''}]} + ]; + ` + }, + { + name: '/src/b.js', + isRoot: false, + contents: ` + import {Component, NgModule} from '@angular/core'; + class PrivateComponent {} + PrivateComponent.decorators = [ + {type: Component, args: [{selectors: 'b', template: ''}]} + ]; + export class ModuleB {} + ModuleB.decorators = [ + {type: NgModule, args: [{declarations: [PrivateComponent]}]} + ]; + ` + }, + { + name: '/src/c.js', + isRoot: false, + contents: ` + import {Component} from '@angular/core'; + export class InternalComponent {} + InternalComponent.decorators = [ + {type: Component, args: [{selectors: 'c', template: ''}]} + ]; + ` + }, + { + name: '/src/mod.js', + isRoot: false, + contents: ` + import {Component, NgModule} from '@angular/core'; + import {PublicComponent} from './a'; + import {ModuleB} from './b'; + import {InternalComponent} from './c'; + export class ModuleA {} + ModuleA.decorators = [ + {type: NgModule, args: [{ + declarations: [PublicComponent, InternalComponent], + imports: [ModuleB] + }]} + ]; + ` + } +]; +const TEST_DTS_PROGRAM = [ + { + name: '/typings/entry_point.d.ts', + isRoot: true, + contents: ` + export {PublicComponent} from './a'; + export {ModuleA} from './mod'; + export {ModuleB} from './b'; + ` + }, + { + name: '/typings/a.d.ts', + isRoot: false, + contents: ` + export declare class PublicComponent {} + ` + }, + { + name: '/typings/b.d.ts', + isRoot: false, + contents: ` + export declare class ModuleB {} + ` + }, + { + name: '/typings/c.d.ts', + isRoot: false, + contents: ` + export declare class InternalComponent {} + ` + }, + { + name: '/typings/mod.d.ts', + isRoot: false, + contents: ` + import {PublicComponent} from './a'; + import {ModuleB} from './b'; + import {InternalComponent} from './c'; + export declare class ModuleA {} + ` + }, +]; + +describe('PrivateDeclarationsAnalyzer', () => { + describe('analyzeProgram()', () => { + it('should find all NgModule declarations that were not publicly exported from the entry-point', + () => { + const program = makeTestProgram(...TEST_PROGRAM); + const dts = makeTestBundleProgram(TEST_DTS_PROGRAM); + const host = new Esm2015ReflectionHost(false, program.getTypeChecker(), dts); + const referencesRegistry = new NgccReferencesRegistry(host); + const analyzer = new PrivateDeclarationsAnalyzer(host, referencesRegistry); + + // Set up the registry with references - this would normally be done by the + // decoration handlers in the `DecorationAnalyzer`. + const publicComponentDeclaration = + getDeclaration(program, '/src/a.js', 'PublicComponent', ts.isClassDeclaration); + referencesRegistry.add( + new ResolvedReference(publicComponentDeclaration, publicComponentDeclaration.name !)); + const privateComponentDeclaration = + getDeclaration(program, '/src/b.js', 'PrivateComponent', ts.isClassDeclaration); + referencesRegistry.add(new ResolvedReference( + privateComponentDeclaration, privateComponentDeclaration.name !)); + const internalComponentDeclaration = + getDeclaration(program, '/src/c.js', 'InternalComponent', ts.isClassDeclaration); + referencesRegistry.add(new ResolvedReference( + internalComponentDeclaration, internalComponentDeclaration.name !)); + + const analyses = analyzer.analyzeProgram(program); + expect(analyses.length).toEqual(2); + expect(analyses).toEqual([ + {identifier: 'PrivateComponent', from: '/src/b.js', dtsFrom: null}, + {identifier: 'InternalComponent', from: '/src/c.js', dtsFrom: '/typings/c.d.ts'}, + ]); + }); + }); +}); From 9b5366e0f870454773d120650664da4c6786de82 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sun, 25 Nov 2018 22:07:51 +0000 Subject: [PATCH 10/13] feat(ivy): ngcc - render private declaration exports Ngcc will now render additional exports for classes that are referenced in `NgModule` decorated classes, but which were not publicly exported from an entry-point of the package. This is important because when ngtsc compiles libraries processed by ngcc it needs to be able to publcly access decorated classes that are referenced by `NgModule` decorated classes in order to build templates that use these classes. Doing this re-exporting is not without its risks. There are chances that the class is not exported correctly: there may already be similarly named exports from the entry-point or the class may be being aliased. But there is not much more we can do from the point of view of ngcc to workaround such scenarios. Generally, packages should have been built so that this approach works. --- .../src/ngcc/src/packages/transformer.ts | 12 ++- .../src/ngcc/src/rendering/esm_renderer.ts | 17 +++- .../src/ngcc/src/rendering/renderer.ts | 44 +++++++-- .../test/rendering/esm2015_renderer_spec.ts | 23 ++++- .../ngcc/test/rendering/esm5_renderer_spec.ts | 21 ++++- .../src/ngcc/test/rendering/renderer_spec.ts | 92 +++++++++++++------ 6 files changed, 165 insertions(+), 44 deletions(-) diff --git a/packages/compiler-cli/src/ngcc/src/packages/transformer.ts b/packages/compiler-cli/src/ngcc/src/packages/transformer.ts index 3de2e644fb46..ce9e389290e8 100644 --- a/packages/compiler-cli/src/ngcc/src/packages/transformer.ts +++ b/packages/compiler-cli/src/ngcc/src/packages/transformer.ts @@ -11,6 +11,7 @@ import {mkdir, mv} from 'shelljs'; import {DecorationAnalyzer} from '../analysis/decoration_analyzer'; import {NgccReferencesRegistry} from '../analysis/ngcc_references_registry'; +import {PrivateDeclarationsAnalyzer} from '../analysis/private_declarations_analyzer'; import {SwitchMarkerAnalyzer} from '../analysis/switch_marker_analyzer'; import {Esm2015ReflectionHost} from '../host/esm2015_host'; import {Esm5ReflectionHost} from '../host/esm5_host'; @@ -62,12 +63,13 @@ export class Transformer { const reflectionHost = this.getHost(isCore, bundle); // Parse and analyze the files. - const {decorationAnalyses, switchMarkerAnalyses} = + const {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} = this.analyzeProgram(reflectionHost, isCore, bundle); // Transform the source files and source maps. const renderer = this.getRenderer(reflectionHost, isCore, bundle); - const renderedFiles = renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); + const renderedFiles = renderer.renderProgram( + decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); // Write out all the transformed files. renderedFiles.forEach(file => this.writeFile(file)); @@ -109,9 +111,13 @@ export class Transformer { const decorationAnalyzer = new DecorationAnalyzer( typeChecker, reflectionHost, referencesRegistry, bundle.rootDirs, isCore); const switchMarkerAnalyzer = new SwitchMarkerAnalyzer(reflectionHost); + const privateDeclarationsAnalyzer = + new PrivateDeclarationsAnalyzer(reflectionHost, referencesRegistry); const decorationAnalyses = decorationAnalyzer.analyzeProgram(bundle.src.program); const switchMarkerAnalyses = switchMarkerAnalyzer.analyzeProgram(bundle.src.program); - return {decorationAnalyses, switchMarkerAnalyses}; + const privateDeclarationsAnalyses = + privateDeclarationsAnalyzer.analyzeProgram(bundle.src.program); + return {decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses}; } writeFile(file: FileInfo): void { 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 60482ab43eda..578641c36a7a 100644 --- a/packages/compiler-cli/src/ngcc/src/rendering/esm_renderer.ts +++ b/packages/compiler-cli/src/ngcc/src/rendering/esm_renderer.ts @@ -5,10 +5,12 @@ * 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'; +import {dirname, relative} from 'canonical-path'; import MagicString from 'magic-string'; +import * as ts from 'typescript'; import {NgccReflectionHost, POST_R3_MARKER, PRE_R3_MARKER, SwitchableVariableDeclaration} from '../host/ngcc_host'; import {CompiledClass} from '../analysis/decoration_analyzer'; +import {ExportInfo} from '../analysis/private_declarations_analyzer'; import {Renderer, stripExtension} from './renderer'; import {EntryPointBundle} from '../packages/entry_point_bundle'; @@ -27,6 +29,19 @@ export class EsmRenderer extends Renderer { imports.forEach(i => { output.appendLeft(0, `import * as ${i.as} from '${i.name}';\n`); }); } + addExports(output: MagicString, entryPointBasePath: string, exports: { + identifier: string, + from: string + }[]): void { + exports.forEach(e => { + const basePath = stripExtension(e.from); + const relativePath = './' + relative(dirname(entryPointBasePath), basePath); + const exportFrom = entryPointBasePath !== basePath ? ` from '${relativePath}'` : ''; + const exportStr = `\nexport {${e.identifier}}${exportFrom};`; + output.append(exportStr); + }); + } + addConstants(output: MagicString, constants: string, file: ts.SourceFile): void { if (constants === '') { return; diff --git a/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts b/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts index 4d1552b4523a..71e851d44ff2 100644 --- a/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts +++ b/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts @@ -18,6 +18,7 @@ import {CompileResult} from '@angular/compiler-cli/src/ngtsc/transform'; import {translateStatement, translateType} from '../../../ngtsc/translator'; import {NgccImportManager} from './ngcc_import_manager'; import {CompiledClass, CompiledFile, DecorationAnalyses} from '../analysis/decoration_analyzer'; +import {PrivateDeclarationsAnalyses, ExportInfo} from '../analysis/private_declarations_analyzer'; import {SwitchMarkerAnalyses, SwitchMarkerAnalysis} from '../analysis/switch_marker_analyzer'; import {IMPORT_PREFIX} from '../constants'; import {NgccReflectionHost, SwitchableVariableDeclaration} from '../host/ngcc_host'; @@ -60,8 +61,9 @@ export abstract class Renderer { protected bundle: EntryPointBundle, protected sourcePath: string, protected targetPath: string) {} - renderProgram(decorationAnalyses: DecorationAnalyses, switchMarkerAnalyses: SwitchMarkerAnalyses): - FileInfo[] { + renderProgram( + decorationAnalyses: DecorationAnalyses, switchMarkerAnalyses: SwitchMarkerAnalyses, + privateDeclarationsAnalyses: PrivateDeclarationsAnalyses): FileInfo[] { const renderedFiles: FileInfo[] = []; // Transform the source files. @@ -70,7 +72,8 @@ export abstract class Renderer { const switchMarkerAnalysis = switchMarkerAnalyses.get(sourceFile); if (compiledFile || switchMarkerAnalysis || sourceFile === this.bundle.src.file) { - renderedFiles.push(...this.renderFile(sourceFile, compiledFile, switchMarkerAnalysis)); + renderedFiles.push(...this.renderFile( + sourceFile, compiledFile, switchMarkerAnalysis, privateDeclarationsAnalyses)); } }); @@ -83,7 +86,9 @@ export abstract class Renderer { if (!dtsFiles.has(this.bundle.dts.file)) { dtsFiles.set(this.bundle.dts.file, []); } - dtsFiles.forEach((classes, file) => renderedFiles.push(...this.renderDtsFile(file, classes))); + dtsFiles.forEach( + (classes, file) => renderedFiles.push( + ...this.renderDtsFile(file, classes, privateDeclarationsAnalyses))); } return renderedFiles; @@ -96,7 +101,8 @@ export abstract class Renderer { */ renderFile( sourceFile: ts.SourceFile, compiledFile: CompiledFile|undefined, - switchMarkerAnalysis: SwitchMarkerAnalysis|undefined): FileInfo[] { + switchMarkerAnalysis: SwitchMarkerAnalysis|undefined, + privateDeclarationsAnalyses: PrivateDeclarationsAnalyses): FileInfo[] { const input = this.extractSourceMap(sourceFile); const outputText = new MagicString(input.source); @@ -129,10 +135,18 @@ export abstract class Renderer { this.removeDecorators(outputText, decoratorsToRemove); } + // Add exports to the entry-point file + if (sourceFile === this.bundle.src.file) { + const entryPointBasePath = stripExtension(this.bundle.src.path); + this.addExports(outputText, entryPointBasePath, privateDeclarationsAnalyses); + } + return this.renderSourceAndMap(sourceFile, input, outputText); } - renderDtsFile(dtsFile: ts.SourceFile, dtsClasses: DtsClassInfo[]): FileInfo[] { + renderDtsFile( + dtsFile: ts.SourceFile, dtsClasses: DtsClassInfo[], + privateDeclarationsAnalyses: PrivateDeclarationsAnalyses): FileInfo[] { const input = this.extractSourceMap(dtsFile); const outputText = new MagicString(input.source); const importManager = new NgccImportManager(false, this.isCore, IMPORT_PREFIX); @@ -149,12 +163,30 @@ export abstract class Renderer { this.addImports( outputText, importManager.getAllImports(dtsFile.fileName, this.bundle.dts !.r3SymbolsFile)); + if (dtsFile === this.bundle.dts !.file) { + const dtsExports = privateDeclarationsAnalyses.map(e => { + if (!e.dtsFrom) { + throw new Error( + `There is no typings path for ${e.identifier} in ${e.from}.\n` + + `We need to add an export for this class to a .d.ts typings file because ` + + `Angular compiler needs to be able to reference this class in compiled code, such as templates.\n` + + `The simplest fix for this is to ensure that this class is exported from the package's entry-point.`); + } + return {identifier: e.identifier, from: e.dtsFrom}; + }); + this.addExports(outputText, dtsFile.fileName, dtsExports); + } + return this.renderSourceAndMap(dtsFile, input, outputText); } protected abstract addConstants(output: MagicString, constants: string, file: ts.SourceFile): void; protected abstract addImports(output: MagicString, imports: {name: string, as: string}[]): void; + protected abstract addExports(output: MagicString, entryPointBasePath: string, exports: { + identifier: string, + from: string + }[]): void; protected abstract addDefinitions( output: MagicString, compiledClass: CompiledClass, definitions: string): void; protected abstract removeDecorators( 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 5e874c7c7897..8e91618073c5 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 @@ -6,15 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ import {dirname} from 'canonical-path'; -import * as ts from 'typescript'; import MagicString from 'magic-string'; - -import {makeTestEntryPointBundle} from '../helpers/utils'; +import * as ts from 'typescript'; import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; import {EsmRenderer} from '../../src/rendering/esm_renderer'; +import {makeTestEntryPointBundle} from '../helpers/utils'; function setup(file: {name: string, contents: string}) { const dir = dirname(file.name); @@ -122,6 +121,24 @@ import * as i1 from '@angular/common'; }); }); + describe('addExports', () => { + it('should insert the given exports at the end of the source file', () => { + const {renderer} = setup(PROGRAM); + const output = new MagicString(PROGRAM.contents); + renderer.addExports(output, PROGRAM.name.replace(/\.js$/, ''), [ + {from: '/some/a.js', identifier: 'ComponentA1'}, + {from: '/some/a.js', identifier: 'ComponentA2'}, + {from: '/some/foo/b.js', identifier: 'ComponentB'}, + {from: PROGRAM.name, identifier: 'TopLevelComponent'}, + ]); + expect(output.toString()).toContain(` +// Some other content +export {ComponentA1} from './a'; +export {ComponentA2} from './a'; +export {ComponentB} from './foo/b'; +export {TopLevelComponent};`); + }); + }); describe('addConstants', () => { it('should insert the given constants after imports in the source file', () => { 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 f36c17d649cb..24e8db34ac12 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 @@ -8,13 +8,12 @@ import {dirname} from 'canonical-path'; import MagicString from 'magic-string'; import * as ts from 'typescript'; - -import {makeTestEntryPointBundle, getDeclaration} from '../helpers/utils'; import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer'; import {Esm5ReflectionHost} from '../../src/host/esm5_host'; import {Esm5Renderer} from '../../src/rendering/esm5_renderer'; +import {makeTestEntryPointBundle, getDeclaration} from '../helpers/utils'; function setup(file: {name: string, contents: string}) { const dir = dirname(file.name); @@ -159,6 +158,24 @@ import * as i1 from '@angular/common'; }); }); + describe('addExports', () => { + it('should insert the given exports at the end of the source file', () => { + const {renderer} = setup(PROGRAM); + const output = new MagicString(PROGRAM.contents); + renderer.addExports(output, PROGRAM.name.replace(/\.js$/, ''), [ + {from: '/some/a.js', identifier: 'ComponentA1'}, + {from: '/some/a.js', identifier: 'ComponentA2'}, + {from: '/some/foo/b.js', identifier: 'ComponentB'}, + {from: PROGRAM.name, identifier: 'TopLevelComponent'}, + ]); + expect(output.toString()).toContain(` +export {A, B, C, NoIife, BadIife}; +export {ComponentA1} from './a'; +export {ComponentA2} from './a'; +export {ComponentB} from './foo/b'; +export {TopLevelComponent};`); + }); + }); describe('addConstants', () => { it('should insert the given constants after imports in the source file', () => { 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 c1682e78f56e..e27c1f45146e 100644 --- a/packages/compiler-cli/src/ngcc/test/rendering/renderer_spec.ts +++ b/packages/compiler-cli/src/ngcc/test/rendering/renderer_spec.ts @@ -11,10 +11,10 @@ import * as ts from 'typescript'; import {fromObject, generateMapFileComment} from 'convert-source-map'; import {CompiledClass, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer'; import {NgccReferencesRegistry} from '../../src/analysis/ngcc_references_registry'; +import {PrivateDeclarationsAnalyzer} from '../../src/analysis/private_declarations_analyzer'; import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer'; import {Esm2015ReflectionHost} from '../../src/host/esm2015_host'; import {Renderer} from '../../src/rendering/renderer'; -import {EntryPoint} from '../../src/packages/entry_point'; import {EntryPointBundle} from '../../src/packages/entry_point_bundle'; import {makeTestEntryPointBundle} from '../helpers/utils'; @@ -25,6 +25,12 @@ class TestRenderer extends Renderer { addImports(output: MagicString, imports: {name: string, as: string}[]) { output.prepend('\n// ADD IMPORTS\n'); } + addExports(output: MagicString, baseEntryPointPath: string, exports: { + identifier: string, + from: string + }[]) { + output.prepend('\n// ADD EXPORTS\n'); + } addConstants(output: MagicString, constants: string, file: ts.SourceFile): void { output.prepend('\n// ADD CONSTANTS\n'); } @@ -51,12 +57,14 @@ function createTestRenderer( new DecorationAnalyzer(typeChecker, host, referencesRegistry, bundle.rootDirs, isCore) .analyzeProgram(bundle.src.program); const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(bundle.src.program); + const privateDeclarationsAnalyses = + new PrivateDeclarationsAnalyzer(host, referencesRegistry).analyzeProgram(bundle.src.program); const renderer = new TestRenderer(host, isCore, bundle); spyOn(renderer, 'addImports').and.callThrough(); spyOn(renderer, 'addDefinitions').and.callThrough(); spyOn(renderer, 'removeDecorators').and.callThrough(); - return {renderer, decorationAnalyses, switchMarkerAnalyses}; + return {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses}; } @@ -83,7 +91,7 @@ describe('Renderer', () => { }); const RENDERED_CONTENTS = - `\n// REMOVE DECORATORS\n\n// ADD IMPORTS\n\n// ADD CONSTANTS\n\n// ADD DEFINITIONS\n` + + `\n// ADD EXPORTS\n\n// REMOVE DECORATORS\n\n// ADD IMPORTS\n\n// ADD CONSTANTS\n\n// ADD DEFINITIONS\n` + INPUT_PROGRAM.contents; const OUTPUT_PROGRAM_MAP = fromObject({ @@ -92,14 +100,14 @@ describe('Renderer', () => { 'sources': ['/src/file.js'], 'sourcesContent': [INPUT_PROGRAM.contents], 'names': [], - 'mappings': ';;;;;;;;AAAA;;;;;;;;;' + 'mappings': ';;;;;;;;;;AAAA;;;;;;;;;' }); const MERGED_OUTPUT_PROGRAM_MAP = fromObject({ 'version': 3, 'sources': ['/src/file.ts'], 'names': [], - 'mappings': ';;;;;;;;AAAA', + 'mappings': ';;;;;;;;;;AAAA', 'file': '/dist/file.js', 'sourcesContent': [INPUT_PROGRAM.contents] }); @@ -107,9 +115,10 @@ describe('Renderer', () => { describe('renderProgram()', () => { it('should render the modified contents; and a new map file, if the original provided no map file.', () => { - const {renderer, decorationAnalyses, switchMarkerAnalyses} = + const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} = createTestRenderer('test-package', [INPUT_PROGRAM]); - const result = renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); + const result = renderer.renderProgram( + decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); expect(result[0].path).toEqual('/dist/file.js'); expect(result[0].contents) .toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('/dist/file.js.map')); @@ -120,9 +129,10 @@ describe('Renderer', () => { describe('calling abstract methods', () => { it('should call addImports with the source code and info about the core Angular library.', () => { - const {decorationAnalyses, renderer, switchMarkerAnalyses} = + const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} = createTestRenderer('test-package', [INPUT_PROGRAM]); - renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); + const result = renderer.renderProgram( + decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addImportsSpy = renderer.addImports as jasmine.Spy; expect(addImportsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS); expect(addImportsSpy.calls.first().args[1]).toEqual([ @@ -132,9 +142,10 @@ describe('Renderer', () => { it('should call addDefinitions with the source code, the analyzed class and the rendered definitions.', () => { - const {decorationAnalyses, renderer, switchMarkerAnalyses} = + const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} = createTestRenderer('test-package', [INPUT_PROGRAM]); - renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); + const result = renderer.renderProgram( + decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const addDefinitionsSpy = renderer.addDefinitions as jasmine.Spy; expect(addDefinitionsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS); expect(addDefinitionsSpy.calls.first().args[1]).toEqual(jasmine.objectContaining({ @@ -151,9 +162,10 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", "" it('should call removeDecorators with the source code, a map of class decorators that have been analyzed', () => { - const {decorationAnalyses, renderer, switchMarkerAnalyses} = + const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} = createTestRenderer('test-package', [INPUT_PROGRAM]); - renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); + const result = renderer.renderProgram( + decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const removeDecoratorsSpy = renderer.removeDecorators as jasmine.Spy; expect(removeDecoratorsSpy.calls.first().args[0].toString()).toEqual(RENDERED_CONTENTS); @@ -175,12 +187,14 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", "" describe('source map merging', () => { it('should merge any inline source map from the original file and write the output as an inline source map', () => { - const {decorationAnalyses, renderer, switchMarkerAnalyses} = createTestRenderer( - 'test-package', [{ - ...INPUT_PROGRAM, - contents: INPUT_PROGRAM.contents + '\n' + INPUT_PROGRAM_MAP.toComment() - }]); - const result = renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); + const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} = + createTestRenderer( + 'test-package', [{ + ...INPUT_PROGRAM, + contents: INPUT_PROGRAM.contents + '\n' + INPUT_PROGRAM_MAP.toComment() + }]); + const result = renderer.renderProgram( + decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); expect(result[0].path).toEqual('/dist/file.js'); expect(result[0].contents) .toEqual(RENDERED_CONTENTS + '\n' + MERGED_OUTPUT_PROGRAM_MAP.toComment()); @@ -191,12 +205,14 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", "" () => { // Mock out reading the map file from disk spyOn(fs, 'readFileSync').and.returnValue(INPUT_PROGRAM_MAP.toJSON()); - const {decorationAnalyses, renderer, switchMarkerAnalyses} = createTestRenderer( - 'test-package', [{ - ...INPUT_PROGRAM, - contents: INPUT_PROGRAM.contents + '\n//# sourceMappingURL=file.js.map' - }]); - const result = renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); + const {decorationAnalyses, renderer, switchMarkerAnalyses, privateDeclarationsAnalyses} = + createTestRenderer( + 'test-package', [{ + ...INPUT_PROGRAM, + contents: INPUT_PROGRAM.contents + '\n//# sourceMappingURL=file.js.map' + }]); + const result = renderer.renderProgram( + decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); expect(result[0].path).toEqual('/dist/file.js'); expect(result[0].contents) .toEqual(RENDERED_CONTENTS + '\n' + generateMapFileComment('/dist/file.js.map')); @@ -250,9 +266,10 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", "" describe('rendering typings', () => { it('should render extract types into typings files', () => { - const {renderer, decorationAnalyses, switchMarkerAnalyses} = + const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} = createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM); - const result = renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); + const result = renderer.renderProgram( + decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !; expect(typingsFile.contents) @@ -261,13 +278,30 @@ A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", "" }); it('should render imports into typings files', () => { - const {renderer, decorationAnalyses, switchMarkerAnalyses} = + const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} = createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM); - const result = renderer.renderProgram(decorationAnalyses, switchMarkerAnalyses); + const result = renderer.renderProgram( + decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !; expect(typingsFile.contents).toContain(`// ADD IMPORTS\nexport declare class A`); }); + + it('should render exports into typings files', () => { + const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses} = + createTestRenderer('test-package', [INPUT_PROGRAM], INPUT_DTS_PROGRAM); + + // Add a mock export to trigger export rendering + privateDeclarationsAnalyses.push( + {identifier: 'ComponentB', from: '/src/file.js', dtsFrom: '/typings/b.d.ts'}); + + const result = renderer.renderProgram( + decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses); + + const typingsFile = result.find(f => f.path === '/typings/file.d.ts') !; + expect(typingsFile.contents) + .toContain(`// ADD EXPORTS\n\n// ADD IMPORTS\nexport declare class A`); + }); }); }); }); From 51f490daf33d796f69bc771a6e2a11cddfc0788e Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Mon, 26 Nov 2018 09:31:01 +0000 Subject: [PATCH 11/13] test(ivy): ngcc - update integration test dependency lock file --- integration/ngcc/yarn.lock | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/integration/ngcc/yarn.lock b/integration/ngcc/yarn.lock index ca538d725fe4..7f24bb30513c 100644 --- a/integration/ngcc/yarn.lock +++ b/integration/ngcc/yarn.lock @@ -3,7 +3,7 @@ "@angular/animations@file:../../dist/packages-dist/animations": - version "7.1.0-beta.1" + version "7.1.0" dependencies: tslib "^1.9.0" @@ -17,14 +17,14 @@ parse5 "^5.0.0" "@angular/common@file:../../dist/packages-dist/common": - version "7.1.0-beta.1" + version "7.1.0" dependencies: tslib "^1.9.0" "@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli": - version "7.1.0-beta.1" + version "7.1.0" dependencies: - canonical-path "0.0.2" + canonical-path "1.0.0" chokidar "^1.4.2" convert-source-map "^1.5.1" dependency-graph "^0.7.2" @@ -33,25 +33,26 @@ reflect-metadata "^0.1.2" shelljs "^0.8.1" source-map "^0.6.1" + tslib "^1.9.0" yargs "9.0.1" "@angular/compiler@file:../../dist/packages-dist/compiler": - version "7.1.0-beta.1" + version "7.1.0" dependencies: tslib "^1.9.0" "@angular/core@file:../../dist/packages-dist/core": - version "7.1.0-beta.1" + version "7.1.0" dependencies: tslib "^1.9.0" "@angular/forms@file:../../dist/packages-dist/forms": - version "7.1.0-beta.1" + version "7.1.0" dependencies: tslib "^1.9.0" "@angular/http@file:../../dist/packages-dist/http": - version "7.1.0-beta.1" + version "7.1.0" dependencies: tslib "^1.9.0" @@ -65,17 +66,17 @@ parse5 "^5.0.0" "@angular/platform-browser-dynamic@file:../../dist/packages-dist/platform-browser-dynamic": - version "7.1.0-beta.1" + version "7.1.0" dependencies: tslib "^1.9.0" "@angular/platform-browser@file:../../dist/packages-dist/platform-browser": - version "7.1.0-beta.1" + version "7.1.0" dependencies: tslib "^1.9.0" "@angular/router@file:../../dist/packages-dist/router": - version "7.1.0-beta.1" + version "7.1.0" dependencies: tslib "^1.9.0" @@ -533,10 +534,10 @@ camelcase@^4.1.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= -canonical-path@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-0.0.2.tgz#e31eb937a8c93ee2a01df1839794721902874574" - integrity sha1-4x65N6jJPuKgHfGDl5RyGQKHRXQ= +canonical-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-1.0.0.tgz#fcb470c23958def85081856be7a86e904f180d1d" + integrity sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg== caseless@~0.12.0: version "0.12.0" From 40315aa8c1e3c8cd27b23437e02a11bd27b8b35e Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Mon, 26 Nov 2018 22:31:16 +0000 Subject: [PATCH 12/13] fixup! refactor(ivy): ngcc - encapsulate variables into "bundles" --- packages/compiler-cli/src/ngcc/src/rendering/esm5_renderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 21a31996fbe7..7b2b3be1e7bd 100644 --- a/packages/compiler-cli/src/ngcc/src/rendering/esm5_renderer.ts +++ b/packages/compiler-cli/src/ngcc/src/rendering/esm5_renderer.ts @@ -14,7 +14,7 @@ import {EntryPointBundle} from '../packages/entry_point_bundle'; export class Esm5Renderer extends EsmRenderer { constructor( - protected host: NgccReflectionHost, isCore: boolean, bundle: EntryPointBundle, + host: NgccReflectionHost, isCore: boolean, bundle: EntryPointBundle, sourcePath: string, targetPath: string) { super(host, isCore, bundle, sourcePath, targetPath); } From c1139c02704b9a1a566add58ae2226e3abebb984 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Tue, 27 Nov 2018 08:02:07 +0000 Subject: [PATCH 13/13] fixup! refactor(ivy): ngcc - encapsulate variables into "bundles" --- packages/compiler-cli/src/ngcc/src/rendering/esm5_renderer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 7b2b3be1e7bd..c412b0a71bda 100644 --- a/packages/compiler-cli/src/ngcc/src/rendering/esm5_renderer.ts +++ b/packages/compiler-cli/src/ngcc/src/rendering/esm5_renderer.ts @@ -14,8 +14,8 @@ import {EntryPointBundle} from '../packages/entry_point_bundle'; export class Esm5Renderer extends EsmRenderer { constructor( - host: NgccReflectionHost, isCore: boolean, bundle: EntryPointBundle, - sourcePath: string, targetPath: string) { + host: NgccReflectionHost, isCore: boolean, bundle: EntryPointBundle, sourcePath: string, + targetPath: string) { super(host, isCore, bundle, sourcePath, targetPath); }