Repository navigation
fix(linker): link files whose ɵ identifiers are written as \u0275 escapes - #528
Merged
Brooooooklyn merged 1 commit intoOct 2, 2026
Conversation
…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
approved these changes
Oct 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
esbuild's default
--minifyuses--charset=ascii, so it writesɵin identifiers as\u0275escapes:e.\u0275\u0275ngDeclareComponent,static \u0275cmp. The linker returnedlinked: falsefor 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
ɵɵngDeclareand never matched escaped files:link()incrates/oxc_angular_compiler/src/linker/mod.rsLINKER_DECLARATION_PREFIXin the Vite linker plugin, which skipped the file before it reached RustEverything 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_parserdecodes identifier escapes there.Fix
Both prefilters now match
ngDeclarewithout theɵɵprefix. Escapes are not decoded anywhere, so string contents stay unchanged. Escaped identifiers that the linker copies through, such asstatic \u0275fac, stay escaped. They are valid JS and equivalent tostatic ɵfac. Angular's output has none because Babel re-prints the whole file.Tests
test_link_unicode_escaped_identifierslinks 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\u0275is decoded. It fails without the fix.test/linker.test.tsruns an escaped chunk through the Vite plugin's transform hook. It fails if the old plugin prefix is put back.Local verification
ngcoutput for a directive and a component, minified by esbuild 0.27 in both charset modes:ReferenceErroror JIT errors).node_modules(Material, CDK, common, router, forms, platform-browser, animations, ngx-cookieconsent) were minified with--charset=asciiand--charset=utf8:ngDeclareleft.\u{275}, mixedɵ\u0275, an escaped namespace identifier, and escaped Injectable and Pipe declarations all link.cargo check --all-features,cargo testandcargo fmt --checkpass.pnpm build-dev,build:ts,pnpm testandpnpm checkpass.compare --fixturespasses andtest:e2ehas 23 passing.cargo docerror counts are unchanged from main.Notes
ngDeclarewithout real declarations now get parsed. The AST walk finds nothing to link and they returnlinked: falseas before.