From b55e1c2ba9d04932e91c5bf9abae337e1ccd297c Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sun, 25 Nov 2018 21:40:25 +0000 Subject: [PATCH] refactor(ivy): ngcc - encapsulate variables into "bundles" (#26906) 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. PR Close #26906 --- .../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 | 243 ++++++++++-------- 19 files changed, 608 insertions(+), 499 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 4e05b3b074..a4d12aa420 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 35f003c2f5..924fc42232 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 872e8ad213..0000000000 --- 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 0000000000..9bbcd8abb8 --- /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 0000000000..3dbbaee262 --- /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 8bc9092034..3de2e644fb 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} - ${bundle.format}`); - console.warn(`Compiling ${entryPoint.name} - ${format}`); - - const options: ts.CompilerOptions = { - allowJs: true, - maxNodeModuleJsDepth: Infinity, - rootDir: entryPoint.path, - }; - - // 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 44e6ae2c49..c412b0a71b 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); + 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 fb9c527c8c..60482ab43e 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 7e8c7ee85f..4d1552b452 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 89868d3b62..1978ba02c8 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 b97daa07ee..ff27fd9d1c 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 705f2e2669..1d466683ab 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 51b2120b8f..1e38b6eb12 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 2878872ec2..790d610cd9 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 60aca577a3..e696d5675f 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 4c6ddd0014..6479edecd4 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 7d51ce172e..5e874c7c78 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 c04bbee610..f36c17d649 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 56806524ae..c1682e78f5 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); + 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); - // 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]' }] }`); - }); + // 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 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(); - }); + 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(); + }); - 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()); - }); + 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`); + }); }); }); });