fix(ivy): ngcc - identify all ESM5 decorated classes (#27848)

In ESM5 decorated classes can be indicated by calls to `__decorate()`.
Previously the `ReflectionHost.findDecoratedClasses()` call would identify
helper calls of the form:

```
SomeClass = tslib_1.__decorate(...);
```

But it was missing calls of the form:

```
SomeClass = SomeClass_1 = tslib_1.__decorate(...);
```

This form is common in `@NgModule()` decorations, where the class
being decorated is referenced inside the decorator or another
member.

This commit now ensures that a chain of assignments, of any length,
is now identified as a class decoration if it results in a call to
`__decorate()`.

Fixes #27841

PR Close #27848
This commit is contained in:
Pete Bacon Darwin 2018-12-27 18:49:03 +00:00 committed by Andrew Kushnir
parent d505468fb7
commit e31afb7118
4 changed files with 37 additions and 14 deletions

View File

@ -12,7 +12,7 @@
"master": {
"uncompressed": {
"runtime": 1440,
"main": 507677,
"main": 584077,
"polyfills": 38390
}
}

View File

@ -3,7 +3,7 @@
"@angular/animations@file:../../dist/packages-dist/animations":
version "7.1.0"
version "7.2.0-rc.0"
dependencies:
tslib "^1.9.0"
@ -17,12 +17,12 @@
parse5 "^5.0.0"
"@angular/common@file:../../dist/packages-dist/common":
version "7.1.0"
version "0.0.0"
dependencies:
tslib "^1.9.0"
"@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli":
version "7.1.0"
version "0.0.0"
dependencies:
canonical-path "1.0.0"
chokidar "^1.4.2"
@ -37,22 +37,22 @@
yargs "9.0.1"
"@angular/compiler@file:../../dist/packages-dist/compiler":
version "7.1.0"
version "7.2.0-rc.0"
dependencies:
tslib "^1.9.0"
"@angular/core@file:../../dist/packages-dist/core":
version "7.1.0"
version "0.0.0"
dependencies:
tslib "^1.9.0"
"@angular/forms@file:../../dist/packages-dist/forms":
version "7.1.0"
version "0.0.0"
dependencies:
tslib "^1.9.0"
"@angular/http@file:../../dist/packages-dist/http":
version "7.1.0"
version "7.2.0-rc.0"
dependencies:
tslib "^1.9.0"
@ -66,17 +66,17 @@
parse5 "^5.0.0"
"@angular/platform-browser-dynamic@file:../../dist/packages-dist/platform-browser-dynamic":
version "7.1.0"
version "7.2.0-rc.0"
dependencies:
tslib "^1.9.0"
"@angular/platform-browser@file:../../dist/packages-dist/platform-browser":
version "7.1.0"
version "0.0.0"
dependencies:
tslib "^1.9.0"
"@angular/router@file:../../dist/packages-dist/router":
version "7.1.0"
version "0.0.0"
dependencies:
tslib "^1.9.0"
@ -3278,7 +3278,7 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
"typescript@file:../../node_modules/typescript":
version "3.1.1"
version "3.2.2"
ua-parser-js@0.7.17:
version "0.7.17"

View File

@ -661,8 +661,10 @@ export class Esm2015ReflectionHost extends TypeScriptReflectionHost implements N
*/
protected getHelperCall(statement: ts.Statement, helperName: string): ts.CallExpression|null {
if (ts.isExpressionStatement(statement)) {
const expression =
isAssignmentStatement(statement) ? statement.expression.right : statement.expression;
let expression = statement.expression;
while (isAssignment(expression)) {
expression = expression.right;
}
if (ts.isCallExpression(expression) && getCalleeName(expression) === helperName) {
return expression;
}

View File

@ -304,6 +304,27 @@ describe('Esm5ReflectionHost [import helper style]', () => {
});
});
describe('findDecoratedClasses', () => {
it('should return an array of all decorated classes in the given source file', () => {
const program = makeTestProgram(...fileSystem.files);
const host = new Esm5ReflectionHost(false, program.getTypeChecker());
const ngModuleFile = program.getSourceFile('/ngmodule.js') !;
const ngModuleClasses = host.findDecoratedClasses(ngModuleFile);
expect(ngModuleClasses.length).toEqual(1);
const ngModuleClass = ngModuleClasses.find(c => c.name === 'HttpClientXsrfModule') !;
expect(ngModuleClass.decorators.map(decorator => decorator.name)).toEqual(['NgModule']);
const someDirectiveFile = program.getSourceFile('/some_directive.js') !;
const someDirectiveClasses = host.findDecoratedClasses(someDirectiveFile);
expect(someDirectiveClasses.length).toEqual(1);
const someDirectiveClass = someDirectiveClasses.find(c => c.name === 'SomeDirective') !;
expect(someDirectiveClass.decorators.map(decorator => decorator.name)).toEqual([
'Directive'
]);
});
});
describe('getDeclarationOfIdentifier', () => {
it('should return the declaration of a locally defined identifier', () => {
const program = makeTestProgram(fileSystem.files[0]);