Skip to content

feat(fast-build): strip state-passing binding attrs from root custom elements#7403

Merged
janechu merged 7 commits intomainfrom
users/janechu/strip-entry-state-binding-attrs-on-root-elements
Apr 7, 2026
Merged

feat(fast-build): strip state-passing binding attrs from root custom elements#7403
janechu merged 7 commits intomainfrom
users/janechu/strip-entry-state-binding-attrs-on-root-elements

Conversation

@janechu
Copy link
Copy Markdown
Collaborator

@janechu janechu commented Apr 7, 2026

Pull Request

📖 Description

When rendering the top-level entry HTML via render_entry_with_templates / render_entry_template_with_locator, root custom elements receive the full root state directly. Before this change, any {{binding}} attribute on a root element was resolved and written into the rendered HTML as-is — e.g. list="{{items}}" became list="[Array]". These noisy rendered values are now handled correctly:

  • Primitive bindings (string, number, boolean) — resolved and rendered. e.g. text="{{message}}"text="Hello world".
  • Non-primitive bindings (array, object, null) — stripped. State is available directly in the template via entry-level rendering.
  • ?attr="{{expr}}" boolean bindings — evaluated as a boolean; bare attribute name emitted when truthy, omitted when falsy.
  • Static attributes (id="main", disabled, etc.) — passed through unchanged.

Additionally, render_entry_with_templates is now exposed as a WASM binding and the CLI updated to use it. The implementation uses strip_client_only_attrs as a first pass to avoid duplicating the client-only attr filter list.

📑 Test Plan

  • 5 new integration tests: primitive resolved, non-primitive stripped, static attrs kept, mixed, boolean ?attr truthy/falsy
  • npm run build:fixtures regenerates correctly — attribute, binding, event fixtures match expected output
  • All 184 Rust tests pass

✅ Checklist

General

  • I have included a change request file using $ npm run change
  • I have added tests for my changes.
  • I have tested my changes.
  • I have updated the project documentation to reflect my changes.
  • I have read the CONTRIBUTING documentation and followed the standards for this project.

janechu and others added 5 commits April 7, 2026 10:52
…elements

When rendering entry HTML via render_entry_with_templates / render_entry_template_with_locator, root custom elements receive the full root state directly. Any attribute whose value is a pure {{expr}} double-brace binding was only needed to forward state via the attribute-based child state mechanism — with entry-level rendering that mechanism is bypassed, so these attributes are now stripped from the rendered HTML output.

Examples stripped: list="{{list}}", item_parent="{{item_parent}}", someData="{{someData}}"
Static attributes (id, class, disabled, etc.) are preserved.

Changes:
- attribute.rs: add is_state_passing_binding() and strip_entry_attrs() (strips client-only attrs + {{binding}} attrs)
- directive.rs: build_element_open_tag gains is_entry param; uses strip_entry_attrs when true
- wasm.rs: expose render_entry_with_templates WASM binding
- fast.js CLI: use render_entry_with_templates when templates are provided
- tests/custom_elements.rs: 4 new tests for entry-level attr stripping behaviour
- Regenerate attribute, binding, event fixture index.html files

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ents; strip non-primitives

For entry-level root custom elements, {{binding}} attribute values are now resolved
from root state:
- Primitive (string/number/bool): rendered with resolved value, e.g. text="{{msg}}" → text="Hello world"
- Non-primitive (array/object/null): stripped — cannot be an HTML attribute value

Static attributes (no binding syntax) are passed through unchanged.

This is implemented via build_entry_element_open_tag in directive.rs which replaces
the earlier strip_entry_attrs approach. HTML escaping is applied to string values.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@janechu janechu marked this pull request as ready for review April 7, 2026 20:46
@janechu janechu requested a review from Copilot April 7, 2026 20:47
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates @microsoft/fast-build entry-level rendering so that root custom elements no longer emit noisy “state-passing” {{binding}} attributes in the rendered HTML, and exposes the entry-level renderer via WASM for the CLI to use when templates are provided.

Changes:

  • Add entry-level opening-tag handling for root custom elements (resolve primitive {{...}} attrs, strip non-primitives).
  • Expose render_entry_with_templates as a WASM binding and switch the fast-build CLI to use it when templates are present.
  • Add integration tests and documentation updates describing the entry-level attribute behavior.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/fast-build/bin/fast.js Switch CLI rendering path to entry-level semantics when templates are provided.
crates/microsoft-fast-build/src/wasm.rs Export render_entry_with_templates for WASM consumers.
crates/microsoft-fast-build/src/directive.rs Implement entry-level root custom element opening-tag attribute handling.
crates/microsoft-fast-build/tests/custom_elements.rs Add integration tests for entry-level root custom element attribute behavior.
crates/microsoft-fast-build/README.md Document entry-level root element attribute handling semantics.
crates/microsoft-fast-build/DESIGN.md Update design notes to reflect new entry-level opening-tag behavior.
change/@microsoft-fast-html-f43a3bf8-58f2-4224-a8b1-d248b5f47b7e.json Record fixture regen for @microsoft/fast-html.
change/@microsoft-fast-build-strip-entry-binding-attrs.json Record minor feature change for @microsoft/fast-build.

janechu and others added 2 commits April 7, 2026 14:06
- Use strip_client_only_attrs as first step in build_entry_element_open_tag
  to avoid duplicating the client-only attr filter list
- Handle ?attr="{{expr}}" boolean attribute bindings: evaluate as boolean,
  emit bare attr name (without ?) when truthy, omit when falsy
- Fix wasm.rs doc comment for render_entry_with_templates to accurately
  describe primitive-kept / non-primitive-stripped behavior
- Add tests: test_root_custom_element_boolean_attr_binding_truthy and _falsy

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@janechu janechu merged commit 7aec78a into main Apr 7, 2026
12 of 14 checks passed
@janechu janechu deleted the users/janechu/strip-entry-state-binding-attrs-on-root-elements branch April 7, 2026 21:26
janechu added a commit that referenced this pull request Apr 8, 2026
Regenerate repeat/index.html after rebasing onto main (which includes
PR #7403 entry-attribute stripping and PR #7380 when fixture).

Changes reflect correct entry-element behavior:
- Non-primitive binding attrs (list, someData) stripped from opening tag
- test-element-no-item-repeat-binding now renders empty repeat since
  list="{{emptyList}}" overrides root list with [] in merged state

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
janechu added a commit that referenced this pull request Apr 8, 2026
Regenerate repeat-event/index.html after rebasing onto main (which
includes #7381 repeat fixture, #7380 when fixture, and #7403
entry-attribute stripping).

Non-primitive binding attrs (items) are now stripped from entry element
opening tags; primitive attrs (show-names="true") are preserved.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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