Skip to content

fix(jit): elide imports whose specifiers are all inline-type - #482

Merged
Brooooooklyn merged 1 commit into
voidzero-dev:mainfrom
ashley-hunter:fix/jit-inline-type-import-elision
Sep 18, 2026
Merged

Brooooooklyn merged 1 commit into
voidzero-dev:mainfrom
ashley-hunter:fix/jit-inline-type-import-elision

Conversation

@ashley-hunter

Copy link
Copy Markdown
Collaborator

Problem

With jit: true, an import statement whose bindings all carry inline type modifiers is preserved as a bare side-effect import instead of being elided:

import { Pipe, type PipeTransform } from '@angular/core';
import { type Get } from 'type-fest';

@Pipe({ name: 'demo' })
export class DemoPipe implements PipeTransform {
  transform(v: string) { return v; }
}

export let sample: Get<{ a: 1 }, 'a'> | undefined;

emits import "type-fest"; at the top of the output. For a types-only package like type-fest (no main/exports), Rolldown fails the build outright; for an ordinary package it survives as a dead import that defeats tree-shaking.

The file must contain an Angular decorator to reproduce, which points at the JIT transform: strip_typescript runs oxc's TS transform with only_remove_type_imports, which drops the inline-type specifiers but keeps the emptied statement as a side-effect import. Mixed imports (import { Pipe, type PipeTransform }) and whole import type { ... } statements were already handled correctly; only the all-inline-type case leaked.

Fix

Before the transformer runs, promote any value import whose named specifiers are all inline-type to import type, so only_remove_type_imports elides the whole statement. Untouched: bare side-effect imports (import 'mod'), empty import {}, existing import type, and any statement with a default or namespace specifier.

Note this intentionally follows Angular CLI's esbuild pipeline rather than tsc verbatimModuleSyntax, which would keep import {} for side effects; code relying on side effects should use an explicit import 'mod', which is preserved.

AOT is unaffected: it uses the separate semantic-based elision pass and already elides these imports.

Testing

  • New integration test covering all five import shapes: all-inline-type elided, mixed value+type keeps the value binding only, whole import type elided, default import with inline-type sibling keeps the default, bare side-effect import preserved. Fails before the fix with the exact reported output, passes after.
  • Full workspace test suite passes (2763 tests), fmt and clippy clean.
  • Verified end-to-end through the napi binding with jit: true and jit: false on the repro above.

In JIT mode, strip_typescript runs oxc's TS transform with
only_remove_type_imports, which drops inline-type specifiers but keeps
the emptied statement as a bare side-effect import. An import like
`import { type Get } from 'type-fest'` therefore survived as
`import "type-fest"`, failing Rolldown builds for types-only packages
and defeating tree-shaking for ordinary ones.

Promote any value import whose named specifiers all carry inline
`type` modifiers to `import type` before the transformer runs, so the
whole statement is elided. Mixed imports, default/namespace imports,
and explicit bare side-effect imports are untouched. This matches
Angular CLI's esbuild pipeline rather than tsc verbatimModuleSyntax,
which would keep `import {}` for side effects.
@Brooooooklyn
Brooooooklyn merged commit 306ce0d into voidzero-dev:main Sep 18, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants