Skip to content

fix(decorator): fold ${const} template literals in metadata fields#300

Merged
Brooooooklyn merged 4 commits into
mainfrom
fix/decorator-template-literal-interpolation
May 27, 2026
Merged

fix(decorator): fold ${const} template literals in metadata fields#300
Brooooooklyn merged 4 commits into
mainfrom
fix/decorator-template-literal-interpolation

Conversation

@Brooooooklyn
Copy link
Copy Markdown
Member

@Brooooooklyn Brooooooklyn commented May 27, 2026

Summary

  • Resolves #286: template: / selector: with ${const} template-literal interpolation were silently dropped (AOT skipped the component, no ɵcmp; or emitted selectors:[["ng-component"]]).
  • extract_string_value in component/decorator.rs and directive/decorator.rs now recursively folds ${...} expressions through the existing StringConsts map; nested/chained consts resolve via an iterative fixed-point in collect_string_consts.
  • JIT mode's build_jit_decorator_text shares the same resolver, so templateUrl / styleUrl / styleUrls accept the same identifier and template-literal shapes as AOT (closes the AOT/JIT asymmetry surfaced by /codex:adversarial-review).

Root cause

extract_string_value rejected any TemplateLiteral with non-empty expressions, so template: `<button class="${twBtn}">x</button>` returned None and the whole @Component decorator was treated as un-extractable. StringConsts from #271 was only consulted for bare identifier references.

Changes

  • crates/oxc_angular_compiler/src/directive/decorator.rs: add resolve_template_literal (interleaves quasis with partial-evaluated ${...} expressions); thread &'a Allocator through extract_string_value; iterative fixed-point in collect_string_consts for chained consts; export extract_string_value as pub(crate).
  • crates/oxc_angular_compiler/src/component/decorator.rs: mirror resolve_template_literal; thread allocator through extract_string_value and extract_string_array (which also accepts [CONST_STR] and [`${CONST}`] array elements).
  • crates/oxc_angular_compiler/src/component/transform.rs: collect string_consts in the JIT path and feed build_jit_decorator_text, which now resolves URL values through the shared extract_string_value so templateUrl: `${DIR}/x.html` and templateUrl: PATH_CONST fold in JIT too.
  • crates/oxc_angular_compiler/src/directive/mod.rs: pub(crate) use decorator::extract_string_value.
  • napi/angular-compiler/src/lib.rs: pass allocator to collect_string_consts in extract_component_urls_sync and extract_metadata_sync.

Test plan

  • cargo test -p oxc_angular_compiler — 2454 passed, 0 failed, 1 ignored (pre-existing).
  • 11 new integration tests in tests/integration_test.rs covering AOT template:/selector: interpolation in @Component and @Directive, multiple interpolations, chained consts (const TAG = \${PREFIX}-x`), styles:array elements, graceful fallback on unresolved identifiers, plus JITtemplateUrl/styleUrl/styleUrls` for both template-literal and identifier shapes.
  • cargo build --all-targets — clean (only pre-existing missing-docs warnings).
  • Reviewed via /codex:adversarial-review — initial pass flagged the JIT gap, which this PR also closes; re-verified with the full extended fix.

🤖 Generated with Claude Code


Note

Medium Risk
Touches core decorator metadata extraction and JIT decorator rewriting; incorrect folding could change selectors, templates, or resource paths, though behavior is constrained to statically resolvable same-file consts with tests covering main shapes.

Overview
Fixes #286: decorator metadata that used `${const}` template literals (or const identifiers) in fields like template, selector, and styles were treated as unextractable, so AOT could skip ɵɵdefineComponent or emit wrong selectors.

Shared partial evaluation in directive/decorator.rs: extract_string_value now folds template literals via resolve_template_literal, and collect_string_consts takes an allocator and resolves chained top-level const values in a fixed-point loop. Component and directive metadata parsing route string fields and styles / styleUrls array elements through this helper instead of local duplicate logic.

JIT parity: build_jit_decorator_text collects string_consts and resolves templateUrl, styleUrl, and styleUrls the same way, so angular:jit:*:file; imports get folded paths. NAPI call sites pass the allocator into collect_string_consts.

Eleven integration tests cover AOT/JIT folding, chained consts, and graceful drop when interpolations cannot be resolved.

Reviewed by Cursor Bugbot for commit c3bb670. Bugbot is set up for automated code reviews on this repo. Configure here.

Brooooooklyn and others added 2 commits May 27, 2026 09:56
…286)

Angular's partial evaluator constant-folds template literals whose `${...}`
interpolations reference same-file string `const`s. OXC was bailing on any
TemplateLiteral with non-empty `expressions`, so `template:`/`selector:`
silently dropped (AOT skipped the component) and JIT `templateUrl`/`styleUrl`/
`styleUrls` ignored the same shapes. Resolve interpolations recursively
through the existing `StringConsts` map (with iterative fixed-point so chained
consts fold) and share the resolver between AOT extraction and JIT decorator
rewriting so both accept identical metadata shapes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 63a84e9. Configure here.

Comment thread crates/oxc_angular_compiler/src/component/decorator.rs Outdated
Brooooooklyn and others added 2 commits May 27, 2026 16:36
Per Cursor Bugbot review: the local `extract_string_value` and
`resolve_template_literal` in `component/decorator.rs` are byte-for-byte
equivalent to the ones exported as `pub(crate)` from `directive/decorator.rs`.
Reuse the directive copies and funnel `extract_string_array` elements through
the same resolver so a future fix doesn't risk drifting between modules.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@Brooooooklyn Brooooooklyn merged commit 492cd85 into main May 27, 2026
10 checks passed
@Brooooooklyn Brooooooklyn deleted the fix/decorator-template-literal-interpolation branch May 27, 2026 09:01
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.

${...} template-literal interpolation in decorator metadata not statically evaluated

1 participant