Skip to content
This repository was archived by the owner on Apr 4, 2025. It is now read-only.

Update Build Optimizer to use TS 2.6 #299

Merged
merged 3 commits into from
Nov 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/angular_devkit/build_optimizer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"dependencies": {
"loader-utils": "^1.1.0",
"source-map": "^0.5.6",
"typescript": "^2.3.3",
"typescript": "~2.6.1",
"webpack-sources": "^1.0.1"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,33 @@ export function getImportTslibTransformer(): ts.TransformerFactory<ts.SourceFile

const visitor: ts.Visitor = (node: ts.Node): ts.Node => {

// Check if node is a TS helper declaration.
if (isTsHelper(node)) {
// Replace node with import for that helper.
return ts.visitEachChild(createTslibImport(node, useRequire), visitor, context);
// Check if node is a TS helper declaration and replace with import if yes
if (ts.isVariableStatement(node)) {
const declarations = node.declarationList.declarations;

if (declarations.length === 1 && ts.isIdentifier(declarations[0].name)) {
// NOTE: the replace is unnecessary with TS2.5+; tests currently run with TS2.4
const name = (declarations[0].name as ts.Identifier).text.replace(/^___/, '__');

if (isHelperName(name)) {
// TODO: maybe add a few more checks, like checking the first part of the assignment.

return createTslibImport(name, useRequire);
}
}
}

// Otherwise return node as is.
return ts.visitEachChild(node, visitor, context);
};

return ts.visitNode(sf, visitor);
return ts.visitEachChild(sf, visitor, context);
};

return transformer;
};
}

