@tishlang/tish 1.9.2 → 1.10.0
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.
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/transform/expr.rs +8 -6
- package/crates/js_to_tish/src/transform/stmt.rs +12 -13
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/cargo_native_registry.rs +4 -1
- package/crates/tish/src/main.rs +11 -8
- package/crates/tish/tests/integration_test.rs +145 -7
- package/crates/tish_ast/src/ast.rs +3 -9
- package/crates/tish_build_utils/src/lib.rs +43 -15
- package/crates/tish_builtins/src/array.rs +2 -3
- package/crates/tish_builtins/src/construct.rs +15 -28
- package/crates/tish_builtins/src/globals.rs +18 -16
- package/crates/tish_builtins/src/helpers.rs +1 -4
- package/crates/tish_builtins/src/lib.rs +1 -0
- package/crates/tish_builtins/src/object.rs +10 -10
- package/crates/tish_builtins/src/string.rs +1 -3
- package/crates/tish_builtins/src/symbol.rs +83 -0
- package/crates/tish_compile/src/codegen.rs +123 -138
- package/crates/tish_compile/src/lib.rs +25 -3
- package/crates/tish_compile/src/resolve.rs +6 -3
- package/crates/tish_compile/src/types.rs +6 -6
- package/crates/tish_compile_js/src/codegen.rs +8 -5
- package/crates/tish_core/src/console_style.rs +9 -0
- package/crates/tish_core/src/json.rs +17 -7
- package/crates/tish_core/src/macros.rs +2 -2
- package/crates/tish_core/src/value.rs +192 -4
- package/crates/tish_cranelift_runtime/Cargo.toml +4 -0
- package/crates/tish_eval/src/eval.rs +135 -73
- package/crates/tish_eval/src/http.rs +18 -12
- package/crates/tish_eval/src/lib.rs +29 -0
- package/crates/tish_eval/src/regex.rs +1 -1
- package/crates/tish_eval/src/value.rs +89 -4
- package/crates/tish_eval/src/value_convert.rs +30 -8
- package/crates/tish_fmt/src/lib.rs +4 -1
- package/crates/tish_lexer/src/lib.rs +7 -2
- package/crates/tish_llvm/src/lib.rs +2 -2
- package/crates/tish_lsp/src/builtin_goto.rs +111 -10
- package/crates/tish_lsp/src/import_goto.rs +35 -22
- package/crates/tish_lsp/src/main.rs +118 -85
- package/crates/tish_native/src/build.rs +187 -10
- package/crates/tish_native/src/lib.rs +92 -8
- package/crates/tish_parser/src/lib.rs +5 -2
- package/crates/tish_parser/src/parser.rs +45 -75
- package/crates/tish_pg/src/error.rs +1 -1
- package/crates/tish_pg/src/lib.rs +61 -73
- package/crates/tish_resolve/src/lib.rs +283 -158
- package/crates/tish_resolve/src/pos.rs +10 -2
- package/crates/tish_runtime/Cargo.toml +3 -0
- package/crates/tish_runtime/src/http.rs +39 -39
- package/crates/tish_runtime/src/http_fetch.rs +12 -12
- package/crates/tish_runtime/src/lib.rs +26 -43
- package/crates/tish_runtime/src/native_promise.rs +0 -11
- package/crates/tish_runtime/src/promise.rs +14 -1
- package/crates/tish_runtime/src/promise_io.rs +1 -4
- package/crates/tish_runtime/src/ws.rs +40 -27
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +10 -8
- package/crates/tish_ui/src/jsx.rs +6 -4
- package/crates/tish_ui/src/lib.rs +2 -2
- package/crates/tish_ui/src/runtime/hooks.rs +5 -15
- package/crates/tish_ui/src/runtime/mod.rs +16 -17
- package/crates/tish_vm/Cargo.toml +2 -0
- package/crates/tish_vm/src/vm.rs +218 -153
- package/crates/tish_wasm/src/lib.rs +33 -7
- package/crates/tish_wasm_runtime/Cargo.toml +4 -1
- package/crates/tish_wasm_runtime/src/lib.rs +2 -1
- package/crates/tishlang_cargo_bindgen/src/classify.rs +1 -3
- package/crates/tishlang_cargo_bindgen/src/discover.rs +10 -5
- package/crates/tishlang_cargo_bindgen/src/infer.rs +18 -8
- package/crates/tishlang_cargo_bindgen/src/lib.rs +25 -26
- package/crates/tishlang_cargo_bindgen/src/main.rs +41 -38
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +4 -1
- package/justfile +3 -3
- package/package.json +1 -1
- package/platform/darwin-arm64/tish +0 -0
- package/platform/darwin-x64/tish +0 -0
- package/platform/linux-arm64/tish +0 -0
- package/platform/linux-x64/tish +0 -0
- package/platform/win32-x64/tish.exe +0 -0
|
@@ -5,10 +5,8 @@
|
|
|
5
5
|
//! - **Connection**: `send(data)`, `close()`, `readyState` (1=OPEN), `receive()` / `receiveTimeout(ms)`
|
|
6
6
|
//! - **Broadcast** (Node pattern): `server.clients.forEach(ws => ws.send(data))` or iterate room conns and `wsSend(ws, data)` (same as `ws.send(data)`)
|
|
7
7
|
|
|
8
|
-
use std::cell::RefCell;
|
|
9
8
|
use tishlang_core::VmRef;
|
|
10
9
|
use std::collections::HashMap;
|
|
11
|
-
use std::rc::Rc;
|
|
12
10
|
use std::sync::atomic::{AtomicU32, Ordering};
|
|
13
11
|
use std::sync::mpsc;
|
|
14
12
|
use std::sync::{Arc, Mutex};
|
|
@@ -151,7 +149,7 @@ fn conn_id_from_value(v: &Value) -> Option<u32> {
|
|
|
151
149
|
Value::Object(o) => {
|
|
152
150
|
let b = o.borrow();
|
|
153
151
|
// Direct conn: { _id, send, ... }
|
|
154
|
-
if let Some(idv) = b.get(
|
|
152
|
+
if let Some(idv) = b.strings.get("_id") {
|
|
155
153
|
if let Value::Number(n) = idv {
|
|
156
154
|
if n.is_finite() && *n >= 0.0 {
|
|
157
155
|
return Some(*n as u32);
|
|
@@ -159,7 +157,7 @@ fn conn_id_from_value(v: &Value) -> Option<u32> {
|
|
|
159
157
|
}
|
|
160
158
|
}
|
|
161
159
|
// Wrapper: { ws: conn, ... }
|
|
162
|
-
if let Some(ws) = b.get(
|
|
160
|
+
if let Some(ws) = b.strings.get("ws") {
|
|
163
161
|
return conn_id_from_value(ws);
|
|
164
162
|
}
|
|
165
163
|
None
|
|
@@ -221,7 +219,7 @@ fn conn_object(id: u32) -> Value {
|
|
|
221
219
|
Some(s) => {
|
|
222
220
|
let mut ev: ObjectMap = ObjectMap::default();
|
|
223
221
|
ev.insert(Arc::from("data"), Value::String(s.into()));
|
|
224
|
-
Value::
|
|
222
|
+
Value::object(ev)
|
|
225
223
|
}
|
|
226
224
|
None => Value::Null,
|
|
227
225
|
}),
|
|
@@ -243,18 +241,18 @@ fn conn_object(id: u32) -> Value {
|
|
|
243
241
|
Some(s) => {
|
|
244
242
|
let mut ev: ObjectMap = ObjectMap::default();
|
|
245
243
|
ev.insert(Arc::from("data"), Value::String(s.into()));
|
|
246
|
-
Value::
|
|
244
|
+
Value::object(ev)
|
|
247
245
|
}
|
|
248
246
|
None => Value::Null,
|
|
249
247
|
}
|
|
250
248
|
}),
|
|
251
249
|
);
|
|
252
|
-
Value::
|
|
250
|
+
Value::object(obj)
|
|
253
251
|
}
|
|
254
252
|
|
|
255
253
|
fn parse_port(args: &[Value]) -> Option<u16> {
|
|
256
254
|
args.first().and_then(|v| match v {
|
|
257
|
-
Value::Object(o) => o.borrow().get(
|
|
255
|
+
Value::Object(o) => o.borrow().strings.get("port").and_then(|v| match v {
|
|
258
256
|
Value::Number(n) if n.is_finite() && *n >= 0.0 => Some(*n as u16),
|
|
259
257
|
_ => None,
|
|
260
258
|
}),
|
|
@@ -485,7 +483,9 @@ pub fn web_socket_server_construct(args: &[Value]) -> Value {
|
|
|
485
483
|
.unwrap_or_default();
|
|
486
484
|
let cb = args.get(2).cloned().unwrap_or(Value::Null);
|
|
487
485
|
if event == "connection" {
|
|
488
|
-
so.borrow_mut()
|
|
486
|
+
so.borrow_mut()
|
|
487
|
+
.strings
|
|
488
|
+
.insert(Arc::from("_onConnection"), cb);
|
|
489
489
|
}
|
|
490
490
|
Value::Null
|
|
491
491
|
});
|
|
@@ -498,14 +498,20 @@ pub fn web_socket_server_construct(args: &[Value]) -> Value {
|
|
|
498
498
|
loop {
|
|
499
499
|
let handle_n = {
|
|
500
500
|
let b = so.borrow();
|
|
501
|
-
match b
|
|
501
|
+
match b
|
|
502
|
+
.strings
|
|
503
|
+
.get("_handle")
|
|
504
|
+
.cloned()
|
|
505
|
+
.unwrap_or(Value::Null)
|
|
506
|
+
{
|
|
502
507
|
Value::Number(n) if n.is_finite() && n >= 0.0 => n,
|
|
503
508
|
_ => break,
|
|
504
509
|
}
|
|
505
510
|
};
|
|
506
511
|
let cb = so
|
|
507
512
|
.borrow()
|
|
508
|
-
.
|
|
513
|
+
.strings
|
|
514
|
+
.get("_onConnection")
|
|
509
515
|
.cloned()
|
|
510
516
|
.unwrap_or(Value::Null);
|
|
511
517
|
let ws = web_socket_server_accept(&[Value::Number(handle_n)]);
|
|
@@ -527,7 +533,8 @@ pub fn web_socket_server_construct(args: &[Value]) -> Value {
|
|
|
527
533
|
};
|
|
528
534
|
let handle_n = so
|
|
529
535
|
.borrow()
|
|
530
|
-
.
|
|
536
|
+
.strings
|
|
537
|
+
.get("_handle")
|
|
531
538
|
.cloned()
|
|
532
539
|
.unwrap_or(Value::Null);
|
|
533
540
|
let timeout_ms = args.get(1).cloned().unwrap_or(Value::Number(100.0));
|
|
@@ -545,7 +552,7 @@ pub fn web_socket_server_construct(args: &[Value]) -> Value {
|
|
|
545
552
|
m.insert(Arc::from("on"), on_fn);
|
|
546
553
|
m.insert(Arc::from("listen"), listen_fn);
|
|
547
554
|
m.insert(Arc::from("acceptTimeout"), accept_timeout_fn);
|
|
548
|
-
Value::
|
|
555
|
+
Value::object(m)
|
|
549
556
|
}
|
|
550
557
|
|
|
551
558
|
#[cfg(test)]
|
|
@@ -560,7 +567,7 @@ mod tests {
|
|
|
560
567
|
let opts = {
|
|
561
568
|
let mut m: ObjectMap = ObjectMap::default();
|
|
562
569
|
m.insert(Arc::from("port"), Value::Number(port as f64));
|
|
563
|
-
Value::
|
|
570
|
+
Value::object(m)
|
|
564
571
|
};
|
|
565
572
|
|
|
566
573
|
let handle = match web_socket_server_listen(std::slice::from_ref(&opts)) {
|
|
@@ -575,20 +582,21 @@ mod tests {
|
|
|
575
582
|
};
|
|
576
583
|
// Echo one message
|
|
577
584
|
for _ in 0..50 {
|
|
578
|
-
let recv_fn = wso.borrow().get(
|
|
585
|
+
let recv_fn = wso.borrow().strings.get("receive").cloned();
|
|
579
586
|
if let Some(Value::Function(rf)) = recv_fn {
|
|
580
587
|
let msg = rf(&[]);
|
|
581
588
|
if !matches!(msg, Value::Null) {
|
|
582
589
|
let data = match msg {
|
|
583
590
|
Value::Object(ev) => ev
|
|
584
591
|
.borrow()
|
|
585
|
-
.
|
|
592
|
+
.strings
|
|
593
|
+
.get("data")
|
|
586
594
|
.map(|v| v.to_display_string())
|
|
587
595
|
.unwrap_or_default(),
|
|
588
596
|
_ => String::new(),
|
|
589
597
|
};
|
|
590
598
|
if let Some(Value::Function(sf)) =
|
|
591
|
-
wso.borrow().get(
|
|
599
|
+
wso.borrow().strings.get("send").cloned()
|
|
592
600
|
{
|
|
593
601
|
let _ = sf(&[Value::String(data.into())]);
|
|
594
602
|
}
|
|
@@ -607,13 +615,13 @@ mod tests {
|
|
|
607
615
|
let Value::Object(co) = client else {
|
|
608
616
|
panic!("client not object");
|
|
609
617
|
};
|
|
610
|
-
let send = co.borrow().get(
|
|
618
|
+
let send = co.borrow().strings.get("send").cloned().unwrap();
|
|
611
619
|
let Value::Function(send_f) = send else {
|
|
612
620
|
panic!("no send");
|
|
613
621
|
};
|
|
614
622
|
let _ = send_f(&[Value::String("hello".into())]);
|
|
615
623
|
|
|
616
|
-
let recv = co.borrow().get(
|
|
624
|
+
let recv = co.borrow().strings.get("receive").cloned().unwrap();
|
|
617
625
|
let Value::Function(recv_f) = recv else {
|
|
618
626
|
panic!("no receive");
|
|
619
627
|
};
|
|
@@ -630,7 +638,8 @@ mod tests {
|
|
|
630
638
|
};
|
|
631
639
|
let data = ev
|
|
632
640
|
.borrow()
|
|
633
|
-
.
|
|
641
|
+
.strings
|
|
642
|
+
.get("data")
|
|
634
643
|
.map(|v| v.to_display_string())
|
|
635
644
|
.unwrap_or_default();
|
|
636
645
|
assert_eq!(data, "hello");
|
|
@@ -645,7 +654,7 @@ mod tests {
|
|
|
645
654
|
let opts = {
|
|
646
655
|
let mut m: ObjectMap = ObjectMap::default();
|
|
647
656
|
m.insert(Arc::from("port"), Value::Number(port as f64));
|
|
648
|
-
Value::
|
|
657
|
+
Value::object(m)
|
|
649
658
|
};
|
|
650
659
|
|
|
651
660
|
let handle = match web_socket_server_listen(std::slice::from_ref(&opts)) {
|
|
@@ -658,7 +667,7 @@ mod tests {
|
|
|
658
667
|
let Value::Object(wso) = ws else {
|
|
659
668
|
panic!("accept failed");
|
|
660
669
|
};
|
|
661
|
-
let recv_fn = wso.borrow().get(
|
|
670
|
+
let recv_fn = wso.borrow().strings.get("receive").cloned();
|
|
662
671
|
let Value::Function(rf) = recv_fn.unwrap() else {
|
|
663
672
|
panic!("no receive");
|
|
664
673
|
};
|
|
@@ -669,7 +678,8 @@ mod tests {
|
|
|
669
678
|
let data = match &msg {
|
|
670
679
|
Value::Object(ev) => ev
|
|
671
680
|
.borrow()
|
|
672
|
-
.
|
|
681
|
+
.strings
|
|
682
|
+
.get("data")
|
|
673
683
|
.map(|v| v.to_display_string())
|
|
674
684
|
.unwrap_or_default(),
|
|
675
685
|
_ => String::new(),
|
|
@@ -695,7 +705,7 @@ mod tests {
|
|
|
695
705
|
let Value::Object(co) = client else {
|
|
696
706
|
panic!("client not object");
|
|
697
707
|
};
|
|
698
|
-
let send = co.borrow().get(
|
|
708
|
+
let send = co.borrow().strings.get("send").cloned().unwrap();
|
|
699
709
|
let Value::Function(send_f) = send else {
|
|
700
710
|
panic!("no send");
|
|
701
711
|
};
|
|
@@ -705,7 +715,8 @@ mod tests {
|
|
|
705
715
|
// Client uses receiveTimeout like the agent
|
|
706
716
|
let recv_timeout = co
|
|
707
717
|
.borrow()
|
|
708
|
-
.
|
|
718
|
+
.strings
|
|
719
|
+
.get("receiveTimeout")
|
|
709
720
|
.cloned()
|
|
710
721
|
.unwrap();
|
|
711
722
|
let Value::Function(recv_timeout_f) = recv_timeout else {
|
|
@@ -719,7 +730,8 @@ mod tests {
|
|
|
719
730
|
};
|
|
720
731
|
let data1 = ev1
|
|
721
732
|
.borrow()
|
|
722
|
-
.
|
|
733
|
+
.strings
|
|
734
|
+
.get("data")
|
|
723
735
|
.map(|v| v.to_display_string())
|
|
724
736
|
.unwrap_or_default();
|
|
725
737
|
assert!(
|
|
@@ -734,7 +746,8 @@ mod tests {
|
|
|
734
746
|
};
|
|
735
747
|
let data2 = ev2
|
|
736
748
|
.borrow()
|
|
737
|
-
.
|
|
749
|
+
.strings
|
|
750
|
+
.get("data")
|
|
738
751
|
.map(|v| v.to_display_string())
|
|
739
752
|
.unwrap_or_default();
|
|
740
753
|
assert!(
|
|
@@ -52,11 +52,15 @@ fn fetch_readable_stream_read_chunks() {
|
|
|
52
52
|
_ => panic!("expected object response, got {:?}", resp),
|
|
53
53
|
};
|
|
54
54
|
assert!(obj
|
|
55
|
+
.strings
|
|
55
56
|
.get(&std::sync::Arc::from("ok"))
|
|
56
57
|
.map(|v| matches!(v, Value::Bool(true)))
|
|
57
58
|
.unwrap_or(false));
|
|
58
59
|
|
|
59
|
-
let body = obj
|
|
60
|
+
let body = obj
|
|
61
|
+
.strings
|
|
62
|
+
.get(&std::sync::Arc::from("body"))
|
|
63
|
+
.expect("body");
|
|
60
64
|
let stream = match body {
|
|
61
65
|
Value::Opaque(s) => s.as_ref(),
|
|
62
66
|
_ => panic!("expected ReadableStream opaque"),
|
|
@@ -74,14 +78,12 @@ fn fetch_readable_stream_read_chunks() {
|
|
|
74
78
|
let (done, chunk_bytes) = match chunk {
|
|
75
79
|
Value::Object(o) => {
|
|
76
80
|
let m = o.borrow();
|
|
77
|
-
let done = m
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
_ => None,
|
|
82
|
-
})
|
|
83
|
-
.unwrap_or(true);
|
|
81
|
+
let done = match m.strings.get(&std::sync::Arc::from("done")) {
|
|
82
|
+
Some(Value::Bool(b)) => *b,
|
|
83
|
+
_ => true,
|
|
84
|
+
};
|
|
84
85
|
let bytes = m
|
|
86
|
+
.strings
|
|
85
87
|
.get(&std::sync::Arc::from("value"))
|
|
86
88
|
.map(|v| byte_array_to_vec(v))
|
|
87
89
|
.unwrap_or_default();
|
|
@@ -344,7 +344,9 @@ fn collect_fun_decl_names_expr(expr: &Expr, names: &mut HashSet<String>) {
|
|
|
344
344
|
| Expr::PrefixInc { .. }
|
|
345
345
|
| Expr::PostfixDec { .. }
|
|
346
346
|
| Expr::PrefixDec { .. } => {}
|
|
347
|
-
Expr::JsxElement {
|
|
347
|
+
Expr::JsxElement {
|
|
348
|
+
props, children, ..
|
|
349
|
+
} => {
|
|
348
350
|
for p in props {
|
|
349
351
|
match p {
|
|
350
352
|
JsxProp::Attr { value, .. } => {
|
|
@@ -466,14 +468,14 @@ where
|
|
|
466
468
|
JsxProp::Spread(e) => {
|
|
467
469
|
let val = emit_expr(e)?;
|
|
468
470
|
parts.push(format!(
|
|
469
|
-
"if let Value::Object(ref _spread) = {} {{ for (k, v) in _spread.borrow().iter() {{ _obj.insert(Arc::clone(k), v.clone()); }} }}",
|
|
471
|
+
"if let Value::Object(ref _spread) = {} {{ for (k, v) in _spread.borrow().strings.iter() {{ _obj.insert(Arc::clone(k), v.clone()); }} }}",
|
|
470
472
|
val
|
|
471
473
|
));
|
|
472
474
|
}
|
|
473
475
|
}
|
|
474
476
|
}
|
|
475
477
|
Ok(format!(
|
|
476
|
-
"{{ let mut _obj: ObjectMap = ObjectMap::default(); {} Value::
|
|
478
|
+
"{{ let mut _obj: ObjectMap = ObjectMap::default(); {} Value::object(_obj) }}",
|
|
477
479
|
parts.join(" ")
|
|
478
480
|
))
|
|
479
481
|
} else {
|
|
@@ -495,7 +497,7 @@ where
|
|
|
495
497
|
}
|
|
496
498
|
}
|
|
497
499
|
Ok(format!(
|
|
498
|
-
"Value::
|
|
500
|
+
"Value::object(ObjectMap::from([{}]))",
|
|
499
501
|
kv.join(", ")
|
|
500
502
|
))
|
|
501
503
|
}
|
|
@@ -14,6 +14,6 @@ pub use runtime::{
|
|
|
14
14
|
alloc_root_id, current_root_id, drop_host_for_root, fragment_value, install_host_for_root,
|
|
15
15
|
install_thread_local_host, native_create_root, native_use_effect, native_use_memo,
|
|
16
16
|
native_use_state, run_with_current_root, ui_h, ui_text, unregister_root,
|
|
17
|
-
unregister_root_hooks_and_effects, with_host_for_root, with_thread_local_host, HeadlessHost,
|
|
18
|
-
FRAGMENT_SENTINEL, LEGACY_ROOT_ID,
|
|
17
|
+
unregister_root_hooks_and_effects, with_host_for_root, with_thread_local_host, HeadlessHost,
|
|
18
|
+
Host, RootId, FRAGMENT_SENTINEL, LEGACY_ROOT_ID,
|
|
19
19
|
};
|
|
@@ -189,20 +189,14 @@ fn memo_dep_eq(a: &Value, b: &Value) -> bool {
|
|
|
189
189
|
if ab.len() != bb.len() {
|
|
190
190
|
return false;
|
|
191
191
|
}
|
|
192
|
-
ab.iter()
|
|
193
|
-
.zip(bb.iter())
|
|
194
|
-
.all(|(x, y)| memo_dep_eq(x, y))
|
|
192
|
+
ab.iter().zip(bb.iter()).all(|(x, y)| memo_dep_eq(x, y))
|
|
195
193
|
}
|
|
196
194
|
_ => false,
|
|
197
195
|
}
|
|
198
196
|
}
|
|
199
197
|
|
|
200
198
|
fn memo_deps_unchanged(prev: &[Value], next: &[Value]) -> bool {
|
|
201
|
-
prev.len() == next.len()
|
|
202
|
-
&& prev
|
|
203
|
-
.iter()
|
|
204
|
-
.zip(next.iter())
|
|
205
|
-
.all(|(a, b)| memo_dep_eq(a, b))
|
|
199
|
+
prev.len() == next.len() && prev.iter().zip(next.iter()).all(|(a, b)| memo_dep_eq(a, b))
|
|
206
200
|
}
|
|
207
201
|
|
|
208
202
|
fn root_id_for_hooks() -> RootId {
|
|
@@ -338,11 +332,7 @@ fn flush_pending_effects(root_id: RootId) {
|
|
|
338
332
|
.map(|st| std::mem::take(&mut *st.pending_effects.borrow_mut()))
|
|
339
333
|
.unwrap_or_default()
|
|
340
334
|
});
|
|
341
|
-
let cells_rc = HOOKS.with(|h|
|
|
342
|
-
h.borrow()
|
|
343
|
-
.get(&root_id)
|
|
344
|
-
.map(|st| st.effect_cells.clone())
|
|
345
|
-
});
|
|
335
|
+
let cells_rc = HOOKS.with(|h| h.borrow().get(&root_id).map(|st| st.effect_cells.clone()));
|
|
346
336
|
let Some(cells_rc) = cells_rc else {
|
|
347
337
|
return;
|
|
348
338
|
};
|
|
@@ -405,10 +395,10 @@ pub fn native_create_root(args: &[Value]) -> Value {
|
|
|
405
395
|
drain_flush_queue();
|
|
406
396
|
Value::Null
|
|
407
397
|
});
|
|
408
|
-
Value::
|
|
398
|
+
Value::object(ObjectMap::from([(
|
|
409
399
|
std::sync::Arc::from("render"),
|
|
410
400
|
render_fn,
|
|
411
|
-
)]))
|
|
401
|
+
)]))
|
|
412
402
|
}
|
|
413
403
|
|
|
414
404
|
/// Request a re-render (coalesced; safe if called during flush).
|
|
@@ -3,19 +3,20 @@
|
|
|
3
3
|
mod hooks;
|
|
4
4
|
|
|
5
5
|
use std::cell::RefCell;
|
|
6
|
-
use std::rc::Rc;
|
|
7
6
|
use std::sync::Arc;
|
|
8
7
|
|
|
9
8
|
pub use hooks::{
|
|
10
9
|
alloc_root_id, current_root_id, drop_host_for_root, install_host_for_root, native_create_root,
|
|
11
10
|
native_use_effect, native_use_memo, native_use_state, run_with_current_root, schedule_flush,
|
|
12
|
-
unregister_root, unregister_root_hooks_and_effects, with_host_for_root, HookState,
|
|
13
|
-
LEGACY_ROOT_ID,
|
|
11
|
+
unregister_root, unregister_root_hooks_and_effects, with_host_for_root, HookState, RootId,
|
|
12
|
+
LEGACY_ROOT_ID,
|
|
14
13
|
};
|
|
15
14
|
|
|
16
15
|
use tishlang_core::{ObjectMap, Value, VmRef};
|
|
17
16
|
|
|
18
|
-
/// Sentinel string for `Fragment` (native).
|
|
17
|
+
/// Sentinel string for `Fragment` (native). Full runtimes may also use `Symbol.for("tish.fragment")`
|
|
18
|
+
/// when the global `Symbol` is present; [`is_fragment_tag`] accepts both that registry symbol and
|
|
19
|
+
/// this string for compatibility.
|
|
19
20
|
pub const FRAGMENT_SENTINEL: &str = "__tish_ui_Fragment__";
|
|
20
21
|
|
|
21
22
|
/// `Fragment` marker value for `h(Fragment, null, children)`.
|
|
@@ -25,7 +26,11 @@ pub fn fragment_value() -> Value {
|
|
|
25
26
|
|
|
26
27
|
/// Returns true if `tag` refers to [`fragment_value`].
|
|
27
28
|
pub fn is_fragment_tag(tag: &Value) -> bool {
|
|
28
|
-
|
|
29
|
+
match tag {
|
|
30
|
+
Value::String(s) => s.as_ref() == FRAGMENT_SENTINEL,
|
|
31
|
+
Value::Symbol(s) => s.registry_key.as_deref() == Some("tish.fragment"),
|
|
32
|
+
_ => false,
|
|
33
|
+
}
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
/// `text(s)` helper — returns string as `Value::String` for JSX text nodes.
|
|
@@ -49,7 +54,7 @@ pub fn ui_h(args: &[Value]) -> Value {
|
|
|
49
54
|
let mut merged = if matches!(props, Value::Null) {
|
|
50
55
|
ObjectMap::default()
|
|
51
56
|
} else if let Value::Object(obj) = props {
|
|
52
|
-
obj.borrow().clone()
|
|
57
|
+
obj.borrow().strings.clone()
|
|
53
58
|
} else {
|
|
54
59
|
ObjectMap::default()
|
|
55
60
|
};
|
|
@@ -59,7 +64,7 @@ pub fn ui_h(args: &[Value]) -> Value {
|
|
|
59
64
|
Value::Array(VmRef::new(children_vec.clone())),
|
|
60
65
|
);
|
|
61
66
|
}
|
|
62
|
-
return f(&[Value::
|
|
67
|
+
return f(&[Value::object(merged)]);
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
if is_fragment_tag(&tag) {
|
|
@@ -107,24 +112,18 @@ fn vnode_element(tag: Arc<str>, props: Value, children: Vec<Value>) -> Value {
|
|
|
107
112
|
props
|
|
108
113
|
},
|
|
109
114
|
);
|
|
110
|
-
m.insert(
|
|
111
|
-
Arc::from("children"),
|
|
112
|
-
Value::Array(VmRef::new(children)),
|
|
113
|
-
);
|
|
115
|
+
m.insert(Arc::from("children"), Value::Array(VmRef::new(children)));
|
|
114
116
|
m.insert(Arc::from("_el"), Value::Null);
|
|
115
|
-
Value::
|
|
117
|
+
Value::object(m)
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
fn vnode_fragment(children: Vec<Value>) -> Value {
|
|
119
121
|
let mut m = ObjectMap::default();
|
|
120
122
|
m.insert(Arc::from("tag"), fragment_value());
|
|
121
123
|
m.insert(Arc::from("props"), Value::Null);
|
|
122
|
-
m.insert(
|
|
123
|
-
Arc::from("children"),
|
|
124
|
-
Value::Array(VmRef::new(children)),
|
|
125
|
-
);
|
|
124
|
+
m.insert(Arc::from("children"), Value::Array(VmRef::new(children)));
|
|
126
125
|
m.insert(Arc::from("_el"), Value::Null);
|
|
127
|
-
Value::
|
|
126
|
+
Value::object(m)
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
/// Pluggable UI backend (Floem, DOM, SwiftUI, …). Main-thread / single-threaded by default.
|
|
@@ -25,6 +25,8 @@ timers = ["dep:tishlang_runtime"]
|
|
|
25
25
|
# Any HTTP build needs Send-safe values so handlers can be dispatched
|
|
26
26
|
# across worker threads or processes. HTTP implies timers (fetch/Promise often pair with setTimeout).
|
|
27
27
|
http = ["dep:tishlang_runtime", "tishlang_runtime/http", "send-values", "timers"]
|
|
28
|
+
# Promise + `tish:http.await` / `Promise` export without the full HTTP client stack (e.g. wasm32-wasip1).
|
|
29
|
+
promise = ["dep:tishlang_runtime", "tishlang_runtime/promise", "send-values"]
|
|
28
30
|
ws = ["dep:tishlang_runtime", "tishlang_runtime/ws"]
|
|
29
31
|
|
|
30
32
|
[dependencies]
|