Skip to content

Commit 840a734

Browse files
authored
fix(add): handle version tags like @latest in deno add for JSR packages (#32859)
## Summary `deno add jsr:@std/cli@latest` panics with: ``` programming error: cannot use matches with a tag: latest ``` The version tag `latest` was passed through to `deno_semver::VersionReq::matches()` which panics on tags. Fix by detecting tags via `version_req.tag()` in the add command's version selection (`find_package_and_select_version_for_req`) and resolving to the latest version via `latest_version()` instead. This matches the pattern used elsewhere in the codebase (LSP, cache_deps, outdated) where `tag()` is checked before calling `matches()`. Closes #31298
1 parent d95ceb5 commit 840a734

4 files changed

Lines changed: 83 additions & 41 deletions

File tree

cli/tools/pm/mod.rs

Lines changed: 54 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -765,46 +765,64 @@ async fn find_package_and_select_version_for_req(
765765
};
766766
let prefixed_name = format!("{}:{}", T::SPECIFIER_PREFIX, req.name);
767767
let help_if_found_in_fallback = S::HELP;
768-
let nv = match main_resolver.req_to_nv(req).await {
769-
Ok(Some(nv)) => nv,
770-
Ok(None) => {
771-
if fallback_resolver
772-
.req_to_nv(req)
773-
.await
774-
.ok()
775-
.flatten()
776-
.is_some()
777-
{
778-
// it's in the other registry
779-
return Ok(PackageAndVersion::NotFound {
780-
package: prefixed_name,
781-
help: Some(help_if_found_in_fallback),
782-
package_req: req.clone(),
783-
});
784-
}
785-
786-
return Ok(PackageAndVersion::NotFound {
787-
package: prefixed_name,
788-
help: None,
789-
package_req: req.clone(),
790-
});
768+
// JSR has no dist-tags, so a tag can't go through req_to_nv
769+
// (VersionReq::matches panics on a tag). "@latest" is conventionally
770+
// understood as "the newest version", so resolve it to the latest
771+
// published version; reject any other tag rather than silently treating
772+
// it as latest. npm resolves dist-tags natively via its registry, so this
773+
// only applies to JSR.
774+
let maybe_nv = if matches!(
775+
&add_package_req.value,
776+
AddRmPackageReqValue::Jsr(_)
777+
) && let Some(tag) = req.version_req.tag()
778+
{
779+
if tag != "latest" {
780+
bail!(
781+
"{} does not support the tag '{tag}'. JSR has no dist-tags; use '@latest' or a version requirement instead.",
782+
prefixed_name,
783+
);
791784
}
792-
Err(err) => {
793-
if req.version_req.version_text() == "*"
794-
&& let Some(pre_release_version) =
795-
main_resolver.latest_version(&req.name).await
796-
{
797-
return Ok(PackageAndVersion::NotFound {
798-
package: prefixed_name,
799-
package_req: req.clone(),
800-
help: Some(NotFoundHelp::PreReleaseVersion(
801-
pre_release_version.clone(),
802-
)),
803-
});
785+
main_resolver
786+
.latest_version(&req.name)
787+
.await
788+
.map(|version| PackageNv {
789+
name: req.name.clone(),
790+
version,
791+
})
792+
} else {
793+
match main_resolver.req_to_nv(req).await {
794+
Ok(maybe_nv) => maybe_nv,
795+
Err(err) => {
796+
if req.version_req.version_text() == "*"
797+
&& let Some(pre_release_version) =
798+
main_resolver.latest_version(&req.name).await
799+
{
800+
return Ok(PackageAndVersion::NotFound {
801+
package: prefixed_name,
802+
package_req: req.clone(),
803+
help: Some(NotFoundHelp::PreReleaseVersion(
804+
pre_release_version.clone(),
805+
)),
806+
});
807+
}
808+
return Err(err);
804809
}
805-
return Err(err);
806810
}
807811
};
812+
let Some(nv) = maybe_nv else {
813+
// Not in the primary registry; point at the other one if it's there.
814+
let help = fallback_resolver
815+
.req_to_nv(req)
816+
.await
817+
.ok()
818+
.flatten()
819+
.map(|_| help_if_found_in_fallback);
820+
return Ok(PackageAndVersion::NotFound {
821+
package: prefixed_name,
822+
help,
823+
package_req: req.clone(),
824+
});
825+
};
808826
let range_symbol = if req.version_req.version_text().starts_with('~') {
809827
"~"
810828
} else if save_exact
Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
11
{
22
"tempDir": true,
3-
"steps": [
4-
{
5-
"args": "add npm:ajv@latest",
6-
"output": "add.out"
3+
"tests": {
4+
"npm_latest": {
5+
"steps": [
6+
{
7+
"args": "add npm:ajv@latest",
8+
"output": "add.out"
9+
}
10+
]
11+
},
12+
"jsr_latest": {
13+
"steps": [
14+
{
15+
"args": "add jsr:@denotest/add@latest",
16+
"output": "add_jsr.out"
17+
}
18+
]
19+
},
20+
"jsr_unsupported_tag": {
21+
"steps": [
22+
{
23+
"args": "add jsr:@denotest/add@beta",
24+
"output": "add_jsr_unsupported_tag.out",
25+
"exitCode": 1
26+
}
27+
]
728
}
8-
]
29+
}
930
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add jsr:@denotest/add@[WILDCARD]
2+
[WILDCARD]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
error: jsr:@denotest/add does not support the tag 'beta'. JSR has no dist-tags; use '@latest' or a version requirement instead.

0 commit comments

Comments
 (0)