@tishlang/tish 2.9.0 → 2.12.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/tish/Cargo.toml +2 -1
- package/crates/tish/src/cli_help.rs +47 -0
- package/crates/tish/src/main.rs +92 -6
- package/crates/tish/tests/integration_test.rs +177 -0
- package/crates/tish_compile/src/codegen.rs +255 -0
- package/crates/tish_compile/tests/perf_codegen_169_173.rs +139 -0
- package/crates/tish_compile_js/src/codegen.rs +161 -3
- package/crates/tish_compile_js/src/lib.rs +7 -3
- package/crates/tish_compile_js/src/tests_jsx.rs +341 -4
- package/crates/tish_ui/src/jsx.rs +250 -0
- 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
|
@@ -541,6 +541,256 @@ pub fn program_contains_jsx(program: &tishlang_ast::Program) -> bool {
|
|
|
541
541
|
program.statements.iter().any(stmt_contains_jsx)
|
|
542
542
|
}
|
|
543
543
|
|
|
544
|
+
/// Whether the program contains any JSX **fragment** (`<>…</>`). Fragments lower to
|
|
545
|
+
/// `h(Fragment, null, …)`, so they require the `Fragment` runtime binding in addition to `h`.
|
|
546
|
+
pub fn program_contains_jsx_fragment(program: &tishlang_ast::Program) -> bool {
|
|
547
|
+
program.statements.iter().any(stmt_contains_jsx_fragment)
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/// Which JSX runtime bindings (`h`, `Fragment`) a module needs auto-imported, given which names are
|
|
551
|
+
/// already in module scope. Any JSX needs `h`; fragments additionally need `Fragment`. A binding is
|
|
552
|
+
/// omitted if it's already provided by a top-level import/declaration (see
|
|
553
|
+
/// [`collect_jsx_runtime_bindings_in_scope`]).
|
|
554
|
+
///
|
|
555
|
+
/// Returns a subset of `["h", "Fragment"]` (in that order), or `[]` when the module has no JSX.
|
|
556
|
+
pub fn jsx_runtime_imports_needed(program: &tishlang_ast::Program) -> Vec<&'static str> {
|
|
557
|
+
if !program_contains_jsx(program) {
|
|
558
|
+
return Vec::new();
|
|
559
|
+
}
|
|
560
|
+
let in_scope = collect_jsx_runtime_bindings_in_scope(program);
|
|
561
|
+
let mut needed = Vec::new();
|
|
562
|
+
if !in_scope.contains("h") {
|
|
563
|
+
needed.push("h");
|
|
564
|
+
}
|
|
565
|
+
if program_contains_jsx_fragment(program) && !in_scope.contains("Fragment") {
|
|
566
|
+
needed.push("Fragment");
|
|
567
|
+
}
|
|
568
|
+
needed
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/// Top-level names that already bind the JSX runtime symbols `h` / `Fragment`, so the auto-import
|
|
572
|
+
/// must not shadow or duplicate them. Covers named imports (local alias), top-level `const`/`let`,
|
|
573
|
+
/// and `fn` declarations, including those wrapped in `export`.
|
|
574
|
+
pub fn collect_jsx_runtime_bindings_in_scope(program: &tishlang_ast::Program) -> HashSet<String> {
|
|
575
|
+
use tishlang_ast::{ExportDeclaration, ImportSpecifier, Statement};
|
|
576
|
+
let mut names = HashSet::new();
|
|
577
|
+
let note = |n: &str, names: &mut HashSet<String>| {
|
|
578
|
+
if n == "h" || n == "Fragment" {
|
|
579
|
+
names.insert(n.to_string());
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
for stmt in &program.statements {
|
|
583
|
+
match stmt {
|
|
584
|
+
Statement::Import { specifiers, .. } => {
|
|
585
|
+
for s in specifiers {
|
|
586
|
+
if let ImportSpecifier::Named { name, alias, .. } = s {
|
|
587
|
+
let local = alias.as_deref().unwrap_or(name.as_ref());
|
|
588
|
+
note(local, &mut names);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
Statement::VarDecl { name, .. } | Statement::FunDecl { name, .. } => {
|
|
593
|
+
note(name.as_ref(), &mut names)
|
|
594
|
+
}
|
|
595
|
+
Statement::Export { declaration, .. } => {
|
|
596
|
+
if let ExportDeclaration::Named(inner) = declaration.as_ref() {
|
|
597
|
+
match inner.as_ref() {
|
|
598
|
+
Statement::VarDecl { name, .. } | Statement::FunDecl { name, .. } => {
|
|
599
|
+
note(name.as_ref(), &mut names)
|
|
600
|
+
}
|
|
601
|
+
_ => {}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
_ => {}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
names
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
fn stmt_contains_jsx_fragment(stmt: &tishlang_ast::Statement) -> bool {
|
|
612
|
+
use tishlang_ast::{ExportDeclaration, Statement};
|
|
613
|
+
match stmt {
|
|
614
|
+
Statement::Block { statements, .. } | Statement::Multi { statements, .. } => {
|
|
615
|
+
statements.iter().any(stmt_contains_jsx_fragment)
|
|
616
|
+
}
|
|
617
|
+
Statement::VarDecl { init, .. } => init.as_ref().is_some_and(expr_contains_jsx_fragment),
|
|
618
|
+
Statement::VarDeclDestructure { init, .. } => expr_contains_jsx_fragment(init),
|
|
619
|
+
Statement::ExprStmt { expr, .. } => expr_contains_jsx_fragment(expr),
|
|
620
|
+
Statement::Return { value, .. } => value.as_ref().is_some_and(expr_contains_jsx_fragment),
|
|
621
|
+
Statement::If {
|
|
622
|
+
cond,
|
|
623
|
+
then_branch,
|
|
624
|
+
else_branch,
|
|
625
|
+
..
|
|
626
|
+
} => {
|
|
627
|
+
expr_contains_jsx_fragment(cond)
|
|
628
|
+
|| stmt_contains_jsx_fragment(then_branch)
|
|
629
|
+
|| else_branch
|
|
630
|
+
.as_ref()
|
|
631
|
+
.is_some_and(|s| stmt_contains_jsx_fragment(s))
|
|
632
|
+
}
|
|
633
|
+
Statement::While { cond, body, .. } | Statement::DoWhile { body, cond, .. } => {
|
|
634
|
+
expr_contains_jsx_fragment(cond) || stmt_contains_jsx_fragment(body)
|
|
635
|
+
}
|
|
636
|
+
Statement::For {
|
|
637
|
+
init,
|
|
638
|
+
cond,
|
|
639
|
+
update,
|
|
640
|
+
body,
|
|
641
|
+
..
|
|
642
|
+
} => {
|
|
643
|
+
init.as_ref().is_some_and(|s| stmt_contains_jsx_fragment(s))
|
|
644
|
+
|| cond.as_ref().is_some_and(expr_contains_jsx_fragment)
|
|
645
|
+
|| update.as_ref().is_some_and(expr_contains_jsx_fragment)
|
|
646
|
+
|| stmt_contains_jsx_fragment(body)
|
|
647
|
+
}
|
|
648
|
+
Statement::ForOf { iterable, body, .. } => {
|
|
649
|
+
expr_contains_jsx_fragment(iterable) || stmt_contains_jsx_fragment(body)
|
|
650
|
+
}
|
|
651
|
+
Statement::Switch {
|
|
652
|
+
expr,
|
|
653
|
+
cases,
|
|
654
|
+
default_body,
|
|
655
|
+
..
|
|
656
|
+
} => {
|
|
657
|
+
expr_contains_jsx_fragment(expr)
|
|
658
|
+
|| cases.iter().any(|(e, ss)| {
|
|
659
|
+
e.as_ref().is_some_and(expr_contains_jsx_fragment)
|
|
660
|
+
|| ss.iter().any(stmt_contains_jsx_fragment)
|
|
661
|
+
})
|
|
662
|
+
|| default_body
|
|
663
|
+
.as_ref()
|
|
664
|
+
.is_some_and(|ss| ss.iter().any(stmt_contains_jsx_fragment))
|
|
665
|
+
}
|
|
666
|
+
Statement::Try {
|
|
667
|
+
body,
|
|
668
|
+
catch_body,
|
|
669
|
+
finally_body,
|
|
670
|
+
..
|
|
671
|
+
} => {
|
|
672
|
+
stmt_contains_jsx_fragment(body)
|
|
673
|
+
|| catch_body
|
|
674
|
+
.as_ref()
|
|
675
|
+
.is_some_and(|s| stmt_contains_jsx_fragment(s))
|
|
676
|
+
|| finally_body
|
|
677
|
+
.as_ref()
|
|
678
|
+
.is_some_and(|s| stmt_contains_jsx_fragment(s))
|
|
679
|
+
}
|
|
680
|
+
Statement::FunDecl { body, .. } => stmt_contains_jsx_fragment(body),
|
|
681
|
+
Statement::Throw { value, .. } => expr_contains_jsx_fragment(value),
|
|
682
|
+
Statement::Export { declaration, .. } => match declaration.as_ref() {
|
|
683
|
+
ExportDeclaration::Named(inner) => stmt_contains_jsx_fragment(inner),
|
|
684
|
+
ExportDeclaration::Default(e) => expr_contains_jsx_fragment(e),
|
|
685
|
+
},
|
|
686
|
+
Statement::Import { .. }
|
|
687
|
+
| Statement::Break { .. }
|
|
688
|
+
| Statement::Continue { .. }
|
|
689
|
+
| Statement::TypeAlias { .. }
|
|
690
|
+
| Statement::DeclareVar { .. }
|
|
691
|
+
| Statement::DeclareFun { .. } => false,
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
fn expr_contains_jsx_fragment(expr: &Expr) -> bool {
|
|
696
|
+
match expr {
|
|
697
|
+
Expr::JsxFragment { .. } => true,
|
|
698
|
+
Expr::JsxElement {
|
|
699
|
+
props, children, ..
|
|
700
|
+
} => {
|
|
701
|
+
props.iter().any(|p| match p {
|
|
702
|
+
JsxProp::Attr {
|
|
703
|
+
value: JsxAttrValue::Expr(e),
|
|
704
|
+
..
|
|
705
|
+
}
|
|
706
|
+
| JsxProp::Spread(e) => expr_contains_jsx_fragment(e),
|
|
707
|
+
_ => false,
|
|
708
|
+
}) || children.iter().any(|c| match c {
|
|
709
|
+
JsxChild::Expr(e) => expr_contains_jsx_fragment(e),
|
|
710
|
+
JsxChild::Text(_) => false,
|
|
711
|
+
})
|
|
712
|
+
}
|
|
713
|
+
Expr::Binary { left, right, .. } => {
|
|
714
|
+
expr_contains_jsx_fragment(left) || expr_contains_jsx_fragment(right)
|
|
715
|
+
}
|
|
716
|
+
Expr::Unary { operand, .. } => expr_contains_jsx_fragment(operand),
|
|
717
|
+
Expr::Assign { value, .. } => expr_contains_jsx_fragment(value),
|
|
718
|
+
Expr::Call { callee, args, .. } => {
|
|
719
|
+
expr_contains_jsx_fragment(callee)
|
|
720
|
+
|| args.iter().any(|a| match a {
|
|
721
|
+
tishlang_ast::CallArg::Expr(e) | tishlang_ast::CallArg::Spread(e) => {
|
|
722
|
+
expr_contains_jsx_fragment(e)
|
|
723
|
+
}
|
|
724
|
+
})
|
|
725
|
+
}
|
|
726
|
+
Expr::Member { object, prop, .. } => {
|
|
727
|
+
expr_contains_jsx_fragment(object)
|
|
728
|
+
|| matches!(prop, tishlang_ast::MemberProp::Expr(e) if expr_contains_jsx_fragment(e))
|
|
729
|
+
}
|
|
730
|
+
Expr::Index { object, index, .. } => {
|
|
731
|
+
expr_contains_jsx_fragment(object) || expr_contains_jsx_fragment(index)
|
|
732
|
+
}
|
|
733
|
+
Expr::Conditional {
|
|
734
|
+
cond,
|
|
735
|
+
then_branch,
|
|
736
|
+
else_branch,
|
|
737
|
+
..
|
|
738
|
+
} => {
|
|
739
|
+
expr_contains_jsx_fragment(cond)
|
|
740
|
+
|| expr_contains_jsx_fragment(then_branch)
|
|
741
|
+
|| expr_contains_jsx_fragment(else_branch)
|
|
742
|
+
}
|
|
743
|
+
Expr::Array { elements, .. } => elements.iter().any(|el| match el {
|
|
744
|
+
ArrayElement::Expr(e) | ArrayElement::Spread(e) => expr_contains_jsx_fragment(e),
|
|
745
|
+
}),
|
|
746
|
+
Expr::Object { props, .. } => props.iter().any(|p| match p {
|
|
747
|
+
ObjectProp::KeyValue(_, e, _) | ObjectProp::Spread(e) => expr_contains_jsx_fragment(e),
|
|
748
|
+
}),
|
|
749
|
+
Expr::ArrowFunction { body, .. } => match body {
|
|
750
|
+
tishlang_ast::ArrowBody::Expr(e) => expr_contains_jsx_fragment(e),
|
|
751
|
+
tishlang_ast::ArrowBody::Block(s) => stmt_contains_jsx_fragment(s),
|
|
752
|
+
},
|
|
753
|
+
Expr::NullishCoalesce { left, right, .. } => {
|
|
754
|
+
expr_contains_jsx_fragment(left) || expr_contains_jsx_fragment(right)
|
|
755
|
+
}
|
|
756
|
+
Expr::TemplateLiteral { exprs, .. } => exprs.iter().any(expr_contains_jsx_fragment),
|
|
757
|
+
Expr::Await { operand, .. } => expr_contains_jsx_fragment(operand),
|
|
758
|
+
Expr::TypeOf { operand, .. } => expr_contains_jsx_fragment(operand),
|
|
759
|
+
Expr::Delete { target, .. } => expr_contains_jsx_fragment(target),
|
|
760
|
+
Expr::CompoundAssign { value, .. } | Expr::LogicalAssign { value, .. } => {
|
|
761
|
+
expr_contains_jsx_fragment(value)
|
|
762
|
+
}
|
|
763
|
+
Expr::MemberAssign { object, value, .. } => {
|
|
764
|
+
expr_contains_jsx_fragment(object) || expr_contains_jsx_fragment(value)
|
|
765
|
+
}
|
|
766
|
+
Expr::IndexAssign {
|
|
767
|
+
object,
|
|
768
|
+
index,
|
|
769
|
+
value,
|
|
770
|
+
..
|
|
771
|
+
} => {
|
|
772
|
+
expr_contains_jsx_fragment(object)
|
|
773
|
+
|| expr_contains_jsx_fragment(index)
|
|
774
|
+
|| expr_contains_jsx_fragment(value)
|
|
775
|
+
}
|
|
776
|
+
Expr::New { callee, args, .. } => {
|
|
777
|
+
expr_contains_jsx_fragment(callee)
|
|
778
|
+
|| args.iter().any(|a| match a {
|
|
779
|
+
tishlang_ast::CallArg::Expr(e) | tishlang_ast::CallArg::Spread(e) => {
|
|
780
|
+
expr_contains_jsx_fragment(e)
|
|
781
|
+
}
|
|
782
|
+
})
|
|
783
|
+
}
|
|
784
|
+
Expr::PostfixInc { .. }
|
|
785
|
+
| Expr::PrefixInc { .. }
|
|
786
|
+
| Expr::PostfixDec { .. }
|
|
787
|
+
| Expr::PrefixDec { .. }
|
|
788
|
+
| Expr::Literal { .. }
|
|
789
|
+
| Expr::Ident { .. }
|
|
790
|
+
| Expr::NativeModuleLoad { .. } => false,
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
544
794
|
fn stmt_contains_jsx(stmt: &tishlang_ast::Statement) -> bool {
|
|
545
795
|
use tishlang_ast::{ExportDeclaration, Statement};
|
|
546
796
|
match stmt {
|
package/package.json
CHANGED
|
Binary file
|
package/platform/darwin-x64/tish
CHANGED
|
Binary file
|
|
Binary file
|
package/platform/linux-x64/tish
CHANGED
|
Binary file
|
|
Binary file
|