WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit e8380f1

Browse files
authored
Fix error handling when rescript.json parsing fails (#8067)
* Fix error handling when rescript.json parsing fails * Add key path to error message * CHANGELOG * CHANGELOG
1 parent cad989f commit e8380f1

File tree

10 files changed

+132
-22
lines changed

10 files changed

+132
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
- Fix missing `ignore` function in some Stdlib modules. https://github.com/rescript-lang/rescript/pull/8060
2525
- Fix signature matching for externals when abstract alias hides function arity. https://github.com/rescript-lang/rescript/pull/8045
2626
- Fix arity detection for arrows returning nested generics. https://github.com/rescript-lang/rescript/pull/8064
27+
- Fix error handling when rescript.json parsing fails and improve error message. https://github.com/rescript-lang/rescript/pull/8067
2728

2829
#### :memo: Documentation
2930

rewatch/Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rewatch/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ regex = "1.7.1"
2525
serde = { version = "1.0.152", features = ["derive"] }
2626
serde_json = { version = "1.0.93" }
2727
serde_ignored = "0.1.11"
28+
serde_path_to_error = "0.1.16"
2829
sysinfo = "0.29.10"
2930
tempfile = "3.10.1"
3031

rewatch/src/build.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::helpers::emojis::*;
1616
use crate::helpers::{self};
1717
use crate::project_context::ProjectContext;
1818
use crate::{config, sourcedirs};
19-
use anyhow::{Result, anyhow};
19+
use anyhow::{Context, Result, anyhow};
2020
use build_types::*;
2121
use console::style;
2222
use indicatif::{ProgressBar, ProgressStyle};
@@ -501,7 +501,7 @@ pub fn build(
501501
plain_output,
502502
warn_error,
503503
)
504-
.map_err(|e| anyhow!("Could not initialize build. Error: {e}"))?;
504+
.with_context(|| "Could not initialize build")?;
505505

506506
match incremental_build(
507507
&mut build_state,

rewatch/src/config.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::build::packages;
22
use crate::helpers;
33
use crate::helpers::deserialize::*;
44
use crate::project_context::ProjectContext;
5-
use anyhow::{Result, bail};
5+
use anyhow::{Result, anyhow, bail};
66
use convert_case::{Case, Casing};
77
use serde::de::{Error as DeError, Visitor};
88
use serde::{Deserialize, Deserializer};
@@ -440,10 +440,19 @@ impl Config {
440440
/// Try to convert a bsconfig from a string to a bsconfig struct
441441
pub fn new_from_json_string(config_str: &str) -> Result<Self> {
442442
let mut deserializer = serde_json::Deserializer::from_str(config_str);
443+
let mut tracker = serde_path_to_error::Track::new();
444+
let path_deserializer = serde_path_to_error::Deserializer::new(&mut deserializer, &mut tracker);
443445
let mut unknown_fields = Vec::new();
444-
let mut config: Config = serde_ignored::deserialize(&mut deserializer, |path| {
445-
unknown_fields.push(path.to_string());
446-
})?;
446+
let mut config: Config =
447+
serde_ignored::deserialize(path_deserializer, |path| unknown_fields.push(path.to_string()))
448+
.map_err(|err: serde_json::Error| {
449+
let path = tracker.path().to_string();
450+
if path.is_empty() {
451+
anyhow!("Failed to parse rescript.json: {err}")
452+
} else {
453+
anyhow!("Failed to parse rescript.json at {path}: {err}")
454+
}
455+
})?;
447456

448457
config.handle_deprecations()?;
449458
config.unknown_fields = unknown_fields;

rewatch/src/main.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ fn main() -> Result<()> {
5656
(*build_args.warn_error).clone(),
5757
) {
5858
Err(e) => {
59-
println!("{e}");
59+
println!("{:#}", e);
6060
std::process::exit(1)
6161
}
6262
Ok(_) => {
@@ -76,17 +76,21 @@ fn main() -> Result<()> {
7676
);
7777
}
7878

79-
watcher::start(
79+
match watcher::start(
8080
&watch_args.filter,
8181
show_progress,
8282
&watch_args.folder,
8383
(*watch_args.after_build).clone(),
8484
*watch_args.create_sourcedirs,
8585
plain_output,
8686
(*watch_args.warn_error).clone(),
87-
);
88-
89-
Ok(())
87+
) {
88+
Err(e) => {
89+
println!("{:#}", e);
90+
std::process::exit(1)
91+
}
92+
Ok(_) => Ok(()),
93+
}
9094
}
9195
cli::Command::Clean { folder, dev } => {
9296
let _lock = get_lock(&folder);

rewatch/src/project_context.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use crate::build::packages;
22
use crate::config::Config;
33
use crate::helpers;
44
use ahash::{AHashMap, AHashSet};
5-
use anyhow::Result;
65
use anyhow::anyhow;
6+
use anyhow::{Context, Result};
77
use log::debug;
88
use std::fmt;
99
use std::path::{Path, PathBuf};
@@ -168,7 +168,7 @@ impl ProjectContext {
168168
pub fn new(path: &Path) -> Result<ProjectContext> {
169169
let path = helpers::get_abs_path(path);
170170
let current_config = packages::read_config(&path)
171-
.map_err(|_| anyhow!("Could not read rescript.json at {}", path.to_string_lossy()))?;
171+
.with_context(|| format!("Could not read rescript.json at {}", path.to_string_lossy()))?;
172172
let nearest_parent_config_path = match path.parent() {
173173
None => Err(anyhow!(
174174
"The current path \"{}\" does not have a parent folder",

rewatch/src/watcher.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::helpers::emojis::*;
88
use crate::lock::LOCKFILE;
99
use crate::queue::FifoQueue;
1010
use crate::queue::*;
11+
use anyhow::{Context, Result};
1112
use futures_timer::Delay;
1213
use notify::event::ModifyKind;
1314
use notify::{Config, Error, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
@@ -78,10 +79,10 @@ async fn async_watch(
7879
plain_output,
7980
warn_error,
8081
}: AsyncWatchArgs<'_>,
81-
) -> notify::Result<()> {
82+
) -> Result<()> {
8283
let mut build_state: build::build_types::BuildCommandState =
8384
build::initialize_build(None, filter, show_progress, path, plain_output, warn_error)
84-
.expect("Can't initialize build");
85+
.with_context(|| "Could not initialize build")?;
8586
let mut needs_compile_type = CompileType::Incremental;
8687
// create a mutex to capture if ctrl-c was pressed
8788
let ctrlc_pressed = Arc::new(Mutex::new(false));
@@ -278,7 +279,7 @@ async fn async_watch(
278279
plain_output,
279280
build_state.get_warn_error_override(),
280281
)
281-
.expect("Can't initialize build");
282+
.expect("Could not initialize build");
282283
let _ = build::incremental_build(
283284
&mut build_state,
284285
None,
@@ -324,7 +325,7 @@ pub fn start(
324325
create_sourcedirs: bool,
325326
plain_output: bool,
326327
warn_error: Option<String>,
327-
) {
328+
) -> Result<()> {
328329
futures::executor::block_on(async {
329330
let queue = Arc::new(FifoQueue::<Result<Event, Error>>::new());
330331
let producer = queue.clone();
@@ -341,7 +342,7 @@ pub fn start(
341342

342343
let path = Path::new(folder);
343344

344-
if let Err(e) = async_watch(AsyncWatchArgs {
345+
async_watch(AsyncWatchArgs {
345346
q: consumer,
346347
path,
347348
show_progress,
@@ -352,8 +353,5 @@ pub fn start(
352353
warn_error: warn_error.clone(),
353354
})
354355
.await
355-
{
356-
println!("{e:?}")
357-
}
358356
})
359357
}

rewatch/tests/experimental.sh

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,88 @@ fi
3838
mv rescript.json.bak rescript.json
3939

4040
success "experimental-features emits -enable-experimental twice as string list"
41+
42+
bold "Test: build surfaces experimental-features list parse error"
43+
44+
cp rescript.json rescript.json.bak
45+
46+
node -e '
47+
const fs = require("fs");
48+
const j = JSON.parse(fs.readFileSync("rescript.json", "utf8"));
49+
j["experimental-features"] = ["LetUnwrap"];
50+
fs.writeFileSync("rescript.json", JSON.stringify(j, null, 2));
51+
'
52+
53+
out=$(rewatch build 2>&1)
54+
status=$?
55+
56+
mv rescript.json.bak rescript.json
57+
rm -f lib/rescript.lock
58+
59+
if [ $status -eq 0 ]; then
60+
error "Expected build to fail for experimental-features list input"
61+
echo "$out"
62+
exit 1
63+
fi
64+
65+
echo "$out" | grep -q "Could not read rescript.json"
66+
if [ $? -ne 0 ]; then
67+
error "Missing rescript.json path context in build error"
68+
echo "$out"
69+
exit 1
70+
fi
71+
72+
echo "$out" | grep -qi "experimental-features.*invalid type"
73+
if [ $? -ne 0 ]; then
74+
error "Missing detailed parse error in build output"
75+
echo "$out"
76+
exit 1
77+
fi
78+
79+
success "Experimental-features list produces detailed build error"
80+
81+
bold "Test: watch reports invalid experimental-features without panicking"
82+
83+
cp rescript.json rescript.json.bak
84+
85+
node -e '
86+
const fs = require("fs");
87+
const cfg = JSON.parse(fs.readFileSync("rescript.json", "utf8"));
88+
cfg["experimental-features"] = ["LetUnwrap"];
89+
fs.writeFileSync("rescript.json", JSON.stringify(cfg, null, 2));
90+
'
91+
92+
out=$(rewatch watch 2>&1)
93+
status=$?
94+
95+
mv rescript.json.bak rescript.json
96+
rm -f lib/rescript.lock
97+
98+
if [ $status -eq 0 ]; then
99+
error "Expected watch to fail for invalid experimental-features list"
100+
echo "$out"
101+
exit 1
102+
fi
103+
104+
echo "$out" | grep -q "Could not read rescript.json"
105+
if [ $? -ne 0 ]; then
106+
error "Missing rescript.json path context in watch error"
107+
echo "$out"
108+
exit 1
109+
fi
110+
111+
echo "$out" | grep -qi "experimental-features.*invalid type"
112+
if [ $? -ne 0 ]; then
113+
error "Missing detailed parse error for experimental-features list"
114+
echo "$out"
115+
exit 1
116+
fi
117+
118+
echo "$out" | grep -q "panicked"
119+
if [ $? -eq 0 ]; then
120+
error "Watcher should not panic when config is invalid"
121+
echo "$out"
122+
exit 1
123+
fi
124+
125+
success "Invalid experimental-features list handled without panic"

rewatch/tests/snapshots/clean-rebuild.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ Use 'dev-dependencies' instead.
1111
The field 'bsc-flags' found in the package config of '@testrepo/deprecated-config' is deprecated and will be removed in a future version.
1212
Use 'compiler-flags' instead.
1313

14-
The field 'ignored-dirs' found in the package config of '@testrepo/deprecated-config' is not supported by ReScript's new build system (rewatch).
14+
The field 'ignored-dirs' found in the package config of '@testrepo/deprecated-config' is not supported by ReScript 12's new build system.
1515

1616
Unknown field 'some-new-field' found in the package config of '@testrepo/deprecated-config'. This option will be ignored.

0 commit comments

Comments
 (0)