function createTslibImport(node: ts.Node, useRequire = false): ts.Node {
const name = getVariableStatementName(node);

if (!name) {
return node;
}

function createTslibImport(name: string, useRequire = false): ts.Node {
if (useRequire) {
// Use `var __helper = /*@__PURE__*/ require("tslib").__helper`.
const requireCall = ts.createCall(ts.createIdentifier('require'), undefined,
Expand All @@ -69,37 +72,15 @@ function createTslibImport(node: ts.Node, useRequire = false): ts.Node {
// Use `import { __helper } from "tslib"`.
const namedImports = ts.createNamedImports([ts.createImportSpecifier(undefined,
ts.createIdentifier(name))]);
// typescript@2.4 is needed for a fix to the function parameter types of ts.createImportClause.
// https://github.com/Microsoft/TypeScript/pull/15999
// Hiding it from lint until we upgrade.
// tslint:disable-next-line:no-any
const importClause = (ts.createImportClause as any)(undefined, namedImports);
const importClause = ts.createImportClause(undefined, namedImports);
const newNode = ts.createImportDeclaration(undefined, undefined, importClause,
ts.createLiteral('tslib'));

return newNode;
}
}

function isTsHelper(node: ts.Node): boolean {
if (node.kind !== ts.SyntaxKind.VariableStatement) {
return false;
}
const varStmt = node as ts.VariableStatement;
if (varStmt.declarationList.declarations.length > 1) {
return false;
}
const varDecl = varStmt.declarationList.declarations[0];
if (varDecl.name.kind !== ts.SyntaxKind.Identifier) {
return false;
}

const name = getVariableStatementName(node);

if (!name) {
return false;
}

function isHelperName(name: string): boolean {
// TODO: there are more helpers than these, should we replace them all?
const tsHelpers = [
'__extends',
Expand All @@ -108,27 +89,5 @@ function isTsHelper(node: ts.Node): boolean {
'__param',
];

if (tsHelpers.indexOf(name) === -1) {
return false;
}

// TODO: maybe add a few more checks, like checking the first part of the assignment.

return true;
}

function getVariableStatementName(node: ts.Node) {
const varStmt = node as ts.VariableStatement;
if (varStmt.declarationList.declarations.length > 1) {
return null;
}
const varDecl = varStmt.declarationList.declarations[0];
if (varDecl.name.kind !== ts.SyntaxKind.Identifier) {
return null;
}

const name = (varDecl.name as ts.Identifier).text;

// node.getText() on a name that starts with two underscores will return three instead.
return name.replace(/^___/, '__');
return tsHelpers.indexOf(name) !== -1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export function getPrefixClassesTransformer(): ts.TransformerFactory<ts.SourceFi
const visitor: ts.Visitor = (node: ts.Node): ts.VisitResult<ts.Node> => {

// Add pure comment to downleveled classes.
if (isVariableStatement(node) && isDownleveledClass(node)) {
if (ts.isVariableStatement(node) && isDownleveledClass(node)) {
const varDecl = node.declarationList.declarations[0];
const varInitializer = varDecl.initializer as ts.Expression;

Expand Down Expand Up @@ -87,54 +87,10 @@ export function getPrefixClassesTransformer(): ts.TransformerFactory<ts.SourceFi
};
}

function isVariableStatement(node: ts.Node): node is ts.VariableStatement {
return node.kind === ts.SyntaxKind.VariableStatement;
}

function isIdentifier(node: ts.Node): node is ts.Identifier {
return node.kind === ts.SyntaxKind.Identifier;
}

function isExpressionStatement(node: ts.Node): node is ts.ExpressionStatement {
return node.kind === ts.SyntaxKind.ExpressionStatement;
}

function isParenthesizedExpression(node: ts.Node): node is ts.ParenthesizedExpression {
return node.kind === ts.SyntaxKind.ParenthesizedExpression;
}

function isCallExpression(node: ts.Node): node is ts.CallExpression {
return node.kind === ts.SyntaxKind.CallExpression;
}

function isFunctionExpression(node: ts.Node): node is ts.FunctionExpression {
return node.kind === ts.SyntaxKind.FunctionExpression;
}

function isArrowFunction(node: ts.Node): node is ts.ArrowFunction {
return node.kind === ts.SyntaxKind.ArrowFunction;
}

function isFunctionDeclaration(node: ts.Node): node is ts.FunctionDeclaration {
return node.kind === ts.SyntaxKind.FunctionDeclaration;
}

function isReturnStatement(node: ts.Node): node is ts.ReturnStatement {
return node.kind === ts.SyntaxKind.ReturnStatement;
}

function isBlock(node: ts.Node): node is ts.Block {
return node.kind === ts.SyntaxKind.Block;
}

function isClassDeclaration(node: ts.Node): node is ts.ClassDeclaration {
return node.kind === ts.SyntaxKind.ClassDeclaration;
}

// Determine if a node matched the structure of a downleveled TS class.
function isDownleveledClass(node: ts.Node): boolean {

if (!isVariableStatement(node)) {
if (!ts.isVariableStatement(node)) {
return false;
}

Expand All @@ -144,7 +100,7 @@ function isDownleveledClass(node: ts.Node): boolean {

const variableDeclaration = node.declarationList.declarations[0];

if (!isIdentifier(variableDeclaration.name)
if (!ts.isIdentifier(variableDeclaration.name)
|| !variableDeclaration.initializer) {
return false;
}
Expand All @@ -154,19 +110,19 @@ function isDownleveledClass(node: ts.Node): boolean {
// TS 2.3 has an unwrapped class IIFE
// TS 2.4 uses a function expression wrapper
// TS 2.5 uses an arrow function wrapper
if (isParenthesizedExpression(potentialClass)) {
if (ts.isParenthesizedExpression(potentialClass)) {
potentialClass = potentialClass.expression;
}

if (!isCallExpression(potentialClass) || potentialClass.arguments.length > 1) {
if (!ts.isCallExpression(potentialClass) || potentialClass.arguments.length > 1) {
return false;
}

let wrapperBody: ts.Block;
if (isFunctionExpression(potentialClass.expression)) {
if (ts.isFunctionExpression(potentialClass.expression)) {
wrapperBody = potentialClass.expression.body;
} else if (isArrowFunction(potentialClass.expression)
&& isBlock(potentialClass.expression.body)) {
} else if (ts.isArrowFunction(potentialClass.expression)
&& ts.isBlock(potentialClass.expression.body)) {
wrapperBody = potentialClass.expression.body;
} else {
return false;
Expand All @@ -192,21 +148,21 @@ function isDownleveledClass(node: ts.Node): boolean {
// find return statement - may not be last statement
let returnStatement: ts.ReturnStatement | undefined;
for (let i = functionStatements.length - 1; i > 0; i--) {
if (isReturnStatement(functionStatements[i])) {
if (ts.isReturnStatement(functionStatements[i])) {
returnStatement = functionStatements[i] as ts.ReturnStatement;
break;
}
}

if (returnStatement == undefined
|| returnStatement.expression == undefined
|| !isIdentifier(returnStatement.expression)) {
|| !ts.isIdentifier(returnStatement.expression)) {
return false;
}

if (functionExpression.parameters.length === 0) {
// potential non-extended class or wrapped es2015 class
return (isFunctionDeclaration(firstStatement) || isClassDeclaration(firstStatement))
return (ts.isFunctionDeclaration(firstStatement) || ts.isClassDeclaration(firstStatement))
&& firstStatement.name !== undefined
&& firstStatement.name.text === className
&& returnStatement.expression.text === firstStatement.name.text;
Expand All @@ -218,21 +174,23 @@ function isDownleveledClass(node: ts.Node): boolean {

const functionParameter = functionExpression.parameters[0];

if (!isIdentifier(functionParameter.name) || functionParameter.name.text !== superParameterName) {
if (!ts.isIdentifier(functionParameter.name)
|| functionParameter.name.text !== superParameterName) {
return false;
}

if (functionStatements.length < 3) {
return false;
}

if (!isExpressionStatement(firstStatement) || !isCallExpression(firstStatement.expression)) {
if (!ts.isExpressionStatement(firstStatement)
|| !ts.isCallExpression(firstStatement.expression)) {
return false;
}

const extendCallExpression = firstStatement.expression;

if (!isIdentifier(extendCallExpression.expression)
if (!ts.isIdentifier(extendCallExpression.expression)
|| !extendCallExpression.expression.text.endsWith(extendsHelperName)) {
return false;
}
Expand All @@ -243,13 +201,13 @@ function isDownleveledClass(node: ts.Node): boolean {

const lastArgument = extendCallExpression.arguments[extendCallExpression.arguments.length - 1];

if (!isIdentifier(lastArgument) || lastArgument.text !== functionParameter.name.text) {
if (!ts.isIdentifier(lastArgument) || lastArgument.text !== functionParameter.name.text) {
return false;
}

const secondStatement = functionStatements[1];

return isFunctionDeclaration(secondStatement)
return ts.isFunctionDeclaration(secondStatement)
&& secondStatement.name !== undefined
&& className.endsWith(secondStatement.name.text)
&& returnStatement.expression.text === secondStatement.name.text;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ describe('prefix-classes', () => {
expect(tags.oneLine`${transform(input)}`).toEqual(tags.oneLine`${output}`);
});

it('prefix TS 2.5 downlevel class', () => {
it('prefix TS 2.5 - 2.6 downlevel class', () => {
const input = tags.stripIndent`
var BasicTestCase = /** @class */ (function () {
function BasicTestCase() {
Expand All @@ -171,7 +171,7 @@ describe('prefix-classes', () => {
expect(tags.oneLine`${transform(input)}`).toEqual(tags.oneLine`${output}`);
});

it('prefix TS 2.5 downlevel class with static variable', () => {
it('prefix TS 2.5 - 2.6 downlevel class with static variable', () => {
const input = tags.stripIndent`
var StaticTestCase = /** @class */ (function () {
function StaticTestCase() {
Expand All @@ -193,7 +193,7 @@ describe('prefix-classes', () => {
expect(tags.oneLine`${transform(input)}`).toEqual(tags.oneLine`${output}`);
});

it('prefix TS 2.5 downlevel class with extends', () => {
it('prefix TS 2.5 - 2.6 downlevel class with extends', () => {
const input = tags.stripIndent`
var ExtendedClass = /** @class */ (function (_super) {
__extends(ExtendedClass, _super);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,6 @@ function isBlockLike(node: ts.Node): node is ts.BlockLike {
|| node.kind === ts.SyntaxKind.SourceFile;
}

// NOTE: 'isXXXX' helper functions can be replaced with native TS helpers with TS 2.4+

function isVariableStatement(node: ts.Node): node is ts.VariableStatement {
return node.kind === ts.SyntaxKind.VariableStatement;
}

function isIdentifier(node: ts.Node): node is ts.Identifier {
return node.kind === ts.SyntaxKind.Identifier;
}

function isObjectLiteralExpression(node: ts.Node): node is ts.ObjectLiteralExpression {
return node.kind === ts.SyntaxKind.ObjectLiteralExpression;
}

export function getWrapEnumsTransformer(): ts.TransformerFactory<ts.SourceFile> {
return (context: ts.TransformationContext): ts.Transformer<ts.SourceFile> => {
const transformer: ts.Transformer<ts.SourceFile> = (sf: ts.SourceFile) => {
Expand Down Expand Up @@ -104,11 +90,11 @@ function visitBlockStatements(
// * have only one declaration
// * have an identifer as a declaration name
if (oIndex < statements.length - 1
&& isVariableStatement(currentStatement)
&& ts.isVariableStatement(currentStatement)
&& currentStatement.declarationList.declarations.length === 1) {

const variableDeclaration = currentStatement.declarationList.declarations[0];
if (isIdentifier(variableDeclaration.name)) {
if (ts.isIdentifier(variableDeclaration.name)) {
const name = variableDeclaration.name.text;

if (!variableDeclaration.initializer) {
Expand All @@ -129,7 +115,7 @@ function visitBlockStatements(
oIndex++;
continue;
}
} else if (isObjectLiteralExpression(variableDeclaration.initializer)
} else if (ts.isObjectLiteralExpression(variableDeclaration.initializer)
&& variableDeclaration.initializer.properties.length === 0) {
const nextStatements = statements.slice(oIndex + 1);
const enumStatements = findTs2_2EnumStatements(name, nextStatements);
Expand All @@ -149,7 +135,7 @@ function visitBlockStatements(
oIndex += enumStatements.length;
continue;
}
} else if (isObjectLiteralExpression(variableDeclaration.initializer)
} else if (ts.isObjectLiteralExpression(variableDeclaration.initializer)
&& variableDeclaration.initializer.properties.length !== 0) {
const literalPropertyCount = variableDeclaration.initializer.properties.length;
const nextStatements = statements.slice(oIndex + 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe('wrap-enums', () => {
expect(tags.oneLine`${transform(input)}`).toEqual(tags.oneLine`${output}`);
});

it('wraps ts 2.3 enums in IIFE', () => {
it('wraps ts 2.3 - 2.6 enums in IIFE', () => {
const input = tags.stripIndent`
export var ChangeDetectionStrategy;
(function (ChangeDetectionStrategy) {
Expand Down