Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
12636c9
Initial test setup
nojaf Sep 8, 2025
f676219
Undo unwanted stuff
nojaf Sep 8, 2025
adb9be1
biome...
nojaf Sep 8, 2025
9087b0d
Add deno sample
nojaf Sep 9, 2025
da9cfe2
Compile again
nojaf Sep 9, 2025
de5f9d8
Add webapi to deno sample
nojaf Sep 9, 2025
9e8ea3f
Failing tests, runtime not found.
nojaf Sep 9, 2025
687c2f1
ignore history folder
nojaf Sep 9, 2025
0bd0836
Add -runtime-path
nojaf Sep 9, 2025
793db89
Resolve runtime via helper
nojaf Sep 9, 2025
b83ead1
Remove -nostdlib from test projects
nojaf Sep 9, 2025
0428735
Pass rescript runtime as environment variable
nojaf Sep 9, 2025
37ab3ea
Consume RESCRIPT_RUNTIME
nojaf Sep 9, 2025
babaf7d
Test projects now consume rescript/runtime
nojaf Sep 9, 2025
d940ecc
Add RESCRIPT_RUNTIME to bash tests
nojaf Sep 9, 2025
7d07617
Use actual deno to invoke tests
nojaf Sep 9, 2025
d7d2ebe
Add resolvePackageRoot to artifacts file
nojaf Sep 9, 2025
8199554
Don't run tests in parallel because of git index conflicts
nojaf Sep 9, 2025
042c0a2
Comment out failing test for now
nojaf Sep 11, 2025
3bf69bc
Include runtime argument in bsb.js
nojaf Sep 11, 2025
07a3303
ignore all lib/bs
nojaf Sep 11, 2025
7a43f22
Add runtime as well to bsc.js
nojaf Sep 11, 2025
575527b
Add -runtime-path to bsb as well.
nojaf Sep 11, 2025
66c22b1
Renamed artifact
nojaf Sep 11, 2025
0c01e92
Remove new rewatch tests
nojaf Sep 12, 2025
ba79149
Revert yarn.lock
nojaf Sep 12, 2025
1287dd3
Add changelog entry
nojaf Sep 12, 2025
88b0483
Merge branch 'master' into moar-rewatch-tests
nojaf Sep 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@

- Playground: Add config options for experimental features and jsx preserve mode. https://github.com/rescript-lang/rescript/pull/7865
- Clean up tests. https://github.com/rescript-lang/rescript/pull/7861 https://github.com/rescript-lang/rescript/pull/7871
- Add `-runtime-path` flag to `bsc` (and `bsb`), we are detecting the location of `@rescript/runtime` in `cli/rescript.js` based on runtime module resolution. https://github.com/rescript-lang/rescript/pull/7858

# 12.0.0-beta.10

Expand Down
2 changes: 1 addition & 1 deletion analysis/src/ModuleResolution.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ let ( /+ ) = Filename.concat
let rec resolveNodeModulePath ~startPath name =
if name = "@rescript/runtime" then
(* Hack: we need a reliable way to resolve modules in monorepos. *)
Some Config.runtime_module_path
Some !Runtime_package.path
else
let scope = Filename.dirname name in
let name = Filename.basename name in
Expand Down
4 changes: 3 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@
"*.d.ts",
"*.exe",
"package.json",
"packages/artifacts.json"
"packages/artifacts.json",
".mypy_cache/**",
".history/**"
]
}
}
4 changes: 4 additions & 0 deletions cli/bsc.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
import { execFileSync } from "node:child_process";

import { bsc_exe } from "./common/bins.js";
import { runtimePath } from "./common/runtime.js";

const delegate_args = process.argv.slice(2);
if (!delegate_args.includes("-runtime-path")) {
delegate_args.push("-runtime-path", runtimePath);
}

