Skip to content

Commit 51cbf9c

Browse files
authored
feat: allow duplicated package names (#51)
1 parent cf8daeb commit 51cbf9c

File tree

3 files changed

+31
-37
lines changed

3 files changed

+31
-37
lines changed

Cargo.lock

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

crates/vite_workspace/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ serde = { workspace = true, features = ["derive"] }
1515
serde_json = { workspace = true, features = ["preserve_order"] }
1616
serde_yml = { workspace = true }
1717
thiserror = { workspace = true }
18+
vec1 = { workspace = true, features = ["smallvec-v1"] }
1819
vite_glob = { workspace = true }
1920
vite_path = { workspace = true }
2021
vite_str = { workspace = true }

crates/vite_workspace/src/lib.rs

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ mod error;
22
pub mod package;
33
mod package_manager;
44

5-
use std::{fs, io};
5+
use std::{collections::hash_map::Entry, fs, io};
66

77
use petgraph::graph::{DefaultIx, DiGraph, EdgeIndex, IndexType, NodeIndex};
88
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
99
use serde::{Deserialize, Serialize};
10+
use vec1::smallvec_v1::SmallVec1;
1011
use vite_glob::GlobPatternSet;
1112
use vite_path::{AbsolutePath, AbsolutePathBuf, RelativePathBuf};
1213
use vite_str::Str;
@@ -104,17 +105,12 @@ pub struct PackageInfo {
104105
#[derive(Default)]
105106
struct PackageGraphBuilder {
106107
id_and_deps_by_path: HashMap<RelativePathBuf, (PackageNodeIndex, Vec<(Str, DependencyType)>)>,
107-
// Only for packages with a name
108-
name_to_path: HashMap<Str, RelativePathBuf>,
108+
name_to_path: HashMap<Str, SmallVec1<[RelativePathBuf; 1]>>,
109109
graph: DiGraph<PackageInfo, DependencyType, PackageIx>,
110110
}
111111

112112
impl PackageGraphBuilder {
113-
fn add_package(
114-
&mut self,
115-
package_path: RelativePathBuf,
116-
package_json: PackageJson,
117-
) -> Result<(), Error> {
113+
fn add_package(&mut self, package_path: RelativePathBuf, package_json: PackageJson) {
118114
let deps = package_json.get_workspace_dependencies().collect::<Vec<_>>();
119115
let package_name = package_json.name.clone();
120116
let id = self.graph.add_node(PackageInfo { package_json, path: package_path.clone() });
@@ -123,22 +119,17 @@ impl PackageGraphBuilder {
123119
self.id_and_deps_by_path.insert(package_path.clone(), (id, deps));
124120

125121
// Also maintain name to path mapping for dependency resolution
126-
if !package_name.is_empty()
127-
&& let Some(existing_path) = self.name_to_path.insert(package_name, package_path)
128-
{
129-
// Duplicate package name found
130-
let existing_id = self.id_and_deps_by_path.get(&existing_path).unwrap().0;
131-
let existing_package_info = &self.graph[existing_id];
132-
return Err(Error::DuplicatedPackageName {
133-
name: existing_package_info.package_json.name.clone(),
134-
path1: existing_package_info.path.clone(),
135-
path2: self.graph[id].path.clone(),
136-
});
122+
match self.name_to_path.entry(package_name.clone()) {
123+
Entry::Vacant(entry) => {
124+
entry.insert(SmallVec1::new(package_path));
125+
}
126+
Entry::Occupied(mut entry) => {
127+
entry.get_mut().push(package_path);
128+
}
137129
}
138-
Ok(())
139130
}
140131

141-
fn build(mut self) -> DiGraph<PackageInfo, DependencyType, PackageIx> {
132+
fn build(mut self) -> Result<DiGraph<PackageInfo, DependencyType, PackageIx>, Error> {
142133
for (id, deps) in self.id_and_deps_by_path.values() {
143134
for (dep_name, dep_type) in deps {
144135
// Skip dependencies on nameless packages (empty string)
@@ -148,15 +139,22 @@ impl PackageGraphBuilder {
148139
}
149140

150141
// Resolve dependency name to path, then find the node
151-
if let Some(dep_path) = self.name_to_path.get(dep_name)
152-
&& let Some((dep_id, _)) = self.id_and_deps_by_path.get(dep_path)
142+
if let Some(dep_paths) = self.name_to_path.get(dep_name)
143+
&& let Some((dep_id, _)) = self.id_and_deps_by_path.get(dep_paths.first())
153144
{
145+
if let [dep_path1, dep_path2, ..] = dep_paths.as_slice() {
146+
return Err(Error::DuplicatedPackageName {
147+
name: dep_name.clone(),
148+
path1: dep_path1.clone(),
149+
path2: dep_path2.clone(),
150+
});
151+
}
154152
self.graph.add_edge(*id, *dep_id, *dep_type);
155153
}
156154
// Silently skip if dependency not found - it might be an external package
157155
}
158156
}
159-
self.graph
157+
Ok(self.graph)
160158
}
161159
}
162160

@@ -205,9 +203,9 @@ pub fn load_package_graph(
205203
WorkspaceFile::NonWorkspacePackage(file) => {
206204
// For non-workspace packages, add the package.json to the graph as a root package
207205
let package_json: PackageJson = serde_json::from_reader(file)?;
208-
graph_builder.add_package(RelativePathBuf::default(), package_json)?;
206+
graph_builder.add_package(RelativePathBuf::default(), package_json);
209207

210-
return Ok(graph_builder.build());
208+
return graph_builder.build();
211209
}
212210
};
213211

@@ -224,15 +222,15 @@ pub fn load_package_graph(
224222
};
225223

226224
has_root_package = has_root_package || package_path.as_str().is_empty();
227-
graph_builder.add_package(package_path, package_json)?;
225+
graph_builder.add_package(package_path, package_json);
228226
}
229227
// try add the root package anyway if the member globs do not include it.
230228
if !has_root_package {
231229
let package_json_path = workspace_root.path.join("package.json");
232230
match fs::read(&package_json_path) {
233231
Ok(package_json) => {
234232
let package_json: PackageJson = serde_json::from_slice(&package_json)?;
235-
graph_builder.add_package(RelativePathBuf::default(), package_json)?;
233+
graph_builder.add_package(RelativePathBuf::default(), package_json);
236234
}
237235
Err(err) => {
238236
if err.kind() != io::ErrorKind::NotFound {
@@ -241,7 +239,7 @@ pub fn load_package_graph(
241239
}
242240
}
243241
}
244-
Ok(graph_builder.build())
242+
graph_builder.build()
245243
}
246244

247245
#[cfg(test)]
@@ -537,15 +535,9 @@ mod tests {
537535
});
538536
fs::write(temp_dir_path.join("packages/pkg-2/package.json"), pkg_2.to_string()).unwrap();
539537

540-
// Should return an error for duplicate package names
538+
// duplicate package names is allowed.
541539
let result = discover_package_graph(temp_dir_path);
542-
assert!(result.is_err());
543-
544-
if let Err(Error::DuplicatedPackageName { name, .. }) = result {
545-
assert_eq!(name, "duplicate");
546-
} else {
547-
panic!("Expected DuplicatedPackageName error, got: {result:?}");
548-
}
540+
assert!(result.is_ok());
549541
}
550542

551543
#[test]

0 commit comments

Comments
 (0)