mirror of
https://github.com/denoland/deno.git
synced 2024-12-25 16:49:18 -05:00
refactor(npm): make NpmCache
, CliNpmRegistryApi
, and NpmResolution
internal to npm::managed
(#20764)
This commit is contained in:
parent
d5b6c636b0
commit
148694eb35
23 changed files with 894 additions and 847 deletions
|
@ -1,12 +1,8 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_npm::registry::NpmRegistryApi;
|
||||
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
|
||||
|
||||
use crate::args::ConfigFile;
|
||||
use crate::Flags;
|
||||
|
@ -49,17 +45,3 @@ pub fn discover(
|
|||
let lockfile = Lockfile::new(filename, flags.lock_write)?;
|
||||
Ok(Some(lockfile))
|
||||
}
|
||||
|
||||
pub async fn snapshot_from_lockfile(
|
||||
lockfile: Arc<Mutex<Lockfile>>,
|
||||
api: &dyn NpmRegistryApi,
|
||||
) -> Result<ValidSerializedNpmResolutionSnapshot, AnyError> {
|
||||
let incomplete_snapshot = {
|
||||
let lock = lockfile.lock();
|
||||
deno_npm::resolution::incomplete_snapshot_from_lockfile(&lock)?
|
||||
};
|
||||
let snapshot =
|
||||
deno_npm::resolution::snapshot_from_lockfile(incomplete_snapshot, api)
|
||||
.await?;
|
||||
Ok(snapshot)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ mod lockfile;
|
|||
pub mod package_json;
|
||||
|
||||
pub use self::import_map::resolve_import_map_from_specifier;
|
||||
pub use self::lockfile::snapshot_from_lockfile;
|
||||
use self::package_json::PackageJsonDeps;
|
||||
use ::import_map::ImportMap;
|
||||
use deno_core::resolve_url_or_path;
|
||||
|
@ -55,6 +54,8 @@ use deno_runtime::inspector_server::InspectorServer;
|
|||
use deno_runtime::permissions::PermissionsOptions;
|
||||
use once_cell::sync::Lazy;
|
||||
use once_cell::sync::OnceCell;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::io::BufReader;
|
||||
|
@ -67,8 +68,6 @@ use std::sync::Arc;
|
|||
use thiserror::Error;
|
||||
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::npm::CliNpmRegistryApi;
|
||||
use crate::npm::NpmProcessState;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists;
|
||||
use crate::util::glob::expand_globs;
|
||||
use crate::version;
|
||||
|
@ -77,6 +76,28 @@ use deno_config::FmtConfig;
|
|||
use deno_config::LintConfig;
|
||||
use deno_config::TestConfig;
|
||||
|
||||
static NPM_REGISTRY_DEFAULT_URL: Lazy<Url> = Lazy::new(|| {
|
||||
let env_var_name = "NPM_CONFIG_REGISTRY";
|
||||
if let Ok(registry_url) = std::env::var(env_var_name) {
|
||||
// ensure there is a trailing slash for the directory
|
||||
let registry_url = format!("{}/", registry_url.trim_end_matches('/'));
|
||||
match Url::parse(®istry_url) {
|
||||
Ok(url) => {
|
||||
return url;
|
||||
}
|
||||
Err(err) => {
|
||||
log::debug!("Invalid {} environment variable: {:#}", env_var_name, err,);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Url::parse("https://registry.npmjs.org").unwrap()
|
||||
});
|
||||
|
||||
pub fn npm_registry_default_url() -> &'static Url {
|
||||
&NPM_REGISTRY_DEFAULT_URL
|
||||
}
|
||||
|
||||
pub fn ts_config_to_emit_options(
|
||||
config: deno_config::TsConfig,
|
||||
) -> deno_ast::EmitOptions {
|
||||
|
@ -545,6 +566,13 @@ pub fn get_root_cert_store(
|
|||
Ok(root_cert_store)
|
||||
}
|
||||
|
||||
/// State provided to the process via an environment variable.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct NpmProcessState {
|
||||
pub snapshot: deno_npm::resolution::SerializedNpmResolutionSnapshot,
|
||||
pub local_node_modules_path: Option<String>,
|
||||
}
|
||||
|
||||
const RESOLUTION_STATE_ENV_VAR_NAME: &str =
|
||||
"DENO_DONT_USE_INTERNAL_NODE_COMPAT_STATE";
|
||||
|
||||
|
@ -560,7 +588,7 @@ static NPM_PROCESS_STATE: Lazy<Option<NpmProcessState>> = Lazy::new(|| {
|
|||
/// Overrides for the options below that when set will
|
||||
/// use these values over the values derived from the
|
||||
/// CLI flags or config file.
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Clone)]
|
||||
struct CliOptionOverrides {
|
||||
import_map_specifier: Option<Option<ModuleSpecifier>>,
|
||||
}
|
||||
|
@ -843,32 +871,15 @@ impl CliOptions {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn resolve_npm_resolution_snapshot(
|
||||
pub fn resolve_npm_resolution_snapshot(
|
||||
&self,
|
||||
api: &CliNpmRegistryApi,
|
||||
) -> Result<Option<ValidSerializedNpmResolutionSnapshot>, AnyError> {
|
||||
if let Some(state) = &*NPM_PROCESS_STATE {
|
||||
// TODO(bartlomieju): remove this clone
|
||||
return Ok(Some(state.snapshot.clone().into_valid()?));
|
||||
Ok(Some(state.snapshot.clone().into_valid()?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
if let Some(lockfile) = self.maybe_lockfile() {
|
||||
if !lockfile.lock().overwrite {
|
||||
let snapshot = snapshot_from_lockfile(lockfile.clone(), api)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed reading lockfile '{}'",
|
||||
lockfile.lock().filename.display()
|
||||
)
|
||||
})?;
|
||||
// clear the memory cache to reduce memory usage
|
||||
api.clear_memory_cache();
|
||||
return Ok(Some(snapshot));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
// If the main module should be treated as being in an npm package.
|
||||
|
@ -892,6 +903,19 @@ impl CliOptions {
|
|||
self.maybe_node_modules_folder.clone()
|
||||
}
|
||||
|
||||
pub fn with_node_modules_dir_path(&self, path: PathBuf) -> Self {
|
||||
Self {
|
||||
flags: self.flags.clone(),
|
||||
initial_cwd: self.initial_cwd.clone(),
|
||||
maybe_node_modules_folder: Some(path),
|
||||
maybe_vendor_folder: self.maybe_vendor_folder.clone(),
|
||||
maybe_config_file: self.maybe_config_file.clone(),
|
||||
maybe_package_json: self.maybe_package_json.clone(),
|
||||
maybe_lockfile: self.maybe_lockfile.clone(),
|
||||
overrides: self.overrides.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_modules_dir_enablement(&self) -> Option<bool> {
|
||||
self.flags.node_modules_dir.or_else(|| {
|
||||
self
|
||||
|
|
142
cli/factory.rs
142
cli/factory.rs
|
@ -29,15 +29,12 @@ use crate::module_loader::ModuleLoadPreparer;
|
|||
use crate::module_loader::NpmModuleLoader;
|
||||
use crate::node::CliCjsCodeAnalyzer;
|
||||
use crate::node::CliNodeCodeTranslator;
|
||||
use crate::npm::create_npm_fs_resolver;
|
||||
use crate::npm::CliNpmRegistryApi;
|
||||
use crate::npm::create_cli_npm_resolver;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::ManagedCliNpmResolver;
|
||||
use crate::npm::NpmCache;
|
||||
use crate::npm::NpmCacheDir;
|
||||
use crate::npm::NpmPackageFsResolver;
|
||||
use crate::npm::NpmResolution;
|
||||
use crate::npm::PackageJsonDepsInstaller;
|
||||
use crate::npm::CliNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedPackageJsonInstallerOption;
|
||||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||
use crate::resolver::CliGraphResolver;
|
||||
use crate::resolver::CliGraphResolverOptions;
|
||||
use crate::standalone::DenoCompileBinaryWriter;
|
||||
|
@ -157,12 +154,8 @@ struct CliFactoryServices {
|
|||
module_load_preparer: Deferred<Arc<ModuleLoadPreparer>>,
|
||||
node_code_translator: Deferred<Arc<CliNodeCodeTranslator>>,
|
||||
node_resolver: Deferred<Arc<NodeResolver>>,
|
||||
npm_api: Deferred<Arc<CliNpmRegistryApi>>,
|
||||
npm_cache: Deferred<Arc<NpmCache>>,
|
||||
npm_resolver: Deferred<Arc<dyn CliNpmResolver>>,
|
||||
npm_resolution: Deferred<Arc<NpmResolution>>,
|
||||
package_json_deps_provider: Deferred<Arc<PackageJsonDepsProvider>>,
|
||||
package_json_deps_installer: Deferred<Arc<PackageJsonDepsInstaller>>,
|
||||
text_only_progress_bar: Deferred<ProgressBar>,
|
||||
type_checker: Deferred<Arc<TypeChecker>>,
|
||||
cjs_resolutions: Deferred<Arc<CjsResolutionStore>>,
|
||||
|
@ -294,47 +287,6 @@ impl CliFactory {
|
|||
.get_or_init(|| self.options.maybe_lockfile())
|
||||
}
|
||||
|
||||
pub fn npm_cache(&self) -> Result<&Arc<NpmCache>, AnyError> {
|
||||
self.services.npm_cache.get_or_try_init(|| {
|
||||
Ok(Arc::new(NpmCache::new(
|
||||
NpmCacheDir::new(self.deno_dir()?.npm_folder_path()),
|
||||
self.options.cache_setting(),
|
||||
self.fs().clone(),
|
||||
self.http_client().clone(),
|
||||
self.text_only_progress_bar().clone(),
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn npm_api(&self) -> Result<&Arc<CliNpmRegistryApi>, AnyError> {
|
||||
self.services.npm_api.get_or_try_init(|| {
|
||||
Ok(Arc::new(CliNpmRegistryApi::new(
|
||||
CliNpmRegistryApi::default_url().to_owned(),
|
||||
self.npm_cache()?.clone(),
|
||||
self.http_client().clone(),
|
||||
self.text_only_progress_bar().clone(),
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn npm_resolution(&self) -> Result<&Arc<NpmResolution>, AnyError> {
|
||||
self
|
||||
.services
|
||||
.npm_resolution
|
||||
.get_or_try_init_async(async {
|
||||
let npm_api = self.npm_api()?;
|
||||
Ok(Arc::new(NpmResolution::from_serialized(
|
||||
npm_api.clone(),
|
||||
self
|
||||
.options
|
||||
.resolve_npm_resolution_snapshot(npm_api)
|
||||
.await?,
|
||||
self.maybe_lockfile().as_ref().cloned(),
|
||||
)))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn npm_resolver(
|
||||
&self,
|
||||
) -> Result<&Arc<dyn CliNpmResolver>, AnyError> {
|
||||
|
@ -342,46 +294,39 @@ impl CliFactory {
|
|||
.services
|
||||
.npm_resolver
|
||||
.get_or_try_init_async(async {
|
||||
let npm_resolution = self.npm_resolution().await?;
|
||||
let fs = self.fs().clone();
|
||||
let npm_fs_resolver = create_npm_fs_resolver(
|
||||
fs.clone(),
|
||||
self.npm_cache()?.clone(),
|
||||
self.text_only_progress_bar(),
|
||||
CliNpmRegistryApi::default_url().to_owned(),
|
||||
npm_resolution.clone(),
|
||||
self.options.node_modules_dir_path(),
|
||||
self.options.npm_system_info(),
|
||||
);
|
||||
Ok(Arc::new(ManagedCliNpmResolver::new(
|
||||
self.npm_api()?.clone(),
|
||||
fs.clone(),
|
||||
npm_resolution.clone(),
|
||||
npm_fs_resolver,
|
||||
self.maybe_lockfile().as_ref().cloned(),
|
||||
self.package_json_deps_installer().await?.clone(),
|
||||
)) as Arc<dyn CliNpmResolver>)
|
||||
create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions {
|
||||
snapshot: match self.options.resolve_npm_resolution_snapshot()? {
|
||||
Some(snapshot) => {
|
||||
CliNpmResolverManagedSnapshotOption::Specified(Some(snapshot))
|
||||
}
|
||||
None => match self.maybe_lockfile() {
|
||||
Some(lockfile) => {
|
||||
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
|
||||
lockfile.clone(),
|
||||
)
|
||||
}
|
||||
None => CliNpmResolverManagedSnapshotOption::Specified(None),
|
||||
},
|
||||
},
|
||||
maybe_lockfile: self.maybe_lockfile().as_ref().cloned(),
|
||||
fs: self.fs().clone(),
|
||||
http_client: self.http_client().clone(),
|
||||
npm_global_cache_dir: self.deno_dir()?.npm_folder_path(),
|
||||
cache_setting: self.options.cache_setting(),
|
||||
text_only_progress_bar: self.text_only_progress_bar().clone(),
|
||||
maybe_node_modules_path: self.options.node_modules_dir_path(),
|
||||
package_json_installer:
|
||||
CliNpmResolverManagedPackageJsonInstallerOption::ConditionalInstall(
|
||||
self.package_json_deps_provider().clone(),
|
||||
),
|
||||
npm_system_info: self.options.npm_system_info(),
|
||||
npm_registry_url: crate::args::npm_registry_default_url().to_owned(),
|
||||
}))
|
||||
.await
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn create_node_modules_npm_fs_resolver(
|
||||
&self,
|
||||
node_modules_dir_path: PathBuf,
|
||||
) -> Result<Arc<dyn NpmPackageFsResolver>, AnyError> {
|
||||
Ok(create_npm_fs_resolver(
|
||||
self.fs().clone(),
|
||||
self.npm_cache()?.clone(),
|
||||
self.text_only_progress_bar(),
|
||||
CliNpmRegistryApi::default_url().to_owned(),
|
||||
self.npm_resolution().await?.clone(),
|
||||
// when an explicit path is provided here, it will create the
|
||||
// local node_modules variant of an npm fs resolver
|
||||
Some(node_modules_dir_path),
|
||||
self.options.npm_system_info(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn package_json_deps_provider(&self) -> &Arc<PackageJsonDepsProvider> {
|
||||
self.services.package_json_deps_provider.get_or_init(|| {
|
||||
Arc::new(PackageJsonDepsProvider::new(
|
||||
|
@ -390,22 +335,6 @@ impl CliFactory {
|
|||
})
|
||||
}
|
||||
|
||||
pub async fn package_json_deps_installer(
|
||||
&self,
|
||||
) -> Result<&Arc<PackageJsonDepsInstaller>, AnyError> {
|
||||
self
|
||||
.services
|
||||
.package_json_deps_installer
|
||||
.get_or_try_init_async(async {
|
||||
Ok(Arc::new(PackageJsonDepsInstaller::new(
|
||||
self.package_json_deps_provider().clone(),
|
||||
self.npm_api()?.clone(),
|
||||
self.npm_resolution().await?.clone(),
|
||||
)))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn maybe_import_map(
|
||||
&self,
|
||||
) -> Result<&Option<Arc<ImportMap>>, AnyError> {
|
||||
|
@ -616,9 +545,6 @@ impl CliFactory {
|
|||
self.file_fetcher()?,
|
||||
self.http_client(),
|
||||
self.deno_dir()?,
|
||||
self.npm_api()?,
|
||||
self.npm_cache()?,
|
||||
self.npm_resolution().await?,
|
||||
self.npm_resolver().await?.as_ref(),
|
||||
self.options.npm_system_info(),
|
||||
self.package_json_deps_provider(),
|
||||
|
|
|
@ -161,14 +161,14 @@ fn code_as_string(code: &Option<lsp::NumberOrString>) -> String {
|
|||
pub struct TsResponseImportMapper<'a> {
|
||||
documents: &'a Documents,
|
||||
maybe_import_map: Option<&'a ImportMap>,
|
||||
npm_resolver: &'a dyn CliNpmResolver,
|
||||
npm_resolver: Option<&'a dyn CliNpmResolver>,
|
||||
}
|
||||
|
||||
impl<'a> TsResponseImportMapper<'a> {
|
||||
pub fn new(
|
||||
documents: &'a Documents,
|
||||
maybe_import_map: Option<&'a ImportMap>,
|
||||
npm_resolver: &'a dyn CliNpmResolver,
|
||||
npm_resolver: Option<&'a dyn CliNpmResolver>,
|
||||
) -> Self {
|
||||
Self {
|
||||
documents,
|
||||
|
@ -194,7 +194,9 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
|
||||
if let Some(npm_resolver) =
|
||||
self.npm_resolver.as_ref().and_then(|r| r.as_managed())
|
||||
{
|
||||
if npm_resolver.in_npm_package(specifier) {
|
||||
if let Ok(Some(pkg_id)) =
|
||||
npm_resolver.resolve_pkg_id_from_specifier(specifier)
|
||||
|
@ -250,8 +252,8 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
let specifier_path = specifier.to_file_path().ok()?;
|
||||
let root_folder = self
|
||||
.npm_resolver
|
||||
.resolve_pkg_folder_from_specifier(specifier)
|
||||
.ok()
|
||||
.as_ref()
|
||||
.and_then(|r| r.resolve_pkg_folder_from_specifier(specifier).ok())
|
||||
.flatten()?;
|
||||
let package_json_path = root_folder.join("package.json");
|
||||
let package_json_text = std::fs::read_to_string(&package_json_path).ok()?;
|
||||
|
|
|
@ -4,6 +4,7 @@ use deno_ast::MediaType;
|
|||
use deno_core::anyhow::anyhow;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::resolve_url;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
|
@ -12,7 +13,6 @@ use deno_core::unsync::spawn;
|
|||
use deno_core::ModuleSpecifier;
|
||||
use deno_graph::GraphKind;
|
||||
use deno_lockfile::Lockfile;
|
||||
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node::NodeResolver;
|
||||
|
@ -82,7 +82,6 @@ use super::urls::LspClientUrl;
|
|||
use crate::args::get_root_cert_store;
|
||||
use crate::args::package_json;
|
||||
use crate::args::resolve_import_map_from_specifier;
|
||||
use crate::args::snapshot_from_lockfile;
|
||||
use crate::args::CaData;
|
||||
use crate::args::CacheSetting;
|
||||
use crate::args::CliOptions;
|
||||
|
@ -102,14 +101,12 @@ use crate::graph_util;
|
|||
use crate::http_util::HttpClient;
|
||||
use crate::lsp::tsc::file_text_changes_to_workspace_edit;
|
||||
use crate::lsp::urls::LspUrlKind;
|
||||
use crate::npm::create_npm_fs_resolver;
|
||||
use crate::npm::CliNpmRegistryApi;
|
||||
use crate::npm::create_cli_npm_resolver_for_lsp;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::ManagedCliNpmResolver;
|
||||
use crate::npm::NpmCache;
|
||||
use crate::npm::NpmCacheDir;
|
||||
use crate::npm::NpmResolution;
|
||||
use crate::npm::PackageJsonDepsInstaller;
|
||||
use crate::npm::CliNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedPackageJsonInstallerOption;
|
||||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||
use crate::tools::fmt::format_file;
|
||||
use crate::tools::fmt::format_parsed_source;
|
||||
use crate::util::fs::remove_dir_all_if_exists;
|
||||
|
@ -130,16 +127,10 @@ impl RootCertStoreProvider for LspRootCertStoreProvider {
|
|||
struct LspNpmServices {
|
||||
/// When this hash changes, the services need updating
|
||||
config_hash: LspNpmConfigHash,
|
||||
/// Npm's registry api.
|
||||
api: Arc<CliNpmRegistryApi>,
|
||||
/// Npm's search api.
|
||||
search_api: CliNpmSearchApi,
|
||||
/// Npm cache
|
||||
cache: Arc<NpmCache>,
|
||||
/// Npm resolution that is stored in memory.
|
||||
resolution: Arc<NpmResolution>,
|
||||
/// Resolver for npm packages.
|
||||
resolver: Arc<dyn CliNpmResolver>,
|
||||
resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
@ -475,72 +466,6 @@ impl LanguageServer {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_npm_api_and_cache(
|
||||
dir: &DenoDir,
|
||||
http_client: Arc<HttpClient>,
|
||||
registry_url: &ModuleSpecifier,
|
||||
progress_bar: &ProgressBar,
|
||||
) -> (Arc<CliNpmRegistryApi>, Arc<NpmCache>) {
|
||||
let npm_cache = Arc::new(NpmCache::new(
|
||||
NpmCacheDir::new(dir.npm_folder_path()),
|
||||
// Use an "only" cache setting in order to make the
|
||||
// user do an explicit "cache" command and prevent
|
||||
// the cache from being filled with lots of packages while
|
||||
// the user is typing.
|
||||
CacheSetting::Only,
|
||||
Arc::new(deno_fs::RealFs),
|
||||
http_client.clone(),
|
||||
progress_bar.clone(),
|
||||
));
|
||||
let api = Arc::new(CliNpmRegistryApi::new(
|
||||
registry_url.clone(),
|
||||
npm_cache.clone(),
|
||||
http_client,
|
||||
progress_bar.clone(),
|
||||
));
|
||||
(api, npm_cache)
|
||||
}
|
||||
|
||||
fn create_npm_resolver_and_resolution(
|
||||
registry_url: &ModuleSpecifier,
|
||||
progress_bar: ProgressBar,
|
||||
api: Arc<CliNpmRegistryApi>,
|
||||
npm_cache: Arc<NpmCache>,
|
||||
node_modules_dir_path: Option<PathBuf>,
|
||||
maybe_snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
|
||||
) -> (Arc<dyn CliNpmResolver>, Arc<NpmResolution>) {
|
||||
let resolution = Arc::new(NpmResolution::from_serialized(
|
||||
api.clone(),
|
||||
maybe_snapshot,
|
||||
// Don't provide the lockfile. We don't want these resolvers
|
||||
// updating it. Only the cache request should update the lockfile.
|
||||
None,
|
||||
));
|
||||
let fs = Arc::new(deno_fs::RealFs);
|
||||
let fs_resolver = create_npm_fs_resolver(
|
||||
fs.clone(),
|
||||
npm_cache,
|
||||
&progress_bar,
|
||||
registry_url.clone(),
|
||||
resolution.clone(),
|
||||
node_modules_dir_path,
|
||||
NpmSystemInfo::default(),
|
||||
);
|
||||
(
|
||||
Arc::new(ManagedCliNpmResolver::new(
|
||||
api,
|
||||
fs,
|
||||
resolution.clone(),
|
||||
fs_resolver,
|
||||
// Don't provide the lockfile. We don't want these resolvers
|
||||
// updating it. Only the cache request should update the lockfile.
|
||||
None,
|
||||
Arc::new(PackageJsonDepsInstaller::no_op()),
|
||||
)),
|
||||
resolution,
|
||||
)
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
fn new(client: Client) -> Self {
|
||||
let dir = DenoDir::new(None).expect("could not access DENO_DIR");
|
||||
|
@ -571,23 +496,6 @@ impl Inner {
|
|||
diagnostics_state.clone(),
|
||||
);
|
||||
let assets = Assets::new(ts_server.clone());
|
||||
let registry_url = CliNpmRegistryApi::default_url();
|
||||
let progress_bar = ProgressBar::new(ProgressBarStyle::TextOnly);
|
||||
|
||||
let (npm_api, npm_cache) = create_npm_api_and_cache(
|
||||
&dir,
|
||||
http_client.clone(),
|
||||
registry_url,
|
||||
&progress_bar,
|
||||
);
|
||||
let (npm_resolver, npm_resolution) = create_npm_resolver_and_resolution(
|
||||
registry_url,
|
||||
progress_bar,
|
||||
npm_api.clone(),
|
||||
npm_cache.clone(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
Self {
|
||||
assets,
|
||||
|
@ -610,11 +518,8 @@ impl Inner {
|
|||
module_registries_location,
|
||||
npm: LspNpmServices {
|
||||
config_hash: LspNpmConfigHash(0), // this will be updated in initialize
|
||||
api: npm_api,
|
||||
search_api: npm_search_api,
|
||||
cache: npm_cache,
|
||||
resolution: npm_resolution,
|
||||
resolver: npm_resolver,
|
||||
resolver: None,
|
||||
},
|
||||
performance,
|
||||
ts_fixable_diagnostics: Default::default(),
|
||||
|
@ -799,41 +704,27 @@ impl Inner {
|
|||
}
|
||||
|
||||
pub fn snapshot(&self) -> Arc<StateSnapshot> {
|
||||
// create a new snapshotted npm resolution and resolver
|
||||
let npm_resolution = Arc::new(NpmResolution::new(
|
||||
self.npm.api.clone(),
|
||||
self.npm.resolution.snapshot(),
|
||||
self.config.maybe_lockfile().cloned(),
|
||||
));
|
||||
let node_fs = Arc::new(deno_fs::RealFs);
|
||||
let npm_resolver = Arc::new(ManagedCliNpmResolver::new(
|
||||
self.npm.api.clone(),
|
||||
node_fs.clone(),
|
||||
npm_resolution.clone(),
|
||||
create_npm_fs_resolver(
|
||||
node_fs.clone(),
|
||||
self.npm.cache.clone(),
|
||||
&ProgressBar::new(ProgressBarStyle::TextOnly),
|
||||
self.npm.api.base_url().clone(),
|
||||
npm_resolution,
|
||||
self.config.maybe_node_modules_dir_path().cloned(),
|
||||
NpmSystemInfo::default(),
|
||||
),
|
||||
self.config.maybe_lockfile().cloned(),
|
||||
Arc::new(PackageJsonDepsInstaller::no_op()),
|
||||
));
|
||||
let node_resolver =
|
||||
Arc::new(NodeResolver::new(node_fs, npm_resolver.clone()));
|
||||
let maybe_state_npm_snapshot = self
|
||||
.npm
|
||||
.resolver
|
||||
.as_ref()
|
||||
.map(|resolver| resolver.clone_snapshotted())
|
||||
.map(|resolver| {
|
||||
let fs = Arc::new(deno_fs::RealFs);
|
||||
let node_resolver =
|
||||
Arc::new(NodeResolver::new(fs, resolver.clone().into_npm_resolver()));
|
||||
StateNpmSnapshot {
|
||||
node_resolver,
|
||||
npm_resolver: resolver,
|
||||
}
|
||||
});
|
||||
Arc::new(StateSnapshot {
|
||||
assets: self.assets.snapshot(),
|
||||
cache_metadata: self.cache_metadata.clone(),
|
||||
config: self.config.snapshot(),
|
||||
documents: self.documents.clone(),
|
||||
maybe_import_map: self.maybe_import_map.clone(),
|
||||
npm: Some(StateNpmSnapshot {
|
||||
node_resolver,
|
||||
npm_resolver,
|
||||
}),
|
||||
npm: maybe_state_npm_snapshot,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -935,25 +826,6 @@ impl Inner {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_npm_snapshot(
|
||||
&self,
|
||||
) -> Option<ValidSerializedNpmResolutionSnapshot> {
|
||||
let lockfile = self.config.maybe_lockfile()?;
|
||||
let snapshot =
|
||||
match snapshot_from_lockfile(lockfile.clone(), &*self.npm.api).await {
|
||||
Ok(snapshot) => snapshot,
|
||||
Err(err) => {
|
||||
lsp_warn!("Failed getting npm snapshot from lockfile: {}", err);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// clear the memory cache to reduce memory usage
|
||||
self.npm.api.clear_memory_cache();
|
||||
|
||||
Some(snapshot)
|
||||
}
|
||||
|
||||
async fn recreate_npm_services_if_necessary(&mut self) {
|
||||
let deno_dir = match DenoDir::new(self.maybe_global_cache_path.clone()) {
|
||||
Ok(deno_dir) => deno_dir,
|
||||
|
@ -967,24 +839,15 @@ impl Inner {
|
|||
return; // no need to do anything
|
||||
}
|
||||
|
||||
let registry_url = CliNpmRegistryApi::default_url();
|
||||
let progress_bar = ProgressBar::new(ProgressBarStyle::TextOnly);
|
||||
(self.npm.api, self.npm.cache) = create_npm_api_and_cache(
|
||||
&deno_dir,
|
||||
self.http_client.clone(),
|
||||
registry_url,
|
||||
&progress_bar,
|
||||
);
|
||||
let maybe_snapshot = self.get_npm_snapshot().await;
|
||||
(self.npm.resolver, self.npm.resolution) =
|
||||
create_npm_resolver_and_resolution(
|
||||
registry_url,
|
||||
progress_bar,
|
||||
self.npm.api.clone(),
|
||||
self.npm.cache.clone(),
|
||||
self.npm.resolver = Some(
|
||||
create_npm_resolver(
|
||||
&deno_dir,
|
||||
&self.http_client,
|
||||
self.config.maybe_lockfile(),
|
||||
self.config.maybe_node_modules_dir_path().cloned(),
|
||||
maybe_snapshot,
|
||||
);
|
||||
)
|
||||
.await,
|
||||
);
|
||||
|
||||
// update the hash
|
||||
self.npm.config_hash = config_hash;
|
||||
|
@ -1217,6 +1080,45 @@ impl Inner {
|
|||
}
|
||||
}
|
||||
|
||||
async fn create_npm_resolver(
|
||||
deno_dir: &DenoDir,
|
||||
http_client: &Arc<HttpClient>,
|
||||
maybe_lockfile: Option<&Arc<Mutex<Lockfile>>>,
|
||||
maybe_node_modules_dir_path: Option<PathBuf>,
|
||||
) -> Arc<dyn CliNpmResolver> {
|
||||
create_cli_npm_resolver_for_lsp(CliNpmResolverCreateOptions::Managed(
|
||||
CliNpmResolverManagedCreateOptions {
|
||||
http_client: http_client.clone(),
|
||||
snapshot: match maybe_lockfile {
|
||||
Some(lockfile) => {
|
||||
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
|
||||
lockfile.clone(),
|
||||
)
|
||||
}
|
||||
None => CliNpmResolverManagedSnapshotOption::Specified(None),
|
||||
},
|
||||
// Don't provide the lockfile. We don't want these resolvers
|
||||
// updating it. Only the cache request should update the lockfile.
|
||||
maybe_lockfile: None,
|
||||
fs: Arc::new(deno_fs::RealFs),
|
||||
npm_global_cache_dir: deno_dir.npm_folder_path(),
|
||||
// Use an "only" cache setting in order to make the
|
||||
// user do an explicit "cache" command and prevent
|
||||
// the cache from being filled with lots of packages while
|
||||
// the user is typing.
|
||||
cache_setting: CacheSetting::Only,
|
||||
text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly),
|
||||
maybe_node_modules_path: maybe_node_modules_dir_path,
|
||||
// do not install while resolving in the lsp—leave that to the cache command
|
||||
package_json_installer:
|
||||
CliNpmResolverManagedPackageJsonInstallerOption::NoInstall,
|
||||
npm_registry_url: crate::args::npm_registry_default_url().to_owned(),
|
||||
npm_system_info: NpmSystemInfo::default(),
|
||||
},
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
// lspower::LanguageServer methods. This file's LanguageServer delegates to us.
|
||||
impl Inner {
|
||||
async fn initialize(
|
||||
|
@ -1371,7 +1273,7 @@ impl Inner {
|
|||
maybe_import_map: self.maybe_import_map.clone(),
|
||||
maybe_config_file: self.config.maybe_config_file(),
|
||||
maybe_package_json: self.maybe_package_json.as_ref(),
|
||||
npm_resolver: Some(self.npm.resolver.clone()),
|
||||
npm_resolver: self.npm.resolver.clone(),
|
||||
});
|
||||
|
||||
// refresh the npm specifiers because it might have discovered
|
||||
|
@ -1446,7 +1348,9 @@ impl Inner {
|
|||
let npm_resolver = self.npm.resolver.clone();
|
||||
// spawn to avoid the LSP's Send requirements
|
||||
let handle = spawn(async move {
|
||||
if let Some(npm_resolver) = npm_resolver.as_managed() {
|
||||
if let Some(npm_resolver) =
|
||||
npm_resolver.as_ref().and_then(|r| r.as_managed())
|
||||
{
|
||||
npm_resolver.set_package_reqs(&package_reqs).await
|
||||
} else {
|
||||
Ok(())
|
||||
|
@ -2165,7 +2069,7 @@ impl Inner {
|
|||
TsResponseImportMapper::new(
|
||||
&self.documents,
|
||||
self.maybe_import_map.as_deref(),
|
||||
self.npm.resolver.as_ref(),
|
||||
self.npm.resolver.as_deref(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -12,8 +12,8 @@ use deno_npm::registry::NpmPackageInfo;
|
|||
use deno_runtime::permissions::PermissionsContainer;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::args::npm_registry_default_url;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::npm::CliNpmRegistryApi;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait NpmSearchApi {
|
||||
|
@ -36,7 +36,7 @@ impl CliNpmSearchApi {
|
|||
pub fn new(file_fetcher: FileFetcher, custom_base_url: Option<Url>) -> Self {
|
||||
Self {
|
||||
base_url: custom_base_url
|
||||
.unwrap_or_else(|| CliNpmRegistryApi::default_url().clone()),
|
||||
.unwrap_or_else(|| npm_registry_default_url().clone()),
|
||||
file_fetcher,
|
||||
info_cache: Default::default(),
|
||||
search_cache: Default::default(),
|
||||
|
|
268
cli/npm/cache_dir.rs
Normal file
268
cli/npm/cache_dir.rs
Normal file
|
@ -0,0 +1,268 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::url::Url;
|
||||
use deno_npm::NpmPackageCacheFolderId;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::Version;
|
||||
|
||||
use crate::util::fs::canonicalize_path;
|
||||
use crate::util::path::root_url_to_safe_local_dirname;
|
||||
|
||||
/// The global cache directory of npm packages.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NpmCacheDir {
|
||||
root_dir: PathBuf,
|
||||
// cached url representation of the root directory
|
||||
root_dir_url: Url,
|
||||
}
|
||||
|
||||
impl NpmCacheDir {
|
||||
pub fn new(root_dir: PathBuf) -> Self {
|
||||
fn try_get_canonicalized_root_dir(
|
||||
root_dir: &Path,
|
||||
) -> Result<PathBuf, AnyError> {
|
||||
if !root_dir.exists() {
|
||||
std::fs::create_dir_all(root_dir)
|
||||
.with_context(|| format!("Error creating {}", root_dir.display()))?;
|
||||
}
|
||||
Ok(canonicalize_path(root_dir)?)
|
||||
}
|
||||
|
||||
// this may fail on readonly file systems, so just ignore if so
|
||||
let root_dir =
|
||||
try_get_canonicalized_root_dir(&root_dir).unwrap_or(root_dir);
|
||||
let root_dir_url = Url::from_directory_path(&root_dir).unwrap();
|
||||
Self {
|
||||
root_dir,
|
||||
root_dir_url,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root_dir_url(&self) -> &Url {
|
||||
&self.root_dir_url
|
||||
}
|
||||
|
||||
pub fn package_folder_for_id(
|
||||
&self,
|
||||
folder_id: &NpmPackageCacheFolderId,
|
||||
registry_url: &Url,
|
||||
) -> PathBuf {
|
||||
if folder_id.copy_index == 0 {
|
||||
self.package_folder_for_name_and_version(&folder_id.nv, registry_url)
|
||||
} else {
|
||||
self
|
||||
.package_name_folder(&folder_id.nv.name, registry_url)
|
||||
.join(format!("{}_{}", folder_id.nv.version, folder_id.copy_index))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn package_folder_for_name_and_version(
|
||||
&self,
|
||||
package: &PackageNv,
|
||||
registry_url: &Url,
|
||||
) -> PathBuf {
|
||||
self
|
||||
.package_name_folder(&package.name, registry_url)
|
||||
.join(package.version.to_string())
|
||||
}
|
||||
|
||||
pub fn package_name_folder(&self, name: &str, registry_url: &Url) -> PathBuf {
|
||||
let mut dir = self.registry_folder(registry_url);
|
||||
if name.to_lowercase() != name {
|
||||
let encoded_name = mixed_case_package_name_encode(name);
|
||||
// Using the encoded directory may have a collision with an actual package name
|
||||
// so prefix it with an underscore since npm packages can't start with that
|
||||
dir.join(format!("_{encoded_name}"))
|
||||
} else {
|
||||
// ensure backslashes are used on windows
|
||||
for part in name.split('/') {
|
||||
dir = dir.join(part);
|
||||
}
|
||||
dir
|
||||
}
|
||||
}
|
||||
|
||||
pub fn registry_folder(&self, registry_url: &Url) -> PathBuf {
|
||||
self
|
||||
.root_dir
|
||||
.join(root_url_to_safe_local_dirname(registry_url))
|
||||
}
|
||||
|
||||
pub fn resolve_package_folder_id_from_specifier(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
registry_url: &Url,
|
||||
) -> Option<NpmPackageCacheFolderId> {
|
||||
let registry_root_dir = self
|
||||
.root_dir_url
|
||||
.join(&format!(
|
||||
"{}/",
|
||||
root_url_to_safe_local_dirname(registry_url)
|
||||
.to_string_lossy()
|
||||
.replace('\\', "/")
|
||||
))
|
||||
// this not succeeding indicates a fatal issue, so unwrap
|
||||
.unwrap();
|
||||
let mut relative_url = registry_root_dir.make_relative(specifier)?;
|
||||
if relative_url.starts_with("../") {
|
||||
return None;
|
||||
}
|
||||
|
||||
// base32 decode the url if it starts with an underscore
|
||||
// * Ex. _{base32(package_name)}/
|
||||
if let Some(end_url) = relative_url.strip_prefix('_') {
|
||||
let mut parts = end_url
|
||||
.split('/')
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<Vec<_>>();
|
||||
match mixed_case_package_name_decode(&parts[0]) {
|
||||
Some(part) => {
|
||||
parts[0] = part;
|
||||
}
|
||||
None => return None,
|
||||
}
|
||||
relative_url = parts.join("/");
|
||||
}
|
||||
|
||||
// examples:
|
||||
// * chalk/5.0.1/
|
||||
// * @types/chalk/5.0.1/
|
||||
// * some-package/5.0.1_1/ -- where the `_1` (/_\d+/) is a copy of the folder for peer deps
|
||||
let is_scoped_package = relative_url.starts_with('@');
|
||||
let mut parts = relative_url
|
||||
.split('/')
|
||||
.enumerate()
|
||||
.take(if is_scoped_package { 3 } else { 2 })
|
||||
.map(|(_, part)| part)
|
||||
.collect::<Vec<_>>();
|
||||
if parts.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
let version_part = parts.pop().unwrap();
|
||||
let name = parts.join("/");
|
||||
let (version, copy_index) =
|
||||
if let Some((version, copy_count)) = version_part.split_once('_') {
|
||||
(version, copy_count.parse::<u8>().ok()?)
|
||||
} else {
|
||||
(version_part, 0)
|
||||
};
|
||||
Some(NpmPackageCacheFolderId {
|
||||
nv: PackageNv {
|
||||
name,
|
||||
version: Version::parse_from_npm(version).ok()?,
|
||||
},
|
||||
copy_index,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_cache_location(&self) -> PathBuf {
|
||||
self.root_dir.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mixed_case_package_name_encode(name: &str) -> String {
|
||||
// use base32 encoding because it's reversible and the character set
|
||||
// only includes the characters within 0-9 and A-Z so it can be lower cased
|
||||
base32::encode(
|
||||
base32::Alphabet::RFC4648 { padding: false },
|
||||
name.as_bytes(),
|
||||
)
|
||||
.to_lowercase()
|
||||
}
|
||||
|
||||
pub fn mixed_case_package_name_decode(name: &str) -> Option<String> {
|
||||
base32::decode(base32::Alphabet::RFC4648 { padding: false }, name)
|
||||
.and_then(|b| String::from_utf8(b).ok())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use deno_core::url::Url;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::Version;
|
||||
|
||||
use super::NpmCacheDir;
|
||||
use crate::npm::cache_dir::NpmPackageCacheFolderId;
|
||||
|
||||
#[test]
|
||||
fn should_get_package_folder() {
|
||||
let deno_dir = crate::cache::DenoDir::new(None).unwrap();
|
||||
let root_dir = deno_dir.npm_folder_path();
|
||||
let cache = NpmCacheDir::new(root_dir.clone());
|
||||
let registry_url = Url::parse("https://registry.npmjs.org/").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
cache.package_folder_for_id(
|
||||
&NpmPackageCacheFolderId {
|
||||
nv: PackageNv {
|
||||
name: "json".to_string(),
|
||||
version: Version::parse_from_npm("1.2.5").unwrap(),
|
||||
},
|
||||
copy_index: 0,
|
||||
},
|
||||
®istry_url,
|
||||
),
|
||||
root_dir
|
||||
.join("registry.npmjs.org")
|
||||
.join("json")
|
||||
.join("1.2.5"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cache.package_folder_for_id(
|
||||
&NpmPackageCacheFolderId {
|
||||
nv: PackageNv {
|
||||
name: "json".to_string(),
|
||||
version: Version::parse_from_npm("1.2.5").unwrap(),
|
||||
},
|
||||
copy_index: 1,
|
||||
},
|
||||
®istry_url,
|
||||
),
|
||||
root_dir
|
||||
.join("registry.npmjs.org")
|
||||
.join("json")
|
||||
.join("1.2.5_1"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cache.package_folder_for_id(
|
||||
&NpmPackageCacheFolderId {
|
||||
nv: PackageNv {
|
||||
name: "JSON".to_string(),
|
||||
version: Version::parse_from_npm("2.1.5").unwrap(),
|
||||
},
|
||||
copy_index: 0,
|
||||
},
|
||||
®istry_url,
|
||||
),
|
||||
root_dir
|
||||
.join("registry.npmjs.org")
|
||||
.join("_jjju6tq")
|
||||
.join("2.1.5"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cache.package_folder_for_id(
|
||||
&NpmPackageCacheFolderId {
|
||||
nv: PackageNv {
|
||||
name: "@types/JSON".to_string(),
|
||||
version: Version::parse_from_npm("2.1.5").unwrap(),
|
||||
},
|
||||
copy_index: 0,
|
||||
},
|
||||
®istry_url,
|
||||
),
|
||||
root_dir
|
||||
.join("registry.npmjs.org")
|
||||
.join("_ib2hs4dfomxuuu2pjy")
|
||||
.join("2.1.5"),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -17,241 +17,15 @@ use deno_npm::registry::NpmPackageVersionDistInfo;
|
|||
use deno_npm::NpmPackageCacheFolderId;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::Version;
|
||||
|
||||
use crate::args::CacheSetting;
|
||||
use crate::http_util::HttpClient;
|
||||
use crate::util::fs::canonicalize_path;
|
||||
use crate::npm::NpmCacheDir;
|
||||
use crate::util::fs::hard_link_dir_recursive;
|
||||
use crate::util::path::root_url_to_safe_local_dirname;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
|
||||
use super::tarball::verify_and_extract_tarball;
|
||||
|
||||
const NPM_PACKAGE_SYNC_LOCK_FILENAME: &str = ".deno_sync_lock";
|
||||
|
||||
pub fn with_folder_sync_lock(
|
||||
package: &PackageNv,
|
||||
output_folder: &Path,
|
||||
action: impl FnOnce() -> Result<(), AnyError>,
|
||||
) -> Result<(), AnyError> {
|
||||
fn inner(
|
||||
output_folder: &Path,
|
||||
action: impl FnOnce() -> Result<(), AnyError>,
|
||||
) -> Result<(), AnyError> {
|
||||
fs::create_dir_all(output_folder).with_context(|| {
|
||||
format!("Error creating '{}'.", output_folder.display())
|
||||
})?;
|
||||
|
||||
// This sync lock file is a way to ensure that partially created
|
||||
// npm package directories aren't considered valid. This could maybe
|
||||
// be a bit smarter in the future to not bother extracting here
|
||||
// if another process has taken the lock in the past X seconds and
|
||||
// wait for the other process to finish (it could try to create the
|
||||
// file with `create_new(true)` then if it exists, check the metadata
|
||||
// then wait until the other process finishes with a timeout), but
|
||||
// for now this is good enough.
|
||||
let sync_lock_path = output_folder.join(NPM_PACKAGE_SYNC_LOCK_FILENAME);
|
||||
match fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&sync_lock_path)
|
||||
{
|
||||
Ok(_) => {
|
||||
action()?;
|
||||
// extraction succeeded, so only now delete this file
|
||||
let _ignore = std::fs::remove_file(&sync_lock_path);
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
bail!(
|
||||
concat!(
|
||||
"Error creating package sync lock file at '{}'. ",
|
||||
"Maybe try manually deleting this folder.\n\n{:#}",
|
||||
),
|
||||
output_folder.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match inner(output_folder, action) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(err) => {
|
||||
if let Err(remove_err) = fs::remove_dir_all(output_folder) {
|
||||
if remove_err.kind() != std::io::ErrorKind::NotFound {
|
||||
bail!(
|
||||
concat!(
|
||||
"Failed setting up package cache directory for {}, then ",
|
||||
"failed cleaning it up.\n\nOriginal error:\n\n{}\n\n",
|
||||
"Remove error:\n\n{}\n\nPlease manually ",
|
||||
"delete this folder or you will run into issues using this ",
|
||||
"package in the future:\n\n{}"
|
||||
),
|
||||
package,
|
||||
err,
|
||||
remove_err,
|
||||
output_folder.display(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NpmCacheDir {
|
||||
root_dir: PathBuf,
|
||||
// cached url representation of the root directory
|
||||
root_dir_url: Url,
|
||||
}
|
||||
|
||||
impl NpmCacheDir {
|
||||
pub fn new(root_dir: PathBuf) -> Self {
|
||||
fn try_get_canonicalized_root_dir(
|
||||
root_dir: &Path,
|
||||
) -> Result<PathBuf, AnyError> {
|
||||
if !root_dir.exists() {
|
||||
std::fs::create_dir_all(root_dir)
|
||||
.with_context(|| format!("Error creating {}", root_dir.display()))?;
|
||||
}
|
||||
Ok(canonicalize_path(root_dir)?)
|
||||
}
|
||||
|
||||
// this may fail on readonly file systems, so just ignore if so
|
||||
let root_dir =
|
||||
try_get_canonicalized_root_dir(&root_dir).unwrap_or(root_dir);
|
||||
let root_dir_url = Url::from_directory_path(&root_dir).unwrap();
|
||||
Self {
|
||||
root_dir,
|
||||
root_dir_url,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root_dir_url(&self) -> &Url {
|
||||
&self.root_dir_url
|
||||
}
|
||||
|
||||
pub fn package_folder_for_id(
|
||||
&self,
|
||||
folder_id: &NpmPackageCacheFolderId,
|
||||
registry_url: &Url,
|
||||
) -> PathBuf {
|
||||
if folder_id.copy_index == 0 {
|
||||
self.package_folder_for_name_and_version(&folder_id.nv, registry_url)
|
||||
} else {
|
||||
self
|
||||
.package_name_folder(&folder_id.nv.name, registry_url)
|
||||
.join(format!("{}_{}", folder_id.nv.version, folder_id.copy_index))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn package_folder_for_name_and_version(
|
||||
&self,
|
||||
package: &PackageNv,
|
||||
registry_url: &Url,
|
||||
) -> PathBuf {
|
||||
self
|
||||
.package_name_folder(&package.name, registry_url)
|
||||
.join(package.version.to_string())
|
||||
}
|
||||
|
||||
pub fn package_name_folder(&self, name: &str, registry_url: &Url) -> PathBuf {
|
||||
let mut dir = self.registry_folder(registry_url);
|
||||
if name.to_lowercase() != name {
|
||||
let encoded_name = mixed_case_package_name_encode(name);
|
||||
// Using the encoded directory may have a collision with an actual package name
|
||||
// so prefix it with an underscore since npm packages can't start with that
|
||||
dir.join(format!("_{encoded_name}"))
|
||||
} else {
|
||||
// ensure backslashes are used on windows
|
||||
for part in name.split('/') {
|
||||
dir = dir.join(part);
|
||||
}
|
||||
dir
|
||||
}
|
||||
}
|
||||
|
||||
pub fn registry_folder(&self, registry_url: &Url) -> PathBuf {
|
||||
self
|
||||
.root_dir
|
||||
.join(root_url_to_safe_local_dirname(registry_url))
|
||||
}
|
||||
|
||||
pub fn resolve_package_folder_id_from_specifier(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
registry_url: &Url,
|
||||
) -> Option<NpmPackageCacheFolderId> {
|
||||
let registry_root_dir = self
|
||||
.root_dir_url
|
||||
.join(&format!(
|
||||
"{}/",
|
||||
root_url_to_safe_local_dirname(registry_url)
|
||||
.to_string_lossy()
|
||||
.replace('\\', "/")
|
||||
))
|
||||
// this not succeeding indicates a fatal issue, so unwrap
|
||||
.unwrap();
|
||||
let mut relative_url = registry_root_dir.make_relative(specifier)?;
|
||||
if relative_url.starts_with("../") {
|
||||
return None;
|
||||
}
|
||||
|
||||
// base32 decode the url if it starts with an underscore
|
||||
// * Ex. _{base32(package_name)}/
|
||||
if let Some(end_url) = relative_url.strip_prefix('_') {
|
||||
let mut parts = end_url
|
||||
.split('/')
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<Vec<_>>();
|
||||
match mixed_case_package_name_decode(&parts[0]) {
|
||||
Some(part) => {
|
||||
parts[0] = part;
|
||||
}
|
||||
None => return None,
|
||||
}
|
||||
relative_url = parts.join("/");
|
||||
}
|
||||
|
||||
// examples:
|
||||
// * chalk/5.0.1/
|
||||
// * @types/chalk/5.0.1/
|
||||
// * some-package/5.0.1_1/ -- where the `_1` (/_\d+/) is a copy of the folder for peer deps
|
||||
let is_scoped_package = relative_url.starts_with('@');
|
||||
let mut parts = relative_url
|
||||
.split('/')
|
||||
.enumerate()
|
||||
.take(if is_scoped_package { 3 } else { 2 })
|
||||
.map(|(_, part)| part)
|
||||
.collect::<Vec<_>>();
|
||||
if parts.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
let version_part = parts.pop().unwrap();
|
||||
let name = parts.join("/");
|
||||
let (version, copy_index) =
|
||||
if let Some((version, copy_count)) = version_part.split_once('_') {
|
||||
(version, copy_count.parse::<u8>().ok()?)
|
||||
} else {
|
||||
(version_part, 0)
|
||||
};
|
||||
Some(NpmPackageCacheFolderId {
|
||||
nv: PackageNv {
|
||||
name,
|
||||
version: Version::parse_from_npm(version).ok()?,
|
||||
},
|
||||
copy_index,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_cache_location(&self) -> PathBuf {
|
||||
self.root_dir.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores a single copy of npm packages in a cache.
|
||||
#[derive(Debug)]
|
||||
pub struct NpmCache {
|
||||
|
@ -282,10 +56,6 @@ impl NpmCache {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn as_readonly(&self) -> NpmCacheDir {
|
||||
self.cache_dir.clone()
|
||||
}
|
||||
|
||||
pub fn cache_setting(&self) -> &CacheSetting {
|
||||
&self.cache_setting
|
||||
}
|
||||
|
@ -434,103 +204,75 @@ impl NpmCache {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn mixed_case_package_name_encode(name: &str) -> String {
|
||||
// use base32 encoding because it's reversible and the character set
|
||||
// only includes the characters within 0-9 and A-Z so it can be lower cased
|
||||
base32::encode(
|
||||
base32::Alphabet::RFC4648 { padding: false },
|
||||
name.as_bytes(),
|
||||
)
|
||||
.to_lowercase()
|
||||
}
|
||||
const NPM_PACKAGE_SYNC_LOCK_FILENAME: &str = ".deno_sync_lock";
|
||||
|
||||
pub fn mixed_case_package_name_decode(name: &str) -> Option<String> {
|
||||
base32::decode(base32::Alphabet::RFC4648 { padding: false }, name)
|
||||
.and_then(|b| String::from_utf8(b).ok())
|
||||
}
|
||||
pub fn with_folder_sync_lock(
|
||||
package: &PackageNv,
|
||||
output_folder: &Path,
|
||||
action: impl FnOnce() -> Result<(), AnyError>,
|
||||
) -> Result<(), AnyError> {
|
||||
fn inner(
|
||||
output_folder: &Path,
|
||||
action: impl FnOnce() -> Result<(), AnyError>,
|
||||
) -> Result<(), AnyError> {
|
||||
fs::create_dir_all(output_folder).with_context(|| {
|
||||
format!("Error creating '{}'.", output_folder.display())
|
||||
})?;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use deno_core::url::Url;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::Version;
|
||||
// This sync lock file is a way to ensure that partially created
|
||||
// npm package directories aren't considered valid. This could maybe
|
||||
// be a bit smarter in the future to not bother extracting here
|
||||
// if another process has taken the lock in the past X seconds and
|
||||
// wait for the other process to finish (it could try to create the
|
||||
// file with `create_new(true)` then if it exists, check the metadata
|
||||
// then wait until the other process finishes with a timeout), but
|
||||
// for now this is good enough.
|
||||
let sync_lock_path = output_folder.join(NPM_PACKAGE_SYNC_LOCK_FILENAME);
|
||||
match fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&sync_lock_path)
|
||||
{
|
||||
Ok(_) => {
|
||||
action()?;
|
||||
// extraction succeeded, so only now delete this file
|
||||
let _ignore = std::fs::remove_file(&sync_lock_path);
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
bail!(
|
||||
concat!(
|
||||
"Error creating package sync lock file at '{}'. ",
|
||||
"Maybe try manually deleting this folder.\n\n{:#}",
|
||||
),
|
||||
output_folder.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use super::NpmCacheDir;
|
||||
use crate::npm::cache::NpmPackageCacheFolderId;
|
||||
|
||||
#[test]
|
||||
fn should_get_package_folder() {
|
||||
let deno_dir = crate::cache::DenoDir::new(None).unwrap();
|
||||
let root_dir = deno_dir.npm_folder_path();
|
||||
let cache = NpmCacheDir::new(root_dir.clone());
|
||||
let registry_url = Url::parse("https://registry.npmjs.org/").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
cache.package_folder_for_id(
|
||||
&NpmPackageCacheFolderId {
|
||||
nv: PackageNv {
|
||||
name: "json".to_string(),
|
||||
version: Version::parse_from_npm("1.2.5").unwrap(),
|
||||
},
|
||||
copy_index: 0,
|
||||
},
|
||||
®istry_url,
|
||||
),
|
||||
root_dir
|
||||
.join("registry.npmjs.org")
|
||||
.join("json")
|
||||
.join("1.2.5"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cache.package_folder_for_id(
|
||||
&NpmPackageCacheFolderId {
|
||||
nv: PackageNv {
|
||||
name: "json".to_string(),
|
||||
version: Version::parse_from_npm("1.2.5").unwrap(),
|
||||
},
|
||||
copy_index: 1,
|
||||
},
|
||||
®istry_url,
|
||||
),
|
||||
root_dir
|
||||
.join("registry.npmjs.org")
|
||||
.join("json")
|
||||
.join("1.2.5_1"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cache.package_folder_for_id(
|
||||
&NpmPackageCacheFolderId {
|
||||
nv: PackageNv {
|
||||
name: "JSON".to_string(),
|
||||
version: Version::parse_from_npm("2.1.5").unwrap(),
|
||||
},
|
||||
copy_index: 0,
|
||||
},
|
||||
®istry_url,
|
||||
),
|
||||
root_dir
|
||||
.join("registry.npmjs.org")
|
||||
.join("_jjju6tq")
|
||||
.join("2.1.5"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cache.package_folder_for_id(
|
||||
&NpmPackageCacheFolderId {
|
||||
nv: PackageNv {
|
||||
name: "@types/JSON".to_string(),
|
||||
version: Version::parse_from_npm("2.1.5").unwrap(),
|
||||
},
|
||||
copy_index: 0,
|
||||
},
|
||||
®istry_url,
|
||||
),
|
||||
root_dir
|
||||
.join("registry.npmjs.org")
|
||||
.join("_ib2hs4dfomxuuu2pjy")
|
||||
.join("2.1.5"),
|
||||
);
|
||||
match inner(output_folder, action) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(err) => {
|
||||
if let Err(remove_err) = fs::remove_dir_all(output_folder) {
|
||||
if remove_err.kind() != std::io::ErrorKind::NotFound {
|
||||
bail!(
|
||||
concat!(
|
||||
"Failed setting up package cache directory for {}, then ",
|
||||
"failed cleaning it up.\n\nOriginal error:\n\n{}\n\n",
|
||||
"Remove error:\n\n{}\n\nPlease manually ",
|
||||
"delete this folder or you will run into issues using this ",
|
||||
"package in the future:\n\n{}"
|
||||
),
|
||||
package,
|
||||
err,
|
||||
remove_err,
|
||||
output_folder.display(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ use deno_semver::package::PackageReq;
|
|||
use crate::args::PackageJsonDepsProvider;
|
||||
use crate::util::sync::AtomicFlag;
|
||||
|
||||
use super::super::CliNpmRegistryApi;
|
||||
use super::CliNpmRegistryApi;
|
||||
use super::NpmResolution;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -6,6 +6,7 @@ use std::path::PathBuf;
|
|||
use std::sync::Arc;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::serde_json;
|
||||
|
@ -14,7 +15,7 @@ use deno_graph::NpmPackageReqResolution;
|
|||
use deno_npm::registry::NpmRegistryApi;
|
||||
use deno_npm::resolution::NpmResolutionSnapshot;
|
||||
use deno_npm::resolution::PackageReqNotFoundError;
|
||||
use deno_npm::resolution::SerializedNpmResolutionSnapshot;
|
||||
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
|
||||
use deno_npm::NpmPackageId;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
|
@ -27,30 +28,213 @@ use deno_semver::npm::NpmPackageReqReference;
|
|||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageNvReference;
|
||||
use deno_semver::package::PackageReq;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::args::Lockfile;
|
||||
use crate::args::NpmProcessState;
|
||||
use crate::args::PackageJsonDepsProvider;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
|
||||
use self::cache::NpmCache;
|
||||
use self::installer::PackageJsonDepsInstaller;
|
||||
use self::registry::CliNpmRegistryApi;
|
||||
use self::resolution::NpmResolution;
|
||||
use self::resolvers::create_npm_fs_resolver;
|
||||
use self::resolvers::NpmPackageFsResolver;
|
||||
|
||||
use super::CliNpmRegistryApi;
|
||||
use super::CliNpmResolver;
|
||||
use super::InnerCliNpmResolverRef;
|
||||
use super::NpmCacheDir;
|
||||
|
||||
pub use self::installer::PackageJsonDepsInstaller;
|
||||
pub use self::resolution::NpmResolution;
|
||||
pub use self::resolvers::create_npm_fs_resolver;
|
||||
pub use self::resolvers::NpmPackageFsResolver;
|
||||
|
||||
mod cache;
|
||||
mod installer;
|
||||
mod registry;
|
||||
mod resolution;
|
||||
mod resolvers;
|
||||
mod tarball;
|
||||
|
||||
/// State provided to the process via an environment variable.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct NpmProcessState {
|
||||
pub snapshot: SerializedNpmResolutionSnapshot,
|
||||
pub local_node_modules_path: Option<String>,
|
||||
pub enum CliNpmResolverManagedSnapshotOption {
|
||||
ResolveFromLockfile(Arc<Mutex<Lockfile>>),
|
||||
Specified(Option<ValidSerializedNpmResolutionSnapshot>),
|
||||
}
|
||||
|
||||
pub enum CliNpmResolverManagedPackageJsonInstallerOption {
|
||||
ConditionalInstall(Arc<PackageJsonDepsProvider>),
|
||||
NoInstall,
|
||||
}
|
||||
|
||||
pub struct CliNpmResolverManagedCreateOptions {
|
||||
pub snapshot: CliNpmResolverManagedSnapshotOption,
|
||||
pub maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
|
||||
pub fs: Arc<dyn deno_runtime::deno_fs::FileSystem>,
|
||||
pub http_client: Arc<crate::http_util::HttpClient>,
|
||||
pub npm_global_cache_dir: PathBuf,
|
||||
pub cache_setting: crate::args::CacheSetting,
|
||||
pub text_only_progress_bar: crate::util::progress_bar::ProgressBar,
|
||||
pub maybe_node_modules_path: Option<PathBuf>,
|
||||
pub npm_system_info: NpmSystemInfo,
|
||||
pub package_json_installer: CliNpmResolverManagedPackageJsonInstallerOption,
|
||||
pub npm_registry_url: Url,
|
||||
}
|
||||
|
||||
pub async fn create_managed_npm_resolver_for_lsp(
|
||||
options: CliNpmResolverManagedCreateOptions,
|
||||
) -> Arc<dyn CliNpmResolver> {
|
||||
let npm_cache = create_cache(&options);
|
||||
let npm_api = create_api(&options, npm_cache.clone());
|
||||
let snapshot = match resolve_snapshot(&npm_api, options.snapshot).await {
|
||||
Ok(snapshot) => snapshot,
|
||||
Err(err) => {
|
||||
log::warn!("failed to resolve snapshot: {}", err);
|
||||
None
|
||||
}
|
||||
};
|
||||
create_inner(
|
||||
npm_cache,
|
||||
npm_api,
|
||||
snapshot,
|
||||
options.maybe_lockfile,
|
||||
options.fs,
|
||||
options.text_only_progress_bar,
|
||||
options.maybe_node_modules_path,
|
||||
options.package_json_installer,
|
||||
options.npm_registry_url,
|
||||
options.npm_system_info,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn create_managed_npm_resolver(
|
||||
options: CliNpmResolverManagedCreateOptions,
|
||||
) -> Result<Arc<dyn CliNpmResolver>, AnyError> {
|
||||
let npm_cache = create_cache(&options);
|
||||
let npm_api = create_api(&options, npm_cache.clone());
|
||||
let snapshot = resolve_snapshot(&npm_api, options.snapshot).await?;
|
||||
Ok(create_inner(
|
||||
npm_cache,
|
||||
npm_api,
|
||||
snapshot,
|
||||
options.maybe_lockfile,
|
||||
options.fs,
|
||||
options.text_only_progress_bar,
|
||||
options.maybe_node_modules_path,
|
||||
options.package_json_installer,
|
||||
options.npm_registry_url,
|
||||
options.npm_system_info,
|
||||
))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_inner(
|
||||
npm_cache: Arc<NpmCache>,
|
||||
npm_api: Arc<CliNpmRegistryApi>,
|
||||
snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
|
||||
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
|
||||
fs: Arc<dyn deno_runtime::deno_fs::FileSystem>,
|
||||
text_only_progress_bar: crate::util::progress_bar::ProgressBar,
|
||||
node_modules_dir_path: Option<PathBuf>,
|
||||
package_json_installer: CliNpmResolverManagedPackageJsonInstallerOption,
|
||||
npm_registry_url: Url,
|
||||
npm_system_info: NpmSystemInfo,
|
||||
) -> Arc<dyn CliNpmResolver> {
|
||||
let resolution = Arc::new(NpmResolution::from_serialized(
|
||||
npm_api.clone(),
|
||||
snapshot,
|
||||
maybe_lockfile.clone(),
|
||||
));
|
||||
let npm_fs_resolver = create_npm_fs_resolver(
|
||||
fs.clone(),
|
||||
npm_cache.clone(),
|
||||
&text_only_progress_bar,
|
||||
npm_registry_url,
|
||||
resolution.clone(),
|
||||
node_modules_dir_path,
|
||||
npm_system_info.clone(),
|
||||
);
|
||||
let package_json_deps_installer = match package_json_installer {
|
||||
CliNpmResolverManagedPackageJsonInstallerOption::ConditionalInstall(
|
||||
provider,
|
||||
) => Arc::new(PackageJsonDepsInstaller::new(
|
||||
provider,
|
||||
npm_api.clone(),
|
||||
resolution.clone(),
|
||||
)),
|
||||
CliNpmResolverManagedPackageJsonInstallerOption::NoInstall => {
|
||||
Arc::new(PackageJsonDepsInstaller::no_op())
|
||||
}
|
||||
};
|
||||
Arc::new(ManagedCliNpmResolver::new(
|
||||
npm_api,
|
||||
fs,
|
||||
resolution,
|
||||
npm_fs_resolver,
|
||||
npm_cache,
|
||||
maybe_lockfile,
|
||||
package_json_deps_installer,
|
||||
text_only_progress_bar,
|
||||
npm_system_info,
|
||||
))
|
||||
}
|
||||
|
||||
fn create_cache(options: &CliNpmResolverManagedCreateOptions) -> Arc<NpmCache> {
|
||||
Arc::new(NpmCache::new(
|
||||
NpmCacheDir::new(options.npm_global_cache_dir.clone()),
|
||||
options.cache_setting.clone(),
|
||||
options.fs.clone(),
|
||||
options.http_client.clone(),
|
||||
options.text_only_progress_bar.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn create_api(
|
||||
options: &CliNpmResolverManagedCreateOptions,
|
||||
npm_cache: Arc<NpmCache>,
|
||||
) -> Arc<CliNpmRegistryApi> {
|
||||
Arc::new(CliNpmRegistryApi::new(
|
||||
options.npm_registry_url.clone(),
|
||||
npm_cache.clone(),
|
||||
options.http_client.clone(),
|
||||
options.text_only_progress_bar.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn resolve_snapshot(
|
||||
api: &CliNpmRegistryApi,
|
||||
snapshot: CliNpmResolverManagedSnapshotOption,
|
||||
) -> Result<Option<ValidSerializedNpmResolutionSnapshot>, AnyError> {
|
||||
match snapshot {
|
||||
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(lockfile) => {
|
||||
if !lockfile.lock().overwrite {
|
||||
let snapshot = snapshot_from_lockfile(lockfile.clone(), api)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed reading lockfile '{}'",
|
||||
lockfile.lock().filename.display()
|
||||
)
|
||||
})?;
|
||||
// clear the memory cache to reduce memory usage
|
||||
api.clear_memory_cache();
|
||||
Ok(Some(snapshot))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
CliNpmResolverManagedSnapshotOption::Specified(snapshot) => Ok(snapshot),
|
||||
}
|
||||
}
|
||||
|
||||
async fn snapshot_from_lockfile(
|
||||
lockfile: Arc<Mutex<Lockfile>>,
|
||||
api: &dyn NpmRegistryApi,
|
||||
) -> Result<ValidSerializedNpmResolutionSnapshot, AnyError> {
|
||||
let incomplete_snapshot = {
|
||||
let lock = lockfile.lock();
|
||||
deno_npm::resolution::incomplete_snapshot_from_lockfile(&lock)?
|
||||
};
|
||||
let snapshot =
|
||||
deno_npm::resolution::snapshot_from_lockfile(incomplete_snapshot, api)
|
||||
.await?;
|
||||
Ok(snapshot)
|
||||
}
|
||||
|
||||
/// An npm resolver where the resolution is managed by Deno rather than
|
||||
|
@ -59,40 +243,45 @@ pub struct ManagedCliNpmResolver {
|
|||
api: Arc<CliNpmRegistryApi>,
|
||||
fs: Arc<dyn FileSystem>,
|
||||
fs_resolver: Arc<dyn NpmPackageFsResolver>,
|
||||
global_npm_cache: Arc<NpmCache>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
|
||||
npm_system_info: NpmSystemInfo,
|
||||
progress_bar: ProgressBar,
|
||||
package_json_deps_installer: Arc<PackageJsonDepsInstaller>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ManagedCliNpmResolver {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ManagedNpmResolver")
|
||||
.field("api", &"<omitted>")
|
||||
.field("fs", &"<omitted>")
|
||||
.field("fs_resolver", &"<omitted>")
|
||||
.field("resolution", &"<omitted>")
|
||||
.field("maybe_lockfile", &"<omitted>")
|
||||
.field("package_json_deps_installer", &"<omitted>")
|
||||
.field("<omitted>", &"<omitted>")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl ManagedCliNpmResolver {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
api: Arc<CliNpmRegistryApi>,
|
||||
fs: Arc<dyn FileSystem>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
fs_resolver: Arc<dyn NpmPackageFsResolver>,
|
||||
global_npm_cache: Arc<NpmCache>,
|
||||
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
|
||||
package_json_deps_installer: Arc<PackageJsonDepsInstaller>,
|
||||
progress_bar: ProgressBar,
|
||||
npm_system_info: NpmSystemInfo,
|
||||
) -> Self {
|
||||
Self {
|
||||
api,
|
||||
fs,
|
||||
fs_resolver,
|
||||
global_npm_cache,
|
||||
resolution,
|
||||
maybe_lockfile,
|
||||
package_json_deps_installer,
|
||||
progress_bar,
|
||||
npm_system_info,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,6 +380,15 @@ impl ManagedCliNpmResolver {
|
|||
self.resolution.snapshot()
|
||||
}
|
||||
|
||||
pub fn serialized_valid_snapshot_for_system(
|
||||
&self,
|
||||
system_info: &NpmSystemInfo,
|
||||
) -> ValidSerializedNpmResolutionSnapshot {
|
||||
self
|
||||
.resolution
|
||||
.serialized_valid_snapshot_for_system(system_info)
|
||||
}
|
||||
|
||||
pub fn lock(&self, lockfile: &mut Lockfile) -> Result<(), AnyError> {
|
||||
self.resolution.lock(lockfile)
|
||||
}
|
||||
|
@ -208,8 +406,11 @@ impl ManagedCliNpmResolver {
|
|||
|
||||
pub async fn resolve_pending(&self) -> Result<(), AnyError> {
|
||||
self.resolution.resolve_pending().await?;
|
||||
self.fs_resolver.cache_packages().await?;
|
||||
Ok(())
|
||||
self.cache_packages().await
|
||||
}
|
||||
|
||||
pub async fn cache_packages(&self) -> Result<(), AnyError> {
|
||||
self.fs_resolver.cache_packages().await
|
||||
}
|
||||
|
||||
fn resolve_pkg_id_from_pkg_req(
|
||||
|
@ -240,6 +441,17 @@ impl ManagedCliNpmResolver {
|
|||
.map(|_| ())
|
||||
.map_err(|err| err.into())
|
||||
}
|
||||
|
||||
pub fn registry_base_url(&self) -> &ModuleSpecifier {
|
||||
self.api.base_url()
|
||||
}
|
||||
|
||||
pub fn registry_folder_in_global_cache(
|
||||
&self,
|
||||
registry_url: &ModuleSpecifier,
|
||||
) -> PathBuf {
|
||||
self.global_npm_cache.registry_folder(registry_url)
|
||||
}
|
||||
}
|
||||
|
||||
impl NpmResolver for ManagedCliNpmResolver {
|
||||
|
@ -283,6 +495,35 @@ impl CliNpmResolver for ManagedCliNpmResolver {
|
|||
self
|
||||
}
|
||||
|
||||
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver> {
|
||||
// create a new snapshotted npm resolution and resolver
|
||||
let npm_resolution = Arc::new(NpmResolution::new(
|
||||
self.api.clone(),
|
||||
self.resolution.snapshot(),
|
||||
self.maybe_lockfile.clone(),
|
||||
));
|
||||
|
||||
Arc::new(ManagedCliNpmResolver::new(
|
||||
self.api.clone(),
|
||||
self.fs.clone(),
|
||||
npm_resolution.clone(),
|
||||
create_npm_fs_resolver(
|
||||
self.fs.clone(),
|
||||
self.global_npm_cache.clone(),
|
||||
&self.progress_bar,
|
||||
self.api.base_url().clone(),
|
||||
npm_resolution,
|
||||
self.node_modules_path(),
|
||||
self.npm_system_info.clone(),
|
||||
),
|
||||
self.global_npm_cache.clone(),
|
||||
self.maybe_lockfile.clone(),
|
||||
self.package_json_deps_installer.clone(),
|
||||
self.progress_bar.clone(),
|
||||
self.npm_system_info.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn root_dir_url(&self) -> &Url {
|
||||
self.fs_resolver.root_dir_url()
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ use deno_core::url::Url;
|
|||
use deno_npm::registry::NpmPackageInfo;
|
||||
use deno_npm::registry::NpmRegistryApi;
|
||||
use deno_npm::registry::NpmRegistryPackageInfoLoadError;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::args::CacheSetting;
|
||||
use crate::cache::CACHE_PERM;
|
||||
|
@ -32,32 +31,10 @@ use crate::util::sync::AtomicFlag;
|
|||
|
||||
use super::cache::NpmCache;
|
||||
|
||||
static NPM_REGISTRY_DEFAULT_URL: Lazy<Url> = Lazy::new(|| {
|
||||
let env_var_name = "NPM_CONFIG_REGISTRY";
|
||||
if let Ok(registry_url) = std::env::var(env_var_name) {
|
||||
// ensure there is a trailing slash for the directory
|
||||
let registry_url = format!("{}/", registry_url.trim_end_matches('/'));
|
||||
match Url::parse(®istry_url) {
|
||||
Ok(url) => {
|
||||
return url;
|
||||
}
|
||||
Err(err) => {
|
||||
log::debug!("Invalid {} environment variable: {:#}", env_var_name, err,);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Url::parse("https://registry.npmjs.org").unwrap()
|
||||
});
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CliNpmRegistryApi(Option<Arc<CliNpmRegistryApiInner>>);
|
||||
|
||||
impl CliNpmRegistryApi {
|
||||
pub fn default_url() -> &'static Url {
|
||||
&NPM_REGISTRY_DEFAULT_URL
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
base_url: Url,
|
||||
cache: Arc<NpmCache>,
|
|
@ -34,7 +34,7 @@ use deno_semver::VersionReq;
|
|||
use crate::args::Lockfile;
|
||||
use crate::util::sync::TaskQueue;
|
||||
|
||||
use super::super::registry::CliNpmRegistryApi;
|
||||
use super::CliNpmRegistryApi;
|
||||
|
||||
/// Handles updating and storing npm resolution in memory where the underlying
|
||||
/// snapshot can be updated concurrently. Additionally handles updating the lockfile
|
||||
|
@ -221,8 +221,6 @@ impl NpmResolution {
|
|||
.map(|pkg| pkg.id.clone())
|
||||
}
|
||||
|
||||
// todo: NEXT
|
||||
|
||||
/// Resolves a package requirement for deno graph. This should only be
|
||||
/// called by deno_graph's NpmResolver or for resolving packages in
|
||||
/// a package.json
|
||||
|
@ -275,14 +273,6 @@ impl NpmResolution {
|
|||
.all_system_packages_partitioned(system_info)
|
||||
}
|
||||
|
||||
// todo: NEXT
|
||||
|
||||
pub fn has_packages(&self) -> bool {
|
||||
!self.snapshot.read().is_empty()
|
||||
}
|
||||
|
||||
// todo: NEXT
|
||||
|
||||
pub fn snapshot(&self) -> NpmResolutionSnapshot {
|
||||
self.snapshot.read().clone()
|
||||
}
|
||||
|
@ -293,8 +283,6 @@ impl NpmResolution {
|
|||
self.snapshot.read().as_valid_serialized()
|
||||
}
|
||||
|
||||
// todo: NEXT
|
||||
|
||||
pub fn serialized_valid_snapshot_for_system(
|
||||
&self,
|
||||
system_info: &NpmSystemInfo,
|
||||
|
|
|
@ -20,7 +20,7 @@ use deno_runtime::deno_fs::FileSystem;
|
|||
use deno_runtime::deno_node::NodePermissions;
|
||||
use deno_runtime::deno_node::NodeResolutionMode;
|
||||
|
||||
use crate::npm::NpmCache;
|
||||
use super::super::cache::NpmCache;
|
||||
|
||||
/// Part of the resolution that interacts with the file system.
|
||||
#[async_trait]
|
||||
|
|
|
@ -20,8 +20,7 @@ use deno_runtime::deno_fs::FileSystem;
|
|||
use deno_runtime::deno_node::NodePermissions;
|
||||
use deno_runtime::deno_node::NodeResolutionMode;
|
||||
|
||||
use crate::npm::NpmCache;
|
||||
|
||||
use super::super::cache::NpmCache;
|
||||
use super::super::resolution::NpmResolution;
|
||||
use super::common::cache_packages;
|
||||
use super::common::types_package_name;
|
||||
|
|
|
@ -12,7 +12,7 @@ use std::path::PathBuf;
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::cache::CACHE_PERM;
|
||||
use crate::npm::cache::mixed_case_package_name_decode;
|
||||
use crate::npm::cache_dir::mixed_case_package_name_decode;
|
||||
use crate::util::fs::atomic_write_file;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
|
||||
use crate::util::fs::symlink_dir;
|
||||
|
@ -41,11 +41,11 @@ use deno_semver::package::PackageNv;
|
|||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::npm::cache::mixed_case_package_name_encode;
|
||||
use crate::npm::NpmCache;
|
||||
use crate::npm::cache_dir::mixed_case_package_name_encode;
|
||||
use crate::util::fs::copy_dir_recursive;
|
||||
use crate::util::fs::hard_link_dir_recursive;
|
||||
|
||||
use super::super::cache::NpmCache;
|
||||
use super::super::resolution::NpmResolution;
|
||||
use super::common::types_package_name;
|
||||
use super::common::NpmPackageFsResolver;
|
||||
|
|
|
@ -11,14 +11,15 @@ use deno_core::url::Url;
|
|||
use deno_npm::NpmSystemInfo;
|
||||
use deno_runtime::deno_fs::FileSystem;
|
||||
|
||||
use crate::npm::NpmCache;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
|
||||
pub use self::common::NpmPackageFsResolver;
|
||||
|
||||
use self::global::GlobalNpmPackageResolver;
|
||||
use self::local::LocalNpmPackageResolver;
|
||||
|
||||
use super::NpmResolution;
|
||||
use super::cache::NpmCache;
|
||||
use super::resolution::NpmResolution;
|
||||
|
||||
pub fn create_npm_fs_resolver(
|
||||
fs: Arc<dyn FileSystem>,
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
mod cache_dir;
|
||||
mod managed;
|
||||
|
||||
// todo(#18967): move the cache, registry, and tarball into the managed folder
|
||||
mod cache;
|
||||
mod registry;
|
||||
mod tarball;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
@ -17,20 +13,45 @@ use deno_core::url::Url;
|
|||
use deno_graph::NpmPackageReqResolution;
|
||||
use deno_npm::resolution::PackageReqNotFoundError;
|
||||
use deno_runtime::deno_node::NpmResolver;
|
||||
|
||||
pub use cache::NpmCache;
|
||||
pub use cache::NpmCacheDir;
|
||||
use deno_semver::npm::NpmPackageNvReference;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
pub use managed::create_npm_fs_resolver;
|
||||
pub use managed::ManagedCliNpmResolver;
|
||||
pub use managed::NpmPackageFsResolver;
|
||||
pub use managed::NpmProcessState;
|
||||
pub use managed::NpmResolution;
|
||||
pub use managed::PackageJsonDepsInstaller;
|
||||
pub use registry::CliNpmRegistryApi;
|
||||
|
||||
pub use self::cache_dir::NpmCacheDir;
|
||||
pub use self::managed::CliNpmResolverManagedCreateOptions;
|
||||
pub use self::managed::CliNpmResolverManagedPackageJsonInstallerOption;
|
||||
pub use self::managed::CliNpmResolverManagedSnapshotOption;
|
||||
pub use self::managed::ManagedCliNpmResolver;
|
||||
|
||||
pub enum CliNpmResolverCreateOptions {
|
||||
Managed(CliNpmResolverManagedCreateOptions),
|
||||
// todo(dsherret): implement this
|
||||
#[allow(dead_code)]
|
||||
Byonm,
|
||||
}
|
||||
|
||||
pub async fn create_cli_npm_resolver_for_lsp(
|
||||
options: CliNpmResolverCreateOptions,
|
||||
) -> Arc<dyn CliNpmResolver> {
|
||||
use CliNpmResolverCreateOptions::*;
|
||||
match options {
|
||||
Managed(options) => {
|
||||
managed::create_managed_npm_resolver_for_lsp(options).await
|
||||
}
|
||||
Byonm => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_cli_npm_resolver(
|
||||
options: CliNpmResolverCreateOptions,
|
||||
) -> Result<Arc<dyn CliNpmResolver>, AnyError> {
|
||||
use CliNpmResolverCreateOptions::*;
|
||||
match options {
|
||||
Managed(options) => managed::create_managed_npm_resolver(options).await,
|
||||
Byonm => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub enum InnerCliNpmResolverRef<'a> {
|
||||
Managed(&'a ManagedCliNpmResolver),
|
||||
|
@ -41,6 +62,8 @@ pub enum InnerCliNpmResolverRef<'a> {
|
|||
pub trait CliNpmResolver: NpmResolver {
|
||||
fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver>;
|
||||
|
||||
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver>;
|
||||
|
||||
fn root_dir_url(&self) -> &Url;
|
||||
|
||||
fn as_inner(&self) -> InnerCliNpmResolverRef;
|
||||
|
|
|
@ -36,11 +36,8 @@ use crate::args::PackageJsonDepsProvider;
|
|||
use crate::cache::DenoDir;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::http_util::HttpClient;
|
||||
use crate::npm::CliNpmRegistryApi;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::InnerCliNpmResolverRef;
|
||||
use crate::npm::NpmCache;
|
||||
use crate::npm::NpmResolution;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
use crate::util::progress_bar::ProgressBarStyle;
|
||||
|
||||
|
@ -342,9 +339,6 @@ pub struct DenoCompileBinaryWriter<'a> {
|
|||
file_fetcher: &'a FileFetcher,
|
||||
client: &'a HttpClient,
|
||||
deno_dir: &'a DenoDir,
|
||||
npm_api: &'a CliNpmRegistryApi,
|
||||
npm_cache: &'a NpmCache,
|
||||
npm_resolution: &'a NpmResolution,
|
||||
npm_resolver: &'a dyn CliNpmResolver,
|
||||
npm_system_info: NpmSystemInfo,
|
||||
package_json_deps_provider: &'a PackageJsonDepsProvider,
|
||||
|
@ -356,9 +350,6 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
file_fetcher: &'a FileFetcher,
|
||||
client: &'a HttpClient,
|
||||
deno_dir: &'a DenoDir,
|
||||
npm_api: &'a CliNpmRegistryApi,
|
||||
npm_cache: &'a NpmCache,
|
||||
npm_resolution: &'a NpmResolution,
|
||||
npm_resolver: &'a dyn CliNpmResolver,
|
||||
npm_system_info: NpmSystemInfo,
|
||||
package_json_deps_provider: &'a PackageJsonDepsProvider,
|
||||
|
@ -367,11 +358,8 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
file_fetcher,
|
||||
client,
|
||||
deno_dir,
|
||||
npm_api,
|
||||
npm_cache,
|
||||
npm_resolver,
|
||||
npm_system_info,
|
||||
npm_resolution,
|
||||
package_json_deps_provider,
|
||||
}
|
||||
}
|
||||
|
@ -502,15 +490,22 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
.resolve_import_map(self.file_fetcher)
|
||||
.await?
|
||||
.map(|import_map| (import_map.base_url().clone(), import_map.to_json()));
|
||||
let (npm_vfs, npm_files) = if self.npm_resolution.has_packages() {
|
||||
let (root_dir, files) = self.build_vfs()?.into_dir_and_files();
|
||||
let snapshot = self
|
||||
.npm_resolution
|
||||
.serialized_valid_snapshot_for_system(&self.npm_system_info);
|
||||
eszip.add_npm_snapshot(snapshot);
|
||||
(Some(root_dir), files)
|
||||
} else {
|
||||
(None, Vec::new())
|
||||
let (npm_vfs, npm_files) = match self.npm_resolver.as_inner() {
|
||||
InnerCliNpmResolverRef::Managed(managed) => {
|
||||
let snapshot =
|
||||
managed.serialized_valid_snapshot_for_system(&self.npm_system_info);
|
||||
if !snapshot.as_serialized().packages.is_empty() {
|
||||
let (root_dir, files) = self.build_vfs()?.into_dir_and_files();
|
||||
eszip.add_npm_snapshot(snapshot);
|
||||
(Some(root_dir), files)
|
||||
} else {
|
||||
(None, Vec::new())
|
||||
}
|
||||
}
|
||||
InnerCliNpmResolverRef::Byonm(_) => {
|
||||
let (root_dir, files) = self.build_vfs()?.into_dir_and_files();
|
||||
(Some(root_dir), files)
|
||||
}
|
||||
};
|
||||
|
||||
let metadata = Metadata {
|
||||
|
@ -555,8 +550,9 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
} else {
|
||||
// DO NOT include the user's registry url as it may contain credentials,
|
||||
// but also don't make this dependent on the registry url
|
||||
let registry_url = self.npm_api.base_url();
|
||||
let root_path = self.npm_cache.registry_folder(registry_url);
|
||||
let registry_url = npm_resolver.registry_base_url();
|
||||
let root_path =
|
||||
npm_resolver.registry_folder_in_global_cache(registry_url);
|
||||
let mut builder = VfsBuilder::new(root_path)?;
|
||||
for package in npm_resolver.all_system_packages(&self.npm_system_info)
|
||||
{
|
||||
|
|
|
@ -14,14 +14,12 @@ use crate::http_util::HttpClient;
|
|||
use crate::module_loader::CjsResolutionStore;
|
||||
use crate::module_loader::NpmModuleLoader;
|
||||
use crate::node::CliCjsCodeAnalyzer;
|
||||
use crate::npm::create_npm_fs_resolver;
|
||||
use crate::npm::CliNpmRegistryApi;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::ManagedCliNpmResolver;
|
||||
use crate::npm::NpmCache;
|
||||
use crate::npm::create_cli_npm_resolver;
|
||||
use crate::npm::CliNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedPackageJsonInstallerOption;
|
||||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||
use crate::npm::NpmCacheDir;
|
||||
use crate::npm::NpmResolution;
|
||||
use crate::npm::PackageJsonDepsInstaller;
|
||||
use crate::resolver::MappedSpecifierResolver;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
use crate::util::progress_bar::ProgressBarStyle;
|
||||
|
@ -40,7 +38,6 @@ use deno_core::ModuleLoader;
|
|||
use deno_core::ModuleSpecifier;
|
||||
use deno_core::ModuleType;
|
||||
use deno_core::ResolutionKind;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node::analyze::NodeCodeTranslator;
|
||||
use deno_runtime::deno_node::NodeResolver;
|
||||
|
@ -309,82 +306,62 @@ pub async fn run(
|
|||
.join(format!("deno-compile-{}", current_exe_name))
|
||||
.join("node_modules");
|
||||
let npm_cache_dir = NpmCacheDir::new(root_path.clone());
|
||||
let (fs, vfs_root, node_modules_path, snapshot) = if let Some(snapshot) =
|
||||
eszip.take_npm_snapshot()
|
||||
{
|
||||
let vfs_root_dir_path = if metadata.node_modules_dir {
|
||||
root_path
|
||||
let npm_global_cache_dir = npm_cache_dir.get_cache_location();
|
||||
let (fs, vfs_root, maybe_node_modules_path, maybe_snapshot) =
|
||||
if let Some(snapshot) = eszip.take_npm_snapshot() {
|
||||
let vfs_root_dir_path = if metadata.node_modules_dir {
|
||||
root_path
|
||||
} else {
|
||||
npm_cache_dir.registry_folder(&npm_registry_url)
|
||||
};
|
||||
let vfs = load_npm_vfs(vfs_root_dir_path.clone())
|
||||
.context("Failed to load npm vfs.")?;
|
||||
let node_modules_path = if metadata.node_modules_dir {
|
||||
Some(vfs.root().to_path_buf())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
(
|
||||
Arc::new(DenoCompileFileSystem::new(vfs))
|
||||
as Arc<dyn deno_fs::FileSystem>,
|
||||
Some(vfs_root_dir_path),
|
||||
node_modules_path,
|
||||
Some(snapshot),
|
||||
)
|
||||
} else {
|
||||
npm_cache_dir.registry_folder(&npm_registry_url)
|
||||
(
|
||||
Arc::new(deno_fs::RealFs) as Arc<dyn deno_fs::FileSystem>,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
};
|
||||
let vfs = load_npm_vfs(vfs_root_dir_path.clone())
|
||||
.context("Failed to load npm vfs.")?;
|
||||
let node_modules_path = if metadata.node_modules_dir {
|
||||
Some(vfs.root().to_path_buf())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
(
|
||||
Arc::new(DenoCompileFileSystem::new(vfs)) as Arc<dyn deno_fs::FileSystem>,
|
||||
Some(vfs_root_dir_path),
|
||||
node_modules_path,
|
||||
Some(snapshot),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
Arc::new(deno_fs::RealFs) as Arc<dyn deno_fs::FileSystem>,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
};
|
||||
|
||||
let npm_cache = Arc::new(NpmCache::new(
|
||||
npm_cache_dir,
|
||||
CacheSetting::Only,
|
||||
fs.clone(),
|
||||
http_client.clone(),
|
||||
progress_bar.clone(),
|
||||
));
|
||||
let npm_api = Arc::new(CliNpmRegistryApi::new(
|
||||
npm_registry_url.clone(),
|
||||
npm_cache.clone(),
|
||||
http_client.clone(),
|
||||
progress_bar.clone(),
|
||||
));
|
||||
let npm_resolution = Arc::new(NpmResolution::from_serialized(
|
||||
npm_api.clone(),
|
||||
snapshot,
|
||||
None,
|
||||
));
|
||||
let has_node_modules_dir = node_modules_path.is_some();
|
||||
let npm_fs_resolver = create_npm_fs_resolver(
|
||||
fs.clone(),
|
||||
npm_cache,
|
||||
&progress_bar,
|
||||
npm_registry_url,
|
||||
npm_resolution.clone(),
|
||||
node_modules_path,
|
||||
NpmSystemInfo::default(),
|
||||
);
|
||||
let has_node_modules_dir = maybe_node_modules_path.is_some();
|
||||
let package_json_deps_provider = Arc::new(PackageJsonDepsProvider::new(
|
||||
metadata
|
||||
.package_json_deps
|
||||
.map(|serialized| serialized.into_deps()),
|
||||
));
|
||||
let package_json_installer = Arc::new(PackageJsonDepsInstaller::new(
|
||||
package_json_deps_provider.clone(),
|
||||
npm_api.clone(),
|
||||
npm_resolution.clone(),
|
||||
));
|
||||
let npm_resolver = Arc::new(ManagedCliNpmResolver::new(
|
||||
npm_api.clone(),
|
||||
fs.clone(),
|
||||
npm_resolution.clone(),
|
||||
npm_fs_resolver,
|
||||
None,
|
||||
package_json_installer,
|
||||
)) as Arc<dyn CliNpmResolver>;
|
||||
let npm_resolver = create_cli_npm_resolver(
|
||||
CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions {
|
||||
snapshot: CliNpmResolverManagedSnapshotOption::Specified(maybe_snapshot),
|
||||
maybe_lockfile: None,
|
||||
fs: fs.clone(),
|
||||
http_client: http_client.clone(),
|
||||
npm_global_cache_dir,
|
||||
cache_setting: CacheSetting::Only,
|
||||
text_only_progress_bar: progress_bar,
|
||||
maybe_node_modules_path,
|
||||
package_json_installer:
|
||||
CliNpmResolverManagedPackageJsonInstallerOption::ConditionalInstall(
|
||||
package_json_deps_provider.clone(),
|
||||
),
|
||||
npm_registry_url,
|
||||
npm_system_info: Default::default(),
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
let node_resolver = Arc::new(NodeResolver::new(
|
||||
fs.clone(),
|
||||
npm_resolver.clone().into_npm_resolver(),
|
||||
|
|
|
@ -98,7 +98,7 @@ fn print_cache_info(
|
|||
let dir = factory.deno_dir()?;
|
||||
#[allow(deprecated)]
|
||||
let modules_cache = factory.global_http_cache()?.get_global_cache_location();
|
||||
let npm_cache = factory.npm_cache()?.as_readonly().get_cache_location();
|
||||
let npm_cache = factory.deno_dir()?.npm_folder_path();
|
||||
let typescript_cache = &dir.gen_cache.location;
|
||||
let registry_cache = dir.registries_folder_path();
|
||||
let mut origin_dir = dir.origin_data_folder_path();
|
||||
|
|
|
@ -86,11 +86,7 @@ pub async fn execute_script(
|
|||
|
||||
// install the npm packages if we're using a managed resolver
|
||||
if let Some(npm_resolver) = npm_resolver.as_managed() {
|
||||
let package_json_deps_installer =
|
||||
factory.package_json_deps_installer().await?;
|
||||
package_json_deps_installer
|
||||
.ensure_top_level_install()
|
||||
.await?;
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
npm_resolver.resolve_pending().await?;
|
||||
}
|
||||
|
||||
|
|
11
cli/tools/vendor/mod.rs
vendored
11
cli/tools/vendor/mod.rs
vendored
|
@ -107,11 +107,12 @@ pub async fn vendor(
|
|||
.map(|config_path| config_path.parent().unwrap().join("node_modules"))
|
||||
});
|
||||
if let Some(node_modules_path) = node_modules_path {
|
||||
factory
|
||||
.create_node_modules_npm_fs_resolver(node_modules_path)
|
||||
.await?
|
||||
.cache_packages()
|
||||
.await?;
|
||||
let cli_options =
|
||||
cli_options.with_node_modules_dir_path(node_modules_path);
|
||||
let factory = CliFactory::from_cli_options(Arc::new(cli_options));
|
||||
if let Some(managed) = factory.npm_resolver().await?.as_managed() {
|
||||
managed.cache_packages().await?;
|
||||
}
|
||||
}
|
||||
log::info!(
|
||||
concat!(
|
||||
|
|
Loading…
Reference in a new issue