Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
776801c
Be more aware of the workspace during clean
nojaf Aug 2, 2025
dff32e7
Only clean current package
nojaf Aug 2, 2025
119d553
fmt
nojaf Aug 2, 2025
78bfd88
Return is_child from packages make
nojaf Aug 4, 2025
81966e0
Invert if
nojaf Aug 4, 2025
7b84cb4
Introduce PackageMap to make things a bit more clear.
nojaf Aug 4, 2025
178a0ec
Use workspace extension if there is a link, otherwise root_package.
nojaf Aug 7, 2025
5b2e427
Add a TODO that we need to clean up some confusing bits.
nojaf Aug 7, 2025
08a146d
Refactor using project_context
nojaf Aug 9, 2025
860e6e2
A rescript.json with an unrelated parent can still be a monorepo.
nojaf Aug 9, 2025
8202315
Remove all from format command.
nojaf Aug 9, 2025
d29f977
Merge branch 'master' into detect-root
nojaf Aug 9, 2025
c2c9183
get_rescript_legacy inside rescript repository.
nojaf Aug 9, 2025
50baa0b
sigh
nojaf Aug 9, 2025
c19670e
StrippedVerbatimPath in before local dep check
nojaf Aug 10, 2025
2b8e412
Use proper --version
nojaf Aug 10, 2025
43097c8
Add playground to monorepo
nojaf Aug 10, 2025
c740f4e
Copilot nitpicks
nojaf Aug 11, 2025
e5b723c
Add test for only formatting the current project.
nojaf Aug 11, 2025
7b9b230
Add clean single project test
nojaf Aug 11, 2025
f76b55b
Add test for compiler-args
nojaf Aug 11, 2025
f993a71
Get root config from project_context
nojaf Aug 11, 2025
21b2758
Return Result for get_root_package
nojaf Aug 11, 2025
7892d7b
Make a conscious split between dev and non-dev local dependencies for…
nojaf Aug 11, 2025
958fac3
Add dev project to root of test-repo
nojaf Aug 11, 2025
66d62ec
Try and add test for format --dev
nojaf Aug 11, 2025
8348342
Respect --dev for packages::make in format
nojaf Aug 11, 2025
c87fa26
Improve success message
nojaf Aug 11, 2025
98cdcc2
Add test to ensure we clean dev-dependency with --dev.
nojaf Aug 11, 2025
1ccc44c
Pass in actual rescript.json
nojaf Aug 11, 2025
f83fb38
Ensure dependencies are cleaned as well.
nojaf Aug 12, 2025
dc05c76
restore instead of clean
nojaf Aug 12, 2025
c8ebab8
Address code review remarks of clean.rs
nojaf Aug 12, 2025
067e983
Store path to config in Config struct
nojaf Aug 12, 2025
2606104
Format
nojaf Aug 12, 2025
c463e94
Add with-ppx example
nojaf Aug 12, 2025
607b412
Ensure parse arguments of source package are used.
nojaf Aug 12, 2025
fec8b5d
Add compiled file from with-ppx
nojaf Aug 12, 2025
ccbd698
Update snapshot because new test project.
nojaf Aug 12, 2025
1db7bbe
Update suffix test.
nojaf Aug 12, 2025
ad06c8b
One more file to format
nojaf Aug 12, 2025
db6883b
Moar code review suggestions.
nojaf Aug 12, 2025
e01de31
Print if a package is local after checking.
nojaf Aug 12, 2025
dbe85aa
Remove debug log
nojaf Aug 12, 2025
70718a6
Merge branch 'master' into detect-root
nojaf Aug 12, 2025
ddb75e3
Windows fix of the day
nojaf Aug 12, 2025
e59a7f4
Wait a bit more on watch file for Windows CI
nojaf Aug 13, 2025
b014cd1
Add changelog entry
nojaf Aug 13, 2025
fc66c24
Extract common data from enum.
nojaf Aug 13, 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
3 changes: 2 additions & 1 deletion rewatch/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod read_compile_state;
use self::compile::compiler_args;
use self::parse::parser_args;
use crate::build::compile::{mark_modules_with_deleted_deps_dirty, mark_modules_with_expired_deps_dirty};
use crate::build::packages::PackageMap;
use crate::helpers::emojis::*;
use crate::helpers::{self, get_workspace_root};
use crate::{config, sourcedirs};
Expand Down Expand Up @@ -131,7 +132,7 @@ pub fn initialize_build(
}

let timing_package_tree = Instant::now();
let packages = packages::make(
let PackageMap { packages, .. } = packages::make(
filter,
&project_root,
&workspace_root,
Expand Down
146 changes: 100 additions & 46 deletions rewatch/src/build/clean.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::build_types::*;
use super::packages;
use crate::build::packages::{Package, PackageMap};
use crate::helpers;
use crate::helpers::emojis::*;
use ahash::AHashSet;
Expand Down Expand Up @@ -28,7 +29,7 @@ fn remove_iast(package: &packages::Package, source_file: &Path) {
));
}

