Skip to content

Commit cf9afee

Browse files
alan-agius4dgp1130
authored andcommitted
fix(@ngtools/webpack): elide type only named imports when using emitDecoratorMetadata
With this change we fix an issue where type only named imports were being emitted. As a result webpack failed to resolve such symbols as they don't exist in JavaScript. Closes #23667
1 parent 3fb569b commit cf9afee

File tree

2 files changed

+45
-38
lines changed

2 files changed

+45
-38
lines changed

packages/ngtools/webpack/src/transformers/elide_imports.ts

+7-38
Original file line numberDiff line numberDiff line change
@@ -54,39 +54,8 @@ export function elideImports(
5454
return;
5555
}
5656

57-
let symbol: ts.Symbol | undefined;
58-
if (ts.isTypeReferenceNode(node)) {
59-
if (!compilerOptions.emitDecoratorMetadata) {
60-
// Skip and mark as unused if emitDecoratorMetadata is disabled.
61-
return;
62-
}
63-
64-
const parent = node.parent;
65-
let isTypeReferenceForDecoratoredNode = false;
66-
67-
switch (parent.kind) {
68-
case ts.SyntaxKind.GetAccessor:
69-
case ts.SyntaxKind.PropertyDeclaration:
70-
case ts.SyntaxKind.MethodDeclaration:
71-
isTypeReferenceForDecoratoredNode = !!parent.decorators?.length;
72-
break;
73-
case ts.SyntaxKind.Parameter:
74-
// - A constructor parameter can be decorated or the class itself is decorated.
75-
// - The parent of the parameter is decorated example a method declaration or a set accessor.
76-
// In all cases we need the type reference not to be elided.
77-
isTypeReferenceForDecoratoredNode = !!(
78-
parent.decorators?.length ||
79-
(ts.isSetAccessor(parent.parent) && !!parent.parent.decorators?.length) ||
80-
(ts.isConstructorDeclaration(parent.parent) &&
81-
!!parent.parent.parent.decorators?.length)
82-
);
83-
break;
84-
}
85-
86-
if (isTypeReferenceForDecoratoredNode) {
87-
symbol = typeChecker.getSymbolAtLocation(node.typeName);
88-
}
89-
} else {
57+
if (!ts.isTypeReferenceNode(node)) {
58+
let symbol: ts.Symbol | undefined;
9059
switch (node.kind) {
9160
case ts.SyntaxKind.Identifier:
9261
const parent = node.parent;
@@ -106,10 +75,10 @@ export function elideImports(
10675
symbol = typeChecker.getShorthandAssignmentValueSymbol(node);
10776
break;
10877
}
109-
}
11078

111-
if (symbol) {
112-
usedSymbols.add(symbol);
79+
if (symbol) {
80+
usedSymbols.add(symbol);
81+
}
11382
}
11483

11584
ts.forEachChild(node, visit);
@@ -153,7 +122,7 @@ export function elideImports(
153122
clausesCount += namedBindings.elements.length;
154123

155124
for (const specifier of namedBindings.elements) {
156-
if (isUnused(specifier.name)) {
125+
if (specifier.isTypeOnly || isUnused(specifier.name)) {
157126
removedClausesCount++;
158127
// in case we don't have any more namedImports we should remove the parent ie the {}
159128
const nodeToRemove =
@@ -168,7 +137,7 @@ export function elideImports(
168137
if (node.importClause.name) {
169138
clausesCount++;
170139

171-
if (isUnused(node.importClause.name)) {
140+
if (node.importClause.isTypeOnly || isUnused(node.importClause.name)) {
172141
specifierNodeRemovals.push(node.importClause.name);
173142
}
174143
}

packages/ngtools/webpack/src/transformers/elide_imports_spec.ts

+38
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,44 @@ describe('@ngtools/webpack transformers', () => {
440440
experimentalDecorators: true,
441441
};
442442

443+
it('should elide type only named imports', () => {
444+
const input = tags.stripIndent`
445+
import { Decorator } from './decorator';
446+
import { type OnChanges, type SimpleChanges } from './type';
447+
448+
@Decorator()
449+
export class Foo implements OnChanges {
450+
ngOnChanges(changes: SimpleChanges) { }
451+
}
452+
453+
${dummyNode}
454+
`;
455+
456+
const output = tags.stripIndent`
457+
import { __decorate } from "tslib";
458+
import { Decorator } from './decorator';
459+
460+
let Foo = class Foo { ngOnChanges(changes) { } };
461+
Foo = __decorate([ Decorator() ], Foo);
462+
export { Foo };
463+
`;
464+
465+
const { program, compilerHost } = createTypescriptContext(
466+
input,
467+
additionalFiles,
468+
true,
469+
extraCompilerOptions,
470+
);
471+
const result = transformTypescript(
472+
undefined,
473+
[transformer(program)],
474+
program,
475+
compilerHost,
476+
);
477+
478+
expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`);
479+
});
480+
443481
it('should not remove ctor parameter type reference', () => {
444482
const input = tags.stripIndent`
445483
import { Decorator } from './decorator';

0 commit comments

Comments
 (0)