Skip to content

fix(linker): accept template literals in styles and host bindings - #527

Merged
Brooooooklyn merged 2 commits into
voidzero-dev:mainfrom
ashley-hunter:fix/linker-template-literal-strings
Oct 2, 2026
Merged

Brooooooklyn merged 2 commits into
voidzero-dev:mainfrom
ashley-hunter:fix/linker-template-literal-strings

Conversation

@ashley-hunter

@ashley-hunter ashley-hunter commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Partial declarations can reach the linker with their strings written as template literals. esbuild, for example, rewrites strings containing quotes or newlines to backticks, which most CSS does:

i0.ɵɵngDeclareComponent({ ..., styles: [`.a {
  content: "x";
}`] })

Previously the linker only read StringLiteral values in these places, so:

  • styles entries were dropped, and with no styles left the component's encapsulation was downgraded from Emulated to None.
  • Host properties and listeners were dropped, so their bindings were never emitted.

The build succeeded with no warning, and the problem only showed up as missing styling or behaviour at runtime.

Changes

  • Extract as_static_string from get_string_property: it accepts a string literal, or a template literal with no substitutions (using its cooked value).
  • Use it for styles entries and for host properties/listeners values.
  • Aliased inputs x: [pub, prop] are now recognised as declaration format, so the [InputFlags, ...] element is inserted; previously the array was passed through verbatim and the runtime read the public name as InputFlags.
  • deps: invalid`` now selects the ɵɵinvalidFactory() path like the quoted form.
  • host.attributes static-string values are emitted cooked and quoted, and attribute keys / input publicName/classPropertyName go through escape_string.
  • The styles loop uses as_expression() so an array elision can't panic it.
  • Template literals with ${...} substitutions are still skipped.

Tests

  • test_link_styles_template_literal_matches_string_literal: backtick and quoted styles link to byte-identical output, including a case with escape sequences.
  • test_link_host_bindings_template_literal_matches_string_literal: same for host properties and listeners.
  • test_link_host_attributes_template_literal_matches_string_literal: cooked template-literal attribute values produce byte-identical hostAttrs.
  • test_link_inputs_template_literal_matches_string_literal: simple, aliased-array, and object input forms all survive the rewrite.
  • test_link_factory_invalid_deps_template_literal: deps: invalid`` emits ɵɵinvalidFactory.
  • Both original tests failed before the fix.
  • Checked end to end through the napi binding: the linked output for the backtick form is identical to the quoted form, and the quoted form matches @angular/compiler-cli 21.2.6's linker output.
  • Local runs: cargo test, conformance (1264/1264, snapshot unchanged), pnpm test (374/374), pnpm check, compare fixtures (979/979) and Playwright e2e (37/37).

Partial declarations whose strings were rewritten as template literals
(for example by esbuild, which prefers backticks for strings containing
quotes or newlines) linked with their styles and host bindings missing.
Emptied styles also downgraded encapsulation from Emulated to None.

Treat a template literal with no substitutions as its cooked string
value for `styles` entries and host `properties`/`listeners`, sharing
the existing handling used by `get_string_property`.
@ashley-hunter
ashley-hunter force-pushed the fix/linker-template-literal-strings branch from 2f17f8a to 7168b52 Compare October 2, 2026 11:39
…terals

The same bundler rewrite (strings → no-substitution template literals)
still leaked through other linker paths:

- `inputs: { x: [`pub`, `prop`] }` failed the first-is-string check, so
  the `[InputFlags, ...]` definition-format element was never inserted.
  The runtime reads element 0 as InputFlags, so a truthy publicName was
  treated as SignalBased and the alias was lost.
- `deps: `invalid`` was not recognised, producing a plain `new Type()`
  factory instead of `ɵɵinvalidFactory()`.
- `host.attributes` values emitted the raw source (`` `x` `` rather
  than `"x"`), diverging from the quoted form; attr keys and input
  public/class names are now emitted through escape_string.
- The styles loop used `to_expression()`, which panics on array
  elisions; `as_expression()` skips them instead.
@Brooooooklyn
Brooooooklyn merged commit a6e1f15 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