2023-01-02 16:00:42 -05:00
|
|
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
2023-04-06 18:46:44 -04:00
|
|
|
|
|
|
|
use std::collections::HashMap;
|
2023-01-23 17:41:02 -05:00
|
|
|
use std::path::PathBuf;
|
2023-04-06 18:46:44 -04:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
use deno_core::anyhow::bail;
|
|
|
|
use deno_core::anyhow::Context;
|
|
|
|
use deno_core::error::AnyError;
|
|
|
|
use deno_core::futures::stream::FuturesOrdered;
|
|
|
|
use deno_core::futures::StreamExt;
|
|
|
|
use deno_core::parking_lot::Mutex;
|
|
|
|
use deno_npm::registry::NpmRegistryApi;
|
2023-04-13 10:47:45 -04:00
|
|
|
use deno_npm::resolution::SerializedNpmResolutionSnapshot;
|
|
|
|
use deno_npm::resolution::SerializedNpmResolutionSnapshotPackage;
|
|
|
|
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
|
2023-04-06 18:46:44 -04:00
|
|
|
use deno_npm::NpmPackageId;
|
|
|
|
use deno_semver::npm::NpmPackageReq;
|
2019-11-03 10:39:27 -05:00
|
|
|
|
2022-12-07 18:13:45 -05:00
|
|
|
use crate::args::config_file::LockConfig;
|
2022-11-02 11:32:30 -04:00
|
|
|
use crate::args::ConfigFile;
|
2023-04-12 18:04:45 -04:00
|
|
|
use crate::npm::CliNpmRegistryApi;
|
2022-11-02 11:32:30 -04:00
|
|
|
use crate::Flags;
|
2021-12-07 19:21:04 -05:00
|
|
|
|
2022-12-20 12:00:57 -05:00
|
|
|
use super::DenoSubcommand;
|
|
|
|
|
2023-01-23 17:41:02 -05:00
|
|
|
pub use deno_lockfile::Lockfile;
|
|
|
|
pub use deno_lockfile::LockfileError;
|
|
|
|
|
|
|
|
pub fn discover(
|
|
|
|
flags: &Flags,
|
|
|
|
maybe_config_file: Option<&ConfigFile>,
|
|
|
|
) -> Result<Option<Lockfile>, AnyError> {
|
|
|
|
if flags.no_lock
|
|
|
|
|| matches!(
|
|
|
|
flags.subcommand,
|
|
|
|
DenoSubcommand::Install(_) | DenoSubcommand::Uninstall(_)
|
|
|
|
)
|
|
|
|
{
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
|
|
|
|
let filename = match flags.lock {
|
|
|
|
Some(ref lock) => PathBuf::from(lock),
|
|
|
|
None => match maybe_config_file {
|
|
|
|
Some(config_file) => {
|
|
|
|
if config_file.specifier.scheme() == "file" {
|
|
|
|
match config_file.to_lock_config()? {
|
|
|
|
Some(LockConfig::Bool(lock)) if !lock => {
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
Some(LockConfig::PathBuf(lock)) => config_file
|
|
|
|
.specifier
|
|
|
|
.to_file_path()
|
|
|
|
.unwrap()
|
|
|
|
.parent()
|
|
|
|
.unwrap()
|
|
|
|
.join(lock),
|
|
|
|
_ => {
|
|
|
|
let mut path = config_file.specifier.to_file_path().unwrap();
|
|
|
|
path.set_file_name("deno.lock");
|
|
|
|
path
|
2022-12-07 18:13:45 -05:00
|
|
|
}
|
2022-11-02 11:32:30 -04:00
|
|
|
}
|
|
|
|
} else {
|
2023-01-23 17:41:02 -05:00
|
|
|
return Ok(None);
|
2022-11-02 11:32:30 -04:00
|
|
|
}
|
|
|
|
}
|
2023-01-23 17:41:02 -05:00
|
|
|
None => return Ok(None),
|
|
|
|
},
|
|
|
|
};
|
2022-10-25 12:20:07 -04:00
|
|
|
|
2023-01-23 17:41:02 -05:00
|
|
|
let lockfile = Lockfile::new(filename, flags.lock_write)?;
|
|
|
|
Ok(Some(lockfile))
|
2019-11-03 10:39:27 -05:00
|
|
|
}
|
2020-10-04 19:32:18 -04:00
|
|
|
|
2023-04-06 18:46:44 -04:00
|
|
|
pub async fn snapshot_from_lockfile(
|
|
|
|
lockfile: Arc<Mutex<Lockfile>>,
|
2023-04-12 18:04:45 -04:00
|
|
|
api: &CliNpmRegistryApi,
|
2023-04-13 10:47:45 -04:00
|
|
|
) -> Result<ValidSerializedNpmResolutionSnapshot, AnyError> {
|
2023-04-06 18:46:44 -04:00
|
|
|
let (root_packages, mut packages) = {
|
|
|
|
let lockfile = lockfile.lock();
|
|
|
|
|
|
|
|
let mut root_packages =
|
|
|
|
HashMap::<NpmPackageReq, NpmPackageId>::with_capacity(
|
|
|
|
lockfile.content.npm.specifiers.len(),
|
|
|
|
);
|
|
|
|
// collect the specifiers to version mappings
|
|
|
|
for (key, value) in &lockfile.content.npm.specifiers {
|
|
|
|
let package_req = NpmPackageReq::from_str(key)
|
|
|
|
.with_context(|| format!("Unable to parse npm specifier: {key}"))?;
|
|
|
|
let package_id = NpmPackageId::from_serialized(value)?;
|
|
|
|
root_packages.insert(package_req, package_id.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
// now fill the packages except for the dist information
|
|
|
|
let mut packages = Vec::with_capacity(lockfile.content.npm.packages.len());
|
|
|
|
for (key, package) in &lockfile.content.npm.packages {
|
|
|
|
let pkg_id = NpmPackageId::from_serialized(key)?;
|
|
|
|
|
|
|
|
// collect the dependencies
|
|
|
|
let mut dependencies = HashMap::with_capacity(package.dependencies.len());
|
|
|
|
for (name, specifier) in &package.dependencies {
|
|
|
|
let dep_id = NpmPackageId::from_serialized(specifier)?;
|
|
|
|
dependencies.insert(name.clone(), dep_id);
|
|
|
|
}
|
|
|
|
|
2023-04-13 10:47:45 -04:00
|
|
|
packages.push(SerializedNpmResolutionSnapshotPackage {
|
2023-04-06 18:46:44 -04:00
|
|
|
pkg_id,
|
|
|
|
dependencies,
|
2023-05-17 17:38:50 -04:00
|
|
|
// temporarily empty
|
|
|
|
os: Default::default(),
|
|
|
|
cpu: Default::default(),
|
|
|
|
dist: Default::default(),
|
|
|
|
optional_dependencies: Default::default(),
|
2023-04-06 18:46:44 -04:00
|
|
|
});
|
2023-01-23 17:41:02 -05:00
|
|
|
}
|
2023-04-06 18:46:44 -04:00
|
|
|
(root_packages, packages)
|
|
|
|
};
|
|
|
|
|
|
|
|
// now that the lockfile is dropped, fetch the package version information
|
2023-04-12 18:04:45 -04:00
|
|
|
let pkg_nvs = packages
|
|
|
|
.iter()
|
|
|
|
.map(|p| p.pkg_id.nv.clone())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
let get_version_infos = || {
|
|
|
|
FuturesOrdered::from_iter(pkg_nvs.iter().map(|nv| async move {
|
|
|
|
let package_info = api.package_info(&nv.name).await?;
|
|
|
|
match package_info.version_info(nv) {
|
|
|
|
Ok(version_info) => Ok(version_info),
|
|
|
|
Err(err) => {
|
|
|
|
bail!("Could not find '{}' specified in the lockfile.", err.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
};
|
|
|
|
let mut version_infos = get_version_infos();
|
2023-04-06 18:46:44 -04:00
|
|
|
let mut i = 0;
|
2023-04-12 18:04:45 -04:00
|
|
|
while let Some(result) = version_infos.next().await {
|
2023-05-17 17:38:50 -04:00
|
|
|
match result {
|
|
|
|
Ok(version_info) => {
|
|
|
|
let mut package = &mut packages[i];
|
|
|
|
package.dist = version_info.dist;
|
|
|
|
package.cpu = version_info.cpu;
|
|
|
|
package.os = version_info.os;
|
|
|
|
package.optional_dependencies =
|
|
|
|
version_info.optional_dependencies.into_keys().collect();
|
|
|
|
}
|
2023-04-12 18:04:45 -04:00
|
|
|
Err(err) => {
|
|
|
|
if api.mark_force_reload() {
|
|
|
|
// reset and try again
|
|
|
|
version_infos = get_version_infos();
|
|
|
|
i = 0;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
return Err(err);
|
|
|
|
}
|
|
|
|
}
|
2023-05-17 17:38:50 -04:00
|
|
|
}
|
2023-04-12 18:04:45 -04:00
|
|
|
|
2023-04-06 18:46:44 -04:00
|
|
|
i += 1;
|
2020-10-04 19:32:18 -04:00
|
|
|
}
|
2023-04-06 18:46:44 -04:00
|
|
|
|
2023-04-12 18:04:45 -04:00
|
|
|
// clear the memory cache to reduce memory usage
|
|
|
|
api.clear_memory_cache();
|
|
|
|
|
2023-04-13 10:47:45 -04:00
|
|
|
SerializedNpmResolutionSnapshot {
|
2023-04-06 18:46:44 -04:00
|
|
|
packages,
|
|
|
|
root_packages,
|
2023-04-13 10:47:45 -04:00
|
|
|
}
|
|
|
|
.into_valid()
|
2023-04-06 18:46:44 -04:00
|
|
|
.context("The lockfile is corrupt. You can recreate it with --lock-write")
|
2020-10-04 19:32:18 -04:00
|
|
|
}
|