1use kotoba_core::prelude::*;
6use crate::frontend::component_ir::{ComponentIR, ElementIR, ElementChild, ComponentType, ExecutionEnvironment};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub enum VirtualNodeIR {
13 Element(ElementIR),
14 Component(ComponentIR),
15 Text(String),
16 Fragment(Vec<VirtualNodeIR>),
17}
18
19impl VirtualNodeIR {
20 pub fn element(tag_name: String) -> Self {
21 VirtualNodeIR::Element(ElementIR::new(tag_name))
22 }
23
24 pub fn component(component: ComponentIR) -> Self {
25 VirtualNodeIR::Component(component)
26 }
27
28 pub fn text(content: String) -> Self {
29 VirtualNodeIR::Text(content)
30 }
31
32 pub fn fragment(children: Vec<VirtualNodeIR>) -> Self {
33 VirtualNodeIR::Fragment(children)
34 }
35}
36
37#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
39pub struct RenderContext {
40 pub environment: ExecutionEnvironment,
41 pub route_params: Properties,
42 pub query_params: Properties,
43 pub global_state: Properties,
44 pub is_server_side: bool,
45 pub is_client_side: bool,
46 pub hydration_id: Option<String>,
47}
48
49impl RenderContext {
50 pub fn new() -> Self {
51 Self {
52 environment: ExecutionEnvironment::Universal,
53 route_params: Properties::new(),
54 query_params: Properties::new(),
55 global_state: Properties::new(),
56 is_server_side: false,
57 is_client_side: false,
58 hydration_id: None,
59 }
60 }
61
62 pub fn server_side() -> Self {
63 Self {
64 is_server_side: true,
65 ..Self::new()
66 }
67 }
68
69 pub fn client_side() -> Self {
70 Self {
71 is_client_side: true,
72 ..Self::new()
73 }
74 }
75
76 pub fn with_route_params(mut self, params: Properties) -> Self {
77 self.route_params = params;
78 self
79 }
80
81 pub fn with_query_params(mut self, params: Properties) -> Self {
82 self.query_params = params;
83 self
84 }
85}
86
87#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
89pub struct RenderResultIR {
90 pub html: String,
91 pub css: String,
92 pub js: String,
93 pub hydration_script: Option<String>,
94 pub head_elements: Vec<HeadElementIR>,
95 pub virtual_dom: VirtualNodeIR,
96 pub render_stats: RenderStats,
97}
98
99#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
100pub struct HeadElementIR {
101 pub element_type: HeadElementType,
102 pub attributes: Properties,
103 pub content: Option<String>,
104}
105
106#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
107pub enum HeadElementType {
108 Title,
109 Meta,
110 Link,
111 Script,
112 Style,
113}
114
115#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
116pub struct RenderStats {
117 pub render_time_ms: u64,
118 pub component_count: usize,
119 pub dom_node_count: usize,
120 pub memory_usage_kb: usize,
121}
122
123#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
125pub struct RenderEngineIR {
126 pub strategies: Vec<RenderStrategy>,
127 pub optimizers: Vec<RenderOptimizer>,
128 pub cache_config: RenderCacheConfig,
129}
130
131#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
132pub enum RenderStrategy {
133 SSR,
135 SSG,
137 CSR,
139 StreamingSSR,
141 ProgressiveHydration,
143}
144
145#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
146pub enum RenderOptimizer {
147 CodeSplitting,
149 TreeShaking,
151 LazyLoading,
153 Preload,
155 Prefetch,
157}
158
159#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
160pub struct RenderCacheConfig {
161 pub enable_cache: bool,
162 pub cache_strategy: CacheStrategy,
163 pub max_cache_size: usize,
164 pub ttl_seconds: u64,
165}
166
167#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
168pub enum CacheStrategy {
169 LRU,
170 LFU,
171 TimeBased,
172 SizeBased,
173}
174
175#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
177pub struct DiffIR {
178 pub patches: Vec<PatchIR>,
179 pub old_tree: VirtualNodeIR,
180 pub new_tree: VirtualNodeIR,
181 pub affected_nodes: Vec<String>,
182}
183
184#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
185pub enum PatchIR {
186 Insert {
188 parent_id: String,
189 node: VirtualNodeIR,
190 index: usize,
191 },
192 Remove {
194 node_id: String,
195 },
196 Update {
198 node_id: String,
199 attributes: Properties,
200 text_content: Option<String>,
201 },
202 Move {
204 node_id: String,
205 new_parent_id: String,
206 new_index: usize,
207 },
208 UpdateAttribute {
210 node_id: String,
211 attribute_name: String,
212 new_value: Option<Value>,
213 },
214}
215
216#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
218pub enum LifecycleEventIR {
219 Mount {
220 component_id: String,
221 props: Properties,
222 },
223 Update {
224 component_id: String,
225 old_props: Properties,
226 new_props: Properties,
227 },
228 Unmount {
229 component_id: String,
230 },
231 Error {
232 component_id: String,
233 error: String,
234 error_boundary_id: Option<String>,
235 },
236 Suspend {
237 component_id: String,
238 fallback: VirtualNodeIR,
239 },
240 Resume {
241 component_id: String,
242 },
243}
244
245#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
247pub struct RenderPipelineIR {
248 pub stages: Vec<RenderStage>,
249 pub error_handling: ErrorHandlingStrategy,
250 pub performance_monitoring: bool,
251}
252
253#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
254pub enum RenderStage {
255 ResolveComponents,
257 MapProps,
259 InitializeState,
261 BuildVirtualDOM,
263 ApplyOptimizations,
265 GenerateHTML,
267 GenerateHydrationScript,
269 BundleAssets,
271}
272
273#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
274pub enum ErrorHandlingStrategy {
275 FailFast,
277 UseErrorBoundaries,
279 FallbackContent,
281 LogOnly,
283}
284
285#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
287pub struct SuspenseBoundaryIR {
288 pub id: String,
289 pub children: Vec<VirtualNodeIR>,
290 pub fallback: VirtualNodeIR,
291 pub pending_promises: Vec<String>,
292 pub resolved: bool,
293}
294
295impl SuspenseBoundaryIR {
296 pub fn new(id: String, fallback: VirtualNodeIR) -> Self {
297 Self {
298 id,
299 children: Vec::new(),
300 fallback,
301 pending_promises: Vec::new(),
302 resolved: false,
303 }
304 }
305
306 pub fn add_child(&mut self, child: VirtualNodeIR) {
307 self.children.push(child);
308 }
309
310 pub fn add_promise(&mut self, promise_id: String) {
311 self.pending_promises.push(promise_id);
312 }
313
314 pub fn resolve_promise(&mut self, promise_id: &str) {
315 self.pending_promises.retain(|id| id != promise_id);
316 if self.pending_promises.is_empty() {
317 self.resolved = true;
318 }
319 }
320}
321
322#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
324pub struct HydrationIR {
325 pub server_html: String,
326 pub client_script: String,
327 pub hydration_map: HashMap<String, HydrationNode>,
328 pub event_listeners: Vec<EventListenerIR>,
329}
330
331#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
332pub struct HydrationNode {
333 pub id: String,
334 pub component_type: ComponentType,
335 pub props: Properties,
336 pub state: Properties,
337}
338
339#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
340pub struct EventListenerIR {
341 pub element_id: String,
342 pub event_type: String,
343 pub handler_function: String,
344 pub options: EventOptions,
345}
346
347#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
348pub struct EventOptions {
349 pub capture: bool,
350 pub once: bool,
351 pub passive: bool,
352}
353
354#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
356pub struct MemoizationIR {
357 pub component_id: String,
358 pub comparison_function: Option<String>,
359 pub cached_props: Properties,
360 pub cache_hit: bool,
361}
362
363impl MemoizationIR {
364 pub fn new(component_id: String) -> Self {
365 Self {
366 component_id,
367 comparison_function: None,
368 cached_props: Properties::new(),
369 cache_hit: false,
370 }
371 }
372
373 pub fn should_update(&self, new_props: &Properties) -> bool {
374 &self.cached_props != new_props
376 }
377
378 pub fn update_cache(&mut self, new_props: Properties) {
379 self.cached_props = new_props;
380 self.cache_hit = true;
381 }
382}
383
384#[cfg(test)]
385mod tests {
386 use super::*;
387 use crate::frontend::component_ir::ComponentIR;
388
389 #[test]
390 fn test_virtual_node_creation() {
391 let element = VirtualNodeIR::element("div".to_string());
392 match element {
393 VirtualNodeIR::Element(el) => assert_eq!(el.tag_name, "div"),
394 _ => panic!("Expected Element"),
395 }
396
397 let text = VirtualNodeIR::text("Hello".to_string());
398 match text {
399 VirtualNodeIR::Text(content) => assert_eq!(content, "Hello"),
400 _ => panic!("Expected Text"),
401 }
402 }
403
404 #[test]
405 fn test_render_context() {
406 let context = RenderContext::server_side()
407 .with_route_params({
408 let mut props = Properties::new();
409 props.insert("id".to_string(), Value::String("123".to_string()));
410 props
411 });
412
413 assert!(context.is_server_side);
414 assert_eq!(context.route_params.get("id"), Some(&Value::String("123".to_string())));
415 }
416
417 #[test]
418 fn test_suspense_boundary() {
419 let fallback = VirtualNodeIR::text("Loading...".to_string());
420 let mut boundary = SuspenseBoundaryIR::new("suspense-1".to_string(), fallback);
421
422 boundary.add_promise("promise-1".to_string());
423 boundary.add_promise("promise-2".to_string());
424
425 assert_eq!(boundary.pending_promises.len(), 2);
426 assert!(!boundary.resolved);
427
428 boundary.resolve_promise("promise-1");
429 assert_eq!(boundary.pending_promises.len(), 1);
430 assert!(!boundary.resolved);
431
432 boundary.resolve_promise("promise-2");
433 assert_eq!(boundary.pending_promises.len(), 0);
434 assert!(boundary.resolved);
435 }
436
437 #[test]
438 fn test_memoization() {
439 let mut memo = MemoizationIR::new("MyComponent".to_string());
440
441 let mut new_props = Properties::new();
442 new_props.insert("count".to_string(), Value::Int(1));
443
444 assert!(memo.should_update(&new_props));
445
446 memo.update_cache(new_props.clone());
447 assert!(!memo.should_update(&new_props)); let mut different_props = Properties::new();
450 different_props.insert("count".to_string(), Value::Int(2));
451 assert!(memo.should_update(&different_props)); }
453}