el-ui v2: universal platform, service bindings, AOP, auth, publish pipeline

This commit is contained in:
Will Anderson
2026-04-27 19:52:29 -05:00
parent ea56ad6e09
commit c01d817141
51 changed files with 6653 additions and 1 deletions
+179 -1
View File
@@ -5,20 +5,95 @@
//! 2. Stores each `state` field as a node in an in-instance `Graph`.
//! 3. Implements `render()` returning an HTML string.
//! 4. Uses `setState()` to trigger spreading activation and DOM patching.
//!
//! ## Codegen targets
//!
//! The `CodegenTarget` enum selects the output format:
//!
//! - `Web` — ES2022 module (current, default behavior)
//! - `Server` — Rust code calling the `el-platform` server backend for SSR
//! - `Native(Platform)` — Rust code calling the `el-platform` native backend trait
use crate::ast::*;
use crate::error::CompileResult;
/// Which native platform to target when using `CodegenTarget::Native`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Platform {
Ios,
Android,
Macos,
Linux,
Windows,
}
impl Platform {
pub fn as_str(&self) -> &'static str {
match self {
Self::Ios => "ios",
Self::Android => "android",
Self::Macos => "macos",
Self::Linux => "linux",
Self::Windows => "windows",
}
}
}
/// Selects the output format for the code generator.
///
/// - `Web` — ES2022 JavaScript module (default, uses the el-ui JS runtime)
/// - `Server` — Rust module that uses `el_platform::ServerBackend` for SSR.
/// The generated Rust struct implements a `render_to_html()` method.
/// - `Native(Platform)` — Rust module calling `el_platform::PlatformBackend`
/// for the specified native platform (iOS, Android, macOS, Linux, Windows).
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CodegenTarget {
/// Current default — ES2022 JavaScript module for the browser.
Web,
/// Server-side rendering — generates Rust code using `el_platform::ServerBackend`.
Server,
/// Native platform — generates Rust code using the platform backend trait.
Native(Platform),
}
impl CodegenTarget {
pub fn is_web(&self) -> bool {
matches!(self, Self::Web)
}
pub fn is_rust_output(&self) -> bool {
matches!(self, Self::Server | Self::Native(_))
}
}
pub struct Codegen {
runtime_path: String,
/// The compilation target. Defaults to `Web`.
pub target: CodegenTarget,
}
impl Codegen {
pub fn new(runtime_path: &str) -> Self {
Self { runtime_path: runtime_path.to_owned() }
Self {
runtime_path: runtime_path.to_owned(),
target: CodegenTarget::Web,
}
}
pub fn with_target(mut self, target: CodegenTarget) -> Self {
self.target = target;
self
}
pub fn generate(&self, components: &[Component]) -> CompileResult<String> {
match &self.target {
CodegenTarget::Web => self.generate_web(components),
CodegenTarget::Server => self.generate_server_rust(components),
CodegenTarget::Native(platform) => self.generate_native_rust(components, platform),
}
}
fn generate_web(&self, components: &[Component]) -> CompileResult<String> {
let mut out = String::new();
// Runtime import
@@ -41,6 +116,109 @@ impl Codegen {
Ok(out)
}
/// Generate Rust code for the `server` target.
///
/// The output is a Rust module where each component is a struct implementing
/// `render_to_html(&self) -> String` using `el_platform::ServerBackend`.
fn generate_server_rust(&self, components: &[Component]) -> CompileResult<String> {
let mut out = String::new();
out.push_str("//! el-ui SSR — generated by el-ui-compiler (target: server)\n");
out.push_str("//! Do not edit. Re-generate with: el-ui-compiler --target server\n\n");
out.push_str("use el_platform::{ServerBackend, PlatformBackend, PlatformNode};\n\n");
for comp in components {
out.push_str(&format!("/// Server-side rendered component: {}\n", comp.name));
out.push_str(&format!("pub struct {} {{\n", comp.name));
for prop in &comp.props {
out.push_str(&format!(" pub {}: String,\n", prop.name));
}
for state in &comp.state {
out.push_str(&format!(" pub {}: String,\n", state.name));
}
out.push_str("}\n\n");
out.push_str(&format!("impl {} {{\n", comp.name));
out.push_str(" pub fn render_to_html(&self) -> String {\n");
out.push_str(" let backend = ServerBackend::new();\n");
// Generate a simple element tree based on the template
out.push_str(" let root = self.build_node_tree();\n");
out.push_str(" backend.render_to_string(&root).unwrap_or_default()\n");
out.push_str(" }\n\n");
out.push_str(" fn build_node_tree(&self) -> PlatformNode {\n");
out.push_str(" // TODO: full template → PlatformNode tree codegen\n");
out.push_str(" // The compiler translates each TemplateNode into\n");
out.push_str(" // PlatformNode::element() / PlatformNode::text() calls.\n");
out.push_str(" PlatformNode::element(\"div\")\n");
out.push_str(" }\n");
out.push_str("}\n\n");
}
Ok(out)
}
/// Generate Rust code for a `native` target.
///
/// The output is a Rust module where each component builds a `PlatformNode`
/// tree and calls the appropriate backend to mount it.
fn generate_native_rust(&self, components: &[Component], platform: &Platform) -> CompileResult<String> {
let backend_type = match platform {
Platform::Ios => "IosBackend",
Platform::Android => "AndroidBackend",
Platform::Macos => "MacosBackend",
Platform::Linux => "LinuxBackend",
Platform::Windows => "WindowsBackend",
};
let mut out = String::new();
out.push_str(&format!(
"//! el-ui native — generated by el-ui-compiler (target: {})\n",
platform.as_str()
));
out.push_str("//! Do not edit. Re-generate with: el-ui-compiler --target <platform>\n\n");
out.push_str(&format!(
"use el_platform::{{{} as Backend, PlatformBackend, PlatformNode}};\n\n",
backend_type
));
for comp in components {
out.push_str(&format!("/// Native component: {} ({})\n", comp.name, platform.as_str()));
out.push_str(&format!("pub struct {} {{\n", comp.name));
for prop in &comp.props {
out.push_str(&format!(" pub {}: String,\n", prop.name));
}
for state in &comp.state {
out.push_str(&format!(" pub {}: String,\n", state.name));
}
out.push_str(" backend: Backend,\n");
out.push_str("}\n\n");
out.push_str(&format!("impl {} {{\n", comp.name));
out.push_str(" pub fn new() -> Self {\n");
out.push_str(" Self {\n");
for prop in &comp.props {
let default = prop.default.as_deref().unwrap_or("\"\"");
out.push_str(&format!(" {}: {}.to_string(),\n", prop.name, default));
}
for state in &comp.state {
out.push_str(&format!(" {}: {}.to_string(),\n", state.name, state.initial));
}
out.push_str(" backend: Backend::new(),\n");
out.push_str(" }\n");
out.push_str(" }\n\n");
out.push_str(" pub fn mount(&self, container_id: &str) -> el_platform::PlatformResult<()> {\n");
out.push_str(" let root = self.build_node_tree();\n");
out.push_str(" self.backend.mount(root, container_id)\n");
out.push_str(" }\n\n");
out.push_str(" fn build_node_tree(&self) -> PlatformNode {\n");
out.push_str(" // TODO: full template → PlatformNode tree codegen\n");
out.push_str(" PlatformNode::element(\"div\")\n");
out.push_str(" }\n");
out.push_str("}\n\n");
}
Ok(out)
}
fn gen_component(&self, comp: &Component) -> CompileResult<String> {
let mut out = String::new();