From 1f1aa54fc8b8fb543cd83d7b9cc148d2732515cb Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Fri, 24 Nov 2017 16:28:57 -0500 Subject: [PATCH 1/3] refactor(@angular-devkit/build-optimizer): use TS2.6 and remove old helpers --- .../build_optimizer/package.json | 2 +- .../src/transforms/prefix-classes.ts | 78 +++++-------------- .../src/transforms/wrap-enums.ts | 22 +----- 3 files changed, 23 insertions(+), 79 deletions(-) diff --git a/packages/angular_devkit/build_optimizer/package.json b/packages/angular_devkit/build_optimizer/package.json index 23807ad81f..a986fb2541 100644 --- a/packages/angular_devkit/build_optimizer/package.json +++ b/packages/angular_devkit/build_optimizer/package.json @@ -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" } } diff --git a/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes.ts b/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes.ts index ab534f3572..a5402a696b 100644 --- a/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes.ts +++ b/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes.ts @@ -46,7 +46,7 @@ export function getPrefixClassesTransformer(): ts.TransformerFactory => { // 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; @@ -87,54 +87,10 @@ export function getPrefixClassesTransformer(): ts.TransformerFactory 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; @@ -192,7 +148,7 @@ 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; } @@ -200,13 +156,13 @@ function isDownleveledClass(node: ts.Node): boolean { 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; @@ -218,7 +174,8 @@ 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; } @@ -226,13 +183,14 @@ function isDownleveledClass(node: ts.Node): boolean { 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; } @@ -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; diff --git a/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums.ts b/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums.ts index 8026591250..267c246a4a 100644 --- a/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums.ts +++ b/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums.ts @@ -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 { return (context: ts.TransformationContext): ts.Transformer => { const transformer: ts.Transformer = (sf: ts.SourceFile) => { @@ -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) { @@ -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); @@ -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); From c9713de669ee15453a8778a81f95d425cb6f8d01 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Fri, 24 Nov 2017 19:48:06 -0500 Subject: [PATCH 2/3] refactor(@angular-devkit/build-optimizer): leverage TS2.6 in tslib pass --- .../src/transforms/import-tslib.ts | 79 +++++-------------- 1 file changed, 19 insertions(+), 60 deletions(-) diff --git a/packages/angular_devkit/build_optimizer/src/transforms/import-tslib.ts b/packages/angular_devkit/build_optimizer/src/transforms/import-tslib.ts index 9880900f42..4e5e2bf282 100644 --- a/packages/angular_devkit/build_optimizer/src/transforms/import-tslib.ts +++ b/packages/angular_devkit/build_optimizer/src/transforms/import-tslib.ts @@ -30,30 +30,33 @@ export function getImportTslibTransformer(): ts.TransformerFactory { - // 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, @@ -69,11 +72,7 @@ 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')); @@ -81,25 +80,7 @@ function createTslibImport(node: ts.Node, useRequire = false): ts.Node { } } -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', @@ -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; } From ec2ca4c8200f00c005657caef448b5a925be81bd Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Fri, 24 Nov 2017 20:19:38 -0500 Subject: [PATCH 3/3] test(@angular-devkit/build-optimizer): update comments for TS2.6 --- .../build_optimizer/src/transforms/prefix-classes_spec.ts | 6 +++--- .../build_optimizer/src/transforms/wrap-enums_spec.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes_spec.ts b/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes_spec.ts index 2944b19eac..c0a3cae25c 100644 --- a/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes_spec.ts +++ b/packages/angular_devkit/build_optimizer/src/transforms/prefix-classes_spec.ts @@ -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() { @@ -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() { @@ -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); diff --git a/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums_spec.ts b/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums_spec.ts index 5f41c7aef1..e4d7f3780e 100644 --- a/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums_spec.ts +++ b/packages/angular_devkit/build_optimizer/src/transforms/wrap-enums_spec.ts @@ -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) {