fix(ngcc): correctly detect outer aliased class identifiers in ES5 (#35527)

In ES5 and ES2015, class identifiers may have aliases. Previously, the
`NgccReflectionHost`s recognized the following formats:
- ES5:
    ```js
    var MyClass = (function () {
      function InnerClass() {}
      InnerClass_1 = InnerClass;
      ...
    }());
    ```
- ES2015:
    ```js
    let MyClass = MyClass_1 = class MyClass { ... };
    ```

In addition to the above, this commit adds support for recognizing an
alias outside the IIFE in ES5 classes (which was previously not
supported):
```js
var MyClass = MyClass_1 = (function () { ... }());
```

Jira issue: [FW-1869](https://angular-team.atlassian.net/browse/FW-1869)

Partially addresses #35399.

PR Close #35527
This commit is contained in:
George Kalpakas
2020-02-18 13:17:59 +02:00
committed by Miško Hevery
parent 2baf90209b
commit fde89156fa
5 changed files with 229 additions and 46 deletions

View File

@ -634,7 +634,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
if (!this.preprocessedSourceFiles.has(sourceFile)) {
this.preprocessedSourceFiles.add(sourceFile);
for (const statement of sourceFile.statements) {
for (const statement of this.getModuleStatements(sourceFile)) {
this.preprocessStatement(statement);
}
}
@ -660,7 +660,7 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
const declaration = declarations[0];
const initializer = declaration.initializer;
if (!ts.isIdentifier(declaration.name) || !initializer || !isAssignment(initializer) ||
!ts.isIdentifier(initializer.left) || !ts.isClassExpression(initializer.right)) {
!ts.isIdentifier(initializer.left) || !this.isClass(declaration)) {
return;
}

View File

@ -295,8 +295,8 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
* @param checker the TS program TypeChecker
* @returns the inner function declaration or `undefined` if it is not a "class".
*/
protected getInnerFunctionDeclarationFromClassDeclaration(decl: ts.Declaration): ts.FunctionDeclaration
|undefined {
protected getInnerFunctionDeclarationFromClassDeclaration(decl: ts.Declaration):
ts.FunctionDeclaration|undefined {
// Extract the IIFE body (if any).
const iifeBody = getIifeBody(decl);
if (!iifeBody) return undefined;
@ -605,7 +605,15 @@ export function getIifeBody(declaration: ts.Declaration): ts.Block|undefined {
return undefined;
}
const call = stripParentheses(declaration.initializer);
// Recognize a variable declaration of one of the forms:
// - `var MyClass = (function () { ... }());`
// - `var MyClass = MyClass_1 = (function () { ... }());`
let parenthesizedCall = declaration.initializer;
while (isAssignment(parenthesizedCall)) {
parenthesizedCall = parenthesizedCall.right;
}
const call = stripParentheses(parenthesizedCall);
if (!ts.isCallExpression(call)) {
return undefined;
}