Skip to content

fix(linker): link files whose ɵ identifiers are written as \u0275 escapes - #528

Merged
Brooooooklyn merged 1 commit into
voidzero-dev:mainfrom
ashley-hunter:t3code/support-escaped-angular-identifiers
Oct 2, 2026
Merged

Brooooooklyn merged 1 commit into
voidzero-dev:mainfrom
ashley-hunter:t3code/support-escaped-angular-identifiers

Conversation

@ashley-hunter

Copy link
Copy Markdown
Collaborator

Problem

esbuild's default --minify uses --charset=ascii, so it writes ɵ in identifiers as \u0275 escapes: e.\u0275\u0275ngDeclareComponent, static \u0275cmp. The linker returned linked: false for such files, so the library fell back to JIT and threw at import: "The directive 't' needs to be compiled using the JIT compiler, but '@angular/compiler' is not available."

Angular's Babel linker links these files.

Cause

Two raw-text prefilters looked for a literal ɵɵngDeclare and never matched escaped files:

  • link() in crates/oxc_angular_compiler/src/linker/mod.rs
  • LINKER_DECLARATION_PREFIX in the Vite linker plugin, which skipped the file before it reached Rust

Everything after the prefilter already works on the AST. That covers callee and declaration-name matching, the namespace and string values such as selectors and templates. oxc_parser decodes identifier escapes there.

Fix

Both prefilters now match ngDeclare without the ɵɵ prefix. Escapes are not decoded anywhere, so string contents stay unchanged. Escaped identifiers that the linker copies through, such as static \u0275fac, stay escaped. They are valid JS and equivalent to static ɵfac. Angular's output has none because Babel re-prints the whole file.

Tests

  • Rust: test_link_unicode_escaped_identifiers links input covering a factory, a directive, class metadata and a component with a template. It then checks that the escaped output equals the utf8 output once \u0275 is decoded. It fails without the fix.
  • TS: a test in test/linker.test.ts runs an escaped chunk through the Vite plugin's transform hook. It fails if the old plugin prefix is put back.

Local verification

  • Repro with ngc output for a directive and a component, minified by esbuild 0.27 in both charset modes:
    • The escaped file now links.
    • Its linked output equals the utf8 file's linked output after decoding.
    • With the separate linker namespace fix applied, the generated template and host-binding functions fail the same way Angular's output does ("no view" errors, not ReferenceError or JIT errors).
  • All 106 partially compiled FESM files in node_modules (Material, CDK, common, router, forms, platform-browser, animations, ngx-cookieconsent) were minified with --charset=ascii and --charset=utf8:
    • 106/106 escaped files link, with no ngDeclare left.
    • Each output equals the utf8 output after decoding escapes.
    • Every output parses as valid JS.
  • Reviewer checks with temporary tests: \u{275}, mixed ɵ\u0275, an escaped namespace identifier, and escaped Injectable and Pipe declarations all link.
  • CI steps:
    • cargo check --all-features, cargo test and cargo fmt --check pass.
    • Conformance is 1252/1252 and the snapshot is unchanged.
    • pnpm build-dev, build:ts, pnpm test and pnpm check pass.
    • compare --fixtures passes and test:e2e has 23 passing.
    • Clippy and cargo doc error counts are unchanged from main.

Notes

  • The looser prefilter means files that mention ngDeclare without real declarations now get parsed. The AST walk finds nothing to link and they return linked: false as before.
  • Escaped files depend on the separate linker namespace fix to run correctly with minified namespaces, the same as unescaped minified files do.

…apes

esbuild's default minify (`--charset=ascii`) writes `ɵ` in identifiers as
`\u0275`, e.g. `e.\u0275\u0275ngDeclareComponent`. The raw-text `ɵɵngDeclare`
prefilter in `link()` and in the Vite linker plugin never matched such
files, so they were skipped and the library fell back to JIT at runtime.

Match `ngDeclare` without the prefix instead. The AST walk already compares
decoded identifier names, so escaped input now links exactly like the
unescaped input.
@Brooooooklyn
Brooooooklyn merged commit 3533bc8 into voidzero-dev:main Oct 2, 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