Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
- Fix `reanalyze` reporting record labels reached through a record coercion as dead. The typed tree now keeps the source type of a coercion, so reading a label on the target counts as reading the source label of the same name. https://github.com/rescript-lang/rescript/issues/8643
- Fix GenType path resolution hanging on Windows when project paths use different short and canonical forms. https://github.com/rescript-lang/rescript/pull/8639
- Allow `rescript clean` to remove generated files when the source module graph is invalid. https://github.com/rescript-lang/rescript/pull/8640
- Keep watch mode running after full rebuild initialization fails so it can recover after a later valid change. https://github.com/rescript-lang/rescript/pull/8641
- Fix constant folding of pattern matches on unboxed variants whose payload overlaps a literal constructor, so inlined calls agree with runtime matching. Reject multi-argument unboxed constructors instead of crashing. https://github.com/rescript-lang/rescript/pull/8631
- Fix escaped backticks and interpolation openers in backquoted `%raw`, `%ffi`, and `%re` payloads leaking into emitted JavaScript. https://github.com/rescript-lang/rescript/pull/8630
- Fix the side-effect analysis treating bigint exponentiation and bounds-checked array and string reads as pure, which let dead-code elimination drop an unused one that throws: `let _ = 2n ** -1n` no longer raised. https://github.com/rescript-lang/rescript/pull/8617
Expand Down
16 changes: 11 additions & 5 deletions rewatch/src/watcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ async fn async_watch(
let timing_total = Instant::now();
// Reinitialization runs cleanup for previous build artifacts, so full rebuilds need
// the same build lock boundary as regular `rescript build`.
let result = build::with_build_lock(path, || {
let result = build::with_build_lock(path, || -> Result<_> {
let mut next_build_state = build::initialize_build(
None,
filter,
Expand All @@ -498,7 +498,7 @@ async fn async_watch(
features.clone(),
SourceMapCommand::Watch,
)
.expect("Could not initialize build");
.context("Could not initialize build")?;

// Full rebuilds can be triggered by editor atomic saves that surface as rename events.
// Preserve warning state for unchanged modules so their warnings are re-emitted after the
Expand All @@ -521,10 +521,10 @@ async fn async_watch(
plain_output,
);
build::write_build_ninja(&build_state);
result
Ok(result)
});
match result {
Ok(result) => {
Ok(Ok(result)) => {
finish_successful_watch_compile(
after_build.clone(),
timing_total,
Expand All @@ -535,7 +535,13 @@ async fn async_watch(
result,
);
}
Err(_) => {
Ok(Err(_)) => {
if should_clear_screen(clear_screen, show_progress, plain_output) {
print_build_failed_footer();
}
}
Err(error) => {
eprintln!("{error:#}");
if should_clear_screen(clear_screen, show_progress, plain_output) {
print_build_failed_footer();
}
Expand Down
1 change: 1 addition & 0 deletions rewatch/tests/suite.sh
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ fi
./watch/04-watch-config-change.sh &&
./watch/05-watch-ignores-non-source.sh &&
./watch/06-watch-missing-source-folder.sh &&
./watch/07-watch-recovers-from-invalid-config.sh &&

# Lock tests
./lock/01-lock-when-watching.sh &&
Expand Down
103 changes: 103 additions & 0 deletions rewatch/tests/watch/07-watch-recovers-from-invalid-config.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/bin/bash
cd $(dirname $0)
source "../utils.sh"
cd ../../testrepo

bold "Test: Watcher recovers from an invalid config change"

wait_for_pattern() {
local file="$1" pattern="$2" timeout="${3:-30}"
while [ "$timeout" -gt 0 ]; do
grep -q "$pattern" "$file" 2> /dev/null && return 0
sleep 1
timeout=$((timeout - 1))
done
return 1
}

wait_for_pattern_count() {
local file="$1" pattern="$2" expected="$3" timeout="${4:-30}"
while [ "$timeout" -gt 0 ]; do
local count
count=$(grep -c "$pattern" "$file" 2> /dev/null || true)
[ "$count" -ge "$expected" ] && return 0
sleep 1
timeout=$((timeout - 1))
done
return 1
}

rewatch clean > /dev/null 2>&1
cp rescript.json rescript.json.watch-recovery.bak
rewatch_bg watch > rewatch.log 2>&1 &

if ! wait_for_file "./src/Test.mjs" 20 || ! wait_for_file "lib/watch.lock" 20; then
error "Initial watch build did not complete"
cat rewatch.log
exit_watcher
mv rescript.json.watch-recovery.bak rescript.json
exit 1
fi

if ! wait_for_pattern rewatch.log "Finished initial compilation" 60; then
error "Initial watch build did not finish"
cat rewatch.log
exit_watcher
mv rescript.json.watch-recovery.bak rescript.json
exit 1
fi

initial_completed=$(grep -c "Finished compilation" rewatch.log 2> /dev/null || true)
printf '{"name":' > rescript.json.watch-recovery.next
mv rescript.json.watch-recovery.next rescript.json

if ! wait_for_pattern rewatch.log "Could not initialize build" 30; then
error "Watcher did not report the invalid config"
cat rewatch.log
exit_watcher
mv rescript.json.watch-recovery.bak rescript.json
exit 1
fi

node -e '
const fs = require("fs");
const config = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
config.suffix = ".recovered.mjs";
fs.writeFileSync(process.argv[2], `${JSON.stringify(config, null, 2)}\n`);
' rescript.json.watch-recovery.bak rescript.json.watch-recovery.next
mv rescript.json.watch-recovery.next rescript.json

expected_completed=$((initial_completed + 1))
if ! wait_for_pattern_count rewatch.log "Finished compilation" "$expected_completed" 30; then
error "Watcher did not recover after restoring a valid config"
cat rewatch.log
exit_watcher
mv rescript.json.watch-recovery.bak rescript.json
exit 1
fi

echo '// watch-recovery-test' >> src/Test.res
if ! wait_for_file "./src/Test.recovered.mjs" 20; then
error "Recovered watcher did not compile with the restored config"
cat rewatch.log
exit_watcher
mv rescript.json.watch-recovery.bak rescript.json
git checkout -- src/Test.res
exit 1
fi

exit_watcher
rewatch clean > /dev/null 2>&1
mv rescript.json.watch-recovery.bak rescript.json
git checkout -- src/Test.res
rm -f rewatch.log
rewatch build > /dev/null 2>&1

if git diff --exit-code . > /dev/null 2>&1 && [ -z "$(git ls-files --others --exclude-standard .)" ]; then
success "Watcher recovered from invalid config"
else
error "Watcher recovery test left repository changes"
git diff .
git ls-files --others --exclude-standard .
exit 1
fi
Loading