use std::path::Path;
use crate::lockfile;
use crate::registry;
use crate::utils;
fn base_name(package: &str) -> &str {
if let Some(idx) = package.rfind('@') {
if idx > 0 && !package[idx + 1..].contains('/') {
return &package[..idx];
}
}
package
}
pub fn prefetch_from_lockfile(quiet: bool) -> Result<(), String> {
let specs = crate::install::resolve_install_from_package_json(true)?;
if specs.is_empty() {
if !quiet {
println!("No dependencies to prefetch.");
}
return Ok(());
}
let (resolved_urls, resolved_integrity) = lockfile::read_resolved_urls_and_integrity_from_dir(Path::new("."))
.ok_or("No package-lock.json or bun.lock found.")?;
let cache_dir = std::path::PathBuf::from(utils::get_cache_dir());
let store_dir = cache_dir.join("store");
std::fs::create_dir_all(&store_dir).map_err(|e| e.to_string())?;
let mut work: Vec<(String, String, Option<String>)> = Vec::new();
for spec in &specs {
if utils::get_cached_tarball(spec).is_some() {
continue;
}
let url = resolved_urls.get(spec).cloned().or_else(|| {
let base = base_name(spec);
let version = spec.rfind('@').map(|i| &spec[i + 1..]).unwrap_or("latest");
Some(lockfile::tarball_url_from_registry(base, version))
});
if let Some(url) = url {
let integrity = resolved_integrity.get(spec).cloned();
work.push((spec.clone(), url, integrity));
}
}
const PREFETCH_CONCURRENCY: usize = 8;
let mut fetched = 0usize;
let mut index_batch: std::collections::HashMap<String, String> = std::collections::HashMap::new();
for chunk in work.chunks(PREFETCH_CONCURRENCY) {
use std::sync::mpsc;
use std::thread;
let (tx, rx) = mpsc::channel();
for (spec, url, integrity) in chunk {
let spec = spec.clone();
let url = url.clone();
let integrity = integrity.clone();
let cache_dir = cache_dir.clone();
let tx = tx.clone();
if !quiet {
println!("Prefetching {}...", spec);
}
thread::spawn(move || {
let res = registry::download_tarball_to_store_hash_only(
&url,
&cache_dir,
&spec,
integrity.as_deref(),
);
let _ = tx.send((spec, res));
});
}
drop(tx);
for (spec, res) in rx {
let hash = res?;
index_batch.insert(spec, hash);
fetched += 1;
}
}
if !index_batch.is_empty() {
let mut index = utils::read_store_index();
index.extend(index_batch);
utils::write_store_index(&index).map_err(|e| e.to_string())?;
}
if !quiet && fetched > 0 {
println!("Prefetched {} package(s).", fetched);
}
Ok(())
}