Skip to content

Commit

Permalink
feat: support for wildcards in deno.json workspace members (#131)
Browse files Browse the repository at this point in the history
  • Loading branch information
bartlomieju authored Oct 25, 2024
1 parent 7d3a053 commit fa8f696
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 47 deletions.
145 changes: 103 additions & 42 deletions src/workspace/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -701,10 +701,100 @@ fn resolve_workspace_for_config_folder(
}
})
};

let collect_member_config_folders =
|kind: &'static str,
pattern_members: Vec<&String>,
file_specifier: &Url,
dir_path: &Path,
config_file_names: &'static [&'static str]|
-> Result<Vec<PathBuf>, WorkspaceDiscoverErrorKind> {
let patterns = pattern_members
.iter()
.flat_map(|raw_member| {
config_file_names.iter().map(|config_file_name| {
PathOrPattern::from_relative(
dir_path,
&format!(
"{}{}",
ensure_trailing_slash(raw_member),
config_file_name
),
)
.map_err(|err| {
ResolveWorkspaceMemberError::MemberToPattern {
kind,
base: root_config_file_directory_url.clone(),
member: raw_member.to_string(),
source: err,
}
})
})
})
.collect::<Result<Vec<_>, _>>()?;

let paths = if patterns.is_empty() {
Vec::new()
} else {
FileCollector::new(|_| true)
.ignore_git_folder()
.ignore_node_modules()
.set_vendor_folder(maybe_vendor_dir.clone())
.collect_file_patterns(
opts.fs,
FilePatterns {
base: dir_path.to_path_buf(),
include: Some(PathOrPatternSet::new(patterns)),
exclude: PathOrPatternSet::new(Vec::new()),
},
)
.map_err(|err| {
WorkspaceDiscoverErrorKind::FailedCollectingMembers {
kind,
config_url: file_specifier.clone(),
source: err,
}
})?
};

Ok(paths)
};

