Skip to main content

webui_parser/plugin/
mod.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT license.
3
4//! Parser plugin trait and built-in plugin implementations.
5//!
6//! The plugin system is framework-aware but phase-local — parser plugins
7//! classify framework-owned attributes, capture plugin-facing component templates,
8//! and emit per-element hydration metadata for the handler.
9
10pub mod fast;
11pub mod fast_v2;
12pub mod fast_v3;
13pub mod webui;
14
15use crate::component_registry::Component;
16use crate::{ParserOptions, Result};
17
18/// Parser-owned decision about how an attribute should be handled.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum AttributeAction {
21    /// Let the parser process the attribute normally.
22    Keep,
23    /// Skip the attribute without incrementing the element binding count.
24    Skip,
25    /// Skip the attribute and count it as a dynamic binding.
26    SkipAndCountBinding,
27}
28
29/// Build artifacts extracted from a parser plugin after parsing completes.
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub enum ParserPluginArtifacts {
32    /// The plugin did not produce any post-parse artifacts.
33    None,
34    /// Client component templates keyed by tag name.
35    ComponentTemplates(Vec<ComponentTemplateArtifact>),
36}
37
38/// Client template payload generated by a parser plugin for one component.
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct ComponentTemplateArtifact {
41    /// Component custom-element tag name.
42    pub tag_name: String,
43    /// Non-WebUI plugin template payload, such as FAST `<f-template>` HTML.
44    pub template: String,
45    /// WebUI JSON-safe template metadata.
46    pub template_json: String,
47    /// WebUI component-local JavaScript condition closure array.
48    pub template_functions: String,
49}
50
51impl ComponentTemplateArtifact {
52    /// Create a non-WebUI template payload.
53    #[must_use]
54    pub fn template(tag_name: String, template: String) -> Self {
55        Self {
56            tag_name,
57            template,
58            template_json: String::new(),
59            template_functions: String::new(),
60        }
61    }
62
63    /// Create a WebUI split metadata/function payload.
64    #[must_use]
65    pub fn webui(tag_name: String, template_json: String, template_functions: String) -> Self {
66        Self {
67            tag_name,
68            template: String::new(),
69            template_json,
70            template_functions,
71        }
72    }
73}
74
75/// A parser plugin that can customize template parsing behavior.
76///
77/// Plugins receive callbacks at key points during HTML parsing:
78/// - **Fragment lifecycle**: `start_fragment` before a fragment parse begins
79/// - **Component registration**: `register_component_template` when a plugin-facing component template is finalized
80/// - **Attribute classification**: `classify_attribute` for framework-specific attrs
81/// - **Element completion**: `finish_element` after attributes are processed
82///
83/// WebUI calls these hooks during parsing; plugins decide what (if anything) to do.
84pub trait ParserPlugin {
85    /// Called before parsing begins for a fragment.
86    ///
87    /// Plugins can use this to reset fragment-local counters while preserving
88    /// global build-level state such as tracked component templates.
89    fn start_fragment(&mut self, _fragment_id: &str) {}
90
91    /// Called when parser output options change.
92    fn configure(&mut self, _options: &ParserOptions) {}
93
94    /// Called with the plugin-facing component template for the active CSS/DOM
95    /// strategies. Authored root `<template>` attributes are preserved here;
96    /// the SSR/internal parse view may strip runtime-only attributes.
97    fn register_component_template(
98        &mut self,
99        tag_name: &str,
100        component: &Component,
101        processed_template: &str,
102    ) -> Result<()>;
103
104    /// Decide how a framework-owned attribute should be handled.
105    fn classify_attribute(&mut self, attr_name: &str) -> AttributeAction;
106
107    /// Called after all attributes on an element have been processed.
108    /// `binding_attribute_count` is the number of dynamic attribute bindings found.
109    /// Returns optional opaque bytes to emit as a `Plugin` protocol fragment.
110    fn finish_element(&mut self, binding_attribute_count: u32) -> Option<Vec<u8>>;
111
112    /// Consume the plugin and return any build artifacts it captured.
113    ///
114    /// # Errors
115    ///
116    /// Returns an error if the plugin encountered an invalid template construct
117    /// while producing its artifacts (e.g. an invalid `@event` handler).
118    fn into_artifacts(self: Box<Self>) -> Result<ParserPluginArtifacts> {
119        Ok(ParserPluginArtifacts::None)
120    }
121}