Skip to content

Commit a673a5c

Browse files
authored
Fix rewatch panic when package.json has no "name" field (#8291)
* Fix rewatch panic when package.json has no "name" field * CHANGELOG
1 parent 4970567 commit a673a5c

3 files changed

Lines changed: 63 additions & 34 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
- Reanalyze server: invalidate cache and recompute results when config changes in `rescript.json`. https://github.com/rescript-lang/rescript/pull/8262
2727
- Fix `null` and array values incorrectly matching the `Object` branch when pattern matching on `JSON.t` (or other untagged variants with an `Object` case) in statement position. https://github.com/rescript-lang/rescript/pull/8279
28+
- Fix rewatch panic when `package.json` has no `name` field. https://github.com/rescript-lang/rescript/pull/8291
2829

2930
#### :memo: Documentation
3031

rewatch/src/build/packages.rs

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -419,35 +419,40 @@ fn flatten_dependencies(dependencies: Vec<Dependency>) -> Vec<Dependency> {
419419
}
420420

421421
pub fn read_package_name(package_dir: &Path) -> Result<String> {
422-
let mut file_name = "package.json";
423-
let package_json_path = package_dir.join(file_name);
424-
425-
let package_json_contents = if Path::exists(&package_json_path) {
426-
fs::read_to_string(&package_json_path).map_err(|e| anyhow!("Could not read package.json: {}", e))?
427-
} else {
428-
let rescript_json_path = package_dir.join("rescript.json");
429-
if Path::exists(&rescript_json_path) {
430-
file_name = "rescript.json";
431-
fs::read_to_string(&rescript_json_path)
432-
.map_err(|e| anyhow!("Could not read rescript.json: {}", e))?
433-
} else {
434-
return Err(anyhow!(
435-
"There is no package.json or rescript.json file in {}",
436-
package_dir.to_string_lossy()
437-
));
422+
let read_name = |file_name: &str| -> Result<Option<String>> {
423+
let path = package_dir.join(file_name);
424+
if !Path::exists(&path) {
425+
return Ok(None);
438426
}
427+
428+
let contents =
429+
fs::read_to_string(&path).map_err(|e| anyhow!("Could not read {}: {}", file_name, e))?;
430+
let json: serde_json::Value =
431+
serde_json::from_str(&contents).map_err(|e| anyhow!("Could not parse {}: {}", file_name, e))?;
432+
433+
Ok(json["name"].as_str().map(|name| name.to_string()))
439434
};
440435

441-
let package_json: serde_json::Value = serde_json::from_str(&package_json_contents)
442-
.map_err(|e| anyhow!("Could not parse {}: {}", file_name, e))?;
436+
if let Some(name) = read_name("package.json")? {
437+
return Ok(name);
438+
}
439+
440+
if let Some(name) = read_name("rescript.json")? {
441+
return Ok(name);
442+
}
443443

444-
package_json["name"]
445-
.as_str()
446-
.map(|s| s.to_string())
447-
.ok_or_else(|| anyhow!("No name field found in package.json"))
444+
Err(anyhow!(
445+
"No name field found in package.json or rescript.json in {}",
446+
package_dir.to_string_lossy()
447+
))
448448
}
449449

450-
fn make_package(config: config::Config, package_path: &Path, is_root: bool, is_local_dep: bool) -> Package {
450+
fn make_package(
451+
config: config::Config,
452+
package_path: &Path,
453+
is_root: bool,
454+
is_local_dep: bool,
455+
) -> Result<Package> {
451456
let source_folders = match config.sources.to_owned() {
452457
Some(config::OneOrMore::Single(source)) => get_source_dirs(source, None),
453458
Some(config::OneOrMore::Multiple(sources)) => {
@@ -474,7 +479,7 @@ fn make_package(config: config::Config, package_path: &Path, is_root: bool, is_l
474479
}
475480
};
476481

477-
let package_name = read_package_name(package_path).expect("Could not read package name");
482+
let package_name = read_package_name(package_path)?;
478483
if package_name != config.name {
479484
log::warn!(
480485
"\nPackage name mismatch for {}:\n\
@@ -486,7 +491,7 @@ This inconsistency will cause issues with package resolution.\n",
486491
);
487492
}
488493

489-
Package {
494+
Ok(Package {
490495
name: package_name,
491496
config: config.to_owned(),
492497
source_folders,
@@ -501,7 +506,7 @@ This inconsistency will cause issues with package resolution.\n",
501506
dirs: None,
502507
is_local_dep,
503508
is_root,
504-
}
509+
})
505510
}
506511

507512
fn read_packages(project_context: &ProjectContext, show_progress: bool) -> Result<AHashMap<String, Package>> {
@@ -514,7 +519,7 @@ fn read_packages(project_context: &ProjectContext, show_progress: bool) -> Resul
514519
.path
515520
.parent()
516521
.ok_or_else(|| anyhow!("Could not the read parent folder or a rescript.json file"))?;
517-
make_package(config.to_owned(), folder, true, true)
522+
make_package(config.to_owned(), folder, true, true)?
518523
};
519524

520525
map.insert(current_package.name.to_string(), current_package);
@@ -528,12 +533,12 @@ fn read_packages(project_context: &ProjectContext, show_progress: bool) -> Resul
528533
/* is local dep */ true,
529534
));
530535

531-
dependencies.iter().for_each(|d| {
536+
for d in dependencies.iter() {
532537
if !map.contains_key(&d.name) {
533-
let package = make_package(d.config.to_owned(), &d.path, false, d.is_local_dep);
538+
let package = make_package(d.config.to_owned(), &d.path, false, d.is_local_dep)?;
534539
map.insert(d.name.to_string(), package);
535540
}
536-
});
541+
}
537542

538543
Ok(map)
539544
}
@@ -1025,9 +1030,11 @@ pub fn validate_packages_dependencies(packages: &AHashMap<String, Package>) -> b
10251030
mod test {
10261031
use crate::config;
10271032

1028-
use super::{Namespace, Package};
1033+
use super::{Namespace, Package, read_package_name};
10291034
use ahash::{AHashMap, AHashSet};
1035+
use std::fs;
10301036
use std::path::PathBuf;
1037+
use tempfile::TempDir;
10311038

10321039
pub struct CreatePackageArgs {
10331040
name: String,
@@ -1133,4 +1140,28 @@ mod test {
11331140
let is_valid = super::validate_packages_dependencies(&packages);
11341141
assert!(is_valid)
11351142
}
1143+
1144+
#[test]
1145+
fn should_report_missing_name_when_package_and_rescript_json_lack_it() {
1146+
let temp_dir = TempDir::new().expect("temp dir should be created");
1147+
let package_dir = temp_dir.path();
1148+
1149+
fs::write(package_dir.join("package.json"), "{\n \"private\": true\n}\n")
1150+
.expect("package.json should be written");
1151+
fs::write(
1152+
package_dir.join("rescript.json"),
1153+
"{\n \"suffix\": \".mjs\"\n}\n",
1154+
)
1155+
.expect("rescript.json should be written");
1156+
1157+
let error = read_package_name(package_dir).expect_err("missing names should fail");
1158+
1159+
assert_eq!(
1160+
error.to_string(),
1161+
format!(
1162+
"No name field found in package.json or rescript.json in {}",
1163+
package_dir.to_string_lossy()
1164+
)
1165+
);
1166+
}
11361167
}

tests/package_tests/installation_test/package.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)