diff --git a/packages/compiler-cli/src/ngcc/src/analysis/decoration_analyzer.ts b/packages/compiler-cli/src/ngcc/src/analysis/decoration_analyzer.ts
index e83a953710..75fb58bfc5 100644
--- a/packages/compiler-cli/src/ngcc/src/analysis/decoration_analyzer.ts
+++ b/packages/compiler-cli/src/ngcc/src/analysis/decoration_analyzer.ts
@@ -13,24 +13,29 @@ import {BaseDefDecoratorHandler, ComponentDecoratorHandler, DirectiveDecoratorHa
import {CompileResult, DecoratorHandler} from '../../../ngtsc/transform';
import {DecoratedClass} from '../host/decorated_class';
-import {DecoratedFile} from '../host/decorated_file';
import {NgccReflectionHost} from '../host/ngcc_host';
import {isDefined} from '../utils';
-export interface AnalyzedClass extends DecoratedClass {
- handler: DecoratorHandler;
- analysis: any;
- diagnostics?: ts.Diagnostic[];
- compilation: CompileResult[];
+export interface AnalyzedFile {
+ sourceFile: ts.SourceFile;
+ analyzedClasses: AnalyzedClass[];
}
-export interface DecorationAnalysis {
- analyzedClasses: AnalyzedClass[];
+export interface AnalyzedClass extends DecoratedClass {
+ diagnostics?: ts.Diagnostic[];
+ handler: DecoratorHandler;
+ analysis: any;
+}
+
+export interface CompiledClass extends AnalyzedClass { compilation: CompileResult[]; }
+
+export interface CompiledFile {
+ compiledClasses: CompiledClass[];
sourceFile: ts.SourceFile;
constantPool: ConstantPool;
}
-export type DecorationAnalyses = Map;
+export type DecorationAnalyses = Map;
export const DecorationAnalyses = Map;
export interface MatchingHandler {
@@ -72,58 +77,59 @@ export class DecorationAnalyzer {
* @returns a map of the source files to the analysis for those files.
*/
analyzeProgram(program: ts.Program): DecorationAnalyses {
- const analyzedFiles = new DecorationAnalyses();
- program.getRootFileNames().forEach(fileName => {
- const entryPoint = program.getSourceFile(fileName) !;
- const decoratedFiles = this.host.findDecoratedFiles(entryPoint);
- decoratedFiles.forEach(
- decoratedFile =>
- analyzedFiles.set(decoratedFile.sourceFile, this.analyzeFile(decoratedFile)));
- });
- return analyzedFiles;
+ const decorationAnalyses = new DecorationAnalyses();
+ const analysedFiles =
+ program.getSourceFiles().map(sourceFile => this.analyzeFile(sourceFile)).filter(isDefined);
+ const compiledFiles = analysedFiles.map(analysedFile => this.compileFile(analysedFile));
+ compiledFiles.forEach(
+ compiledFile => decorationAnalyses.set(compiledFile.sourceFile, compiledFile));
+ return decorationAnalyses;
}
- /**
- * Analyze a decorated file to generate the information about decorated classes that
- * should be converted to use ivy definitions.
- * @param file The file to be analysed for decorated classes.
- * @returns the analysis of the file
- */
- protected analyzeFile(file: DecoratedFile): DecorationAnalysis {
- const constantPool = new ConstantPool();
- const analyzedClasses =
- file.decoratedClasses.map(clazz => this.analyzeClass(constantPool, clazz))
- .filter(isDefined);
-
- return {
- analyzedClasses,
- sourceFile: file.sourceFile, constantPool,
- };
+ protected analyzeFile(sourceFile: ts.SourceFile): AnalyzedFile|undefined {
+ const decoratedClasses = this.host.findDecoratedClasses(sourceFile);
+ return decoratedClasses.length ? {
+ sourceFile,
+ analyzedClasses: decoratedClasses.map(clazz => this.analyzeClass(clazz)).filter(isDefined)
+ } :
+ undefined;
}
- protected analyzeClass(pool: ConstantPool, clazz: DecoratedClass): AnalyzedClass|undefined {
+ protected analyzeClass(clazz: DecoratedClass): AnalyzedClass|null {
const matchingHandlers = this.handlers
- .map(handler => ({
- handler,
- match: handler.detect(clazz.declaration, clazz.decorators),
- }))
+ .map(handler => {
+ const match =
+ handler.detect(clazz.declaration, clazz.decorators);
+ return {handler, match};
+ })
.filter(isMatchingHandler);
if (matchingHandlers.length > 1) {
throw new Error('TODO.Diagnostic: Class has multiple Angular decorators.');
}
-
if (matchingHandlers.length === 0) {
- return undefined;
+ return null;
}
-
const {handler, match} = matchingHandlers[0];
const {analysis, diagnostics} = handler.analyze(clazz.declaration, match);
- let compilation = handler.compile(clazz.declaration, analysis, pool);
+ return {...clazz, handler, analysis, diagnostics};
+ }
+
+ protected compileFile(analyzedFile: AnalyzedFile): CompiledFile {
+ const constantPool = new ConstantPool();
+ const compiledClasses: CompiledClass[] = analyzedFile.analyzedClasses.map(analyzedClass => {
+ const compilation = this.compileClass(analyzedClass, constantPool);
+ return {...analyzedClass, compilation};
+ });
+ return {constantPool, sourceFile: analyzedFile.sourceFile, compiledClasses};
+ }
+
+ protected compileClass(clazz: AnalyzedClass, constantPool: ConstantPool): CompileResult[] {
+ let compilation = clazz.handler.compile(clazz.declaration, clazz.analysis, constantPool);
if (!Array.isArray(compilation)) {
compilation = [compilation];
}
- return {...clazz, handler, analysis, diagnostics, compilation};
+ return compilation;
}
}
diff --git a/packages/compiler-cli/src/ngcc/src/host/decorated_file.ts b/packages/compiler-cli/src/ngcc/src/host/decorated_file.ts
deleted file mode 100644
index 0d42307ab0..0000000000
--- a/packages/compiler-cli/src/ngcc/src/host/decorated_file.ts
+++ /dev/null
@@ -1,21 +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';
-import {DecoratedClass} from './decorated_class';
-
-/**
- * Information about a source file that contains decorated exported classes.
- */
-export class DecoratedFile {
- /**
- * The decorated exported classes that have been found in the file.
- */
- public decoratedClasses: DecoratedClass[] = [];
- constructor(public sourceFile: ts.SourceFile) {}
-}
diff --git a/packages/compiler-cli/src/ngcc/src/host/dts_mapper.ts b/packages/compiler-cli/src/ngcc/src/host/dts_mapper.ts
deleted file mode 100644
index db560d02fe..0000000000
--- a/packages/compiler-cli/src/ngcc/src/host/dts_mapper.ts
+++ /dev/null
@@ -1,32 +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 {relative, resolve} from 'canonical-path';
-
-/**
- * Map source files to their associated typings definitions files.
- */
-export class DtsMapper {
- constructor(private sourceRoot: string, private dtsRoot: string) {}
-
- /**
- * Given the absolute path to a source file, return the absolute path to the corresponding `.d.ts`
- * file. Assume that source files and `.d.ts` files have the same directory layout and the names
- * of the `.d.ts` files can be derived by replacing the `.js` extension of the source file with
- * `.d.ts`.
- *
- * @param sourceFileName The absolute path to the source file whose corresponding `.d.ts` file
- * should be returned.
- *
- * @returns The absolute path to the `.d.ts` file that corresponds to the specified source file.
- */
- getDtsFileNameFor(sourceFileName: string): string {
- const relativeSourcePath = relative(this.sourceRoot, sourceFileName);
- return resolve(this.dtsRoot, relativeSourcePath).replace(/\.js$/, '.d.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 50707b679c..a59610ee4b 100644
--- a/packages/compiler-cli/src/ngcc/src/host/esm2015_host.ts
+++ b/packages/compiler-cli/src/ngcc/src/host/esm2015_host.ts
@@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {readFileSync} from 'fs';
import * as ts from 'typescript';
import {ClassMember, ClassMemberKind, CtorParameter, Decorator, Import} from '../../../ngtsc/host';
@@ -14,8 +13,6 @@ import {TypeScriptReflectionHost, reflectObjectLiteral} from '../../../ngtsc/met
import {findAll, getNameText, getOriginalSymbol, isDefined} from '../utils';
import {DecoratedClass} from './decorated_class';
-import {DecoratedFile} from './decorated_file';
-import {DtsMapper} from './dts_mapper';
import {NgccReflectionHost, PRE_R3_MARKER, SwitchableVariableDeclaration, isSwitchableVariableDeclaration} from './ngcc_host';
export const DECORATORS = 'decorators' as ts.__String;
@@ -51,8 +48,14 @@ export const CONSTRUCTOR_PARAMS = 'ctorParameters' as ts.__String;
* a static method called `ctorParameters`.
*/
export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements NgccReflectionHost {
- constructor(protected isCore: boolean, checker: ts.TypeChecker, protected dtsMapper?: DtsMapper) {
+ protected dtsClassMap: Map|null;
+ constructor(
+ protected isCore: boolean, checker: ts.TypeChecker, dtsRootFileName?: string,
+ dtsProgram?: ts.Program|null) {
super(checker);
+ this.dtsClassMap = (dtsRootFileName && dtsProgram) ?
+ this.computeDtsClassMap(dtsRootFileName, dtsProgram) :
+ null;
}
/**
@@ -73,12 +76,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
if (!symbol) {
return null;
}
- const decoratorsProperty = this.getStaticProperty(symbol, DECORATORS);
- if (decoratorsProperty) {
- return this.getClassDecoratorsFromStaticProperty(decoratorsProperty);
- } else {
- return this.getClassDecoratorsFromHelperCall(symbol);
- }
+ return this.getDecoratorsOfSymbol(symbol);
}
/**
@@ -206,10 +204,11 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
if (ts.isClassDeclaration(declaration)) {
return declaration.name && this.checker.getSymbolAtLocation(declaration.name);
}
- if (ts.isVariableDeclaration(declaration) && declaration.initializer &&
- ts.isClassExpression(declaration.initializer)) {
- return declaration.initializer.name &&
- this.checker.getSymbolAtLocation(declaration.initializer.name);
+ if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
+ declaration = declaration.initializer;
+ }
+ if (ts.isClassExpression(declaration)) {
+ return declaration.name && this.checker.getSymbolAtLocation(declaration.name);
}
return undefined;
}
@@ -299,46 +298,29 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
return super.getImportOfIdentifier(id) || this.getImportOfNamespacedIdentifier(id);
}
- /*
- * Find all the files accessible via an entry-point, that contain decorated classes.
- * @param entryPoint The starting point file for finding files that contain decorated classes.
- * @returns A collection of files objects that hold info about the decorated classes and import
- * information.
+ /**
+ * Find all the classes that contain decorations in a given file.
+ * @param sourceFile The source file to search for decorated classes.
+ * @returns An array of decorated classes.
*/
- findDecoratedFiles(entryPoint: ts.SourceFile): Map {
- const moduleSymbol = this.checker.getSymbolAtLocation(entryPoint);
- const map = new Map();
- if (moduleSymbol) {
- const exportedSymbols =
- this.checker.getExportsOfModule(moduleSymbol).map(getOriginalSymbol(this.checker));
- const exportedDeclarations =
- exportedSymbols.map(exportSymbol => exportSymbol.valueDeclaration).filter(isDefined);
-
- const decoratedClasses =
- exportedDeclarations
- .map(declaration => {
- if (ts.isClassDeclaration(declaration) || ts.isVariableDeclaration(declaration)) {
- const name = declaration.name && ts.isIdentifier(declaration.name) ?
- declaration.name.text :
- undefined;
- const decorators = this.getDecoratorsOfDeclaration(declaration);
- return decorators && isDefined(name) ?
- new DecoratedClass(name, declaration, decorators) :
- undefined;
- }
- return undefined;
- })
- .filter(isDefined);
-
- decoratedClasses.forEach(clazz => {
- const file = clazz.declaration.getSourceFile();
- if (!map.has(file)) {
- map.set(file, new DecoratedFile(file));
+ findDecoratedClasses(sourceFile: ts.SourceFile): DecoratedClass[] {
+ const classes: DecoratedClass[] = [];
+ sourceFile.statements.map(statement => {
+ if (ts.isVariableStatement(statement)) {
+ statement.declarationList.declarations.forEach(declaration => {
+ const decoratedClass = this.getDecoratedClassFromSymbol(this.getClassSymbol(declaration));
+ if (decoratedClass) {
+ classes.push(decoratedClass);
+ }
+ });
+ } else if (ts.isClassDeclaration(statement)) {
+ const decoratedClass = this.getDecoratedClassFromSymbol(this.getClassSymbol(statement));
+ if (decoratedClass) {
+ classes.push(decoratedClass);
}
- map.get(file) !.decoratedClasses.push(clazz);
- });
- }
- return map;
+ }
+ });
+ return classes;
}
/**
@@ -348,21 +330,38 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
* is not a class or has an unknown number of type parameters.
*/
getGenericArityOfClass(clazz: ts.Declaration): number|null {
- if (this.dtsMapper && ts.isClassDeclaration(clazz) && clazz.name) {
- const sourcePath = clazz.getSourceFile();
- const dtsPath = this.dtsMapper.getDtsFileNameFor(sourcePath.fileName);
- const dtsContents = readFileSync(dtsPath, 'utf8');
- // TODO: investigate caching parsed .d.ts files as they're needed for several different
- // purposes in ngcc.
- const dtsFile = ts.createSourceFile(
- dtsPath, dtsContents, ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
+ const dtsClass = this.getDtsDeclarationOfClass(clazz);
+ if (dtsClass) {
+ return dtsClass.typeParameters ? dtsClass.typeParameters.length : 0;
+ }
+ return null;
+ }
- for (let i = dtsFile.statements.length - 1; i >= 0; i--) {
- const stmt = dtsFile.statements[i];
- if (ts.isClassDeclaration(stmt) && stmt.name !== undefined &&
- stmt.name.text === clazz.name.text) {
- return stmt.typeParameters ? stmt.typeParameters.length : 0;
+ /**
+ * Take an exported declaration of a class (maybe downleveled to a variable) and look up the
+ * declaration of its type in a separate .d.ts tree.
+ *
+ * This function is allowed to return `null` if the current compilation unit does not have a
+ * separate .d.ts tree. When compiling TypeScript code this is always the case, since .d.ts files
+ * are produced only during the emit of such a compilation. When compiling .js code, however,
+ * there is frequently a parallel .d.ts tree which this method exposes.
+ *
+ * Note that the `ts.ClassDeclaration` returned from this function may not be from the same
+ * `ts.Program` as the input declaration.
+ */
+ getDtsDeclarationOfClass(declaration: ts.Declaration): ts.ClassDeclaration|null {
+ if (this.dtsClassMap) {
+ if (ts.isClassDeclaration(declaration)) {
+ if (!declaration.name || !ts.isIdentifier(declaration.name)) {
+ throw new Error(
+ `Cannot get the dts file for a class declaration that has no indetifier: ${declaration.getText()} in ${declaration.getSourceFile().fileName}`);
}
+ const dtsDeclaration = this.dtsClassMap.get(declaration.name.text);
+ if (!dtsDeclaration) {
+ throw new Error(
+ `Unable to find matching typings (.d.ts) declaration for ${declaration.name.text} in ${declaration.getSourceFile().fileName}`);
+ }
+ return dtsDeclaration;
}
}
return null;
@@ -371,6 +370,25 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
///////////// Protected Helpers /////////////
+ protected getDecoratorsOfSymbol(symbol: ts.Symbol): Decorator[]|null {
+ const decoratorsProperty = this.getStaticProperty(symbol, DECORATORS);
+ if (decoratorsProperty) {
+ return this.getClassDecoratorsFromStaticProperty(decoratorsProperty);
+ } else {
+ return this.getClassDecoratorsFromHelperCall(symbol);
+ }
+ }
+
+ protected getDecoratedClassFromSymbol(symbol: ts.Symbol|undefined): DecoratedClass|null {
+ if (symbol) {
+ const decorators = this.getDecoratorsOfSymbol(symbol);
+ if (decorators && decorators.length) {
+ return new DecoratedClass(symbol.name, symbol.valueDeclaration, decorators);
+ }
+ }
+ return null;
+ }
+
/**
* Walk the AST looking for an assignment to the specified symbol.
* @param node The current node we are searching.
@@ -691,7 +709,6 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
let value: ts.Expression|null = null;
let name: string|null = null;
let nameNode: ts.Identifier|null = null;
- let type = null;
const node = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0];
@@ -744,6 +761,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
node.modifiers.some(mod => mod.kind === ts.SyntaxKind.StaticKeyword);
}
+ const type: ts.TypeNode = (node as any).type || null;
return {
node,
implementation: node, kind, type, name, nameNode, value, isStatic,
@@ -967,13 +985,41 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
*
* @param decorator the decorator to test.
*/
- isFromCore(decorator: Decorator): boolean {
+ protected isFromCore(decorator: Decorator): boolean {
if (this.isCore) {
return !decorator.import || /^\./.test(decorator.import.from);
} else {
return !!decorator.import && decorator.import.from === '@angular/core';
}
}
+
+ protected computeDtsClassMap(dtsRootFileName: string, dtsProgram: ts.Program):
+ Map {
+ const dtsClassMap = new Map();
+ const checker = dtsProgram.getTypeChecker();
+ const dtsRootFile = dtsProgram.getSourceFile(dtsRootFileName);
+ const rootModule = dtsRootFile && checker.getSymbolAtLocation(dtsRootFile);
+ const moduleExports = rootModule && checker.getExportsOfModule(rootModule);
+ if (moduleExports) {
+ moduleExports.forEach(exportedSymbol => {
+ if (exportedSymbol.flags & ts.SymbolFlags.Alias) {
+ exportedSymbol = checker.getAliasedSymbol(exportedSymbol);
+ }
+ const declaration = exportedSymbol.declarations[0];
+ if (declaration && ts.isClassDeclaration(declaration)) {
+ const name = exportedSymbol.name;
+ const previousDeclaration = dtsClassMap.get(name);
+ if (previousDeclaration && previousDeclaration !== declaration) {
+ console.warn(
+ `Ambiguous class name ${name} in typings files: ${previousDeclaration.getSourceFile().fileName} and ${declaration.getSourceFile().fileName}`);
+ } else {
+ dtsClassMap.set(name, declaration);
+ }
+ }
+ });
+ }
+ return dtsClassMap;
+ }
}
///////////// Exported Helpers /////////////
diff --git a/packages/compiler-cli/src/ngcc/src/host/esm5_host.ts b/packages/compiler-cli/src/ngcc/src/host/esm5_host.ts
index 5ea5c0b95c..a1d5d013ee 100644
--- a/packages/compiler-cli/src/ngcc/src/host/esm5_host.ts
+++ b/packages/compiler-cli/src/ngcc/src/host/esm5_host.ts
@@ -10,10 +10,8 @@ import * as ts from 'typescript';
import {ClassMember, ClassMemberKind, Decorator, FunctionDefinition, Parameter} from '../../../ngtsc/host';
import {reflectObjectLiteral} from '../../../ngtsc/metadata';
-import {getNameText, getOriginalSymbol, isDefined} from '../utils';
+import {getNameText} from '../utils';
-import {DecoratedClass} from './decorated_class';
-import {DecoratedFile} from './decorated_file';
import {Esm2015ReflectionHost, ParamInfo, getPropertyValueFromSymbol, isAssignmentStatement} from './esm2015_host';
@@ -125,41 +123,6 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
return {node, body: statements || null, parameters};
}
- /**
- * Find all the files accessible via an entry-point, that contain decorated classes.
- * @param entryPoint The starting point file for finding files that contain decorated classes.
- * @returns A collection of files objects that hold info about the decorated classes and import
- * information.
- */
- findDecoratedFiles(entryPoint: ts.SourceFile): Map {
- const moduleSymbol = this.checker.getSymbolAtLocation(entryPoint);
- const map = new Map();
- const getParsedClass = (declaration: ts.VariableDeclaration) => {
- const decorators = this.getDecoratorsOfDeclaration(declaration);
- if (decorators) {
- return new DecoratedClass(getNameText(declaration.name), declaration, decorators);
- }
- };
-
- if (moduleSymbol) {
- const classDeclarations = this.checker.getExportsOfModule(moduleSymbol)
- .map(getOriginalSymbol(this.checker))
- .map(exportSymbol => exportSymbol.valueDeclaration)
- .filter(isDefined)
- .filter(ts.isVariableDeclaration);
-
- const decoratedClasses = classDeclarations.map(getParsedClass).filter(isDefined);
-
- decoratedClasses.forEach(clazz => {
- const file = clazz.declaration.getSourceFile();
- if (!map.has(file)) {
- map.set(file, new DecoratedFile(file));
- }
- map.get(file) !.decoratedClasses.push(clazz);
- });
- }
- return map;
- }
///////////// Protected Helpers /////////////
diff --git a/packages/compiler-cli/src/ngcc/src/host/ngcc_host.ts b/packages/compiler-cli/src/ngcc/src/host/ngcc_host.ts
index 483ade6be0..d8555c9295 100644
--- a/packages/compiler-cli/src/ngcc/src/host/ngcc_host.ts
+++ b/packages/compiler-cli/src/ngcc/src/host/ngcc_host.ts
@@ -7,7 +7,7 @@
*/
import * as ts from 'typescript';
import {ReflectionHost} from '../../../ngtsc/host';
-import {DecoratedFile} from './decorated_file';
+import {DecoratedClass} from './decorated_class';
export const PRE_R3_MARKER = '__PRE_R3__';
export const POST_R3_MARKER = '__POST_R3__';
@@ -40,10 +40,9 @@ export interface NgccReflectionHost extends ReflectionHost {
getSwitchableDeclarations(module: ts.Node): SwitchableVariableDeclaration[];
/**
- * Find all the files accessible via an entry-point, that contain decorated classes.
- * @param entryPoint The starting point file for finding files that contain decorated classes.
- * @returns A collection of files objects that hold info about the decorated classes and import
- * information.
+ * Find all the classes that contain decorations in a given file.
+ * @param sourceFile The source file to search for decorated classes.
+ * @returns An array of decorated classes.
*/
- findDecoratedFiles(entryPoint: ts.SourceFile): Map;
+ findDecoratedClasses(sourceFile: ts.SourceFile): DecoratedClass[];
}
diff --git a/packages/compiler-cli/src/ngcc/src/main.ts b/packages/compiler-cli/src/ngcc/src/main.ts
index 851589d7f0..35f003c2f5 100644
--- a/packages/compiler-cli/src/ngcc/src/main.ts
+++ b/packages/compiler-cli/src/ngcc/src/main.ts
@@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as path from 'canonical-path';
-import {existsSync, lstatSync, readFileSync, readdirSync} from 'fs';
import * as yargs from 'yargs';
import {DependencyHost} from './packages/dependency_host';
@@ -48,8 +47,14 @@ export function mainNgcc(args: string[]): number {
try {
const {entryPoints} = finder.findEntryPoints(sourcePath);
- entryPoints.forEach(
- entryPoint => formats.forEach(format => transformer.transform(entryPoint, format)));
+ entryPoints.forEach(entryPoint => {
+ // 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));
+ });
} catch (e) {
console.error(e.stack);
return 1;
diff --git a/packages/compiler-cli/src/ngcc/src/packages/transformer.ts b/packages/compiler-cli/src/ngcc/src/packages/transformer.ts
index a7955f68a4..a742f8352b 100644
--- a/packages/compiler-cli/src/ngcc/src/packages/transformer.ts
+++ b/packages/compiler-cli/src/ngcc/src/packages/transformer.ts
@@ -12,7 +12,6 @@ import * as ts from 'typescript';
import {DecorationAnalyzer} from '../analysis/decoration_analyzer';
import {SwitchMarkerAnalyzer} from '../analysis/switch_marker_analyzer';
-import {DtsMapper} from '../host/dts_mapper';
import {Esm2015ReflectionHost} from '../host/esm2015_host';
import {Esm5ReflectionHost} from '../host/esm5_host';
import {NgccReflectionHost} from '../host/ngcc_host';
@@ -44,7 +43,7 @@ import {EntryPoint, EntryPointFormat} from './entry_point';
export class Transformer {
constructor(private sourcePath: string, private targetPath: string) {}
- transform(entryPoint: EntryPoint, format: EntryPointFormat): void {
+ transform(entryPoint: EntryPoint, format: EntryPointFormat, transformDts: boolean): void {
if (checkMarkerFile(entryPoint, format)) {
console.warn(`Skipping ${entryPoint.name} : ${format} (already built).`);
return;
@@ -73,19 +72,24 @@ export class Transformer {
const r3SymbolsPath = isCore ? this.findR3SymbolsPath(dirname(entryPointFilePath)) : null;
const rootPaths = r3SymbolsPath ? [entryPointFilePath, r3SymbolsPath] : [entryPointFilePath];
const packageProgram = ts.createProgram(rootPaths, options, host);
- const dtsMapper = new DtsMapper(dirname(entryPointFilePath), dirname(entryPoint.typings));
- const reflectionHost = this.getHost(isCore, format, packageProgram, dtsMapper);
+ console.time(entryPoint.name + '(dtsmappper creation)');
+ const dtsFilePath = entryPoint.typings;
+ const dtsProgram = transformDts ? ts.createProgram([entryPoint.typings], options, host) : null;
+ console.timeEnd(entryPoint.name + '(dtsmappper creation)');
+ const reflectionHost = this.getHost(isCore, format, packageProgram, dtsFilePath, dtsProgram);
const r3SymbolsFile = r3SymbolsPath && packageProgram.getSourceFile(r3SymbolsPath) || null;
// Parse and analyze the files.
const {decorationAnalyses, switchMarkerAnalyses} =
this.analyzeProgram(packageProgram, reflectionHost, rootDirs, isCore);
+ console.time(entryPoint.name + '(rendering)');
// Transform the source files and source maps.
const renderer =
- this.getRenderer(format, packageProgram, reflectionHost, isCore, r3SymbolsFile, dtsMapper);
+ this.getRenderer(format, packageProgram, reflectionHost, isCore, r3SymbolsFile);
const renderedFiles =
renderer.renderProgram(packageProgram, decorationAnalyses, switchMarkerAnalyses);
+ console.timeEnd(entryPoint.name + '(rendering)');
// Write out all the transformed files.
renderedFiles.forEach(file => this.writeFile(file));
@@ -104,12 +108,13 @@ export class Transformer {
}
}
- getHost(isCore: boolean, format: string, program: ts.Program, dtsMapper: DtsMapper):
- NgccReflectionHost {
+ getHost(
+ isCore: boolean, format: string, program: ts.Program, dtsFilePath: string,
+ dtsProgram: ts.Program|null): NgccReflectionHost {
switch (format) {
case 'esm2015':
case 'fesm2015':
- return new Esm2015ReflectionHost(isCore, program.getTypeChecker(), dtsMapper);
+ return new Esm2015ReflectionHost(isCore, program.getTypeChecker(), dtsFilePath, dtsProgram);
case 'esm5':
case 'fesm5':
return new Esm5ReflectionHost(isCore, program.getTypeChecker());
@@ -120,13 +125,14 @@ export class Transformer {
getRenderer(
format: string, program: ts.Program, host: NgccReflectionHost, isCore: boolean,
- rewriteCoreImportsTo: ts.SourceFile|null, dtsMapper: DtsMapper|null): Renderer {
+ rewriteCoreImportsTo: ts.SourceFile|null): Renderer {
switch (format) {
case 'esm2015':
case 'esm5':
case 'fesm2015':
case 'fesm5':
- return new EsmRenderer(host, isCore, rewriteCoreImportsTo, this.sourcePath, this.targetPath, dtsMapper);
+ return new EsmRenderer(
+ host, isCore, rewriteCoreImportsTo, this.sourcePath, this.targetPath);
default:
throw new Error(`Renderer for "${format}" not yet implemented.`);
}
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 087e4de324..a0b3394c74 100644
--- a/packages/compiler-cli/src/ngcc/src/rendering/esm_renderer.ts
+++ b/packages/compiler-cli/src/ngcc/src/rendering/esm_renderer.ts
@@ -7,17 +7,16 @@
*/
import * as ts from 'typescript';
import MagicString from 'magic-string';
-import {DtsMapper} from '../host/dts_mapper';
import {NgccReflectionHost, POST_R3_MARKER, PRE_R3_MARKER, SwitchableVariableDeclaration} from '../host/ngcc_host';
-import {AnalyzedClass} from '../analysis/decoration_analyzer';
+import {CompiledClass} from '../analysis/decoration_analyzer';
import {Renderer} from './renderer';
export class EsmRenderer extends Renderer {
constructor(
protected host: NgccReflectionHost, protected isCore: boolean,
protected rewriteCoreImportsTo: ts.SourceFile|null, protected sourcePath: string,
- protected targetPath: string, dtsMapper: DtsMapper|null) {
- super(host, isCore, rewriteCoreImportsTo, sourcePath, targetPath, dtsMapper);
+ protected targetPath: string) {
+ super(host, isCore, rewriteCoreImportsTo, sourcePath, targetPath);
}
/**
@@ -45,10 +44,10 @@ export class EsmRenderer extends Renderer {
/**
* Add the definitions to each decorated class
*/
- addDefinitions(output: MagicString, analyzedClass: AnalyzedClass, definitions: string): void {
- const classSymbol = this.host.getClassSymbol(analyzedClass.declaration);
+ addDefinitions(output: MagicString, compiledClass: CompiledClass, definitions: string): void {
+ const classSymbol = this.host.getClassSymbol(compiledClass.declaration);
if (!classSymbol) {
- throw new Error(`Analyzed class does not have a valid symbol: ${analyzedClass.name}`);
+ throw new Error(`Compiled class does not have a valid symbol: ${compiledClass.name}`);
}
const insertionPoint = classSymbol.valueDeclaration !.getEnd();
output.appendLeft(insertionPoint, '\n' + definitions);
diff --git a/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts b/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts
index 9a05bf143e..e20979050f 100644
--- a/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts
+++ b/packages/compiler-cli/src/ngcc/src/rendering/renderer.ts
@@ -14,13 +14,12 @@ import {SourceMapConsumer, SourceMapGenerator, RawSourceMap} from 'source-map';
import * as ts from 'typescript';
import {Decorator} from '../../../ngtsc/host';
-import {DtsFileTransformer} from '../../../ngtsc/transform';
-import {translateStatement} from '../../../ngtsc/translator';
+import {CompileResult} from '@angular/compiler-cli/src/ngtsc/transform';
+import {translateStatement, translateType} from '../../../ngtsc/translator';
import {NgccImportManager} from './ngcc_import_manager';
-import {AnalyzedClass, DecorationAnalysis, DecorationAnalyses} from '../analysis/decoration_analyzer';
+import {CompiledClass, CompiledFile, DecorationAnalyses} from '../analysis/decoration_analyzer';
import {SwitchMarkerAnalyses, SwitchMarkerAnalysis} from '../analysis/switch_marker_analyzer';
import {IMPORT_PREFIX} from '../constants';
-import {DtsMapper} from '../host/dts_mapper';
import {NgccReflectionHost, SwitchableVariableDeclaration} from '../host/ngcc_host';
interface SourceMapInfo {
@@ -29,20 +28,6 @@ interface SourceMapInfo {
isInline: boolean;
}
-/**
- * The results of rendering an analyzed file.
- */
-export interface RenderResult {
- /**
- * The rendered source file.
- */
- source: FileInfo;
- /**
- * The rendered source map file.
- */
- map: FileInfo|null;
-}
-
/**
* Information about a file that has been rendered.
*/
@@ -57,6 +42,11 @@ export interface FileInfo {
contents: string;
}
+interface DtsClassInfo {
+ dtsDeclaration: ts.Declaration;
+ compilation: CompileResult[];
+}
+
/**
* A base-class for rendering an `AnalyzedFile`.
*
@@ -67,39 +57,38 @@ export abstract class Renderer {
constructor(
protected host: NgccReflectionHost, protected isCore: boolean,
protected rewriteCoreImportsTo: ts.SourceFile|null, protected sourcePath: string,
- protected targetPath: string, protected dtsMapper: DtsMapper|null) {}
+ protected targetPath: string) {}
renderProgram(
program: ts.Program, decorationAnalyses: DecorationAnalyses,
switchMarkerAnalyses: SwitchMarkerAnalyses): FileInfo[] {
const renderedFiles: FileInfo[] = [];
- // Transform the source files, source maps and typings files.
+ // Transform the source files.
program.getSourceFiles().map(sourceFile => {
- const decorationAnalysis = decorationAnalyses.get(sourceFile);
+ const compiledFile = decorationAnalyses.get(sourceFile);
const switchMarkerAnalysis = switchMarkerAnalyses.get(sourceFile);
- if (decorationAnalysis || switchMarkerAnalysis) {
- const targetPath = resolve(this.targetPath, relative(this.sourcePath, sourceFile.fileName));
- renderedFiles.push(
- ...this.renderFile(sourceFile, decorationAnalysis, switchMarkerAnalysis, targetPath));
- }
-
- if (decorationAnalyses) {
- renderedFiles.push(...this.renderTypings(decorationAnalyses));
+ if (compiledFile || switchMarkerAnalysis) {
+ renderedFiles.push(...this.renderFile(sourceFile, compiledFile, switchMarkerAnalysis));
}
});
+
+ // Transform the .d.ts files
+ const dtsFiles = this.getTypingsFilesToRender(decorationAnalyses);
+ dtsFiles.forEach((classes, file) => renderedFiles.push(...this.renderDtsFile(file, classes)));
+
return renderedFiles;
}
/**
* Render the source code and source-map for an Analyzed file.
- * @param decorationAnalysis The analyzed file to render.
+ * @param compiledFile The analyzed file to render.
* @param targetPath The absolute path where the rendered file will be written.
*/
renderFile(
- sourceFile: ts.SourceFile, decorationAnalysis: DecorationAnalysis|undefined,
- switchMarkerAnalysis: SwitchMarkerAnalysis|undefined, targetPath: string): FileInfo[] {
+ sourceFile: ts.SourceFile, compiledFile: CompiledFile|undefined,
+ switchMarkerAnalysis: SwitchMarkerAnalysis|undefined): FileInfo[] {
const input = this.extractSourceMap(sourceFile);
const outputText = new MagicString(input.source);
@@ -108,46 +97,59 @@ export abstract class Renderer {
outputText, switchMarkerAnalysis.sourceFile, switchMarkerAnalysis.declarations);
}
- if (decorationAnalysis) {
+ if (compiledFile) {
const importManager =
new NgccImportManager(!this.rewriteCoreImportsTo, this.isCore, IMPORT_PREFIX);
const decoratorsToRemove = new Map();
- decorationAnalysis.analyzedClasses.forEach(clazz => {
- const renderedDefinition =
- renderDefinitions(decorationAnalysis.sourceFile, clazz, importManager);
+ compiledFile.compiledClasses.forEach(clazz => {
+ const renderedDefinition = renderDefinitions(compiledFile.sourceFile, clazz, importManager);
this.addDefinitions(outputText, clazz, renderedDefinition);
this.trackDecorators(clazz.decorators, decoratorsToRemove);
});
this.addConstants(
outputText,
- renderConstantPool(
- decorationAnalysis.sourceFile, decorationAnalysis.constantPool, importManager),
- decorationAnalysis.sourceFile);
+ renderConstantPool(compiledFile.sourceFile, compiledFile.constantPool, importManager),
+ compiledFile.sourceFile);
this.addImports(
- outputText, importManager.getAllImports(
- decorationAnalysis.sourceFile.fileName, this.rewriteCoreImportsTo));
+ outputText,
+ importManager.getAllImports(compiledFile.sourceFile.fileName, this.rewriteCoreImportsTo));
// TODO: remove contructor param metadata and property decorators (we need info from the
// handlers to do this)
this.removeDecorators(outputText, decoratorsToRemove);
}
- const {source, map} = this.renderSourceAndMap(sourceFile, input, outputText, targetPath);
- const renderedFiles = [source];
- if (map) {
- renderedFiles.push(map);
- }
- return renderedFiles;
+ return this.renderSourceAndMap(sourceFile, input, outputText);
+ }
+
+ renderDtsFile(dtsFile: ts.SourceFile, dtsClasses: DtsClassInfo[]): FileInfo[] {
+ const input = this.extractSourceMap(dtsFile);
+ const outputText = new MagicString(input.source);
+ const importManager = new NgccImportManager(false, this.isCore, IMPORT_PREFIX);
+
+ dtsClasses.forEach(dtsClass => {
+ const endOfClass = dtsClass.dtsDeclaration.getEnd();
+ dtsClass.compilation.forEach(declaration => {
+ const type = translateType(declaration.type, importManager);
+ const newStatement = ` static ${declaration.name}: ${type};\n`;
+ outputText.appendRight(endOfClass - 1, newStatement);
+ });
+ });
+
+ this.addImports(
+ outputText, importManager.getAllImports(dtsFile.fileName, this.rewriteCoreImportsTo));
+
+ return this.renderSourceAndMap(dtsFile, input, outputText);
}
protected abstract addConstants(output: MagicString, constants: string, file: ts.SourceFile):
void;
protected abstract addImports(output: MagicString, imports: {name: string, as: string}[]): void;
protected abstract addDefinitions(
- output: MagicString, analyzedClass: AnalyzedClass, definitions: string): void;
+ output: MagicString, compiledClass: CompiledClass, definitions: string): void;
protected abstract removeDecorators(
output: MagicString, decoratorsToRemove: Map): void;
protected abstract rewriteSwitchableDeclarations(
@@ -219,8 +221,8 @@ export abstract class Renderer {
* with an appropriate source-map comment pointing to the merged source-map.
*/
protected renderSourceAndMap(
- sourceFile: ts.SourceFile, input: SourceMapInfo, output: MagicString,
- outputPath: string): RenderResult {
+ sourceFile: ts.SourceFile, input: SourceMapInfo, output: MagicString): FileInfo[] {
+ const outputPath = resolve(this.targetPath, relative(this.sourcePath, sourceFile.fileName));
const outputMapPath = `${outputPath}.map`;
const outputMap = output.generateMap({
source: sourceFile.fileName,
@@ -235,41 +237,34 @@ export abstract class Renderer {
const mergedMap =
mergeSourceMaps(input.map && input.map.toObject(), JSON.parse(outputMap.toString()));
+ const result: FileInfo[] = [];
if (input.isInline) {
- return {
- source: {path: outputPath, contents: `${output.toString()}\n${mergedMap.toComment()}`},
- map: null
- };
+ result.push({path: outputPath, contents: `${output.toString()}\n${mergedMap.toComment()}`});
} else {
- return {
- source: {
- path: outputPath,
- contents: `${output.toString()}\n${generateMapFileComment(outputMapPath)}`
- },
- map: {path: outputMapPath, contents: mergedMap.toJSON()}
- };
+ result.push({
+ path: outputPath,
+ contents: `${output.toString()}\n${generateMapFileComment(outputMapPath)}`
+ });
+ result.push({path: outputMapPath, contents: mergedMap.toJSON()});
}
+ return result;
}
- // TODO(gkalpak): What about `.d.ts` source maps? (See
- // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html#new---declarationmap.)
- renderTypings(decorationAnalyses: DecorationAnalyses): FileInfo[] {
- const renderedFiles: FileInfo[] = [];
- if (this.dtsMapper) {
- const dtsTransformer = new DtsFileTransformer(this.rewriteCoreImportsTo, IMPORT_PREFIX);
- decorationAnalyses.forEach((analysis, sourceFile) => {
- const sourceFileName = sourceFile.fileName;
- const dtsFileName = this.dtsMapper !.getDtsFileNameFor(sourceFileName);
- const dtsContents = readFileSync(dtsFileName, 'utf8');
- analysis.analyzedClasses.forEach(analyzedClass => dtsTransformer.recordStaticField(analyzedClass.name, analyzedClass.compilation));
- const newDtsFileName = resolve(this.targetPath, relative(this.sourcePath, dtsFileName));
- const newDtsContents = dtsTransformer.transform(dtsContents, sourceFileName);
- renderedFiles.push({path: newDtsFileName, contents: newDtsContents});
+ protected getTypingsFilesToRender(analyses: DecorationAnalyses):
+ Map {
+ const dtsMap = new Map();
+ analyses.forEach(compiledFile => {
+ compiledFile.compiledClasses.forEach(compiledClass => {
+ const dtsDeclaration = this.host.getDtsDeclarationOfClass(compiledClass.declaration);
+ if (dtsDeclaration) {
+ const dtsFile = dtsDeclaration.getSourceFile();
+ const classes = dtsMap.get(dtsFile) || [];
+ classes.push({dtsDeclaration, compilation: compiledClass.compilation});
+ dtsMap.set(dtsFile, classes);
+ }
});
- }
- return renderedFiles;
-
- // }
+ });
+ return dtsMap;
}
}
@@ -321,11 +316,11 @@ export function renderConstantPool(
* @param imports An object that tracks the imports that are needed by the rendered definitions.
*/
export function renderDefinitions(
- sourceFile: ts.SourceFile, analyzedClass: AnalyzedClass, imports: NgccImportManager): string {
+ sourceFile: ts.SourceFile, compiledClass: CompiledClass, imports: NgccImportManager): string {
const printer = ts.createPrinter();
- const name = (analyzedClass.declaration as ts.NamedDeclaration).name !;
+ const name = (compiledClass.declaration as ts.NamedDeclaration).name !;
const definitions =
- analyzedClass.compilation
+ compiledClass.compilation
.map(
c => c.statements.map(statement => translateStatement(statement, imports))
.concat(translateStatement(
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 c1abe7709e..2d435ca638 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
@@ -27,6 +27,34 @@ const TEST_PROGRAM = {
`
};
+const INTERNAL_COMPONENT_PROGRAM = [
+ {
+ name: 'entrypoint.js',
+ contents: `
+ import {Component, NgModule} from '@angular/core';
+ import {ImportedComponent} from './component';
+
+ export class LocalComponent {}
+ LocalComponent.decorators = [{type: Component}];
+
+ export class MyModule {}
+ MyModule.decorators = [{type: NgModule, args: [{
+ declarations: [ImportedComponent, LocalComponent],
+ exports: [ImportedComponent, LocalComponent],
+ },] }];
+ `
+ },
+ {
+ name: 'component.js',
+ contents: `
+ import {Component} from '@angular/core';
+ export class ImportedComponent {}
+ ImportedComponent.decorators = [{type: Component}];
+ `,
+ isRoot: false,
+ }
+];
+
function createTestHandler() {
const handler = jasmine.createSpyObj>('TestDecoratorHandler', [
'detect',
@@ -79,9 +107,9 @@ describe('DecorationAnalyzer', () => {
it('should return an object containing the classes that were analyzed', () => {
const file = program.getSourceFile(TEST_PROGRAM.name) !;
- const analysis = result.get(file) !;
- expect(analysis.analyzedClasses.length).toEqual(1);
- expect(analysis.analyzedClasses[0].name).toEqual('MyComponent');
+ const compiledFile = result.get(file) !;
+ expect(compiledFile.compiledClasses.length).toEqual(1);
+ expect(compiledFile.compiledClasses[0].name).toEqual('MyComponent');
});
it('should analyze and compile the classes that are detected', () => {
@@ -91,5 +119,41 @@ describe('DecorationAnalyzer', () => {
expect(testHandler.compile).toHaveBeenCalledTimes(1);
expect(testHandler.compile.calls.allArgs()[0][1]).toEqual('Component');
});
+
+ describe('internal components', () => {
+ // The problem of exposing the type of these internal components in the .d.ts typing files
+ // 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 analyzer = new DecorationAnalyzer(
+ program.getTypeChecker(), new Esm2015ReflectionHost(false, program.getTypeChecker()),
+ [''], false);
+ const testHandler = createTestHandler();
+ analyzer.handlers = [testHandler];
+ const result = analyzer.analyzeProgram(program);
+ const file = program.getSourceFile('component.js') !;
+ const analysis = result.get(file) !;
+ expect(analysis).toBeDefined();
+ const ImportedComponent =
+ analysis.compiledClasses.find(f => f.name === 'ImportedComponent') !;
+ expect(ImportedComponent).toBeDefined();
+ });
+
+ it('should analyze an internally defined component, which is not exported at all', () => {
+ const program = makeProgram(...INTERNAL_COMPONENT_PROGRAM);
+ const analyzer = new DecorationAnalyzer(
+ program.getTypeChecker(), new Esm2015ReflectionHost(false, program.getTypeChecker()),
+ [''], false);
+ const testHandler = createTestHandler();
+ analyzer.handlers = [testHandler];
+ const result = analyzer.analyzeProgram(program);
+ const file = program.getSourceFile('entrypoint.js') !;
+ const analysis = result.get(file) !;
+ expect(analysis).toBeDefined();
+ const LocalComponent = analysis.compiledClasses.find(f => f.name === 'LocalComponent') !;
+ expect(LocalComponent).toBeDefined();
+ });
+ });
});
});
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 05e310e056..e9493946b0 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
@@ -6,10 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
-import * as fs from 'fs';
import * as ts from 'typescript';
import {ClassMemberKind, Import} from '../../../ngtsc/host';
-import {DtsMapper} from '../../src/host/dts_mapper';
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
import {getDeclaration, makeProgram} from '../helpers/utils';
@@ -399,6 +397,7 @@ const DECORATED_FILES = [
name: '/primary.js',
contents: `
import {Directive} from '@angular/core';
+ import {D} from '/secondary';
class A {}
A.decorators = [
{ type: Directive, args: [{ selector: '[a]' }] }
@@ -411,7 +410,6 @@ const DECORATED_FILES = [
];
class C {}
export { A, x, C };
- export { D } from '/secondary';
`
},
{
@@ -422,7 +420,7 @@ const DECORATED_FILES = [
D.decorators = [
{ type: Directive, args: [{ selector: '[d]' }] }
];
- export { D };
+ export {D};
`
}
];
@@ -439,13 +437,38 @@ const ARITY_CLASSES = [
{
name: '/typings/class.d.ts',
contents: `
- export class NoTypeParam {}
- export class OneTypeParam {}
- export class TwoTypeParams {}
+ export declare class NoTypeParam {}
+ export declare class OneTypeParam {}
+ export declare class TwoTypeParams {}
`,
},
];
+const TYPINGS_SRC_FILES = [
+ {name: '/src/index.js', contents: `export * from './class1'; export * from './class2';`},
+ {name: '/src/class1.js', contents: 'export class Class1 {}\nexport class MissingClass1 {}'},
+ {name: '/src/class2.js', contents: 'export class Class2 {}'},
+ {name: '/src/missing-class.js', contents: 'export class MissingClass2 {}'}, {
+ name: '/src/flat-file.js',
+ contents:
+ 'export class Class1 {}\nexport class MissingClass1 {}\nexport class MissingClass2 {}\class Class3 {}\nexport {Class3 as xClass3};',
+ }
+];
+
+const TYPINGS_DTS_FILES = [
+ {name: '/typings/index.d.ts', contents: `export * from './class1'; export * from './class2';`},
+ {
+ name: '/typings/class1.d.ts',
+ contents: `export declare class Class1 {}\nexport declare class OtherClass {}`
+ },
+ {
+ name: '/typings/class2.d.ts',
+ contents:
+ `export declare class Class2 {}\nexport declare interface SomeInterface {}\nexport {Class3 as xClass3} from './class3';`
+ },
+ {name: '/typings/class3.d.ts', contents: `export declare class Class3 {}`},
+];
+
describe('Fesm2015ReflectionHost', () => {
describe('getDecoratorsOfDeclaration()', () => {
@@ -1186,12 +1209,10 @@ describe('Fesm2015ReflectionHost', () => {
describe('getGenericArityOfClass()', () => {
it('should properly count type parameters', () => {
- // Mock out reading the `d.ts` file from disk
- const readFileSyncSpy = spyOn(fs, 'readFileSync').and.returnValue(ARITY_CLASSES[1].contents);
+ const dtsProgram = makeProgram(ARITY_CLASSES[1]);
const program = makeProgram(ARITY_CLASSES[0]);
-
- const dtsMapper = new DtsMapper('/src', '/typings');
- const host = new Esm2015ReflectionHost(false, program.getTypeChecker(), dtsMapper);
+ const host = new Esm2015ReflectionHost(
+ false, program.getTypeChecker(), ARITY_CLASSES[1].name, dtsProgram);
const noTypeParamClass =
getDeclaration(program, '/src/class.js', 'NoTypeParam', ts.isClassDeclaration);
expect(host.getGenericArityOfClass(noTypeParamClass)).toBe(0);
@@ -1217,30 +1238,95 @@ describe('Fesm2015ReflectionHost', () => {
});
});
- describe('findDecoratedFiles()', () => {
- it('should return an array of objects for each file that has exported and decorated classes',
- () => {
- const program = makeProgram(...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) !;
- const decoratedFiles = host.findDecoratedFiles(primaryFile);
+ describe('findDecoratedClasses()', () => {
+ it('should return an array of all decorated classes in the given source file', () => {
+ const program = makeProgram(...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) !;
- expect(decoratedFiles.size).toEqual(2);
+ const primaryDecoratedClasses = host.findDecoratedClasses(primaryFile);
+ expect(primaryDecoratedClasses.length).toEqual(2);
+ const classA = primaryDecoratedClasses.find(c => c.name === 'A') !;
+ expect(ts.isClassDeclaration(classA.declaration)).toBeTruthy();
+ expect(classA.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
+ // Note that `B` is not exported from `primary.js`
+ const classB = primaryDecoratedClasses.find(c => c.name === 'B') !;
+ expect(ts.isClassDeclaration(classB.declaration)).toBeTruthy();
+ expect(classA.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
- const primary = decoratedFiles.get(primaryFile) !;
- expect(primary.decoratedClasses.length).toEqual(1);
- const classA = primary.decoratedClasses.find(c => c.name === 'A') !;
- expect(classA.name).toEqual('A');
- expect(ts.isClassDeclaration(classA.declaration)).toBeTruthy();
- expect(classA.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
-
- const secondary = decoratedFiles.get(secondaryFile) !;
- expect(secondary.decoratedClasses.length).toEqual(1);
- const classD = secondary.decoratedClasses.find(c => c.name === 'D') !;
- expect(classD.name).toEqual('D');
- expect(ts.isClassDeclaration(classD.declaration)).toBeTruthy();
- expect(classD.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
- });
+ const secondaryDecoratedClasses = host.findDecoratedClasses(secondaryFile) !;
+ expect(secondaryDecoratedClasses.length).toEqual(1);
+ // Note that `D` is exported from `secondary.js` but not exported from `primary.js`
+ const classD = secondaryDecoratedClasses.find(c => c.name === 'D') !;
+ expect(classD.name).toEqual('D');
+ expect(ts.isClassDeclaration(classD.declaration)).toBeTruthy();
+ expect(classD.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
+ });
});
+
+ 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 class1 = getDeclaration(srcProgram, '/src/class1.js', 'Class1', ts.isClassDeclaration);
+ const host = new Esm2015ReflectionHost(
+ false, srcProgram.getTypeChecker(), TYPINGS_DTS_FILES[0].name, dtsProgram);
+
+ const dtsDeclaration = host.getDtsDeclarationOfClass(class1);
+ expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class1.d.ts');
+ });
+
+ it('should throw an error if there is no matching class in the matching dts file', () => {
+ const srcProgram = makeProgram(...TYPINGS_SRC_FILES);
+ const dtsProgram = makeProgram(...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);
+
+ expect(() => host.getDtsDeclarationOfClass(missingClass))
+ .toThrowError(
+ 'Unable to find matching typings (.d.ts) declaration for MissingClass1 in /src/class1.js');
+ });
+
+ it('should throw an error if there is no matching dts file', () => {
+ const srcProgram = makeProgram(...TYPINGS_SRC_FILES);
+ const dtsProgram = makeProgram(...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);
+
+ expect(() => host.getDtsDeclarationOfClass(missingClass))
+ .toThrowError(
+ 'Unable to find matching typings (.d.ts) declaration for MissingClass2 in /src/missing-class.js');
+ });
+
+ 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 class1 =
+ getDeclaration(srcProgram, '/src/flat-file.js', 'Class1', ts.isClassDeclaration);
+ const host = new Esm2015ReflectionHost(
+ false, srcProgram.getTypeChecker(), TYPINGS_DTS_FILES[0].name, dtsProgram);
+
+ 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 class3 =
+ getDeclaration(srcProgram, '/src/flat-file.js', 'Class3', ts.isClassDeclaration);
+ const host = new Esm2015ReflectionHost(
+ false, srcProgram.getTypeChecker(), TYPINGS_DTS_FILES[0].name, dtsProgram);
+
+ const dtsDeclaration = host.getDtsDeclarationOfClass(class3);
+ expect(dtsDeclaration !.getSourceFile().fileName).toEqual('/typings/class3.d.ts');
+ });
+ });
+
});
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 b3ed33794d..4c6ddd0014 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
@@ -432,6 +432,7 @@ const DECORATED_FILES = [
name: '/primary.js',
contents: `
import {Directive} from '@angular/core';
+ import { D } from '/secondary';
var A = (function() {
function A() {}
A.decorators = [
@@ -453,7 +454,6 @@ const DECORATED_FILES = [
return C;
});
export { A, x, C };
- export { D } from '/secondary';
`
},
{
@@ -1252,26 +1252,27 @@ describe('Esm5ReflectionHost', () => {
});
});
- describe('fileDecoratedFiles()', () => {
- it('should return an array of objects for each file that has exported and decorated classes',
- () => {
- const program = makeProgram(...DECORATED_FILES);
- const host = new Esm5ReflectionHost(false, program.getTypeChecker());
- const primary = program.getSourceFile(DECORATED_FILES[0].name) !;
- const decoratedFiles = host.findDecoratedFiles(primary);
- expect(decoratedFiles.size).toEqual(2);
- const primaryClasses = decoratedFiles.get(primary) !.decoratedClasses;
- expect(primaryClasses.length).toEqual(1);
- const classA = primaryClasses.find(c => c.name === 'A') !;
- expect(classA.name).toEqual('A');
- expect(classA.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
+ describe('findDecoratedClasses()', () => {
+ it('should return an array of all decorated classes in the given source file', () => {
+ const program = makeProgram(...DECORATED_FILES);
+ const host = new Esm5ReflectionHost(false, program.getTypeChecker());
+ const primary = program.getSourceFile(DECORATED_FILES[0].name) !;
- const secondary = program.getSourceFile(DECORATED_FILES[1].name) !;
- const secondaryClasses = decoratedFiles.get(secondary) !.decoratedClasses;
- expect(secondaryClasses.length).toEqual(1);
- const classD = secondaryClasses.find(c => c.name === 'D') !;
- expect(classD.name).toEqual('D');
- expect(classD.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
- });
+ const primaryDecoratedClasses = host.findDecoratedClasses(primary);
+ expect(primaryDecoratedClasses.length).toEqual(2);
+ const classA = primaryDecoratedClasses.find(c => c.name === 'A') !;
+ expect(classA.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
+ // Note that `B` is not exported from `primary.js`
+ const classB = primaryDecoratedClasses.find(c => c.name === 'B') !;
+ expect(classB.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
+
+ const secondary = program.getSourceFile(DECORATED_FILES[1].name) !;
+ const secondaryDecoratedClasses = host.findDecoratedClasses(secondary);
+ expect(secondaryDecoratedClasses.length).toEqual(1);
+ // Note that `D` is exported from `secondary.js` but not exported from `primary.js`
+ const classD = secondaryDecoratedClasses.find(c => c.name === 'D') !;
+ expect(classD.name).toEqual('D');
+ expect(classD.decorators.map(decorator => decorator.name)).toEqual(['Directive']);
+ });
});
});
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 bfdb94ce89..f0b07ef6bf 100644
--- a/packages/compiler-cli/src/ngcc/test/rendering/esm2015_renderer_spec.ts
+++ b/packages/compiler-cli/src/ngcc/test/rendering/esm2015_renderer_spec.ts
@@ -12,7 +12,6 @@ import MagicString from 'magic-string';
import {makeProgram} from '../helpers/utils';
import {DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
-import {DtsMapper} from '../../src/host/dts_mapper';
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
import {EsmRenderer} from '../../src/rendering/esm_renderer';
@@ -24,7 +23,7 @@ function setup(file: {name: string, contents: string}, transformDts: boolean = f
const decorationAnalyses =
new DecorationAnalyzer(program.getTypeChecker(), host, [''], false).analyzeProgram(program);
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(program);
- const renderer = new EsmRenderer(host, false, null, dir, dir, null);
+ const renderer = new EsmRenderer(host, false, null, dir, dir);
return {host, program, sourceFile, renderer, decorationAnalyses, switchMarkerAnalyses};
}
@@ -161,8 +160,9 @@ export class A {}`);
it('should insert the definitions directly after the class declaration', () => {
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM);
const output = new MagicString(PROGRAM.contents);
- const analyzedClass = decorationAnalyses.get(sourceFile) !.analyzedClasses[0];
- renderer.addDefinitions(output, analyzedClass, 'SOME DEFINITION TEXT');
+ const compiledClass =
+ decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
+ renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT');
expect(output.toString()).toContain(`
export class A {}
SOME DEFINITION TEXT
@@ -179,9 +179,9 @@ A.decorators = [
() => {
const {decorationAnalyses, sourceFile, renderer} = setup(PROGRAM);
const output = new MagicString(PROGRAM.contents);
- const analyzedClass =
- decorationAnalyses.get(sourceFile) !.analyzedClasses.find(c => c.name === 'A') !;
- const decorator = analyzedClass.decorators[0];
+ const compiledClass =
+ decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
+ const decorator = compiledClass.decorators[0];
const decoratorsToRemove = new Map();
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
renderer.removeDecorators(output, decoratorsToRemove);
@@ -198,9 +198,9 @@ A.decorators = [
() => {
const {decorationAnalyses, sourceFile, renderer} = setup(PROGRAM);
const output = new MagicString(PROGRAM.contents);
- const analyzedClass =
- decorationAnalyses.get(sourceFile) !.analyzedClasses.find(c => c.name === 'B') !;
- const decorator = analyzedClass.decorators[0];
+ const compiledClass =
+ decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
+ const decorator = compiledClass.decorators[0];
const decoratorsToRemove = new Map();
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
renderer.removeDecorators(output, decoratorsToRemove);
@@ -217,9 +217,9 @@ A.decorators = [
() => {
const {decorationAnalyses, sourceFile, renderer} = setup(PROGRAM);
const output = new MagicString(PROGRAM.contents);
- const analyzedClass =
- decorationAnalyses.get(sourceFile) !.analyzedClasses.find(c => c.name === 'C') !;
- const decorator = analyzedClass.decorators[0];
+ const compiledClass =
+ decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
+ const decorator = compiledClass.decorators[0];
const decoratorsToRemove = new Map();
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
renderer.removeDecorators(output, decoratorsToRemove);
@@ -238,9 +238,9 @@ A.decorators = [
it('should delete the decorator (and following comma) that was matched in the analysis', () => {
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER);
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
- const analyzedClass =
- decorationAnalyses.get(sourceFile) !.analyzedClasses.find(c => c.name === 'A') !;
- const decorator = analyzedClass.decorators.find(d => d.name === 'Directive') !;
+ const compiledClass =
+ decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
+ const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
const decoratorsToRemove = new Map();
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
renderer.removeDecorators(output, decoratorsToRemove);
@@ -255,9 +255,9 @@ A.decorators = [
() => {
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER);
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
- const analyzedClass =
- decorationAnalyses.get(sourceFile) !.analyzedClasses.find(c => c.name === 'B') !;
- const decorator = analyzedClass.decorators.find(d => d.name === 'Directive') !;
+ const compiledClass =
+ decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
+ const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
const decoratorsToRemove = new Map();
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
renderer.removeDecorators(output, decoratorsToRemove);
@@ -273,9 +273,9 @@ A.decorators = [
() => {
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER);
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
- const analyzedClass =
- decorationAnalyses.get(sourceFile) !.analyzedClasses.find(c => c.name === 'C') !;
- const decorator = analyzedClass.decorators.find(d => d.name === 'Directive') !;
+ const compiledClass =
+ decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
+ const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
const decoratorsToRemove = new Map();
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
renderer.removeDecorators(output, decoratorsToRemove);
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 8cc31708b0..88d54d6280 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
@@ -20,7 +20,7 @@ function setup(file: {name: string, contents: string}) {
const decorationAnalyses =
new DecorationAnalyzer(program.getTypeChecker(), host, [''], false).analyzeProgram(program);
const switchMarkerAnalyses = new SwitchMarkerAnalyzer(host).analyzeProgram(program);
- const renderer = new EsmRenderer(host, false, null, '', '', null);
+ const renderer = new EsmRenderer(host, false, null, '', '');
return {host, program, sourceFile, renderer, decorationAnalyses, switchMarkerAnalyses};
}
@@ -182,8 +182,9 @@ var A = (function() {`);
it('should insert the definitions directly after the class declaration', () => {
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM);
const output = new MagicString(PROGRAM.contents);
- const analyzedClass = decorationAnalyses.get(sourceFile) !.analyzedClasses[0];
- renderer.addDefinitions(output, analyzedClass, 'SOME DEFINITION TEXT');
+ const compiledClass =
+ decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
+ renderer.addDefinitions(output, compiledClass, 'SOME DEFINITION TEXT');
expect(output.toString()).toContain(`
function A() {}
SOME DEFINITION TEXT
@@ -199,9 +200,9 @@ SOME DEFINITION TEXT
it('should delete the decorator (and following comma) that was matched in the analysis', () => {
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM);
const output = new MagicString(PROGRAM.contents);
- const analyzedClass =
- decorationAnalyses.get(sourceFile) !.analyzedClasses.find(c => c.name === 'A') !;
- const decorator = analyzedClass.decorators[0];
+ const compiledClass =
+ decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
+ const decorator = compiledClass.decorators[0];
const decoratorsToRemove = new Map();
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
renderer.removeDecorators(output, decoratorsToRemove);
@@ -217,9 +218,9 @@ SOME DEFINITION TEXT
() => {
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM);
const output = new MagicString(PROGRAM.contents);
- const analyzedClass =
- decorationAnalyses.get(sourceFile) !.analyzedClasses.find(c => c.name === 'B') !;
- const decorator = analyzedClass.decorators[0];
+ const compiledClass =
+ decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
+ const decorator = compiledClass.decorators[0];
const decoratorsToRemove = new Map();
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
renderer.removeDecorators(output, decoratorsToRemove);
@@ -236,9 +237,9 @@ SOME DEFINITION TEXT
() => {
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM);
const output = new MagicString(PROGRAM.contents);
- const analyzedClass =
- decorationAnalyses.get(sourceFile) !.analyzedClasses.find(c => c.name === 'C') !;
- const decorator = analyzedClass.decorators[0];
+ const compiledClass =
+ decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
+ const decorator = compiledClass.decorators[0];
const decoratorsToRemove = new Map();
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
renderer.removeDecorators(output, decoratorsToRemove);
@@ -257,9 +258,9 @@ SOME DEFINITION TEXT
it('should delete the decorator (and following comma) that was matched in the analysis', () => {
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER);
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
- const analyzedClass =
- decorationAnalyses.get(sourceFile) !.analyzedClasses.find(c => c.name === 'A') !;
- const decorator = analyzedClass.decorators.find(d => d.name === 'Directive') !;
+ const compiledClass =
+ decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
+ const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
const decoratorsToRemove = new Map();
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
renderer.removeDecorators(output, decoratorsToRemove);
@@ -274,9 +275,9 @@ SOME DEFINITION TEXT
() => {
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER);
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
- const analyzedClass =
- decorationAnalyses.get(sourceFile) !.analyzedClasses.find(c => c.name === 'B') !;
- const decorator = analyzedClass.decorators.find(d => d.name === 'Directive') !;
+ const compiledClass =
+ decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
+ const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
const decoratorsToRemove = new Map();
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
renderer.removeDecorators(output, decoratorsToRemove);
@@ -292,9 +293,9 @@ SOME DEFINITION TEXT
() => {
const {renderer, decorationAnalyses, sourceFile} = setup(PROGRAM_DECORATE_HELPER);
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
- const analyzedClass =
- decorationAnalyses.get(sourceFile) !.analyzedClasses.find(c => c.name === 'C') !;
- const decorator = analyzedClass.decorators.find(d => d.name === 'Directive') !;
+ const compiledClass =
+ decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
+ const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
const decoratorsToRemove = new Map();
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
renderer.removeDecorators(output, decoratorsToRemove);
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 54f69e0d8f..467acbc128 100644
--- a/packages/compiler-cli/src/ngcc/test/rendering/renderer_spec.ts
+++ b/packages/compiler-cli/src/ngcc/test/rendering/renderer_spec.ts
@@ -11,20 +11,20 @@ import * as ts from 'typescript';
import MagicString from 'magic-string';
import {fromObject, generateMapFileComment} from 'convert-source-map';
import {makeProgram} from '../helpers/utils';
-import {AnalyzedClass, DecorationAnalyzer, DecorationAnalyses} from '../../src/analysis/decoration_analyzer';
+import {CompiledClass, DecorationAnalyzer} from '../../src/analysis/decoration_analyzer';
import {SwitchMarkerAnalyzer} from '../../src/analysis/switch_marker_analyzer';
import {Esm2015ReflectionHost} from '../../src/host/esm2015_host';
import {Renderer} from '../../src/rendering/renderer';
class TestRenderer extends Renderer {
- constructor(host: Esm2015ReflectionHost) { super(host, false, null, '/src', '/dist', null); }
+ constructor(host: Esm2015ReflectionHost) { super(host, false, null, '/src', '/dist'); }
addImports(output: MagicString, imports: {name: string, as: string}[]) {
output.prepend('\n// ADD IMPORTS\n');
}
addConstants(output: MagicString, constants: string, file: ts.SourceFile): void {
output.prepend('\n// ADD CONSTANTS\n');
}
- addDefinitions(output: MagicString, analyzedClass: AnalyzedClass, definitions: string) {
+ addDefinitions(output: MagicString, compiledClass: CompiledClass, definitions: string) {
output.prepend('\n// ADD DEFINITIONS\n');
}
removeDecorators(output: MagicString, decoratorsToRemove: Map) {
diff --git a/packages/compiler-cli/src/ngtsc/testing/in_memory_typescript.ts b/packages/compiler-cli/src/ngtsc/testing/in_memory_typescript.ts
index b963c868a5..36a5b90b0d 100644
--- a/packages/compiler-cli/src/ngtsc/testing/in_memory_typescript.ts
+++ b/packages/compiler-cli/src/ngtsc/testing/in_memory_typescript.ts
@@ -12,12 +12,13 @@ import * as path from 'path';
import * as ts from 'typescript';
export function makeProgram(
- files: {name: string, contents: string}[], options?: ts.CompilerOptions,
+ files: {name: string, contents: string, isRoot?: boolean}[], options?: ts.CompilerOptions,
host: ts.CompilerHost = new InMemoryHost(),
checkForErrors: boolean = true): {program: ts.Program, host: ts.CompilerHost} {
files.forEach(file => host.writeFile(file.name, file.contents, false, undefined, []));
- const rootNames = files.map(file => host.getCanonicalFileName(file.name));
+ const rootNames =
+ files.filter(file => file.isRoot !== false).map(file => host.getCanonicalFileName(file.name));
const program = ts.createProgram(
rootNames, {
noLib: true,