From d9cc4b0289eaf382782a994a15497e9526c5a4a2 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Thu, 24 Nov 2022 12:21:50 +0000 Subject: [PATCH] fix(@ngtools/webpack): elide unused type references In the case the import declaration has an unused import we did not properly elide type references when `emitDecoratorMetadata` is disabled. Closes #24295 (cherry picked from commit 856720b913ec1ba82b96db2d3a61628cb3795f11) --- .../webpack/src/transformers/elide_imports.ts | 49 ++++++++++--------- .../src/transformers/elide_imports_spec.ts | 40 +++++++++++++++ 2 files changed, 66 insertions(+), 23 deletions(-) diff --git a/packages/ngtools/webpack/src/transformers/elide_imports.ts b/packages/ngtools/webpack/src/transformers/elide_imports.ts index babfd93904f5..3c8bed0e944e 100644 --- a/packages/ngtools/webpack/src/transformers/elide_imports.ts +++ b/packages/ngtools/webpack/src/transformers/elide_imports.ts @@ -54,31 +54,34 @@ export function elideImports( return; } - if (!ts.isTypeReferenceNode(node)) { - let symbol: ts.Symbol | undefined; - switch (node.kind) { - case ts.SyntaxKind.Identifier: - const parent = node.parent; - if (parent && ts.isShorthandPropertyAssignment(parent)) { - const shorthandSymbol = typeChecker.getShorthandAssignmentValueSymbol(parent); - if (shorthandSymbol) { - symbol = shorthandSymbol; - } - } else { - symbol = typeChecker.getSymbolAtLocation(node); + // Type reference imports do not need to be emitted when emitDecoratorMetadata is disabled. + if (ts.isTypeReferenceNode(node) && !compilerOptions.emitDecoratorMetadata) { + return; + } + + let symbol: ts.Symbol | undefined; + switch (node.kind) { + case ts.SyntaxKind.Identifier: + const parent = node.parent; + if (parent && ts.isShorthandPropertyAssignment(parent)) { + const shorthandSymbol = typeChecker.getShorthandAssignmentValueSymbol(parent); + if (shorthandSymbol) { + symbol = shorthandSymbol; } - break; - case ts.SyntaxKind.ExportSpecifier: - symbol = typeChecker.getExportSpecifierLocalTargetSymbol(node as ts.ExportSpecifier); - break; - case ts.SyntaxKind.ShorthandPropertyAssignment: - symbol = typeChecker.getShorthandAssignmentValueSymbol(node); - break; - } + } else { + symbol = typeChecker.getSymbolAtLocation(node); + } + break; + case ts.SyntaxKind.ExportSpecifier: + symbol = typeChecker.getExportSpecifierLocalTargetSymbol(node as ts.ExportSpecifier); + break; + case ts.SyntaxKind.ShorthandPropertyAssignment: + symbol = typeChecker.getShorthandAssignmentValueSymbol(node); + break; + } - if (symbol) { - usedSymbols.add(symbol); - } + if (symbol) { + usedSymbols.add(symbol); } ts.forEachChild(node, visit); diff --git a/packages/ngtools/webpack/src/transformers/elide_imports_spec.ts b/packages/ngtools/webpack/src/transformers/elide_imports_spec.ts index 196cbf3b6d8b..9e6a51e80c1b 100644 --- a/packages/ngtools/webpack/src/transformers/elide_imports_spec.ts +++ b/packages/ngtools/webpack/src/transformers/elide_imports_spec.ts @@ -66,6 +66,7 @@ describe('@ngtools/webpack transformers', () => { `, 'service.ts': ` export class Service { } + export class Service2 { } `, 'type.ts': ` export interface OnChanges { @@ -385,6 +386,45 @@ describe('@ngtools/webpack transformers', () => { expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`); }); + + it('should remove ctor parameter type reference and unused named import from same declaration', () => { + const input = tags.stripIndent` + import { Decorator } from './decorator'; + import { Service, Service2 as ServiceUnused } from './service'; + + @Decorator() + export class Foo { + constructor(param: Service) { + } + } + + ${dummyNode} + `; + + const output = tags.stripIndent` + import { __decorate } from "tslib"; + import { Decorator } from './decorator'; + + let Foo = class Foo { constructor(param) { } }; + Foo = __decorate([ Decorator() ], Foo); + export { Foo }; + `; + + const { program, compilerHost } = createTypescriptContext( + input, + additionalFiles, + true, + extraCompilerOptions, + ); + const result = transformTypescript( + undefined, + [transformer(program)], + program, + compilerHost, + ); + + expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`); + }); }); it('keeps jsxFactory imports when configured', () => {