Skip to content
Merged
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
@@ -1,5 +1,6 @@
# Changelog

- **Fixed** npm workspace patterns beginning with `./` now discover matching packages correctly ([vite-plus#2201](https://github.com/voidzero-dev/vite-plus/issues/2201), [#547](https://github.com/voidzero-dev/vite-task/pull/547)).
- **Fixed** Failures while forwarding output from a started task process no longer incorrectly say the process failed to spawn ([#506](https://github.com/voidzero-dev/vite-task/issues/506)).
- **Fixed** An issue where Bun tasks on macOS did not rerun when files they read, wrote, or listed changed ([#532](https://github.com/voidzero-dev/vite-task/issues/532), [#542](https://github.com/voidzero-dev/vite-task/pull/542)).
- **Improved** Windows file-access tracking now uses sparse temporary backing files where supported, avoiding upfront allocation of the full backing file on disk ([#524](https://github.com/voidzero-dev/vite-task/pull/524)).
Expand Down
83 changes: 79 additions & 4 deletions crates/vite_workspace/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,31 @@ impl WorkspaceMemberGlobs {
let mut has_negated = false;
let mut inclusions = Vec::<Str>::new();
let mut all = Vec::<Str>::new();
for mut pattern in self.workspaces {
pattern.push_str(if pattern.ends_with('/') { "package.json" } else { "/package.json" });
if pattern.starts_with('!') {
for pattern in self.workspaces {
// Ported from npm's workspace pattern normalization:
// https://github.com/npm/map-workspaces/blob/76ea3c852171790e245052e006329de15b09e60e/lib/index.js#L17
let exclusion_count = pattern.bytes().take_while(|byte| *byte == b'!').count();
let path = &pattern[exclusion_count..];
let path_without_optional_dot = path.strip_prefix('.').unwrap_or(path);
let path = if path_without_optional_dot.starts_with('/') {
path_without_optional_dot.trim_start_matches('/')
} else {
path
};
let is_negated = exclusion_count % 2 == 1;

let mut pattern = Str::with_capacity(pattern.len() + "/package.json".len());
if is_negated {
pattern.push('!');
}
pattern.push_str(path);
pattern.push_str(if path.is_empty() || path.ends_with('/') {
"package.json"
} else {
"/package.json"
});

if is_negated {
has_negated = true;
} else {
inclusions.push(pattern.clone());
Expand Down Expand Up @@ -925,7 +947,7 @@ mod tests {
let root_package = serde_json::json!({
"name": "npm-monorepo",
"private": true,
"workspaces": ["packages/*", "apps/*"]
"workspaces": ["./packages/*", "./apps/*"]
});
fs::write(temp_dir_path.join("package.json"), root_package.to_string()).unwrap();

Expand Down Expand Up @@ -1008,6 +1030,59 @@ mod tests {
assert!(found_web_to_ui, "Web app should depend on UI");
}

#[test]
fn test_get_package_graph_npm_workspace_with_root_relative_prefixes() {
let temp_dir = TempDir::new().unwrap();
let temp_dir_path = AbsolutePath::new(temp_dir.path()).unwrap();

let root_package = serde_json::json!({
"name": "npm-monorepo",
"private": true,
"workspaces": [
"./packages/single-slash",
".//packages/double-slash",
".///packages/triple-slash",
"/packages/rooted",
"//packages/double-rooted",
"!.///packages/excluded",
"!!./packages/even-negations",
"./packages/odd-negations",
"!!!.//packages/odd-negations"
]
});
fs::write(temp_dir_path.join("package.json"), root_package.to_string()).unwrap();

let package_names = [
"single-slash",
"double-slash",
"triple-slash",
"rooted",
"double-rooted",
"even-negations",
];
for package_name in package_names.iter().copied().chain(["excluded", "odd-negations"]) {
let package_path = temp_dir_path.join("packages").join(package_name);
fs::create_dir_all(&package_path).unwrap();
fs::write(
package_path.join("package.json"),
serde_json::json!({ "name": package_name }).to_string(),
)
.unwrap();
}

let graph = discover_package_graph(temp_dir_path).unwrap();
let packages_found: FxHashSet<_> =
graph.node_weights().map(|node| node.package_json.name.as_str()).collect();

assert_eq!(graph.node_count(), package_names.len() + 1);
assert!(packages_found.contains("npm-monorepo"));
for package_name in package_names {
assert!(packages_found.contains(package_name), "Should have found {package_name}");
}
assert!(!packages_found.contains("excluded"));
assert!(!packages_found.contains("odd-negations"));
}

#[test]
fn test_get_package_graph_yarn_workspace() {
let temp_dir = TempDir::new().unwrap();
Expand Down
Loading