From 3bcdd46f6d11a15d8e5627fbbf1197d86e3aebe8 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Mon, 15 Jan 2018 12:13:18 -0500 Subject: [PATCH] fix(@angular/cli): workaround TS decorator transformer defect --- .../src/transformers/make_transform.ts | 15 ++++-- .../transformers/remove_decorators.spec.ts | 48 +++++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/packages/@ngtools/webpack/src/transformers/make_transform.ts b/packages/@ngtools/webpack/src/transformers/make_transform.ts index ae6befae01bf..c2f6ed85eb59 100644 --- a/packages/@ngtools/webpack/src/transformers/make_transform.ts +++ b/packages/@ngtools/webpack/src/transformers/make_transform.ts @@ -129,11 +129,18 @@ function visitEachChildWorkaround(node: ts.Node, visitor: ts.Visitor, } -// If TS sees an empty decorator array, it will still emit a `__decorate` call. -// This seems to be a TS bug. +// 1) If TS sees an empty decorator array, it will still emit a `__decorate` call. +// This seems to be a TS bug. +// 2) Also ensure nodes with modified decorators have parents +// built in TS transformers assume certain nodes have parents (fixed in TS 2.7+) function cleanupDecorators(node: ts.Node) { - if (node.decorators && node.decorators.length == 0) { - node.decorators = undefined; + if (node.decorators) { + if (node.decorators.length == 0) { + node.decorators = undefined; + } else if (node.parent == undefined) { + const originalNode = ts.getParseTreeNode(node); + node.parent = originalNode.parent; + } } ts.forEachChild(node, node => cleanupDecorators(node)); diff --git a/packages/@ngtools/webpack/src/transformers/remove_decorators.spec.ts b/packages/@ngtools/webpack/src/transformers/remove_decorators.spec.ts index 3fbfe4ad7203..c25503be355b 100644 --- a/packages/@ngtools/webpack/src/transformers/remove_decorators.spec.ts +++ b/packages/@ngtools/webpack/src/transformers/remove_decorators.spec.ts @@ -76,6 +76,54 @@ describe('@ngtools/webpack transformers', () => { expect(oneLine`${result}`).toEqual(oneLine`${output}`); }); + it('should keep other decorators on class member', () => { + const input = stripIndent` + import { Component, HostListener } from '@angular/core'; + import { AnotherDecorator } from 'another-lib'; + + @Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] + }) + export class AppComponent { + title = 'app'; + + @HostListener('document:keydown.escape') + @AnotherDecorator() + onEscape() { + console.log('run'); + } + } + `; + const output = stripIndent` + import * as tslib_1 from "tslib"; + import { AnotherDecorator } from 'another-lib'; + + export class AppComponent { + constructor() { + this.title = 'app'; + } + + onEscape() { + console.log('run'); + } + } + tslib_1.__decorate([ + AnotherDecorator() + ], AppComponent.prototype, "onEscape", null); + `; + + const { program, compilerHost } = createTypescriptContext(input); + const transformer = removeDecorators( + () => true, + () => program.getTypeChecker(), + ); + const result = transformTypescript(undefined, [transformer], program, compilerHost); + + expect(oneLine`${result}`).toEqual(oneLine`${output}`); + }); + it('should remove imports for identifiers within the decorator', () => { const input = stripIndent` import { Component } from '@angular/core';