Skip to content

fix(@ngtools/webpack): elide imports used only in type re-exports or type declarations - #34582

Merged
alan-agius4 merged 1 commit into
angular:mainfrom
alan-agius4:fix/elide-imports-type-exports
Oct 29, 2026
Merged

alan-agius4 merged 1 commit into
angular:mainfrom
alan-agius4:fix/elide-imports-type-exports

Conversation

@alan-agius4

Copy link
Copy Markdown
Collaborator

When elideImports removes unused import specifiers (such as after removeIvyJitSupportCalls removes ɵsetClassMetadata), it previously walked into interface declarations, type alias declarations, and type-only export declarations/specifiers (export type { X }, export { type X }). This caused it to mark those type-only imported symbols as used at runtime and rewrite the ImportDeclaration while retaining those specifiers.

Because the ImportDeclaration AST node was replaced by a before transformer, TypeScript skipped its own type-only import elision on the cloned declaration, leaving the type-only specifier in the emitted JS output. With webpack 5.112.0+, unused import specifiers are still link-checked against the target module, causing builds with strictExportPresence: true to fail.

Skip pure type declarations (InterfaceDeclaration, TypeAliasDeclaration, TypeParameterDeclaration) and type-only exports (ExportDeclaration and ExportSpecifier with isTypeOnly) when collecting used identifiers in elideImports.

Closes #34580

…type declarations

When `elideImports` removes unused import specifiers (such as after
`removeIvyJitSupportCalls` removes `ɵsetClassMetadata`), it previously
walked into `interface` declarations, `type` alias declarations, and
type-only `export` declarations/specifiers (`export type { X }`,
`export { type X }`). This caused it to mark those type-only imported
symbols as used at runtime and rewrite the `ImportDeclaration` while
retaining those specifiers.

Because the `ImportDeclaration` AST node was replaced by a `before`
transformer, TypeScript skipped its own type-only import elision on the
cloned declaration, leaving the type-only specifier in the emitted JS
output. With webpack 5.112.0+, unused import specifiers are still
link-checked against the target module, causing builds with
`strictExportPresence: true` to fail.

Skip pure type declarations (`InterfaceDeclaration`,
`TypeAliasDeclaration`, `TypeParameterDeclaration`) and type-only
exports (`ExportDeclaration` and `ExportSpecifier` with `isTypeOnly`)
when collecting used identifiers in `elideImports`.

Closes angular#34580
@alan-agius4 alan-agius4 added action: review The PR is still awaiting reviews from at least one requested reviewer target: patch This PR is targeted for the next patch release labels Oct 29, 2026
@alan-agius4
alan-agius4 requested a review from clydin October 29, 2026 08:24

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the import elision logic in both the JIT bootstrap transformer and the Webpack elide_imports transformer to correctly identify and erase pure type declarations and type-only exports at runtime. This ensures that imports are properly removed when their specifiers are only used in type-only contexts, such as interfaces, type aliases, type parameters, and type-only exports. A comprehensive unit test has been added to verify this behavior. There are no review comments to address, and I have no additional feedback to provide.

@alan-agius4 alan-agius4 added action: merge The PR is ready for merge by the caretaker and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Oct 29, 2026
@alan-agius4
alan-agius4 merged commit b6bfa2c into angular:main Oct 29, 2026
42 of 43 checks passed
@alan-agius4
alan-agius4 deleted the fix/elide-imports-type-exports branch October 29, 2026 14:49
@alan-agius4

Copy link
Copy Markdown
Collaborator Author

This PR was merged into the repository. The changes were merged into the following branches:

clydin added a commit to clydin/angular-cli that referenced this pull request Oct 29, 2026
…y AST constructs

Follow up to angular#34582 to cover remaining pure-type import elision cases and
partial specifier removal in `replaceBootstrap`:

- Elide imported alias symbols that do not resolve to a runtime value (or
  traverse a type-only alias declaration), handling non-`type` interface
  re-exports (`export { Foo }`) and interface parameter types under
  `emitDecoratorMetadata`.
- Only rewrite a partially elided import declaration when a runtime value
  import must be removed or when the declaration was already replaced by a
  previous transformer. TypeScript skips its own import elision for
  declarations replaced by a transformer, so leaving other declarations
  untouched avoids retaining specifiers that TypeScript would have otherwise
  elided (such as inlined `const enum` imports).
- Skip all non-`ExpressionWithTypeArguments` `TypeNode`s when
  `emitDecoratorMetadata` is disabled, and unconditionally skip
  `TypeQueryNode` (`typeof X`), `ImportTypeNode`,
  `IndexSignatureDeclaration`, ambient `declare` statements, and external
  `export ... from '...'` declarations.
