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/node",
"@npm//@types/webpack", "@npm//@types/webpack",
"@npm//source-map", "@npm//source-map",
"@npm//tslib",
"@npm//typescript", "@npm//typescript",
], ],
) )

View File

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

View File

@ -100,6 +100,44 @@ describe('build-optimizer', () => {
expect(boOutput.emitSkipped).toEqual(false); 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', () => { it('should not wrap classes which had all static properties dropped in IIFE', () => {
const classDeclaration = tags.oneLine` const classDeclaration = tags.oneLine`
import { Injectable } from '@angular/core'; 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 * 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 * found in the LICENSE file at https://angular.io/license
*/ */
import * as tslib from 'tslib';
import * as ts from 'typescript'; import * as ts from 'typescript';
const pureFunctionComment = '@__PURE__'; 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. // 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[] { export function collectDeepNodes<T extends ts.Node>(node: ts.Node, kind: ts.SyntaxKind): T[] {
const nodes: T[] = []; const nodes: T[] = [];
@ -41,3 +46,7 @@ export function hasPureComment(node: ts.Node): boolean {
return !!leadingComment && leadingComment.some(comment => comment.text === pureFunctionComment); 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 * found in the LICENSE file at https://angular.io/license
*/ */
import * as ts from 'typescript'; import * as ts from 'typescript';
import { isHelperName } from '../helpers/ast-utils';
/** /**
* @deprecated From 0.9.0 * @deprecated From 0.9.0
@ -89,15 +90,3 @@ function createTslibImport(
return newNode; 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 * found in the LICENSE file at https://angular.io/license
*/ */
import * as ts from 'typescript'; 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> { export function getPrefixFunctionsTransformer(): ts.TransformerFactory<ts.SourceFile> {
return (context: ts.TransformationContext): ts.Transformer<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. // 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 // 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. // processing as enums to prevent property setting from causing the class to be retained.
if (ts.isFunctionDeclaration(node) if (ts.isFunctionLike(node)
|| ts.isFunctionExpression(node) || ts.isClassLike(node)
|| ts.isClassDeclaration(node)
|| ts.isClassExpression(node)
|| ts.isArrowFunction(node) || ts.isArrowFunction(node)
|| ts.isMethodDeclaration(node) || ts.isMethodDeclaration(node)
) { ) {
@ -78,9 +76,15 @@ export function findTopLevelFunctions(parentNode: ts.Node): Set<ts.Node> {
topLevelFunctions.add(node); topLevelFunctions.add(node);
} else if (ts.isCallExpression(innerNode)) { } else if (ts.isCallExpression(innerNode)) {
let expression: ts.Expression = innerNode.expression; let expression: ts.Expression = innerNode.expression;
if (ts.isIdentifier(expression) && isHelperName(expression.text)) {
return;
}
while (expression && ts.isParenthesizedExpression(expression)) { while (expression && ts.isParenthesizedExpression(expression)) {
expression = expression.expression; expression = expression.expression;
} }
if (expression) { if (expression) {
if (ts.isFunctionExpression(expression)) { if (ts.isFunctionExpression(expression)) {
// Skip IIFE's with arguments // Skip IIFE's with arguments

View File

@ -186,4 +186,28 @@ describe('prefix-functions', () => {
expect(tags.oneLine`${transform(input)}`).toEqual(tags.oneLine`${output}`); 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}`);
});
}); });