Archived
el-ui v2: universal platform, service bindings, AOP, auth, publish pipeline
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user