@tishlang/tish 2.10.1 → 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 +1 -1
- package/crates/tish/src/cli_help.rs +16 -0
- package/crates/tish/src/main.rs +24 -4
- package/crates/tish/tests/integration_test.rs +69 -0
- package/crates/tish_compile_js/src/codegen.rs +58 -4
- package/crates/tish_compile_js/src/lib.rs +5 -2
- package/crates/tish_compile_js/src/tests_jsx.rs +175 -7
- 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
package/bin/tish
CHANGED
|
Binary file
|
package/crates/tish/Cargo.toml
CHANGED
|
@@ -563,6 +563,14 @@ pub(crate) struct BuildArgs {
|
|
|
563
563
|
/// For `--target js`: `bundle` (default) merges all modules into one file; `esm` emits one `.js` per `.tish` module with real `import`/`export` (so `-o` is a directory) for Vite/Rollup tree-shaking.
|
|
564
564
|
#[arg(long, default_value = "bundle", value_name = "FORMAT", help_heading = "Options")]
|
|
565
565
|
pub format: String,
|
|
566
|
+
/// For `--target js --format esm`: package the JSX runtime (`h`/`Fragment`) is auto-imported from when a module uses JSX without importing them (default `lattish`).
|
|
567
|
+
#[arg(
|
|
568
|
+
long = "jsx-import-source",
|
|
569
|
+
default_value = "lattish",
|
|
570
|
+
value_name = "PKG",
|
|
571
|
+
help_heading = "Options"
|
|
572
|
+
)]
|
|
573
|
+
pub jsx_import_source: String,
|
|
566
574
|
/// Run the gradual type checker: `warn` prints `line:col` type diagnostics; `error` also fails the build on them. (Equivalent to setting `TISH_CHECK`.)
|
|
567
575
|
#[arg(long, value_name = "MODE", help_heading = "Options")]
|
|
568
576
|
pub check: Option<String>,
|
|
@@ -594,6 +602,14 @@ pub(crate) struct CompileModuleArgs {
|
|
|
594
602
|
/// Disable AST optimizations (forced on when a source map is emitted).
|
|
595
603
|
#[arg(long, help_heading = "Options")]
|
|
596
604
|
pub no_optimize: bool,
|
|
605
|
+
/// Package the JSX runtime (`h`/`Fragment`) is auto-imported from when a module uses JSX without importing them (default `lattish`).
|
|
606
|
+
#[arg(
|
|
607
|
+
long = "jsx-import-source",
|
|
608
|
+
default_value = "lattish",
|
|
609
|
+
value_name = "PKG",
|
|
610
|
+
help_heading = "Options"
|
|
611
|
+
)]
|
|
612
|
+
pub jsx_import_source: String,
|
|
597
613
|
/// Entry `.tish` file to compile (only this file is read; dependencies are left to the bundler).
|
|
598
614
|
#[arg(required = true, value_name = "FILE", help_heading = "Arguments")]
|
|
599
615
|
pub file: String,
|
package/crates/tish/src/main.rs
CHANGED
|
@@ -137,6 +137,7 @@ fn main() {
|
|
|
137
137
|
a.ios_triple.as_deref(),
|
|
138
138
|
&a.crate_type,
|
|
139
139
|
&a.format,
|
|
140
|
+
&a.jsx_import_source,
|
|
140
141
|
)
|
|
141
142
|
}
|
|
142
143
|
Some(Commands::CompileModule(a)) => compile_module(&a),
|
|
@@ -544,6 +545,7 @@ fn compile_to_js(
|
|
|
544
545
|
optimize: bool,
|
|
545
546
|
source_map: bool,
|
|
546
547
|
format: &str,
|
|
548
|
+
jsx_import_source: &str,
|
|
547
549
|
) -> Result<(), String> {
|
|
548
550
|
if format != "bundle" && format != "esm" {
|
|
549
551
|
return Err(format!(
|
|
@@ -574,7 +576,14 @@ fn compile_to_js(
|
|
|
574
576
|
}
|
|
575
577
|
});
|
|
576
578
|
if format == "esm" {
|
|
577
|
-
return compile_to_js_esm(
|
|
579
|
+
return compile_to_js_esm(
|
|
580
|
+
input_path,
|
|
581
|
+
output_path,
|
|
582
|
+
optimize,
|
|
583
|
+
source_map,
|
|
584
|
+
project_root,
|
|
585
|
+
jsx_import_source,
|
|
586
|
+
);
|
|
578
587
|
}
|
|
579
588
|
let out_path = Path::new(output_path);
|
|
580
589
|
let out_path = if out_path.extension().is_none()
|
|
@@ -656,6 +665,7 @@ fn compile_to_js_esm(
|
|
|
656
665
|
optimize: bool,
|
|
657
666
|
source_map: bool,
|
|
658
667
|
project_root: Option<&Path>,
|
|
668
|
+
jsx_import_source: &str,
|
|
659
669
|
) -> Result<(), String> {
|
|
660
670
|
if source_map {
|
|
661
671
|
return Err(
|
|
@@ -671,8 +681,9 @@ fn compile_to_js_esm(
|
|
|
671
681
|
.into(),
|
|
672
682
|
);
|
|
673
683
|
}
|
|
674
|
-
let modules =
|
|
675
|
-
|
|
684
|
+
let modules =
|
|
685
|
+
tishlang_compile_js::compile_project_esm(input_path, project_root, optimize, jsx_import_source)
|
|
686
|
+
.map_err(|e| format!("{}", e))?;
|
|
676
687
|
let out_dir = Path::new(output_path);
|
|
677
688
|
fs::create_dir_all(out_dir)
|
|
678
689
|
.map_err(|e| format!("Cannot create output directory {}: {}", out_dir.display(), e))?;
|
|
@@ -753,6 +764,7 @@ fn compile_module(args: &CompileModuleArgs) -> Result<(), String> {
|
|
|
753
764
|
optimize,
|
|
754
765
|
import_rewrite,
|
|
755
766
|
want_map,
|
|
767
|
+
&args.jsx_import_source,
|
|
756
768
|
)
|
|
757
769
|
.map_err(|e| format!("{}", e))?;
|
|
758
770
|
|
|
@@ -781,6 +793,7 @@ fn build_file(
|
|
|
781
793
|
ios_triple: Option<&str>,
|
|
782
794
|
crate_type: &str,
|
|
783
795
|
format: &str,
|
|
796
|
+
jsx_import_source: &str,
|
|
784
797
|
) -> Result<(), String> {
|
|
785
798
|
let optimize = !no_optimize;
|
|
786
799
|
let input_path = Path::new(input_path)
|
|
@@ -790,7 +803,14 @@ fn build_file(
|
|
|
790
803
|
let is_js = input_path.extension().map(|e| e == "js") == Some(true);
|
|
791
804
|
|
|
792
805
|
if target == "js" {
|
|
793
|
-
return compile_to_js(
|
|
806
|
+
return compile_to_js(
|
|
807
|
+
&input_path,
|
|
808
|
+
output_path,
|
|
809
|
+
optimize,
|
|
810
|
+
source_map,
|
|
811
|
+
format,
|
|
812
|
+
jsx_import_source,
|
|
813
|
+
);
|
|
794
814
|
}
|
|
795
815
|
|
|
796
816
|
// `wasm-gpu` (#277): same pipeline as `wasm` but builds the `--features gpu` WebGPU runtime and
|
|
@@ -846,6 +846,75 @@ fn test_compile_module_no_source_map_prints_raw_js() {
|
|
|
846
846
|
);
|
|
847
847
|
}
|
|
848
848
|
|
|
849
|
+
/// #291: `compile-module` auto-imports the JSX runtime (`h` / `Fragment`) when a module uses JSX
|
|
850
|
+
/// but doesn't import them itself, so per-module ESM output doesn't throw `ReferenceError` at load.
|
|
851
|
+
/// `--jsx-import-source` overrides the runtime package.
|
|
852
|
+
#[test]
|
|
853
|
+
fn test_compile_module_jsx_auto_imports_runtime() {
|
|
854
|
+
let bin = tish_bin();
|
|
855
|
+
if !bin.exists() {
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
let widget = workspace_root()
|
|
859
|
+
.join("tests")
|
|
860
|
+
.join("modules")
|
|
861
|
+
.join("esm")
|
|
862
|
+
.join("vite_hmr")
|
|
863
|
+
.join("widget.tish");
|
|
864
|
+
if !widget.exists() {
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
// Default runtime source (`lattish`), Vite-dev so the specifier stays bare.
|
|
868
|
+
let out = Command::new(&bin)
|
|
869
|
+
.args(["compile-module"])
|
|
870
|
+
.arg(&widget)
|
|
871
|
+
.args([
|
|
872
|
+
"--target",
|
|
873
|
+
"js",
|
|
874
|
+
"--format",
|
|
875
|
+
"esm",
|
|
876
|
+
"--vite-dev",
|
|
877
|
+
"--no-source-map",
|
|
878
|
+
])
|
|
879
|
+
.current_dir(workspace_root())
|
|
880
|
+
.output()
|
|
881
|
+
.expect("run compile-module");
|
|
882
|
+
assert!(
|
|
883
|
+
out.status.success(),
|
|
884
|
+
"compile-module failed: stderr={}",
|
|
885
|
+
String::from_utf8_lossy(&out.stderr)
|
|
886
|
+
);
|
|
887
|
+
let stdout = String::from_utf8_lossy(&out.stdout);
|
|
888
|
+
assert!(
|
|
889
|
+
stdout.contains("import { h, Fragment } from \"lattish\";"),
|
|
890
|
+
"JSX module with a fragment auto-imports both h and Fragment:\n{stdout}"
|
|
891
|
+
);
|
|
892
|
+
|
|
893
|
+
// Custom runtime source via --jsx-import-source.
|
|
894
|
+
let out2 = Command::new(&bin)
|
|
895
|
+
.args(["compile-module"])
|
|
896
|
+
.arg(&widget)
|
|
897
|
+
.args([
|
|
898
|
+
"--target",
|
|
899
|
+
"js",
|
|
900
|
+
"--format",
|
|
901
|
+
"esm",
|
|
902
|
+
"--vite-dev",
|
|
903
|
+
"--no-source-map",
|
|
904
|
+
"--jsx-import-source",
|
|
905
|
+
"@tishlang/lattish",
|
|
906
|
+
])
|
|
907
|
+
.current_dir(workspace_root())
|
|
908
|
+
.output()
|
|
909
|
+
.expect("run compile-module");
|
|
910
|
+
assert!(out2.status.success());
|
|
911
|
+
let stdout2 = String::from_utf8_lossy(&out2.stdout);
|
|
912
|
+
assert!(
|
|
913
|
+
stdout2.contains("from \"@tishlang/lattish\";"),
|
|
914
|
+
"--jsx-import-source overrides the runtime package:\n{stdout2}"
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
|
|
849
918
|
/// Ignored: tishlang_eval::run() does not run the event loop.
|
|
850
919
|
#[test]
|
|
851
920
|
#[cfg(feature = "http")]
|
|
@@ -11,6 +11,10 @@ use tishlang_ast::{
|
|
|
11
11
|
|
|
12
12
|
use crate::error::CompileError;
|
|
13
13
|
|
|
14
|
+
/// Default module specifier the JSX runtime (`h` / `Fragment`) is auto-imported from (issue #291).
|
|
15
|
+
/// Matches the repo convention (`import { h } from "lattish"`); overridable via `--jsx-import-source`.
|
|
16
|
+
pub const DEFAULT_JSX_IMPORT_SOURCE: &str = "lattish";
|
|
17
|
+
|
|
14
18
|
/// JS output mode. `Bundle` flattens every module into one file (imports/exports are resolved away
|
|
15
19
|
/// by `merge_modules`). `Esm` emits one file per module with real ES `import`/`export` statements so
|
|
16
20
|
/// a bundler (Vite/Rollup) can tree-shake and code-split. See issue #177's sibling, #282.
|
|
@@ -42,6 +46,9 @@ struct Codegen {
|
|
|
42
46
|
module_path: PathBuf,
|
|
43
47
|
/// ESM only: the project root, so import targets can be located relative to the output tree.
|
|
44
48
|
project_root: PathBuf,
|
|
49
|
+
/// ESM only: the module specifier the JSX runtime (`h` / `Fragment`) is auto-imported from when a
|
|
50
|
+
/// module uses JSX but doesn't import them itself (issue #291). Defaults to `lattish`.
|
|
51
|
+
jsx_import_source: String,
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
fn stmt_terminates_switch(stmt: Option<&Statement>) -> bool {
|
|
@@ -63,10 +70,16 @@ impl Codegen {
|
|
|
63
70
|
import_rewrite: ImportRewrite::Disk,
|
|
64
71
|
module_path: PathBuf::new(),
|
|
65
72
|
project_root: PathBuf::new(),
|
|
73
|
+
jsx_import_source: DEFAULT_JSX_IMPORT_SOURCE.to_string(),
|
|
66
74
|
}
|
|
67
75
|
}
|
|
68
76
|
|
|
69
|
-
fn new_esm(
|
|
77
|
+
fn new_esm(
|
|
78
|
+
module_path: PathBuf,
|
|
79
|
+
project_root: PathBuf,
|
|
80
|
+
import_rewrite: ImportRewrite,
|
|
81
|
+
jsx_import_source: String,
|
|
82
|
+
) -> Self {
|
|
70
83
|
Self {
|
|
71
84
|
output: String::new(),
|
|
72
85
|
indent: 0,
|
|
@@ -75,6 +88,7 @@ impl Codegen {
|
|
|
75
88
|
import_rewrite,
|
|
76
89
|
module_path,
|
|
77
90
|
project_root,
|
|
91
|
+
jsx_import_source,
|
|
78
92
|
}
|
|
79
93
|
}
|
|
80
94
|
|
|
@@ -139,6 +153,7 @@ impl Codegen {
|
|
|
139
153
|
map_builder: Option<&mut SourceMapBuilder>,
|
|
140
154
|
) -> Result<(), CompileError> {
|
|
141
155
|
self.write("// Generated by tishlang_compile_js\n");
|
|
156
|
+
self.emit_jsx_runtime_auto_import(program)?;
|
|
142
157
|
match (map_sources, map_builder) {
|
|
143
158
|
(Some((srcs, root)), Some(sm)) => {
|
|
144
159
|
for (i, stmt) in program.statements.iter().enumerate() {
|
|
@@ -176,6 +191,34 @@ impl Codegen {
|
|
|
176
191
|
Ok(())
|
|
177
192
|
}
|
|
178
193
|
|
|
194
|
+
/// Auto-import the JSX runtime (`h` / `Fragment`) for ESM modules that use JSX but don't bring
|
|
195
|
+
/// those bindings into scope themselves (issue #291). In `--format bundle` the merged scope makes
|
|
196
|
+
/// a single `lattish` import visible everywhere, but each ES module is its own scope, so a JSX
|
|
197
|
+
/// module that didn't `import { h }` throws `ReferenceError` at load. We inject only the missing
|
|
198
|
+
/// names (so an explicit `import { h }` isn't duplicated) from `jsx_import_source`, rewritten the
|
|
199
|
+
/// same way as user imports (disk `.js` path vs. Vite-dev bare specifier). No-op outside ESM.
|
|
200
|
+
fn emit_jsx_runtime_auto_import(&mut self, program: &Program) -> Result<(), CompileError> {
|
|
201
|
+
if self.emit_mode != EmitMode::Esm {
|
|
202
|
+
return Ok(());
|
|
203
|
+
}
|
|
204
|
+
let needed = tishlang_ui::jsx::jsx_runtime_imports_needed(program);
|
|
205
|
+
if needed.is_empty() {
|
|
206
|
+
return Ok(());
|
|
207
|
+
}
|
|
208
|
+
let spec = rewrite_import_to_js(
|
|
209
|
+
&self.jsx_import_source,
|
|
210
|
+
&self.module_path,
|
|
211
|
+
&self.project_root,
|
|
212
|
+
self.import_rewrite,
|
|
213
|
+
)?;
|
|
214
|
+
self.writeln(&format!(
|
|
215
|
+
"import {{ {} }} from \"{}\";",
|
|
216
|
+
needed.join(", "),
|
|
217
|
+
spec
|
|
218
|
+
));
|
|
219
|
+
Ok(())
|
|
220
|
+
}
|
|
221
|
+
|
|
179
222
|
fn emit_statement(&mut self, stmt: &Statement) -> Result<(), CompileError> {
|
|
180
223
|
match stmt {
|
|
181
224
|
Statement::Block { statements, .. } => {
|
|
@@ -1235,6 +1278,7 @@ pub fn compile_project_esm(
|
|
|
1235
1278
|
entry_path: &Path,
|
|
1236
1279
|
project_root: Option<&Path>,
|
|
1237
1280
|
optimize: bool,
|
|
1281
|
+
jsx_import_source: &str,
|
|
1238
1282
|
) -> Result<Vec<EmittedJsModule>, CompileError> {
|
|
1239
1283
|
let modules = tishlang_compile::resolve_project(entry_path, project_root)
|
|
1240
1284
|
.map_err(|e| CompileError { message: e })?;
|
|
@@ -1274,8 +1318,12 @@ pub fn compile_project_esm(
|
|
|
1274
1318
|
} else {
|
|
1275
1319
|
module.program.clone()
|
|
1276
1320
|
};
|
|
1277
|
-
let mut gen =
|
|
1278
|
-
|
|
1321
|
+
let mut gen = Codegen::new_esm(
|
|
1322
|
+
mod_canon.clone(),
|
|
1323
|
+
res_root_canon.clone(),
|
|
1324
|
+
ImportRewrite::Disk,
|
|
1325
|
+
jsx_import_source.to_string(),
|
|
1326
|
+
);
|
|
1279
1327
|
gen.emit_program(&program, None, None)?;
|
|
1280
1328
|
out.push(EmittedJsModule {
|
|
1281
1329
|
relative_path: rel_js,
|
|
@@ -1297,6 +1345,7 @@ pub fn compile_module_esm(
|
|
|
1297
1345
|
optimize: bool,
|
|
1298
1346
|
import_rewrite: ImportRewrite,
|
|
1299
1347
|
source_map: bool,
|
|
1348
|
+
jsx_import_source: &str,
|
|
1300
1349
|
) -> Result<JsBundle, CompileError> {
|
|
1301
1350
|
if source_map && optimize {
|
|
1302
1351
|
return Err(CompileError {
|
|
@@ -1324,7 +1373,12 @@ pub fn compile_module_esm(
|
|
|
1324
1373
|
.unwrap_or_else(|| PathBuf::from("."));
|
|
1325
1374
|
let root_canon = root.canonicalize().unwrap_or(root);
|
|
1326
1375
|
|
|
1327
|
-
let mut gen = Codegen::new_esm(
|
|
1376
|
+
let mut gen = Codegen::new_esm(
|
|
1377
|
+
module_canon.clone(),
|
|
1378
|
+
root_canon.clone(),
|
|
1379
|
+
import_rewrite,
|
|
1380
|
+
jsx_import_source.to_string(),
|
|
1381
|
+
);
|
|
1328
1382
|
if source_map {
|
|
1329
1383
|
let stmt_sources = vec![module_canon.clone(); program.statements.len()];
|
|
1330
1384
|
let mut builder = SourceMapBuilder::new(Some("module.js"));
|
|
@@ -10,11 +10,14 @@ mod tests_jsx;
|
|
|
10
10
|
pub use codegen::{
|
|
11
11
|
compile_module_esm, compile_project_esm, compile_project_with_jsx,
|
|
12
12
|
compile_project_with_jsx_and_source_map, compile_with_jsx, EmittedJsModule, ImportRewrite,
|
|
13
|
-
JsBundle,
|
|
13
|
+
JsBundle, DEFAULT_JSX_IMPORT_SOURCE,
|
|
14
14
|
};
|
|
15
15
|
pub use error::CompileError;
|
|
16
16
|
|
|
17
|
-
/// JSX lowers to `h` / `Fragment`;
|
|
17
|
+
/// Compile a single program to a bundle-style JS string. JSX lowers to `h` / `Fragment`; in bundle
|
|
18
|
+
/// mode those resolve against the merged scope (merge the `lattish` runtime for hooks and DOM). The
|
|
19
|
+
/// per-module ESM paths ([`compile_module_esm`] / [`compile_project_esm`]) auto-import the runtime
|
|
20
|
+
/// for JSX modules that don't import it themselves (issue #291).
|
|
18
21
|
pub fn compile(program: &tishlang_ast::Program, optimize: bool) -> Result<String, CompileError> {
|
|
19
22
|
compile_with_jsx(program, optimize)
|
|
20
23
|
}
|
|
@@ -475,7 +475,8 @@ fn factory() {
|
|
|
475
475
|
f.write_all(src.as_bytes()).unwrap();
|
|
476
476
|
f.sync_all().unwrap();
|
|
477
477
|
}
|
|
478
|
-
compile_project_esm(&dir.join(entry), Some(dir), false
|
|
478
|
+
compile_project_esm(&dir.join(entry), Some(dir), false, "lattish")
|
|
479
|
+
.expect("compile_project_esm failed")
|
|
479
480
|
}
|
|
480
481
|
|
|
481
482
|
fn module_js<'a>(mods: &'a [EmittedJsModule], rel: &str) -> &'a str {
|
|
@@ -557,6 +558,7 @@ fn factory() {
|
|
|
557
558
|
&base.join("app/src/main.tish"),
|
|
558
559
|
Some(&base.join("app")),
|
|
559
560
|
false,
|
|
561
|
+
"lattish",
|
|
560
562
|
)
|
|
561
563
|
.expect("compile_project_esm failed for sibling dep");
|
|
562
564
|
let rels: Vec<String> = mods
|
|
@@ -607,7 +609,7 @@ fn factory() {
|
|
|
607
609
|
"import { ping } from \"pkg\"\nconsole.log(ping())\n",
|
|
608
610
|
)
|
|
609
611
|
.unwrap();
|
|
610
|
-
let mods = compile_project_esm(&base.join("src/main.tish"), Some(base), false)
|
|
612
|
+
let mods = compile_project_esm(&base.join("src/main.tish"), Some(base), false, "lattish")
|
|
611
613
|
.expect("compile_project_esm failed for node_modules dep");
|
|
612
614
|
let rels: Vec<String> = mods
|
|
613
615
|
.iter()
|
|
@@ -635,7 +637,7 @@ fn factory() {
|
|
|
635
637
|
let dir = tmp.path();
|
|
636
638
|
let p = dir.join("main.tish");
|
|
637
639
|
std::fs::write(&p, "import { readFile } from \"fs\"\nconsole.log(1)\n").unwrap();
|
|
638
|
-
let err = compile_project_esm(&p, Some(dir), false).unwrap_err();
|
|
640
|
+
let err = compile_project_esm(&p, Some(dir), false, "lattish").unwrap_err();
|
|
639
641
|
assert!(
|
|
640
642
|
err.message.contains("Native module import") && err.message.contains("esm"),
|
|
641
643
|
"expected a native-import rejection for ESM, got: {}",
|
|
@@ -665,6 +667,7 @@ fn factory() {
|
|
|
665
667
|
false,
|
|
666
668
|
ImportRewrite::ViteDev,
|
|
667
669
|
false,
|
|
670
|
+
"lattish",
|
|
668
671
|
)
|
|
669
672
|
.expect("compile_module_esm failed")
|
|
670
673
|
.js
|
|
@@ -741,7 +744,7 @@ fn factory() {
|
|
|
741
744
|
let dir = tmp.path();
|
|
742
745
|
let p = dir.join("main.tish");
|
|
743
746
|
std::fs::write(&p, "import { readFile } from \"fs\"\nconsole.log(1)\n").unwrap();
|
|
744
|
-
let err = compile_module_esm(&p, Some(dir), false, ImportRewrite::ViteDev, false)
|
|
747
|
+
let err = compile_module_esm(&p, Some(dir), false, ImportRewrite::ViteDev, false, "lattish")
|
|
745
748
|
.unwrap_err();
|
|
746
749
|
assert!(
|
|
747
750
|
err.message.contains("Native module import"),
|
|
@@ -758,7 +761,8 @@ fn factory() {
|
|
|
758
761
|
std::fs::write(&p, "export fn greet(n) { return \"hi \" + n }\nconsole.log(greet(\"x\"))\n")
|
|
759
762
|
.unwrap();
|
|
760
763
|
let bundle =
|
|
761
|
-
compile_module_esm(&p, Some(dir), false, ImportRewrite::ViteDev, true)
|
|
764
|
+
compile_module_esm(&p, Some(dir), false, ImportRewrite::ViteDev, true, "lattish")
|
|
765
|
+
.unwrap();
|
|
762
766
|
let map = bundle
|
|
763
767
|
.source_map_json
|
|
764
768
|
.expect("source map requested → must be present");
|
|
@@ -782,7 +786,8 @@ fn factory() {
|
|
|
782
786
|
let src = "export fn greet(n) { return \"hi \" + n }\n";
|
|
783
787
|
std::fs::write(&p, src).unwrap();
|
|
784
788
|
let bundle =
|
|
785
|
-
compile_module_esm(&p, Some(dir), false, ImportRewrite::ViteDev, true)
|
|
789
|
+
compile_module_esm(&p, Some(dir), false, ImportRewrite::ViteDev, true, "lattish")
|
|
790
|
+
.unwrap();
|
|
786
791
|
let map = bundle.source_map_json.expect("source map requested");
|
|
787
792
|
assert!(
|
|
788
793
|
map.contains("\"sourcesContent\""),
|
|
@@ -800,7 +805,7 @@ fn factory() {
|
|
|
800
805
|
let dir = tmp.path();
|
|
801
806
|
let p = dir.join("main.tish");
|
|
802
807
|
std::fs::write(&p, "console.log(1)\n").unwrap();
|
|
803
|
-
let err = compile_module_esm(&p, Some(dir), true, ImportRewrite::ViteDev, true)
|
|
808
|
+
let err = compile_module_esm(&p, Some(dir), true, ImportRewrite::ViteDev, true, "lattish")
|
|
804
809
|
.unwrap_err();
|
|
805
810
|
assert!(
|
|
806
811
|
err.message.to_lowercase().contains("optimiz"),
|
|
@@ -808,4 +813,167 @@ fn factory() {
|
|
|
808
813
|
err.message
|
|
809
814
|
);
|
|
810
815
|
}
|
|
816
|
+
|
|
817
|
+
// ── #291: JSX runtime auto-import (h / Fragment) for per-module ESM ────────────────────────
|
|
818
|
+
|
|
819
|
+
#[test]
|
|
820
|
+
fn compile_module_esm_jsx_auto_imports_h() {
|
|
821
|
+
// A module that uses a JSX element but never imports `h` must get the runtime auto-imported,
|
|
822
|
+
// or it throws `ReferenceError: h` at load in per-module ESM (issue #291).
|
|
823
|
+
let js = build_module_vite("main.tish", &[("main.tish", "fn X() { return <div/> }\n")]);
|
|
824
|
+
assert!(
|
|
825
|
+
js.contains("import { h } from \"lattish\";"),
|
|
826
|
+
"JSX element module must auto-import h:\n{js}"
|
|
827
|
+
);
|
|
828
|
+
assert!(
|
|
829
|
+
!js.contains("Fragment"),
|
|
830
|
+
"no fragment used → Fragment must not be imported:\n{js}"
|
|
831
|
+
);
|
|
832
|
+
assert!(js.contains("h(\"div\""), "JSX still lowers to h(...):\n{js}");
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
#[test]
|
|
836
|
+
fn compile_module_esm_jsx_auto_imports_fragment() {
|
|
837
|
+
// A fragment lowers to `h(Fragment, …)`, so both `h` and `Fragment` must be auto-imported.
|
|
838
|
+
let js = build_module_vite(
|
|
839
|
+
"main.tish",
|
|
840
|
+
&[("main.tish", "fn X() { return <>{\"a\"}</> }\n")],
|
|
841
|
+
);
|
|
842
|
+
assert!(
|
|
843
|
+
js.contains("import { h, Fragment } from \"lattish\";"),
|
|
844
|
+
"fragment module must auto-import both h and Fragment:\n{js}"
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
#[test]
|
|
849
|
+
fn compile_module_esm_jsx_skips_import_when_present() {
|
|
850
|
+
// An explicit `import { h, Fragment } from "lattish"` must not be duplicated.
|
|
851
|
+
let js = build_module_vite(
|
|
852
|
+
"main.tish",
|
|
853
|
+
&[(
|
|
854
|
+
"main.tish",
|
|
855
|
+
"import { h, Fragment } from \"lattish\"\nfn X() { return <></> }\n",
|
|
856
|
+
)],
|
|
857
|
+
);
|
|
858
|
+
assert_eq!(
|
|
859
|
+
js.matches("from \"lattish\"").count(),
|
|
860
|
+
1,
|
|
861
|
+
"exactly one lattish import (the author's), no auto-import duplicate:\n{js}"
|
|
862
|
+
);
|
|
863
|
+
assert!(
|
|
864
|
+
js.contains("import { h, Fragment } from \"lattish\";"),
|
|
865
|
+
"the author's import is preserved:\n{js}"
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
#[test]
|
|
870
|
+
fn compile_module_esm_jsx_partial_import_extends() {
|
|
871
|
+
// The author imported only `h`; a fragment also needs `Fragment`, so just that one missing
|
|
872
|
+
// binding is auto-imported (no re-import of `h`).
|
|
873
|
+
let js = build_module_vite(
|
|
874
|
+
"main.tish",
|
|
875
|
+
&[(
|
|
876
|
+
"main.tish",
|
|
877
|
+
"import { h } from \"lattish\"\nfn X() { return <></> }\n",
|
|
878
|
+
)],
|
|
879
|
+
);
|
|
880
|
+
assert!(
|
|
881
|
+
js.contains("import { Fragment } from \"lattish\";"),
|
|
882
|
+
"only the missing Fragment binding is auto-imported:\n{js}"
|
|
883
|
+
);
|
|
884
|
+
assert!(
|
|
885
|
+
js.contains("import { h } from \"lattish\";"),
|
|
886
|
+
"the author's h import is preserved:\n{js}"
|
|
887
|
+
);
|
|
888
|
+
assert!(
|
|
889
|
+
!js.contains("import { h, Fragment }"),
|
|
890
|
+
"h must not be re-imported alongside Fragment:\n{js}"
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
#[test]
|
|
895
|
+
fn compile_module_esm_vite_dev_jsx_keeps_bare_runtime_import() {
|
|
896
|
+
// In Vite-dev mode the auto-import specifier stays the bare package so Node/Vite resolves it
|
|
897
|
+
// (it is NOT rewritten to a relative `.js` path the way disk ESM does).
|
|
898
|
+
let js = build_module_vite("main.tish", &[("main.tish", "fn X() { return <div/> }\n")]);
|
|
899
|
+
assert!(
|
|
900
|
+
js.contains("from \"lattish\";"),
|
|
901
|
+
"Vite dev keeps the bare runtime specifier:\n{js}"
|
|
902
|
+
);
|
|
903
|
+
assert!(
|
|
904
|
+
!js.contains("lattish.js") && !js.contains("./lattish"),
|
|
905
|
+
"Vite dev must not rewrite the runtime specifier to a path:\n{js}"
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
#[test]
|
|
910
|
+
fn compile_module_esm_jsx_import_source_is_configurable() {
|
|
911
|
+
// The runtime package is overridable (e.g. the scoped `@tishlang/lattish`, or a custom runtime).
|
|
912
|
+
let tmp = tempfile::tempdir().expect("tempdir");
|
|
913
|
+
let dir = tmp.path();
|
|
914
|
+
let p = dir.join("main.tish");
|
|
915
|
+
std::fs::write(&p, "fn X() { return <div/> }\n").unwrap();
|
|
916
|
+
let js = compile_module_esm(
|
|
917
|
+
&p,
|
|
918
|
+
Some(dir),
|
|
919
|
+
false,
|
|
920
|
+
ImportRewrite::ViteDev,
|
|
921
|
+
false,
|
|
922
|
+
"@tishlang/lattish",
|
|
923
|
+
)
|
|
924
|
+
.expect("compile_module_esm failed")
|
|
925
|
+
.js;
|
|
926
|
+
assert!(
|
|
927
|
+
js.contains("import { h } from \"@tishlang/lattish\";"),
|
|
928
|
+
"custom jsx import source used:\n{js}"
|
|
929
|
+
);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
#[test]
|
|
933
|
+
fn compile_project_esm_jsx_auto_import_per_module() {
|
|
934
|
+
// Only the module that actually uses JSX gets the runtime import; plain modules are untouched.
|
|
935
|
+
let tmp = tempfile::tempdir().expect("tempdir");
|
|
936
|
+
let base = tmp.path();
|
|
937
|
+
std::fs::create_dir_all(base.join("src")).unwrap();
|
|
938
|
+
std::fs::create_dir_all(base.join("node_modules/lattish")).unwrap();
|
|
939
|
+
std::fs::write(
|
|
940
|
+
base.join("node_modules/lattish/package.json"),
|
|
941
|
+
"{\"name\":\"lattish\",\"main\":\"index.tish\"}\n",
|
|
942
|
+
)
|
|
943
|
+
.unwrap();
|
|
944
|
+
std::fs::write(
|
|
945
|
+
base.join("node_modules/lattish/index.tish"),
|
|
946
|
+
"export fn h(tag, props, children) { return tag }\nexport let Fragment = \"frag\"\n",
|
|
947
|
+
)
|
|
948
|
+
.unwrap();
|
|
949
|
+
std::fs::write(
|
|
950
|
+
base.join("src/view.tish"),
|
|
951
|
+
"export fn V() { return <div/> }\n",
|
|
952
|
+
)
|
|
953
|
+
.unwrap();
|
|
954
|
+
std::fs::write(
|
|
955
|
+
base.join("src/plain.tish"),
|
|
956
|
+
"export fn add(a, b) { return a + b }\n",
|
|
957
|
+
)
|
|
958
|
+
.unwrap();
|
|
959
|
+
std::fs::write(
|
|
960
|
+
base.join("src/main.tish"),
|
|
961
|
+
"import { V } from \"./view.tish\"\nimport { add } from \"./plain.tish\"\nconsole.log(add(1, 2))\n",
|
|
962
|
+
)
|
|
963
|
+
.unwrap();
|
|
964
|
+
let mods = compile_project_esm(&base.join("src/main.tish"), Some(base), false, "lattish")
|
|
965
|
+
.expect("compile_project_esm failed");
|
|
966
|
+
// The runtime isn't imported by any module, so it's not in the graph; the common-ancestor
|
|
967
|
+
// output base is `src/`, making the emitted paths flat (`view.js`, `plain.js`, `main.js`).
|
|
968
|
+
let view = module_js(&mods, "view.js");
|
|
969
|
+
let plain = module_js(&mods, "plain.js");
|
|
970
|
+
assert!(
|
|
971
|
+
view.contains("import { h } from") && view.contains("lattish"),
|
|
972
|
+
"JSX module auto-imports the runtime:\n{view}"
|
|
973
|
+
);
|
|
974
|
+
assert!(
|
|
975
|
+
!plain.contains("import { h }") && !plain.contains("Fragment"),
|
|
976
|
+
"non-JSX module must not get a runtime import:\n{plain}"
|
|
977
|
+
);
|
|
978
|
+
}
|
|
811
979
|
}
|
|
@@ -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
|