From 0b417683945f1490a62b701239e22d5bd12fc451 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Tue, 28 Jan 2025 15:17:57 +0100 Subject: [PATCH 1/2] Rewatch 1.0.10 --- rewatch/Cargo.lock | 326 +-------------- rewatch/Cargo.toml | 16 +- rewatch/benches/base_bench.rs | 54 --- rewatch/src/build.rs | 392 +++++++++--------- rewatch/src/build/build_types.rs | 12 +- rewatch/src/build/clean.rs | 89 ++-- rewatch/src/build/compile.rs | 256 ++++++------ rewatch/src/build/deps.rs | 19 +- rewatch/src/build/packages.rs | 288 +++++++------ rewatch/src/build/parse.rs | 63 ++- rewatch/src/build/read_compile_state.rs | 2 +- rewatch/src/cmd.rs | 6 +- rewatch/src/{bsconfig.rs => config.rs} | 300 +++++++++++--- rewatch/src/helpers.rs | 21 +- rewatch/src/lib.rs | 2 +- rewatch/src/lock.rs | 6 +- rewatch/src/main.rs | 42 +- rewatch/src/sourcedirs.rs | 10 +- rewatch/src/structure_hashmap.rs | 54 --- rewatch/src/watcher.rs | 67 ++- rewatch/tests/lib/rewatch.lock | 2 +- rewatch/tests/lock.sh | 4 +- rewatch/tests/snapshots/dependency-cycle.txt | 17 +- rewatch/tests/snapshots/remove-file.txt | 17 +- .../rename-file-internal-dep-namespace.txt | 17 +- .../snapshots/rename-file-internal-dep.txt | 17 +- .../snapshots/rename-file-with-interface.txt | 17 +- rewatch/tests/snapshots/rename-file.txt | 14 +- .../tests/snapshots/rename-interface-file.txt | 17 +- 29 files changed, 1048 insertions(+), 1099 deletions(-) delete mode 100644 rewatch/benches/base_bench.rs rename rewatch/src/{bsconfig.rs => config.rs} (55%) delete mode 100644 rewatch/src/structure_hashmap.rs diff --git a/rewatch/Cargo.lock b/rewatch/Cargo.lock index 2bec9e740b..6c09783cd8 100644 --- a/rewatch/Cargo.lock +++ b/rewatch/Cargo.lock @@ -24,12 +24,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - [[package]] name = "anstream" version = "0.6.13" @@ -78,6 +72,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "anyhow" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" + [[package]] name = "arrayref" version = "0.3.7" @@ -90,17 +90,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.2.0" @@ -132,18 +121,6 @@ dependencies = [ "constant_time_eq", ] -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - [[package]] name = "cc" version = "1.0.92" @@ -162,53 +139,24 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - [[package]] name = "clap" -version = "3.2.25" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ - "bitflags 1.3.2", - "clap_lex 0.2.4", - "indexmap", - "textwrap", + "clap_builder", + "clap_derive", ] [[package]] -name = "clap" -version = "4.5.4" +name = "clap-verbosity-flag" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "e099138e1807662ff75e2cebe4ae2287add879245574489f9b1588eb5e5564ed" dependencies = [ - "clap_builder", - "clap_derive", + "clap", + "log", ] [[package]] @@ -219,7 +167,7 @@ checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", - "clap_lex 0.7.0", + "clap_lex", "strsim", ] @@ -235,15 +183,6 @@ dependencies = [ "syn", ] -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - [[package]] name = "clap_lex" version = "0.7.0" @@ -290,42 +229,6 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" -[[package]] -name = "criterion" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" -dependencies = [ - "anes", - "atty", - "cast", - "ciborium", - "clap 3.2.25", - "criterion-plot", - "itertools", - "lazy_static", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" -dependencies = [ - "cast", - "itertools", -] - [[package]] name = "crossbeam-channel" version = "0.5.12" @@ -360,12 +263,6 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "ctrlc" version = "3.4.4" @@ -528,37 +425,12 @@ dependencies = [ "wasi", ] -[[package]] -name = "half" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" -dependencies = [ - "cfg-if", - "crunchy", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.9" @@ -571,16 +443,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown", -] - [[package]] name = "indicatif" version = "0.17.8" @@ -629,35 +491,17 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", "windows-sys 0.52.0", ] -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" -[[package]] -name = "js-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "kqueue" version = "1.0.8" @@ -764,15 +608,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "num-traits" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" -dependencies = [ - "autocfg", -] - [[package]] name = "number_prefix" version = "0.4.0" @@ -785,18 +620,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "oorandom" -version = "11.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" - -[[package]] -name = "os_str_bytes" -version = "6.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" - [[package]] name = "pin-project-lite" version = "0.2.14" @@ -809,34 +632,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "plotters" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" - -[[package]] -name = "plotters-svg" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" -dependencies = [ - "plotters-backend", -] - [[package]] name = "portable-atomic" version = "1.6.0" @@ -921,14 +716,15 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rewatch" -version = "1.0.9" +version = "1.0.10" dependencies = [ "ahash", + "anyhow", "blake3", - "clap 4.5.4", + "clap", + "clap-verbosity-flag", "console", "convert_case", - "criterion", "ctrlc", "env_logger", "futures", @@ -1041,22 +837,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "unicode-ident" version = "1.0.12" @@ -1103,70 +883,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasm-bindgen" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "winapi" version = "0.3.9" diff --git a/rewatch/Cargo.toml b/rewatch/Cargo.toml index a17f55d39c..79e5166038 100644 --- a/rewatch/Cargo.toml +++ b/rewatch/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rewatch" -version = "1.0.9" +version = "1.0.10" edition = "2021" [dependencies] @@ -11,10 +11,7 @@ serde_json = { version = "1.0.93" } ahash = "0.8.3" convert_case = "0.6.0" rayon = "1.6.1" -log = { version = "0.4.17", features = [ - "max_level_debug", - "release_max_level_warn", -] } +log = { version = "0.4.17" } env_logger = "0.10.0" indicatif = "0.17.3" console = "0.15.5" @@ -26,15 +23,10 @@ futures-timer = "3.0.2" clap = { version = "4.3.17", features = ["derive"] } sysinfo = "0.29.10" ctrlc = "3.4.4" +clap-verbosity-flag = "2.2.2" +anyhow = "1.0.93" [profile.release] codegen-units = 1 lto = true - -[dev-dependencies] -criterion = "0.4" - -[[bench]] -name = "base_bench" -harness = false diff --git a/rewatch/benches/base_bench.rs b/rewatch/benches/base_bench.rs deleted file mode 100644 index 8b781a0390..0000000000 --- a/rewatch/benches/base_bench.rs +++ /dev/null @@ -1,54 +0,0 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use rewatch::build; -use rewatch::build::clean; -use rewatch::build::packages; -use rewatch::helpers; - -use std::fs::File; -use std::io::prelude::*; - -fn criterion_benchmark(c: &mut Criterion) { - c.bench_function("build-package-tree", |b| { - // Folder for the testrepo - let folder = "walnut_monorepo"; - let project_root = helpers::get_abs_path(folder); - - b.iter(|| { - packages::make(&None, &project_root, &None); - }) - }); - - c.bench_function("clean-build-change-build", |b| { - // Folder for the testrepo - let folder = "testrepo"; - let filename = "testrepo/packages/dep02/src/Dep02.res"; - // Clean the build - clean::clean(folder); - // Read the file we'll be mutating - let mut file = File::options() - .read(true) - .write(true) - .append(true) - .open(filename) - .unwrap(); - let mut contents = String::new(); - file.read_to_string(&mut contents).unwrap(); - - b.iter(|| { - // Create initial build - let _ = build::build(&None, folder, false); - // Update the file - let _ = writeln!(file, r#"let log2 = () => ["a", "b"]->forEach(Js.log);log2()"#); - // Create another build - let _ = build::build(&None, folder, false); - - // Reset state - File::create(filename).unwrap(); - file.write_all(contents.as_bytes()).unwrap(); - let _ = build::build(&None, folder, false); - }) - }); -} - -criterion_group!(benches, criterion_benchmark); -criterion_main!(benches); diff --git a/rewatch/src/build.rs b/rewatch/src/build.rs index aece691c98..92fb31c668 100644 --- a/rewatch/src/build.rs +++ b/rewatch/src/build.rs @@ -13,9 +13,11 @@ use crate::helpers::emojis::*; use crate::helpers::{self, get_workspace_root}; use crate::sourcedirs; use ahash::AHashSet; +use anyhow::{anyhow, Result}; use build_types::*; use console::style; use indicatif::{ProgressBar, ProgressStyle}; +use log::log_enabled; use serde::Serialize; use std::fmt; use std::fs::File; @@ -53,15 +55,19 @@ pub struct CompilerArgs { pub parser_args: Vec, } -pub fn get_compiler_args(path: &str, rescript_version: Option, bsc_path: Option) -> String { +pub fn get_compiler_args( + path: &str, + rescript_version: Option, + bsc_path: Option, +) -> Result { let filename = &helpers::get_abs_path(path); let package_root = helpers::get_abs_path( - &helpers::get_nearest_bsconfig(&std::path::PathBuf::from(path)).expect("Couldn't find package root"), + &helpers::get_nearest_config(&std::path::PathBuf::from(path)).expect("Couldn't find package root"), ); let workspace_root = get_workspace_root(&package_root).map(|p| helpers::get_abs_path(&p)); let root_rescript_config = - packages::read_bsconfig(&workspace_root.to_owned().unwrap_or(package_root.to_owned())); - let rescript_config = packages::read_bsconfig(&package_root); + packages::read_config(&workspace_root.to_owned().unwrap_or(package_root.to_owned()))?; + let rescript_config = packages::read_config(&package_root)?; let rescript_version = if let Some(rescript_version) = rescript_version { rescript_version } else { @@ -110,73 +116,67 @@ pub fn get_compiler_args(path: &str, rescript_version: Option, bsc_path: &workspace_root, &None, ); - serde_json::to_string_pretty(&CompilerArgs { + + let result = serde_json::to_string_pretty(&CompilerArgs { compiler_args, parser_args, - }) - .unwrap() -} + })?; -#[derive(Debug, Clone)] -pub enum InitializeBuildError { - PackageDependencyValidation, -} - -impl fmt::Display for InitializeBuildError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::PackageDependencyValidation => write!( - f, - "{} {}Could not Validate Package Dependencies", - LINE_CLEAR, CROSS, - ), - } - } + Ok(result) } pub fn initialize_build( default_timing: Option, filter: &Option, + show_progress: bool, path: &str, bsc_path: Option, -) -> Result { +) -> Result { let project_root = helpers::get_abs_path(path); let workspace_root = helpers::get_workspace_root(&project_root); let bsc_path = match bsc_path { Some(bsc_path) => bsc_path, None => helpers::get_bsc(&project_root, workspace_root.to_owned()), }; - let root_config_name = packages::get_package_name(&project_root); + let root_config_name = packages::get_package_name(&project_root)?; let rescript_version = helpers::get_rescript_version(&bsc_path); - print!("{}{}Building package tree...", style("[1/7]").bold().dim(), TREE); - let _ = stdout().flush(); + if show_progress { + println!("{} {}Building package tree...", style("[1/7]").bold().dim(), TREE); + let _ = stdout().flush(); + } + let timing_package_tree = Instant::now(); - let packages = packages::make(filter, &project_root, &workspace_root); + let packages = packages::make(filter, &project_root, &workspace_root, show_progress)?; let timing_package_tree_elapsed = timing_package_tree.elapsed(); - println!( - "{}{} {}Built package tree in {:.2}s", - LINE_CLEAR, - style("[1/7]").bold().dim(), - TREE, - default_timing - .unwrap_or(timing_package_tree_elapsed) - .as_secs_f64() - ); + if show_progress { + println!( + "{}{} {}Built package tree in {:.2}s", + LINE_CLEAR, + style("[1/7]").bold().dim(), + TREE, + default_timing + .unwrap_or(timing_package_tree_elapsed) + .as_secs_f64() + ); + } if !packages::validate_packages_dependencies(&packages) { - return Err(InitializeBuildError::PackageDependencyValidation); + return Err(anyhow!("Failed to validate package dependencies")); } let timing_source_files = Instant::now(); - print!( - "{} {}Finding source files...", - style("[2/7]").bold().dim(), - LOOKING_GLASS - ); - let _ = stdout().flush(); + if show_progress { + println!( + "{} {}Finding source files...", + style("[2/7]").bold().dim(), + LOOKING_GLASS + ); + let _ = stdout().flush(); + } + let mut build_state = BuildState::new( project_root, root_config_name, @@ -187,52 +187,62 @@ pub fn initialize_build( ); packages::parse_packages(&mut build_state); let timing_source_files_elapsed = timing_source_files.elapsed(); - println!( - "{}{} {}Found source files in {:.2}s", - LINE_CLEAR, - style("[2/7]").bold().dim(), - LOOKING_GLASS, - default_timing - .unwrap_or(timing_source_files_elapsed) - .as_secs_f64() - ); - print!( - "{} {}Reading compile state...", - style("[3/7]").bold().dim(), - COMPILE_STATE - ); - let _ = stdout().flush(); + if show_progress { + println!( + "{}{} {}Found source files in {:.2}s", + LINE_CLEAR, + style("[2/7]").bold().dim(), + LOOKING_GLASS, + default_timing + .unwrap_or(timing_source_files_elapsed) + .as_secs_f64() + ); + + println!( + "{} {}Reading compile state...", + style("[3/7]").bold().dim(), + COMPILE_STATE + ); + let _ = stdout().flush(); + } let timing_compile_state = Instant::now(); let compile_assets_state = read_compile_state::read(&mut build_state); let timing_compile_state_elapsed = timing_compile_state.elapsed(); - println!( - "{}{} {}Read compile state {:.2}s", - LINE_CLEAR, - style("[3/7]").bold().dim(), - COMPILE_STATE, - default_timing - .unwrap_or(timing_compile_state_elapsed) - .as_secs_f64() - ); - print!( - "{} {}Cleaning up previous build...", - style("[4/7]").bold().dim(), - SWEEP - ); + if show_progress { + println!( + "{}{} {}Read compile state {:.2}s", + LINE_CLEAR, + style("[3/7]").bold().dim(), + COMPILE_STATE, + default_timing + .unwrap_or(timing_compile_state_elapsed) + .as_secs_f64() + ); + + println!( + "{} {}Cleaning up previous build...", + style("[4/7]").bold().dim(), + SWEEP + ); + } let timing_cleanup = Instant::now(); let (diff_cleanup, total_cleanup) = clean::cleanup_previous_build(&mut build_state, compile_assets_state); let timing_cleanup_elapsed = timing_cleanup.elapsed(); - println!( - "{}{} {}Cleaned {}/{} {:.2}s", - LINE_CLEAR, - style("[4/7]").bold().dim(), - SWEEP, - diff_cleanup, - total_cleanup, - default_timing.unwrap_or(timing_cleanup_elapsed).as_secs_f64() - ); + + if show_progress { + println!( + "{}{} {}Cleaned {}/{} {:.2}s", + LINE_CLEAR, + style("[4/7]").bold().dim(), + SWEEP, + diff_cleanup, + total_cleanup, + default_timing.unwrap_or(timing_cleanup_elapsed).as_secs_f64() + ); + } + Ok(build_state) } @@ -243,14 +253,19 @@ fn format_step(current: usize, total: usize) -> console::StyledObject { #[derive(Debug, Clone)] pub enum IncrementalBuildError { SourceFileParseError, - CompileError, + CompileError(Option), } impl fmt::Display for IncrementalBuildError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::SourceFileParseError => write!(f, "{} {}Could not parse Source Files", LINE_CLEAR, CROSS,), - Self::CompileError => write!(f, "{} {}Failed to Compile. See Errors Above", LINE_CLEAR, CROSS,), + Self::CompileError(Some(e)) => { + write!(f, "{} {}Failed to Compile. Error: {e}", LINE_CLEAR, CROSS,) + } + Self::CompileError(None) => { + write!(f, "{} {}Failed to Compile. See Errors Above", LINE_CLEAR, CROSS,) + } } } } @@ -259,12 +274,17 @@ pub fn incremental_build( build_state: &mut BuildState, default_timing: Option, initial_build: bool, + show_progress: bool, only_incremental: bool, create_sourcedirs: bool, ) -> Result<(), IncrementalBuildError> { logs::initialize(&build_state.packages); let num_dirty_modules = build_state.modules.values().filter(|m| is_dirty(m)).count() as u64; - let pb = ProgressBar::new(num_dirty_modules); + let pb = if show_progress { + ProgressBar::new(num_dirty_modules) + } else { + ProgressBar::hidden() + }; let mut current_step = if only_incremental { 1 } else { 5 }; let total_steps = if only_incremental { 3 } else { 7 }; pb.set_style( @@ -281,27 +301,31 @@ pub fn incremental_build( let timing_ast_elapsed = timing_ast.elapsed(); match result_asts { - Ok(err) => { - println!( - "{}{} {}Parsed {} source files in {:.2}s", - LINE_CLEAR, - format_step(current_step, total_steps), - CODE, - num_dirty_modules, - default_timing.unwrap_or(timing_ast_elapsed).as_secs_f64() - ); - print!("{}", &err); + Ok(_ast) => { + if show_progress { + println!( + "{}{} {}Parsed {} source files in {:.2}s", + LINE_CLEAR, + format_step(current_step, total_steps), + CODE, + num_dirty_modules, + default_timing.unwrap_or(timing_ast_elapsed).as_secs_f64() + ); + } } Err(err) => { logs::finalize(&build_state.packages); - println!( - "{}{} {}Error parsing source files in {:.2}s", - LINE_CLEAR, - format_step(current_step, total_steps), - CROSS, - default_timing.unwrap_or(timing_ast_elapsed).as_secs_f64() - ); - print!("{}", &err); + if show_progress { + println!( + "{}{} {}Error parsing source files in {:.2}s", + LINE_CLEAR, + format_step(current_step, total_steps), + CROSS, + default_timing.unwrap_or(timing_ast_elapsed).as_secs_f64() + ); + } + + log::error!("Could not parse source files: {}", &err); return Err(IncrementalBuildError::SourceFileParseError); } } @@ -310,13 +334,15 @@ pub fn incremental_build( let timing_deps_elapsed = timing_deps.elapsed(); current_step += 1; - println!( - "{}{} {}Collected deps in {:.2}s", - LINE_CLEAR, - format_step(current_step, total_steps), - DEPS, - default_timing.unwrap_or(timing_deps_elapsed).as_secs_f64() - ); + if show_progress { + println!( + "{}{} {}Collected deps in {:.2}s", + LINE_CLEAR, + format_step(current_step, total_steps), + DEPS, + default_timing.unwrap_or(timing_deps_elapsed).as_secs_f64() + ); + } // track the compile dirty state, we reset it when the compile fails let mut tracked_dirty_modules = AHashSet::new(); @@ -332,95 +358,79 @@ pub fn incremental_build( mark_modules_with_deleted_deps_dirty(build_state); current_step += 1; - // print all the compile_dirty modules - // for (module_name, module) in build_state.modules.iter() { - // if module.compile_dirty { - // println!("compile dirty: {}", module_name); - // } - // } + //print all the compile_dirty modules + if log_enabled!(log::Level::Trace) { + for (module_name, module) in build_state.modules.iter() { + if module.compile_dirty { + println!("compile dirty: {}", module_name); + } + } + }; let start_compiling = Instant::now(); - let pb = ProgressBar::new(build_state.modules.len().try_into().unwrap()); - pb.set_style( - ProgressStyle::with_template(&format!( - "{} {}Compiling... {{spinner}} {{pos}}/{{len}} {{msg}}", - format_step(current_step, total_steps), - SWORDS - )) - .unwrap(), - ); + let pb = if show_progress { + ProgressBar::new(build_state.modules.len().try_into().unwrap()) + } else { + ProgressBar::hidden() + }; + let (compile_errors, compile_warnings, num_compiled_modules) = - compile::compile(build_state, || pb.inc(1), |size| pb.set_length(size)); + compile::compile(build_state, || pb.inc(1), |size| pb.set_length(size)) + .map_err(|e| IncrementalBuildError::CompileError(Some(e.to_string())))?; + let compile_duration = start_compiling.elapsed(); logs::finalize(&build_state.packages); if create_sourcedirs { - sourcedirs::print(&build_state); + sourcedirs::print(build_state); } pb.finish(); if !compile_errors.is_empty() { if helpers::contains_ascii_characters(&compile_warnings) { - println!("{}", &compile_warnings); + log::error!("{}", &compile_warnings); } - println!( - "{}{} {}Compiled {} modules in {:.2}s", - LINE_CLEAR, - format_step(current_step, total_steps), - CROSS, - num_compiled_modules, - default_timing.unwrap_or(compile_duration).as_secs_f64() - ); - print!("{}", &compile_errors); + if show_progress { + println!( + "{}{} {}Compiled {} modules in {:.2}s", + LINE_CLEAR, + format_step(current_step, total_steps), + CROSS, + num_compiled_modules, + default_timing.unwrap_or(compile_duration).as_secs_f64() + ); + } + log::error!("{}", &compile_errors); // mark the original files as dirty again, because we didn't complete a full build for (module_name, module) in build_state.modules.iter_mut() { if tracked_dirty_modules.contains(module_name) { module.compile_dirty = true; } } - Err(IncrementalBuildError::CompileError) + Err(IncrementalBuildError::CompileError(None)) } else { - println!( - "{}{} {}Compiled {} modules in {:.2}s", - LINE_CLEAR, - format_step(current_step, total_steps), - SWORDS, - num_compiled_modules, - default_timing.unwrap_or(compile_duration).as_secs_f64() - ); + if show_progress { + println!( + "{}{} {}Compiled {} modules in {:.2}s", + LINE_CLEAR, + format_step(current_step, total_steps), + SWORDS, + num_compiled_modules, + default_timing.unwrap_or(compile_duration).as_secs_f64() + ); + } if helpers::contains_ascii_characters(&compile_warnings) { - print!("{}", &compile_warnings); + log::warn!("{}", &compile_warnings); } Ok(()) } } -#[derive(Debug, Clone)] -pub enum BuildError { - InitializeBuild(InitializeBuildError), - IncrementalBuild(IncrementalBuildError), -} - -impl fmt::Display for BuildError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::InitializeBuild(e) => { - write!(f, "{} {}Error Initializing Build: {}", LINE_CLEAR, CROSS, e) - } - Self::IncrementalBuild(e) => write!( - f, - "{} {}Error Running Incremental Build: {}", - LINE_CLEAR, CROSS, e - ), - } - } -} - +// write build.ninja files in the packages after a non-incremental build +// this is necessary to bust the editor tooling cache. The editor tooling +// is watching this file. +// we don't need to do this in an incremental build because there are no file +// changes (deletes / additions) pub fn write_build_ninja(build_state: &BuildState) { - // write build.ninja files in the packages after a non-incremental build - // this is necessary to bust the editor tooling cache. The editor tooling - // is watching this file. - // we don't need to do this in an incremental build because there are no file - // changes (deletes / additions) for package in build_state.packages.values() { // write empty file: let mut f = File::create(std::path::Path::new(&package.get_bs_build_path()).join("build.ninja")) @@ -432,28 +442,38 @@ pub fn write_build_ninja(build_state: &BuildState) { pub fn build( filter: &Option, path: &str, + show_progress: bool, no_timing: bool, create_sourcedirs: bool, bsc_path: Option, -) -> Result { +) -> Result { let default_timing: Option = if no_timing { Some(std::time::Duration::new(0.0 as u64, 0.0 as u32)) } else { None }; let timing_total = Instant::now(); - let mut build_state = - initialize_build(default_timing, filter, path, bsc_path).map_err(BuildError::InitializeBuild)?; - - match incremental_build(&mut build_state, default_timing, true, false, create_sourcedirs) { + let mut build_state = initialize_build(default_timing, filter, show_progress, path, bsc_path) + .map_err(|e| anyhow!("Could not initialize build. Error: {e}"))?; + + match incremental_build( + &mut build_state, + default_timing, + true, + show_progress, + false, + create_sourcedirs, + ) { Ok(_) => { - let timing_total_elapsed = timing_total.elapsed(); - println!( - "\n{}{}Finished Compilation in {:.2}s", - LINE_CLEAR, - SPARKLES, - default_timing.unwrap_or(timing_total_elapsed).as_secs_f64() - ); + if show_progress { + let timing_total_elapsed = timing_total.elapsed(); + println!( + "\n{}{}Finished Compilation in {:.2}s", + LINE_CLEAR, + SPARKLES, + default_timing.unwrap_or(timing_total_elapsed).as_secs_f64() + ); + } clean::cleanup_after_build(&build_state); write_build_ninja(&build_state); Ok(build_state) @@ -461,7 +481,7 @@ pub fn build( Err(e) => { clean::cleanup_after_build(&build_state); write_build_ninja(&build_state); - Err(BuildError::IncrementalBuild(e)) + Err(anyhow!("Incremental build failed. Error: {e}")) } } } diff --git a/rewatch/src/build/build_types.rs b/rewatch/src/build/build_types.rs index 4dc6cfad80..95e9039673 100644 --- a/rewatch/src/build/build_types.rs +++ b/rewatch/src/build/build_types.rs @@ -1,6 +1,6 @@ use crate::build::packages::{Namespace, Package}; use ahash::{AHashMap, AHashSet}; -use std::time::SystemTime; +use std::{fmt::Display, time::SystemTime}; #[derive(Debug, Clone, PartialEq)] pub enum ParseState { @@ -52,6 +52,15 @@ pub enum SourceType { MlMap(MlMap), } +impl Display for SourceType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SourceType::SourceFile(_) => write!(f, "SourceFile"), + SourceType::MlMap(_) => write!(f, "MlMap"), + } + } +} + #[derive(Debug, Clone)] pub struct Module { pub source_type: SourceType, @@ -119,6 +128,7 @@ impl BuildState { deps_initialized: false, } } + pub fn insert_module(&mut self, module_name: &str, module: Module) { self.modules.insert(module_name.to_owned(), module); self.module_names.insert(module_name.to_owned()); diff --git a/rewatch/src/build/clean.rs b/rewatch/src/build/clean.rs index b55f2ee20f..8101c58498 100644 --- a/rewatch/src/build/clean.rs +++ b/rewatch/src/build/clean.rs @@ -3,6 +3,7 @@ use super::packages; use crate::helpers; use crate::helpers::emojis::*; use ahash::AHashSet; +use anyhow::Result; use console::style; use rayon::prelude::*; use std::io::Write; @@ -74,7 +75,7 @@ pub fn clean_mjs_files(build_state: &BuildState) { .join(&source_file.implementation.path) .to_string_lossy() .to_string(), - root_package.bsconfig.get_suffix(), + root_package.config.get_suffix(), )) } _ => None, @@ -125,7 +126,7 @@ pub fn cleanup_previous_build( .get(package_name) .expect("Could not find package"); remove_compile_assets(package, res_file_location); - remove_mjs_file(res_file_location, &suffix); + remove_mjs_file(res_file_location, suffix); remove_iast(package, res_file_location); remove_ast(package, res_file_location); match helpers::get_extension(ast_file_path).as_str() { @@ -234,14 +235,13 @@ pub fn cleanup_previous_build( let deleted_module_names = ast_module_names .difference(&all_module_names) - .map(|module_name| { + .flat_map(|module_name| { // if the module is a namespace, we need to mark the whole namespace as dirty when a module has been deleted if let Some(namespace) = helpers::get_namespace_from_module_name(module_name) { return vec![namespace, module_name.to_string()]; } vec![module_name.to_string()] }) - .flatten() .collect::>(); build_state.deleted_modules = deleted_module_names; @@ -319,11 +319,11 @@ pub fn cleanup_after_build(build_state: &BuildState) { }); } -pub fn clean(path: &str, bsc_path: Option) { +pub fn clean(path: &str, show_progress: bool, bsc_path: Option) -> Result<()> { let project_root = helpers::get_abs_path(path); let workspace_root = helpers::get_workspace_root(&project_root); - let packages = packages::make(&None, &project_root, &workspace_root); - let root_config_name = packages::get_package_name(&project_root); + let packages = packages::make(&None, &project_root, &workspace_root, show_progress)?; + let root_config_name = packages::get_package_name(&project_root)?; let bsc_path = match bsc_path { Some(bsc_path) => bsc_path, None => helpers::get_bsc(&project_root, workspace_root.to_owned()), @@ -332,21 +332,25 @@ pub fn clean(path: &str, bsc_path: Option) { let rescript_version = helpers::get_rescript_version(&bsc_path); let timing_clean_compiler_assets = Instant::now(); - print!( - "{} {} Cleaning compiler assets...", - style("[1/2]").bold().dim(), - SWEEP - ); - std::io::stdout().flush().unwrap(); - packages.iter().for_each(|(_, package)| { - print!( - "{}{} {} Cleaning {}...", - LINE_CLEAR, + if show_progress { + println!( + "{} {}Cleaning compiler assets...", style("[1/2]").bold().dim(), - SWEEP, - package.name + SWEEP ); - std::io::stdout().flush().unwrap(); + let _ = std::io::stdout().flush(); + }; + packages.iter().for_each(|(_, package)| { + if show_progress { + println!( + "{}{} {}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); @@ -358,18 +362,22 @@ pub fn clean(path: &str, bsc_path: Option) { }); let timing_clean_compiler_assets_elapsed = timing_clean_compiler_assets.elapsed(); - println!( - "{}{} {}Cleaned compiler assets in {:.2}s", - LINE_CLEAR, - style("[1/2]").bold().dim(), - SWEEP, - timing_clean_compiler_assets_elapsed.as_secs_f64() - ); - std::io::stdout().flush().unwrap(); + if show_progress { + println!( + "{}{} {}Cleaned compiler assets in {:.2}s", + LINE_CLEAR, + style("[1/2]").bold().dim(), + SWEEP, + timing_clean_compiler_assets_elapsed.as_secs_f64() + ); + let _ = std::io::stdout().flush(); + } let timing_clean_mjs = Instant::now(); - print!("{} {} Cleaning mjs files...", style("[2/2]").bold().dim(), SWEEP); - std::io::stdout().flush().unwrap(); + if show_progress { + println!("{} {}Cleaning mjs files...", style("[2/2]").bold().dim(), SWEEP); + let _ = std::io::stdout().flush(); + } let mut build_state = BuildState::new( project_root.to_owned(), root_config_name, @@ -381,12 +389,17 @@ pub fn clean(path: &str, bsc_path: Option) { packages::parse_packages(&mut build_state); clean_mjs_files(&build_state); let timing_clean_mjs_elapsed = timing_clean_mjs.elapsed(); - println!( - "{}{} {}Cleaned mjs files in {:.2}s", - LINE_CLEAR, - style("[2/2]").bold().dim(), - SWEEP, - timing_clean_mjs_elapsed.as_secs_f64() - ); - std::io::stdout().flush().unwrap(); + + if show_progress { + println!( + "{}{} {}Cleaned mjs files in {:.2}s", + LINE_CLEAR, + style("[2/2]").bold().dim(), + SWEEP, + timing_clean_mjs_elapsed.as_secs_f64() + ); + let _ = std::io::stdout().flush(); + } + + Ok(()) } diff --git a/rewatch/src/build/compile.rs b/rewatch/src/build/compile.rs index ea52e6a3f3..e4cb17809e 100644 --- a/rewatch/src/build/compile.rs +++ b/rewatch/src/build/compile.rs @@ -5,12 +5,12 @@ mod dependency_cycle; use super::build_types::*; use super::logs; use super::packages; -use crate::bsconfig; +use crate::config; use crate::helpers; use ahash::{AHashMap, AHashSet}; +use anyhow::{anyhow, Result}; use console::style; -use log::debug; -use log::{info, log_enabled, Level::Info}; +use log::{debug, log_enabled, trace, Level::Info}; use rayon::prelude::*; use std::path::Path; use std::process::Command; @@ -20,7 +20,7 @@ pub fn compile( build_state: &mut BuildState, inc: impl Fn() + std::marker::Sync, set_length: impl Fn(u64), -) -> (String, String, usize) { +) -> Result<(String, String, usize)> { let mut compiled_modules = AHashSet::::new(); let dirty_modules = build_state .modules @@ -98,7 +98,7 @@ pub fn compile( files_current_loop_count = 0; loop_count += 1; - info!( + trace!( "Compiled: {} out of {}. Compile loop: {}", files_total_count, compile_universe.len(), @@ -107,9 +107,9 @@ pub fn compile( let current_in_progres_modules = in_progress_modules.clone(); - current_in_progres_modules + let results = current_in_progres_modules .par_iter() - .map(|module_name| { + .filter_map(|module_name| { let module = build_state.get_module(module_name).unwrap(); let package = build_state .get_package(&module.package_name) @@ -185,17 +185,8 @@ pub fn compile( &build_state.project_root, &build_state.workspace_root, ); - // if let Err(error) = result.to_owned() { - // println!("{}", error); - // panic!("Implementation compilation error!"); - // } let cmi_digest_after = helpers::compute_file_hash(&cmi_path); - // println!( - // "cmi path {}, digest: {:?} / {:?}", - // cmi_path, cmi_digest, cmi_digest_after - // ); - // we want to compare both the hash of interface and the implementation // compile assets to verify that nothing changed. We also need to checke the interface // because we can include MyModule, so the modules that depend on this module might @@ -220,121 +211,111 @@ pub fn compile( } else { None } - .map(|res| { + .inspect(|_res| { if !(log_enabled!(Info)) { inc(); } - res }) }) - .collect::, String>, - Option, String>>, - bool, - bool, - )>, - >>() - .iter() - .for_each(|result| match result { - Some((module_name, result, interface_result, is_clean, is_compiled)) => { - in_progress_modules.remove(module_name); - - if *is_compiled { - num_compiled_modules += 1; - } + .collect::>(); - files_current_loop_count += 1; - compiled_modules.insert(module_name.to_string()); + for result in results.iter() { + let (module_name, result, interface_result, is_clean, is_compiled) = result; + in_progress_modules.remove(module_name); - if *is_clean { - // actually add it to a list of clean modules - clean_modules.insert(module_name.to_string()); - } + if *is_compiled { + num_compiled_modules += 1; + } + + files_current_loop_count += 1; + compiled_modules.insert(module_name.to_string()); - let module_dependents = build_state.get_module(module_name).unwrap().dependents.clone(); + if *is_clean { + // actually add it to a list of clean modules + clean_modules.insert(module_name.to_string()); + } + + let module_dependents = build_state.get_module(module_name).unwrap().dependents.clone(); - // if not clean -- compile modules that depend on this module - for dep in module_dependents.iter() { - // mark the reverse dep as dirty when the source is not clean - if !*is_clean { - let dep_module = build_state.modules.get_mut(dep).unwrap(); - // mark the reverse dep as dirty when the source is not clean - dep_module.compile_dirty = true; + // if not clean -- compile modules that depend on this module + for dep in module_dependents.iter() { + // mark the reverse dep as dirty when the source is not clean + if !*is_clean { + let dep_module = build_state.modules.get_mut(dep).unwrap(); + // mark the reverse dep as dirty when the source is not clean + dep_module.compile_dirty = true; + } + if !compiled_modules.contains(dep) { + in_progress_modules.insert(dep.to_string()); + } + } + + let module = build_state + .modules + .get_mut(module_name) + .ok_or(anyhow!("Module not found"))?; + + let package = build_state + .packages + .get(&module.package_name) + .ok_or(anyhow!("Package name not found"))?; + + match module.source_type { + SourceType::MlMap(ref mut mlmap) => { + module.compile_dirty = false; + mlmap.parse_dirty = false; + } + SourceType::SourceFile(ref mut source_file) => { + match result { + Ok(Some(err)) => { + source_file.implementation.compile_state = CompileState::Warning; + logs::append(package, err); + compile_warnings.push_str(err); } - if !compiled_modules.contains(dep) { - in_progress_modules.insert(dep.to_string()); + Ok(None) => { + source_file.implementation.compile_state = CompileState::Success; + } + Err(err) => { + source_file.implementation.compile_state = CompileState::Error; + logs::append(package, &err.to_string()); + compile_errors.push_str(&err.to_string()); + } + }; + match interface_result { + Some(Ok(Some(err))) => { + source_file.interface.as_mut().unwrap().compile_state = CompileState::Warning; + logs::append(package, &err.to_string()); + compile_warnings.push_str(&err.to_string()); + } + Some(Ok(None)) => { + if let Some(interface) = source_file.interface.as_mut() { + interface.compile_state = CompileState::Success; + } } - } - let module = build_state.modules.get_mut(module_name).unwrap(); - let package = build_state - .packages - .get(&module.package_name) - .expect("Package not found"); - match module.source_type { - SourceType::MlMap(ref mut mlmap) => { - module.compile_dirty = false; - mlmap.parse_dirty = false; + Some(Err(err)) => { + source_file.interface.as_mut().unwrap().compile_state = CompileState::Error; + logs::append(package, &err.to_string()); + compile_errors.push_str(&err.to_string()); } - SourceType::SourceFile(ref mut source_file) => { - match result { - Ok(Some(err)) => { - source_file.implementation.compile_state = CompileState::Warning; - logs::append(package, err); - compile_warnings.push_str(err); - } - Ok(None) => { - source_file.implementation.compile_state = CompileState::Success; - } - Err(err) => { - source_file.implementation.compile_state = CompileState::Error; - logs::append(package, err); - compile_errors.push_str(err); - } - }; - match interface_result { - Some(Ok(Some(err))) => { - source_file.interface.as_mut().unwrap().compile_state = - CompileState::Warning; - logs::append(package, err); - compile_warnings.push_str(err); - } - Some(Ok(None)) => { - if let Some(interface) = source_file.interface.as_mut() { - interface.compile_state = CompileState::Success; - } - } + _ => (), + }; - Some(Err(err)) => { - source_file.interface.as_mut().unwrap().compile_state = - CompileState::Error; - logs::append(package, err); - compile_errors.push_str(err); - } - _ => (), - }; - match (result, interface_result) { - // successfull compilation - (Ok(None), Some(Ok(None))) | (Ok(None), None) => { - module.compile_dirty = false; - module.last_compiled_cmi = Some(SystemTime::now()); - module.last_compiled_cmt = Some(SystemTime::now()); - } - // some error or warning - (Err(_), _) - | (_, Some(Err(_))) - | (Ok(Some(_)), _) - | (_, Some(Ok(Some(_)))) => { - module.compile_dirty = true; - } - } + match (result, interface_result) { + // successfull compilation + (Ok(None), Some(Ok(None))) | (Ok(None), None) => { + module.compile_dirty = false; + module.last_compiled_cmi = Some(SystemTime::now()); + module.last_compiled_cmt = Some(SystemTime::now()); + } + // some error or warning + (Err(_), _) | (_, Some(Err(_))) | (Ok(Some(_)), _) | (_, Some(Ok(Some(_)))) => { + module.compile_dirty = true; } } } - None => (), - }); + } + } files_total_count += files_current_loop_count; @@ -349,6 +330,7 @@ pub fn compile( .map(|s| (s, build_state.get_module(s).unwrap())) .collect::>(), ); + compile_errors.push_str(&format!( "\n{}\n{}\n", style("Can't continue... Found a circular dependency in your code:").red(), @@ -360,12 +342,12 @@ pub fn compile( }; } - (compile_errors, compile_warnings, num_compiled_modules) + Ok((compile_errors, compile_warnings, num_compiled_modules)) } pub fn compiler_args( - config: &bsconfig::Config, - root_config: &bsconfig::Config, + config: &config::Config, + root_config: &config::Config, ast_path: &str, version: &str, file_path: &str, @@ -379,17 +361,17 @@ pub fn compiler_args( ) -> Vec { let normal_deps = config.bs_dependencies.as_ref().unwrap_or(&vec![]).to_owned(); - let bsc_flags = bsconfig::flatten_flags(&config.bsc_flags); + let bsc_flags = config::flatten_flags(&config.bsc_flags); // don't compile dev-deps yet // let dev_deps = source // .package - // .bsconfig + // .config // .bs_dev_dependencies // .as_ref() // .unwrap_or(&vec![]) // .to_owned(); - let deps = vec![normal_deps] + let deps = [normal_deps] .concat() .par_iter() .map(|package_name| { @@ -434,6 +416,7 @@ pub fn compiler_args( let jsx_module_args = root_config.get_jsx_module_args(); let jsx_mode_args = root_config.get_jsx_mode_args(); let uncurried_args = root_config.get_uncurried_args(version); + let gentype_arg = root_config.get_gentype_arg(); let warning_args: Vec = match config.warnings.to_owned() { None => vec![], @@ -446,16 +429,16 @@ pub fn compiler_args( }; let warn_error = match warnings.error { - Some(bsconfig::Error::Catchall(true)) => { + Some(config::Error::Catchall(true)) => { vec!["-warn-error".to_string(), "A".to_string()] } - Some(bsconfig::Error::Qualified(errors)) => { + Some(config::Error::Qualified(errors)) => { vec!["-warn-error".to_string(), errors.to_string()] } _ => vec![], }; - vec![warn_number, warn_error].concat() + [warn_number, warn_error].concat() } }; @@ -494,6 +477,7 @@ pub fn compiler_args( read_cmi_args, vec!["-I".to_string(), ".".to_string()], deps.concat(), + gentype_arg, jsx_args, jsx_module_args, jsx_mode_args, @@ -526,17 +510,21 @@ fn compile_file( packages: &AHashMap, project_root: &str, workspace_root: &Option, -) -> Result, String> { +) -> Result> { let build_path_abs = package.get_build_path(); - let implementation_file_path = match module.source_type { - SourceType::SourceFile(ref source_file) => &source_file.implementation.path, - _ => panic!("Not a source file"), - }; + let implementation_file_path = match &module.source_type { + SourceType::SourceFile(ref source_file) => Ok(&source_file.implementation.path), + sourcetype => Err(anyhow!( + "Tried to compile a file that is not a source file ({}). Path to AST: {}. ", + sourcetype, + ast_path + )), + }?; let module_name = helpers::file_path_to_module_name(implementation_file_path, &package.namespace); let has_interface = module.get_interface().is_some(); let to_mjs_args = compiler_args( - &package.bsconfig, - &root_package.bsconfig, + &package.config, + &root_package.config, ast_path, version, implementation_file_path, @@ -556,9 +544,13 @@ fn compile_file( Ok(x) if !x.status.success() => { let stderr = String::from_utf8_lossy(&x.stderr); let stdout = String::from_utf8_lossy(&x.stdout); - Err(stderr.to_string() + &stdout) + Err(anyhow!(stderr.to_string() + &stdout)) } - Err(e) => Err(format!("ERROR, {}, {:?}", e, ast_path)), + Err(e) => Err(anyhow!( + "Could not compile file. Error: {}. Path to AST: {:?}", + e, + ast_path + )), Ok(x) => { let err = std::str::from_utf8(&x.stderr) .expect("stdout should be non-null") diff --git a/rewatch/src/build/deps.rs b/rewatch/src/build/deps.rs index 5bd19395a8..2ce82b39ca 100644 --- a/rewatch/src/build/deps.rs +++ b/rewatch/src/build/deps.rs @@ -84,18 +84,15 @@ pub fn get_deps(build_state: &mut BuildState, deleted_modules: &AHashSet all_mod, ); - match &source_file.interface { - Some(interface) => { - let iast_path = package.get_iast_path(&interface.path); + if let Some(interface) = &source_file.interface { + let iast_path = package.get_iast_path(&interface.path); - deps.extend(get_dep_modules( - &iast_path, - package.namespace.to_suffix(), - package.modules.as_ref().unwrap(), - all_mod, - )) - } - None => (), + deps.extend(get_dep_modules( + &iast_path, + package.namespace.to_suffix(), + package.modules.as_ref().unwrap(), + all_mod, + )) } match &package.namespace { packages::Namespace::NamespaceWithEntry { namespace: _, entry } diff --git a/rewatch/src/build/packages.rs b/rewatch/src/build/packages.rs index b5b3ec10b8..c77334cf7c 100644 --- a/rewatch/src/build/packages.rs +++ b/rewatch/src/build/packages.rs @@ -1,10 +1,11 @@ use super::build_types::*; use super::namespaces; use super::packages; -use crate::bsconfig; +use crate::config; use crate::helpers; use crate::helpers::emojis::*; use ahash::{AHashMap, AHashSet}; +use anyhow::Result; use console::style; use log::{debug, error}; use rayon::prelude::*; @@ -39,7 +40,7 @@ impl Namespace { #[derive(Debug, Clone)] struct Dependency { name: String, - bsconfig: bsconfig::Config, + config: config::Config, path: String, is_pinned: bool, dependencies: Vec, @@ -48,8 +49,8 @@ struct Dependency { #[derive(Debug, Clone)] pub struct Package { pub name: String, - pub bsconfig: bsconfig::Config, - pub source_folders: AHashSet, + pub config: config::Config, + pub source_folders: AHashSet, // these are the relative file paths (relative to the package root) pub source_files: Option>, pub namespace: Namespace, @@ -150,7 +151,7 @@ pub fn read_folders( if metadata.file_type().is_dir() && recurse { match read_folders(filter, package_dir, &new_path, recurse) { Ok(s) => map.extend(s), - Err(e) => println!("Error reading directory: {}", e), + Err(e) => log::error!("Could not read directory: {}", e), } } @@ -167,8 +168,8 @@ pub fn read_folders( ); } - Ok(_) => println!("Filtered: {:?}", name), - Err(ref e) => println!("Error reading directory: {}", e), + Ok(_) => log::info!("Filtered: {:?}", name), + Err(ref e) => log::error!("Could not read directory: {}", e), }, _ => (), } @@ -177,36 +178,38 @@ pub fn read_folders( Ok(map) } -/// Given a projects' root folder and a `bsconfig::Source`, this recursively creates all the +/// Given a projects' root folder and a `config::Source`, this recursively creates all the /// sources in a flat list. In the process, it removes the children, as they are being resolved /// because of the recursiveness. So you get a flat list of files back, retaining the type_ and /// whether it needs to recurse into all structures -fn get_source_dirs(source: bsconfig::Source, sub_path: Option) -> AHashSet { - let mut source_folders: AHashSet = AHashSet::new(); +fn get_source_dirs(source: config::Source, sub_path: Option) -> AHashSet { + let mut source_folders: AHashSet = AHashSet::new(); + + let source_folder = source.to_qualified_without_children(sub_path.to_owned()); + source_folders.insert(source_folder.to_owned()); let (subdirs, full_recursive) = match source.to_owned() { - bsconfig::Source::Shorthand(_) - | bsconfig::Source::Qualified(bsconfig::PackageSource { subdirs: None, .. }) => (None, false), - bsconfig::Source::Qualified(bsconfig::PackageSource { - subdirs: Some(bsconfig::Subdirs::Recurse(recurse)), + config::Source::Shorthand(_) + | config::Source::Qualified(config::PackageSource { subdirs: None, .. }) => (None, false), + config::Source::Qualified(config::PackageSource { + subdirs: Some(config::Subdirs::Recurse(recurse)), .. }) => (None, recurse), - bsconfig::Source::Qualified(bsconfig::PackageSource { - subdirs: Some(bsconfig::Subdirs::Qualified(subdirs)), + config::Source::Qualified(config::PackageSource { + subdirs: Some(config::Subdirs::Qualified(subdirs)), .. }) => (Some(subdirs), false), }; - let source_folder = bsconfig::to_qualified_without_children(&source, sub_path.to_owned()); - source_folders.insert(source_folder.to_owned()); - if !full_recursive { let sub_path = Path::new(&source_folder.dir).to_path_buf(); subdirs .unwrap_or(vec![]) .par_iter() - .map(|subdir| get_source_dirs(subdir.to_owned(), Some(sub_path.to_owned()))) - .collect::>>() + .map(|subsource| { + get_source_dirs(subsource.set_type(source.get_type()), Some(sub_path.to_owned())) + }) + .collect::>>() .into_iter() .for_each(|subdir| source_folders.extend(subdir)) } @@ -214,7 +217,7 @@ fn get_source_dirs(source: bsconfig::Source, sub_path: Option) -> AHash source_folders } -pub fn read_bsconfig(package_dir: &str) -> bsconfig::Config { +pub fn read_config(package_dir: &str) -> Result { let prefix = if package_dir.is_empty() { "".to_string() } else { @@ -225,9 +228,9 @@ pub fn read_bsconfig(package_dir: &str) -> bsconfig::Config { let bsconfig_json_path = prefix.to_string() + "bsconfig.json"; if Path::new(&rescript_json_path).exists() { - bsconfig::read(rescript_json_path) + config::read(rescript_json_path) } else { - bsconfig::read(bsconfig_json_path) + config::read(bsconfig_json_path) } } @@ -276,23 +279,24 @@ pub fn read_dependency( /// # Make Package -/// Given a bsconfig, recursively finds all dependencies. +/// Given a config, recursively finds all dependencies. /// 1. It starts with registering dependencies and -/// prevents the operation for the ones which are already -/// registerd for the parent packages. Especially relevant for peerDependencies. -/// 2. In parallel performs IO to read the dependencies bsconfig and -/// recursively continues operation for their dependencies as well. +/// prevents the operation for the ones which are already +/// registerd for the parent packages. Especially relevant for peerDependencies. +/// 2. In parallel performs IO to read the dependencies config and +/// recursively continues operation for their dependencies as well. fn read_dependencies( registered_dependencies_set: &mut AHashSet, - parent_bsconfig: &bsconfig::Config, + parent_config: &config::Config, parent_path: &str, project_root: &str, workspace_root: Option, + show_progress: bool, ) -> Vec { - return parent_bsconfig + return parent_config .bs_dependencies .to_owned() - .unwrap_or(vec![]) + .unwrap_or_default() .iter() .filter_map(|package_name| { if registered_dependencies_set.contains(package_name) { @@ -303,39 +307,58 @@ fn read_dependencies( } }) .collect::>() - // Read all bsconfig files in parallel instead of blocking + // Read all config files in parallel instead of blocking .par_iter() .map(|package_name| { - let (bsconfig, canonical_path) = + let (config, canonical_path) = match read_dependency(package_name, parent_path, project_root, &workspace_root) { Err(error) => { - print!( + if show_progress { + println!( "{} {} Error building package tree. {}", style("[1/2]").bold().dim(), CROSS, error ); + } + + log::error!( + "We could not build package tree reading depencency '{package_name}', at path '{parent_path}'. Error: {error}", + ); + std::process::exit(2) } - Ok(canonical_path) => (read_bsconfig(&canonical_path), canonical_path), + Ok(canonical_path) => { + match read_config(&canonical_path) { + Ok(config) => (config, canonical_path), + Err(error) => { + log::error!( + "We could not build package tree '{package_name}', at path '{parent_path}', Error: {error}", + ); + std::process::exit(2) + } + } + } }; - let is_pinned = parent_bsconfig + + let is_pinned = parent_config .pinned_dependencies .as_ref() - .map(|p| p.contains(&bsconfig.name)) + .map(|p| p.contains(&config.name)) .unwrap_or(false); let dependencies = read_dependencies( &mut registered_dependencies_set.to_owned(), - &bsconfig, + &config, &canonical_path, project_root, workspace_root.to_owned(), + show_progress ); Dependency { name: package_name.to_owned(), - bsconfig, + config, path: canonical_path, is_pinned, dependencies, @@ -354,32 +377,34 @@ fn flatten_dependencies(dependencies: Vec) -> Vec { flattened } -fn make_package( - bsconfig: bsconfig::Config, - package_path: &str, - is_pinned_dep: bool, - is_root: bool, -) -> Package { - let source_folders = match bsconfig.sources.to_owned() { - bsconfig::OneOrMore::Single(source) => get_source_dirs(source, None), - bsconfig::OneOrMore::Multiple(sources) => { - let mut source_folders: AHashSet = AHashSet::new(); +fn make_package(config: config::Config, package_path: &str, is_pinned_dep: bool, is_root: bool) -> Package { + let source_folders = match config.sources.to_owned() { + Some(config::OneOrMore::Single(source)) => get_source_dirs(source, None), + Some(config::OneOrMore::Multiple(sources)) => { + let mut source_folders: AHashSet = AHashSet::new(); sources .iter() .map(|source| get_source_dirs(source.to_owned(), None)) - .collect::>>() + .collect::>>() .into_iter() .for_each(|source| source_folders.extend(source)); source_folders } + None => { + if !is_root { + log::warn!("Package '{}' has not defined any sources, but is not the root package. This is likely a mistake. It is located: {}", config.name, package_path); + } + + AHashSet::new() + } }; Package { - name: bsconfig.name.to_owned(), - bsconfig: bsconfig.to_owned(), + name: config.name.to_owned(), + config: config.to_owned(), source_folders, source_files: None, - namespace: bsconfig.get_namespace(), + namespace: config.get_namespace(), modules: None, // we canonicalize the path name so it's always the same path: PathBuf::from(package_path) @@ -393,58 +418,64 @@ fn make_package( } } -fn read_packages(project_root: &str, workspace_root: Option) -> AHashMap { - let root_bsconfig = read_bsconfig(project_root); +fn read_packages( + project_root: &str, + workspace_root: Option, + show_progress: bool, +) -> Result> { + let root_config = read_config(project_root)?; // Store all packages and completely deduplicate them let mut map: AHashMap = AHashMap::new(); map.insert( - root_bsconfig.name.to_owned(), - make_package(root_bsconfig.to_owned(), project_root, false, true), + root_config.name.to_owned(), + make_package(root_config.to_owned(), project_root, false, true), ); let mut registered_dependencies_set: AHashSet = AHashSet::new(); let dependencies = flatten_dependencies(read_dependencies( &mut registered_dependencies_set, - &root_bsconfig, + &root_config, project_root, project_root, workspace_root, + show_progress, )); dependencies.iter().for_each(|d| { if !map.contains_key(&d.name) { map.insert( d.name.to_owned(), - make_package(d.bsconfig.to_owned(), &d.path, d.is_pinned, false), + make_package(d.config.to_owned(), &d.path, d.is_pinned, false), ); } }); - map + Ok(map) } /// `get_source_files` is essentially a wrapper around `read_structure`, which read a /// list of files in a folder to a hashmap of `string` / `fs::Metadata` (file metadata). Reason for -/// this wrapper is the recursiveness of the `bsconfig.json` subfolders. Some sources in bsconfig +/// this wrapper is the recursiveness of the `config.json` subfolders. Some sources in config /// can be specified as being fully recursive (`{ subdirs: true }`). This wrapper pulls out that /// data from the config and pushes it forwards. Another thing is the 'type_', some files / folders /// can be marked with the type 'dev'. Which means that they may not be around in the distributed /// NPM package. The file reader allows for this, just warns when this happens. /// TODO -> Check whether we actually need the `fs::Metadata` pub fn get_source_files( + package_name: &String, package_dir: &Path, filter: &Option, - source: &bsconfig::PackageSource, + source: &config::PackageSource, ) -> AHashMap { let mut map: AHashMap = AHashMap::new(); let (recurse, type_) = match source { - bsconfig::PackageSource { - subdirs: Some(bsconfig::Subdirs::Recurse(subdirs)), + config::PackageSource { + subdirs: Some(config::Subdirs::Recurse(subdirs)), type_, .. } => (subdirs.to_owned(), type_), - bsconfig::PackageSource { type_, .. } => (false, type_), + config::PackageSource { type_, .. } => (false, type_), }; let path_dir = Path::new(&source.dir); @@ -452,13 +483,18 @@ pub fn get_source_files( if type_ != &Some("dev".to_string()) { match read_folders(filter, package_dir, path_dir, recurse) { Ok(files) => map.extend(files), - Err(_e) if type_ == &Some("dev".to_string()) => { - println!( - "Could not read folder: {}... Probably ok as type is dev", - path_dir.to_string_lossy() - ) - } - Err(_e) => println!("Could not read folder: {}...", path_dir.to_string_lossy()), + // Err(_e) if type_ == &Some("dev".to_string()) => { + // log::warn!( + // "Could not read folder: {}... Probably ok as type is dev", + // path_dir.to_string_lossy() + // ) + // } + Err(_e) => log::error!( + "Could not read folder: {:?}. Specified in dependency: {}, located {:?}...", + path_dir.to_path_buf().into_os_string(), + package_name, + package_dir + ), } } @@ -471,21 +507,21 @@ fn extend_with_children( filter: &Option, mut build: AHashMap, ) -> AHashMap { - for (_key, value) in build.iter_mut() { + for (_key, package) in build.iter_mut() { let mut map: AHashMap = AHashMap::new(); - value + package .source_folders .par_iter() - .map(|source| get_source_files(Path::new(&value.path), filter, source)) + .map(|source| get_source_files(&package.name, Path::new(&package.path), filter, source)) .collect::>>() .into_iter() .for_each(|source| map.extend(source)); let mut modules = AHashSet::from_iter( map.keys() - .map(|key| helpers::file_path_to_module_name(key, &value.namespace)), + .map(|key| helpers::file_path_to_module_name(key, &package.namespace)), ); - match value.namespace.to_owned() { + match package.namespace.to_owned() { Namespace::Namespace(namespace) => { let _ = modules.insert(namespace); } @@ -494,46 +530,52 @@ fn extend_with_children( } Namespace::NoNamespace => (), } - value.modules = Some(modules); + package.modules = Some(modules); let mut dirs = AHashSet::new(); map.keys().for_each(|path| { let dir = std::path::Path::new(&path).parent().unwrap(); dirs.insert(dir.to_owned()); }); - value.dirs = Some(dirs); - value.source_files = Some(map); + package.dirs = Some(dirs); + package.source_files = Some(map); } build } -/// Make turns a folder, that should contain a bsconfig, into a tree of Packages. +/// 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 bsconfig +/// 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 '.re', '.res', '.ml' and /// interface files. +/// /// The two step process is there to reduce IO overhead pub fn make( filter: &Option, root_folder: &str, workspace_root: &Option, -) -> AHashMap { - let map = read_packages(root_folder, workspace_root.to_owned()); + show_progress: bool, +) -> Result> { + let map = read_packages(root_folder, workspace_root.to_owned(), show_progress)?; /* 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); - result.values().for_each(|package| match &package.dirs { - Some(dirs) => dirs.iter().for_each(|dir| { - let _ = std::fs::create_dir_all(std::path::Path::new(&package.get_bs_build_path()).join(dir)); - }), - None => (), + + result.values().for_each(|package| { + if let Some(dirs) = &package.dirs { + dirs.iter().for_each(|dir| { + let _ = std::fs::create_dir_all(std::path::Path::new(&package.get_bs_build_path()).join(dir)); + }) + } }); - result + + Ok(result) } -pub fn get_package_name(path: &str) -> String { - let bsconfig = read_bsconfig(path); - bsconfig.name +pub fn get_package_name(path: &str) -> Result { + let config = read_config(path)?; + + Ok(config.name) } pub fn parse_packages(build_state: &mut BuildState) { @@ -666,10 +708,11 @@ pub fn parse_packages(build_state: &mut BuildState) { implementation_filename.pop(); match source_files.get(&implementation_filename) { None => { - println!( - "{}Warning: No implementation file found for interface file (skipping): {}", - LINE_CLEAR, file - ) + log::warn!( + "{} No implementation file found for interface file (skipping): {}", + LINE_CLEAR, + file + ) } Some(_) => { build_state @@ -723,19 +766,19 @@ pub fn parse_packages(build_state: &mut BuildState) { impl Package { pub fn get_jsx_args(&self) -> Vec { - self.bsconfig.get_jsx_args() + self.config.get_jsx_args() } pub fn get_jsx_mode_args(&self) -> Vec { - self.bsconfig.get_jsx_mode_args() + self.config.get_jsx_mode_args() } pub fn get_jsx_module_args(&self) -> Vec { - self.bsconfig.get_jsx_module_args() + self.config.get_jsx_module_args() } pub fn get_uncurried_args(&self, version: &str, root_package: &packages::Package) -> Vec { - root_package.bsconfig.get_uncurried_args(version) + root_package.config.get_uncurried_args(version) } } @@ -746,7 +789,7 @@ fn get_unallowed_dependents( ) -> Option { for deps_package_name in dependencies { if let Some(deps_package) = packages.get(deps_package_name) { - let deps_allowed_dependents = deps_package.bsconfig.allowed_dependents.to_owned(); + let deps_allowed_dependents = deps_package.config.allowed_dependents.to_owned(); if let Some(allowed_dependents) = deps_allowed_dependents { if !allowed_dependents.contains(package_name) { return Some(deps_package_name.to_string()); @@ -767,11 +810,11 @@ pub fn validate_packages_dependencies(packages: &AHashMap) -> b let mut detected_unallowed_dependencies: AHashMap = AHashMap::new(); for (package_name, package) in packages { - let bs_dependencies = &package.bsconfig.bs_dependencies.to_owned().unwrap_or(vec![]); - let pinned_dependencies = &package.bsconfig.pinned_dependencies.to_owned().unwrap_or(vec![]); - let dev_dependencies = &package.bsconfig.bs_dev_dependencies.to_owned().unwrap_or(vec![]); + let bs_dependencies = &package.config.bs_dependencies.to_owned().unwrap_or(vec![]); + let pinned_dependencies = &package.config.pinned_dependencies.to_owned().unwrap_or(vec![]); + let dev_dependencies = &package.config.bs_dev_dependencies.to_owned().unwrap_or(vec![]); - vec![ + [ ("bs-dependencies", bs_dependencies), ("pinned-dependencies", pinned_dependencies), ("bs-dev-dependencies", dev_dependencies), @@ -799,13 +842,13 @@ pub fn validate_packages_dependencies(packages: &AHashMap) -> b }); } for (package_name, unallowed_deps) in detected_unallowed_dependencies.iter() { - println!( + log::error!( "\n{}: {} has the following unallowed dependencies:", console::style("Error").red(), console::style(package_name).bold() ); - vec![ + [ ("bs-dependencies", unallowed_deps.bs_deps.to_owned()), ("pinned-dependencies", unallowed_deps.pinned_deps.to_owned()), ("bs-dev-dependencies", unallowed_deps.bs_dev_deps.to_owned()), @@ -813,7 +856,7 @@ pub fn validate_packages_dependencies(packages: &AHashMap) -> b .iter() .for_each(|(deps_type, map)| { if !map.is_empty() { - println!( + log::info!( "{} dependencies: {}", console::style(deps_type).bold().dim(), console::style(map.join(" \n -")).bold().dim() @@ -824,10 +867,10 @@ pub fn validate_packages_dependencies(packages: &AHashMap) -> b let has_any_unallowed_dependent = detected_unallowed_dependencies.len() > 0; if has_any_unallowed_dependent { - println!( + log::error!( "\nUpdate the {} value in the {} of the unallowed dependencies to solve the issue!", console::style("unallowed_dependents").bold().dim(), - console::style("bsconfig.json").bold().dim() + console::style("config.json").bold().dim() ) } !has_any_unallowed_dependent @@ -835,7 +878,7 @@ pub fn validate_packages_dependencies(packages: &AHashMap) -> b #[cfg(test)] mod test { - use crate::bsconfig::Source; + use crate::config::Source; use ahash::{AHashMap, AHashSet}; use super::{Namespace, Package}; @@ -847,11 +890,13 @@ mod test { dev_deps: Vec, allowed_dependents: Option>, ) -> Package { - return Package { + Package { name: name.clone(), - bsconfig: crate::bsconfig::Config { + config: crate::config::Config { name: name.clone(), - sources: crate::bsconfig::OneOrMore::Single(Source::Shorthand(String::from("Source"))), + sources: Some(crate::config::OneOrMore::Single(Source::Shorthand(String::from( + "Source", + )))), package_specs: None, warnings: None, suffix: None, @@ -864,6 +909,7 @@ mod test { namespace: None, jsx: None, uncurried: None, + gentype_config: None, namespace_entry: None, allowed_dependents, }, @@ -875,7 +921,7 @@ mod test { dirs: None, is_pinned_dep: false, is_root: false, - }; + } } #[test] fn should_return_false_with_invalid_parents_as_bs_dependencies() { @@ -902,7 +948,7 @@ mod test { ); let is_valid = super::validate_packages_dependencies(&packages); - assert_eq!(is_valid, false) + assert!(!is_valid) } #[test] @@ -930,7 +976,7 @@ mod test { ); let is_valid = super::validate_packages_dependencies(&packages); - assert_eq!(is_valid, false) + assert!(!is_valid) } #[test] @@ -958,7 +1004,7 @@ mod test { ); let is_valid = super::validate_packages_dependencies(&packages); - assert_eq!(is_valid, false) + assert!(!is_valid) } #[test] @@ -986,6 +1032,6 @@ mod test { ); let is_valid = super::validate_packages_dependencies(&packages); - assert_eq!(is_valid, true) + assert!(is_valid) } } diff --git a/rewatch/src/build/parse.rs b/rewatch/src/build/parse.rs index 678a4a353c..b8a344941a 100644 --- a/rewatch/src/build/parse.rs +++ b/rewatch/src/build/parse.rs @@ -2,8 +2,8 @@ use super::build_types::*; use super::logs; use super::namespaces; use super::packages; -use crate::bsconfig; -use crate::bsconfig::OneOrMore; +use crate::config; +use crate::config::OneOrMore; use crate::helpers; use ahash::AHashSet; use log::debug; @@ -172,7 +172,6 @@ pub fn generate_asts( } Ok(None) => { // The file had no interface file associated - () } } }; @@ -249,8 +248,8 @@ pub fn generate_asts( } pub fn parser_args( - config: &bsconfig::Config, - root_config: &bsconfig::Config, + config: &config::Config, + root_config: &config::Config, filename: &str, version: &str, workspace_root: &Option, @@ -261,7 +260,7 @@ pub fn parser_args( let path = PathBuf::from(filename); let ast_extension = path_to_ast_extension(&path); let ast_path = (helpers::get_basename(&file.to_string()).to_owned()) + ast_extension; - let ppx_flags = bsconfig::flatten_ppx_flags( + let ppx_flags = config::flatten_ppx_flags( &if let Some(workspace_root) = workspace_root { format!("{}/node_modules", &workspace_root) } else { @@ -274,12 +273,12 @@ pub fn parser_args( let jsx_module_args = root_config.get_jsx_module_args(); let jsx_mode_args = root_config.get_jsx_mode_args(); let uncurried_args = root_config.get_uncurried_args(version); - let bsc_flags = bsconfig::flatten_flags(&config.bsc_flags); + let bsc_flags = config::flatten_flags(&config.bsc_flags); let file = "../../".to_string() + file; ( ast_path.to_string(), - vec![ + [ vec!["-bs-v".to_string(), format!("{}", version)], ppx_flags, jsx_args, @@ -312,8 +311,8 @@ fn generate_ast( let build_path_abs = package.get_build_path(); let (ast_path, parser_args) = parser_args( - &package.bsconfig, - &root_package.bsconfig, + &package.config, + &root_package.config, filename, version, workspace_root, @@ -340,23 +339,21 @@ fn generate_ast( Ok((ast_path, None)) } } else { - println!("Parsing file {}...", filename); + log::info!("Parsing file {}...", filename); + Err(format!( "Could not find canonicalize_string_path for file {} in package {}", filename, package.name )) }; - match &result { - Ok((ast_path, _)) => { - let dir = std::path::Path::new(filename).parent().unwrap(); - let _ = std::fs::copy( - build_path_abs.to_string() + "/" + ast_path, - std::path::Path::new(&package.get_bs_build_path()) - .join(dir) - .join(ast_path), - ); - } - Err(_) => (), + if let Ok((ast_path, _)) = &result { + let dir = std::path::Path::new(filename).parent().unwrap(); + let _ = std::fs::copy( + build_path_abs.to_string() + "/" + ast_path, + std::path::Path::new(&package.get_bs_build_path()) + .join(dir) + .join(ast_path), + ); } result } @@ -373,17 +370,17 @@ fn path_to_ast_extension(path: &Path) -> &str { fn include_ppx(flag: &str, contents: &str) -> bool { if flag.contains("bisect") { return std::env::var("BISECT_ENABLE").is_ok(); - } else if (flag.contains("graphql-ppx") || flag.contains("graphql_ppx")) && !contents.contains("%graphql") + } + + if ((flag.contains("graphql-ppx") || flag.contains("graphql_ppx")) && !contents.contains("%graphql")) + || (flag.contains("spice") && !contents.contains("@spice")) + || (flag.contains("rescript-relay") && !contents.contains("%relay")) + || (flag.contains("re-formality") && !contents.contains("%form")) { return false; - } else if flag.contains("spice") && !contents.contains("@spice") { - return false; - } else if flag.contains("rescript-relay") && !contents.contains("%relay") { - return false; - } else if flag.contains("re-formality") && !contents.contains("%form") { - return false; - } - return true; + }; + + true } fn filter_ppx_flags( @@ -395,8 +392,8 @@ fn filter_ppx_flags( flags .iter() .filter(|flag| match flag { - bsconfig::OneOrMore::Single(str) => include_ppx(str, contents), - bsconfig::OneOrMore::Multiple(str) => include_ppx(str.first().unwrap(), contents), + config::OneOrMore::Single(str) => include_ppx(str, contents), + config::OneOrMore::Multiple(str) => include_ppx(str.first().unwrap(), contents), }) .map(|x| x.to_owned()) .collect::>>() diff --git a/rewatch/src/build/read_compile_state.rs b/rewatch/src/build/read_compile_state.rs index 7d983f5a50..b6cbc9a3aa 100644 --- a/rewatch/src/build/read_compile_state.rs +++ b/rewatch/src/build/read_compile_state.rs @@ -103,7 +103,7 @@ pub fn read(build_state: &mut BuildState) -> CompileAssetsState { last_modified: last_modified.to_owned(), ast_file_path, is_root: *package_is_root, - suffix: root_package.bsconfig.get_suffix(), + suffix: root_package.config.get_suffix(), }, ); let _ = ast_rescript_file_locations.insert(res_file_path); diff --git a/rewatch/src/cmd.rs b/rewatch/src/cmd.rs index acc3c10fa6..be149338ae 100644 --- a/rewatch/src/cmd.rs +++ b/rewatch/src/cmd.rs @@ -7,7 +7,7 @@ use std::time::Instant; pub fn run(command_string: String) { let start_subcommand = Instant::now(); - print!( + log::info!( "{} {}Running subcommand... \n{}\n", style("[...]").bold().dim(), COMMAND, @@ -39,11 +39,11 @@ pub fn run(command_string: String) { } for line in std_err { - println!("{}", line.unwrap()); + eprintln!("{}", line.unwrap()); } let subcommand_duration = start_subcommand.elapsed(); - println!( + log::info!( "{}{} {}Ran subcommand in {:.2}s", LINE_CLEAR, style("[...]").bold().dim(), diff --git a/rewatch/src/bsconfig.rs b/rewatch/src/config.rs similarity index 55% rename from rewatch/src/bsconfig.rs rename to rewatch/src/config.rs index 3abcf40621..c575b90e25 100644 --- a/rewatch/src/bsconfig.rs +++ b/rewatch/src/config.rs @@ -1,4 +1,6 @@ use crate::build::packages; +use crate::helpers::deserialize::*; +use anyhow::Result; use convert_case::{Case, Casing}; use serde::Deserialize; use std::fs; @@ -27,45 +29,6 @@ pub struct PackageSource { pub type_: Option, } -/// `to_qualified_without_children` takes a tree like structure of dependencies, coming in from -/// `bsconfig`, and turns it into a flat list. The main thing we extract here are the source -/// folders, and optional subdirs, where potentially, the subdirs recurse or not. -pub fn to_qualified_without_children(s: &Source, sub_path: Option) -> PackageSource { - match s { - Source::Shorthand(dir) => PackageSource { - dir: sub_path - .map(|p| p.join(Path::new(dir))) - .unwrap_or(Path::new(dir).to_path_buf()) - .to_string_lossy() - .to_string(), - subdirs: None, - type_: None, - }, - Source::Qualified(PackageSource { - dir, - type_, - subdirs: Some(Subdirs::Recurse(should_recurse)), - }) => PackageSource { - dir: sub_path - .map(|p| p.join(Path::new(dir))) - .unwrap_or(Path::new(dir).to_path_buf()) - .to_string_lossy() - .to_string(), - subdirs: Some(Subdirs::Recurse(*should_recurse)), - type_: type_.to_owned(), - }, - Source::Qualified(PackageSource { dir, type_, .. }) => PackageSource { - dir: sub_path - .map(|p| p.join(Path::new(dir))) - .unwrap_or(Path::new(dir).to_path_buf()) - .to_string_lossy() - .to_string(), - subdirs: None, - type_: type_.to_owned(), - }, - } -} - impl Eq for PackageSource {} #[derive(Deserialize, Debug, Clone, PartialEq, Hash)] @@ -74,12 +37,76 @@ pub enum Source { Shorthand(String), Qualified(PackageSource), } + +impl Source { + /// When reading, we should propagate the sources all the way through the tree + pub fn get_type(&self) -> Option { + match self { + Source::Shorthand(_) => None, + Source::Qualified(PackageSource { type_, .. }) => type_.clone(), + } + } + pub fn set_type(&self, type_: Option) -> Source { + match (self, type_) { + (Source::Shorthand(dir), Some(type_)) => Source::Qualified(PackageSource { + dir: dir.to_string(), + subdirs: None, + type_: Some(type_), + }), + (Source::Qualified(package_source), type_) => Source::Qualified(PackageSource { + type_, + ..package_source.clone() + }), + (source, _) => source.clone(), + } + } + + /// `to_qualified_without_children` takes a tree like structure of dependencies, coming in from + /// `bsconfig`, and turns it into a flat list. The main thing we extract here are the source + /// folders, and optional subdirs, where potentially, the subdirs recurse or not. + pub fn to_qualified_without_children(&self, sub_path: Option) -> PackageSource { + match self { + Source::Shorthand(dir) => PackageSource { + dir: sub_path + .map(|p| p.join(Path::new(dir))) + .unwrap_or(Path::new(dir).to_path_buf()) + .to_string_lossy() + .to_string(), + subdirs: None, + type_: self.get_type(), + }, + Source::Qualified(PackageSource { + dir, + type_, + subdirs: Some(Subdirs::Recurse(should_recurse)), + }) => PackageSource { + dir: sub_path + .map(|p| p.join(Path::new(dir))) + .unwrap_or(Path::new(dir).to_path_buf()) + .to_string_lossy() + .to_string(), + subdirs: Some(Subdirs::Recurse(*should_recurse)), + type_: type_.to_owned(), + }, + Source::Qualified(PackageSource { dir, type_, .. }) => PackageSource { + dir: sub_path + .map(|p| p.join(Path::new(dir))) + .unwrap_or(Path::new(dir).to_path_buf()) + .to_string_lossy() + .to_string(), + subdirs: None, + type_: type_.to_owned(), + }, + } + } +} + impl Eq for Source {} #[derive(Deserialize, Debug, Clone)] pub struct PackageSpec { pub module: String, - #[serde(rename = "in-source")] + #[serde(rename = "in-source", default = "default_true")] pub in_source: bool, pub suffix: Option, } @@ -97,10 +124,14 @@ pub struct Warnings { pub error: Option, } -#[derive(Deserialize, Debug, Clone)] -pub struct Reason { - #[serde(rename = "react-jsx")] - pub react_jsx: i32, +#[derive(Deserialize, Debug, Clone, PartialEq, Hash)] +#[serde(untagged)] +pub enum Reason { + Versioned { + #[serde(rename = "react-jsx")] + react_jsx: i32, + }, + Unversioned(bool), } #[derive(Deserialize, Debug, Clone)] @@ -110,21 +141,22 @@ pub enum NamespaceConfig { String(String), } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug, Clone, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] pub enum JsxMode { - #[serde(rename = "classic")] Classic, - #[serde(rename = "automatic")] Automatic, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug, Clone, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +#[serde(untagged)] pub enum JsxModule { - #[serde(rename = "react")] React, + Other(String), } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug, Clone, Eq, PartialEq)] pub struct JsxSpecs { pub version: Option, pub module: Option, @@ -133,12 +165,17 @@ pub struct JsxSpecs { pub v3_dependencies: Option>, } +/// We do not care about the internal structure because the gentype config is loaded by bsc. +pub type GenTypeConfig = serde_json::Value; + /// # bsconfig.json representation /// This is tricky, there is a lot of ambiguity. This is probably incomplete. #[derive(Deserialize, Debug, Clone)] pub struct Config { pub name: String, - pub sources: OneOrMore, + // In the case of monorepos, the root source won't necessarily have to have sources. It can + // just be sources in packages + pub sources: Option>, #[serde(rename = "package-specs")] pub package_specs: Option>, pub warnings: Option, @@ -157,6 +194,8 @@ pub struct Config { pub namespace: Option, pub jsx: Option, pub uncurried: Option, + #[serde(rename = "gentypeconfig")] + pub gentype_config: Option, // this is a new feature of rewatch, and it's not part of the bsconfig.json spec #[serde(rename = "namespace-entry")] pub namespace_entry: Option, @@ -229,17 +268,22 @@ pub fn flatten_ppx_flags( } /// Try to convert a bsconfig from a certain path to a bsconfig struct -pub fn read(path: String) -> Config { - fs::read_to_string(path.clone()) - .map_err(|e| format!("Could not read bsconfig. {path} - {e}")) - .and_then(|x| { - serde_json::from_str::(&x).map_err(|e| format!("Could not parse bsconfig. {path} - {e}")) - }) - .expect("Errors reading bsconfig") +pub fn read(path: String) -> Result { + let read = fs::read_to_string(path.clone())?; + let parse = serde_json::from_str::(&read)?; + + Ok(parse) } -fn check_if_rescript11_or_higher(version: &str) -> bool { - version.split('.').next().unwrap().parse::().unwrap() >= 11 +fn check_if_rescript11_or_higher(version: &str) -> Result { + version + .split('.') + .next() + .and_then(|s| s.parse::().ok()) + .map_or( + Err("Could not parse version".to_string()), + |major| Ok(major >= 11), + ) } fn namespace_from_package_name(package_name: &str) -> String { @@ -295,8 +339,12 @@ impl Config { Some(_version) => panic!("Unsupported JSX version"), None => vec![], }, - (Some(reason), None) => { - vec!["-bs-jsx".to_string(), format!("{}", reason.react_jsx)] + (Some(Reason::Versioned { react_jsx }), None) => { + vec!["-bs-jsx".to_string(), format!("{}", react_jsx)] + } + (Some(Reason::Unversioned(true)), None) => { + // If Reason is 'true' - we should default to the latest + vec!["-bs-jsx".to_string()] } _ => vec![], } @@ -324,6 +372,9 @@ impl Config { Some(JsxModule::React) => { vec!["-bs-jsx-module".to_string(), "react".to_string()] } + Some(JsxModule::Other(module)) => { + vec!["-bs-jsx-module".to_string(), module] + } None => vec![], }, _ => vec![], @@ -331,14 +382,17 @@ impl Config { } pub fn get_uncurried_args(&self, version: &str) -> Vec { - if check_if_rescript11_or_higher(version) { - match self.uncurried.to_owned() { + match check_if_rescript11_or_higher(version) { + Ok(true) => match self.uncurried.to_owned() { // v11 is always uncurried except iff explicitly set to false in the root rescript.json Some(false) => vec![], _ => vec!["-uncurried".to_string()], + }, + Ok(false) => vec![], + Err(_) => { + eprintln!("Could not establish Rescript Version number for uncurried mode. Defaulting to Rescript < 11, disabling uncurried mode. Please specify an exact version if you need > 11 and default uncurried mode. Version: {}", version); + vec![] } - } else { - vec![] } } @@ -366,6 +420,13 @@ impl Config { .or(self.suffix.to_owned()) .unwrap_or(".js".to_string()) } + + pub fn get_gentype_arg(&self) -> Vec { + match &self.gentype_config { + Some(_) => vec!["-bs-gentype".to_string()], + None => vec![], + } + } } #[cfg(test)] @@ -389,4 +450,111 @@ mod tests { assert_eq!(config.get_suffix(), ".mjs"); assert_eq!(config.get_module(), "es6"); } + + #[test] + fn test_sources() { + let json = r#" + { + "name": "@rescript/core", + "version": "0.5.0", + "sources": { + "dir": "test", + "subdirs": ["intl"], + "type": "dev" + }, + "suffix": ".mjs", + "package-specs": { + "module": "esmodule", + "in-source": true + }, + "bs-dev-dependencies": ["@rescript/tools"], + "warnings": { + "error": "+101" + } + } + "#; + + let config = serde_json::from_str::(json).unwrap(); + if let Some(OneOrMore::Single(source)) = config.sources { + let source = source.to_qualified_without_children(None); + assert_eq!(source.type_, Some(String::from("dev"))); + } else { + dbg!(config.sources); + unreachable!() + } + } + + #[test] + fn test_detect_gentypeconfig() { + let json = r#" + { + "name": "my-monorepo", + "sources": [ { "dir": "src/", "subdirs": true } ], + "package-specs": [ { "module": "es6", "in-source": true } ], + "suffix": ".mjs", + "pinned-dependencies": [ "@teamwalnut/app" ], + "bs-dependencies": [ "@teamwalnut/app" ], + "gentypeconfig": { + "module": "esmodule", + "generatedFileExtension": ".gen.tsx" + } + } + "#; + + let config = serde_json::from_str::(json).unwrap(); + assert!(config.gentype_config.is_some()); + assert_eq!(config.get_gentype_arg(), vec!["-bs-gentype".to_string()]); + } + + #[test] + fn test_other_jsx_module() { + let json = r#" + { + "name": "my-monorepo", + "sources": [ { "dir": "src/", "subdirs": true } ], + "package-specs": [ { "module": "es6", "in-source": true } ], + "suffix": ".mjs", + "pinned-dependencies": [ "@teamwalnut/app" ], + "bs-dependencies": [ "@teamwalnut/app" ], + "jsx": { + "module": "Voby.JSX" + } + } + "#; + + let config = serde_json::from_str::(json).unwrap(); + assert!(config.jsx.is_some()); + assert_eq!( + config.jsx.unwrap(), + JsxSpecs { + version: None, + module: Some(JsxModule::Other(String::from("Voby.JSX"))), + mode: None, + v3_dependencies: None, + }, + ); + } + + #[test] + fn test_check_if_rescript11_or_higher() { + assert_eq!(check_if_rescript11_or_higher("11.0.0"), Ok(true)); + assert_eq!(check_if_rescript11_or_higher("11.0.1"), Ok(true)); + assert_eq!(check_if_rescript11_or_higher("11.1.0"), Ok(true)); + + assert_eq!(check_if_rescript11_or_higher("12.0.0"), Ok(true)); + + assert_eq!(check_if_rescript11_or_higher("10.0.0"), Ok(false)); + assert_eq!(check_if_rescript11_or_higher("9.0.0"), Ok(false)); + } + + #[test] + fn test_check_if_rescript11_or_higher_misc() { + assert_eq!(check_if_rescript11_or_higher("11"), Ok(true)); + assert_eq!(check_if_rescript11_or_higher("12.0.0-alpha.4"), Ok(true)); + + match check_if_rescript11_or_higher("*") { + Ok(_) => unreachable!("Should not parse"), + Err(_) => assert!(true), + } + } } diff --git a/rewatch/src/helpers.rs b/rewatch/src/helpers.rs index 0202f111d1..0f1e76da07 100644 --- a/rewatch/src/helpers.rs +++ b/rewatch/src/helpers.rs @@ -10,14 +10,24 @@ use std::time::{SystemTime, UNIX_EPOCH}; pub type StdErr = String; +pub mod deserialize { + pub fn default_false() -> bool { + false + } + + pub fn default_true() -> bool { + true + } +} + pub mod emojis { use console::Emoji; pub static COMMAND: Emoji<'_, '_> = Emoji("πŸƒ ", ""); pub static TREE: Emoji<'_, '_> = Emoji("πŸ“¦ ", ""); pub static SWEEP: Emoji<'_, '_> = Emoji("🧹 ", ""); - pub static LOOKING_GLASS: Emoji<'_, '_> = Emoji("πŸ•΅οΈ ", ""); + pub static LOOKING_GLASS: Emoji<'_, '_> = Emoji("πŸ•΅οΈ ", ""); pub static CODE: Emoji<'_, '_> = Emoji("🧱 ", ""); - pub static SWORDS: Emoji<'_, '_> = Emoji("🀺 ️", ""); + pub static SWORDS: Emoji<'_, '_> = Emoji("βš”οΈ ", ""); pub static DEPS: Emoji<'_, '_> = Emoji("️🌴 ", ""); pub static CHECKMARK: Emoji<'_, '_> = Emoji("οΈβœ… ", ""); pub static CROSS: Emoji<'_, '_> = Emoji("οΈπŸ›‘ ", ""); @@ -140,6 +150,7 @@ pub fn get_bsc(root_path: &str, workspace_root: Option) -> String { let subfolder = match (std::env::consts::OS, std::env::consts::ARCH) { ("macos", "aarch64") => "darwinarm64", ("macos", _) => "darwin", + ("linux", "aarch64") => "linuxarm64", ("linux", _) => "linux", ("windows", _) => "win32", _ => panic!("Unsupported architecture"), @@ -296,11 +307,11 @@ fn has_rescript_config(path: &Path) -> bool { pub fn get_workspace_root(package_root: &str) -> Option { std::path::PathBuf::from(&package_root) .parent() - .and_then(get_nearest_bsconfig) + .and_then(get_nearest_config) } -// traverse up the directory tree until we find a bsconfig.json, if not return None -pub fn get_nearest_bsconfig(path_buf: &Path) -> Option { +// traverse up the directory tree until we find a config.json, if not return None +pub fn get_nearest_config(path_buf: &Path) -> Option { let mut current_dir = path_buf.to_owned(); loop { if has_rescript_config(¤t_dir) { diff --git a/rewatch/src/lib.rs b/rewatch/src/lib.rs index b84aed04a4..9dc6f5591c 100644 --- a/rewatch/src/lib.rs +++ b/rewatch/src/lib.rs @@ -1,6 +1,6 @@ -pub mod bsconfig; pub mod build; pub mod cmd; +pub mod config; pub mod helpers; pub mod lock; pub mod queue; diff --git a/rewatch/src/lock.rs b/rewatch/src/lock.rs index 98ab21769f..dfadcfbb4e 100644 --- a/rewatch/src/lock.rs +++ b/rewatch/src/lock.rs @@ -21,9 +21,9 @@ pub enum Error { impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let msg = match self { - Error::Locked(pid) => format!("Rewatch is already running with PID {}", pid), - Error::ParsingLockfile(e) => format!("Could not parse lockfile: \n {}", e), - Error::ReadingLockfile(e) => format!("Could not read lockfile: \n {}", e), + Error::Locked(pid) => format!("Rewatch is already running. The process ID (PID) is {}", pid), + Error::ParsingLockfile(e) => format!("Could not parse lockfile: \n {} \n (try removing it and running the command again)", e), + Error::ReadingLockfile(e) => format!("Could not read lockfile: \n {} \n (try removing it and running the command again)", e), Error::WritingLockfile(e) => format!("Could not write lockfile: \n {}", e), }; write!(f, "{}", msg) diff --git a/rewatch/src/main.rs b/rewatch/src/main.rs index 3624035212..565db3d93f 100644 --- a/rewatch/src/main.rs +++ b/rewatch/src/main.rs @@ -1,5 +1,9 @@ +use anyhow::Result; use clap::{Parser, ValueEnum}; +use clap_verbosity_flag::InfoLevel; +use log::LevelFilter; use regex::Regex; +use std::io::Write; use rewatch::{build, cmd, lock, watcher}; @@ -22,7 +26,7 @@ struct Args { #[arg(value_enum)] command: Option, - /// The relative path to where the main bsconfig.json resides. IE - the root of your project. + /// The relative path to where the main rescript.json resides. IE - the root of your project. folder: Option, /// Filter allows for a regex to be supplied which will filter the files to be compiled. For @@ -40,6 +44,16 @@ struct Args { #[arg(short, long)] no_timing: Option, + /// Verbosity: + /// -v -> Debug + /// -vv -> Trace + /// -q -> Warn + /// -qq -> Error + /// -qqq -> Off. + /// Default (/ no argument given): 'info' + #[command(flatten)] + verbose: clap_verbosity_flag::Verbosity, + /// This creates a source_dirs.json file at the root of the monorepo, which is needed when you /// want to use Reanalyze #[arg(short, long)] @@ -59,9 +73,15 @@ struct Args { bsc_path: Option, } -fn main() { - env_logger::init(); +fn main() -> Result<()> { let args = Args::parse(); + let log_level_filter = args.verbose.log_level_filter(); + + env_logger::Builder::new() + .format(|buf, record| writeln!(buf, "{}:\n{}", record.level(), record.args())) + .filter_level(log_level_filter) + .target(env_logger::fmt::Target::Stdout) + .init(); let command = args.command.unwrap_or(Command::Build); let folder = args.folder.unwrap_or(".".to_string()); @@ -74,29 +94,34 @@ fn main() { Some(path) => { println!( "{}", - build::get_compiler_args(&path, args.rescript_version, args.bsc_path) + build::get_compiler_args(&path, args.rescript_version, args.bsc_path)? ); std::process::exit(0); } } + // The 'normal run' mode will show the 'pretty' formatted progress. But if we turn off the log + // level, we should never show that. + let show_progress = log_level_filter == LevelFilter::Info; + match lock::get(&folder) { lock::Lock::Error(ref e) => { - eprintln!("Error while trying to get lock: {e}"); + log::error!("Could not start Rewatch: {e}"); std::process::exit(1) } lock::Lock::Aquired(_) => match command { - Command::Clean => build::clean::clean(&folder, args.bsc_path), + Command::Clean => build::clean::clean(&folder, show_progress, args.bsc_path), Command::Build => { match build::build( &filter, &folder, + show_progress, args.no_timing.unwrap_or(false), args.create_sourcedirs.unwrap_or(false), args.bsc_path, ) { Err(e) => { - eprintln!("Error Building: {e}"); + log::error!("{e}"); std::process::exit(1) } Ok(_) => { @@ -110,10 +135,13 @@ fn main() { Command::Watch => { watcher::start( &filter, + show_progress, &folder, args.after_build, args.create_sourcedirs.unwrap_or(false), ); + + Ok(()) } }, } diff --git a/rewatch/src/sourcedirs.rs b/rewatch/src/sourcedirs.rs index d50f5636aa..ff206639eb 100644 --- a/rewatch/src/sourcedirs.rs +++ b/rewatch/src/sourcedirs.rs @@ -20,7 +20,7 @@ pub struct SourceDirs<'a> { pub generated: &'a Vec, } -fn package_to_dirs<'a>(package: &'a Package, root_package_path: &String) -> AHashSet { +fn package_to_dirs(package: &Package, root_package_path: &String) -> AHashSet { let relative_path = PathBuf::from(&package.path) .strip_prefix(PathBuf::from(&root_package_path)) .unwrap() @@ -72,13 +72,13 @@ pub fn print(buildstate: &BuildState) { .filter(|(_name, package)| !package.is_root) .map(|(_name, package)| { // Extract Directories - let dirs = package_to_dirs(&package, &root_package.path); + let dirs = package_to_dirs(package, &root_package.path); // Extract Pkgs let pkgs = [ - &package.bsconfig.pinned_dependencies, - &package.bsconfig.bs_dependencies, - &package.bsconfig.bs_dev_dependencies, + &package.config.pinned_dependencies, + &package.config.bs_dependencies, + &package.config.bs_dev_dependencies, ] .into_iter() .map(|dependencies| deps_to_pkgs(&buildstate.packages, dependencies)); diff --git a/rewatch/src/structure_hashmap.rs b/rewatch/src/structure_hashmap.rs deleted file mode 100644 index 1e3abf4094..0000000000 --- a/rewatch/src/structure_hashmap.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::helpers::{is_source_file, LexicalAbsolute}; -use ahash::AHashMap; -use std::path::PathBuf; -use std::{error, fs}; - -pub fn read_folders( - filter: &Option, - path: &str, - recurse: bool, -) -> Result, Box> { - let mut map: AHashMap = AHashMap::new(); - - let path_buf = PathBuf::from(path); - let abs_path = path_buf - .to_lexical_absolute() - .map(|x| x.to_str().map(|y| y.to_string()).unwrap_or("".to_string())) - .and_then(|x| fs::metadata(x.to_owned()).map(|m| (x.to_owned(), m))); - - for entry in fs::read_dir(path.replace("//", "/"))? { - let path_buf = entry.map(|entry| entry.path())?; - let metadata = fs::metadata(&path_buf)?; - let name = path_buf - .file_name() - .and_then(|x| x.to_str()) - .unwrap_or("Unknown") - .to_string(); - - let path_ext = path_buf.extension().and_then(|x| x.to_str()); - - if metadata.file_type().is_dir() && recurse { - match read_folders(&filter, &(path.to_owned() + "/" + &name + "/"), recurse) { - Ok(s) => map.extend(s), - Err(e) => println!("Error reading directory: {}", e), - } - } - match path_ext { - Some(extension) if is_source_file(extension) => match abs_path { - Ok((ref path, _)) - if filter - .as_ref() - .map(|re| !re.is_match(&name)) - .unwrap_or(true) => - { - map.insert(path.to_owned() + "/" + &name, metadata); - } - Ok(_) => println!("Filtered: {:?}", name), - Err(ref e) => println!("Error reading directory: {}", e), - }, - _ => (), - } - } - - Ok(map) -} diff --git a/rewatch/src/watcher.rs b/rewatch/src/watcher.rs index 74251e5045..9fc140a181 100644 --- a/rewatch/src/watcher.rs +++ b/rewatch/src/watcher.rs @@ -49,11 +49,13 @@ fn matches_filter(path_buf: &Path, filter: &Option) -> bool { async fn async_watch( q: Arc>>, path: &str, + show_progress: bool, filter: &Option, after_build: Option, create_sourcedirs: bool, ) -> notify::Result<()> { - let mut build_state = build::initialize_build(None, filter, path, None).expect("Can't initialize build"); + let mut build_state = + build::initialize_build(None, filter, show_progress, path, None).expect("Can't initialize build"); let mut needs_compile_type = CompileType::Incremental; // create a mutex to capture if ctrl-c was pressed let ctrlc_pressed = Arc::new(Mutex::new(false)); @@ -70,7 +72,9 @@ async fn async_watch( loop { if *ctrlc_pressed_clone.lock().unwrap() { - println!("\nExiting..."); + if show_progress { + println!("\nExiting..."); + } clean::cleanup_after_build(&build_state); break Ok(()); } @@ -183,6 +187,7 @@ async fn async_watch( &mut build_state, None, initial_build, + show_progress, !initial_build, create_sourcedirs, ) @@ -192,23 +197,31 @@ async fn async_watch( cmd::run(a) } let timing_total_elapsed = timing_total.elapsed(); - println!( - "\n{}{}Finished {} compilation in {:.2}s\n", - LINE_CLEAR, - SPARKLES, - if initial_build { "initial" } else { "incremental" }, - timing_total_elapsed.as_secs_f64() - ); + if show_progress { + println!( + "\n{}{}Finished {} compilation in {:.2}s\n", + LINE_CLEAR, + SPARKLES, + if initial_build { "initial" } else { "incremental" }, + timing_total_elapsed.as_secs_f64() + ); + } } needs_compile_type = CompileType::None; initial_build = false; } CompileType::Full => { let timing_total = Instant::now(); - build_state = - build::initialize_build(None, filter, path, None).expect("Can't initialize build"); - let _ = - build::incremental_build(&mut build_state, None, initial_build, false, create_sourcedirs); + build_state = build::initialize_build(None, filter, show_progress, path, None) + .expect("Can't initialize build"); + let _ = build::incremental_build( + &mut build_state, + None, + initial_build, + show_progress, + false, + create_sourcedirs, + ); if let Some(a) = after_build.clone() { cmd::run(a) } @@ -216,12 +229,14 @@ async fn async_watch( build::write_build_ninja(&build_state); let timing_total_elapsed = timing_total.elapsed(); - println!( - "\n{}{}Finished compilation in {:.2}s\n", - LINE_CLEAR, - SPARKLES, - timing_total_elapsed.as_secs_f64() - ); + if show_progress { + println!( + "\n{}{}Finished compilation in {:.2}s\n", + LINE_CLEAR, + SPARKLES, + timing_total_elapsed.as_secs_f64() + ); + } needs_compile_type = CompileType::None; initial_build = false; } @@ -236,6 +251,7 @@ async fn async_watch( pub fn start( filter: &Option, + show_progress: bool, folder: &str, after_build: Option, create_sourcedirs: bool, @@ -251,8 +267,17 @@ pub fn start( .watch(folder.as_ref(), RecursiveMode::Recursive) .expect("Could not start watcher"); - if let Err(e) = async_watch(consumer, folder, filter, after_build, create_sourcedirs).await { - println!("error: {:?}", e) + if let Err(e) = async_watch( + consumer, + folder, + show_progress, + filter, + after_build, + create_sourcedirs, + ) + .await + { + log::error!("{:?}", e) } }) } diff --git a/rewatch/tests/lib/rewatch.lock b/rewatch/tests/lib/rewatch.lock index da27e6c542..22da5d207a 100644 --- a/rewatch/tests/lib/rewatch.lock +++ b/rewatch/tests/lib/rewatch.lock @@ -1 +1 @@ -57150 \ No newline at end of file +36062 \ No newline at end of file diff --git a/rewatch/tests/lock.sh b/rewatch/tests/lock.sh index 65d48cb6fd..019e6166b0 100755 --- a/rewatch/tests/lock.sh +++ b/rewatch/tests/lock.sh @@ -23,7 +23,7 @@ success "Watcher Started" sleep 1 -if rewatch watch 2>&1 | grep 'Error while trying to get lock:' &> /dev/null; +if rewatch watch 2>&1 | grep 'Could not start Rewatch:' &> /dev/null; then success "Lock is correctly set" exit_watcher @@ -41,7 +41,7 @@ success "Watcher Started" sleep 1 -if cat tmp.txt | grep 'Error while trying to get lock:' &> /dev/null; +if cat tmp.txt | grep 'Could not start Rewatch:' &> /dev/null; then error "Lock not removed correctly" exit_watcher diff --git a/rewatch/tests/snapshots/dependency-cycle.txt b/rewatch/tests/snapshots/dependency-cycle.txt index ff0f93b536..b30824a963 100644 --- a/rewatch/tests/snapshots/dependency-cycle.txt +++ b/rewatch/tests/snapshots/dependency-cycle.txt @@ -1,11 +1,18 @@ -[1/7]πŸ“¦ Building package tree... [1/7] πŸ“¦ Built package tree in 0.00s -[2/7] πŸ•΅οΈ Finding source files... [2/7] πŸ•΅οΈ Found source files in 0.00s -[3/7] πŸ“ Reading compile state... [3/7] πŸ“ Read compile state 0.00s -[4/7] 🧹 Cleaning up previous build... [4/7] 🧹 Cleaned 0/11 0.00s +[1/7] πŸ“¦ Building package tree... + [1/7] πŸ“¦ Built package tree in 0.00s +[2/7] πŸ•΅οΈ Finding source files... + [2/7] πŸ•΅οΈ Found source files in 0.00s +[3/7] πŸ“ Reading compile state... + [3/7] πŸ“ Read compile state 0.00s +[4/7] 🧹 Cleaning up previous build... + [4/7] 🧹 Cleaned 0/11 0.00s  [5/7] 🧱 Parsed 1 source files in 0.00s  [6/7] ️🌴 Collected deps in 0.00s  [7/7] οΈπŸ›‘ Compiled 0 modules in 0.00s +ERROR: Can't continue... Found a circular dependency in your code: NewNamespace.NS_alias -> Dep01 -> Dep02 -> NS -> NewNamespace.NS_alias -Error Building:  οΈπŸ›‘ Error Running Incremental Build:  οΈπŸ›‘ Failed to Compile. See Errors Above + +ERROR: +Incremental build failed. Error:  οΈπŸ›‘ Failed to Compile. See Errors Above diff --git a/rewatch/tests/snapshots/remove-file.txt b/rewatch/tests/snapshots/remove-file.txt index 08447639b3..344d8d3d42 100644 --- a/rewatch/tests/snapshots/remove-file.txt +++ b/rewatch/tests/snapshots/remove-file.txt @@ -1,10 +1,15 @@ -[1/7]πŸ“¦ Building package tree... [1/7] πŸ“¦ Built package tree in 0.00s -[2/7] πŸ•΅οΈ Finding source files... [2/7] πŸ•΅οΈ Found source files in 0.00s -[3/7] πŸ“ Reading compile state... [3/7] πŸ“ Read compile state 0.00s -[4/7] 🧹 Cleaning up previous build... [4/7] 🧹 Cleaned 1/11 0.00s +[1/7] πŸ“¦ Building package tree... + [1/7] πŸ“¦ Built package tree in 0.00s +[2/7] πŸ•΅οΈ Finding source files... + [2/7] πŸ•΅οΈ Found source files in 0.00s +[3/7] πŸ“ Reading compile state... + [3/7] πŸ“ Read compile state 0.00s +[4/7] 🧹 Cleaning up previous build... + [4/7] 🧹 Cleaned 1/11 0.00s  [5/7] 🧱 Parsed 0 source files in 0.00s  [6/7] ️🌴 Collected deps in 0.00s  [7/7] οΈπŸ›‘ Compiled 1 modules in 0.00s +ERROR: We've found a bug for you! /packages/dep01/src/Dep01.res:3:9-17 @@ -21,4 +26,6 @@ - Did you include the file's directory to the "sources" in bsconfig.json? -Error Building:  οΈπŸ›‘ Error Running Incremental Build:  οΈπŸ›‘ Failed to Compile. See Errors Above + +ERROR: +Incremental build failed. Error:  οΈπŸ›‘ Failed to Compile. See Errors Above diff --git a/rewatch/tests/snapshots/rename-file-internal-dep-namespace.txt b/rewatch/tests/snapshots/rename-file-internal-dep-namespace.txt index 5230dcfbe5..b251dbce0a 100644 --- a/rewatch/tests/snapshots/rename-file-internal-dep-namespace.txt +++ b/rewatch/tests/snapshots/rename-file-internal-dep-namespace.txt @@ -1,10 +1,15 @@ -[1/7]πŸ“¦ Building package tree... [1/7] πŸ“¦ Built package tree in 0.00s -[2/7] πŸ•΅οΈ Finding source files... [2/7] πŸ•΅οΈ Found source files in 0.00s -[3/7] πŸ“ Reading compile state... [3/7] πŸ“ Read compile state 0.00s -[4/7] 🧹 Cleaning up previous build... [4/7] 🧹 Cleaned 2/11 0.00s +[1/7] πŸ“¦ Building package tree... + [1/7] πŸ“¦ Built package tree in 0.00s +[2/7] πŸ•΅οΈ Finding source files... + [2/7] πŸ•΅οΈ Found source files in 0.00s +[3/7] πŸ“ Reading compile state... + [3/7] πŸ“ Read compile state 0.00s +[4/7] 🧹 Cleaning up previous build... + [4/7] 🧹 Cleaned 2/11 0.00s  [5/7] 🧱 Parsed 2 source files in 0.00s  [6/7] ️🌴 Collected deps in 0.00s  [7/7] οΈπŸ›‘ Compiled 3 modules in 0.00s +ERROR: We've found a bug for you! /packages/new-namespace/src/NS_alias.res:2:1-16 @@ -21,4 +26,6 @@ Hint: Did you mean Other_module2? -Error Building:  οΈπŸ›‘ Error Running Incremental Build:  οΈπŸ›‘ Failed to Compile. See Errors Above + +ERROR: +Incremental build failed. Error:  οΈπŸ›‘ Failed to Compile. See Errors Above diff --git a/rewatch/tests/snapshots/rename-file-internal-dep.txt b/rewatch/tests/snapshots/rename-file-internal-dep.txt index c22caa4230..4e1d142c37 100644 --- a/rewatch/tests/snapshots/rename-file-internal-dep.txt +++ b/rewatch/tests/snapshots/rename-file-internal-dep.txt @@ -1,10 +1,15 @@ -[1/7]πŸ“¦ Building package tree... [1/7] πŸ“¦ Built package tree in 0.00s -[2/7] πŸ•΅οΈ Finding source files... [2/7] πŸ•΅οΈ Found source files in 0.00s -[3/7] πŸ“ Reading compile state... [3/7] πŸ“ Read compile state 0.00s -[4/7] 🧹 Cleaning up previous build... [4/7] 🧹 Cleaned 2/11 0.00s +[1/7] πŸ“¦ Building package tree... + [1/7] πŸ“¦ Built package tree in 0.00s +[2/7] πŸ•΅οΈ Finding source files... + [2/7] πŸ•΅οΈ Found source files in 0.00s +[3/7] πŸ“ Reading compile state... + [3/7] πŸ“ Read compile state 0.00s +[4/7] 🧹 Cleaning up previous build... + [4/7] 🧹 Cleaned 2/11 0.00s  [5/7] 🧱 Parsed 2 source files in 0.00s  [6/7] ️🌴 Collected deps in 0.00s  [7/7] οΈπŸ›‘ Compiled 2 modules in 0.00s +ERROR: We've found a bug for you! /packages/main/src/Main.res:4:8-24 @@ -21,4 +26,6 @@ - Did you include the file's directory to the "sources" in bsconfig.json? -Error Building:  οΈπŸ›‘ Error Running Incremental Build:  οΈπŸ›‘ Failed to Compile. See Errors Above + +ERROR: +Incremental build failed. Error:  οΈπŸ›‘ Failed to Compile. See Errors Above diff --git a/rewatch/tests/snapshots/rename-file-with-interface.txt b/rewatch/tests/snapshots/rename-file-with-interface.txt index 30534637f4..9927321c2f 100644 --- a/rewatch/tests/snapshots/rename-file-with-interface.txt +++ b/rewatch/tests/snapshots/rename-file-with-interface.txt @@ -1,10 +1,15 @@ -[1/7]πŸ“¦ Building package tree... [1/7] πŸ“¦ Built package tree in 0.00s -[2/7] πŸ•΅οΈ Finding source files... Warning: No implementation file found for interface file (skipping): src/ModuleWithInterface.resi - [2/7] πŸ•΅οΈ Found source files in 0.00s -[3/7] πŸ“ Reading compile state... [3/7] πŸ“ Read compile state 0.00s -[4/7] 🧹 Cleaning up previous build... [4/7] 🧹 Cleaned 2/11 0.00s +[1/7] πŸ“¦ Building package tree... + [1/7] πŸ“¦ Built package tree in 0.00s +[2/7] πŸ•΅οΈ Finding source files... +WARN: + No implementation file found for interface file (skipping): src/ModuleWithInterface.resi + [2/7] πŸ•΅οΈ Found source files in 0.00s +[3/7] πŸ“ Reading compile state... + [3/7] πŸ“ Read compile state 0.00s +[4/7] 🧹 Cleaning up previous build... + [4/7] 🧹 Cleaned 2/11 0.00s  [5/7] 🧱 Parsed 1 source files in 0.00s  [6/7] ️🌴 Collected deps in 0.00s - [7/7] 🀺 ️Compiled 1 modules in 0.00s + [7/7] βš”οΈ Compiled 1 modules in 0.00s  ✨ Finished Compilation in 0.00s diff --git a/rewatch/tests/snapshots/rename-file.txt b/rewatch/tests/snapshots/rename-file.txt index 43550cc620..d8206e1b53 100644 --- a/rewatch/tests/snapshots/rename-file.txt +++ b/rewatch/tests/snapshots/rename-file.txt @@ -1,9 +1,13 @@ -[1/7]πŸ“¦ Building package tree... [1/7] πŸ“¦ Built package tree in 0.00s -[2/7] πŸ•΅οΈ Finding source files... [2/7] πŸ•΅οΈ Found source files in 0.00s -[3/7] πŸ“ Reading compile state... [3/7] πŸ“ Read compile state 0.00s -[4/7] 🧹 Cleaning up previous build... [4/7] 🧹 Cleaned 1/11 0.00s +[1/7] πŸ“¦ Building package tree... + [1/7] πŸ“¦ Built package tree in 0.00s +[2/7] πŸ•΅οΈ Finding source files... + [2/7] πŸ•΅οΈ Found source files in 0.00s +[3/7] πŸ“ Reading compile state... + [3/7] πŸ“ Read compile state 0.00s +[4/7] 🧹 Cleaning up previous build... + [4/7] 🧹 Cleaned 1/11 0.00s  [5/7] 🧱 Parsed 1 source files in 0.00s  [6/7] ️🌴 Collected deps in 0.00s - [7/7] 🀺 ️Compiled 1 modules in 0.00s + [7/7] βš”οΈ Compiled 1 modules in 0.00s  ✨ Finished Compilation in 0.00s diff --git a/rewatch/tests/snapshots/rename-interface-file.txt b/rewatch/tests/snapshots/rename-interface-file.txt index a46d80f66d..50dcd91249 100644 --- a/rewatch/tests/snapshots/rename-interface-file.txt +++ b/rewatch/tests/snapshots/rename-interface-file.txt @@ -1,10 +1,15 @@ -[1/7]πŸ“¦ Building package tree... [1/7] πŸ“¦ Built package tree in 0.00s -[2/7] πŸ•΅οΈ Finding source files... Warning: No implementation file found for interface file (skipping): src/ModuleWithInterface2.resi - [2/7] πŸ•΅οΈ Found source files in 0.00s -[3/7] πŸ“ Reading compile state... [3/7] πŸ“ Read compile state 0.00s -[4/7] 🧹 Cleaning up previous build... [4/7] 🧹 Cleaned 1/11 0.00s +[1/7] πŸ“¦ Building package tree... + [1/7] πŸ“¦ Built package tree in 0.00s +[2/7] πŸ•΅οΈ Finding source files... +WARN: + No implementation file found for interface file (skipping): src/ModuleWithInterface2.resi + [2/7] πŸ•΅οΈ Found source files in 0.00s +[3/7] πŸ“ Reading compile state... + [3/7] πŸ“ Read compile state 0.00s +[4/7] 🧹 Cleaning up previous build... + [4/7] 🧹 Cleaned 1/11 0.00s  [5/7] 🧱 Parsed 1 source files in 0.00s  [6/7] ️🌴 Collected deps in 0.00s - [7/7] 🀺 ️Compiled 1 modules in 0.00s + [7/7] βš”οΈ Compiled 1 modules in 0.00s  ✨ Finished Compilation in 0.00s From bd2ea51e5f73eb36625ead16da29f63e348c239d Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Tue, 28 Jan 2025 19:25:55 +0100 Subject: [PATCH 2/2] CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3361e96640..07615aa2ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,10 @@ - AST cleanup: use inline record for `Ptyp_arrow`. https://github.com/rescript-lang/rescript/pull/7250 - Playground: Bundle stdlib runtime so that the playground can execute functions from Core/Belt/Js. (#7255) +#### :nail_care: Polish + +- Rewatch 1.0.10. https://github.com/rescript-lang/rescript/pull/7259 + # 12.0.0-alpha.7 #### :bug: Bug fix