fix(ivy): ngcc generates setClassMetadata calls for ES5 bundles (#27438)

ngcc would feed ngtsc with the function declaration inside of an IIFE as
that is considered the class symbol's declaration node, according to
TypeScript's `ts.Symbol.valueDeclaration`. ngtsc however only considered
variable decls and actual class decls as potential class declarations,
so given the function declaration node it would fail to generate the
`setClassMetadata` call.

ngtsc no longer makes its own assumptions about what classes look like,
but always asks the reflection host to yield this kind of information.

PR Close #27438
This commit is contained in:
JoostK 2018-12-04 22:10:37 +01:00 committed by Miško Hevery
parent 84084b1bdb
commit 52544ffaa3
5 changed files with 8 additions and 13 deletions

View File

@ -38,7 +38,9 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
/**
* Check whether the given node actually represents a class.
*/
isClass(node: ts.Node): boolean { return super.isClass(node) || !!this.getClassSymbol(node); }
isClass(node: ts.Node): node is ts.NamedDeclaration {
return super.isClass(node) || !!this.getClassSymbol(node);
}
/**
* Find a symbol for a node that we think is a class.

View File

@ -20,10 +20,7 @@ import {CtorParameter, Decorator, ReflectionHost} from '../../host';
*/
export function generateSetClassMetadataCall(
clazz: ts.Declaration, reflection: ReflectionHost, isCore: boolean): Statement|null {
// Classes come in two flavors, class declarations (ES2015) and variable declarations (ES5).
// Both must have a declared name to have metadata set on them.
if ((!ts.isClassDeclaration(clazz) && !ts.isVariableDeclaration(clazz)) ||
clazz.name === undefined || !ts.isIdentifier(clazz.name)) {
if (!reflection.isClass(clazz) || clazz.name === undefined || !ts.isIdentifier(clazz.name)) {
return null;
}
const id = ts.updateIdentifier(clazz.name);

View File

@ -423,7 +423,7 @@ export interface ReflectionHost {
/**
* Check whether the given node actually represents a class.
*/
isClass(node: ts.Node): boolean;
isClass(node: ts.Node): node is ts.NamedDeclaration;
hasBaseClass(node: ts.Declaration): boolean;

View File

@ -145,7 +145,7 @@ export class TypeScriptReflectionHost implements ReflectionHost {
return map;
}
isClass(node: ts.Node): boolean {
isClass(node: ts.Node): node is ts.NamedDeclaration {
// In TypeScript code, classes are ts.ClassDeclarations.
return ts.isClassDeclaration(node);
}

View File

@ -353,7 +353,7 @@ class StaticInterpreter {
return this.visitExpression(node.expression, context);
} else if (ts.isNonNullExpression(node)) {
return this.visitExpression(node.expression, context);
} else if (isPossibleClassDeclaration(node) && this.host.isClass(node)) {
} else if (this.host.isClass(node)) {
return this.visitDeclaration(node, context);
} else {
return DYNAMIC_VALUE;
@ -565,7 +565,7 @@ class StaticInterpreter {
return lhs[rhs];
} else if (lhs instanceof Reference) {
const ref = lhs.node;
if (isPossibleClassDeclaration(ref) && this.host.isClass(ref)) {
if (this.host.isClass(ref)) {
let absoluteModuleName = context.absoluteModuleName;
if (lhs instanceof NodeReference || lhs instanceof AbsoluteReference) {
absoluteModuleName = lhs.moduleName || absoluteModuleName;
@ -761,10 +761,6 @@ function identifierOfDeclaration(decl: ts.Declaration): ts.Identifier|undefined
}
}
function isPossibleClassDeclaration(node: ts.Node): node is ts.Declaration {
return ts.isClassDeclaration(node) || ts.isVariableDeclaration(node);
}
function isVariableDeclarationDeclared(node: ts.VariableDeclaration): boolean {
if (node.parent === undefined || !ts.isVariableDeclarationList(node.parent)) {
return false;