Skip to content

Support scoped static component asset emission #364

Description

@mohamedmansour

Summary

webui::render_component_assets(protocol, roots, template) currently renders static component assets from an already-built WebUIProtocol. That is a good API for the full build/CLI path, but it still requires a full webui::build() first: app entry parsing, full protocol construction, plugin artifact extraction, and protocol serialization inputs.

For dev/HMR and future bundler-plugin flows, we should support a scoped asset-emission path that compiles only the requested asset roots and their conservative dependency closure.

Motivation

When a developer changes one lazy component, a watcher or bundler plugin should be able to re-emit that component's .webui.js asset without rebuilding the full app entry and unrelated protocol graph. This matters for:

  • HMR latency in large apps with many templates.
  • Bundler plugins that want to emit one changed root into the module graph.
  • Lower memory use during watch mode by avoiding full protocol construction for unrelated fragments.

Current behavior

PR #360 introduced static component assets. The full build path parses requested roots through synthetic non-entry fragments so the normal parser/plugin component compilation path runs, then removes the synthetic wrapper fragments before protocol serialization.

That proves the parser already has most of the mechanism needed for scoped roots, but today the public asset renderer still consumes a full protocol.

Proposed direction

Add a build/compiler entry point that:

  1. Registers app/external components using the existing discovery path.
  2. Skips parsing the SSR entry file.
  3. Parses only requested component asset roots through the existing synthetic-root/component compilation path.
  4. Computes the same conservative closure used by render_component_assets.
  5. Emits one or more ComponentAssetFiles without producing unrelated protocol fragments.

Possible API shapes:

pub fn build_component_assets(options: ComponentAssetBuildOptions) -> Result<Vec<ComponentAssetFile>, WebUIError>;

or an incremental parser API that a bundler plugin can reuse:

HtmlParser::compile_component_asset_roots(&mut self, roots: &[String]) -> Result<...>;

Constraints

  • Preserve the same output shape as PR feat: add ESM static component assets #360 (webui-component-asset, version 1, templates, templateStyles, templateFunctions).
  • Preserve build-time validation: unknown roots, invalid tag names, missing plugin metadata, and filename collisions must fail before writing.
  • Do not add recursion; closure walking should remain iterative.
  • Avoid request-time ProtocolIndex / inventory filtering in this build-time path.
  • Keep CSS/module/link behavior identical to the full build path.

Acceptance criteria

  • A caller can emit a static asset for one root without parsing the app entry template.
  • Output bytes match the full-build asset output for the same root and build options.
  • Tests cover nested component, <if>, <for>, attribute-template, and route-branch closure dependencies.
  • Benchmarks or timing show improved latency for a one-root re-emit in a multi-component fixture.

Related: PR #360 review discussion.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestrustPull requests that update rust code
    No fields configured for Feature.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions