@tishlang/tish 1.0.28 → 1.0.33

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 (65) hide show
  1. package/Cargo.toml +1 -0
  2. package/crates/js_to_tish/src/transform/expr.rs +15 -6
  3. package/crates/tish/Cargo.toml +1 -1
  4. package/crates/tish/src/main.rs +8 -55
  5. package/crates/tish/tests/integration_test.rs +4 -3
  6. package/crates/tish_ast/src/ast.rs +65 -2
  7. package/crates/tish_build_utils/src/lib.rs +10 -2
  8. package/crates/tish_builtins/src/construct.rs +177 -0
  9. package/crates/tish_builtins/src/globals.rs +3 -5
  10. package/crates/tish_builtins/src/helpers.rs +2 -3
  11. package/crates/tish_builtins/src/lib.rs +1 -0
  12. package/crates/tish_builtins/src/object.rs +3 -4
  13. package/crates/tish_bytecode/src/compiler.rs +85 -11
  14. package/crates/tish_bytecode/src/opcode.rs +7 -3
  15. package/crates/tish_compile/Cargo.toml +1 -0
  16. package/crates/tish_compile/src/codegen.rs +233 -71
  17. package/crates/tish_compile/src/lib.rs +35 -0
  18. package/crates/tish_compile_js/Cargo.toml +1 -1
  19. package/crates/tish_compile_js/src/codegen.rs +43 -147
  20. package/crates/tish_compile_js/src/lib.rs +4 -7
  21. package/crates/tish_compile_js/src/tests_jsx.rs +89 -19
  22. package/crates/tish_compiler_wasm/src/lib.rs +2 -3
  23. package/crates/tish_core/Cargo.toml +4 -0
  24. package/crates/tish_core/src/console_style.rs +7 -1
  25. package/crates/tish_core/src/json.rs +1 -2
  26. package/crates/tish_core/src/macros.rs +2 -3
  27. package/crates/tish_core/src/value.rs +10 -5
  28. package/crates/tish_eval/Cargo.toml +2 -0
  29. package/crates/tish_eval/src/eval.rs +149 -72
  30. package/crates/tish_eval/src/http.rs +3 -4
  31. package/crates/tish_eval/src/regex.rs +3 -2
  32. package/crates/tish_eval/src/value.rs +11 -13
  33. package/crates/tish_eval/src/value_convert.rs +4 -8
  34. package/crates/tish_fmt/src/lib.rs +49 -10
  35. package/crates/tish_jsx_web/Cargo.toml +1 -1
  36. package/crates/tish_jsx_web/README.md +3 -16
  37. package/crates/tish_jsx_web/src/lib.rs +2 -157
  38. package/crates/tish_lexer/src/token.rs +2 -0
  39. package/crates/tish_lint/src/lib.rs +9 -0
  40. package/crates/tish_lsp/README.md +1 -1
  41. package/crates/tish_native/src/build.rs +16 -2
  42. package/crates/tish_opt/src/lib.rs +15 -0
  43. package/crates/tish_parser/src/lib.rs +101 -1
  44. package/crates/tish_parser/src/parser.rs +161 -50
  45. package/crates/tish_runtime/src/http.rs +4 -5
  46. package/crates/tish_runtime/src/http_fetch.rs +9 -10
  47. package/crates/tish_runtime/src/lib.rs +9 -2
  48. package/crates/tish_runtime/src/promise.rs +2 -3
  49. package/crates/tish_runtime/src/promise_io.rs +2 -3
  50. package/crates/tish_runtime/src/ws.rs +7 -7
  51. package/crates/tish_ui/Cargo.toml +17 -0
  52. package/crates/tish_ui/src/jsx.rs +390 -0
  53. package/crates/tish_ui/src/lib.rs +16 -0
  54. package/crates/tish_ui/src/runtime/hooks.rs +122 -0
  55. package/crates/tish_ui/src/runtime/mod.rs +173 -0
  56. package/crates/tish_vm/src/vm.rs +121 -27
  57. package/justfile +3 -3
  58. package/package.json +1 -1
  59. package/platform/darwin-arm64/tish +0 -0
  60. package/platform/darwin-x64/tish +0 -0
  61. package/platform/linux-arm64/tish +0 -0
  62. package/platform/linux-x64/tish +0 -0
  63. package/platform/win32-x64/tish.exe +0 -0
  64. package/crates/tish_compile_js/src/js_intrinsics.rs +0 -82
  65. package/crates/tish_jsx_web/vendor/Lattish.tish +0 -362
