fix(install): exclude npm workspace packages from graph roots in install (#28401)

Fixes #28374.
This commit is contained in:
Nathan Whitaker 2025-03-05 12:41:05 -08:00 committed by GitHub
parent be73f11f83
commit 0c0757fe66
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 89 additions and 2 deletions

View file

@ -1,12 +1,16 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use std::borrow::Cow;
use std::collections::HashMap;
use std::collections::HashSet;
use std::sync::Arc;
use deno_core::error::AnyError;
use deno_core::futures::stream::FuturesUnordered;
use deno_core::futures::StreamExt;
use deno_semver::jsr::JsrPackageReqReference;
use deno_semver::npm::NpmPackageReqReference;
use deno_semver::Version;
use crate::factory::CliFactory;
use crate::graph_container::ModuleGraphContainer;
@ -54,7 +58,17 @@ pub async fn cache_top_level_deps(
let mut info_futures = FuturesUnordered::new();
let mut seen_reqs = std::collections::HashSet::new();
let mut seen_reqs = HashSet::new();
let workspace_npm_packages = resolver
.package_jsons()
.filter_map(|pkg_json| {
pkg_json
.name
.as_deref()
.and_then(|name| Some((name, pkg_json.version.as_deref()?)))
})
.collect::<HashMap<_, _>>();
for entry in import_map.imports().entries().chain(
import_map
@ -94,7 +108,27 @@ pub async fn cache_top_level_deps(
});
}
}
"npm" => roots.push(specifier.clone()),
"npm" => {
let Ok(req_ref) =
NpmPackageReqReference::from_str(specifier.as_str())
else {
continue;
};
let version = workspace_npm_packages.get(&*req_ref.req().name);
if let Some(version) = version {
let Ok(version) = Version::parse_from_npm(version) else {
continue;
};
let version_req = &req_ref.req().version_req;
if version_req.tag().is_none() && version_req.matches(&version) {
// if version req matches the workspace package's version, use that
// (so it doesn't need to be installed)
continue;
}
}
roots.push(specifier.clone())
}
_ => {
if entry.key.ends_with('/') && specifier.as_str().ends_with('/') {
continue;

View file

@ -0,0 +1,26 @@
{
"tempDir": true,
"tests": {
"use_workspace": {
"steps": [
{
"args": "install",
"output": ""
}
]
},
"dont_use_workspace": {
"steps": [
{
"args": "run -A edit-import.ts ./b/deno.json @test/a2 npm:@test/a@2.0.0",
"output": ""
},
{
"args": "install",
"output": "Download http://localhost:4260/@test%2fa\nerror: npm package '@test/a' does not exist.\n",
"exitCode": 1
}
]
}
}
}

View file

@ -0,0 +1,8 @@
{
"name": "@test/a",
"exports": {
".": "./mod.js",
"./foo": "./foo.js"
},
"version": "0.1.0"
}

View file

@ -0,0 +1,10 @@
{
"name": "@test/b",
"exports": "./mod.ts",
"version": "0.1.0",
"imports": {
"@test/a": "npm:@test/a",
"foo": "npm:@test/a/foo",
"@test/a2": "npm:@test/a@0.1.0"
}
}

View file

@ -0,0 +1,3 @@
{
"workspace": ["./a", "./b"]
}

View file

@ -0,0 +1,6 @@
const file = Deno.args[0].trim();
const importKey = Deno.args[1].trim();
const newValue = Deno.args[2].trim();
const json = JSON.parse(Deno.readTextFileSync(file));
json["imports"][importKey] = newValue;
Deno.writeTextFileSync(file, JSON.stringify(json));