- Recursively visit child AST nodes when applying `elideImports` removals in
  `replaceBootstrap` so individual `ImportSpecifier`s are removed when
  `platformBrowserDynamic` shares an import with other used runtime exports.
clydin added a commit to clydin/angular-cli that referenced this pull request Oct 29, 2026
…AST constructs

Follow up to angular#34582 to apply the same import elision fixes made to
`@ngtools/webpack` to the `replaceBootstrap` transformer used by the
application builder:

- Elide imported alias symbols that do not resolve to a runtime value (or
  traverse a type-only alias declaration), handling non-`type` interface
  re-exports (`export { Foo }`) and interface parameter types under
  `emitDecoratorMetadata`.
- Only rewrite a partially elided import declaration when a runtime value
  import must be removed or when the declaration was already replaced by a
  previous transformer, so TypeScript's own import elision continues to
  apply to all other declarations.
- Skip all non-`ExpressionWithTypeArguments` `TypeNode`s when
  `emitDecoratorMetadata` is disabled, and unconditionally skip
  `TypeQueryNode` (`typeof X`), `ImportTypeNode`,
  `IndexSignatureDeclaration`, ambient `declare` statements, and external
  `export ... from '...'` declarations.
- Recursively visit child AST nodes when applying import removals in
  `replaceBootstrap` so individual `ImportSpecifier`s are removed when
  `platformBrowserDynamic` shares an import with other used runtime exports.
clydin added a commit to clydin/angular-cli that referenced this pull request Oct 29, 2026
…y AST constructs

Follow up to angular#34582 to cover remaining pure-type import elision cases and
partial specifier removal in `replaceBootstrap`:

- Elide imported alias symbols that do not resolve to a runtime value (or
  traverse a type-only alias declaration), handling non-`type` interface
  re-exports (`export { Foo }`) and interface parameter types under
  `emitDecoratorMetadata`.
- Only rewrite a partially elided import declaration when a runtime value
  import must be removed or when the declaration was already replaced by a
  previous transformer. TypeScript skips its own import elision for
  declarations replaced by a transformer, so leaving other declarations
  untouched avoids retaining specifiers that TypeScript would have otherwise
  elided (such as inlined `const enum` imports).
- Skip all non-`ExpressionWithTypeArguments` `TypeNode`s when
  `emitDecoratorMetadata` is disabled, and unconditionally skip
  `TypeQueryNode` (`typeof X`), `ImportTypeNode`,
  `IndexSignatureDeclaration`, ambient `declare` statements, and external
  `export ... from '...'` declarations.
- Recursively visit child AST nodes when applying `elideImports` removals in
  `replaceBootstrap` so individual `ImportSpecifier`s are removed when
  `platformBrowserDynamic` shares an import with other used runtime exports.
clydin added a commit to clydin/angular-cli that referenced this pull request Oct 29, 2026
…AST constructs

Follow up to angular#34582 to apply the same import elision fixes made to
`@ngtools/webpack` to the `replaceBootstrap` transformer used by the
application builder:

- Elide imported alias symbols that do not resolve to a runtime value (or
  traverse a type-only alias declaration), handling non-`type` interface
  re-exports (`export { Foo }`) and interface parameter types under
  `emitDecoratorMetadata`.
- Only rewrite a partially elided import declaration when a runtime value
  import must be removed or when the declaration was already replaced by a
  previous transformer, so TypeScript's own import elision continues to
  apply to all other declarations.
- Skip all non-`ExpressionWithTypeArguments` `TypeNode`s when
  `emitDecoratorMetadata` is disabled, and unconditionally skip
  `TypeQueryNode` (`typeof X`), `ImportTypeNode`,
  `IndexSignatureDeclaration`, ambient `declare` statements, and external
  `export ... from '...'` declarations.
- Recursively visit child AST nodes when applying import removals in
  `replaceBootstrap` so individual `ImportSpecifier`s are removed when
  `platformBrowserDynamic` shares an import with other used runtime exports.
alan-agius4 pushed a commit that referenced this pull request Oct 30, 2026
…y AST constructs

Follow up to #34582 to cover remaining pure-type import elision cases and
partial specifier removal in `replaceBootstrap`:

- Elide imported alias symbols that do not resolve to a runtime value (or
  traverse a type-only alias declaration), handling non-`type` interface
  re-exports (`export { Foo }`) and interface parameter types under
  `emitDecoratorMetadata`.
- Only rewrite a partially elided import declaration when a runtime value
  import must be removed or when the declaration was already replaced by a
  previous transformer. TypeScript skips its own import elision for
  declarations replaced by a transformer, so leaving other declarations
  untouched avoids retaining specifiers that TypeScript would have otherwise
  elided (such as inlined `const enum` imports).
