fix(ngcc): ensure that adjacent statements go after helper calls (#33689)
Previously the renderers were fixed so that they inserted extra "adjacent" statements after the last static property of classes. In order to help the build-optimizer (in Angular CLI) to be able to tree-shake classes effectively, these statements should also appear after any helper calls, such as `__decorate()`. This commit moves the computation of this positioning into the `NgccReflectionHost` via the `getEndOfClass()` method, which returns the last statement that is related to the class. FW-1668 PR Close #33689
This commit is contained in:

committed by
Kara Erickson

parent
f67802ddc0
commit
c5400616f8
@ -534,6 +534,35 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
|
||||
return infos;
|
||||
}
|
||||
|
||||
getEndOfClass(classSymbol: NgccClassSymbol): ts.Node {
|
||||
let last: ts.Node = classSymbol.declaration.valueDeclaration;
|
||||
|
||||
// If there are static members on this class then find the last one
|
||||
if (classSymbol.declaration.exports !== undefined) {
|
||||
classSymbol.declaration.exports.forEach(exportSymbol => {
|
||||
if (exportSymbol.valueDeclaration === undefined) {
|
||||
return;
|
||||
}
|
||||
const exportStatement = getContainingStatement(exportSymbol.valueDeclaration);
|
||||
if (exportStatement !== null && last.getEnd() < exportStatement.getEnd()) {
|
||||
last = exportStatement;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// If there are helper calls for this class then find the last one
|
||||
const helpers = this.getHelperCallsForClass(
|
||||
classSymbol, ['__decorate', '__extends', '__param', '__metadata']);
|
||||
helpers.forEach(helper => {
|
||||
const helperStatement = getContainingStatement(helper);
|
||||
if (helperStatement !== null && last.getEnd() < helperStatement.getEnd()) {
|
||||
last = helperStatement;
|
||||
}
|
||||
});
|
||||
|
||||
return last;
|
||||
}
|
||||
|
||||
///////////// Protected Helpers /////////////
|
||||
|
||||
/**
|
||||
@ -1891,3 +1920,17 @@ function isSynthesizedSuperCall(expression: ts.Expression): boolean {
|
||||
return ts.isSpreadElement(argument) && ts.isIdentifier(argument.expression) &&
|
||||
argument.expression.text === 'arguments';
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the statement that contains the given node
|
||||
* @param node a node whose containing statement we wish to find
|
||||
*/
|
||||
function getContainingStatement(node: ts.Node): ts.ExpressionStatement|null {
|
||||
while (node) {
|
||||
if (ts.isExpressionStatement(node)) {
|
||||
break;
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
return node || null;
|
||||
}
|
||||
|
@ -103,6 +103,22 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
|
||||
return this.getInternalNameOfClass(clazz);
|
||||
}
|
||||
|
||||
getEndOfClass(classSymbol: NgccClassSymbol): ts.Node {
|
||||
const iifeBody = getIifeBody(classSymbol.declaration.valueDeclaration);
|
||||
if (!iifeBody) {
|
||||
throw new Error(
|
||||
`Compiled class declaration is not inside an IIFE: ${classSymbol.name} in ${classSymbol.declaration.valueDeclaration.getSourceFile().fileName}`);
|
||||
}
|
||||
|
||||
const returnStatementIndex = iifeBody.statements.findIndex(ts.isReturnStatement);
|
||||
if (returnStatementIndex === -1) {
|
||||
throw new Error(
|
||||
`Compiled class wrapper IIFE does not have a return statement: ${classSymbol.name} in ${classSymbol.declaration.valueDeclaration.getSourceFile().fileName}`);
|
||||
}
|
||||
|
||||
// Return the statement before the IIFE return statement
|
||||
return iifeBody.statements[returnStatementIndex - 1];
|
||||
}
|
||||
/**
|
||||
* In ES5, the implementation of a class is a function expression that is hidden inside an IIFE,
|
||||
* whose value is assigned to a variable (which represents the class to the rest of the program).
|
||||
|
@ -116,4 +116,16 @@ export interface NgccReflectionHost extends ReflectionHost {
|
||||
* objects.
|
||||
*/
|
||||
getModuleWithProvidersFunctions(f: ts.SourceFile): ModuleWithProvidersFunction[];
|
||||
|
||||
/**
|
||||
* Find the last node that is relevant to the specified class.
|
||||
*
|
||||
* As well as the main declaration, classes can have additional statements such as static
|
||||
* properties (`SomeClass.staticProp = ...;`) and decorators (`__decorate(SomeClass, ...);`).
|
||||
* It is useful to know exactly where the class "ends" so that we can inject additional
|
||||
* statements after that point.
|
||||
*
|
||||
* @param classSymbol The class whose statements we want.
|
||||
*/
|
||||
getEndOfClass(classSymbol: NgccClassSymbol): ts.Node;
|
||||
}
|
||||
|
Reference in New Issue
Block a user