Skip to content

Commit

Permalink
fix(ngcc): ensure reflection hosts can handle TS 3.9 IIFE wrapped cla…
Browse files Browse the repository at this point in the history
…sses (#36989)

In TS 3.9, ES2015 output can contain ES classes that are wrapped in an
IIFE. So now ES2015 class declarations can look like one of:

```
class OuterClass1 {}
```

```
let OuterClass = class InnerClass {};
```

```
var AliasClass;
let OuterClass = AliasClass = class InnerClass {};
```

```
let OuterClass = (() => class InnerClass {}};
```

```
var AliasClass;
let OuterClass = AliasClass = (() => class InnerClass {})();
```

```
let OuterClass = (() => {
  let AdjacentClass = class InnerClass {};
  // ... static properties or decorators attached to `AdjacentClass`
  return AdjacentClass;
})();
```

```
var AliasClass;
let OuterClass = AliasClass = (() => {
  let AdjacentClass = class InnerClass {};
  // ... static properties or decorators attached to `AdjacentClass`
  return AdjacentClass;
})();
```

The `Esm5ReflectionHost` already handles slightly different IIFE wrappers
around function-based classes. This can be substantially reused when
fixing `Esm2015ReflectionHost`, since there is a lot of commonality
between the two.

This commit moves code from the `Esm5ReflectionHost` into the `Esm2015ReflectionHost`
and looks to share as much as possible between the two hosts.

PR Close #36989
  • Loading branch information
petebacondarwin authored and kara committed May 14, 2020
1 parent a2b8dc1 commit d7440c4
Show file tree
Hide file tree
Showing 8 changed files with 793 additions and 468 deletions.
663 changes: 464 additions & 199 deletions packages/compiler-cli/ngcc/src/host/esm2015_host.ts

Large diffs are not rendered by default.

318 changes: 80 additions & 238 deletions packages/compiler-cli/ngcc/src/host/esm5_host.ts

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions packages/compiler-cli/ngcc/src/host/ngcc_host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ export interface NgccClassSymbol {
* declaration.
*/
implementation: ts.Symbol;

/**
* Represents the symbol corresponding to a variable within a class IIFE that may be used to
* attach static properties or decorated.
*/
adjacent?: ts.Symbol;
}

/**
Expand Down
20 changes: 13 additions & 7 deletions packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {getDeclaration} from '../../../src/ngtsc/testing';
import {loadFakeCore, loadTestFiles} from '../../../test/helpers';
import {CommonJsReflectionHost} from '../../src/host/commonjs_host';
import {DelegatingReflectionHost} from '../../src/host/delegating_host';
import {getIifeBody} from '../../src/host/esm5_host';
import {getIifeBody} from '../../src/host/esm2015_host';
import {NgccReflectionHost} from '../../src/host/ngcc_host';
import {BundleProgram} from '../../src/packages/bundle_program';
import {MockLogger} from '../helpers/mock_logger';
Expand Down Expand Up @@ -2212,7 +2212,8 @@ exports.MissingClass2 = MissingClass2;
createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle));
const outerNode = getDeclaration(
bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration);
const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!;
const innerNode = (getIifeBody(outerNode.initializer!) as ts.Block)
.statements.find(isNamedFunctionDeclaration)!;
const classSymbol = host.getClassSymbol(outerNode);

expect(classSymbol).toBeDefined();
Expand All @@ -2227,7 +2228,8 @@ exports.MissingClass2 = MissingClass2;
createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle));
const outerNode = getDeclaration(
bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration);
const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!;
const innerNode = (getIifeBody(outerNode.initializer!) as ts.Block)
.statements.find(isNamedFunctionDeclaration)!;
const classSymbol = host.getClassSymbol(innerNode);

expect(classSymbol).toBeDefined();
Expand All @@ -2243,7 +2245,8 @@ exports.MissingClass2 = MissingClass2;
createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle));
const outerNode = getDeclaration(
bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration);
const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!;
const innerNode = (getIifeBody(outerNode.initializer!) as ts.Block)
.statements.find(isNamedFunctionDeclaration)!;

const innerSymbol = host.getClassSymbol(innerNode)!;
const outerSymbol = host.getClassSymbol(outerNode)!;
Expand All @@ -2260,7 +2263,8 @@ exports.MissingClass2 = MissingClass2;
const outerNode = getDeclaration(
bundle.program, SIMPLE_CLASS_FILE.name, 'NoParensClass',
isNamedVariableDeclaration);
const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!;
const innerNode = (getIifeBody(outerNode.initializer!) as ts.Block)
.statements.find(isNamedFunctionDeclaration)!;
const classSymbol = host.getClassSymbol(outerNode);

expect(classSymbol).toBeDefined();
Expand All @@ -2277,7 +2281,8 @@ exports.MissingClass2 = MissingClass2;
const outerNode = getDeclaration(
bundle.program, SIMPLE_CLASS_FILE.name, 'InnerParensClass',
isNamedVariableDeclaration);
const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!;
const innerNode = (getIifeBody(outerNode.initializer!) as ts.Block)
.statements.find(isNamedFunctionDeclaration)!;
const classSymbol = host.getClassSymbol(outerNode);

expect(classSymbol).toBeDefined();
Expand Down Expand Up @@ -2345,7 +2350,8 @@ exports.MissingClass2 = MissingClass2;
createHost(bundle, new CommonJsReflectionHost(new MockLogger(), false, bundle));
const outerNode = getDeclaration(
bundle.program, SIMPLE_CLASS_FILE.name, 'EmptyClass', ts.isVariableDeclaration);
const innerNode = getIifeBody(outerNode)!.statements.find(isNamedFunctionDeclaration)!;
const innerNode = (getIifeBody(outerNode.initializer!) as ts.Block)
.statements.find(isNamedFunctionDeclaration)!;
expect(host.isClass(innerNode)).toBe(true);
});

Expand Down
Loading

0 comments on commit d7440c4

Please sign in to comment.