fix(@angular-devkit/build-optimizer): don't add pure comments to tslib helpers (#15303)

Closes #15301
This commit is contained in:
Alan Agius 2019-08-12 20:07:24 +02:00 committed by Minko Gechev
parent b3b45469f5
commit e2bb482b44
7 changed files with 83 additions and 17 deletions

View File

@ -28,6 +28,7 @@ ts_library(
"@npm//@types/node",
"@npm//@types/webpack",
"@npm//source-map",
"@npm//tslib",
"@npm//typescript",
],
)

View File

@ -11,6 +11,7 @@
"dependencies": {
"loader-utils": "1.2.3",
"source-map": "0.7.3",
"tslib": "1.10.0",
"typescript": "3.5.3",
"webpack-sources": "1.4.3"
}

View File

@ -100,6 +100,44 @@ describe('build-optimizer', () => {
expect(boOutput.emitSkipped).toEqual(false);
});
it(`doesn't add pure comments to tslib helpers`, () => {
const input = tags.stripIndent`
class LanguageState {
}
LanguageState.ctorParameters = () => [
{ type: TranslateService },
{ type: undefined, decorators: [{ type: Inject, args: [LANGUAGE_CONFIG,] }] }
];
__decorate([
Action(CheckLanguage),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], LanguageState.prototype, "checkLanguage", null);
`;
const output = tags.oneLine`
let LanguageState = /*@__PURE__*/ (() => {
class LanguageState {
}
__decorate([
Action(CheckLanguage),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], LanguageState.prototype, "checkLanguage", null);
return LanguageState;
})();
`;
const boOutput = buildOptimizer({ content: input, isSideEffectFree: true });
expect(tags.oneLine`${boOutput.content}`).toEqual(output);
expect(boOutput.emitSkipped).toEqual(false);
});
it('should not wrap classes which had all static properties dropped in IIFE', () => {
const classDeclaration = tags.oneLine`
import { Injectable } from '@angular/core';

View File

@ -5,10 +5,15 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as tslib from 'tslib';
import * as ts from 'typescript';
const pureFunctionComment = '@__PURE__';
// We include only exports that start with '__' because tslib helpers
// all start with a suffix of two underscores.
const tslibHelpers = new Set<string>(Object.keys(tslib).filter(h => h.startsWith('__')));
// Find all nodes from the AST in the subtree of node of SyntaxKind kind.
export function collectDeepNodes<T extends ts.Node>(node: ts.Node, kind: ts.SyntaxKind): T[] {
const nodes: T[] = [];
@ -41,3 +46,7 @@ export function hasPureComment(node: ts.Node): boolean {
return !!leadingComment && leadingComment.some(comment => comment.text === pureFunctionComment);
}
export function isHelperName(name: string): boolean {
return tslibHelpers.has(name);
}

View File

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import { isHelperName } from '../helpers/ast-utils';
/**
* @deprecated From 0.9.0
@ -89,15 +90,3 @@ function createTslibImport(
return newNode;
}
}
function isHelperName(name: string): boolean {
// TODO: there are more helpers than these, should we replace them all?
const tsHelpers = [
'__extends',
'__decorate',
'__metadata',
'__param',
];
return tsHelpers.indexOf(name) !== -1;
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import { addPureComment, hasPureComment } from '../helpers/ast-utils';
import { addPureComment, hasPureComment, isHelperName } from '../helpers/ast-utils';
export function getPrefixFunctionsTransformer(): ts.TransformerFactory<ts.SourceFile> {
return (context: ts.TransformationContext): ts.Transformer<ts.SourceFile> => {
@ -44,10 +44,8 @@ export function findTopLevelFunctions(parentNode: ts.Node): Set<ts.Node> {
// need to mark function calls inside them as pure.
// Class static initializers in ES2015 are an exception we don't cover. They would need similar
// processing as enums to prevent property setting from causing the class to be retained.
if (ts.isFunctionDeclaration(node)
|| ts.isFunctionExpression(node)
|| ts.isClassDeclaration(node)
|| ts.isClassExpression(node)
if (ts.isFunctionLike(node)
|| ts.isClassLike(node)
|| ts.isArrowFunction(node)
|| ts.isMethodDeclaration(node)
) {
@ -78,9 +76,15 @@ export function findTopLevelFunctions(parentNode: ts.Node): Set<ts.Node> {
topLevelFunctions.add(node);
} else if (ts.isCallExpression(innerNode)) {
let expression: ts.Expression = innerNode.expression;
if (ts.isIdentifier(expression) && isHelperName(expression.text)) {
return;
}
while (expression && ts.isParenthesizedExpression(expression)) {
expression = expression.expression;
}
if (expression) {
if (ts.isFunctionExpression(expression)) {
// Skip IIFE's with arguments

View File

@ -186,4 +186,28 @@ describe('prefix-functions', () => {
expect(tags.oneLine`${transform(input)}`).toEqual(tags.oneLine`${output}`);
});
it(`doesn't add pure comments to tslib helpers`, () => {
const input = tags.stripIndent`
class LanguageState {
}
LanguageState.ctorParameters = () => [
{ type: TranslateService },
{ type: undefined, decorators: [{ type: Inject, args: [LANGUAGE_CONFIG,] }] }
];
__decorate([
Action(CheckLanguage),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], LanguageState.prototype, "checkLanguage", null);
`;
const output = input;
expect(tags.oneLine`${transform(input)}`).toEqual(tags.oneLine`${output}`);
});
});