Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
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 @@ -31,6 +31,7 @@
#### :nail_care: Polish

- Add (dev-)dependencies to build schema. https://github.com/rescript-lang/rescript/pull/7892
- Rewatch: Traverse upwards for package resolution in single context projects. https://github.com/rescript-lang/rescript/pull/7896

#### :house: Internal

Expand Down
71 changes: 71 additions & 0 deletions rewatch/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::fs::File;
use std::io::Read;
use std::io::{self, BufRead};
use std::path::{Component, Path, PathBuf};
use std::sync::{LazyLock, RwLock};
use std::time::{SystemTime, UNIX_EPOCH};

pub type StdErr = String;
Expand All @@ -31,6 +32,25 @@ pub mod emojis {
pub static LINE_CLEAR: &str = "\x1b[2K\r";
}

// Cache existence checks for candidate node_modules paths during upward traversal.
// Keyed by the absolute candidate path string; value is the existence boolean.
static NODE_MODULES_EXIST_CACHE: LazyLock<RwLock<ahash::AHashMap<String, bool>>> =
LazyLock::new(|| RwLock::new(ahash::AHashMap::new()));

fn cached_path_exists(path: &Path) -> bool {
let key = path.to_string_lossy().to_string();
if let Ok(cache) = NODE_MODULES_EXIST_CACHE.read() {
if let Some(exists) = cache.get(&key) {
return *exists;
}
}
let exists = path.exists();
if let Ok(mut cache) = NODE_MODULES_EXIST_CACHE.write() {
cache.insert(key, exists);
}
exists
}

/// This trait is used to strip the verbatim prefix from a Windows path.
/// On non-Windows systems, it simply returns the original path.
/// This is needed until the rescript compiler can handle such paths.
Expand Down Expand Up @@ -154,12 +174,63 @@ pub fn try_package_path(
} else if path_from_root.exists() {
Ok(path_from_root)
} else {
// As a last resort, when we're in a Single project context, traverse upwards
// starting from the parent of the package root (package_config.path.parent().parent())
// and probe each ancestor's node_modules for the dependency. This covers hoisted
// workspace setups when building a package standalone.
if project_context.monorepo_context.is_none() {
match package_config.path.parent().and_then(|p| p.parent()) {
Some(start_dir) => {
return find_dep_in_upward_node_modules(start_dir, package_name);
}
None => {
log::debug!(
"try_package_path: cannot compute start directory for upward traversal from '{}'",
package_config.path.to_string_lossy()
);
}
}
}

Err(anyhow!(
"The package \"{package_name}\" is not found (are node_modules up-to-date?)..."
))
}
}

fn find_dep_in_upward_node_modules(start_dir: &Path, package_name: &str) -> anyhow::Result<PathBuf> {
log::debug!(
"try_package_path: falling back to upward traversal for '{}' starting at '{}'",
package_name,
start_dir.to_string_lossy()
);

let mut current = Some(start_dir);
while let Some(dir) = current {
let candidate = package_path(dir, package_name);
log::debug!("try_package_path: checking '{}'", candidate.to_string_lossy());
if cached_path_exists(&candidate) {
log::debug!(
"try_package_path: found '{}' at '{}' via upward traversal",
package_name,
candidate.to_string_lossy()
);
return Ok(candidate);
}
current = dir.parent();
}
log::debug!(
"try_package_path: no '{}' found during upward traversal from '{}'",
package_name,
start_dir.to_string_lossy()
);
Err(anyhow!(
"try_package_path: upward traversal did not find '{}' starting at '{}'",
package_name,
start_dir.to_string_lossy()
))
}

pub fn get_abs_path(path: &Path) -> PathBuf {
let abs_path_buf = PathBuf::from(path);

Expand Down
9 changes: 5 additions & 4 deletions rewatch/testrepo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"packages/file-casing-no-namespace",
"packages/pure-dev",
"packages/with-ppx",
"packages/nohoist"
"packages/nohoist",
"packages/standalone"
],
"nohoist": [
"rescript-bun"
Expand All @@ -26,11 +27,11 @@
"rescript": "12.0.0-beta.1"
},
"scripts": {
"build": "../target/release/rewatch build .",
"build": "../target/release/rescript build .",
"build:rescript": "rescript legacy build",
"watch": "../target/release/rewatch watch .",
"watch": "../target/release/rescript watch .",
"watch:rescript": "rescript legacy watch",
"clean": "../target/release/rewatch clean .",
"clean": "../target/release/rescript clean .",
"clean:rescript": "rescript clean"
}
}
7 changes: 7 additions & 0 deletions rewatch/testrepo/packages/standalone/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "@testrepo/standalone",
"version": "1.0.0",
"dependencies": {
"@testrepo/dep01": "*"
}
}
13 changes: 13 additions & 0 deletions rewatch/testrepo/packages/standalone/rescript.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "standalone",
"sources": {
"dir": "src",
"subdirs": true
},
"package-specs": {
"module": "es6",
"in-source": true
},
"suffix": ".mjs",
"dependencies": ["@testrepo/dep01"]
}
13 changes: 13 additions & 0 deletions rewatch/testrepo/packages/standalone/src/Standalone.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Generated by ReScript, PLEASE EDIT WITH CARE

import * as Dep01 from "@testrepo/dep01/src/Dep01.mjs";

function standalone() {
Dep01.log();
console.log("standalone");
}

export {
standalone,
}
/* Dep01 Not a pure module */
4 changes: 4 additions & 0 deletions rewatch/testrepo/packages/standalone/src/Standalone.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
let standalone = () => {
Dep01.log()
Js.log("standalone")
}
8 changes: 8 additions & 0 deletions rewatch/testrepo/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,14 @@ __metadata:
languageName: unknown
linkType: soft

"@testrepo/standalone@workspace:packages/standalone":
version: 0.0.0-use.local
resolution: "@testrepo/standalone@workspace:packages/standalone"
dependencies:
"@testrepo/dep01": "npm:*"
languageName: unknown
linkType: soft

"@testrepo/with-dev-deps@workspace:packages/with-dev-deps":
version: 0.0.0-use.local
resolution: "@testrepo/with-dev-deps@workspace:packages/with-dev-deps"
Expand Down
15 changes: 15 additions & 0 deletions rewatch/tests/compile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,21 @@ else
exit 1
fi

# Build from standalone package folder using `rescript build`
bold "Test: Standalone package can build via rescript from package folder"
pushd ./packages/standalone > /dev/null
error_output=$("../../$REWATCH_EXECUTABLE" build 2>&1)
if [ $? -eq 0 ];
then
success "Standalone package built"
else
error "Error building standalone package"
printf "%s\n" "$error_output" >&2
popd > /dev/null
exit 1
fi
popd > /dev/null

node ./packages/main/src/Main.mjs > ./packages/main/src/output.txt

mv ./packages/main/src/Main.res ./packages/main/src/Main2.res
Expand Down
Loading