fn remove_mjs_file(source_file: &Path, suffix: &String) {
fn remove_mjs_file(source_file: &Path, suffix: &str) {
let _ = std::fs::remove_file(source_file.with_extension(
// suffix.to_string includes the ., so we need to remove it
&suffix.to_string()[1..],
Expand Down Expand Up @@ -58,36 +59,52 @@ pub fn remove_compile_assets(package: &packages::Package, source_file: &Path) {
}
}

fn clean_source_files(build_state: &BuildState, root_package: &packages::Package) {
fn clean_source_files(
build_state: &BuildState,
// If the root_package is part of a workspace, we only want to clean that package,
// not the entire build_state modules.
workspace_has_root_config: bool,
root_package: &Package,
suffix: &str,
) {
// get all rescript file locations
let rescript_file_locations = build_state
.modules
.values()
.filter_map(|module| match &module.source_type {
SourceType::SourceFile(source_file) => {
let package = build_state.packages.get(&module.package_name).unwrap();
Some(
root_package
.config
.get_package_specs()
.iter()
.filter_map(|spec| {
if spec.in_source {
Some((
package.path.join(&source_file.implementation.path),
root_package.config.get_suffix(spec),
))
} else {
None
}
})
.collect::<Vec<(PathBuf, String)>>(),
)
if workspace_has_root_config && module.package_name != root_package.name {
// Skip this package as we only want to clean the root_package
// if is the child of the workspace.
None
} else {
let package = build_state.packages.get(&module.package_name).unwrap();
Some(
root_package
.config
.get_package_specs()
.iter()
.filter_map(|spec| {
if spec.in_source {
Some((
package.path.join(&source_file.implementation.path),
match &root_package.config.suffix {
None => suffix,
Some(sfx) => sfx,
},
))
} else {
None
}
})
.collect::<Vec<(PathBuf, &str)>>(),
)
}
}
_ => None,
})
.flatten()
.collect::<Vec<(PathBuf, String)>>();
.collect::<Vec<(PathBuf, &str)>>();

rescript_file_locations
.par_iter()
Expand Down Expand Up @@ -329,7 +346,13 @@ pub fn cleanup_after_build(build_state: &BuildState) {
pub fn clean(path: &Path, show_progress: bool, snapshot_output: bool, build_dev_deps: bool) -> Result<()> {
let project_root = helpers::get_abs_path(path);
let workspace_root = helpers::get_workspace_root(&project_root);
let packages = packages::make(
let workspace_config_name = &workspace_root
.as_ref()
.and_then(|wr| packages::read_package_name(wr).ok());
let PackageMap {
packages,
root_is_child_of_workspace: workspace_has_root_config,
} = packages::make(
&None,
&project_root,
&workspace_root,
Expand All @@ -348,30 +371,21 @@ pub fn clean(path: &Path, show_progress: bool, snapshot_output: bool, build_dev_
);
let _ = std::io::stdout().flush();
};
packages.iter().for_each(|(_, package)| {
if show_progress {
if snapshot_output {
println!("Cleaning {}", package.name)
} else {
print!(
"{}{} {}Cleaning {}...",
LINE_CLEAR,
style("[1/2]").bold().dim(),
SWEEP,
package.name
);
}
let _ = std::io::stdout().flush();
}

let path_str = package.get_build_path();
let path = std::path::Path::new(&path_str);
let _ = std::fs::remove_dir_all(path);
if workspace_has_root_config {
// The workspace package was found in the packages.
// This means that the root_config and workspace_config have a parent/child relationship.
// So we only want to clean the root package in this case.
let package = packages
.get(&root_config_name)
.expect("Could not find package during clean");
clean_package(show_progress, snapshot_output, package);
} else {
packages.iter().for_each(|(_, package)| {
clean_package(show_progress, snapshot_output, package);
});
}

let path_str = package.get_ocaml_build_path();
let path = std::path::Path::new(&path_str);
let _ = std::fs::remove_dir_all(path);
});
let timing_clean_compiler_assets_elapsed = timing_clean_compiler_assets.elapsed();

if !snapshot_output && show_progress {
Expand Down Expand Up @@ -399,7 +413,22 @@ pub fn clean(path: &Path, show_progress: bool, snapshot_output: bool, build_dev_
.get(&build_state.root_config_name)
.expect("Could not find root package");

let suffix = root_package.config.suffix.as_deref().unwrap_or(".res.mjs");
// Use the current package suffix if present.
// Otherwise, use the parent suffix if present.
// Fall back to .res.mjs
let suffix = match root_package.config.suffix.as_deref() {
Some(suffix) => suffix,
None => match &workspace_config_name {
None => ".res.mjs",
Some(workspace_config_name) => {
if let Some(package) = build_state.packages.get(workspace_config_name) {
package.config.suffix.as_deref().unwrap_or(".res.mjs")
} else {
".res.mjs"
}
}
},
};

if !snapshot_output && show_progress {
println!(
Expand All @@ -411,7 +440,7 @@ pub fn clean(path: &Path, show_progress: bool, snapshot_output: bool, build_dev_
let _ = std::io::stdout().flush();
}

clean_source_files(&build_state, root_package);
clean_source_files(&build_state, workspace_has_root_config, root_package, suffix);
let timing_clean_mjs_elapsed = timing_clean_mjs.elapsed();

if !snapshot_output && show_progress {
Expand All @@ -428,3 +457,28 @@ pub fn clean(path: &Path, show_progress: bool, snapshot_output: bool, build_dev_

Ok(())
}

fn clean_package(show_progress: bool, snapshot_output: bool, package: &Package) {
if show_progress {
if snapshot_output {
println!("Cleaning {}", package.name)
} else {
print!(
"{}{} {}Cleaning {}...",
LINE_CLEAR,
style("[1/2]").bold().dim(),
SWEEP,
package.name
);
}
let _ = std::io::stdout().flush();
}

let path_str = package.get_build_path();
let path = std::path::Path::new(&path_str);
let _ = std::fs::remove_dir_all(path);

let path_str = package.get_ocaml_build_path();
let path = std::path::Path::new(&path_str);
let _ = std::fs::remove_dir_all(path);
}
51 changes: 47 additions & 4 deletions rewatch/src/build/packages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,27 +597,70 @@ fn extend_with_children(
build
}

pub struct PackageMap {
pub packages: AHashMap<String, Package>,
/// When packages::make is invoked with a workspace_root,
/// this flag determines if there is a parent/child relation
/// between root_folder and workspace_root.
pub root_is_child_of_workspace: bool,
}

/// Make turns a folder, that should contain a config, into a tree of Packages.
/// It does so in two steps:
/// 1. Get all the packages parsed, and take all the source folders from the config
/// 2. Take the (by then deduplicated) packages, and find all the '.res' and
/// interface files.
///
/// The two step process is there to reduce IO overhead
/// The two step process is there to reduce IO overhead.
/// `is_child` is true when the workspace_root rescript.json has the name of the root_folder rescript.json
pub fn make(
filter: &Option<regex::Regex>,
root_folder: &Path,
workspace_root: &Option<PathBuf>,
show_progress: bool,
build_dev_deps: bool,
) -> Result<AHashMap<String, Package>> {
let map = read_packages(root_folder, workspace_root, show_progress, build_dev_deps)?;
) -> Result<PackageMap> {
let mut root_is_child_of_workspace = false;
let map = match &workspace_root {
Some(wr) => {
// If the workspace root contains the root_folder as a dependency,
// it should be considered as related, and we read the workspace.
let root_name = read_package_name(root_folder)?;
let workspace_config = read_config(wr)?;

root_is_child_of_workspace = workspace_config
.dependencies
.to_owned()
.unwrap_or_default()
.iter()
.any(|dep| dep == &root_name)
|| workspace_config
.dev_dependencies
.to_owned()
.unwrap_or_default()
.iter()
.any(|dep| dep == &root_name);

if root_is_child_of_workspace {
let root_folder_str = root_folder.to_string_lossy();
let workspace_root_str = wr.to_string_lossy();
log::info!("Building workspace: {workspace_root_str} for {root_folder_str}",);
read_packages(wr, workspace_root, show_progress, build_dev_deps)?
} else {
read_packages(root_folder, workspace_root, show_progress, build_dev_deps)?
}
}
None => read_packages(root_folder, workspace_root, show_progress, build_dev_deps)?,
};

/* Once we have the deduplicated packages, we can add the source files for each - to minimize
* the IO */
let result = extend_with_children(filter, map, build_dev_deps);

Ok(result)
Ok(PackageMap {
packages: result,
root_is_child_of_workspace,
})
}

pub fn parse_packages(build_state: &mut BuildState) {
Expand Down
5 changes: 3 additions & 2 deletions rewatch/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ fn get_all_files() -> Result<Vec<String>> {
let project_root = helpers::get_abs_path(&current_dir);
let workspace_root_option = helpers::get_workspace_root(&project_root);

let build_state = packages::make(&None, &project_root, &workspace_root_option, false, false)?;
let packages::PackageMap { packages, .. } =
packages::make(&None, &project_root, &workspace_root_option, false, false)?;
let mut files: Vec<String> = Vec::new();

for (_package_name, package) in build_state {
for (_package_name, package) in packages {
if package.is_local_dep
&& let Some(source_files) = package.source_files
{
Expand Down
Loading