Proof of concept: one shot file compilation
Use case: I want to compile a single ReScript file and run it immediately.
With this PR I could do rescript compile-file MyFile.res | bun - and bun automatically runs it.
(Would not be a stretch to create a Bun plugin and just be able to run bun run MyFile.res if plugin is configured)
Going to keep this as draft until after v12.
rescript
npm i https://pkg.pr.new/rescript-lang/rescript@8002
@rescript/darwin-arm64
npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/darwin-arm64@8002
@rescript/darwin-x64
npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/darwin-x64@8002
@rescript/linux-arm64
npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/linux-arm64@8002
@rescript/linux-x64
npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/linux-x64@8002
@rescript/runtime
npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/runtime@8002
@rescript/win32-x64
npm i https://pkg.pr.new/rescript-lang/rescript/@rescript/win32-x64@8002
commit: 102f806
Split bsc module system flags for stdout compilation
Problem
When bsc compiles to stdout (no -bs-package-output specified), it hardcoded the Commonjs module system:
(* compiler/core/lam_compile_main.ml:295 - old code *)
if Js_packages_info.is_empty package_info && !Js_config.js_stdout then
Js_dump_program.dump_deps_program ~output_prefix Commonjs (* <- hardcoded! *)
This meant stdout output was always Commonjs, regardless of project configuration. Since the new compile-file command outputs to stdout, it could only generate Commonjs, not ES6 modules.
Solution
Split -bs-package-output into three independent flags:
Before (coupled):
-bs-package-output esmodule:lib/es6:.mjs
# Format: module_system:path:suffix (all three or none)
After (decoupled):
-bs-module-system esmodule # Controls import/export syntax
-bs-suffix .mjs # Controls import path extensions
-bs-package-output lib/es6 # Output directory (optional)
Implementation
New compiler state (compiler/common/js_config.ml):
let default_module_system = ref Ext_module_system.Commonjs
let default_suffix = ref Literals.suffix_js
New flags (compiler/bsc/rescript_compiler_main.ml):
("-bs-module-system", string_call set_module_system, "Set module system: commonjs, esmodule, es6-global");
("-bs-suffix", string_call set_suffix, "Set import file suffix: .js, .mjs, .cjs");
Stdout output now uses configured values (compiler/core/lam_compile_main.ml):
if Js_packages_info.is_empty package_info && !Js_config.js_stdout then
(* Use configured module system instead of hardcoded Commonjs *)
Js_dump_program.dump_deps_program ~output_prefix !Js_config.default_module_system
Import path generation uses configured suffix (compiler/core/js_name_of_module_id.ml):
(* For Package_script mode (stdout), use configured suffix *)
let js_file = Ext_namespace.js_name_of_modulename dep_module_id.id.name case !Js_config.default_suffix
Critical exception: Runtime package imports (@rescript/runtime) always use .js because the runtime is pre-compiled and distributed with .js files:
(* Runtime package is pre-compiled and always uses .js suffix *)
let js_file = Ext_namespace.js_name_of_modulename dep_module_id.id.name Upper Literals.suffix_js
Build System Updates
Rewatch (rewatch/src/build/compile.rs):
// Old format:
"-bs-package-output", "esmodule:lib/es6:.mjs"
// New format:
"-bs-module-system", "esmodule",
"-bs-suffix", ".mjs",
"-bs-package-output", "lib/es6"
Legacy bsb (compiler/bsb/bsb_package_specs.ml):
(* Old: single compound flag *)
"-bs-package-output esmodule:lib/es6:.mjs"
(* New: three separate flags *)
"-bs-module-system esmodule -bs-suffix .mjs -bs-package-output lib/es6"
Backward Compatibility
The -bs-package-output parser still accepts the old format:
module:path:suffix- all three explicit (old format)module:path- uses configured suffixpath- uses configured module system and suffix
This allows gradual migration, though since these are internal flags (users don't call bsc directly), backward compatibility isn't critical.
Result
bsc can now output ES6 modules to stdout:
$ bsc -bs-module-system esmodule -bs-suffix .mjs file.ast
// Generated by ReScript, PLEASE EDIT WITH CARE
import * as MyDep from "./MyDep.mjs";
import * as Belt_Array from "@rescript/runtime/lib/es6/Belt_Array.js";
export { ... }
Notice:
- User module imports:
.mjs(from-bs-suffix) - Runtime imports:
.js(always, because runtime is pre-compiled)
This enables compile-file to respect project configuration when outputting to stdout.
//cc @cristianoc , @zth
Hi @cristianoc , the splitting of the bsc arg turned out quite ugly. To satisfy bsb, a lot of code was generated while it seems a lot more straightforward in Rewatch. Any pointers on what could be improved?