fix(@angular-devkit/build-optimizer): wrap es2015 class expressions for better tree-shaking#14585
fix(@angular-devkit/build-optimizer): wrap es2015 class expressions for better tree-shaking#14585alexeagle merged 3 commits intoangular:masterfrom alan-agius4:call-expressions
Conversation
…or better tree-shaking
ClassExpressions such as the below are not treeshakable unless we wrap them in an IIFE
```js
let AggregateColumnDirective = class AggregateColumnDirective {
constructor(viewContainerRef) { }
};
AggregateColumnDirective = __decorate([
Directive({}),
__metadata("design:paramtypes", [ViewContainerRef])
], AggregateColumnDirective);
```
With this change we wrap the above in an IIFE and mark it as a PURE function.
```js
const AggregateColumnDirective = /*@__PURE__*/ (() => {
let AggregateColumnDirective = class AggregateColumnDirective {
constructor(viewContainerRef) { }
};
AggregateColumnDirective = __decorate([
Directive({}),
__metadata("design:paramtypes", [ViewContainerRef])
], AggregateColumnDirective);
return AggregateColumnDirective;
})();
```
With this pattern if the class is unused it will be dropped.
Note: In future we should rename `wrap-enums` to something more generic, and combine class-fold with this transformer especially considering the future fix that needs to be done for #14610
Fixes #14577
|
It appears classes that self-reference using var TestUnshakeableDirective_1;
export const SOME_TOKEN = new InjectionToken('some token');
let TestUnshakeableDirective = TestUnshakeableDirective_1 = class TestUnshakeableDirective {
constructor() { }
};
TestUnshakeableDirective = TestUnshakeableDirective_1 = __decorate([
Directive({
selector: '[libUnshakeable]',
providers: [
{
provide: SOME_TOKEN,
useExisting: forwardRef(() => TestUnshakeableDirective_1)
},
]
})
], TestUnshakeableDirective);
export { TestUnshakeableDirective };and var TestUnshakeable2Directive_1;
let TestUnshakeable2Directive = TestUnshakeable2Directive_1 = class TestUnshakeable2Directive {
constructor(parent) { }
};
TestUnshakeable2Directive = TestUnshakeable2Directive_1 = __decorate([
Directive({
selector: '[libUnshakeable2]',
}),
__param(0, SkipSelf()), __param(0, Inject(forwardRef(() => TestUnshakeable2Directive_1))),
__metadata("design:paramtypes", [TestUnshakeable2Directive])
], TestUnshakeable2Directive);
export { TestUnshakeable2Directive };The output doesn't change for codeconst SOME_TOKEN = new InjectionToken('some token');
const PROVIDE_SOME_TOKEN = {
provide: SOME_TOKEN,
useExisting: forwardRef(() => TestShakeableDirective)
};
let TestShakeableDirective = class TestShakeableDirective {
constructor() { }
};
TestShakeableDirective = __decorate([
Directive({
selector: '[libShakeable]',
providers: [
PROVIDE_SOME_TOKEN,
]
})
], TestShakeableDirective);and let DoorDirective = class DoorDirective {
constructor(lock) {
this.lock = lock;
}
};
DoorDirective = __decorate([
Directive({ selector: '[libDoor]' }),
__param(0, Inject(forwardRef(() => LockDirective))),
__metadata("design:paramtypes", [LockDirective])
], DoorDirective);
let LockDirective = class LockDirective {
};
LockDirective = __decorate([
Directive({ selector: '[libLock]' })
], LockDirective); |
|
@bgotink, thanks for the above, it will surly help to improve the logic to handle the above case. |
filipesilva
left a comment
There was a problem hiding this comment.
The CI failure looks legit:
Running "tests/build/prod-build" (15 of 51 [1:4] (43/189))...
==========================================================================================
Running `ng "build" "--prod"` [silent]...
CWD: /tmp/angular-cli-e2e-201953-110-nkn7c4.kb2o/test-project
ENV: undefined
Last step took 21.09s...
Error: Expected main-es2015.647cfbad8918a52f10b1.js size to be less than 139.81279296875Kb but it was 163.63671875Kb.
at verifySize (/home/circleci/ng/tests/legacy-cli/e2e/tests/build/prod-build.ts:15:15)
at default_1 (/home/circleci/ng/tests/legacy-cli/e2e/tests/build/prod-build.ts:46:9)
Test "tests/build/prod-build" failed...
Expected main-es2015.647cfbad8918a52f10b1.js size to be less than 139.81279296875Kb but it was 163.63671875Kb.
Error: Expected main-es2015.647cfbad8918a52f10b1.js size to be less than 139.81279296875Kb but it was 163.63671875Kb.
Unsure why this happens but it's a bit severe. Some of these changes must be causing more to be retained than before.
|
@filipesilva comments fixed. PTAL |
|
Should we do the class wrapping only when it's sideEffects free? |
…FE for better treeshaking
With this change we wrap ClassDeclarations inside an IIFE, also we move some code from the class fold into the wrap-enums.
This changes the below code:
```js
export class Foo {
method() {
}
}
Foo.bar = 'barValue';
__decorate([
methodDecorator
], Foo.prototype, "method", null);
```
to
```js
export const Foo = /*@__PURE__*/ (() => {
class Foo {
method() {
}
}
Foo.bar = 'barValue';
__decorate([
methodDecorator
], Foo.prototype, "method", null);
return Foo;
})();
```
Fixes #14610
|
It should always happen to be consistent with TS ES5 class behavior and hopefully standard TS behavior in general moving forward. |
|
I've removed myself as reviewer because I think @clydin understand this particular transformer much better than me. |
…dles wrapping of tslib and tsickle classes Related to ngrx/platform#1905 and ng-packagr/ng-packagr#1307 Fixes #14613
|
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
fix(@angular-devkit/build-optimizer): wrap es2015 class expressions for better tree-shaking
ClassExpressions such as the below are not treeshakable unless we wrap them in an IIFE
With this change we wrap the above in an IIFE and mark it as a PURE function.
With this pattern if the class is unused it will be dropped.
Note: In future we should rename
wrap-enumsto something more generic, and combine class-fold with this transformer especially considering the future fix that needs to be done for #14610Fixes #14577
fix(@angular-devkit/build-optimizer) wrap ClassDeclarations in an IIFE for better treeshaking
With this change we wrap ClassDeclarations inside an IIFE, also we move some code from the class fold into the wrap-enums.
This changes the below code:
to
Fixes #14610
test: add test to verify that the new es2015 class wrapping logic handles wrapping of tslib and tsickle classes
Related to ngrx/platform#1905 and ng-packagr/ng-packagr#1307
Fixes: #14613