fusee-framework 2.7.0 → 2.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +98 -26
  2. package/bin/create-fusee-linux +0 -0
  3. package/bin/create-fusee-mac-arm64 +0 -0
  4. package/bin/create-fusee-mac-intel +0 -0
  5. package/bin/create-fusee-win.exe +0 -0
  6. package/framework/auto-import-preset.js +20 -12
  7. package/framework/auto-imports.d.ts +68 -39
  8. package/framework/core/compiler.js +310 -310
  9. package/framework/core/component-core.js +445 -0
  10. package/framework/core/component.js +11 -440
  11. package/framework/core/composable.js +29 -29
  12. package/framework/core/h.js +119 -44
  13. package/framework/core/jsx-helpers.js +11 -2
  14. package/framework/core/jsx-runtime.js +73 -73
  15. package/framework/core/rust-compiler/pkg/fusee_compiler_bg.wasm +0 -0
  16. package/framework/core/store.js +333 -333
  17. package/framework/globals.js +42 -0
  18. package/framework/index.js +22 -55
  19. package/framework/router/file-router.js +184 -184
  20. package/framework/server/actions.js +183 -183
  21. package/framework/server/generate-manifest.js +3 -3
  22. package/framework/types/component.d.ts +97 -86
  23. package/framework/types/index.d.ts +118 -117
  24. package/package.json +83 -80
  25. package/framework/core/rust-compiler/Cargo.toml +0 -31
  26. package/framework/core/rust-compiler/README.md +0 -69
  27. package/framework/core/rust-compiler/pkg/README.md +0 -69
  28. package/framework/core/rust-compiler/pkg/fusee_compiler.d.ts +0 -6
  29. package/framework/core/rust-compiler/pkg/fusee_compiler.js +0 -9
  30. package/framework/core/rust-compiler/pkg/fusee_compiler_bg.js +0 -137
  31. package/framework/core/rust-compiler/pkg/fusee_compiler_bg.wasm.d.ts +0 -11
  32. package/framework/core/rust-compiler/pkg/package.json +0 -18
  33. package/framework/core/rust-compiler/src/ast.rs +0 -274
  34. package/framework/core/rust-compiler/src/errors.rs +0 -204
  35. package/framework/core/rust-compiler/src/generator.rs +0 -583
  36. package/framework/core/rust-compiler/src/lexer.rs +0 -414
  37. package/framework/core/rust-compiler/src/lib.rs +0 -87
  38. package/framework/core/rust-compiler/src/main_compiler.rs +0 -165
  39. package/framework/core/rust-compiler/src/parser.rs +0 -516
  40. package/framework/core/rust-compiler/src/ssr_generator.rs +0 -688
  41. package/framework/core/rust-compiler/src/transformer.rs +0 -562
  42. package/framework/core/rust-compiler/src/utils.rs +0 -10
  43. package/framework/engine-go/actions.go +0 -112
  44. package/framework/engine-go/cmd/server/main.go +0 -233
  45. package/framework/engine-go/comet/comet.go +0 -41
  46. package/framework/engine-go/comet/fragment.go +0 -51
  47. package/framework/engine-go/dispatcher.go +0 -120
  48. package/framework/engine-go/elements.go +0 -248
  49. package/framework/engine-go/evalexpr/evalexpr.go +0 -733
  50. package/framework/engine-go/go.mod +0 -3
  51. package/framework/engine-go/hydration.go +0 -43
  52. package/framework/engine-go/load-helpers.go +0 -29
  53. package/framework/engine-go/manifest.go +0 -41
  54. package/framework/engine-go/reactive.go +0 -123
  55. package/framework/engine-go/render.go +0 -635
  56. package/framework/engine-go/reqcontext.go +0 -48
  57. package/framework/engine-go/router.go +0 -85
  58. package/framework/engine-go/rustast/rustast.go +0 -317
  59. package/framework/engine-go/ssr.go +0 -103
  60. package/framework/engine-go/static-assets.go +0 -139
  61. package/framework/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
