From a816562e5eb59a1f2bcb38add16a6cfef6b28afc Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Wed, 6 Nov 2019 16:15:03 +0000 Subject: [PATCH] fix(@ngtools/webpack): also elide type references on transform Fix #14617 --- .../webpack/src/transformers/elide_imports.ts | 4 +- .../src/transformers/elide_imports_spec.ts | 19 +++++++-- .../transformers/remove_decorators_spec.ts | 42 +++++++++++++++++++ 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/packages/ngtools/webpack/src/transformers/elide_imports.ts b/packages/ngtools/webpack/src/transformers/elide_imports.ts index c7b2cd10654e..bc61bc58b762 100644 --- a/packages/ngtools/webpack/src/transformers/elide_imports.ts +++ b/packages/ngtools/webpack/src/transformers/elide_imports.ts @@ -33,8 +33,8 @@ export function elideImports( const imports: ts.ImportDeclaration[] = []; ts.forEachChild(sourceFile, function visit(node) { - // Skip removed nodes - if (removedNodes.includes(node)) { + // Skip type references and removed nodes. We consider both unused. + if (node.kind == ts.SyntaxKind.TypeReference || removedNodes.includes(node)) { return; } diff --git a/packages/ngtools/webpack/src/transformers/elide_imports_spec.ts b/packages/ngtools/webpack/src/transformers/elide_imports_spec.ts index 07ae53b5b173..c7ed3ece60f9 100644 --- a/packages/ngtools/webpack/src/transformers/elide_imports_spec.ts +++ b/packages/ngtools/webpack/src/transformers/elide_imports_spec.ts @@ -33,6 +33,19 @@ describe('@ngtools/webpack transformers', () => { `, }; + it('should remove unused imports', () => { + const input = tags.stripIndent` + import { promise } from './const'; + import { take } from './const'; + const unused = promise; + `; + + const { program, compilerHost } = createTypescriptContext(input, additionalFiles); + const result = transformTypescript(undefined, [transformer(program)], program, compilerHost); + + expect(tags.oneLine`${result}`).toEqual(''); + }); + it('should remove unused aliased imports', () => { const input = tags.stripIndent` import { promise as fromPromise } from './const'; @@ -188,7 +201,7 @@ describe('@ngtools/webpack transformers', () => { expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`); }); - it('should only drop unused default imports when named and default', () => { + it('should only drop unused default imports when named and default (1)', () => { const input = tags.stripIndent` import promise, { promise as fromPromise } from './const'; const used = fromPromise; @@ -206,7 +219,7 @@ describe('@ngtools/webpack transformers', () => { expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`); }); - it('should only drop unused named imports when named and default', () => { + it('should only drop unused named imports when named and default (2)', () => { const input = tags.stripIndent` import promise, { promise as fromPromise, take } from './const'; const used = fromPromise; @@ -224,7 +237,7 @@ describe('@ngtools/webpack transformers', () => { expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`); }); - it('should only drop default imports when having named and default', () => { + it('should only drop default imports when having named and default (3)', () => { const input = tags.stripIndent` import promise, { fromPromise } from './const'; const used = promise; diff --git a/packages/ngtools/webpack/src/transformers/remove_decorators_spec.ts b/packages/ngtools/webpack/src/transformers/remove_decorators_spec.ts index 4ef3c52e9965..962f4fe9d434 100644 --- a/packages/ngtools/webpack/src/transformers/remove_decorators_spec.ts +++ b/packages/ngtools/webpack/src/transformers/remove_decorators_spec.ts @@ -248,5 +248,47 @@ describe('@ngtools/webpack transformers', () => { expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`); }); + + it('should not prevent removal of unused imports', () => { + // Note: this is actually testing the behaviour of `./elide_imports.ts`, but the problem + // only came up when used together with `./remove_decorators.ts` so we test it here. + // Originally reported as https://github.com/angular/angular-cli/issues/14617 + // The bug happened because `elideImports` would attempt to remove `BigDependency` and + // in the process modify the import statement, which then prevented TS from removing it + // the interface too. + const input = tags.stripIndent` + import {BigDependency, BigDependencyOptions} from 'src/app/lorem/big-dependency'; + import {Injectable} from '@angular/core'; + + const bigDependencyLoader = () => import('../lorem/big-dependency'); + + @Injectable({providedIn: 'root'}) + export class LoremService { + load(options: BigDependencyOptions): Promise { + return bigDependencyLoader() + .then((m: {BigDependency}) => new m.BigDependency().setOptions(options)); + } + } + + `; + const output = tags.stripIndent` + const bigDependencyLoader = () => import('../lorem/big-dependency'); + export class LoremService { + load(options) { + return bigDependencyLoader() + .then((m) => new m.BigDependency().setOptions(options)); + } + } + `; + + const { program, compilerHost } = createTypescriptContext(input); + const transformer = removeDecorators( + () => true, + () => program.getTypeChecker(), + ); + const result = transformTypescript(undefined, [transformer], program, compilerHost); + + expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`); + }); }); });