Skip to content

Commit 3bb67d8

Browse files
alan-agius4mgechev
authored andcommitted
fix(@angular-devkit/build-optimizer): incorrectly augmented ES2015 default class exports
Fixes #14769
1 parent c9535a5 commit 3bb67d8

File tree

2 files changed

+117
-16
lines changed

2 files changed

+117
-16
lines changed

packages/angular_devkit/build_optimizer/src/transforms/wrap-enums.ts

+37-16
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ function visitBlockStatements(
6363
// 'oIndex' is the original statement index; 'uIndex' is the updated statement index
6464
for (let oIndex = 0, uIndex = 0; oIndex < statements.length - 1; oIndex++, uIndex++) {
6565
const currentStatement = statements[oIndex];
66-
let newStatement: ts.Statement | undefined;
66+
let newStatement: ts.Statement[] | undefined;
6767
let oldStatementsLength = 0;
6868

6969
// these can't contain an enum declaration
@@ -181,12 +181,15 @@ function visitBlockStatements(
181181
oIndex += classStatements.length - 1;
182182
}
183183

184-
if (newStatement) {
184+
if (newStatement && newStatement.length > 0) {
185185
if (!updatedStatements) {
186186
updatedStatements = [...statements];
187187
}
188188

189-
updatedStatements.splice(uIndex, oldStatementsLength, newStatement);
189+
updatedStatements.splice(uIndex, oldStatementsLength, ...newStatement);
190+
// When having more than a single new statement
191+
// we need to update the update Index
192+
uIndex += (newStatement ? newStatement.length - 1 : 0);
190193
}
191194

192195
const result = ts.visitNode(currentStatement, visitor);
@@ -531,7 +534,7 @@ function updateEnumIife(
531534
hostNode: ts.VariableStatement,
532535
iife: ts.CallExpression,
533536
exportAssignment?: ts.Expression,
534-
): ts.Statement {
537+
): ts.Statement[] {
535538
if (!ts.isParenthesizedExpression(iife.expression)
536539
|| !ts.isFunctionExpression(iife.expression.expression)) {
537540
throw new Error('Invalid IIFE Structure');
@@ -583,7 +586,7 @@ function updateEnumIife(
583586
updatedIife);
584587
}
585588

586-
return updateHostNode(hostNode, value);
589+
return [updateHostNode(hostNode, value)];
587590
}
588591

589592
function createWrappedEnum(
@@ -592,7 +595,7 @@ function createWrappedEnum(
592595
statements: Array<ts.Statement>,
593596
literalInitializer: ts.ObjectLiteralExpression = ts.createObjectLiteral(),
594597
addExportModifier = false,
595-
): ts.Statement {
598+
): ts.Statement[] {
596599
const node = addExportModifier
597600
? ts.updateVariableStatement(
598601
hostNode,
@@ -616,13 +619,13 @@ function createWrappedEnum(
616619
innerReturn,
617620
]);
618621

619-
return updateHostNode(node, addPureComment(ts.createParen(iife)));
622+
return [updateHostNode(node, addPureComment(ts.createParen(iife)))];
620623
}
621624

622625
function createWrappedClass(
623626
hostNode: ts.ClassDeclaration | ts.VariableDeclaration,
624627
statements: ts.Statement[],
625-
): ts.Statement {
628+
): ts.Statement[] {
626629
const name = (hostNode.name as ts.Identifier).text;
627630

628631
const updatedStatements = [...statements];
@@ -645,12 +648,30 @@ function createWrappedClass(
645648
]),
646649
);
647650

648-
return ts.createVariableStatement(
649-
hostNode.modifiers,
650-
ts.createVariableDeclarationList([
651-
ts.createVariableDeclaration(name, undefined, pureIife),
652-
],
653-
ts.NodeFlags.Const,
654-
),
655-
);
651+
const modifiers = hostNode.modifiers;
652+
const isDefault = !!modifiers
653+
&& modifiers.some(x => x.kind === ts.SyntaxKind.DefaultKeyword);
654+
655+
const newStatement: ts.Statement[] = [];
656+
newStatement.push(
657+
ts.createVariableStatement(
658+
isDefault ? undefined : modifiers,
659+
ts.createVariableDeclarationList([
660+
ts.createVariableDeclaration(name, undefined, pureIife),
661+
],
662+
ts.NodeFlags.Const,
663+
),
664+
));
665+
666+
if (isDefault) {
667+
newStatement.push(
668+
ts.createExportAssignment(
669+
undefined,
670+
undefined,
671+
false,
672+
ts.createIdentifier(name),
673+
));
674+
}
675+
676+
return newStatement;
656677
}

packages/angular_devkit/build_optimizer/src/transforms/wrap-enums_spec.ts

+80
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,46 @@ const transform = (content: string) => transformJavascript(
1717
// tslint:disable:no-big-function
1818
describe('wrap enums and classes transformer', () => {
1919
describe('wraps class declarations', () => {
20+
it('should wrap default exported classes', () => {
21+
const defaultClass = tags.stripIndent`
22+
export default class CustomComponentEffects {
23+
constructor(_actions) {
24+
this._actions = _actions;
25+
this.doThis = this._actions;
26+
}
27+
}
28+
CustomComponentEffects.decorators = [{ type: Injectable }];
29+
`;
30+
31+
const namedClass = tags.stripIndent`
32+
class CustomComponent {
33+
constructor(_actions) {
34+
this._actions = _actions;
35+
this.doThis = this._actions;
36+
}
37+
}
38+
CustomComponent.decorators = [{ type: Injectable }];
39+
`;
40+
41+
const output = tags.stripIndent`
42+
const CustomComponentEffects = /*@__PURE__*/ (() => {
43+
${defaultClass.replace('export default ', '')}
44+
45+
return CustomComponentEffects;
46+
})();
47+
export default CustomComponentEffects;
48+
49+
const CustomComponent = /*@__PURE__*/ (() => {
50+
${namedClass}
51+
52+
return CustomComponent;
53+
})();
54+
`;
55+
56+
const input = defaultClass + namedClass;
57+
expect(tags.oneLine`${transform(input)}`).toEqual(tags.oneLine`${output}`);
58+
});
59+
2060
it('should wrap tsickle emitted classes which followed by metadata', () => {
2161
const input = tags.stripIndent`
2262
class CustomComponentEffects {
@@ -169,6 +209,46 @@ describe('wrap enums and classes transformer', () => {
169209
});
170210

171211
describe('wrap class expressions', () => {
212+
it('should wrap default exported classes', () => {
213+
const defaultClass = tags.stripIndent`
214+
let Foo = class Foo {
215+
};
216+
Foo.bar = 'bar';
217+
Foo = __decorate([
218+
component()
219+
], Foo);
220+
export default Foo;
221+
`;
222+
223+
const namedClass = tags.stripIndent`
224+
let AggregateColumnDirective = class AggregateColumnDirective {
225+
constructor(viewContainerRef) { }
226+
};
227+
AggregateColumnDirective = __decorate([
228+
Directive({}),
229+
__metadata("design:paramtypes", [ViewContainerRef])
230+
], AggregateColumnDirective);
231+
`;
232+
233+
const output = tags.stripIndent`
234+
const Foo = /*@__PURE__*/ (() => {
235+
${defaultClass.replace('export default Foo;', '')}
236+
237+
return Foo;
238+
})();
239+
export default Foo;
240+
241+
const AggregateColumnDirective = /*@__PURE__*/ (() => {
242+
${namedClass}
243+
244+
return AggregateColumnDirective;
245+
})();
246+
`;
247+
248+
const input = defaultClass + namedClass;
249+
expect(tags.oneLine`${transform(input)}`).toEqual(tags.oneLine`${output}`);
250+
});
251+
172252
it('without property decorators in IIFE', () => {
173253
const input = tags.stripIndent`
174254
let AggregateColumnDirective = class AggregateColumnDirective {

0 commit comments

Comments
 (0)