Skip to content

Commit db4344e

Browse files
committed
feat: clean on build_metadata change
1 parent f49037f commit db4344e

File tree

5 files changed

+139
-21
lines changed

5 files changed

+139
-21
lines changed

rewatch/src/build_metadata.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use std::{
2+
fs,
3+
hash::{DefaultHasher, Hash, Hasher},
4+
path::Path,
5+
};
6+
7+
use crate::{config::Config, helpers, project_context::ProjectContext};
8+
9+
pub struct BuildMetadata {
10+
pub version: String,
11+
pub config_hash: String,
12+
}
13+
14+
impl BuildMetadata {
15+
pub fn new(version: &str, config: &Config) -> Self {
16+
let mut default_hasher = DefaultHasher::new();
17+
config.hash(&mut default_hasher);
18+
let config_hash = default_hasher.finish().to_string();
19+
Self {
20+
version: version.to_string(),
21+
config_hash,
22+
}
23+
}
24+
}
25+
26+
const BUILD_METADATA_FILE_NAME: &str = ".build-metadata";
27+
28+
pub fn read_build_metadata(path: &Path) -> Option<BuildMetadata> {
29+
let file_path = path.join(BUILD_METADATA_FILE_NAME);
30+
let file_content = fs::read_to_string(file_path).ok()?;
31+
let file_content = file_content.trim();
32+
33+
let mut lines = file_content.lines();
34+
let version = lines.next()?.to_owned();
35+
let config_hash = lines.next()?.to_owned();
36+
37+
Some(BuildMetadata { version, config_hash })
38+
}
39+
40+
pub fn current_build_metadata(path: &Path, version: &str) -> BuildMetadata {
41+
let config = helpers::get_nearest_config(&path).expect("Couldn't find package root");
42+
let project_context = ProjectContext::new(&config).expect("Couldn't create project context");
43+
44+
BuildMetadata::new(version, project_context.get_root_config())
45+
}
46+
47+
pub fn is_build_metadata_different(
48+
last_build_metadata: Option<&BuildMetadata>,
49+
current_build_metadata: &BuildMetadata,
50+
) -> bool {
51+
match last_build_metadata {
52+
Some(last_metadata) => {
53+
last_metadata.version != current_build_metadata.version
54+
|| last_metadata.config_hash != current_build_metadata.config_hash
55+
}
56+
None => true,
57+
}
58+
}
59+
60+
pub fn write_build_metadata(path: &Path, build_metadata: &BuildMetadata) {
61+
let file_path = path.join(BUILD_METADATA_FILE_NAME);
62+
let mut file_content = String::from("");
63+
64+
file_content.push_str(&format!("{}\n", build_metadata.version));
65+
file_content.push_str(&format!("{}\n", build_metadata.config_hash));
66+
67+
fs::write(file_path, file_content).unwrap();
68+
}

rewatch/src/config.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use serde::Deserialize;
66
use std::fs;
77
use std::path::{Path, PathBuf};
88