@@ -1,688 +0,0 @@
1
- use crate::ast::*;
2
- use crate::errors::{CompileError, ErrorCode};
3
- use once_cell::sync::Lazy;
4
- use std::collections::{HashMap, HashSet};
5
-
6
- const DEFAULT_SSR_RUNTIME: &str = "fusee/runtime/ssr.js";
7
- const VOID_ELEMENTS: &[&str] = &["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"];
8
-
9
- pub struct SSRGenerateOptions {
10
- pub source: String,
11
- pub runtime_path: Option<String>,
12
- }
13
-
14
- pub fn generate_ssr(root: &RootNode, options: SSRGenerateOptions) -> Result<String, CompileError> {
15
- let mut gen = SSRGenerator::new(&options.source, options.runtime_path.as_deref());
16
- gen.generate(root)
17
- }
18
-
19
- struct OrderedSet {
20
- items: Vec<String>,
21
- seen: HashSet<String>,
22
- }
23
-
24
- impl OrderedSet {
25
- fn new(initial: &[&str]) -> Self {
26
- let mut s = OrderedSet {
27
- items: Vec::new(),
28
- seen: HashSet::new(),
29
- };
30
- for i in initial {
31
- s.add(i);
32
- }
33
- s
34
- }
35
-
36
- fn add(&mut self, s: &str) {
37
- if self.seen.insert(s.to_string()) {
38
- self.items.push(s.to_string());
39
- }
40
- }
41
-
42
- fn join(&self, sep: &str) -> String {
43
- self.items.join(sep)
44
- }
45
- }
46
-
47
- struct SSRGenerator<'a> {
48
- source: &'a str,
49
- runtime_path: String,
50
- imports: OrderedSet,
51
- local_scopes: Vec<HashSet<String>>,
52
- anchor_idx: usize,
53
- hoisted_lookup: HashMap<NodeId, String>,
54
- hoisted_order: Vec<(NodeId, String)>,
55
- }
56
-
57
- impl<'a> SSRGenerator<'a> {
58
- fn new(source: &'a str, runtime_path: Option<&str>) -> Self {
59
- SSRGenerator {
60
- source,
61
- runtime_path: runtime_path.unwrap_or(DEFAULT_SSR_RUNTIME).to_string(),
62
- imports: OrderedSet::new(&[
63
- "escapeHtml",
64
- "escapeAttr",
65
- "renderClass",
66
- "renderStyle",
67
- "ssrEnumerate",
68
- "renderComponentSSR",
69
- "ssrVal",
70
- ]),
71
- local_scopes: Vec::new(),
72
- anchor_idx: 0,
73
- hoisted_lookup: HashMap::new(),
74
- hoisted_order: Vec::new(),
75
- }
76
- }
77
-
78
- fn next_anchor(&mut self) -> usize {
79
- let idx = self.anchor_idx;
80
- self.anchor_idx += 1;
81
- idx
82
- }
83
-
84
- fn generate(&mut self, root: &RootNode) -> Result<String, CompileError> {
85
- let mut id_to_node: HashMap<NodeId, &ElementNode> = HashMap::new();
86
- for c in &root.children {
87
- collect_element_refs(c, &mut id_to_node);
88
- }
89
-
90
- for (idx, nid) in root.hoisted.iter().enumerate() {
91
- let name = format!("_ss{}", idx);
92
- self.hoisted_lookup.insert(*nid, name.clone());
93
- self.hoisted_order.push((*nid, name));
94
- }
95
-
96
- let mut body: Vec<String> = vec!["let __html = ''".to_string()];
97
- self.emit_children(&root.children, &mut body, "__html")?;
98
- body.push("return __html".to_string());
99
-
100
- let hoisted_order = self.hoisted_order.clone();
101
- let mut hoisted_decls = Vec::new();
102
- for (nid, name) in &hoisted_order {
103
- if let Some(&node) = id_to_node.get(nid) {
104
- let mut stmts: Vec<String> = vec!["let __h = ''".to_string()];
105
- self.emit_element(node, &mut stmts, "__h", true)?;
106
- stmts.push("return __h".to_string());
107
- let inner = stmts
108
- .iter()
109
- .map(|l| format!(" {}", l))
110
- .collect::<Vec<_>>()
111
- .join("\n");
112
- hoisted_decls.push(format!("const {} = (() => {{\n{}\n}})()", name, inner));
113
- }
114
- }
115
-
116
- let import_list = self.imports.join(", ");
117
- let mut output: Vec<String> = vec![
118
- format!("import {{ {} }} from '{}'", import_list, self.runtime_path),
119
- String::new(),
120
- ];
121
- if !hoisted_decls.is_empty() {
122
- output.extend(hoisted_decls);
123
- output.push(String::new());
124
- }
125
- output.push("export async function renderSSR(_ctx, _components) {".to_string());
126
- for line in &body {
127
- output.push(format!(" {}", line));
128
- }
129
- output.push("}".to_string());
130
-
131
- Ok(output.join("\n"))
132
- }
133
-
134
- fn emit_children(&mut self, children: &[Node], out: &mut Vec<String>, var_name: &str) -> Result<(), CompileError> {
135
- for child in children {
136
- if let Some(for_dir) = find_for_dir(child) {
137
- self.emit_for_node(child, for_dir, out, var_name)?;
138
- } else {
139
- self.emit_node(child, out, var_name, false)?;
140
- }
141
- }
142
- Ok(())
143
- }
144
-
145
- fn emit_node(&mut self, node: &Node, out: &mut Vec<String>, var_name: &str, in_hoist: bool) -> Result<(), CompileError> {
146
- if !in_hoist {
147
- if let Node::Element(e) = node {
148
- if let Some(name) = self.hoisted_lookup.get(&e.id) {
149
- out.push(format!("{} += {}", var_name, name));
150
- return Ok(());
151
- }
152
- }
153
- }
154
-
155
- match node {
156
- Node::Text(t) => Ok(self.emit_text(t, out, var_name)),
157
- Node::Interpolation(i) => Ok(self.emit_interpolation(i, out, var_name)),
158
- Node::Element(e) => self.emit_element(e, out, var_name, in_hoist),
159
- Node::Component(c) => self.emit_component(c, out, var_name),
160
- Node::SlotOutlet(s) => Ok(self.emit_slot_outlet(s, out, var_name)),
161
- Node::If(i) => self.emit_if(i, out, var_name),
162
- other => Err(CompileError::new(
163
- ErrorCode::UnknownNodeType,
164
- other.loc().clone(),
165
- self.source,
166
- &[node_type_name(other).to_string()],
167
- )),
168
- }
169
- }
170
-
171
- fn emit_text(&mut self, node: &TextNode, out: &mut Vec<String>, var_name: &str) {
172
- out.push(format!("{} += {}", var_name, json_string(&node.content)));
173
- }
174
-
175
- fn emit_interpolation(&mut self, node: &InterpolationNode, out: &mut Vec<String>, var_name: &str) {
176
- let expr = &node.expression.content;
177
- if node.expression.is_static {
178
- out.push(format!("{} += escapeHtml(String({}))", var_name, expr));
179
- return;
180
- }
181
- let id = self.next_anchor();
182
- let wrapped = self.wrap_expr(expr);
183
- out.push(format!(
184
- "{} += '<!--f-bind:{}-->' + escapeHtml(String({})) + '<!--/f-bind:{}-->'",
185
- var_name, id, wrapped, id
186
- ));
187
- }
188
-
189
- fn emit_element(&mut self, node: &ElementNode, out: &mut Vec<String>, var_name: &str, _in_hoist: bool) -> Result<(), CompileError> {
190
- let tag = &node.tag;
191
- let is_void = node.self_closing || VOID_ELEMENTS.contains(&tag.to_lowercase().as_str());
192
-
193
- let html_dir = node
194
- .props
195
- .iter()
196
- .find_map(|p| p.as_directive().filter(|d| d.name == "html"));
197
-
198
- let needs_anchor = node.props.iter().any(|p| match p {
199
- Prop::Binding(_) => true,
200
- Prop::Event(_) => true,
201
- Prop::Directive(d) => !["if", "else-if", "else", "for"].contains(&d.name.as_str()),
202
- Prop::Attribute(_) => false,
203
- });
204
- let anchor_id = if needs_anchor { Some(self.next_anchor()) } else { None };
205
-
206
- out.push(format!("{} += '<{}'", var_name, tag));
207
- let is_custom_element = tag.contains('-');
208
- self.emit_attrs(&node.props, out, var_name, is_custom_element)?;
209
- if let Some(aid) = anchor_id {
210
- out.push(format!("{} += ' data-f-id=\"{}\"'", var_name, aid));
211
- }
212
- out.push(format!(
213
- "{} += '{}'",
214
- var_name,
215
- if is_void { "/>" } else { ">" }
216
- ));
217
- if is_void {
218
- return Ok(());
219
- }
220
-
221
- if let Some(dir) = html_dir {
222
- let expr = self.wrap_expr(&dir.expression.as_ref().unwrap().content);
223
- out.push(format!("{} += String({})", var_name, expr));
224
- } else {
225
- self.emit_children(&node.children, out, var_name)?;
226
- }
227
- out.push(format!("{} += '</{}>'", var_name, tag));
228
- Ok(())
229
- }
230
-
231
- fn emit_attrs(&mut self, props: &[Prop], out: &mut Vec<String>, var_name: &str, is_custom_element: bool) -> Result<(), CompileError> {
232
- for prop in props {
233
- match prop {
234
- Prop::Attribute(a) => self.emit_static_attr(a, out, var_name),
235
- Prop::Binding(b) => self.emit_binding_attr(b, out, var_name, is_custom_element)?,
236
- Prop::Directive(d) => self.emit_directive_attr(d, out, var_name)?,
237
- Prop::Event(_) => {}
238
- }
239
- }
240
- Ok(())
241
- }
242
-
243
- fn emit_static_attr(&mut self, prop: &AttributeNode, out: &mut Vec<String>, var_name: &str) {
244
- match &prop.value {
245
- None => {
246
- out.push(format!("{} += ' {}'", var_name, prop.name));
247
- }
248
- Some(v) => {
249
- out.push(format!(
250
- "{} += ' {}=\"' + escapeAttr({}) + '\"'",
251
- var_name,
252
- prop.name,
253
- json_string(v)
254
- ));
255
- }
256
- }
257
- }
258
-
259
- fn emit_binding_attr(&mut self, prop: &BindingNode, out: &mut Vec<String>, var_name: &str, is_custom_element: bool) -> Result<(), CompileError> {
260
- let expr = self.wrap_expr(&prop.expression.content);
261
- if prop.is_prop {
262
- return Ok(());
263
- }
264
- if prop.name == "class" {
265
- out.push(format!(
266
- "{} += ' class=\"' + escapeAttr(renderClass({})) + '\"'",
267
- var_name, expr
268
- ));
269
- } else if prop.name == "style" {
270
- out.push(format!(
271
- "{} += ' style=\"' + escapeAttr(renderStyle({})) + '\"'",
272
- var_name, expr
273
- ));
274
- } else {
275
- let serialize = if is_custom_element {
276
- "(typeof v === 'object' ? JSON.stringify(v) : String(v))"
277
- } else {
278
- "String(v)"
279
- };
280
- out.push(format!(
281
- "{} += (function(v){{ if (v === false || v == null) return ''; if (v === true) return ' {}'; return ' {}=\"' + escapeAttr({}) + '\"' }})({})",
282
- var_name, prop.name, prop.name, serialize, expr
283
- ));
284
- }
285
- Ok(())
286
- }
287
-
288
- fn emit_directive_attr(&mut self, dir: &DirectiveNode, out: &mut Vec<String>, var_name: &str) -> Result<(), CompileError> {
289
- match dir.name.as_str() {
290
- "show" => {
291
- let expr = self.wrap_expr(&dir.expression.as_ref().unwrap().content);
292
- out.push(format!(
293
- "{} += ({}) ? '' : ' style=\"display:none\"'",
294
- var_name, expr
295
- ));
296
- }
297
- "model" => {
298
- let expr = self.wrap_expr(&dir.expression.as_ref().unwrap().content);
299
- out.push(format!(
300
- "{} += ' value=\"' + escapeAttr(String({} ?? '')) + '\"'",
301
- var_name, expr
302
- ));
303
- }
304
- _ => {}
305
- }
306
- Ok(())
307
- }
308
-
309
- fn emit_if(&mut self, node: &IfNode, out: &mut Vec<String>, var_name: &str) -> Result<(), CompileError> {
310
- let id = self.next_anchor();
311
- out.push(format!("{} += '<!--f-if:{}-->'", var_name, id));
312
-
313
- let mut first = true;
314
- for branch in &node.branches {
315
- let mut block_stmts: Vec<String> = Vec::new();
316
- self.emit_node(&branch.node, &mut block_stmts, var_name, false)?;
317
- let indented = block_stmts
318
- .iter()
319
- .map(|s| format!(" {}", s))
320
- .collect::<Vec<_>>()
321
- .join("\n");
322
-
323
- match &branch.condition {
324
- Some(cond) => {
325
- let cond_js = self.wrap_expr(&cond.content);
326
- if first {
327
- out.push(format!("if ({}) {{\n{}\n}}", cond_js, indented));
328
- } else {
329
- out.push(format!("else if ({}) {{\n{}\n}}", cond_js, indented));
330
- }
331
- }
332
- None => {
333
- out.push(format!("else {{\n{}\n}}", indented));
334
- }
335
- }
336
- first = false;
337
- }
338
-
339
- out.push(format!("{} += '<!--/f-if:{}-->'", var_name, id));
340
- Ok(())
341
- }
342
-
343
- fn emit_for_node(&mut self, node: &Node, for_dir: &DirectiveNode, out: &mut Vec<String>, var_name: &str) -> Result<(), CompileError> {
344
- let id = self.next_anchor();
345
- let for_arg = for_dir.arg.as_ref().expect("f-for must have parsed arg");
346
- let item = for_arg.item.clone();
347
- let index = for_arg.index.clone();
348
- let source_expr = self.wrap_expr(&for_arg.source);
349
-
350
- let mut scope = HashSet::new();
351
- scope.insert(item.clone());
352
- if let Some(idx) = &index {
353
- scope.insert(idx.clone());
354
- }
355
- self.local_scopes.push(scope);
356
-
357
- let props = node.props().cloned().unwrap_or_default();
358
- let inner_props: Vec<Prop> = props
359
- .into_iter()
360
- .filter(|p| !matches!(p, Prop::Directive(d) if d.name == "for"))
361
- .collect();
362
-
363
- let key_binding = inner_props
364
- .iter()
365
- .find_map(|p| p.as_binding().filter(|b| b.name == "key"));
366
- let key_expr = key_binding
367
- .map(|b| b.expression.content.clone())
368
- .unwrap_or_else(|| "undefined".to_string());
369
-
370
- let inner_node = with_props(node.clone(), inner_props);
371
- let item_param = match &index {
372
- Some(idx) => format!("{}, {}", item, idx),
373
- None => item.clone(),
374
- };
375
-
376
- let key_wrapped = self.wrap_expr(&key_expr);
377
- let mut inner_stmts: Vec<String> = Vec::new();
378
- self.emit_node(&inner_node, &mut inner_stmts, var_name, false)?;
379
-
380
- self.local_scopes.pop();
381
-
382
- out.push(format!("{} += '<!--f-for:{}-->'", var_name, id));
383
- out.push(format!(
384
- "for (const [{}] of ssrEnumerate({})) {{",
385
- item_param, source_expr
386
- ));
387
- out.push(format!(" const __key{} = {}", id, key_wrapped));
388
- out.push(format!(
389
- " {} += '<!--f-for-item:{}:' + escapeAttr(String(__key{})) + '-->'",
390
- var_name, id, id
391
- ));
392
- for s in &inner_stmts {
393
- out.push(format!(" {}", s));
394
- }
395
- out.push(format!(
396
- " {} += '<!--/f-for-item:{}-->'",
397
- var_name, id
398
- ));
399
- out.push("}".to_string());
400
- out.push(format!("{} += '<!--/f-for:{}-->'", var_name, id));
401
- Ok(())
402
- }
403
-
404
- fn emit_component(&mut self, node: &ComponentNode, out: &mut Vec<String>, var_name: &str) -> Result<(), CompileError> {
405
- let id = self.next_anchor();
406
- let name_str = json_string(&node.name);
407
- let props_obj = self.build_component_props_obj(&node.props)?;
408
- let slots_obj = self.build_slots_obj(&node.slots)?;
409
- let id_str = json_string(&id.to_string());
410
- out.push(format!(
411
- "{} += '<!--f-component:{}:{}-->'",
412
- var_name, node.name, id
413
- ));
414
- out.push(format!(
415
- "{} += await renderComponentSSR(_components[{}], {}, {}, {})",
416
- var_name, name_str, props_obj, slots_obj, id_str
417
- ));
418
- out.push(format!("{} += '<!--/f-component:{}-->'", var_name, id));
419
- Ok(())
420
- }
421
-
422
- fn build_component_props_obj(&mut self, props: &[Prop]) -> Result<String, CompileError> {
423
- let mut entries = Vec::new();
424
- for p in props {
425
- match p {
426
- Prop::Attribute(a) => {
427
- let val = match &a.value {
428
- Some(v) => json_string(v),
429
- None => "true".to_string(),
430
- };
431
- entries.push(format!("{}: {}", json_string(&a.name), val));
432
- }
433
- Prop::Binding(b) => {
434
- let expr = self.wrap_expr(&b.expression.content);
435
- entries.push(format!("{}: {}", json_string(&b.name), expr));
436
- }
437
- _ => {}
438
- }
439
- }
440
- Ok(format!("{{ {} }}", entries.join(", ")))
441
- }
442
-
443
- fn build_slots_obj(&mut self, slots: &[(String, Vec<Node>)]) -> Result<String, CompileError> {
444
- if slots.is_empty() {
445
- return Ok("{}".to_string());
446
- }
447
- let mut entries = Vec::new();
448
- for (name, children) in slots {
449
- let mut stmts: Vec<String> = vec!["let __s = ''".to_string()];
450
- self.emit_children(children, &mut stmts, "__s")?;
451
- stmts.push("return __s".to_string());
452
- let inner = stmts
453
- .iter()
454
- .map(|l| format!(" {}", l))
455
- .collect::<Vec<_>>()
456
- .join("\n");
457
- entries.push(format!("{}: () => {{\n{}\n }}", json_string(name), inner));
458
- }
459
- Ok(format!("{{ {} }}", entries.join(", ")))
460
- }
461
-
462
- fn emit_slot_outlet(&mut self, node: &SlotOutletNode, out: &mut Vec<String>, var_name: &str) {
463
- let id = self.next_anchor();
464
- let name_str = json_string(&node.slot_name);
465
- let mut fallback_stmts: Vec<String> = vec!["let __f = ''".to_string()];
466
- for child in &node.fallback {
467
- let _ = self.emit_node(child, &mut fallback_stmts, "__f", false);
468
- }
469
- let fb = fallback_stmts.join("; ");
470
- out.push(format!("{} += '<!--f-slot:{}-->'", var_name, id));
471
- out.push(format!(
472
- "{} += (_ctx._slots && _ctx._slots[{}]) ? _ctx._slots[{}]() : (() => {{ {}; return __f }})()",
473
- var_name, name_str, name_str, fb
474
- ));
475
- out.push(format!("{} += '<!--/f-slot:{}-->'", var_name, id));
476
- }
477
-
478
- fn is_local(&self, id: &str) -> bool {
479
- self.local_scopes.iter().any(|s| s.contains(id))
480
- }
481
-
482
- fn wrap_expr(&mut self, expr: &str) -> String {
483
- if expr.contains("_ctx.") {
484
- return expr.to_string();
485
- }
486
- if is_dotted_identifier_path(expr.trim()) {
487
- let id = expr.trim().to_string();
488
- let root_var = id.split('.').next().unwrap_or("");
489
- if self.is_local(root_var) {
490
- return id;
491
- }
492
- return format!("ssrVal(_ctx.{})", id);
493
- }
494
- self.rewrite_expr(expr)
495
- }
496
-
497
- fn rewrite_expr(&self, expr: &str) -> String {
498
- let chars: Vec<char> = expr.chars().collect();
499
- let mut out = String::new();
500
- let mut i = 0;
501
- let mut skip_next_identifier = false;
502
-
503
- while i < chars.len() {
504
- let c = chars[i];
505
-
506
- if c == '\'' || c == '"' || c == '`' {
507
- let quote = c;
508
- out.push(c);
509
- i += 1;
510
- while i < chars.len() {
511
- let cc = chars[i];
512
- if cc == '\\' && i + 1 < chars.len() {
513
- out.push(cc);
514
- out.push(chars[i + 1]);
515
- i += 2;
516
- continue;
517
- }
518
- out.push(cc);
519
- i += 1;
520
- if cc == quote {
521
- break;
522
- }
523
- }
524
- continue;
525
- }
526
-
527
- if c.is_ascii_alphabetic() || c == '_' || c == '$' {
528
- let start = i;
529
- let mut j = i;
530
- while j < chars.len()
531
- && (chars[j].is_ascii_alphanumeric() || chars[j] == '_' || chars[j] == '$')
532
- {
533
- j += 1;
534
- }
535
- let id: String = chars[start..j].iter().collect();
536
-
537
- if skip_next_identifier {
538
- out.push_str(&id);
539
- skip_next_identifier = false;
540
- i = j;
541
- continue;
542
- }
543
-
544
- let preceded_by_dot_or_word = start > 0
545
- && (chars[start - 1] == '.'
546
- || chars[start - 1].is_ascii_alphanumeric()
547
- || chars[start - 1] == '_'
548
- || chars[start - 1] == '$');
549
-
550
- let mut preceded_by_colon = false;
551
- if start > 0 {
552
- let mut p = start - 1;
553
- while p > 0 && chars[p].is_whitespace() {
554
- p -= 1;
555
- }
556
- if chars[p] == ':' {
557
- preceded_by_colon = true;
558
- }
559
- }
560
-
561
- let mut k = j;
562
- while k < chars.len() && chars[k].is_whitespace() {
563
- k += 1;
564
- }
565
- let followed_by_colon = k < chars.len() && chars[k] == ':';
566
-
567
- if preceded_by_dot_or_word || preceded_by_colon || followed_by_colon {
568
- out.push_str(&id);
569
- } else if SSR_GLOBALS.contains(id.as_str()) {
570
- out.push_str(&id);
571
- if id == "as" || id == "satisfies" {
572
- skip_next_identifier = true;
573
- }
574
- } else if self.is_local(&id) {
575
- out.push_str(&id);
576
- } else {
577
- out.push_str("ssrVal(_ctx.");
578
- out.push_str(&id);
579
- out.push_str(")");
580
- }
581
- i = j;
582
- } else {
583
- out.push(c);
584
- i += 1;
585
- }
586
- }
587
-
588
- out
589
- }
590
- }
591
-
592
- fn node_type_name(node: &Node) -> &'static str {
593
- match node {
594
- Node::Root(_) => "Root",
595
- Node::Element(_) => "Element",
596
- Node::Component(_) => "Component",
597
- Node::Text(_) => "Text",
598
- Node::Interpolation(_) => "Interpolation",
599
- Node::SlotOutlet(_) => "SlotOutlet",
600
- Node::SlotContent(_) => "SlotContent",
601
- Node::If(_) => "If",
602
- }
603
- }
604
-
605
- fn find_for_dir(node: &Node) -> Option<&DirectiveNode> {
606
- node.props()?
607
- .iter()
608
- .find_map(|p| p.as_directive().filter(|d| d.name == "for"))
609
- }
610
-
611
- fn with_props(node: Node, props: Vec<Prop>) -> Node {
612
- match node {
613
- Node::Element(mut e) => {
614
- e.props = props;
615
- Node::Element(e)
616
- }
617
- Node::Component(mut c) => {
618
- c.props = props;
619
- Node::Component(c)
620
- }
621
- other => other,
622
- }
623
- }
624
-
625
- fn collect_element_refs<'a>(node: &'a Node, out: &mut HashMap<NodeId, &'a ElementNode>) {
626
- match node {
627
- Node::Element(e) => {
628
- out.insert(e.id, e);
629
- for c in &e.children {
630
- collect_element_refs(c, out);
631
- }
632
- }
633
- Node::Component(c) => {
634
- for (_, children) in &c.slots {
635
- for child in children {
636
- collect_element_refs(child, out);
637
- }
638
- }
639
- }
640
- Node::If(i) => {
641
- for b in &i.branches {
642
- collect_element_refs(&b.node, out);
643
- }
644
- }
645
- _ => {}
646
- }
647
- }
648
-
649
- fn json_string(s: &str) -> String {
650
- serde_json::to_string(s).unwrap_or_else(|_| "\"\"".to_string())
651
- }
652
-
653
- fn is_dotted_identifier_path(s: &str) -> bool {
654
- if s.is_empty() {
655
- return false;
656
- }
657
- let mut chars = s.chars();
658
- match chars.next() {
659
- Some(c) if c.is_ascii_alphabetic() || c == '_' || c == '$' => {}
660
- _ => return false,
661
- }
662
- chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '$' || c == '.')
663
- }
664
-
665
- static SSR_GLOBALS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
666
- [
667
- // literals / special values
668
- "true", "false", "null", "undefined", "NaN", "Infinity",
669
- // keywords
670
- "typeof", "instanceof", "in", "of", "new", "delete", "void",
671
- "return", "if", "else", "for", "while", "do", "switch", "case",
672
- "break", "continue", "throw", "try", "catch", "finally",
673
- "class", "function", "var", "let", "const", "import", "export",
674
- "async", "await", "yield", "static", "super", "this",
675
- // runtime helpers (already in scope via imports)
676
- "escapeHtml", "escapeAttr", "renderClass", "renderStyle",
677
- "ssrEnumerate", "renderComponentSSR", "ssrVal",
678
- // common browser / Node globals
679
- "console", "Math", "JSON", "Object", "Array", "String", "Number",
680
- "Boolean", "Date", "RegExp", "Error", "Promise", "Map", "Set",
681
- "Symbol", "WeakMap", "WeakSet", "globalThis", "window", "document",
682
- "parseInt", "parseFloat", "isNaN", "isFinite",
683
- "encodeURI", "decodeURI", "encodeURIComponent", "decodeURIComponent",
684
- "as", "satisfies",
685
- ]
686
- .into_iter()
687
- .collect()
688
- });