try {
execFileSync(bsc_exe, delegate_args, { stdio: "inherit" });
Expand Down
6 changes: 6 additions & 0 deletions cli/common/bsb.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { createServer } from "node:http";
import * as os from "node:os";
import * as path from "node:path";

import { runtimePath } from "../common/runtime.js";
import { rescript_legacy_exe } from "./bins.js";
import { WebSocket } from "./minisocket.js";

Expand Down Expand Up @@ -49,6 +50,11 @@ function acquireBuild(args, options) {
if (ownerProcess) {
return null;
}

if (args[0] === "build" && !args.includes("-runtime-path")) {
args.push("-runtime-path", runtimePath);
}

try {
ownerProcess = child_process.spawn(rescript_legacy_exe, args, {
stdio: "inherit",
Expand Down
78 changes: 78 additions & 0 deletions cli/common/runtime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { promises as fs } from "node:fs";
import { createRequire } from "node:module";
import path from "node:path";
import { fileURLToPath } from "node:url";

/**
* 🚨 Why this hack exists:
*
* Unlike Node or Bun, Deno's `import.meta.resolve("npm:...")` does NOT return a
* filesystem path. It just echoes back the npm: specifier. The actual package
* tarballs are unpacked into `node_modules/.deno/...` when you use
* `--node-modules-dir`, and normal `node_modules/<pkg>` symlinks only exist for
* *direct* dependencies. Transitive deps (like @rescript/runtime in our case)
* only live inside `.deno/` and have no symlink.
*
* Because Deno doesn't expose an API for “give me the absolute path of this npm
* package”, the only way to emulate Node’s/Bun’s `require.resolve` behaviour is
* to glob inside `.deno/` and reconstruct the path manually.
*
* TL;DR: This function exists to compensate for the fact that Deno deliberately hides its
* npm cache layout. If you want a stable on‑disk path for a package in Deno,
* you have to spelunk `node_modules/.deno/>pkg@version>/node_modules/<pkg>`.
*
* If Deno ever ships a proper API for this, replace this hack immediately.
*/
async function resolvePackageInDeno(pkgName) {
const base = path.resolve("node_modules/.deno");
const pkgId = pkgName.startsWith("@") ? pkgName.replace("/", "+") : pkgName;

const { expandGlob } = await import("https://deno.land/std/fs/mod.ts");
for await (const entry of expandGlob(
path.join(base, `${pkgId}@*/node_modules/${pkgName}`),
)) {
if (entry.isDirectory) {
return await fs.realpath(entry.path);
}
}

throw new Error(
`Could not resolve ${pkgName} in Deno. Did you enable --node-modules-dir?`,
);
}

async function resolvePackageRoot(pkgName) {
const specifier =
typeof globalThis.Deno !== "undefined"
? `npm:${pkgName}/package.json`
: `${pkgName}/package.json`;

if (typeof import.meta.resolve === "function") {
const url = import.meta.resolve(specifier);

if (url.startsWith("file://")) {
// Node & Bun: real local file
const abs = path.dirname(fileURLToPath(url));
return await fs.realpath(abs);
}

if (typeof globalThis.Deno !== "undefined") {
return await resolvePackageInDeno(pkgName);
}

throw new Error(
`Could not resolve ${pkgName} (no physical path available)`,
);
}

// Node fallback
const require = createRequire(import.meta.url);
try {
const abs = path.dirname(require.resolve(`${pkgName}/package.json`));
return await fs.realpath(abs);
} catch {
throw new Error(`Could not resolve ${pkgName} in Node runtime`);
}
}

export const runtimePath = await resolvePackageRoot("@rescript/runtime");
2 changes: 2 additions & 0 deletions cli/rescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import * as child_process from "node:child_process";
import { rescript_exe } from "./common/bins.js";
import { runtimePath } from "./common/runtime.js";

const args = process.argv.slice(2);

Expand All @@ -18,6 +19,7 @@ const args = process.argv.slice(2);
// exit the parent with the correct status only after the child has exited.
const child = child_process.spawn(rescript_exe, args, {
stdio: "inherit",
env: { ...process.env, RESCRIPT_RUNTIME: runtimePath },
});

// Map POSIX signal names to conventional exit status numbers so we can
Expand Down
7 changes: 5 additions & 2 deletions compiler/bsb/bsb_arg.ml
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,11 @@ let usage_b (buf : Ext_buffer.t) ~usage (speclist : t) =
else (
buf +> "\nOptions:\n";
let max_col = ref 0 in
Ext_array.iter speclist (fun (key, _, _) ->
if String.length key > !max_col then max_col := String.length key);
Ext_array.iter speclist (fun (key, _, doc) ->
if
(not (Ext_string.starts_with doc "*internal*"))
&& String.length key > !max_col
then max_col := String.length key);
Ext_array.iter speclist (fun (key, _, doc) ->
if not (Ext_string.starts_with doc "*internal*") then (
buf +> " ";
Expand Down
2 changes: 1 addition & 1 deletion compiler/bsb/bsb_exception.ml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ let print (fmt : Format.formatter) (x : error) =
modname
| Package_not_found name ->
let name = Bsb_pkg_types.to_string name in
if Ext_string.equal name Bs_version.package_name then
if Ext_string.equal name Runtime_package.name then
Format.fprintf fmt
"File \"rescript.json\", line 1\n\
@{<error>Error:@} package @{<error>%s@} is not found\n\
Expand Down
2 changes: 2 additions & 0 deletions compiler/bsb/bsb_ninja_rule.ml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ let make_custom_rules ~(gentype_config : Bsb_config_types.gentype_config)
string =
Ext_buffer.clear buf;
Ext_buffer.add_string buf bsc;
Ext_buffer.add_string buf (" -runtime-path " ^ !Runtime_package.path);
Ext_buffer.add_string buf ns_flag;
if read_cmi = `yes then Ext_buffer.add_string buf " -bs-read-cmi";
(* The include order matters below *)
Expand Down Expand Up @@ -139,6 +140,7 @@ let make_custom_rules ~(gentype_config : Bsb_config_types.gentype_config)
let mk_ast =
Ext_buffer.clear buf;
Ext_buffer.add_string buf bsc;
Ext_buffer.add_string buf (" -runtime-path " ^ !Runtime_package.path);
Ext_buffer.add_char_string buf ' ' warnings;
(match ppx_files with
| [] -> ()
Expand Down
5 changes: 5 additions & 0 deletions compiler/bsb_exe/rescript_main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ let install_target () =
let eid = Bsb_unix.run_command_execv install_command in
if eid <> 0 then Bsb_unix.command_fatal_error install_command eid

let setup_runtime_path path = Runtime_package.path := path

let build_subcommand ~start argv argv_len =
let i = Ext_array.rfind_with_index argv Ext_string.equal separator in

Expand Down Expand Up @@ -141,6 +143,9 @@ let build_subcommand ~start argv argv_len =
unit_set_spec no_deps_mode,
"*internal* Needed for watcher to build without dependencies on file \
change" );
( "-runtime-path",
string_call setup_runtime_path,
"*internal* Set the path of the runtime package (@rescript/runtime)" );
( "-warn-error",
string_call (fun s -> warning_as_error := Some s),
"Warning numbers and whether to turn them into errors, e.g., \
Expand Down
5 changes: 5 additions & 0 deletions compiler/bsc/rescript_compiler_main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ let set_abs_input_name sourcefile =
sourcefile
let setup_outcome_printer () = Lazy.force Res_outcome_printer.setup

let setup_runtime_path path = Runtime_package.path := path

let process_file sourcefile ?kind ppf =
(* This is a better default then "", it will be changed later
The {!Location.input_name} relies on that we write the binary ast
Expand Down Expand Up @@ -394,6 +396,9 @@ let command_line_flags : (string * Bsc_args.spec * string) array =
( "-unsafe",
set Clflags.fast,
"*internal* Do not compile bounds checking on array and string access" );
( "-runtime-path",
string_call setup_runtime_path,
"*internal* Set the path of the runtime package (@rescript/runtime)" );
( "-warn-help",
unit_call Warnings.help_warnings,
"Show description of warning numbers" );
Expand Down
1 change: 0 additions & 1 deletion compiler/common/bs_version.ml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,3 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
let version = "12.0.0-beta.11"
let header = "// Generated by ReScript, PLEASE EDIT WITH CARE"
let package_name = "@rescript/runtime"
2 changes: 0 additions & 2 deletions compiler/common/bs_version.mli
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,3 @@
val version : string

val header : string

val package_name : string
2 changes: 1 addition & 1 deletion compiler/core/js_name_of_module_id.ml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ let get_runtime_module_path
(*Invariant: the package path to rescript, it is used to
calculate relative js path
*)
(Config.runtime_module_path // dep_path // js_file)
(!Runtime_package.path // dep_path // js_file)

(* [output_dir] is decided by the command line argument *)
let string_of_module_id
Expand Down
4 changes: 2 additions & 2 deletions compiler/core/js_packages_info.ml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ let runtime_dir_of_module_system (ms : module_system) =
| Esmodule | Es6_global -> "es6"

let runtime_package_path (ms : module_system) js_file =
Bs_version.package_name // "lib" // runtime_dir_of_module_system ms // js_file
Runtime_package.name // "lib" // runtime_dir_of_module_system ms // js_file

type t = {name: package_name; module_systems: package_info list}

Expand Down Expand Up @@ -163,7 +163,7 @@ let query_package_infos ({name; module_systems} : t)
with
| Some k ->
let rel_path = k.path in
let pkg_rel_path = Bs_version.package_name // rel_path in
let pkg_rel_path = Runtime_package.name // rel_path in
Package_found {rel_path; pkg_rel_path; suffix = k.suffix}
| None -> Package_not_found)

Expand Down
10 changes: 6 additions & 4 deletions compiler/core/res_compmisc.ml
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

let init_path () =
let dirs = !Clflags.include_dirs in
let exp_dirs =
List.map (Misc.expand_directory Config.standard_library) dirs
let stdlib_dir =
let ( // ) = Filename.concat in
!Runtime_package.path // "lib" // "ocaml"
in
let dirs = !Clflags.include_dirs in
let exp_dirs = List.map (Misc.expand_directory stdlib_dir) dirs in
Config.load_path :=
if !Js_config.no_stdlib then exp_dirs
else List.rev_append exp_dirs [Config.standard_library];
else List.rev_append exp_dirs [stdlib_dir];
Env.reset_cache ()

(* Return the initial environment in which compilation proceeds. *)
Expand Down
37 changes: 0 additions & 37 deletions compiler/ext/config.ml
Original file line number Diff line number Diff line change
@@ -1,40 +1,3 @@
(* This resolves the location of the standard library starting from the location of bsc.exe
(@rescript/{platform}/bin/bsc.exe), handling different supported package layouts. *)
let runtime_module_path =
let build_path rest path =
String.concat Filename.dir_sep (List.rev_append rest path)
in
match
Sys.executable_name |> Filename.dirname
|> String.split_on_char Filename.dir_sep.[0]
|> List.rev
with
(* 1. Packages installed via pnpm
- bin: node_modules/.pnpm/@[email protected]/node_modules/@rescript/darwin-arm64/bin
- runtime: node_modules/.pnpm/node_modules/@rescript/runtime (symlink)
*)
| "bin" :: _platform :: "@rescript" :: "node_modules" :: _package :: ".pnpm"
:: "node_modules" :: rest ->
build_path rest
["node_modules"; ".pnpm"; "node_modules"; "@rescript"; "runtime"]
(* 2. Packages installed via npm
- bin: node_modules/@rescript/{platform}/bin
- runtime: node_modules/@rescript/runtime
*)
| "bin" :: _platform :: "@rescript" :: "node_modules" :: rest ->
build_path rest ["node_modules"; "@rescript"; "runtime"]
(* 3. Several other cases that can occur in local development, e.g.
- bin: <repo>/packages/@rescript/{platform}/bin, <repo>/_build/install/default/bin
- runtime: <repo>/packages/@rescript/runtime
*)
| _ :: _ :: _ :: _ :: rest ->
build_path rest ["packages"; "@rescript"; "runtime"]
| _ -> ""

let standard_library =
let ( // ) = Filename.concat in
runtime_module_path // "lib" // "ocaml"

let cmi_magic_number = "Caml1999I022"

and ast_impl_magic_number = "Caml1999M022"
Expand Down
6 changes: 0 additions & 6 deletions compiler/ext/config.mli
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,6 @@

(* System configuration *)

(* The directory containing the runtime module (@rescript/runtime) *)
val runtime_module_path : string

(* The directory containing the runtime artifacts (@rescript/runtime/lib/ocaml) *)
val standard_library : string

(* Directories in the search path for .cmi and .cmo files *)
val load_path : string list ref

Expand Down
29 changes: 29 additions & 0 deletions compiler/ext/runtime_package.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
let name = "@rescript/runtime"

(* Simple default approach to find the runtime package path. This will not work with all package managers/layouts. *)
let default_path =
let build_path rest path =
String.concat Filename.dir_sep (List.rev_append rest path)
in
match
Sys.executable_name |> Filename.dirname
|> String.split_on_char Filename.dir_sep.[0]
|> List.rev
with
(* 1. Packages installed via npm
- bin: node_modules/@rescript/{platform}/bin
- runtime: node_modules/@rescript/runtime
*)
| "bin" :: _platform :: "@rescript" :: "node_modules" :: rest ->
build_path rest ["node_modules"; "@rescript"; "runtime"]
(* 2. Several other cases that can occur in local development, e.g.
- bin: <repo>/packages/@rescript/{platform}/bin, <repo>/_build/install/default/bin
- runtime: <repo>/packages/@rescript/runtime
*)
| _ :: _ :: _ :: _ :: rest ->
build_path rest ["packages"; "@rescript"; "runtime"]
| _ -> ""

(* To support pnpm and other package managers/layouts, we determine the path on the JS side and pass it in
via -runtime-path to override the default. *)
let path = ref default_path
2 changes: 2 additions & 0 deletions compiler/ext/runtime_package.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
val name : string
val path : string ref
1 change: 1 addition & 0 deletions packages/artifacts.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"cli/common/bins.js",
"cli/common/bsb.js",
"cli/common/minisocket.js",
"cli/common/runtime.js",
"cli/rescript-legacy.js",
"cli/rescript-legacy/dump.js",
"cli/rescript-legacy/format.js",
Expand Down
2 changes: 1 addition & 1 deletion rewatch/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ pub fn get_compiler_args(rescript_file_path: &Path) -> Result<String> {
&None,
is_type_dev,
true,
);
)?;

let result = serde_json::to_string_pretty(&CompilerArgs {
compiler_args,
Expand Down
Loading
Loading