9-
#[derive(Deserialize, Debug, Clone)]
9+
#[derive(Deserialize, Debug, Clone, Hash)]
1010
#[serde(untagged)]
1111
pub enum OneOrMore<T> {
1212
Multiple(Vec<T>),
@@ -141,7 +141,7 @@ impl Source {
141141

142142
impl Eq for Source {}
143143

144-
#[derive(Deserialize, Debug, Clone)]
144+
#[derive(Deserialize, Debug, Clone, Hash)]
145145
pub struct PackageSpec {
146146
pub module: String,
147147
#[serde(rename = "in-source", default = "default_true")]
@@ -167,42 +167,42 @@ impl PackageSpec {
167167
}
168168
}
169169

170-
#[derive(Deserialize, Debug, Clone)]
170+
#[derive(Deserialize, Debug, Clone, Hash)]
171171
#[serde(untagged)]
172172
pub enum Error {
173173
Catchall(bool),
174174
Qualified(String),
175175
}
176176

177-
#[derive(Deserialize, Debug, Clone)]
177+
#[derive(Deserialize, Debug, Clone, Hash)]
178178
pub struct Warnings {
179179
pub number: Option<String>,
180180
pub error: Option<Error>,
181181
}
182182

183-
#[derive(Deserialize, Debug, Clone)]
183+
#[derive(Deserialize, Debug, Clone, Hash)]
184184
#[serde(untagged)]
185185
pub enum NamespaceConfig {
186186
Bool(bool),
187187
String(String),
188188
}
189189

190-
#[derive(Deserialize, Debug, Clone, Eq, PartialEq)]
190+
#[derive(Deserialize, Debug, Clone, Eq, PartialEq, Hash)]
191191
#[serde(rename_all = "camelCase")]
192192
pub enum JsxMode {
193193
Classic,
194194
Automatic,
195195
}
196196

197-
#[derive(Deserialize, Debug, Clone, Eq, PartialEq)]
197+
#[derive(Deserialize, Debug, Clone, Eq, PartialEq, Hash)]
198198
#[serde(rename_all = "camelCase")]
199199
#[serde(untagged)]
200200
pub enum JsxModule {
201201
React,
202202
Other(String),
203203
}
204204

205-
#[derive(Deserialize, Debug, Clone, Eq, PartialEq)]
205+
#[derive(Deserialize, Debug, Clone, Eq, PartialEq, Hash)]
206206
pub struct JsxSpecs {
207207
pub version: Option<i32>,
208208
pub module: Option<JsxModule>,
@@ -215,7 +215,7 @@ pub struct JsxSpecs {
215215
/// We do not care about the internal structure because the gentype config is loaded by bsc.
216216
pub type GenTypeConfig = serde_json::Value;
217217

218-
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
218+
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Hash)]
219219
pub enum DeprecationWarning {
220220
BsDependencies,
221221
BsDevDependencies,
@@ -224,7 +224,7 @@ pub enum DeprecationWarning {
224224

225225
/// # rescript.json representation
226226
/// This is tricky, there is a lot of ambiguity. This is probably incomplete.
227-
#[derive(Deserialize, Debug, Clone, Default)]
227+
#[derive(Deserialize, Debug, Clone, Hash, Default)]
228228
pub struct Config {
229229
pub name: String,
230230
// In the case of monorepos, the root source won't necessarily have to have sources. It can

rewatch/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod build;
2+
pub mod build_metadata;
23
pub mod cli;
34
pub mod cmd;
45
pub mod config;

rewatch/src/main.rs

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use anyhow::Result;
2-
use clap::Parser;
2+
use clap::{CommandFactory, Parser};
33
use log::LevelFilter;
44
use std::{io::Write, path::Path};
55

6-
use rescript::{build, cli, cmd, format, lock, watcher};
6+
use rescript::{build, build_metadata, cli, cmd, format, lock, watcher};
77

88
fn main() -> Result<()> {
9+
let cli_as_command = cli::Cli::command();
10+
let version = cli_as_command.get_version().expect("version is set");
911
let args = cli::Cli::parse();
1012

1113
let log_level_filter = args.verbose.log_level_filter();
@@ -37,6 +39,21 @@ fn main() -> Result<()> {
3739
cli::Command::Build(build_args) => {
3840
let _lock = get_lock(&build_args.folder);
3941

42+
if is_clean_build_needed(&build_args.folder, version) {
43+
match build::clean::clean(
44+
&build_args.folder,
45+
show_progress,
46+
*build_args.snapshot_output,
47+
*build_args.dev,
48+
) {
49+
Ok(_) => {}
50+
Err(e) => {
51+
println!("{e}");
52+
std::process::exit(1);
53+
}
54+
}
55+
}
56+
4057
match build::build(
4158
&build_args.filter,
4259
&build_args.folder,
@@ -54,24 +71,53 @@ fn main() -> Result<()> {
5471
if let Some(args_after_build) = (*build_args.after_build).clone() {
5572
cmd::run(args_after_build)
5673
}
74+
build_metadata::write_build_metadata(
75+
&build_args.folder.join("lib"),
76+
&build_metadata::current_build_metadata(&build_args.folder, version),
77+
);
5778
std::process::exit(0)
5879
}
5980
};
6081
}
6182
cli::Command::Watch(watch_args) => {
6283
let _lock = get_lock(&watch_args.folder);
6384

64-
watcher::start(
85+
if is_clean_build_needed(&watch_args.folder, version) {
86+
match build::clean::clean(
87+
&watch_args.folder,
88+
show_progress,
89+
*watch_args.snapshot_output,
90+
*watch_args.dev,
91+
) {
92+
Ok(_) => {}
93+
Err(e) => {
94+
println!("{e}");
95+
std::process::exit(1);
96+
}
97+
}
98+
}
99+
100+
match watcher::start(
65101
&watch_args.filter,
66102
show_progress,
67103
&watch_args.folder,
68104
(*watch_args.after_build).clone(),
69105
*watch_args.create_sourcedirs,
70106
*watch_args.dev,
71107
*watch_args.snapshot_output,
72-
);
73-
74-
Ok(())
108+
) {
109+
Ok(_) => {
110+
build_metadata::write_build_metadata(
111+
&watch_args.folder.join("lib"),
112+
&build_metadata::current_build_metadata(&watch_args.folder, version),
113+
);
114+
std::process::exit(0)
115+
}
116+
Err(e) => {
117+
println!("{e:?}");
118+
std::process::exit(1)
119+
}
120+
}
75121
}
76122
cli::Command::Clean {
77123
folder,
@@ -95,6 +141,12 @@ fn main() -> Result<()> {
95141
}
96142
}
97143

144+
fn is_clean_build_needed(path: &Path, version: &str) -> bool {
145+
let last_build_metadata = build_metadata::read_build_metadata(path);
146+
let current_build_metadata = build_metadata::current_build_metadata(path, version);
147+
build_metadata::is_build_metadata_different(last_build_metadata.as_ref(), &current_build_metadata)
148+
}
149+
98150
fn get_lock(folder: &Path) -> lock::Lock {
99151
match lock::get(folder) {
100152
lock::Lock::Error(error) => {

rewatch/src/watcher.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ pub fn start(
316316
create_sourcedirs: bool,
317317
build_dev_deps: bool,
318318
snapshot_output: bool,
319-
) {
319+
) -> Result<(), Error> {
320320
futures::executor::block_on(async {
321321
let queue = Arc::new(FifoQueue::<Result<Event, Error>>::new());
322322
let producer = queue.clone();
@@ -331,7 +331,7 @@ pub fn start(
331331
.watch(folder.as_ref(), RecursiveMode::Recursive)
332332
.expect("Could not start watcher");
333333

334-
if let Err(e) = async_watch(AsyncWatchArgs {
334+
async_watch(AsyncWatchArgs {
335335
q: consumer,
336336
path: folder,
337337
show_progress,
@@ -342,8 +342,5 @@ pub fn start(
342342
snapshot_output,
343343
})
344344
.await
345-
{
346-
println!("{e:?}")
347-
}
348345
})
349346
}

0 commit comments

Comments
 (0)