- Skip all non-`ExpressionWithTypeArguments` `TypeNode`s when
  `emitDecoratorMetadata` is disabled, and unconditionally skip
  `TypeQueryNode` (`typeof X`), `ImportTypeNode`,
  `IndexSignatureDeclaration`, ambient `declare` statements, and external
  `export ... from '...'` declarations.
- Recursively visit child AST nodes when applying `elideImports` removals in
  `replaceBootstrap` so individual `ImportSpecifier`s are removed when
  `platformBrowserDynamic` shares an import with other used runtime exports.
alan-agius4 pushed a commit that referenced this pull request Oct 30, 2026
…AST constructs

Follow up to #34582 to apply the same import elision fixes made to
`@ngtools/webpack` to the `replaceBootstrap` transformer used by the
application builder:

- Elide imported alias symbols that do not resolve to a runtime value (or
  traverse a type-only alias declaration), handling non-`type` interface
  re-exports (`export { Foo }`) and interface parameter types under
  `emitDecoratorMetadata`.
- Only rewrite a partially elided import declaration when a runtime value
  import must be removed or when the declaration was already replaced by a
  previous transformer, so TypeScript's own import elision continues to
  apply to all other declarations.
- Skip all non-`ExpressionWithTypeArguments` `TypeNode`s when
  `emitDecoratorMetadata` is disabled, and unconditionally skip
  `TypeQueryNode` (`typeof X`), `ImportTypeNode`,
  `IndexSignatureDeclaration`, ambient `declare` statements, and external
  `export ... from '...'` declarations.
- Recursively visit child AST nodes when applying import removals in
  `replaceBootstrap` so individual `ImportSpecifier`s are removed when
  `platformBrowserDynamic` shares an import with other used runtime exports.
alan-agius4 pushed a commit that referenced this pull request Oct 30, 2026
…y AST constructs

Follow up to #34582 to cover remaining pure-type import elision cases and
partial specifier removal in `replaceBootstrap`:

- Elide imported alias symbols that do not resolve to a runtime value (or
  traverse a type-only alias declaration), handling non-`type` interface
  re-exports (`export { Foo }`) and interface parameter types under
  `emitDecoratorMetadata`.
- Only rewrite a partially elided import declaration when a runtime value
  import must be removed or when the declaration was already replaced by a
  previous transformer. TypeScript skips its own import elision for
  declarations replaced by a transformer, so leaving other declarations
  untouched avoids retaining specifiers that TypeScript would have otherwise
  elided (such as inlined `const enum` imports).
- Skip all non-`ExpressionWithTypeArguments` `TypeNode`s when
  `emitDecoratorMetadata` is disabled, and unconditionally skip
  `TypeQueryNode` (`typeof X`), `ImportTypeNode`,
  `IndexSignatureDeclaration`, ambient `declare` statements, and external
  `export ... from '...'` declarations.
- Recursively visit child AST nodes when applying `elideImports` removals in
  `replaceBootstrap` so individual `ImportSpecifier`s are removed when
  `platformBrowserDynamic` shares an import with other used runtime exports.

(cherry picked from commit 9e6c1ce)
alan-agius4 pushed a commit that referenced this pull request Oct 30, 2026
…AST constructs

Follow up to #34582 to apply the same import elision fixes made to
`@ngtools/webpack` to the `replaceBootstrap` transformer used by the
application builder:

- Elide imported alias symbols that do not resolve to a runtime value (or
  traverse a type-only alias declaration), handling non-`type` interface
  re-exports (`export { Foo }`) and interface parameter types under
  `emitDecoratorMetadata`.
- Only rewrite a partially elided import declaration when a runtime value
  import must be removed or when the declaration was already replaced by a
  previous transformer, so TypeScript's own import elision continues to
  apply to all other declarations.
- Skip all non-`ExpressionWithTypeArguments` `TypeNode`s when
  `emitDecoratorMetadata` is disabled, and unconditionally skip
  `TypeQueryNode` (`typeof X`), `ImportTypeNode`,
  `IndexSignatureDeclaration`, ambient `declare` statements, and external
  `export ... from '...'` declarations.
- Recursively visit child AST nodes when applying import removals in
  `replaceBootstrap` so individual `ImportSpecifier`s are removed when
  `platformBrowserDynamic` shares an import with other used runtime exports.

(cherry picked from commit 174eb34)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action: merge The PR is ready for merge by the caretaker area: @ngtools/webpack target: patch This PR is targeted for the next patch release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@ngtools/webpack: elideImports keeps type-only specifiers that are re-exported or extends-ed, so webpack 5.112 (build-angular 21.2.28) fails the build

2 participants