From 17aec4cda9e020e65d87cd455d28b00f806c9c4c Mon Sep 17 00:00:00 2001 From: Doron Behar Date: Tue, 13 Jan 2026 16:44:42 +0200 Subject: [PATCH] prefetch-npm-deps: warn about unresolved lockfile deps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upstream projects' `package-lock.json` file might include many deps with no resolved line in them, as described here: https://github.com/npm/cli/issues/6301 This can cause dependencies to be missing after generating a hash for `.npmDeps`. If such dependencies are encountered, it's nice to print them and warn the user that an upstream patch might be needed. Co-Authored-By: Jörg Thalheim --- .../node/prefetch-npm-deps/src/parse/lock.rs | 63 +++++++++++++++---- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/pkgs/build-support/node/prefetch-npm-deps/src/parse/lock.rs b/pkgs/build-support/node/prefetch-npm-deps/src/parse/lock.rs index c420fb1e3ede..efa8a5ce5f25 100644 --- a/pkgs/build-support/node/prefetch-npm-deps/src/parse/lock.rs +++ b/pkgs/build-support/node/prefetch-npm-deps/src/parse/lock.rs @@ -11,6 +11,36 @@ use std::{ }; use url::Url; +fn warn_unresolved_packages( + packages_without_resolved: &[(String, Package)], + total_count: usize, +) { + let missing_count = packages_without_resolved.len(); + if missing_count == 0 { + return; + } + + let percentage = (missing_count as f64 / total_count as f64) * 100.0; + eprintln!( + "warning: {} out of {} packages ({:.1}%) are missing 'resolved' URLs and will not be cached.", + missing_count, total_count, percentage + ); + eprintln!("warning: Packages without 'resolved' URLs:"); + for (name, _) in packages_without_resolved.iter().take(10) { + eprintln!("warning: - {}", name.trim_start_matches("node_modules/")); + } + if missing_count > 10 { + eprintln!("warning: ... and {} more", missing_count - 10); + } + if percentage > 50.0 { + eprintln!( + "warning: More than 50% of packages are missing 'resolved' URLs. This may indicate an issue with the lockfile.\n\ + warning: This is a known issue with some npm versions. See: https://github.com/npm/cli/issues/6301\n\ + warning: Consider regenerating upstream's lockfile with: npm install --package-lock-only (sending an upstream PR is best)" + ); + } +} + pub(super) fn packages(content: &str) -> anyhow::Result> { let lockfile: Lockfile = serde_json::from_str(content)?; @@ -20,18 +50,27 @@ pub(super) fn packages(content: &str) -> anyhow::Result> { to_new_packages(lockfile.dependencies.unwrap_or_default(), &initial_url)? } - 2 | 3 => lockfile - .packages - .unwrap_or_default() - .into_iter() - .filter(|(n, p)| !n.is_empty() && matches!(p.resolved, Some(UrlOrString::Url(_)))) - .map(|(n, p)| Package { - // Use the package's own name if present (for aliases like string-width-cjs -> string-width), - // otherwise extract from the lockfile key - name: Some(p.name.unwrap_or(n)), - ..p - }) - .collect(), + 2 | 3 => { + let (packages_with_resolved, packages_without_resolved): (Vec<_>, Vec<_>) = lockfile + .packages + .unwrap_or_default() + .into_iter() + .filter(|(n, _)| !n.is_empty()) + .partition(|(_, p)| matches!(p.resolved, Some(UrlOrString::Url(_)))); + + let total_count = packages_with_resolved.len() + packages_without_resolved.len(); + warn_unresolved_packages(&packages_without_resolved, total_count); + + packages_with_resolved + .into_iter() + .map(|(n, p)| Package { + // Use the package's own name if present (for aliases like string-width-cjs -> string-width), + // otherwise extract from the lockfile key + name: Some(p.name.unwrap_or(n)), + ..p + }) + .collect() + } _ => bail!( "We don't support lockfile version {}, please file an issue.", lockfile.version