@@ -53,9 +53,9 @@ macro_rules! binary_multi_op {
53
53
 
54
54
  use tishlang_ast::{
55
55
  ArrowBody, ArrayElement, BinOp, CallArg, CompoundOp, DestructElement, DestructPattern,
56
- DestructProp, ExportDeclaration, Expr, ImportSpecifier, JsxAttrValue, JsxChild, JsxProp,
57
- Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Span, Statement, TypeAnnotation,
58
- TypedParam, UnaryOp,
56
+ DestructProp, ExportDeclaration, Expr, FunParam, ImportSpecifier, JsxAttrValue, JsxChild,
57
+ JsxProp, Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Span, Statement,
58
+ TypeAnnotation, TypedParam, UnaryOp,
59
59
  };
60
60
  use tishlang_lexer::{Token, TokenKind};
61
61
 
@@ -363,6 +363,55 @@ impl<'a> Parser<'a> {
363
363
  self.expect(TokenKind::RBrace)?;
364
364
  Ok(DestructPattern::Object(props))
365
365
  }
366
+
367
+ /// One formal parameter: `name`, `name: T`, `name = expr`, or a destructuring pattern.
368
+ fn parse_fun_param(&mut self) -> Result<FunParam, String> {
369
+ if matches!(
370
+ self.peek_kind(),
371
+ Some(TokenKind::LBracket | TokenKind::LBrace)
372
+ ) {
373
+ let pattern = self.parse_destruct_pattern()?;
374
+ let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
375
+ self.advance();
376
+ Some(self.parse_type_annotation()?)
377
+ } else {
378
+ None
379
+ };
380
+ let default = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
381
+ self.advance();
382
+ Some(self.parse_expr()?)
383
+ } else {
384
+ None
385
+ };
386
+ return Ok(FunParam::Destructure {
387
+ pattern,
388
+ type_ann,
389
+ default,
390
+ });
391
+ }
392
+ let param_name = self
393
+ .expect(TokenKind::Ident)?
394
+ .literal
395
+ .clone()
396
+ .ok_or("Expected param name")?;
397
+ let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
398
+ self.advance();
399
+ Some(self.parse_type_annotation()?)
400
+ } else {
401
+ None
402
+ };
403
+ let default = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
404
+ self.advance();
405
+ Some(self.parse_expr()?)
406
+ } else {
407
+ None
408
+ };
409
+ Ok(FunParam::Simple(TypedParam {
410
+ name: param_name,
411
+ type_ann,
412
+ default,
413
+ }))
414
+ }
366
415
 
367
416
  /// Parse a type annotation (number, string, T[], {a: T}, etc.)
368
417
  fn parse_type_annotation(&mut self) -> Result<TypeAnnotation, String> {
@@ -480,26 +529,7 @@ impl<'a> Parser<'a> {
480
529
  }
481
530
  break;
482
531
  }
483
- let param_name = self
484
- .expect(TokenKind::Ident)?
485
- .literal
486
- .clone()
487
- .ok_or("Expected param name")?;
488
- // Optional type annotation
489
- let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
490
- self.advance();
491
- Some(self.parse_type_annotation()?)
492
- } else {
493
- None
494
- };
495
- // Optional default value
496
- let default = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
497
- self.advance();
498
- Some(self.parse_expr()?)
499
- } else {
500
- None
501
- };
502
- params.push(TypedParam { name: param_name, type_ann, default });
532
+ params.push(self.parse_fun_param()?);
503
533
  if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
504
534
  self.expect(TokenKind::Comma)?;
505
535
  }
@@ -1117,8 +1147,97 @@ impl<'a> Parser<'a> {
1117
1147
  })
1118
1148
  }
1119
1149
 
