Skip to content

fix: preserve plugin template root attributes#359

Merged
mohamedmansour merged 1 commit into
mainfrom
mmansour/preserve-plugin-template-attrs
Jun 19, 2026
Merged

fix: preserve plugin template root attributes#359
mohamedmansour merged 1 commit into
mainfrom
mmansour/preserve-plugin-template-attrs

Conversation

@mohamedmansour

Copy link
Copy Markdown
Contributor

Summary

  • Preserve user-authored root <template> attributes for parser plugin/client component templates while continuing to strip runtime-only attributes from the SSR/internal parse view.
  • Build explicit component template views in the parser: an SSR view for fragment parsing and a plugin/client artifact view for ParserPlugin::register_component_template.
  • Append shadowrootadoptedstylesheets="<component-name>" centrally for CSS module strategy when a component has CSS and the authored wrapper is missing the attribute.
  • Remove the obsolete ParserError::MissingAdoptedStylesheets contract and update DESIGN.md, CLI docs, plugin docs, and the AI reference.
  • Add todo-fast Playwright coverage and wire it into cargo xtask e2e so FAST delegated root events are covered without requiring CSS module mode.

Why

FAST component templates were receiving the SSR-cleaned component template string. That string had root runtime attributes such as @toggle-item, @delete-item, and @click stripped from user-authored <template> wrappers before FAST generated <f-template> artifacts. As a result, examples/app/todo-fast rendered and could handle local child events, but delegated root events from child components did not wire up, so toggling and deleting existing todos failed.

The correct contract is that authored root <template> attributes belong to the plugin/client template surface. SSR can still use a clean view that removes runtime-only syntax, but plugins should not have to reconstruct or special-case raw component source.

Design

This change centralizes the behavior in HtmlParser rather than adding a FAST-specific workaround:

  • BuiltComponentTemplate::ssr is used for internal fragment parsing and SSR output.
  • BuiltComponentTemplate::artifact() is passed to parser plugins.
  • The artifact view preserves user-authored root <template> attributes.
  • The SSR view strips runtime-only root attrs (@, :, ?) so rendered HTML remains clean.
  • CSS module adopted stylesheet attrs are appended in the shared builder when missing, and authored multi-specifier values are preserved verbatim.

This keeps WebUI and FAST on the same parser/plugin contract. WebUI’s existing raw-source root-event path remains compatible, but plugin correctness no longer depends on duplicating that pattern in FAST.

Performance notes

  • No regex or recursion added.
  • The second artifact template is only built when a plugin exists and authored root runtime attrs would otherwise be stripped.
  • Existing quote-aware scanner helpers are reused for template tag and attribute handling.
  • Buffers are pre-sized for CSS injection and adopted stylesheet insertion.

Validation

  • cargo test -p microsoft-webui-parser --lib
  • cargo test -p microsoft-webui-parser
  • pnpm --dir examples/app/todo-fast run build
  • pnpm --dir examples/app/todo-fast test
  • pnpm --dir examples/app/todo-webui test
  • pnpm --dir docs build
  • cargo xtask check

Preserve authored root <template> attributes for plugin/client component templates while keeping SSR output clean.

Append CSS module adopted stylesheet attributes centrally when missing and add todo-fast E2E coverage for delegated root events.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

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 fixes a parser/plugin contract issue where user-authored root <template> attributes (notably FAST root delegated @event handlers) were being stripped before parser plugins could see them, breaking delegated root event wiring in examples/app/todo-fast. It does so by building two explicit component-template views: an SSR/internal parse view (cleaned) and a plugin/client artifact view (preserving authored root <template> attributes), while also centralizing CSS module adopted-stylesheet attribute insertion.

Changes:

  • Build dual component template views in HtmlParser (ssr vs plugin-facing artifact) and pass the plugin-facing view to ParserPlugin::register_component_template.
  • Append shadowrootadoptedstylesheets="<component-name>" for --css module when a component has CSS and the authored wrapper is missing the attribute; remove the obsolete ParserError::MissingAdoptedStylesheets contract.
  • Add Playwright E2E coverage for examples/app/todo-fast and wire it into cargo xtask e2e; update DESIGN.md and docs to reflect the new contract.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated no comments.

Show a summary per file
File Description
xtask/src/e2e.rs Adds todo-fast to the xtask-managed Playwright E2E suite list.
pnpm-lock.yaml Records @playwright/test for the todo-fast example workspace importer.
examples/app/todo-fast/tests/todo-fast.spec.ts Adds SSR + interactivity Playwright coverage, including assertions for preserved root runtime attrs in FAST templates.
examples/app/todo-fast/playwright.config.ts Introduces Playwright config for the todo-fast example (baseURL/timeout/projects).
examples/app/todo-fast/package.json Adds Playwright test scripts and the @playwright/test dev dependency.
docs/guide/concepts/plugins/index.md Documents that plugin-facing component templates preserve authored root <template> attributes.
docs/guide/cli/index.md Updates --css module behavior to reflect automatic shadowrootadoptedstylesheets appending + wrapper attr preservation.
docs/ai.md Updates AI reference to note wrapper attribute preservation and automatic adopted-stylesheets insertion in module CSS mode.
DESIGN.md Updates the spec: removes the “MissingAdoptedStylesheets” error contract and documents the new append-if-missing behavior + plugin template surface.
crates/webui-parser/src/plugin/webui.rs Adjusts internal docs/comments to reflect “plugin-facing” template capture semantics.
crates/webui-parser/src/plugin/mod.rs Updates plugin trait docs to specify plugin-facing template semantics and root attribute preservation.
crates/webui-parser/src/lib.rs Implements dual template views (BuiltComponentTemplate), passes artifact view to plugins, appends adopted-stylesheets attr when missing, and updates/extends tests accordingly.
crates/webui-parser/src/error.rs Removes ParserError::MissingAdoptedStylesheets variant and its error message contract.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Comment thread crates/webui-parser/src/lib.rs
@mohamedmansour mohamedmansour merged commit 53a71db into main Jun 19, 2026
22 checks passed
@mohamedmansour mohamedmansour deleted the mmansour/preserve-plugin-template-attrs branch June 19, 2026 19:17
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.

4 participants