fix(@angular-devkit/build-optimizer): incorrectly augmented ES2015 default class exports

Fixes #14769
This commit is contained in:
Alan 2019-06-18 08:22:14 +02:00 committed by Minko Gechev
parent c9535a53c2
commit 3bb67d81fb
2 changed files with 117 additions and 16 deletions

View File

@ -63,7 +63,7 @@ function visitBlockStatements(
// 'oIndex' is the original statement index; 'uIndex' is the updated statement index
for (let oIndex = 0, uIndex = 0; oIndex < statements.length - 1; oIndex++, uIndex++) {
const currentStatement = statements[oIndex];
let newStatement: ts.Statement | undefined;
let newStatement: ts.Statement[] | undefined;
let oldStatementsLength = 0;
// these can't contain an enum declaration
@ -181,12 +181,15 @@ function visitBlockStatements(
oIndex += classStatements.length - 1;
}
if (newStatement) {
if (newStatement && newStatement.length > 0) {
if (!updatedStatements) {
updatedStatements = [...statements];
}
updatedStatements.splice(uIndex, oldStatementsLength, newStatement);
updatedStatements.splice(uIndex, oldStatementsLength, ...newStatement);
// When having more than a single new statement
// we need to update the update Index
uIndex += (newStatement ? newStatement.length - 1 : 0);
}
const result = ts.visitNode(currentStatement, visitor);
@ -531,7 +534,7 @@ function updateEnumIife(
hostNode: ts.VariableStatement,
iife: ts.CallExpression,
exportAssignment?: ts.Expression,
): ts.Statement {
): ts.Statement[] {
if (!ts.isParenthesizedExpression(iife.expression)
|| !ts.isFunctionExpression(iife.expression.expression)) {
throw new Error('Invalid IIFE Structure');
@ -583,7 +586,7 @@ function updateEnumIife(
updatedIife);
}
return updateHostNode(hostNode, value);
return [updateHostNode(hostNode, value)];
}
function createWrappedEnum(
@ -592,7 +595,7 @@ function createWrappedEnum(
statements: Array<ts.Statement>,
literalInitializer: ts.ObjectLiteralExpression = ts.createObjectLiteral(),
addExportModifier = false,
): ts.Statement {
): ts.Statement[] {
const node = addExportModifier
? ts.updateVariableStatement(
hostNode,
@ -616,13 +619,13 @@ function createWrappedEnum(
innerReturn,
]);
return updateHostNode(node, addPureComment(ts.createParen(iife)));
return [updateHostNode(node, addPureComment(ts.createParen(iife)))];
}
function createWrappedClass(
hostNode: ts.ClassDeclaration | ts.VariableDeclaration,
statements: ts.Statement[],
): ts.Statement {
): ts.Statement[] {
const name = (hostNode.name as ts.Identifier).text;
const updatedStatements = [...statements];
@ -645,12 +648,30 @@ function createWrappedClass(
]),
);
return ts.createVariableStatement(
hostNode.modifiers,
ts.createVariableDeclarationList([
ts.createVariableDeclaration(name, undefined, pureIife),
],
ts.NodeFlags.Const,
),
);
const modifiers = hostNode.modifiers;
const isDefault = !!modifiers
&& modifiers.some(x => x.kind === ts.SyntaxKind.DefaultKeyword);
const newStatement: ts.Statement[] = [];
newStatement.push(
ts.createVariableStatement(
isDefault ? undefined : modifiers,
ts.createVariableDeclarationList([
ts.createVariableDeclaration(name, undefined, pureIife),
],
ts.NodeFlags.Const,
),
));
if (isDefault) {
newStatement.push(
ts.createExportAssignment(
undefined,
undefined,
false,
ts.createIdentifier(name),
));
}
return newStatement;
}

View File

@ -17,6 +17,46 @@ const transform = (content: string) => transformJavascript(
// tslint:disable:no-big-function
describe('wrap enums and classes transformer', () => {
describe('wraps class declarations', () => {
it('should wrap default exported classes', () => {
const defaultClass = tags.stripIndent`
export default class CustomComponentEffects {
constructor(_actions) {
this._actions = _actions;
this.doThis = this._actions;
}
}
CustomComponentEffects.decorators = [{ type: Injectable }];
`;
const namedClass = tags.stripIndent`
class CustomComponent {
constructor(_actions) {
this._actions = _actions;
this.doThis = this._actions;
}
}
CustomComponent.decorators = [{ type: Injectable }];
`;
const output = tags.stripIndent`
const CustomComponentEffects = /*@__PURE__*/ (() => {
${defaultClass.replace('export default ', '')}
return CustomComponentEffects;
})();
export default CustomComponentEffects;
const CustomComponent = /*@__PURE__*/ (() => {
${namedClass}
return CustomComponent;
})();
`;
const input = defaultClass + namedClass;
expect(tags.oneLine`${transform(input)}`).toEqual(tags.oneLine`${output}`);
});
it('should wrap tsickle emitted classes which followed by metadata', () => {
const input = tags.stripIndent`
class CustomComponentEffects {
@ -169,6 +209,46 @@ describe('wrap enums and classes transformer', () => {
});
describe('wrap class expressions', () => {
it('should wrap default exported classes', () => {
const defaultClass = tags.stripIndent`
let Foo = class Foo {
};
Foo.bar = 'bar';
Foo = __decorate([
component()
], Foo);
export default Foo;
`;
const namedClass = tags.stripIndent`
let AggregateColumnDirective = class AggregateColumnDirective {
constructor(viewContainerRef) { }
};
AggregateColumnDirective = __decorate([
Directive({}),
__metadata("design:paramtypes", [ViewContainerRef])
], AggregateColumnDirective);
`;
const output = tags.stripIndent`
const Foo = /*@__PURE__*/ (() => {
${defaultClass.replace('export default Foo;', '')}
return Foo;
})();
export default Foo;
const AggregateColumnDirective = /*@__PURE__*/ (() => {
${namedClass}
return AggregateColumnDirective;
})();
`;
const input = defaultClass + namedClass;
expect(tags.oneLine`${transform(input)}`).toEqual(tags.oneLine`${output}`);
});
it('without property decorators in IIFE', () => {
const input = tags.stripIndent`
let AggregateColumnDirective = class AggregateColumnDirective {