1120
- fn parse_postfix(&mut self) -> Result<Expr, String> {
1150
+ /// Member chain (`.`, `?.`, `[]`) without consuming a call `(...)`.
1151
+ fn parse_member_expression_no_call(&mut self) -> Result<Expr, String> {
1121
1152
  let mut expr = self.parse_primary()?;
1153
+ while let Some(kind) = self.peek_kind() {
1154
+ match kind {
1155
+ TokenKind::Dot | TokenKind::OptionalChain => {
1156
+ let optional = kind == TokenKind::OptionalChain;
1157
+ self.advance();
1158
+ let prop = self
1159
+ .expect(TokenKind::Ident)?
1160
+ .literal
1161
+ .clone()
1162
+ .ok_or("Expected property name")?;
1163
+ let start = expr.span().start;
1164
+ let end = self.peek().map(|x| x.span.start).unwrap_or(start);
1165
+ expr = Expr::Member {
1166
+ object: Box::new(expr),
1167
+ prop: MemberProp::Name(prop),
1168
+ optional,
1169
+ span: Span { start, end },
1170
+ };
1171
+ }
1172
+ TokenKind::LBracket => {
1173
+ self.advance();
1174
+ let index = self.parse_expr()?;
1175
+ self.expect(TokenKind::RBracket)?;
1176
+ let start = expr.span().start;
1177
+ let end = self.peek().map(|x| x.span.start).unwrap_or(start);
1178
+ expr = Expr::Index {
1179
+ object: Box::new(expr),
1180
+ index: Box::new(index),
1181
+ optional: false,
1182
+ span: Span { start, end },
1183
+ };
1184
+ }
1185
+ _ => break,
1186
+ }
1187
+ }
1188
+ Ok(expr)
1189
+ }
1190
+
1191
+ /// ECMAScript `NewExpression`: `new` chains, then member expression without call, optional `(...)`.
1192
+ fn parse_new_expression(&mut self) -> Result<Expr, String> {
1193
+ if matches!(self.peek_kind(), Some(TokenKind::New)) {
1194
+ let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
1195
+ self.advance();
1196
+ let callee = Box::new(self.parse_new_expression()?);
1197
+ let args = if matches!(self.peek_kind(), Some(TokenKind::LParen)) {
1198
+ self.advance();
1199
+ let mut args = Vec::new();
1200
+ while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
1201
+ if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
1202
+ self.advance();
1203
+ let arg_expr = self.parse_expr()?;
1204
+ args.push(CallArg::Spread(arg_expr));
1205
+ } else {
1206
+ let arg_expr = self.parse_expr()?;
1207
+ args.push(CallArg::Expr(arg_expr));
1208
+ }
1209
+ if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
1210
+ self.expect(TokenKind::Comma)?;
1211
+ }
1212
+ }
1213
+ self.expect(TokenKind::RParen)?;
1214
+ args
1215
+ } else {
1216
+ Vec::new()
1217
+ };
1218
+ let end = self
1219
+ .peek()
1220
+ .map(|x| x.span.start)
1221
+ .unwrap_or(callee.as_ref().span().end);
1222
+ Ok(Expr::New {
1223
+ callee,
1224
+ args,
1225
+ span: Span {
1226
+ start: span_start,
1227
+ end,
1228
+ },
1229
+ })
1230
+ } else {
1231
+ self.parse_member_expression_no_call()
1232
+ }
1233
+ }
1234
+
1235
+ fn parse_postfix(&mut self) -> Result<Expr, String> {
1236
+ let mut expr = if matches!(self.peek_kind(), Some(TokenKind::New)) {
1237
+ self.parse_new_expression()?
1238
+ } else {
1239
+ self.parse_primary()?
1240
+ };
1122
1241
  while let Some(kind) = self.peek_kind() {
1123
1242
  match kind {
1124
1243
  TokenKind::LParen => {
@@ -1243,7 +1362,11 @@ impl<'a> Parser<'a> {
1243
1362
  let body = self.parse_arrow_body()?;
1244
1363
  let end = self.previous_span_end();
1245
1364
  return Ok(Expr::ArrowFunction {
1246
- params: vec![TypedParam { name: name.clone(), type_ann: None, default: None }],
1365
+ params: vec![FunParam::Simple(TypedParam {
1366
+ name: name.clone(),
1367
+ type_ann: None,
1368
+ default: None,
1369
+ })],
1247
1370
  body,
1248
1371
  span: Span { start: span.start, end },
1249
1372
  });
@@ -1418,39 +1541,26 @@ impl<'a> Parser<'a> {
1418
1541
  is_arrow = true;
1419
1542
  }
1420
1543
  } else {
1421
- // Try to parse params: (x, y, z) or (x: Type, y: Type)
1544
+ // Try to parse params: (x, y), ({ a }), ([a, b]), with optional types/defaults
1545
+ let mut params_ok = true;
1422
1546
  loop {
1423
- if !matches!(self.peek_kind(), Some(TokenKind::Ident)) {
1424
- break; // Not a valid arrow function param list
1547
+ if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
1548
+ break;
1549
+ }
1550
+ match self.parse_fun_param() {
1551
+ Ok(param) => params.push(param),
1552
+ Err(_) => {
1553
+ params_ok = false;
1554
+ break;
1555
+ }
1425
1556
  }
1426
- let name = self.advance().unwrap().literal.clone().ok_or("Expected param name")?;
1427
-
1428
- // Optional type annotation
1429
- let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
1430
- self.advance();
1431
- Some(self.parse_type_annotation()?)
1432
- } else {
1433
- None
1434
- };
1435
-
1436
- // Optional default value
1437
- let default = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
1438
- self.advance();
1439
- Some(self.parse_expr()?)
1440
- } else {
1441
- None
1442
- };
1443
-
1444
- params.push(TypedParam { name, type_ann, default });
1445
-
1446
1557
  if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
1447
1558
  self.advance();
1448
1559
  } else {
1449
1560
  break;
1450
1561
  }
1451
1562
  }
1452
-
1453
- if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
1563
+ if params_ok && matches!(self.peek_kind(), Some(TokenKind::RParen)) {
1454
1564
  self.advance(); // consume )
1455
1565
  if matches!(self.peek_kind(), Some(TokenKind::Arrow)) {
1456
1566
  self.advance(); // consume =>
@@ -1787,6 +1897,7 @@ impl ExprSpan for Expr {
1787
1897
  Expr::Binary { span, .. } => *span,
1788
1898
  Expr::Unary { span, .. } => *span,
1789
1899
  Expr::Call { span, .. } => *span,
1900
+ Expr::New { span, .. } => *span,
1790
1901
  Expr::Member { span, .. } => *span,
1791
1902
  Expr::Index { span, .. } => *span,
1792
1903
  Expr::Conditional { span, .. } => *span,
@@ -1,12 +1,11 @@
1
1
  //! HTTP server + shared request parsing. Client `fetch` lives in `http_fetch.rs`.
2
2
 
3
3
  use std::cell::RefCell;
4
- use std::collections::HashMap;
5
4
  use std::fs::File;
6
5
  use std::io::Write;
7
6
  use std::rc::Rc;
8
7
  use std::sync::Arc;
9
- use tishlang_core::Value;
8
+ use tishlang_core::{ObjectMap, Value};
10
9
  use tokio::runtime::Runtime;
11
10
 
12
11
  thread_local! {
@@ -80,7 +79,7 @@ pub(crate) fn extract_body(options: Option<&Value>) -> Option<String> {
80
79
  }
81
80
 
82
81
  pub(crate) fn build_error_response(error: &str) -> Value {
83
- let mut obj: HashMap<Arc<str>, Value> = HashMap::with_capacity(2);
82
+ let mut obj: ObjectMap = ObjectMap::with_capacity(2);
84
83
  obj.insert(Arc::from("error"), Value::String(error.into()));
85
84
  obj.insert(Arc::from("ok"), Value::Bool(false));
86
85
  Value::Object(Rc::new(RefCell::new(obj)))
@@ -147,7 +146,7 @@ pub fn create_server(port: u16) -> Result<tiny_http::Server, String> {
147
146
  }
148
147
 
149
148
  pub fn request_to_value(request: &mut tiny_http::Request) -> Value {
150
- let mut obj: HashMap<Arc<str>, Value> = HashMap::with_capacity(6);
149
+ let mut obj: ObjectMap = ObjectMap::with_capacity(6);
151
150
 
152
151
  obj.insert(
153
152
  Arc::from("method"),
@@ -164,7 +163,7 @@ pub fn request_to_value(request: &mut tiny_http::Request) -> Value {
164
163
  let query_string = request.url().split('?').nth(1).unwrap_or("");
165
164
  obj.insert(Arc::from("query"), Value::String(query_string.into()));
166
165
 
167
- let mut headers_obj: HashMap<Arc<str>, Value> = HashMap::with_capacity(request.headers().len());
166
+ let mut headers_obj: ObjectMap = ObjectMap::with_capacity(request.headers().len());
168
167
  for header in request.headers() {
169
168
  headers_obj.insert(
170
169
  Arc::from(header.field.as_str().as_str()),
@@ -1,7 +1,6 @@
1
1
  //! Web Fetch–aligned Response, ReadableStream, reader.read(), text()/json().
2
2
 
3
3
  use std::cell::RefCell;
4
- use std::collections::HashMap;
5
4
  use std::pin::Pin;
6
5
  use std::rc::Rc;
7
6
  use std::sync::{Arc, Mutex};
@@ -9,7 +8,7 @@ use std::sync::{Arc, Mutex};
9
8
  use bytes::Bytes;
10
9
  use futures::Stream;
11
10
  use futures::StreamExt;
12
- use tishlang_core::{NativeFn, TishOpaque, TishPromise, Value};
11
+ use tishlang_core::{NativeFn, ObjectMap, TishOpaque, TishPromise, Value};
13
12
 
14
13
  use crate::http::{build_error_response, extract_body, extract_headers, extract_method};
15
14
 
@@ -77,14 +76,14 @@ impl TishPromise for ReadChunkPromise {
77
76
  let r = crate::http::block_on_http(rx);
78
77
  match r {
79
78
  Ok(Ok(ReadChunk::Done)) => {
80
- let mut o = HashMap::new();
79
+ let mut o = ObjectMap::default();
81
80
  o.insert(Arc::from("done"), Value::Bool(true));
82
81
  o.insert(Arc::from("value"), Value::Null);
83
82
  Ok(Value::Object(Rc::new(RefCell::new(o))))
84
83
  }
85
84
  Ok(Ok(ReadChunk::Bytes(b))) => {
86
85
  let arr: Vec<Value> = b.iter().map(|u| Value::Number(*u as f64)).collect();
87
- let mut o = HashMap::new();
86
+ let mut o = ObjectMap::default();
88
87
  o.insert(Arc::from("done"), Value::Bool(false));
89
88
  o.insert(
90
89
  Arc::from("value"),
@@ -93,7 +92,7 @@ impl TishPromise for ReadChunkPromise {
93
92
  Ok(Value::Object(Rc::new(RefCell::new(o))))
94
93
  }
95
94
  Ok(Err(e)) => Err({
96
- let mut obj = HashMap::new();
95
+ let mut obj = ObjectMap::default();
97
96
  obj.insert(Arc::from("error"), Value::String(e.into()));
98
97
  Value::Object(Rc::new(RefCell::new(obj)))
99
98
  }),
@@ -118,13 +117,13 @@ impl TishPromise for JsonTextPromise {
118
117
  Ok(Ok(s)) => match tishlang_core::json_parse(&s) {
119
118
  Ok(v) => Ok(v),
120
119
  Err(e) => Err({
121
- let mut obj = HashMap::new();
120
+ let mut obj = ObjectMap::default();
122
121
  obj.insert(Arc::from("error"), Value::String(e.into()));
123
122
  Value::Object(Rc::new(RefCell::new(obj)))
124
123
  }),
125
124
  },
126
125
  Ok(Err(e)) => Err({
127
- let mut obj = HashMap::new();
126
+ let mut obj = ObjectMap::default();
128
127
  obj.insert(Arc::from("error"), Value::String(e.into()));
129
128
  Value::Object(Rc::new(RefCell::new(obj)))
130
129
  }),
@@ -226,7 +225,7 @@ impl TishOpaque for HttpReadableStream {
226
225
  }))
227
226
  }
228
227
  Err(e) => {
229
- let mut m = HashMap::new();
228
+ let mut m = ObjectMap::default();
230
229
  m.insert(Arc::from("error"), Value::String(e.into()));
231
230
  Value::Object(Rc::new(RefCell::new(m)))
232
231
  }
@@ -283,7 +282,7 @@ impl TishOpaque for HttpStreamReader {
283
282
  }
284
283
 
285
284
  fn headers_to_value(headers: &reqwest::header::HeaderMap) -> Value {
286
- let mut headers_obj: HashMap<Arc<str>, Value> = HashMap::with_capacity(headers.len());
285
+ let mut headers_obj: ObjectMap = ObjectMap::with_capacity(headers.len());
287
286
  for (key, value) in headers.iter() {
288
287
  if let Ok(v) = value.to_str() {
289
288
  headers_obj.insert(Arc::from(key.as_str()), Value::String(v.into()));
@@ -327,7 +326,7 @@ pub fn response_value_from_reqwest(response: reqwest::Response) -> Value {
327
326
  rx: Mutex::new(Some(rx)),
328
327
  }))
329
328
  });
330
- let mut obj: HashMap<Arc<str>, Value> = HashMap::new();
329
+ let mut obj: ObjectMap = ObjectMap::default();
331
330
  obj.insert(Arc::from("status"), Value::Number(status));
332
331
  obj.insert(Arc::from("ok"), Value::Bool(ok));
333
332
  obj.insert(Arc::from("headers"), headers_val);
@@ -13,8 +13,15 @@ use tishlang_builtins::helpers::extract_num;
13
13
  #[cfg(feature = "fs")]
14
14
  use tishlang_builtins::helpers::make_error_value;
15
15
 
16
+ pub use tishlang_core::ObjectMap;
16
17
  pub use tishlang_core::Value;
17
18
 
19
+ pub use tishlang_builtins::construct::{
20
+ audio_context_constructor_value as tish_audio_context_constructor,
21
+ construct as tish_construct,
22
+ uint8_array_constructor_value as tish_uint8_array_constructor,
23
+ };
24
+
18
25
  // Re-export array methods from tishlang_builtins
19
26
  pub use tishlang_builtins::array::{
20
27
  push as array_push_impl,
@@ -754,7 +761,7 @@ pub fn regexp_exec(re: &Value, input: &Value) -> Value {
754
761
 
755
762
  #[cfg(feature = "regex")]
756
763
  fn regexp_exec_impl(re: &mut tishlang_core::TishRegExp, input: &str) -> Value {
757
- use std::collections::HashMap;
764
+ use tishlang_core::ObjectMap;
758
765
 
759
766
  let start = if re.flags.global || re.flags.sticky {
760
767
  re.last_index
@@ -785,7 +792,7 @@ fn regexp_exec_impl(re: &mut tishlang_core::TishRegExp, input: &str) -> Value {
785
792
  let match_byte_start = byte_start + full_match.start();
786
793
  let match_char_index = input[..match_byte_start].chars().count();
787
794
 
788
- let mut obj: HashMap<std::sync::Arc<str>, Value> = HashMap::new();
795
+ let mut obj: ObjectMap = ObjectMap::default();
789
796
  obj.insert(Arc::from("0"), Value::String(full_match.as_str().into()));
790
797
  for i in 1..caps.len() {
791
798
  let val = match caps.get(i) {
@@ -1,10 +1,9 @@
1
1
  //! Promise static methods for compiled Tish (resolve, reject, all, race).
2
2
 
3
3
  use std::cell::RefCell;
4
- use std::collections::HashMap;
5
4
  use std::rc::Rc;
6
5
  use std::sync::Arc;
7
- use tishlang_core::Value;
6
+ use tishlang_core::{ObjectMap, Value};
8
7
 
9
8
  /// Promise.resolve(value) - returns the value (immediate resolve).
10
9
  pub fn promise_resolve(args: &[Value]) -> Value {
@@ -56,7 +55,7 @@ pub fn promise_race(args: &[Value]) -> Value {
56
55
 
57
56
  /// Build the Promise object with resolve, reject, all, race static methods.
58
57
  pub fn promise_object() -> Value {
59
- let mut map: HashMap<Arc<str>, Value> = HashMap::new();
58
+ let mut map: ObjectMap = ObjectMap::default();
60
59
  map.insert(
61
60
  Arc::from("resolve"),
62
61
  Value::Function(Rc::new(|args: &[Value]| promise_resolve(args))),
@@ -1,14 +1,13 @@
1
1
  //! Promises carrying only Send payloads (string results for text(), etc.).
2
2
 
3
3
  use std::cell::RefCell;
4
- use std::collections::HashMap;
5
4
  use std::rc::Rc;
6
5
  use std::sync::{Arc, Mutex};
7
- use tishlang_core::{Value, TishPromise};
6
+ use tishlang_core::{ObjectMap, TishPromise, Value};
8
7
  use tokio::sync::oneshot;
9
8
 
10
9
  fn error_value(msg: String) -> Value {
11
- let mut obj: HashMap<Arc<str>, Value> = HashMap::with_capacity(2);
10
+ let mut obj: ObjectMap = ObjectMap::with_capacity(2);
12
11
  obj.insert(Arc::from("error"), Value::String(msg.into()));
13
12
  obj.insert(Arc::from("ok"), Value::Bool(false));
14
13
  Value::Object(Rc::new(RefCell::new(obj)))
@@ -15,7 +15,7 @@ use std::time::{Duration, Instant};
15
15
 
16
16
  use futures_util::{SinkExt, StreamExt};
17
17
  use lazy_static::lazy_static;
18
- use tishlang_core::Value;
18
+ use tishlang_core::{ObjectMap, Value};
19
19
  use tokio::sync::mpsc as tokio_mpsc;
20
20
  use tokio::runtime::Runtime;
21
21
 
@@ -194,7 +194,7 @@ pub fn ws_broadcast_native(args: &[Value]) -> Value {
194
194
 
195
195
  /// Build connection object: { _id, send, close, readyState, receive }. JS-like.
196
196
  fn conn_object(id: u32) -> Value {
197
- let mut obj: HashMap<Arc<str>, Value> = HashMap::new();
197
+ let mut obj: ObjectMap = ObjectMap::default();
198
198
  obj.insert(Arc::from("_id"), Value::Number(id as f64));
199
199
  obj.insert(Arc::from("readyState"), Value::Number(1.0)); // OPEN
200
200
  obj.insert(
@@ -216,7 +216,7 @@ fn conn_object(id: u32) -> Value {
216
216
  Value::Function(Rc::new(move |_args: &[Value]| {
217
217
  match conn_receive(id) {
218
218
  Some(s) => {
219
- let mut ev: HashMap<Arc<str>, Value> = HashMap::new();
219
+ let mut ev: ObjectMap = ObjectMap::default();
220
220
  ev.insert(Arc::from("data"), Value::String(s.into()));
221
221
  Value::Object(Rc::new(RefCell::new(ev)))
222
222
  }
@@ -237,7 +237,7 @@ fn conn_object(id: u32) -> Value {
237
237
  .unwrap_or(1000);
238
238
  match conn_receive_timeout(id_timeout, timeout_ms) {
239
239
  Some(s) => {
240
- let mut ev: HashMap<Arc<str>, Value> = HashMap::new();
240
+ let mut ev: ObjectMap = ObjectMap::default();
241
241
  ev.insert(Arc::from("data"), Value::String(s.into()));
242
242
  Value::Object(Rc::new(RefCell::new(ev)))
243
243
  }
@@ -529,7 +529,7 @@ pub fn web_socket_server_construct(args: &[Value]) -> Value {
529
529
  ws
530
530
  });
531
531
 
532
- let mut m: HashMap<Arc<str>, Value> = HashMap::new();
532
+ let mut m: ObjectMap = ObjectMap::default();
533
533
  m.insert(Arc::from("_handle"), handle_val);
534
534
  m.insert(Arc::from("_onConnection"), Value::Null);
535
535
  m.insert(Arc::from("clients"), Value::Array(clients));
@@ -549,7 +549,7 @@ mod tests {
549
549
  fn ws_echo_roundtrip() {
550
550
  let port: u16 = 18_742;
551
551
  let opts = {
552
- let mut m: HashMap<Arc<str>, Value> = HashMap::new();
552
+ let mut m: ObjectMap = ObjectMap::default();
553
553
  m.insert(Arc::from("port"), Value::Number(port as f64));
554
554
  Value::Object(Rc::new(RefCell::new(m)))
555
555
  };
@@ -632,7 +632,7 @@ mod tests {
632
632
  fn ws_gateway_agent_flow() {
633
633
  let port: u16 = 18_743;
634
634
  let opts = {
635
- let mut m: HashMap<Arc<str>, Value> = HashMap::new();
635
+ let mut m: ObjectMap = ObjectMap::default();
636
636
  m.insert(Arc::from("port"), Value::Number(port as f64));
637
637
  Value::Object(Rc::new(RefCell::new(m)))
638
638
  };
@@ -0,0 +1,17 @@
1
+ [package]
2
+ name = "tishlang_ui"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ description = "Shared JSX lowering and UI runtime (vnode, hooks, host protocol) for Tish"
6
+
7
+ license-file = { workspace = true }
8
+ repository = { workspace = true }
9
+
10
+ [features]
11
+ default = ["runtime"]
12
+ compiler = ["dep:tishlang_ast"]
13
+ runtime = ["dep:tishlang_core"]
14
+
15
+ [dependencies]
16
+ tishlang_ast = { path = "../tish_ast", version = ">=0.1", optional = true }
17
+ tishlang_core = { path = "../tish_core", version = ">=0.1", optional = true }