if let Some(deno_json) = root_config_folder.deno_json() {
if let Some(workspace_config) = deno_json.to_workspace_config()? {
for raw_member in &workspace_config.members {
let member_dir_url = resolve_member_url(raw_member)?;
let (pattern_members, path_members): (Vec<_>, Vec<_>) = workspace_config
.members
.iter()
.partition(|member| is_glob_pattern(member) || member.starts_with('!'));

let deno_json_paths = collect_member_config_folders(
"Deno",
pattern_members,
&deno_json.specifier,
&deno_json.dir_path(),
&["deno.json", "deno.jsonc"],
)?;

let mut member_dir_urls =
IndexSet::with_capacity(path_members.len() + deno_json_paths.len());
for path_member in path_members {
let member_dir_url = resolve_member_url(path_member)?;
member_dir_urls.insert((path_member.clone(), member_dir_url));
}
for deno_json_path in deno_json_paths {
let member_dir_url =
url_from_directory_path(deno_json_path.parent().unwrap()).unwrap();
member_dir_urls.insert((
deno_json_path
.parent()
.unwrap()
.to_string_lossy()
.to_string(),
member_dir_url,
));
}

for (raw_member, member_dir_url) in member_dir_urls {
if member_dir_url == root_config_file_directory_url {
return Err(
ResolveWorkspaceMemberError::InvalidSelfReference {
Expand All @@ -715,8 +805,8 @@ fn resolve_workspace_for_config_folder(
}
validate_member_url_is_descendant(&member_dir_url)?;
let member_config_folder = find_member_config_folder(&member_dir_url)?;
let previous_member =
final_members.insert(new_rc(member_dir_url), member_config_folder);
let previous_member = final_members
.insert(new_rc(member_dir_url.clone()), member_config_folder);
if previous_member.is_some() {
return Err(
ResolveWorkspaceMemberError::Duplicate {
Expand All @@ -733,44 +823,15 @@ fn resolve_workspace_for_config_folder(
let (pattern_members, path_members): (Vec<_>, Vec<_>) = members
.iter()
.partition(|member| is_glob_pattern(member) || member.starts_with('!'));
let patterns = pattern_members
.iter()
.map(|raw_member| {
PathOrPattern::from_relative(
pkg_json.dir_path(),
&format!("{}package.json", ensure_trailing_slash(raw_member)),
)
.map_err(|err| {
ResolveWorkspaceMemberError::NpmMemberToPattern {
base: root_config_file_directory_url.clone(),
member: raw_member.to_string(),
source: err,
}
})
})
.collect::<Result<Vec<_>, _>>()?;
let pkg_json_paths = if patterns.is_empty() {
Vec::new()
} else {
FileCollector::new(|_| true)
.ignore_git_folder()
.ignore_node_modules()
.set_vendor_folder(maybe_vendor_dir.clone())
.collect_file_patterns(
opts.fs,
FilePatterns {
base: pkg_json.dir_path().to_path_buf(),
include: Some(PathOrPatternSet::new(patterns)),
exclude: PathOrPatternSet::new(Vec::new()),
},
)
.map_err(|err| {
WorkspaceDiscoverErrorKind::FailedCollectingNpmMembers {
package_json_url: pkg_json.specifier(),
source: err,
}
})?
};

let pkg_json_paths = collect_member_config_folders(
"npm",
pattern_members,
&pkg_json.specifier(),
pkg_json.dir_path(),
&["package.json"],
)?;

let mut member_dir_urls =
IndexSet::with_capacity(path_members.len() + pkg_json_paths.len());
for path_member in path_members {
Expand Down
61 changes: 56 additions & 5 deletions src/workspace/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,12 @@ pub enum ResolveWorkspaceMemberError {
source: url::ParseError,
},
#[error(
"Failed converting npm workspace member '{}' to pattern for config '{}'.",
"Failed converting {kind} workspace member '{}' to pattern for config '{}'.",
member,
base
)]
NpmMemberToPattern {
MemberToPattern {
kind: &'static str,
base: Url,
member: String,
// this error has the text that failed
Expand Down Expand Up @@ -272,10 +273,11 @@ pub enum WorkspaceDiscoverErrorKind {
config_url: Url,
},
#[error(
"Failed collecting npm workspace members.\n Config: {package_json_url}"
"Failed collecting {kind} workspace members.\n Config: {config_url}"
)]
FailedCollectingNpmMembers {
package_json_url: Url,
FailedCollectingMembers {
kind: &'static str,
config_url: Url,
#[source]
source: AnyError,
},
Expand Down Expand Up @@ -3571,6 +3573,55 @@ mod test {
assert_eq!(workspace_dir.workspace.deno_jsons().count(), 1);
}

#[test]
fn test_deno_workspace_globs() {
let mut fs = TestFileSystem::default();
fs.insert_json(
root_dir().join("deno.json"),
json!({
"workspace": ["./packages/*"]
}),
);
fs.insert_json(root_dir().join("packages/package-a/deno.json"), json!({}));
fs.insert_json(root_dir().join("packages/package-b/deno.json"), json!({}));
fs.insert_json(root_dir().join("packages/package-c/deno.jsonc"), json!({}));
let workspace_dir =
workspace_at_start_dir(&fs, &root_dir().join("packages"));
assert_eq!(workspace_dir.workspace.diagnostics(), Vec::new());
assert_eq!(workspace_dir.workspace.deno_jsons().count(), 4);
}

#[test]
fn test_deno_workspace_negations() {
for negation in ["!ignored/package-c", "!ignored/**"] {
let mut fs = TestFileSystem::default();
fs.insert_json(
root_dir().join("deno.json"),
json!({
"workspace": [
"**/*",
negation,
]
}),
);
fs.insert_json(
root_dir().join("packages/package-a/deno.json"),
json!({}),
);
fs.insert_json(
root_dir().join("packages/package-b/deno.jsonc"),
json!({}),
);
fs.insert_json(
root_dir().join("ignored/package-c/deno.jsonc"),
json!({}),
);
let workspace_dir = workspace_at_start_dir(&fs, &root_dir());
assert_eq!(workspace_dir.workspace.diagnostics(), Vec::new());
assert_eq!(workspace_dir.workspace.deno_jsons().count(), 3);
}
}

#[test]
fn test_deno_workspace_member_no_config_file_error() {
let mut fs = TestFileSystem::default();
Expand Down

0 comments on commit fa8f696

Please sign in to comment.