Skip to content

Commit 2ff169c

Browse files
authored
Initial implementation of js-post-build in rewatch (#8151)
* Initial implementation of js-post-build in rewatch * Add changelog
1 parent a8c25fd commit 2ff169c

File tree

5 files changed

+65
-5
lines changed

5 files changed

+65
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
- Reanalyze: add reactive incremental analysis (`-reactive`, `-runs`, `-churn`) and Mermaid pipeline dumping (`-mermaid`). https://github.com/rescript-lang/rescript/pull/8092
2323

2424
- Reanalyze: add `reanalyze-server` (long-lived server) with transparent delegation for `rescript-tools reanalyze -json`. https://github.com/rescript-lang/rescript/pull/8127
25+
- Add support of `js-post-build` in Rewatch. Note that `${file}` is now an absolute path. https://github.com/rescript-lang/rescript/pull/8151
2526

2627
#### :bug: Bug fix
2728

rewatch/CompilerConfigurationSpec.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ This document contains a list of all bsconfig parameters with remarks, and wheth
2222
| warnings | Warnings | | [x] |
2323
| ppx-flags | array of string | | [x] |
2424
| pp-flags | array of string | | [_] |
25-
| js-post-build | Js-Post-Build | | [_] |
25+
| js-post-build | Js-Post-Build | `${file}` is now an absolute path | [x] |
2626
| package-specs | array of Module-Format | | [_] |
2727
| package-specs | array of Package-Spec | | [x] |
2828
| entries | array of Target-Item | | [_] |
@@ -135,7 +135,7 @@ Currently supported features:
135135

136136
| Parameter | JSON type | Remark | Implemented? |
137137
| --------- | --------- | ------ | :----------: |
138-
| cmd | string | | [_] |
138+
| cmd | string | `${file}` is now an absolute path | [x] |
139139

140140
### Package-Spec
141141

rewatch/src/build/compile.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,33 @@ use std::process::Command;
2121
use std::sync::OnceLock;
2222
use std::time::SystemTime;
2323

24+
/// Execute js-post-build command for a compiled JavaScript file.
25+
/// Unlike bsb which passes relative paths, rewatch passes absolute paths for clarity.
26+
fn execute_post_build_command(cmd: &str, js_file_path: &Path) -> Result<()> {
27+
let full_command = format!("{} {}", cmd, js_file_path.display());
28+
29+
let output = if cfg!(target_os = "windows") {
30+
Command::new("cmd").args(["/C", &full_command]).output()
31+
} else {
32+
Command::new("sh").args(["-c", &full_command]).output()
33+
};
34+
35+
match output {
36+
Ok(output) if !output.status.success() => {
37+
let stderr = String::from_utf8_lossy(&output.stderr);
38+
let stdout = String::from_utf8_lossy(&output.stdout);
39+
Err(anyhow!(
40+
"js-post-build command failed for {}: {}{}",
41+
js_file_path.display(),
42+
stderr,
43+
stdout
44+
))
45+
}
46+
Err(e) => Err(anyhow!("Failed to execute js-post-build command: {}", e)),
47+
Ok(_) => Ok(()),
48+
}
49+
}
50+
2451
pub fn compile(
2552
build_state: &mut BuildCommandState,
2653
show_progress: bool,
@@ -815,6 +842,29 @@ fn compile_file(
815842
}
816843
});
817844

845+
// Execute js-post-build command if configured
846+
// Only run for implementation files (not interfaces)
847+
if !is_interface
848+
&& let Some(js_post_build) = &package.config.js_post_build
849+
&& let SourceType::SourceFile(SourceFile {
850+
implementation: Implementation { path, .. },
851+
..
852+
}) = &module.source_type
853+
{
854+
// Execute post-build command for each package spec (each output format)
855+
for spec in root_config.get_package_specs() {
856+
let js_file = helpers::get_source_file_from_rescript_file(
857+
&package.get_build_path().join(path),
858+
&root_config.get_suffix(&spec),
859+
);
860+
861+
if js_file.exists() {
862+
// Fail the build if post-build command fails (matches bsb behavior with &&)
863+
execute_post_build_command(&js_post_build.cmd, &js_file)?;
864+
}
865+
}
866+
}
867+
818868
if helpers::contains_ascii_characters(&err) {
819869
if package.is_local_dep {
820870
// suppress warnings of external deps

rewatch/src/config.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,13 @@ pub struct JsxSpecs {
219219
/// We do not care about the internal structure because the gentype config is loaded by bsc.
220220
pub type GenTypeConfig = serde_json::Value;
221221

222+
/// Configuration for running a command after each JavaScript file is compiled.
223+
/// Note: Unlike bsb, rewatch passes absolute paths to the command for clarity.
224+
#[derive(Deserialize, Debug, Clone)]
225+
pub struct JsPostBuild {
226+
pub cmd: String,
227+
}
228+
222229
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
223230
pub enum DeprecationWarning {
224231
BsDependencies,
@@ -299,6 +306,8 @@ pub struct Config {
299306
pub experimental_features: Option<HashMap<ExperimentalFeature, bool>>,
300307
#[serde(rename = "gentypeconfig")]
301308
pub gentype_config: Option<GenTypeConfig>,
309+
#[serde(rename = "js-post-build")]
310+
pub js_post_build: Option<JsPostBuild>,
302311
// Used by the VS Code extension; ignored by rewatch but should not emit warnings.
303312
// Payload is not validated here, only in the VS Code extension.
304313
pub editor: Option<serde_json::Value>,
@@ -707,7 +716,6 @@ impl Config {
707716
"generators",
708717
"cut-generators",
709718
"pp-flags",
710-
"js-post-build",
711719
"entries",
712720
"use-stdlib",
713721
"external-stdlib",
@@ -800,6 +808,7 @@ pub mod tests {
800808
namespace: None,
801809
jsx: None,
802810
gentype_config: None,
811+
js_post_build: None,
803812
editor: None,
804813
namespace_entry: None,
805814
deprecation_warnings: vec![],

tests/build_tests/post-build/input.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
import * as assert from "node:assert";
44
import { setup } from "#dev/process";
55

6-
const { execBuildLegacy } = setup(import.meta.dirname);
6+
const { execBuild } = setup(import.meta.dirname);
77

88
if (process.platform === "win32") {
99
console.log("Skipping test on Windows");
1010
process.exit(0);
1111
}
1212

13-
const out = await execBuildLegacy();
13+
const out = await execBuild();
1414

1515
if (out.status !== 0) {
1616
assert.fail(out.stdout + out.stderr);

0 commit comments

Comments
 (0)