@tishlang/tish 2.8.0 → 2.10.1
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 +3 -1
- package/crates/tish/src/cli_help.rs +34 -0
- package/crates/tish/src/main.rs +138 -3
- package/crates/tish/tests/integration_test.rs +173 -0
- package/crates/tish_compile/src/codegen.rs +1569 -107
- package/crates/tish_compile/src/infer.rs +1946 -52
- package/crates/tish_compile/tests/perf_codegen_169_173.rs +139 -0
- package/crates/tish_compile_js/Cargo.toml +3 -0
- package/crates/tish_compile_js/src/codegen.rs +414 -5
- package/crates/tish_compile_js/src/lib.rs +3 -1
- package/crates/tish_compile_js/src/tests_jsx.rs +354 -1
- 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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "tishlang"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.10.1"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
description = "Tish CLI - run, REPL, compile to native"
|
|
6
6
|
license-file = { workspace = true }
|
|
@@ -59,8 +59,10 @@ tishlang_runtime = { path = "../tish_runtime", version = ">=0.1" }
|
|
|
59
59
|
tishlang_core = { path = "../tish_core", version = ">=0.1" }
|
|
60
60
|
tishlang_js_to_tish = { path = "../js_to_tish", version = ">=0.1" }
|
|
61
61
|
clap = { version = "4.6.0", features = ["derive", "color"] }
|
|
62
|
+
serde_json = "1"
|
|
62
63
|
tishlang_pg = { path = "../tish_pg", version = ">=0.1", optional = true }
|
|
63
64
|
mimalloc = { version = "0.1", optional = true }
|
|
64
65
|
|
|
65
66
|
[dev-dependencies]
|
|
66
67
|
rayon = "1.11"
|
|
68
|
+
tempfile = "3"
|
|
@@ -560,6 +560,9 @@ pub(crate) struct BuildArgs {
|
|
|
560
560
|
/// For `--target js` project builds: emit `OUTPUT.js.map` and `//# sourceMappingURL=…` so JS/TS tools can jump to original `.tish` (implies `--no-optimize` for that build).
|
|
561
561
|
#[arg(long, help_heading = "Options")]
|
|
562
562
|
pub source_map: bool,
|
|
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
|
+
#[arg(long, default_value = "bundle", value_name = "FORMAT", help_heading = "Options")]
|
|
565
|
+
pub format: String,
|
|
563
566
|
/// Run the gradual type checker: `warn` prints `line:col` type diagnostics; `error` also fails the build on them. (Equivalent to setting `TISH_CHECK`.)
|
|
564
567
|
#[arg(long, value_name = "MODE", help_heading = "Options")]
|
|
565
568
|
pub check: Option<String>,
|
|
@@ -568,6 +571,34 @@ pub(crate) struct BuildArgs {
|
|
|
568
571
|
pub file: String,
|
|
569
572
|
}
|
|
570
573
|
|
|
574
|
+
#[derive(Parser)]
|
|
575
|
+
pub(crate) struct CompileModuleArgs {
|
|
576
|
+
/// `--target js` only (the single-module ESM path is JS-specific).
|
|
577
|
+
#[arg(long, default_value = "js", value_name = "TARGET", help_heading = "Options")]
|
|
578
|
+
pub target: String,
|
|
579
|
+
/// `--format esm` only (one ES module per file).
|
|
580
|
+
#[arg(long, default_value = "esm", value_name = "FORMAT", help_heading = "Options")]
|
|
581
|
+
pub format: String,
|
|
582
|
+
/// Keep relative `.tish` specifiers and bare packages verbatim so Vite's resolveId/load re-enters the plugin per module (in-graph HMR). Omit for disk-style `.tish`->`.js` rewriting.
|
|
583
|
+
#[arg(long, help_heading = "Options")]
|
|
584
|
+
pub vite_dev: bool,
|
|
585
|
+
/// Emit a v3 source map back to the `.tish` source as a `{ "js", "map" }` JSON envelope on stdout (on by default; implies no optimization).
|
|
586
|
+
#[arg(long, help_heading = "Options")]
|
|
587
|
+
pub source_map: bool,
|
|
588
|
+
/// Disable the source map; print raw ES module JS to stdout instead of the JSON envelope.
|
|
589
|
+
#[arg(long = "no-source-map", help_heading = "Options")]
|
|
590
|
+
pub no_source_map: bool,
|
|
591
|
+
/// Project root for resolving bare specifiers / `node_modules` (defaults to the file's parent).
|
|
592
|
+
#[arg(long, value_name = "DIR", help_heading = "Options")]
|
|
593
|
+
pub project_root: Option<String>,
|
|
594
|
+
/// Disable AST optimizations (forced on when a source map is emitted).
|
|
595
|
+
#[arg(long, help_heading = "Options")]
|
|
596
|
+
pub no_optimize: bool,
|
|
597
|
+
/// Entry `.tish` file to compile (only this file is read; dependencies are left to the bundler).
|
|
598
|
+
#[arg(required = true, value_name = "FILE", help_heading = "Arguments")]
|
|
599
|
+
pub file: String,
|
|
600
|
+
}
|
|
601
|
+
|
|
571
602
|
#[derive(Subcommand)]
|
|
572
603
|
pub(crate) enum Commands {
|
|
573
604
|
/// Run a Tish file (interpret)
|
|
@@ -576,6 +607,9 @@ pub(crate) enum Commands {
|
|
|
576
607
|
Repl(ReplArgs),
|
|
577
608
|
/// Build native binary, wasm, wasi, or JavaScript output
|
|
578
609
|
Build(BuildArgs),
|
|
610
|
+
/// Compile a single `.tish` module to one ES module (for Vite dev / HMR); prints to stdout
|
|
611
|
+
#[command(name = "compile-module")]
|
|
612
|
+
CompileModule(CompileModuleArgs),
|
|
579
613
|
/// Parse and dump AST
|
|
580
614
|
#[command(name = "dump-ast")]
|
|
581
615
|
DumpAst {
|
package/crates/tish/src/main.rs
CHANGED
|
@@ -22,7 +22,7 @@ use tishlang_core::VmRef;
|
|
|
22
22
|
use clap::FromArgMatches;
|
|
23
23
|
use rustyline::{Behavior, ColorMode, CompletionType, Config, Editor};
|
|
24
24
|
|
|
25
|
-
use cli_help::{Cli, Commands};
|
|
25
|
+
use cli_help::{Cli, Commands, CompileModuleArgs};
|
|
26
26
|
|
|
27
27
|
/// Normalize `--feature` / `--feature http,timers,fs` / `--feature full` for VM runs and native builds.
|
|
28
28
|
fn normalize_capability_flags(features: &[String]) -> HashSet<String> {
|
|
@@ -73,7 +73,7 @@ fn native_build_features_from_cli(cli_features: &[String]) -> Vec<String> {
|
|
|
73
73
|
fn argv_with_implicit_run(mut argv: Vec<String>) -> Vec<String> {
|
|
74
74
|
if argv.len() >= 2 {
|
|
75
75
|
let first = argv[1].as_str();
|
|
76
|
-
const SUBCOMMANDS: &[&str] = &["run", "repl", "build", "dump-ast"];
|
|
76
|
+
const SUBCOMMANDS: &[&str] = &["run", "repl", "build", "compile-module", "dump-ast"];
|
|
77
77
|
let looks_like_file = !first.starts_with('-') && !SUBCOMMANDS.contains(&first);
|
|
78
78
|
if looks_like_file {
|
|
79
79
|
argv.insert(1, "run".to_string());
|
|
@@ -136,8 +136,10 @@ fn main() {
|
|
|
136
136
|
a.source_map,
|
|
137
137
|
a.ios_triple.as_deref(),
|
|
138
138
|
&a.crate_type,
|
|
139
|
+
&a.format,
|
|
139
140
|
)
|
|
140
141
|
}
|
|
142
|
+
Some(Commands::CompileModule(a)) => compile_module(&a),
|
|
141
143
|
Some(Commands::DumpAst {
|
|
142
144
|
file,
|
|
143
145
|
ignore_indent,
|
|
@@ -541,7 +543,14 @@ fn compile_to_js(
|
|
|
541
543
|
output_path: &str,
|
|
542
544
|
optimize: bool,
|
|
543
545
|
source_map: bool,
|
|
546
|
+
format: &str,
|
|
544
547
|
) -> Result<(), String> {
|
|
548
|
+
if format != "bundle" && format != "esm" {
|
|
549
|
+
return Err(format!(
|
|
550
|
+
"Unknown --format '{}' for --target js. Use 'bundle' (single merged file) or 'esm' (one file per module).",
|
|
551
|
+
format
|
|
552
|
+
));
|
|
553
|
+
}
|
|
545
554
|
if source_map && optimize {
|
|
546
555
|
return Err(
|
|
547
556
|
"tish build --target js --source-map requires --no-optimize (mappings follow unmerged statement order)."
|
|
@@ -564,6 +573,9 @@ fn compile_to_js(
|
|
|
564
573
|
Some(p)
|
|
565
574
|
}
|
|
566
575
|
});
|
|
576
|
+
if format == "esm" {
|
|
577
|
+
return compile_to_js_esm(input_path, output_path, optimize, source_map, project_root);
|
|
578
|
+
}
|
|
567
579
|
let out_path = Path::new(output_path);
|
|
568
580
|
let out_path = if out_path.extension().is_none()
|
|
569
581
|
|| out_path.extension() == Some(std::ffi::OsStr::new(""))
|
|
@@ -635,6 +647,128 @@ fn compile_to_js(
|
|
|
635
647
|
Ok(())
|
|
636
648
|
}
|
|
637
649
|
|
|
650
|
+
/// `--target js --format esm`: emit one `.js` per `.tish` module under the output directory,
|
|
651
|
+
/// preserving the source tree layout, with real ES `import`/`export` so a bundler (Vite/Rollup)
|
|
652
|
+
/// can tree-shake and code-split. `-o` is treated as a directory.
|
|
653
|
+
fn compile_to_js_esm(
|
|
654
|
+
input_path: &Path,
|
|
655
|
+
output_path: &str,
|
|
656
|
+
optimize: bool,
|
|
657
|
+
source_map: bool,
|
|
658
|
+
project_root: Option<&Path>,
|
|
659
|
+
) -> Result<(), String> {
|
|
660
|
+
if source_map {
|
|
661
|
+
return Err(
|
|
662
|
+
"tish build --target js --format esm does not yet support --source-map; use --format bundle for source maps."
|
|
663
|
+
.into(),
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
if input_path.extension().map(|e| e == "jsx") == Some(true)
|
|
667
|
+
|| input_path.extension().map(|e| e == "js") == Some(true)
|
|
668
|
+
{
|
|
669
|
+
return Err(
|
|
670
|
+
"tish build --target js --format esm is only supported for .tish entry files; use --format bundle for .jsx / .js inputs."
|
|
671
|
+
.into(),
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
let modules = tishlang_compile_js::compile_project_esm(input_path, project_root, optimize)
|
|
675
|
+
.map_err(|e| format!("{}", e))?;
|
|
676
|
+
let out_dir = Path::new(output_path);
|
|
677
|
+
fs::create_dir_all(out_dir)
|
|
678
|
+
.map_err(|e| format!("Cannot create output directory {}: {}", out_dir.display(), e))?;
|
|
679
|
+
for module in &modules {
|
|
680
|
+
let out = out_dir.join(&module.relative_path);
|
|
681
|
+
if let Some(parent) = out.parent() {
|
|
682
|
+
fs::create_dir_all(parent)
|
|
683
|
+
.map_err(|e| format!("Cannot create output directory {}: {}", parent.display(), e))?;
|
|
684
|
+
}
|
|
685
|
+
fs::write(&out, &module.js)
|
|
686
|
+
.map_err(|e| format!("Cannot write {}: {}", out.display(), e))?;
|
|
687
|
+
}
|
|
688
|
+
println!("Built {} module(s) to {}", modules.len(), out_dir.display());
|
|
689
|
+
// The entry may sit in a subtree (the output is rooted at the directory common to every module,
|
|
690
|
+
// which can be an ancestor of the entry when deps live in sibling packages / node_modules), so
|
|
691
|
+
// point the user at the actual entry `.js` they should hand to a bundler.
|
|
692
|
+
let entry_js = input_path
|
|
693
|
+
.canonicalize()
|
|
694
|
+
.unwrap_or_else(|_| input_path.to_path_buf())
|
|
695
|
+
.with_extension("js");
|
|
696
|
+
if let Some(entry_rel) = modules
|
|
697
|
+
.iter()
|
|
698
|
+
.filter(|m| entry_js.ends_with(&m.relative_path))
|
|
699
|
+
.max_by_key(|m| m.relative_path.components().count())
|
|
700
|
+
.map(|m| m.relative_path.clone())
|
|
701
|
+
{
|
|
702
|
+
println!("Entry: {}", out_dir.join(entry_rel).display());
|
|
703
|
+
}
|
|
704
|
+
Ok(())
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/// `tish compile-module FILE` (#284): compile a single `.tish` file to one ES module and print it
|
|
708
|
+
/// to stdout. This is the fast, in-graph path the Vite plugin shells out to in `load()` — only the
|
|
709
|
+
/// one file is read (no dependency graph resolution), so Vite owns the module graph and can HMR a
|
|
710
|
+
/// leaf module without a full reload.
|
|
711
|
+
///
|
|
712
|
+
/// With a source map (default), stdout is a JSON envelope `{"js":…,"map":…}` so the plugin gets
|
|
713
|
+
/// both artifacts from one spawn; with `--no-source-map`, stdout is raw ES module JS.
|
|
714
|
+
fn compile_module(args: &CompileModuleArgs) -> Result<(), String> {
|
|
715
|
+
if args.target != "js" {
|
|
716
|
+
return Err(format!(
|
|
717
|
+
"tish compile-module only supports --target js (got '{}').",
|
|
718
|
+
args.target
|
|
719
|
+
));
|
|
720
|
+
}
|
|
721
|
+
if args.format != "esm" {
|
|
722
|
+
return Err(format!(
|
|
723
|
+
"tish compile-module only supports --format esm (got '{}').",
|
|
724
|
+
args.format
|
|
725
|
+
));
|
|
726
|
+
}
|
|
727
|
+
let input_path = Path::new(&args.file)
|
|
728
|
+
.canonicalize()
|
|
729
|
+
.map_err(|e| format!("Cannot resolve {}: {}", args.file, e))?;
|
|
730
|
+
if input_path.extension().map(|e| e != "tish").unwrap_or(true) {
|
|
731
|
+
return Err(format!(
|
|
732
|
+
"tish compile-module expects a .tish file (got '{}').",
|
|
733
|
+
args.file
|
|
734
|
+
));
|
|
735
|
+
}
|
|
736
|
+
let want_map = !args.no_source_map;
|
|
737
|
+
// A source map needs unmerged statement order, so it forces no optimization (same rule as
|
|
738
|
+
// `build --target js --source-map`).
|
|
739
|
+
let optimize = !args.no_optimize && !want_map;
|
|
740
|
+
let import_rewrite = if args.vite_dev {
|
|
741
|
+
tishlang_compile_js::ImportRewrite::ViteDev
|
|
742
|
+
} else {
|
|
743
|
+
tishlang_compile_js::ImportRewrite::Disk
|
|
744
|
+
};
|
|
745
|
+
let project_root = args
|
|
746
|
+
.project_root
|
|
747
|
+
.as_ref()
|
|
748
|
+
.map(PathBuf::from)
|
|
749
|
+
.or_else(|| input_path.parent().map(Path::to_path_buf));
|
|
750
|
+
let bundle = tishlang_compile_js::compile_module_esm(
|
|
751
|
+
&input_path,
|
|
752
|
+
project_root.as_deref(),
|
|
753
|
+
optimize,
|
|
754
|
+
import_rewrite,
|
|
755
|
+
want_map,
|
|
756
|
+
)
|
|
757
|
+
.map_err(|e| format!("{}", e))?;
|
|
758
|
+
|
|
759
|
+
if let Some(map_json) = bundle.source_map_json {
|
|
760
|
+
let map: serde_json::Value = serde_json::from_str(&map_json)
|
|
761
|
+
.map_err(|e| format!("Internal: source map is not valid JSON: {}", e))?;
|
|
762
|
+
let envelope = serde_json::json!({ "js": bundle.js, "map": map });
|
|
763
|
+
let line = serde_json::to_string(&envelope)
|
|
764
|
+
.map_err(|e| format!("Cannot serialize compile-module output: {}", e))?;
|
|
765
|
+
println!("{}", line);
|
|
766
|
+
} else {
|
|
767
|
+
print!("{}", bundle.js);
|
|
768
|
+
}
|
|
769
|
+
Ok(())
|
|
770
|
+
}
|
|
771
|
+
|
|
638
772
|
#[allow(clippy::vec_init_then_push, clippy::too_many_arguments)] // build_file maps CLI build flags 1:1
|
|
639
773
|
fn build_file(
|
|
640
774
|
input_path: &str,
|
|
@@ -646,6 +780,7 @@ fn build_file(
|
|
|
646
780
|
source_map: bool,
|
|
647
781
|
ios_triple: Option<&str>,
|
|
648
782
|
crate_type: &str,
|
|
783
|
+
format: &str,
|
|
649
784
|
) -> Result<(), String> {
|
|
650
785
|
let optimize = !no_optimize;
|
|
651
786
|
let input_path = Path::new(input_path)
|
|
@@ -655,7 +790,7 @@ fn build_file(
|
|
|
655
790
|
let is_js = input_path.extension().map(|e| e == "js") == Some(true);
|
|
656
791
|
|
|
657
792
|
if target == "js" {
|
|
658
|
-
return compile_to_js(&input_path, output_path, optimize, source_map);
|
|
793
|
+
return compile_to_js(&input_path, output_path, optimize, source_map, format);
|
|
659
794
|
}
|
|
660
795
|
|
|
661
796
|
// `wasm-gpu` (#277): same pipeline as `wasm` but builds the `--features gpu` WebGPU runtime and
|
|
@@ -673,6 +673,179 @@ fn test_module_private_binding_isolation() {
|
|
|
673
673
|
}
|
|
674
674
|
}
|
|
675
675
|
|
|
676
|
+
/// #282: `--target js --format esm` emits one ES module per `.tish` file with real
|
|
677
|
+
/// `import`/`export`, so two modules exporting the same name (`export fn activate`) keep separate
|
|
678
|
+
/// scopes instead of colliding. The merge-based pipeline (interp/VM/native and `--format bundle`)
|
|
679
|
+
/// still flattens both into one scope — a separately-tracked collision — so this test pins the ESM
|
|
680
|
+
/// JS target, which is the path that isolates them. Builds the module tree and runs it under Node.
|
|
681
|
+
#[test]
|
|
682
|
+
fn test_js_esm_export_collision() {
|
|
683
|
+
let bin = tish_bin();
|
|
684
|
+
if !bin.exists() {
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
let main = workspace_root()
|
|
688
|
+
.join("tests")
|
|
689
|
+
.join("modules")
|
|
690
|
+
.join("esm")
|
|
691
|
+
.join("export_collision")
|
|
692
|
+
.join("main.tish");
|
|
693
|
+
if !main.exists() {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
let expected = "ab\n";
|
|
697
|
+
|
|
698
|
+
// JS ESM target: build the module tree, then load + run the entry under Node.
|
|
699
|
+
let node_available = Command::new("node")
|
|
700
|
+
.arg("--version")
|
|
701
|
+
.output()
|
|
702
|
+
.map(|o| o.status.success())
|
|
703
|
+
.unwrap_or(false);
|
|
704
|
+
if node_available {
|
|
705
|
+
let tmp = tempfile::tempdir().expect("tempdir");
|
|
706
|
+
let out_dir = tmp.path().join("esm_out");
|
|
707
|
+
let build = Command::new(&bin)
|
|
708
|
+
.args(["build"])
|
|
709
|
+
.arg(&main)
|
|
710
|
+
.args(["-o"])
|
|
711
|
+
.arg(&out_dir)
|
|
712
|
+
.args(["--target", "js", "--format", "esm"])
|
|
713
|
+
.current_dir(workspace_root())
|
|
714
|
+
.output()
|
|
715
|
+
.expect("build esm");
|
|
716
|
+
assert!(
|
|
717
|
+
build.status.success(),
|
|
718
|
+
"esm build failed: stderr={}",
|
|
719
|
+
String::from_utf8_lossy(&build.stderr)
|
|
720
|
+
);
|
|
721
|
+
let entry = out_dir.join("main.js");
|
|
722
|
+
assert!(entry.exists(), "esm entry {} not emitted", entry.display());
|
|
723
|
+
let out = Command::new("node")
|
|
724
|
+
.arg(&entry)
|
|
725
|
+
.current_dir(workspace_root())
|
|
726
|
+
.output()
|
|
727
|
+
.expect("run node");
|
|
728
|
+
assert!(
|
|
729
|
+
out.status.success(),
|
|
730
|
+
"esm JS run failed (collision not isolated?): stderr={}",
|
|
731
|
+
String::from_utf8_lossy(&out.stderr)
|
|
732
|
+
);
|
|
733
|
+
assert_eq!(
|
|
734
|
+
String::from_utf8_lossy(&out.stdout),
|
|
735
|
+
expected,
|
|
736
|
+
"esm export_collision mismatch on JS ESM target"
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/// #284: `tish compile-module --vite-dev --source-map` compiles a single `.tish` file (no graph
|
|
742
|
+
/// resolution) to one ES module, keeps relative `.tish` specifiers, and emits a `{ js, map }` JSON
|
|
743
|
+
/// envelope whose map references the `.tish` source. This is the in-graph path the Vite plugin uses.
|
|
744
|
+
#[test]
|
|
745
|
+
fn test_compile_module_vite_dev_with_source_map() {
|
|
746
|
+
let bin = tish_bin();
|
|
747
|
+
if !bin.exists() {
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
let main = workspace_root()
|
|
751
|
+
.join("tests")
|
|
752
|
+
.join("modules")
|
|
753
|
+
.join("esm")
|
|
754
|
+
.join("vite_hmr")
|
|
755
|
+
.join("main.tish");
|
|
756
|
+
if !main.exists() {
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
let out = Command::new(&bin)
|
|
760
|
+
.args(["compile-module"])
|
|
761
|
+
.arg(&main)
|
|
762
|
+
.args([
|
|
763
|
+
"--target",
|
|
764
|
+
"js",
|
|
765
|
+
"--format",
|
|
766
|
+
"esm",
|
|
767
|
+
"--vite-dev",
|
|
768
|
+
"--source-map",
|
|
769
|
+
])
|
|
770
|
+
.current_dir(workspace_root())
|
|
771
|
+
.output()
|
|
772
|
+
.expect("run compile-module");
|
|
773
|
+
assert!(
|
|
774
|
+
out.status.success(),
|
|
775
|
+
"compile-module failed: stderr={}",
|
|
776
|
+
String::from_utf8_lossy(&out.stderr)
|
|
777
|
+
);
|
|
778
|
+
let stdout = String::from_utf8_lossy(&out.stdout);
|
|
779
|
+
let parsed: serde_json::Value =
|
|
780
|
+
serde_json::from_str(&stdout).unwrap_or_else(|e| panic!("stdout is not JSON: {e}\n{stdout}"));
|
|
781
|
+
let js = parsed["js"].as_str().expect("envelope has string `js`");
|
|
782
|
+
assert!(
|
|
783
|
+
js.contains("import { makeCounter } from \"./counter.tish\";"),
|
|
784
|
+
"Vite-dev keeps the .tish specifier:\n{js}"
|
|
785
|
+
);
|
|
786
|
+
assert!(
|
|
787
|
+
!js.contains("./counter.js"),
|
|
788
|
+
"Vite-dev must not rewrite .tish to .js:\n{js}"
|
|
789
|
+
);
|
|
790
|
+
let map = &parsed["map"];
|
|
791
|
+
assert!(map.is_object(), "envelope has object `map`: {map}");
|
|
792
|
+
assert_eq!(map["version"], serde_json::json!(3), "v3 source map");
|
|
793
|
+
let sources = map["sources"].as_array().expect("map has sources");
|
|
794
|
+
assert!(
|
|
795
|
+
sources
|
|
796
|
+
.iter()
|
|
797
|
+
.any(|s| s.as_str().map(|s| s.contains("main.tish")).unwrap_or(false)),
|
|
798
|
+
"map sources reference the .tish file: {sources:?}"
|
|
799
|
+
);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/// #284: without `--source-map`, `compile-module` prints raw ES module JS to stdout (no envelope),
|
|
803
|
+
/// so the output can be consumed directly.
|
|
804
|
+
#[test]
|
|
805
|
+
fn test_compile_module_no_source_map_prints_raw_js() {
|
|
806
|
+
let bin = tish_bin();
|
|
807
|
+
if !bin.exists() {
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
let counter = workspace_root()
|
|
811
|
+
.join("tests")
|
|
812
|
+
.join("modules")
|
|
813
|
+
.join("esm")
|
|
814
|
+
.join("vite_hmr")
|
|
815
|
+
.join("counter.tish");
|
|
816
|
+
if !counter.exists() {
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
let out = Command::new(&bin)
|
|
820
|
+
.args(["compile-module"])
|
|
821
|
+
.arg(&counter)
|
|
822
|
+
.args([
|
|
823
|
+
"--target",
|
|
824
|
+
"js",
|
|
825
|
+
"--format",
|
|
826
|
+
"esm",
|
|
827
|
+
"--vite-dev",
|
|
828
|
+
"--no-source-map",
|
|
829
|
+
])
|
|
830
|
+
.current_dir(workspace_root())
|
|
831
|
+
.output()
|
|
832
|
+
.expect("run compile-module");
|
|
833
|
+
assert!(
|
|
834
|
+
out.status.success(),
|
|
835
|
+
"compile-module failed: stderr={}",
|
|
836
|
+
String::from_utf8_lossy(&out.stderr)
|
|
837
|
+
);
|
|
838
|
+
let stdout = String::from_utf8_lossy(&out.stdout);
|
|
839
|
+
assert!(
|
|
840
|
+
stdout.contains("export function makeCounter"),
|
|
841
|
+
"raw JS output includes the export:\n{stdout}"
|
|
842
|
+
);
|
|
843
|
+
assert!(
|
|
844
|
+
!stdout.trim_start().starts_with('{'),
|
|
845
|
+
"no-source-map output is raw JS, not a JSON envelope:\n{stdout}"
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
|
|
676
849
|
/// Ignored: tishlang_eval::run() does not run the event loop.
|
|
677
850
|
#[test]
|
|
678
851
|
#[cfg(feature = "http")]
|