Skip to content

Commit bde70a6

Browse files
authored
Merge pull request #8 from dev-five-git/support-wildcard
Support wildcard
2 parents 6c6ab19 + c462060 commit bde70a6

File tree

5 files changed

+67
-59
lines changed

5 files changed

+67
-59
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"changes":{"bridge/node/package.json":"Patch","bridge/python/pyproject.toml":"Patch","crates/core/Cargo.toml":"Patch","crates/node/Cargo.toml":"Patch","crates/python/Cargo.toml":"Patch","crates/rust/Cargo.toml":"Patch","crates/cli/Cargo.toml":"Patch"},"note":"Support wildcard","date":"2026-04-08T11:47:50.105992Z"}

Cargo.lock

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

crates/node/src/parser.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,14 @@ impl PackageJsonManifest {
7171
/// Check if a dependency value is a resolvable version spec.
7272
///
7373
/// Filters out non-semver specifiers like workspace protocols, npm aliases,
74-
/// git URLs, file paths, and link protocols.
74+
/// git URLs, file paths, and link protocols. Also filters out unresolvable
75+
/// "always-newest" specifiers like `latest`, `*`, `x`, and `X`, since
76+
/// updating those would be a no-op (they already mean "the latest version").
7577
fn is_version_spec(value: &str) -> bool {
78+
let trimmed = value.trim();
79+
if matches!(trimmed, "latest" | "*" | "x" | "X" | "") {
80+
return false;
81+
}
7682
!value.starts_with("workspace:")
7783
&& !value.starts_with("npm:")
7884
&& !value.starts_with("git+")
@@ -298,7 +304,31 @@ mod tests {
298304
}
299305
}"#;
300306
let manifest = PackageJsonManifest::parse(json).unwrap();
301-
assert_eq!(manifest.dependencies.len(), 8);
307+
// `*` and `latest` are filtered out as unresolvable always-newest specs.
308+
assert_eq!(manifest.dependencies.len(), 6);
309+
let names: Vec<_> = manifest
310+
.dependencies
311+
.iter()
312+
.map(|d| d.name.as_str())
313+
.collect();
314+
assert!(!names.contains(&"d"));
315+
assert!(!names.contains(&"e"));
316+
}
317+
318+
#[test]
319+
fn test_latest_and_wildcard_skipped() {
320+
let json = r#"{
321+
"dependencies": {
322+
"always-new": "latest",
323+
"any": "*",
324+
"x-any": "x",
325+
"X-any": "X",
326+
"react": "^18.0.0"
327+
}
328+
}"#;
329+
let manifest = PackageJsonManifest::parse(json).unwrap();
330+
assert_eq!(manifest.dependencies.len(), 1);
331+
assert_eq!(manifest.dependencies[0].name, "react");
302332
}
303333

304334
#[test]

crates/python/src/parser.rs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,23 +87,27 @@ impl PyProjectManifest {
8787
continue; // Skip python version constraint
8888
}
8989
if let Some(version) = extract_poetry_version(item) {
90-
deps.push(DependencySpec {
91-
name: name.to_owned(),
92-
current_req: version,
93-
section: DependencySection::Dependencies,
94-
});
90+
if !is_wildcard_req(&version) {
91+
deps.push(DependencySpec {
92+
name: name.to_owned(),
93+
current_req: version,
94+
section: DependencySection::Dependencies,
95+
});
96+
}
9597
}
9698
}
9799
}
98100
// Poetry dev-dependencies
99101
if let Some(dev_deps) = poetry.get("dev-dependencies").and_then(Item::as_table) {
100102
for (name, item) in dev_deps {
101103
if let Some(version) = extract_poetry_version(item) {
102-
deps.push(DependencySpec {
103-
name: name.to_owned(),
104-
current_req: version,
105-
section: DependencySection::DevDependencies,
106-
});
104+
if !is_wildcard_req(&version) {
105+
deps.push(DependencySpec {
106+
name: name.to_owned(),
107+
current_req: version,
108+
section: DependencySection::DevDependencies,
109+
});
110+
}
107111
}
108112
}
109113
}
@@ -210,6 +214,10 @@ fn parse_pep508_spec(spec: &str, section: DependencySection) -> Option<Dependenc
210214
return None; // No version constraint
211215
}
212216

217+
if is_wildcard_req(rest) {
218+
return None; // `*`, `==*`, etc. already mean "any version"
219+
}
220+
213221
Some(DependencySpec {
214222
name: name.to_owned(),
215223
current_req: rest.to_owned(),
@@ -257,6 +265,18 @@ fn replace_version_in_pep508(spec: &str, new_version: &str) -> String {
257265
format!("{name}{extras}{new_version}{marker}")
258266
}
259267

268+
/// Check if a version requirement is an unresolvable wildcard like `*` or `==*`.
269+
///
270+
/// Such requirements already mean "any/latest version", so updating them
271+
/// would be a meaningless no-op and we filter them out at parse time.
272+
fn is_wildcard_req(req: &str) -> bool {
273+
let stripped = req
274+
.trim()
275+
.trim_start_matches(['=', '~', '^', '>', '<'])
276+
.trim();
277+
matches!(stripped, "" | "*")
278+
}
279+
260280
/// Extract a version string from a Poetry dependency value.
261281
fn extract_poetry_version(item: &Item) -> Option<String> {
262282
match item {

crates/rust/src/parser.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,10 @@ impl CargoTomlManifest {
7171
) {
7272
for (name, item) in table {
7373
if let Some(version) = Self::extract_version(item) {
74-
// Skip path/git dependencies without a version
75-
if !version.is_empty() {
74+
// Skip path/git dependencies without a version, and skip
75+
// wildcard-only requirements like `*` which already mean
76+
// "any version" — updating them would be a meaningless no-op.
77+
if !version.is_empty() && version.trim() != "*" {
7678
deps.push(DependencySpec {
7779
name: name.to_owned(),
7880
current_req: version,

0 commit comments

Comments
 (0)