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
10 changes: 5 additions & 5 deletions src/disk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ pub struct Disk {
}

impl Disk {
pub fn from_workspace<T: DiskIo>(io: &T, directory: &Path) -> Self {
pub fn from_workspace<T: DiskIo>(io: &T, directory: &Path) -> Result<Self, DiskIoError> {
let lerna_json = io.read_json_file(&directory.join("lerna.json"));
let package_json_root_file = io.read_json_file(&directory.join("package.json"));
let package_manager = if io.path_exists(&directory.join("pnpm-lock.yaml")) || io.path_exists(&directory.join("pnpm-workspace.yaml")) {
Expand All @@ -182,7 +182,7 @@ impl Disk {
Some(PackageManager::Unknown)
};
let pnpm_workspace = if let Some(PackageManager::Pnpm) = package_manager {
io.read_yaml_file(&directory.join("pnpm-workspace.yaml"))
io.read_yaml_file(&directory.join("pnpm-workspace.yaml")).transpose()?
} else {
None
};
Expand All @@ -194,14 +194,14 @@ impl Disk {
package_json_files.push(root_file);
}

Self {
Ok(Self {
cwd: directory.to_path_buf(),
lerna_json: lerna_json.and_then(Result::ok),
package_json_files,
package_json_root_idx,
package_manager,
pnpm_workspace: pnpm_workspace.and_then(Result::ok),
}
pnpm_workspace,
})
}

/// Borrow the root package.json if one was loaded.
Expand Down
38 changes: 29 additions & 9 deletions src/disk_test.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use {
crate::{
disk::{
Disk, File, PackageManager, PendingYamlOp, empty_yaml_file, ensure_object_path, insert_catalog_definition, json_view,
parse_yaml_file, remove_catalog_definition, remove_prop, render_yaml_bytes, set_nested_prop, set_prop,
Disk, DiskIoError, File, PackageManager, PendingYamlOp, empty_yaml_file, ensure_object_path, insert_catalog_definition, json_view,
parse_yaml_file, parse_yaml_file_strict, remove_catalog_definition, remove_prop, render_yaml_bytes, set_nested_prop, set_prop,
},
specifier::Specifier,
test::mock_disk::MockDiskIo,
Expand All @@ -24,7 +24,7 @@ fn json_file_from_value(value: serde_json::Value) -> File<serde_json::Value> {
fn detects_pnpm_from_workspace_yaml_without_lockfile() {
let mut io = MockDiskIo::new();
io.add_file("pnpm-workspace.yaml", "packages:\n - 'apps/*'\n".to_string());
let disk = Disk::from_workspace(&io, io.root());
let disk = Disk::from_workspace(&io, io.root()).unwrap();
assert_eq!(disk.package_manager, Some(PackageManager::Pnpm));
assert!(disk.pnpm_workspace.is_some(), "pnpm-workspace.yaml should be loaded");
}
Expand All @@ -33,15 +33,15 @@ fn detects_pnpm_from_workspace_yaml_without_lockfile() {
fn detects_bun_from_legacy_binary_lockfile() {
let mut io = MockDiskIo::new();
io.add_file("bun.lockb", String::new());
let disk = Disk::from_workspace(&io, io.root());
let disk = Disk::from_workspace(&io, io.root()).unwrap();
assert_eq!(disk.package_manager, Some(PackageManager::Bun));
}

#[test]
fn file_dirty_defaults_false() {
let mut io = MockDiskIo::new();
io.add_file("package.json", "{\"name\":\"root\"}".to_string());
let disk = Disk::from_workspace(&io, io.root());
let disk = Disk::from_workspace(&io, io.root()).unwrap();
let pkg = disk.package_json_root().expect("root should be loaded");
assert!(!pkg.dirty, "freshly parsed File should default dirty=false");
assert!(!pkg.is_dirty(), "is_dirty() should match field");
Expand All @@ -65,7 +65,7 @@ fn file_dirty_settable() {
fn disk_from_workspace_works_without_lifetime() {
// Disk owns no borrow of `io`; returned Disk has no lifetime parameter.
let io = MockDiskIo::new();
let disk: Disk = Disk::from_workspace(&io, io.root());
let disk: Disk = Disk::from_workspace(&io, io.root()).unwrap();
// Drop io to prove Disk does not borrow it.
drop(io);
assert!(disk.package_json_root().is_none());
Expand All @@ -90,7 +90,7 @@ fn disk_constructs_without_io_field() {
fn disk_root_idx_set_when_root_pkg_exists() {
let mut io = MockDiskIo::new();
io.add_file("package.json", "{\"name\":\"root\"}".to_string());
let disk = Disk::from_workspace(&io, io.root());
let disk = Disk::from_workspace(&io, io.root()).unwrap();
assert_eq!(disk.package_json_root_idx, Some(0), "root_idx should be Some(0) when root exists");
assert_eq!(disk.package_json_files.len(), 1, "root pkg should be in package_json_files");
let root = disk.package_json_root().expect("root accessor returns Some");
Expand All @@ -107,7 +107,7 @@ fn disk_load_package_files_appends_after_root() {
io.add_file("package.json", "{\"name\":\"root\"}".to_string());
io.add_file("packages/a/package.json", "{\"name\":\"a\"}".to_string());
io.add_file("packages/b/package.json", "{\"name\":\"b\"}".to_string());
let mut disk = Disk::from_workspace(&io, io.root());
let mut disk = Disk::from_workspace(&io, io.root()).unwrap();
assert_eq!(disk.package_json_files.len(), 1, "starts with just root");
let a_path = io.root().join("packages/a/package.json");
let b_path = io.root().join("packages/b/package.json");
Expand All @@ -120,7 +120,7 @@ fn disk_load_package_files_skips_root_path() {
let mut io = MockDiskIo::new();
io.add_file("package.json", "{\"name\":\"root\"}".to_string());
io.add_file("packages/a/package.json", "{\"name\":\"a\"}".to_string());
let mut disk = Disk::from_workspace(&io, io.root());
let mut disk = Disk::from_workspace(&io, io.root()).unwrap();
let root_path = io.root().join("package.json");
let a_path = io.root().join("packages/a/package.json");
disk.load_package_files(&io, &[root_path, a_path]);
Expand Down Expand Up @@ -650,6 +650,26 @@ fn parse_yaml_file_captures_raw_text() {
assert!(yaml.patches.is_empty(), "fresh YamlFile starts with no pending patches");
}

#[test]
fn parse_yaml_file_strict_returns_error_for_invalid_yaml() {
let filepath = PathBuf::from("/test/pnpm-workspace.yaml");
let err = parse_yaml_file_strict("catalogs:\n tooling: [\n".to_string(), filepath).unwrap_err();
let DiskIoError::YamlParse(source) = &err else {
panic!("invalid YAML must return a YAML parse error");
};
let location = source.location().expect("syntax error should have a location");
let message = err.to_string();
assert!(
message.contains(&source.to_string()),
"error message should include the parser's reason"
);
assert!(message.contains(&format!("line {} column {}", location.line(), location.column())));
assert!(
std::error::Error::source(&err).is_some(),
"original parser error should remain in the error chain"
);
}

#[test]
fn empty_yaml_file_has_empty_raw() {
let yaml = empty_yaml_file(PathBuf::from("/test/pnpm-workspace.yaml"));
Expand Down
2 changes: 1 addition & 1 deletion src/syncpack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub async fn syncpack<D: DiskIo>(
/// All disk reads happen here.
fn analyse<D: DiskIo>(cli: Cli, io: &D) -> Result<Context, SyncpackError> {
logger::configure(&cli);
let mut disk = Disk::from_workspace(io, &cli.cwd);
let mut disk = Disk::from_workspace(io, &cli.cwd)?;
let rcfile = Rcfile::from_disk(&disk, io, &cli).map_err(SyncpackError::RcfileError)?;
let config = Config {
cli,
Expand Down
2 changes: 1 addition & 1 deletion src/version_group/bun_catalog_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ async fn bun_dual_path_aborts() {
}),
);
io.add_file("bun.lock", "{}".to_string());
let disk = Disk::from_workspace(&io, std::env::current_dir().unwrap().as_path());
let disk = Disk::from_workspace(&io, std::env::current_dir().unwrap().as_path()).unwrap();
assert_eq!(disk.package_manager, Some(PackageManager::Bun));
let err = catalogs::make_catalog_dep_types(&disk).unwrap_err();
assert!(matches!(err, SyncpackError::BunDualCatalogPath));
Expand Down