1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-19 12:16:17 -05:00

refactor: create NpmInstaller (#27626)

This separates npm resolution code from npm installation (more work
towards moving resolution code out of the CLI and cleaning up this
code).
This commit is contained in:
David Sherret 2025-01-13 17:35:18 -05:00 committed by GitHub
parent 5a39f2f096
commit 9dbb99a83c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 1367 additions and 1141 deletions

4
Cargo.lock generated
View file

@ -2074,9 +2074,9 @@ dependencies = [
[[package]] [[package]]
name = "deno_npm" name = "deno_npm"
version = "0.27.0" version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f818ad5dc4c206b50b5cfa6f10b4b94b127e15c8342c152768eba40c225ca23" checksum = "4adceb4c34f10e837d0e3ae76e88dddefb13e83c05c1ef1699fa5519241c9d27"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"capacity_builder 0.5.0", "capacity_builder 0.5.0",

View file

@ -54,7 +54,7 @@ deno_bench_util = { version = "0.179.0", path = "./bench_util" }
deno_config = { version = "=0.43.0", features = ["workspace", "sync"] } deno_config = { version = "=0.43.0", features = ["workspace", "sync"] }
deno_lockfile = "=0.24.0" deno_lockfile = "=0.24.0"
deno_media_type = { version = "0.2.3", features = ["module_specifier"] } deno_media_type = { version = "0.2.3", features = ["module_specifier"] }
deno_npm = "=0.27.0" deno_npm = "=0.27.2"
deno_path_util = "=0.3.0" deno_path_util = "=0.3.0"
deno_permissions = { version = "0.44.0", path = "./runtime/permissions" } deno_permissions = { version = "0.44.0", path = "./runtime/permissions" }
deno_runtime = { version = "0.193.0", path = "./runtime" } deno_runtime = { version = "0.193.0", path = "./runtime" }

View file

@ -11,8 +11,10 @@ use deno_core::error::AnyError;
use deno_core::futures::FutureExt; use deno_core::futures::FutureExt;
use deno_core::FeatureChecker; use deno_core::FeatureChecker;
use deno_error::JsErrorBox; use deno_error::JsErrorBox;
use deno_npm_cache::NpmCacheSetting;
use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::cjs::IsCjsResolutionMode;
use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions;
use deno_resolver::npm::managed::NpmResolutionCell;
use deno_resolver::npm::CreateInNpmPkgCheckerOptions; use deno_resolver::npm::CreateInNpmPkgCheckerOptions;
use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::npm::NpmReqResolverOptions;
use deno_resolver::sloppy_imports::SloppyImportsCachedFs; use deno_resolver::sloppy_imports::SloppyImportsCachedFs;
@ -67,19 +69,27 @@ use crate::node::CliNodeCodeTranslator;
use crate::node::CliNodeResolver; use crate::node::CliNodeResolver;
use crate::node::CliPackageJsonResolver; use crate::node::CliPackageJsonResolver;
use crate::npm::create_cli_npm_resolver; use crate::npm::create_cli_npm_resolver;
use crate::npm::installer::NpmInstaller;
use crate::npm::installer::NpmResolutionInstaller;
use crate::npm::CliByonmNpmResolverCreateOptions; use crate::npm::CliByonmNpmResolverCreateOptions;
use crate::npm::CliManagedNpmResolverCreateOptions; use crate::npm::CliManagedNpmResolverCreateOptions;
use crate::npm::CliNpmCache;
use crate::npm::CliNpmCacheHttpClient;
use crate::npm::CliNpmRegistryInfoProvider;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverCreateOptions;
use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CliNpmResolverManagedSnapshotOption;
use crate::npm::CliNpmTarballCache;
use crate::npm::NpmRegistryReadPermissionChecker; use crate::npm::NpmRegistryReadPermissionChecker;
use crate::npm::NpmRegistryReadPermissionCheckerMode; use crate::npm::NpmRegistryReadPermissionCheckerMode;
use crate::npm::NpmResolutionInitializer;
use crate::resolver::CjsTracker; use crate::resolver::CjsTracker;
use crate::resolver::CliDenoResolver; use crate::resolver::CliDenoResolver;
use crate::resolver::CliNpmGraphResolver;
use crate::resolver::CliNpmReqResolver; use crate::resolver::CliNpmReqResolver;
use crate::resolver::CliResolver; use crate::resolver::CliResolver;
use crate::resolver::CliResolverOptions;
use crate::resolver::CliSloppyImportsResolver; use crate::resolver::CliSloppyImportsResolver;
use crate::resolver::FoundPackageJsonDepFlag;
use crate::resolver::NpmModuleLoader; use crate::resolver::NpmModuleLoader;
use crate::standalone::binary::DenoCompileBinaryWriter; use crate::standalone::binary::DenoCompileBinaryWriter;
use crate::sys::CliSys; use crate::sys::CliSys;
@ -188,6 +198,7 @@ struct CliFactoryServices {
emitter: Deferred<Arc<Emitter>>, emitter: Deferred<Arc<Emitter>>,
feature_checker: Deferred<Arc<FeatureChecker>>, feature_checker: Deferred<Arc<FeatureChecker>>,
file_fetcher: Deferred<Arc<CliFileFetcher>>, file_fetcher: Deferred<Arc<CliFileFetcher>>,
found_pkg_json_dep_flag: Arc<FoundPackageJsonDepFlag>,
fs: Deferred<Arc<dyn deno_fs::FileSystem>>, fs: Deferred<Arc<dyn deno_fs::FileSystem>>,
global_http_cache: Deferred<Arc<GlobalHttpCache>>, global_http_cache: Deferred<Arc<GlobalHttpCache>>,
http_cache: Deferred<Arc<dyn HttpCache>>, http_cache: Deferred<Arc<dyn HttpCache>>,
@ -202,9 +213,18 @@ struct CliFactoryServices {
module_load_preparer: Deferred<Arc<ModuleLoadPreparer>>, module_load_preparer: Deferred<Arc<ModuleLoadPreparer>>,
node_code_translator: Deferred<Arc<CliNodeCodeTranslator>>, node_code_translator: Deferred<Arc<CliNodeCodeTranslator>>,
node_resolver: Deferred<Arc<CliNodeResolver>>, node_resolver: Deferred<Arc<CliNodeResolver>>,
npm_cache: Deferred<Arc<CliNpmCache>>,
npm_cache_dir: Deferred<Arc<NpmCacheDir>>, npm_cache_dir: Deferred<Arc<NpmCacheDir>>,
npm_cache_http_client: Deferred<Arc<CliNpmCacheHttpClient>>,
npm_graph_resolver: Deferred<Arc<CliNpmGraphResolver>>,
npm_installer: Deferred<Arc<NpmInstaller>>,
npm_registry_info_provider: Deferred<Arc<CliNpmRegistryInfoProvider>>,
npm_req_resolver: Deferred<Arc<CliNpmReqResolver>>, npm_req_resolver: Deferred<Arc<CliNpmReqResolver>>,
npm_resolution: Arc<NpmResolutionCell>,
npm_resolution_initializer: Deferred<Arc<NpmResolutionInitializer>>,
npm_resolution_installer: Deferred<Arc<NpmResolutionInstaller>>,
npm_resolver: Deferred<Arc<dyn CliNpmResolver>>, npm_resolver: Deferred<Arc<dyn CliNpmResolver>>,
npm_tarball_cache: Deferred<Arc<CliNpmTarballCache>>,
parsed_source_cache: Deferred<Arc<ParsedSourceCache>>, parsed_source_cache: Deferred<Arc<ParsedSourceCache>>,
permission_desc_parser: permission_desc_parser:
Deferred<Arc<RuntimePermissionDescriptorParser<CliSys>>>, Deferred<Arc<RuntimePermissionDescriptorParser<CliSys>>>,
@ -398,6 +418,18 @@ impl CliFactory {
}) })
} }
pub fn npm_cache(&self) -> Result<&Arc<CliNpmCache>, AnyError> {
self.services.npm_cache.get_or_try_init(|| {
let cli_options = self.cli_options()?;
Ok(Arc::new(CliNpmCache::new(
self.npm_cache_dir()?.clone(),
self.sys(),
NpmCacheSetting::from_cache_setting(&cli_options.cache_setting()),
cli_options.npmrc().clone(),
)))
})
}
pub fn npm_cache_dir(&self) -> Result<&Arc<NpmCacheDir>, AnyError> { pub fn npm_cache_dir(&self) -> Result<&Arc<NpmCacheDir>, AnyError> {
self.services.npm_cache_dir.get_or_try_init(|| { self.services.npm_cache_dir.get_or_try_init(|| {
let global_path = self.deno_dir()?.npm_folder_path(); let global_path = self.deno_dir()?.npm_folder_path();
@ -410,6 +442,123 @@ impl CliFactory {
}) })
} }
pub fn npm_cache_http_client(&self) -> &Arc<CliNpmCacheHttpClient> {
self.services.npm_cache_http_client.get_or_init(|| {
Arc::new(CliNpmCacheHttpClient::new(
self.http_client_provider().clone(),
self.text_only_progress_bar().clone(),
))
})
}
pub fn npm_graph_resolver(
&self,
) -> Result<&Arc<CliNpmGraphResolver>, AnyError> {
self.services.npm_graph_resolver.get_or_try_init(|| {
let cli_options = self.cli_options()?;
Ok(Arc::new(CliNpmGraphResolver::new(
self.npm_installer_if_managed()?.cloned(),
self.services.found_pkg_json_dep_flag.clone(),
cli_options.unstable_bare_node_builtins(),
cli_options.default_npm_caching_strategy(),
)))
})
}
pub fn npm_installer_if_managed(
&self,
) -> Result<Option<&Arc<NpmInstaller>>, AnyError> {
let options = self.cli_options()?;
if options.use_byonm() || options.no_npm() {
Ok(None)
} else {
Ok(Some(self.npm_installer()?))
}
}
pub fn npm_installer(&self) -> Result<&Arc<NpmInstaller>, AnyError> {
self.services.npm_installer.get_or_try_init(|| {
let cli_options = self.cli_options()?;
Ok(Arc::new(NpmInstaller::new(
self.npm_cache()?.clone(),
Arc::new(NpmInstallDepsProvider::from_workspace(
cli_options.workspace(),
)),
self.npm_resolution().clone(),
self.npm_resolution_initializer()?.clone(),
self.npm_resolution_installer()?.clone(),
self.text_only_progress_bar(),
self.sys(),
self.npm_tarball_cache()?.clone(),
cli_options.maybe_lockfile().cloned(),
cli_options.node_modules_dir_path().cloned(),
cli_options.lifecycle_scripts_config(),
cli_options.npm_system_info(),
)))
})
}
pub fn npm_registry_info_provider(
&self,
) -> Result<&Arc<CliNpmRegistryInfoProvider>, AnyError> {
self
.services
.npm_registry_info_provider
.get_or_try_init(|| {
let cli_options = self.cli_options()?;
Ok(Arc::new(CliNpmRegistryInfoProvider::new(
self.npm_cache()?.clone(),
self.npm_cache_http_client().clone(),
cli_options.npmrc().clone(),
)))
})
}
pub fn npm_resolution(&self) -> &Arc<NpmResolutionCell> {
&self.services.npm_resolution
}
pub fn npm_resolution_initializer(
&self,
) -> Result<&Arc<NpmResolutionInitializer>, AnyError> {
self
.services
.npm_resolution_initializer
.get_or_try_init(|| {
let cli_options = self.cli_options()?;
Ok(Arc::new(NpmResolutionInitializer::new(
self.npm_registry_info_provider()?.clone(),
self.npm_resolution().clone(),
match cli_options.resolve_npm_resolution_snapshot()? {
Some(snapshot) => {
CliNpmResolverManagedSnapshotOption::Specified(Some(snapshot))
}
None => match cli_options.maybe_lockfile() {
Some(lockfile) => {
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
lockfile.clone(),
)
}
None => CliNpmResolverManagedSnapshotOption::Specified(None),
},
},
)))
})
}
pub fn npm_resolution_installer(
&self,
) -> Result<&Arc<NpmResolutionInstaller>, AnyError> {
self.services.npm_resolution_installer.get_or_try_init(|| {
let cli_options = self.cli_options()?;
Ok(Arc::new(NpmResolutionInstaller::new(
self.npm_registry_info_provider()?.clone(),
self.npm_resolution().clone(),
cli_options.maybe_lockfile().cloned(),
)))
})
}
pub async fn npm_resolver( pub async fn npm_resolver(
&self, &self,
) -> Result<&Arc<dyn CliNpmResolver>, AnyError> { ) -> Result<&Arc<dyn CliNpmResolver>, AnyError> {
@ -419,7 +568,7 @@ impl CliFactory {
.get_or_try_init_async( .get_or_try_init_async(
async { async {
let cli_options = self.cli_options()?; let cli_options = self.cli_options()?;
create_cli_npm_resolver(if cli_options.use_byonm() { Ok(create_cli_npm_resolver(if cli_options.use_byonm() {
CliNpmResolverCreateOptions::Byonm( CliNpmResolverCreateOptions::Byonm(
CliByonmNpmResolverCreateOptions { CliByonmNpmResolverCreateOptions {
sys: self.sys(), sys: self.sys(),
@ -438,52 +587,43 @@ impl CliFactory {
}, },
) )
} else { } else {
self
.npm_resolution_initializer()?
.ensure_initialized()
.await?;
CliNpmResolverCreateOptions::Managed( CliNpmResolverCreateOptions::Managed(
CliManagedNpmResolverCreateOptions { CliManagedNpmResolverCreateOptions {
http_client_provider: self.http_client_provider().clone(),
npm_install_deps_provider: Arc::new(
NpmInstallDepsProvider::from_workspace(
cli_options.workspace(),
),
),
sys: self.sys(), sys: self.sys(),
snapshot: match cli_options.resolve_npm_resolution_snapshot()? { npm_resolution: self.npm_resolution().clone(),
Some(snapshot) => {
CliNpmResolverManagedSnapshotOption::Specified(Some(
snapshot,
))
}
None => match cli_options.maybe_lockfile() {
Some(lockfile) => {
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
lockfile.clone(),
)
}
None => {
CliNpmResolverManagedSnapshotOption::Specified(None)
}
},
},
maybe_lockfile: cli_options.maybe_lockfile().cloned(),
npm_cache_dir: self.npm_cache_dir()?.clone(), npm_cache_dir: self.npm_cache_dir()?.clone(),
cache_setting: cli_options.cache_setting(),
text_only_progress_bar: self.text_only_progress_bar().clone(),
maybe_node_modules_path: cli_options maybe_node_modules_path: cli_options
.node_modules_dir_path() .node_modules_dir_path()
.cloned(), .cloned(),
npm_system_info: cli_options.npm_system_info(), npm_system_info: cli_options.npm_system_info(),
npmrc: cli_options.npmrc().clone(), npmrc: cli_options.npmrc().clone(),
lifecycle_scripts: cli_options.lifecycle_scripts_config(),
}, },
) )
}) }))
.await
} }
.boxed_local(), .boxed_local(),
) )
.await .await
} }
pub fn npm_tarball_cache(
&self,
) -> Result<&Arc<CliNpmTarballCache>, AnyError> {
self.services.npm_tarball_cache.get_or_try_init(|| {
let cli_options = self.cli_options()?;
Ok(Arc::new(CliNpmTarballCache::new(
self.npm_cache()?.clone(),
self.npm_cache_http_client().clone(),
self.sys(),
cli_options.npmrc().clone(),
)))
})
}
pub fn sloppy_imports_resolver( pub fn sloppy_imports_resolver(
&self, &self,
) -> Result<Option<&Arc<CliSloppyImportsResolver>>, AnyError> { ) -> Result<Option<&Arc<CliSloppyImportsResolver>>, AnyError> {
@ -571,17 +711,10 @@ impl CliFactory {
.resolver .resolver
.get_or_try_init_async( .get_or_try_init_async(
async { async {
let cli_options = self.cli_options()?; Ok(Arc::new(CliResolver::new(
Ok(Arc::new(CliResolver::new(CliResolverOptions { self.deno_resolver().await?.clone(),
npm_resolver: if cli_options.no_npm() { self.services.found_pkg_json_dep_flag.clone(),
None )))
} else {
Some(self.npm_resolver().await?.clone())
},
bare_node_builtins_enabled: cli_options
.unstable_bare_node_builtins(),
deno_resolver: self.deno_resolver().await?.clone(),
})))
} }
.boxed_local(), .boxed_local(),
) )
@ -752,6 +885,7 @@ impl CliFactory {
cli_options.clone(), cli_options.clone(),
self.module_graph_builder().await?.clone(), self.module_graph_builder().await?.clone(),
self.node_resolver().await?.clone(), self.node_resolver().await?.clone(),
self.npm_installer_if_managed()?.cloned(),
self.npm_resolver().await?.clone(), self.npm_resolver().await?.clone(),
self.sys(), self.sys(),
))) )))
@ -777,6 +911,8 @@ impl CliFactory {
cli_options.maybe_lockfile().cloned(), cli_options.maybe_lockfile().cloned(),
self.maybe_file_watcher_reporter().clone(), self.maybe_file_watcher_reporter().clone(),
self.module_info_cache()?.clone(), self.module_info_cache()?.clone(),
self.npm_graph_resolver()?.clone(),
self.npm_installer_if_managed()?.cloned(),
self.npm_resolver().await?.clone(), self.npm_resolver().await?.clone(),
self.parsed_source_cache().clone(), self.parsed_source_cache().clone(),
self.resolver().await?.clone(), self.resolver().await?.clone(),
@ -797,7 +933,7 @@ impl CliFactory {
let cli_options = self.cli_options()?; let cli_options = self.cli_options()?;
Ok(Arc::new(ModuleGraphCreator::new( Ok(Arc::new(ModuleGraphCreator::new(
cli_options.clone(), cli_options.clone(),
self.npm_resolver().await?.clone(), self.npm_installer_if_managed()?.cloned(),
self.module_graph_builder().await?.clone(), self.module_graph_builder().await?.clone(),
self.type_checker().await?.clone(), self.type_checker().await?.clone(),
))) )))
@ -997,6 +1133,7 @@ impl CliFactory {
self.sys(), self.sys(),
)), )),
node_resolver.clone(), node_resolver.clone(),
self.npm_installer_if_managed()?.cloned(),
npm_resolver.clone(), npm_resolver.clone(),
pkg_json_resolver, pkg_json_resolver,
self.root_cert_store_provider().clone(), self.root_cert_store_provider().clone(),

View file

@ -7,6 +7,7 @@ use std::sync::Arc;
use deno_config::deno_json; use deno_config::deno_json;
use deno_config::deno_json::JsxImportSourceConfig; use deno_config::deno_json::JsxImportSourceConfig;
use deno_config::deno_json::NodeModulesDirMode;
use deno_config::workspace::JsrPackageConfig; use deno_config::workspace::JsrPackageConfig;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex; use deno_core::parking_lot::Mutex;
@ -51,8 +52,11 @@ use crate::cache::ModuleInfoCache;
use crate::cache::ParsedSourceCache; use crate::cache::ParsedSourceCache;
use crate::colors; use crate::colors;
use crate::file_fetcher::CliFileFetcher; use crate::file_fetcher::CliFileFetcher;
use crate::npm::installer::NpmInstaller;
use crate::npm::installer::PackageCaching;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::resolver::CjsTracker; use crate::resolver::CjsTracker;
use crate::resolver::CliNpmGraphResolver;
use crate::resolver::CliResolver; use crate::resolver::CliResolver;
use crate::resolver::CliSloppyImportsResolver; use crate::resolver::CliSloppyImportsResolver;
use crate::sys::CliSys; use crate::sys::CliSys;
@ -263,7 +267,7 @@ pub struct CreateGraphOptions<'a> {
pub struct ModuleGraphCreator { pub struct ModuleGraphCreator {
options: Arc<CliOptions>, options: Arc<CliOptions>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_installer: Option<Arc<NpmInstaller>>,
module_graph_builder: Arc<ModuleGraphBuilder>, module_graph_builder: Arc<ModuleGraphBuilder>,
type_checker: Arc<TypeChecker>, type_checker: Arc<TypeChecker>,
} }
@ -271,13 +275,13 @@ pub struct ModuleGraphCreator {
impl ModuleGraphCreator { impl ModuleGraphCreator {
pub fn new( pub fn new(
options: Arc<CliOptions>, options: Arc<CliOptions>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_installer: Option<Arc<NpmInstaller>>,
module_graph_builder: Arc<ModuleGraphBuilder>, module_graph_builder: Arc<ModuleGraphBuilder>,
type_checker: Arc<TypeChecker>, type_checker: Arc<TypeChecker>,
) -> Self { ) -> Self {
Self { Self {
options, options,
npm_resolver, npm_installer,
module_graph_builder, module_graph_builder,
type_checker, type_checker,
} }
@ -400,9 +404,9 @@ impl ModuleGraphCreator {
.build_graph_with_npm_resolution(&mut graph, options) .build_graph_with_npm_resolution(&mut graph, options)
.await?; .await?;
if let Some(npm_resolver) = self.npm_resolver.as_managed() { if let Some(npm_installer) = &self.npm_installer {
if graph.has_node_specifier && self.options.type_check_mode().is_true() { if graph.has_node_specifier && self.options.type_check_mode().is_true() {
npm_resolver.inject_synthetic_types_node_package().await?; npm_installer.inject_synthetic_types_node_package().await?;
} }
} }
@ -497,6 +501,8 @@ pub struct ModuleGraphBuilder {
lockfile: Option<Arc<CliLockfile>>, lockfile: Option<Arc<CliLockfile>>,
maybe_file_watcher_reporter: Option<FileWatcherReporter>, maybe_file_watcher_reporter: Option<FileWatcherReporter>,
module_info_cache: Arc<ModuleInfoCache>, module_info_cache: Arc<ModuleInfoCache>,
npm_graph_resolver: Arc<CliNpmGraphResolver>,
npm_installer: Option<Arc<NpmInstaller>>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_resolver: Arc<dyn CliNpmResolver>,
parsed_source_cache: Arc<ParsedSourceCache>, parsed_source_cache: Arc<ParsedSourceCache>,
resolver: Arc<CliResolver>, resolver: Arc<CliResolver>,
@ -516,6 +522,8 @@ impl ModuleGraphBuilder {
lockfile: Option<Arc<CliLockfile>>, lockfile: Option<Arc<CliLockfile>>,
maybe_file_watcher_reporter: Option<FileWatcherReporter>, maybe_file_watcher_reporter: Option<FileWatcherReporter>,
module_info_cache: Arc<ModuleInfoCache>, module_info_cache: Arc<ModuleInfoCache>,
npm_graph_resolver: Arc<CliNpmGraphResolver>,
npm_installer: Option<Arc<NpmInstaller>>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_resolver: Arc<dyn CliNpmResolver>,
parsed_source_cache: Arc<ParsedSourceCache>, parsed_source_cache: Arc<ParsedSourceCache>,
resolver: Arc<CliResolver>, resolver: Arc<CliResolver>,
@ -532,6 +540,8 @@ impl ModuleGraphBuilder {
lockfile, lockfile,
maybe_file_watcher_reporter, maybe_file_watcher_reporter,
module_info_cache, module_info_cache,
npm_graph_resolver,
npm_installer,
npm_resolver, npm_resolver,
parsed_source_cache, parsed_source_cache,
resolver, resolver,
@ -630,10 +640,7 @@ impl ModuleGraphBuilder {
Some(loader) => MutLoaderRef::Borrowed(loader), Some(loader) => MutLoaderRef::Borrowed(loader),
None => MutLoaderRef::Owned(self.create_graph_loader()), None => MutLoaderRef::Owned(self.create_graph_loader()),
}; };
let cli_resolver = &self.resolver;
let graph_resolver = self.create_graph_resolver()?; let graph_resolver = self.create_graph_resolver()?;
let graph_npm_resolver =
cli_resolver.create_graph_npm_resolver(options.npm_caching);
let maybe_file_watcher_reporter = self let maybe_file_watcher_reporter = self
.maybe_file_watcher_reporter .maybe_file_watcher_reporter
.as_ref() .as_ref()
@ -654,7 +661,7 @@ impl ModuleGraphBuilder {
executor: Default::default(), executor: Default::default(),
file_system: &self.sys, file_system: &self.sys,
jsr_url_provider: &CliJsrUrlProvider, jsr_url_provider: &CliJsrUrlProvider,
npm_resolver: Some(&graph_npm_resolver), npm_resolver: Some(self.npm_graph_resolver.as_ref()),
module_analyzer: &analyzer, module_analyzer: &analyzer,
reporter: maybe_file_watcher_reporter, reporter: maybe_file_watcher_reporter,
resolver: Some(&graph_resolver), resolver: Some(&graph_resolver),
@ -678,16 +685,15 @@ impl ModuleGraphBuilder {
if self if self
.cli_options .cli_options
.node_modules_dir()? .node_modules_dir()?
.map(|m| m.uses_node_modules_dir()) .map(|m| m == NodeModulesDirMode::Auto)
.unwrap_or(false) .unwrap_or(false)
{ {
if let Some(npm_resolver) = self.npm_resolver.as_managed() { if let Some(npm_installer) = &self.npm_installer {
let already_done = let already_done = npm_installer
npm_resolver.ensure_top_level_package_json_install().await?; .ensure_top_level_package_json_install()
.await?;
if !already_done && matches!(npm_caching, NpmCachingStrategy::Eager) { if !already_done && matches!(npm_caching, NpmCachingStrategy::Eager) {
npm_resolver npm_installer.cache_packages(PackageCaching::All).await?;
.cache_packages(crate::npm::PackageCaching::All)
.await?;
} }
} }
} }
@ -775,11 +781,7 @@ impl ModuleGraphBuilder {
None None
}; };
let parser = self.parsed_source_cache.as_capturing_parser(); let parser = self.parsed_source_cache.as_capturing_parser();
let cli_resolver = &self.resolver;
let graph_resolver = self.create_graph_resolver()?; let graph_resolver = self.create_graph_resolver()?;
let graph_npm_resolver = cli_resolver.create_graph_npm_resolver(
self.cli_options.default_npm_caching_strategy(),
);
graph.build_fast_check_type_graph( graph.build_fast_check_type_graph(
deno_graph::BuildFastCheckTypeGraphOptions { deno_graph::BuildFastCheckTypeGraphOptions {
@ -788,7 +790,7 @@ impl ModuleGraphBuilder {
fast_check_dts: false, fast_check_dts: false,
jsr_url_provider: &CliJsrUrlProvider, jsr_url_provider: &CliJsrUrlProvider,
resolver: Some(&graph_resolver), resolver: Some(&graph_resolver),
npm_resolver: Some(&graph_npm_resolver), npm_resolver: Some(self.npm_graph_resolver.as_ref()),
workspace_fast_check: options.workspace_fast_check, workspace_fast_check: options.workspace_fast_check,
}, },
); );

View file

@ -480,7 +480,7 @@ impl Document {
let is_cjs_resolver = let is_cjs_resolver =
resolver.as_is_cjs_resolver(self.file_referrer.as_ref()); resolver.as_is_cjs_resolver(self.file_referrer.as_ref());
let npm_resolver = let npm_resolver =
resolver.create_graph_npm_resolver(self.file_referrer.as_ref()); resolver.as_graph_npm_resolver(self.file_referrer.as_ref());
let config_data = resolver.as_config_data(self.file_referrer.as_ref()); let config_data = resolver.as_config_data(self.file_referrer.as_ref());
let jsx_import_source_config = let jsx_import_source_config =
config_data.and_then(|d| d.maybe_jsx_import_source_config()); config_data.and_then(|d| d.maybe_jsx_import_source_config());
@ -503,7 +503,7 @@ impl Document {
s, s,
&CliJsrUrlProvider, &CliJsrUrlProvider,
Some(&resolver), Some(&resolver),
Some(&npm_resolver), Some(npm_resolver.as_ref()),
), ),
) )
}) })
@ -513,7 +513,7 @@ impl Document {
Arc::new(d.with_new_resolver( Arc::new(d.with_new_resolver(
&CliJsrUrlProvider, &CliJsrUrlProvider,
Some(&resolver), Some(&resolver),
Some(&npm_resolver), Some(npm_resolver.as_ref()),
)) ))
}); });
is_script = self.is_script; is_script = self.is_script;
@ -1702,7 +1702,7 @@ fn analyze_module(
) -> (ModuleResult, ResolutionMode) { ) -> (ModuleResult, ResolutionMode) {
match parsed_source_result { match parsed_source_result {
Ok(parsed_source) => { Ok(parsed_source) => {
let npm_resolver = resolver.create_graph_npm_resolver(file_referrer); let npm_resolver = resolver.as_graph_npm_resolver(file_referrer);
let cli_resolver = resolver.as_cli_resolver(file_referrer); let cli_resolver = resolver.as_cli_resolver(file_referrer);
let is_cjs_resolver = resolver.as_is_cjs_resolver(file_referrer); let is_cjs_resolver = resolver.as_is_cjs_resolver(file_referrer);
let config_data = resolver.as_config_data(file_referrer); let config_data = resolver.as_config_data(file_referrer);
@ -1731,7 +1731,7 @@ fn analyze_module(
file_system: &deno_graph::source::NullFileSystem, file_system: &deno_graph::source::NullFileSystem,
jsr_url_provider: &CliJsrUrlProvider, jsr_url_provider: &CliJsrUrlProvider,
maybe_resolver: Some(&resolver), maybe_resolver: Some(&resolver),
maybe_npm_resolver: Some(&npm_resolver), maybe_npm_resolver: Some(npm_resolver.as_ref()),
}, },
)), )),
module_resolution_mode, module_resolution_mode,

View file

@ -9,7 +9,6 @@ use std::sync::Arc;
use dashmap::DashMap; use dashmap::DashMap;
use deno_ast::MediaType; use deno_ast::MediaType;
use deno_cache_dir::file_fetcher::CacheSetting;
use deno_cache_dir::npm::NpmCacheDir; use deno_cache_dir::npm::NpmCacheDir;
use deno_cache_dir::HttpCache; use deno_cache_dir::HttpCache;
use deno_config::deno_json::JsxImportSourceConfig; use deno_config::deno_json::JsxImportSourceConfig;
@ -21,9 +20,11 @@ use deno_graph::GraphImport;
use deno_graph::ModuleSpecifier; use deno_graph::ModuleSpecifier;
use deno_graph::Range; use deno_graph::Range;
use deno_npm::NpmSystemInfo; use deno_npm::NpmSystemInfo;
use deno_npm_cache::TarballCache;
use deno_path_util::url_to_file_path; use deno_path_util::url_to_file_path;
use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::cjs::IsCjsResolutionMode;
use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions;
use deno_resolver::npm::managed::NpmResolutionCell;
use deno_resolver::npm::CreateInNpmPkgCheckerOptions; use deno_resolver::npm::CreateInNpmPkgCheckerOptions;
use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::npm::NpmReqResolverOptions;
use deno_resolver::DenoResolverOptions; use deno_resolver::DenoResolverOptions;
@ -42,6 +43,8 @@ use super::cache::LspCache;
use super::jsr::JsrCacheResolver; use super::jsr::JsrCacheResolver;
use crate::args::create_default_npmrc; use crate::args::create_default_npmrc;
use crate::args::CliLockfile; use crate::args::CliLockfile;
use crate::args::LifecycleScriptsConfig;
use crate::args::NpmCachingStrategy;
use crate::args::NpmInstallDepsProvider; use crate::args::NpmInstallDepsProvider;
use crate::factory::Deferred; use crate::factory::Deferred;
use crate::graph_util::to_node_resolution_kind; use crate::graph_util::to_node_resolution_kind;
@ -53,19 +56,25 @@ use crate::lsp::config::ConfigData;
use crate::lsp::logging::lsp_warn; use crate::lsp::logging::lsp_warn;
use crate::node::CliNodeResolver; use crate::node::CliNodeResolver;
use crate::node::CliPackageJsonResolver; use crate::node::CliPackageJsonResolver;
use crate::npm::create_cli_npm_resolver_for_lsp; use crate::npm::create_cli_npm_resolver;
use crate::npm::installer::NpmInstaller;
use crate::npm::installer::NpmResolutionInstaller;
use crate::npm::CliByonmNpmResolverCreateOptions; use crate::npm::CliByonmNpmResolverCreateOptions;
use crate::npm::CliManagedNpmResolverCreateOptions; use crate::npm::CliManagedNpmResolverCreateOptions;
use crate::npm::CliNpmCache;
use crate::npm::CliNpmCacheHttpClient;
use crate::npm::CliNpmRegistryInfoProvider;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverCreateOptions;
use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CliNpmResolverManagedSnapshotOption;
use crate::npm::ManagedCliNpmResolver; use crate::npm::ManagedCliNpmResolver;
use crate::npm::NpmResolutionInitializer;
use crate::resolver::CliDenoResolver; use crate::resolver::CliDenoResolver;
use crate::resolver::CliNpmGraphResolver;
use crate::resolver::CliNpmReqResolver; use crate::resolver::CliNpmReqResolver;
use crate::resolver::CliResolver; use crate::resolver::CliResolver;
use crate::resolver::CliResolverOptions; use crate::resolver::FoundPackageJsonDepFlag;
use crate::resolver::IsCjsResolver; use crate::resolver::IsCjsResolver;
use crate::resolver::WorkerCliNpmGraphResolver;
use crate::sys::CliSys; use crate::sys::CliSys;
use crate::tsc::into_specifier_and_media_type; use crate::tsc::into_specifier_and_media_type;
use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBar;
@ -77,6 +86,8 @@ struct LspScopeResolver {
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>, in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
is_cjs_resolver: Arc<IsCjsResolver>, is_cjs_resolver: Arc<IsCjsResolver>,
jsr_resolver: Option<Arc<JsrCacheResolver>>, jsr_resolver: Option<Arc<JsrCacheResolver>>,
npm_graph_resolver: Arc<CliNpmGraphResolver>,
npm_installer: Option<Arc<NpmInstaller>>,
npm_resolver: Option<Arc<dyn CliNpmResolver>>, npm_resolver: Option<Arc<dyn CliNpmResolver>>,
node_resolver: Option<Arc<CliNodeResolver>>, node_resolver: Option<Arc<CliNodeResolver>>,
npm_pkg_req_resolver: Option<Arc<CliNpmReqResolver>>, npm_pkg_req_resolver: Option<Arc<CliNpmReqResolver>>,
@ -96,6 +107,8 @@ impl Default for LspScopeResolver {
in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(), in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(),
is_cjs_resolver: factory.is_cjs_resolver().clone(), is_cjs_resolver: factory.is_cjs_resolver().clone(),
jsr_resolver: None, jsr_resolver: None,
npm_graph_resolver: factory.npm_graph_resolver().clone(),
npm_installer: None,
npm_resolver: None, npm_resolver: None,
node_resolver: None, node_resolver: None,
npm_pkg_req_resolver: None, npm_pkg_req_resolver: None,
@ -121,6 +134,7 @@ impl LspScopeResolver {
} }
let in_npm_pkg_checker = factory.in_npm_pkg_checker().clone(); let in_npm_pkg_checker = factory.in_npm_pkg_checker().clone();
let npm_resolver = factory.npm_resolver().cloned(); let npm_resolver = factory.npm_resolver().cloned();
let npm_installer = factory.npm_installer().cloned();
let node_resolver = factory.node_resolver().cloned(); let node_resolver = factory.node_resolver().cloned();
let npm_pkg_req_resolver = factory.npm_pkg_req_resolver().cloned(); let npm_pkg_req_resolver = factory.npm_pkg_req_resolver().cloned();
let cli_resolver = factory.cli_resolver().clone(); let cli_resolver = factory.cli_resolver().clone();
@ -133,8 +147,7 @@ impl LspScopeResolver {
cache.for_specifier(config_data.map(|d| d.scope.as_ref())), cache.for_specifier(config_data.map(|d| d.scope.as_ref())),
config_data.and_then(|d| d.lockfile.clone()), config_data.and_then(|d| d.lockfile.clone()),
))); )));
let npm_graph_resolver = cli_resolver let npm_graph_resolver = factory.npm_graph_resolver();
.create_graph_npm_resolver(crate::graph_util::NpmCachingStrategy::Eager);
let maybe_jsx_import_source_config = let maybe_jsx_import_source_config =
config_data.and_then(|d| d.maybe_jsx_import_source_config()); config_data.and_then(|d| d.maybe_jsx_import_source_config());
let graph_imports = config_data let graph_imports = config_data
@ -156,7 +169,7 @@ impl LspScopeResolver {
imports, imports,
&CliJsrUrlProvider, &CliJsrUrlProvider,
Some(&resolver), Some(&resolver),
Some(&npm_graph_resolver), Some(npm_graph_resolver.as_ref()),
); );
(referrer, graph_import) (referrer, graph_import)
}) })
@ -207,8 +220,10 @@ impl LspScopeResolver {
in_npm_pkg_checker, in_npm_pkg_checker,
is_cjs_resolver: factory.is_cjs_resolver().clone(), is_cjs_resolver: factory.is_cjs_resolver().clone(),
jsr_resolver, jsr_resolver,
npm_graph_resolver: factory.npm_graph_resolver().clone(),
npm_pkg_req_resolver, npm_pkg_req_resolver,
npm_resolver, npm_resolver,
npm_installer,
node_resolver, node_resolver,
pkg_json_resolver, pkg_json_resolver,
redirect_resolver, redirect_resolver,
@ -231,6 +246,9 @@ impl LspScopeResolver {
in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(), in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(),
is_cjs_resolver: factory.is_cjs_resolver().clone(), is_cjs_resolver: factory.is_cjs_resolver().clone(),
jsr_resolver: self.jsr_resolver.clone(), jsr_resolver: self.jsr_resolver.clone(),
npm_graph_resolver: factory.npm_graph_resolver().clone(),
// npm installer isn't necessary for a snapshot
npm_installer: None,
npm_pkg_req_resolver: factory.npm_pkg_req_resolver().cloned(), npm_pkg_req_resolver: factory.npm_pkg_req_resolver().cloned(),
npm_resolver: factory.npm_resolver().cloned(), npm_resolver: factory.npm_resolver().cloned(),
node_resolver: factory.node_resolver().cloned(), node_resolver: factory.node_resolver().cloned(),
@ -318,14 +336,12 @@ impl LspResolver {
if let Some(dep_info) = dep_info { if let Some(dep_info) = dep_info {
*resolver.dep_info.lock() = dep_info.clone(); *resolver.dep_info.lock() = dep_info.clone();
} }
if let Some(npm_resolver) = resolver.npm_resolver.as_ref() { if let Some(npm_installer) = resolver.npm_installer.as_ref() {
if let Some(npm_resolver) = npm_resolver.as_managed() { let reqs = dep_info
let reqs = dep_info .map(|i| i.npm_reqs.iter().cloned().collect::<Vec<_>>())
.map(|i| i.npm_reqs.iter().cloned().collect::<Vec<_>>()) .unwrap_or_default();
.unwrap_or_default(); if let Err(err) = npm_installer.set_package_reqs(&reqs).await {
if let Err(err) = npm_resolver.set_package_reqs(&reqs).await { lsp_warn!("Could not set npm package requirements: {:#}", err);
lsp_warn!("Could not set npm package requirements: {:#}", err);
}
} }
} }
} }
@ -339,14 +355,12 @@ impl LspResolver {
resolver.resolver.as_ref() resolver.resolver.as_ref()
} }
pub fn create_graph_npm_resolver( pub fn as_graph_npm_resolver(
&self, &self,
file_referrer: Option<&ModuleSpecifier>, file_referrer: Option<&ModuleSpecifier>,
) -> WorkerCliNpmGraphResolver { ) -> &Arc<CliNpmGraphResolver> {
let resolver = self.get_scope_resolver(file_referrer); let resolver = self.get_scope_resolver(file_referrer);
resolver &resolver.npm_graph_resolver
.resolver
.create_graph_npm_resolver(crate::graph_util::NpmCachingStrategy::Eager)
} }
pub fn as_is_cjs_resolver( pub fn as_is_cjs_resolver(
@ -590,9 +604,12 @@ pub struct ScopeDepInfo {
#[derive(Default)] #[derive(Default)]
struct ResolverFactoryServices { struct ResolverFactoryServices {
cli_resolver: Deferred<Arc<CliResolver>>, cli_resolver: Deferred<Arc<CliResolver>>,
found_pkg_json_dep_flag: Arc<FoundPackageJsonDepFlag>,
in_npm_pkg_checker: Deferred<Arc<dyn InNpmPackageChecker>>, in_npm_pkg_checker: Deferred<Arc<dyn InNpmPackageChecker>>,
is_cjs_resolver: Deferred<Arc<IsCjsResolver>>, is_cjs_resolver: Deferred<Arc<IsCjsResolver>>,
node_resolver: Deferred<Option<Arc<CliNodeResolver>>>, node_resolver: Deferred<Option<Arc<CliNodeResolver>>>,
npm_graph_resolver: Deferred<Arc<CliNpmGraphResolver>>,
npm_installer: Option<Arc<NpmInstaller>>,
npm_pkg_req_resolver: Deferred<Option<Arc<CliNpmReqResolver>>>, npm_pkg_req_resolver: Deferred<Option<Arc<CliNpmReqResolver>>>,
npm_resolver: Option<Arc<dyn CliNpmResolver>>, npm_resolver: Option<Arc<dyn CliNpmResolver>>,
} }
@ -616,6 +633,10 @@ impl<'a> ResolverFactory<'a> {
} }
} }
// todo(dsherret): probably this method could be removed in the future
// and instead just `npm_resolution_initializer.ensure_initialized()` could
// be called. The reason this exists is because creating the npm resolvers
// used to be async.
async fn init_npm_resolver( async fn init_npm_resolver(
&mut self, &mut self,
http_client_provider: &Arc<HttpClientProvider>, http_client_provider: &Arc<HttpClientProvider>,
@ -645,11 +666,31 @@ impl<'a> ResolverFactory<'a> {
cache.deno_dir().npm_folder_path(), cache.deno_dir().npm_folder_path(),
npmrc.get_all_known_registries_urls(), npmrc.get_all_known_registries_urls(),
)); ));
CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions { let npm_cache = Arc::new(CliNpmCache::new(
http_client_provider: http_client_provider.clone(), npm_cache_dir.clone(),
// only used for top level install, so we can ignore this sys.clone(),
npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()), // Use an "only" cache setting in order to make the
snapshot: match self.config_data.and_then(|d| d.lockfile.as_ref()) { // user do an explicit "cache" command and prevent
// the cache from being filled with lots of packages while
// the user is typing.
deno_npm_cache::NpmCacheSetting::Only,
npmrc.clone(),
));
let pb = ProgressBar::new(ProgressBarStyle::TextOnly);
let npm_client = Arc::new(CliNpmCacheHttpClient::new(
http_client_provider.clone(),
pb.clone(),
));
let registry_info_provider = Arc::new(CliNpmRegistryInfoProvider::new(
npm_cache.clone(),
npm_client.clone(),
npmrc.clone(),
));
let npm_resolution = Arc::new(NpmResolutionCell::default());
let npm_resolution_initializer = Arc::new(NpmResolutionInitializer::new(
registry_info_provider.clone(),
npm_resolution.clone(),
match self.config_data.and_then(|d| d.lockfile.as_ref()) {
Some(lockfile) => { Some(lockfile) => {
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
lockfile.clone(), lockfile.clone(),
@ -657,26 +698,62 @@ impl<'a> ResolverFactory<'a> {
} }
None => CliNpmResolverManagedSnapshotOption::Specified(None), 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.
let maybe_lockfile: Option<Arc<CliLockfile>> = None;
let maybe_node_modules_path =
self.config_data.and_then(|d| d.node_modules_dir.clone());
let tarball_cache = Arc::new(TarballCache::new(
npm_cache.clone(),
npm_client.clone(),
sys.clone(),
npmrc.clone(),
));
let npm_resolution_installer = Arc::new(NpmResolutionInstaller::new(
registry_info_provider,
npm_resolution.clone(),
maybe_lockfile.clone(),
));
let npm_installer = Arc::new(NpmInstaller::new(
npm_cache.clone(),
Arc::new(NpmInstallDepsProvider::empty()),
npm_resolution.clone(),
npm_resolution_initializer.clone(),
npm_resolution_installer,
&pb,
sys.clone(),
tarball_cache.clone(),
maybe_lockfile,
maybe_node_modules_path.clone(),
LifecycleScriptsConfig::default(),
NpmSystemInfo::default(),
));
self.set_npm_installer(npm_installer);
// spawn due to the lsp's `Send` requirement
deno_core::unsync::spawn(async move {
if let Err(err) = npm_resolution_initializer.ensure_initialized().await
{
log::warn!("failed to initialize npm resolution: {}", err);
}
})
.await
.unwrap();
CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions {
sys: CliSys::default(), sys: CliSys::default(),
npm_cache_dir, npm_cache_dir,
// Use an "only" cache setting in order to make the maybe_node_modules_path,
// 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),
// Don't provide the lockfile. We don't want these resolvers
// updating it. Only the cache request should update the lockfile.
maybe_lockfile: None,
maybe_node_modules_path: self
.config_data
.and_then(|d| d.node_modules_dir.clone()),
npmrc, npmrc,
npm_resolution,
npm_system_info: NpmSystemInfo::default(), npm_system_info: NpmSystemInfo::default(),
lifecycle_scripts: Default::default(),
}) })
}; };
self.set_npm_resolver(create_cli_npm_resolver_for_lsp(options).await); self.set_npm_resolver(create_cli_npm_resolver(options));
}
pub fn set_npm_installer(&mut self, npm_installer: Arc<NpmInstaller>) {
self.services.npm_installer = Some(npm_installer);
} }
pub fn set_npm_resolver(&mut self, npm_resolver: Arc<dyn CliNpmResolver>) { pub fn set_npm_resolver(&mut self, npm_resolver: Arc<dyn CliNpmResolver>) {
@ -720,13 +797,27 @@ impl<'a> ResolverFactory<'a> {
is_byonm: self.config_data.map(|d| d.byonm).unwrap_or(false), is_byonm: self.config_data.map(|d| d.byonm).unwrap_or(false),
maybe_vendor_dir: self.config_data.and_then(|d| d.vendor_dir.as_ref()), maybe_vendor_dir: self.config_data.and_then(|d| d.vendor_dir.as_ref()),
})); }));
Arc::new(CliResolver::new(CliResolverOptions { Arc::new(CliResolver::new(
deno_resolver, deno_resolver,
npm_resolver: self.npm_resolver().cloned(), self.services.found_pkg_json_dep_flag.clone(),
bare_node_builtins_enabled: self ))
})
}
pub fn npm_installer(&self) -> Option<&Arc<NpmInstaller>> {
self.services.npm_installer.as_ref()
}
pub fn npm_graph_resolver(&self) -> &Arc<CliNpmGraphResolver> {
self.services.npm_graph_resolver.get_or_init(|| {
Arc::new(CliNpmGraphResolver::new(
None,
self.services.found_pkg_json_dep_flag.clone(),
self
.config_data .config_data
.is_some_and(|d| d.unstable.contains("bare-node-builtins")), .is_some_and(|d| d.unstable.contains("bare-node-builtins")),
})) NpmCachingStrategy::Eager,
))
}) })
} }

View file

@ -40,7 +40,6 @@ use deno_core::error::AnyError;
use deno_core::error::CoreError; use deno_core::error::CoreError;
use deno_core::futures::FutureExt; use deno_core::futures::FutureExt;
use deno_core::unsync::JoinHandle; use deno_core::unsync::JoinHandle;
use deno_npm::resolution::SnapshotFromLockfileError;
use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError; use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError;
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
use deno_runtime::fmt_errors::format_js_error; use deno_runtime::fmt_errors::format_js_error;
@ -52,6 +51,7 @@ use factory::CliFactory;
use standalone::MODULE_NOT_FOUND; use standalone::MODULE_NOT_FOUND;
use standalone::UNSUPPORTED_SCHEME; use standalone::UNSUPPORTED_SCHEME;
use self::npm::ResolveSnapshotError;
use crate::args::flags_from_vec; use crate::args::flags_from_vec;
use crate::args::DenoSubcommand; use crate::args::DenoSubcommand;
use crate::args::Flags; use crate::args::Flags;
@ -376,13 +376,15 @@ fn exit_for_error(error: AnyError) -> ! {
util::result::any_and_jserrorbox_downcast_ref::<CoreError>(&error) util::result::any_and_jserrorbox_downcast_ref::<CoreError>(&error)
{ {
error_string = format_js_error(e); error_string = format_js_error(e);
} else if let Some(SnapshotFromLockfileError::IntegrityCheckFailed(e)) = } else if let Some(e @ ResolveSnapshotError { .. }) =
util::result::any_and_jserrorbox_downcast_ref::<SnapshotFromLockfileError>( util::result::any_and_jserrorbox_downcast_ref::<ResolveSnapshotError>(
&error, &error,
) )
{ {
error_string = e.to_string(); if let Some(e) = e.maybe_integrity_check_error() {
error_code = 10; error_string = e.to_string();
error_code = 10;
}
} }
exit_with_message(&error_string, error_code); exit_with_message(&error_string, error_code);

View file

@ -8,8 +8,7 @@ use std::path::PathBuf;
use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::resolution::NpmResolutionSnapshot;
use deno_npm::NpmPackageId; use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage;
use crate::npm::managed::NpmResolutionPackage;
#[derive(Default)] #[derive(Default)]
pub struct BinEntries<'a> { pub struct BinEntries<'a> {

View file

@ -3,14 +3,14 @@
use async_trait::async_trait; use async_trait::async_trait;
use deno_error::JsErrorBox; use deno_error::JsErrorBox;
use crate::npm::PackageCaching; use super::PackageCaching;
pub mod bin_entries; pub mod bin_entries;
pub mod lifecycle_scripts; pub mod lifecycle_scripts;
/// Part of the resolution that interacts with the file system. /// Part of the resolution that interacts with the file system.
#[async_trait(?Send)] #[async_trait(?Send)]
pub trait NpmPackageFsInstaller: Send + Sync { pub trait NpmPackageFsInstaller: std::fmt::Debug + Send + Sync {
async fn cache_packages<'a>( async fn cache_packages<'a>(
&self, &self,
caching: PackageCaching<'a>, caching: PackageCaching<'a>,

View file

@ -11,14 +11,14 @@ use deno_core::futures::StreamExt;
use deno_error::JsErrorBox; use deno_error::JsErrorBox;
use deno_npm::NpmResolutionPackage; use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo; use deno_npm::NpmSystemInfo;
use deno_resolver::npm::managed::NpmResolution; use deno_resolver::npm::managed::NpmResolutionCell;
use super::common::lifecycle_scripts::LifecycleScriptsStrategy; use super::common::lifecycle_scripts::LifecycleScriptsStrategy;
use super::common::NpmPackageFsInstaller; use super::common::NpmPackageFsInstaller;
use super::PackageCaching;
use crate::args::LifecycleScriptsConfig; use crate::args::LifecycleScriptsConfig;
use crate::cache::FastInsecureHasher; use crate::cache::FastInsecureHasher;
use crate::colors; use crate::colors;
use crate::npm::managed::PackageCaching;
use crate::npm::CliNpmCache; use crate::npm::CliNpmCache;
use crate::npm::CliNpmTarballCache; use crate::npm::CliNpmTarballCache;
@ -27,25 +27,25 @@ use crate::npm::CliNpmTarballCache;
pub struct GlobalNpmPackageInstaller { pub struct GlobalNpmPackageInstaller {
cache: Arc<CliNpmCache>, cache: Arc<CliNpmCache>,
tarball_cache: Arc<CliNpmTarballCache>, tarball_cache: Arc<CliNpmTarballCache>,
resolution: Arc<NpmResolution>, resolution: Arc<NpmResolutionCell>,
system_info: NpmSystemInfo,
lifecycle_scripts: LifecycleScriptsConfig, lifecycle_scripts: LifecycleScriptsConfig,
system_info: NpmSystemInfo,
} }
impl GlobalNpmPackageInstaller { impl GlobalNpmPackageInstaller {
pub fn new( pub fn new(
cache: Arc<CliNpmCache>, cache: Arc<CliNpmCache>,
tarball_cache: Arc<CliNpmTarballCache>, tarball_cache: Arc<CliNpmTarballCache>,
resolution: Arc<NpmResolution>, resolution: Arc<NpmResolutionCell>,
system_info: NpmSystemInfo,
lifecycle_scripts: LifecycleScriptsConfig, lifecycle_scripts: LifecycleScriptsConfig,
system_info: NpmSystemInfo,
) -> Self { ) -> Self {
Self { Self {
cache, cache,
tarball_cache, tarball_cache,
resolution, resolution,
system_info,
lifecycle_scripts, lifecycle_scripts,
system_info,
} }
} }
} }

View file

@ -25,7 +25,7 @@ use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo; use deno_npm::NpmSystemInfo;
use deno_path_util::fs::atomic_write_file_with_retries; use deno_path_util::fs::atomic_write_file_with_retries;
use deno_resolver::npm::get_package_folder_id_folder_name; use deno_resolver::npm::get_package_folder_id_folder_name;
use deno_resolver::npm::managed::NpmResolution; use deno_resolver::npm::managed::NpmResolutionCell;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use deno_semver::StackString; use deno_semver::StackString;
use serde::Deserialize; use serde::Deserialize;
@ -33,11 +33,11 @@ use serde::Serialize;
use super::common::bin_entries; use super::common::bin_entries;
use super::common::NpmPackageFsInstaller; use super::common::NpmPackageFsInstaller;
use super::PackageCaching;
use crate::args::LifecycleScriptsConfig; use crate::args::LifecycleScriptsConfig;
use crate::args::NpmInstallDepsProvider; use crate::args::NpmInstallDepsProvider;
use crate::cache::CACHE_PERM; use crate::cache::CACHE_PERM;
use crate::colors; use crate::colors;
use crate::npm::managed::PackageCaching;
use crate::npm::CliNpmCache; use crate::npm::CliNpmCache;
use crate::npm::CliNpmTarballCache; use crate::npm::CliNpmTarballCache;
use crate::sys::CliSys; use crate::sys::CliSys;
@ -54,12 +54,12 @@ pub struct LocalNpmPackageInstaller {
cache: Arc<CliNpmCache>, cache: Arc<CliNpmCache>,
npm_install_deps_provider: Arc<NpmInstallDepsProvider>, npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
progress_bar: ProgressBar, progress_bar: ProgressBar,
resolution: Arc<NpmResolution>, resolution: Arc<NpmResolutionCell>,
sys: CliSys, sys: CliSys,
tarball_cache: Arc<CliNpmTarballCache>, tarball_cache: Arc<CliNpmTarballCache>,
lifecycle_scripts: LifecycleScriptsConfig,
root_node_modules_path: PathBuf, root_node_modules_path: PathBuf,
system_info: NpmSystemInfo, system_info: NpmSystemInfo,
lifecycle_scripts: LifecycleScriptsConfig,
} }
impl LocalNpmPackageInstaller { impl LocalNpmPackageInstaller {
@ -68,12 +68,12 @@ impl LocalNpmPackageInstaller {
cache: Arc<CliNpmCache>, cache: Arc<CliNpmCache>,
npm_install_deps_provider: Arc<NpmInstallDepsProvider>, npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
progress_bar: ProgressBar, progress_bar: ProgressBar,
resolution: Arc<NpmResolution>, resolution: Arc<NpmResolutionCell>,
sys: CliSys, sys: CliSys,
tarball_cache: Arc<CliNpmTarballCache>, tarball_cache: Arc<CliNpmTarballCache>,
node_modules_folder: PathBuf, node_modules_folder: PathBuf,
system_info: NpmSystemInfo,
lifecycle_scripts: LifecycleScriptsConfig, lifecycle_scripts: LifecycleScriptsConfig,
system_info: NpmSystemInfo,
) -> Self { ) -> Self {
Self { Self {
cache, cache,
@ -82,9 +82,9 @@ impl LocalNpmPackageInstaller {
resolution, resolution,
tarball_cache, tarball_cache,
sys, sys,
lifecycle_scripts,
root_node_modules_path: node_modules_folder, root_node_modules_path: node_modules_folder,
system_info, system_info,
lifecycle_scripts,
} }
} }
} }

283
cli/npm/installer/mod.rs Normal file
View file

@ -0,0 +1,283 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use std::borrow::Cow;
use std::path::PathBuf;
use std::sync::Arc;
use deno_core::error::AnyError;
use deno_core::unsync::sync::AtomicFlag;
use deno_error::JsErrorBox;
use deno_npm::registry::NpmPackageInfo;
use deno_npm::registry::NpmRegistryPackageInfoLoadError;
use deno_npm::NpmSystemInfo;
use deno_resolver::npm::managed::NpmResolutionCell;
use deno_runtime::colors;
use deno_semver::package::PackageReq;
pub use self::common::NpmPackageFsInstaller;
use self::global::GlobalNpmPackageInstaller;
use self::local::LocalNpmPackageInstaller;
pub use self::resolution::AddPkgReqsResult;
pub use self::resolution::NpmResolutionInstaller;
use super::NpmResolutionInitializer;
use crate::args::CliLockfile;
use crate::args::LifecycleScriptsConfig;
use crate::args::NpmInstallDepsProvider;
use crate::args::PackageJsonDepValueParseWithLocationError;
use crate::npm::CliNpmCache;
use crate::npm::CliNpmTarballCache;
use crate::sys::CliSys;
use crate::util::progress_bar::ProgressBar;
mod common;
mod global;
mod local;
mod resolution;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PackageCaching<'a> {
Only(Cow<'a, [PackageReq]>),
All,
}
#[derive(Debug)]
pub struct NpmInstaller {
fs_installer: Arc<dyn NpmPackageFsInstaller>,
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
npm_resolution_initializer: Arc<NpmResolutionInitializer>,
npm_resolution_installer: Arc<NpmResolutionInstaller>,
maybe_lockfile: Option<Arc<CliLockfile>>,
npm_resolution: Arc<NpmResolutionCell>,
top_level_install_flag: AtomicFlag,
}
impl NpmInstaller {
#[allow(clippy::too_many_arguments)]
pub fn new(
npm_cache: Arc<CliNpmCache>,
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
npm_resolution: Arc<NpmResolutionCell>,
npm_resolution_initializer: Arc<NpmResolutionInitializer>,
npm_resolution_installer: Arc<NpmResolutionInstaller>,
progress_bar: &ProgressBar,
sys: CliSys,
tarball_cache: Arc<CliNpmTarballCache>,
maybe_lockfile: Option<Arc<CliLockfile>>,
maybe_node_modules_path: Option<PathBuf>,
lifecycle_scripts: LifecycleScriptsConfig,
system_info: NpmSystemInfo,
) -> Self {
let fs_installer: Arc<dyn NpmPackageFsInstaller> =
match maybe_node_modules_path {
Some(node_modules_folder) => Arc::new(LocalNpmPackageInstaller::new(
npm_cache,
npm_install_deps_provider.clone(),
progress_bar.clone(),
npm_resolution.clone(),
sys,
tarball_cache,
node_modules_folder,
lifecycle_scripts,
system_info,
)),
None => Arc::new(GlobalNpmPackageInstaller::new(
npm_cache,
tarball_cache,
npm_resolution.clone(),
lifecycle_scripts,
system_info,
)),
};
Self {
fs_installer,
npm_install_deps_provider,
npm_resolution,
npm_resolution_initializer,
npm_resolution_installer,
maybe_lockfile,
top_level_install_flag: Default::default(),
}
}
/// Adds package requirements to the resolver and ensures everything is setup.
/// This includes setting up the `node_modules` directory, if applicable.
pub async fn add_and_cache_package_reqs(
&self,
packages: &[PackageReq],
) -> Result<(), JsErrorBox> {
self.npm_resolution_initializer.ensure_initialized().await?;
self
.add_package_reqs_raw(
packages,
Some(PackageCaching::Only(packages.into())),
)
.await
.dependencies_result
}
pub async fn add_package_reqs_no_cache(
&self,
packages: &[PackageReq],
) -> Result<(), JsErrorBox> {
self.npm_resolution_initializer.ensure_initialized().await?;
self
.add_package_reqs_raw(packages, None)
.await
.dependencies_result
}
pub async fn add_package_reqs(
&self,
packages: &[PackageReq],
caching: PackageCaching<'_>,
) -> Result<(), JsErrorBox> {
self
.add_package_reqs_raw(packages, Some(caching))
.await
.dependencies_result
}
pub async fn add_package_reqs_raw<'a>(
&self,
packages: &[PackageReq],
caching: Option<PackageCaching<'a>>,
) -> AddPkgReqsResult {
if packages.is_empty() {
return AddPkgReqsResult {
dependencies_result: Ok(()),
results: vec![],
};
}
#[cfg(debug_assertions)]
self.npm_resolution_initializer.debug_assert_initialized();
let mut result = self
.npm_resolution_installer
.add_package_reqs(packages)
.await;
if result.dependencies_result.is_ok() {
if let Some(lockfile) = self.maybe_lockfile.as_ref() {
result.dependencies_result = lockfile.error_if_changed();
}
}
if result.dependencies_result.is_ok() {
if let Some(caching) = caching {
result.dependencies_result = self.cache_packages(caching).await;
}
}
result
}
/// Sets package requirements to the resolver, removing old requirements and adding new ones.
///
/// This will retrieve and resolve package information, but not cache any package files.
pub async fn set_package_reqs(
&self,
packages: &[PackageReq],
) -> Result<(), AnyError> {
self
.npm_resolution_installer
.set_package_reqs(packages)
.await
}
pub async fn inject_synthetic_types_node_package(
&self,
) -> Result<(), JsErrorBox> {
self.npm_resolution_initializer.ensure_initialized().await?;
let reqs = &[PackageReq::from_str("@types/node").unwrap()];
// add and ensure this isn't added to the lockfile
self
.add_package_reqs(reqs, PackageCaching::Only(reqs.into()))
.await?;
Ok(())
}
pub async fn cache_package_info(
&self,
package_name: &str,
) -> Result<Arc<NpmPackageInfo>, NpmRegistryPackageInfoLoadError> {
self
.npm_resolution_installer
.cache_package_info(package_name)
.await
}
pub async fn cache_packages(
&self,
caching: PackageCaching<'_>,
) -> Result<(), JsErrorBox> {
self.npm_resolution_initializer.ensure_initialized().await?;
self.fs_installer.cache_packages(caching).await
}
pub fn ensure_no_pkg_json_dep_errors(
&self,
) -> Result<(), Box<PackageJsonDepValueParseWithLocationError>> {
for err in self.npm_install_deps_provider.pkg_json_dep_errors() {
match err.source.as_kind() {
deno_package_json::PackageJsonDepValueParseErrorKind::VersionReq(_) => {
return Err(Box::new(err.clone()));
}
deno_package_json::PackageJsonDepValueParseErrorKind::Unsupported {
..
} => {
// only warn for this one
log::warn!(
"{} {}\n at {}",
colors::yellow("Warning"),
err.source,
err.location,
)
}
}
}
Ok(())
}
/// Ensures that the top level `package.json` dependencies are installed.
/// This may set up the `node_modules` directory.
///
/// Returns `true` if the top level packages are already installed. A
/// return value of `false` means that new packages were added to the NPM resolution.
pub async fn ensure_top_level_package_json_install(
&self,
) -> Result<bool, JsErrorBox> {
if !self.top_level_install_flag.raise() {
return Ok(true); // already did this
}
self.npm_resolution_initializer.ensure_initialized().await?;
let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs();
if pkg_json_remote_pkgs.is_empty() {
return Ok(true);
}
// check if something needs resolving before bothering to load all
// the package information (which is slow)
if pkg_json_remote_pkgs.iter().all(|pkg| {
self
.npm_resolution
.resolve_pkg_id_from_pkg_req(&pkg.req)
.is_ok()
}) {
log::debug!(
"All package.json deps resolvable. Skipping top level install."
);
return Ok(true); // everything is already resolvable
}
let pkg_reqs = pkg_json_remote_pkgs
.iter()
.map(|pkg| pkg.req.clone())
.collect::<Vec<_>>();
self.add_package_reqs_no_cache(&pkg_reqs).await?;
Ok(false)
}
}

View file

@ -8,12 +8,14 @@ use deno_core::error::AnyError;
use deno_error::JsErrorBox; use deno_error::JsErrorBox;
use deno_lockfile::NpmPackageDependencyLockfileInfo; use deno_lockfile::NpmPackageDependencyLockfileInfo;
use deno_lockfile::NpmPackageLockfileInfo; use deno_lockfile::NpmPackageLockfileInfo;
use deno_npm::registry::NpmPackageInfo;
use deno_npm::registry::NpmRegistryApi; use deno_npm::registry::NpmRegistryApi;
use deno_npm::registry::NpmRegistryPackageInfoLoadError;
use deno_npm::resolution::AddPkgReqsOptions; use deno_npm::resolution::AddPkgReqsOptions;
use deno_npm::resolution::NpmResolutionError; use deno_npm::resolution::NpmResolutionError;
use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::resolution::NpmResolutionSnapshot;
use deno_npm::NpmResolutionPackage; use deno_npm::NpmResolutionPackage;
use deno_resolver::npm::managed::NpmResolution; use deno_resolver::npm::managed::NpmResolutionCell;
use deno_semver::jsr::JsrDepPackageReq; use deno_semver::jsr::JsrDepPackageReq;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
@ -35,9 +37,10 @@ pub struct AddPkgReqsResult {
} }
/// Updates the npm resolution with the provided package requirements. /// Updates the npm resolution with the provided package requirements.
#[derive(Debug)]
pub struct NpmResolutionInstaller { pub struct NpmResolutionInstaller {
registry_info_provider: Arc<CliNpmRegistryInfoProvider>, registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
resolution: Arc<NpmResolution>, resolution: Arc<NpmResolutionCell>,
maybe_lockfile: Option<Arc<CliLockfile>>, maybe_lockfile: Option<Arc<CliLockfile>>,
update_queue: TaskQueue, update_queue: TaskQueue,
} }
@ -45,7 +48,7 @@ pub struct NpmResolutionInstaller {
impl NpmResolutionInstaller { impl NpmResolutionInstaller {
pub fn new( pub fn new(
registry_info_provider: Arc<CliNpmRegistryInfoProvider>, registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
resolution: Arc<NpmResolution>, resolution: Arc<NpmResolutionCell>,
maybe_lockfile: Option<Arc<CliLockfile>>, maybe_lockfile: Option<Arc<CliLockfile>>,
) -> Self { ) -> Self {
Self { Self {
@ -56,6 +59,14 @@ impl NpmResolutionInstaller {
} }
} }
pub async fn cache_package_info(
&self,
package_name: &str,
) -> Result<Arc<NpmPackageInfo>, NpmRegistryPackageInfoLoadError> {
// this will internally cache the package information
self.registry_info_provider.package_info(package_name).await
}
pub async fn add_package_reqs( pub async fn add_package_reqs(
&self, &self,
package_reqs: &[PackageReq], package_reqs: &[PackageReq],

496
cli/npm/managed.rs Normal file
View file

@ -0,0 +1,496 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use deno_ast::ModuleSpecifier;
use deno_cache_dir::npm::NpmCacheDir;
use deno_core::parking_lot::Mutex;
use deno_core::serde_json;
use deno_core::url::Url;
use deno_error::JsError;
use deno_error::JsErrorBox;
use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::registry::NpmRegistryApi;
use deno_npm::resolution::NpmResolutionSnapshot;
use deno_npm::resolution::PackageReqNotFoundError;
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo;
use deno_resolver::npm::managed::NpmResolutionCell;
use deno_resolver::npm::managed::ResolvePkgFolderFromDenoModuleError;
use deno_resolver::npm::managed::ResolvePkgFolderFromPkgIdError;
use deno_resolver::npm::managed::ResolvePkgIdFromSpecifierError;
use deno_resolver::npm::ByonmOrManagedNpmResolver;
use deno_resolver::npm::ManagedNpmResolver;
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
use deno_runtime::ops::process::NpmProcessStateProvider;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
use node_resolver::NpmPackageFolderResolver;
use sys_traits::FsMetadata;
use thiserror::Error;
use super::CliNpmRegistryInfoProvider;
use super::CliNpmResolver;
use super::InnerCliNpmResolverRef;
use crate::args::CliLockfile;
use crate::args::NpmProcessState;
use crate::args::NpmProcessStateKind;
use crate::cache::FastInsecureHasher;
use crate::sys::CliSys;
#[derive(Debug, Clone)]
pub enum CliNpmResolverManagedSnapshotOption {
ResolveFromLockfile(Arc<CliLockfile>),
Specified(Option<ValidSerializedNpmResolutionSnapshot>),
}
#[derive(Debug)]
enum SyncState {
Pending(Option<CliNpmResolverManagedSnapshotOption>),
Err(ResolveSnapshotError),
Success,
}
#[derive(Debug)]
pub struct NpmResolutionInitializer {
npm_registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
npm_resolution: Arc<NpmResolutionCell>,
queue: tokio::sync::Mutex<()>,
sync_state: Mutex<SyncState>,
}
impl NpmResolutionInitializer {
pub fn new(
npm_registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
npm_resolution: Arc<NpmResolutionCell>,
snapshot_option: CliNpmResolverManagedSnapshotOption,
) -> Self {
Self {
npm_registry_info_provider,
npm_resolution,
queue: tokio::sync::Mutex::new(()),
sync_state: Mutex::new(SyncState::Pending(Some(snapshot_option))),
}
}
#[cfg(debug_assertions)]
pub fn debug_assert_initialized(&self) {
if !matches!(*self.sync_state.lock(), SyncState::Success) {
panic!("debug assert: npm resolution must be initialized before calling this code");
}
}
pub async fn ensure_initialized(&self) -> Result<(), JsErrorBox> {
// fast exit if not pending
{
match &*self.sync_state.lock() {
SyncState::Pending(_) => {}
SyncState::Err(err) => return Err(JsErrorBox::from_err(err.clone())),
SyncState::Success => return Ok(()),
}
}
// only allow one task in here at a time
let _guard = self.queue.lock().await;
let snapshot_option = {
let mut sync_state = self.sync_state.lock();
match &mut *sync_state {
SyncState::Pending(snapshot_option) => {
// this should never panic, but if it does it means that a
// previous future was dropped while initialization occurred...
// that should never happen because this is initialized during
// startup
snapshot_option.take().unwrap()
}
// another thread updated the state while we were waiting
SyncState::Err(resolve_snapshot_error) => {
return Err(JsErrorBox::from_err(resolve_snapshot_error.clone()));
}
SyncState::Success => {
return Ok(());
}
}
};
match resolve_snapshot(&self.npm_registry_info_provider, snapshot_option)
.await
{
Ok(maybe_snapshot) => {
if let Some(snapshot) = maybe_snapshot {
self
.npm_resolution
.set_snapshot(NpmResolutionSnapshot::new(snapshot));
}
let mut sync_state = self.sync_state.lock();
*sync_state = SyncState::Success;
Ok(())
}
Err(err) => {
let mut sync_state = self.sync_state.lock();
*sync_state = SyncState::Err(err.clone());
Err(JsErrorBox::from_err(err))
}
}
}
}
pub struct CliManagedNpmResolverCreateOptions {
pub npm_cache_dir: Arc<NpmCacheDir>,
pub sys: CliSys,
pub maybe_node_modules_path: Option<PathBuf>,
pub npm_system_info: NpmSystemInfo,
pub npmrc: Arc<ResolvedNpmRc>,
pub npm_resolution: Arc<NpmResolutionCell>,
}
pub fn create_managed_npm_resolver(
options: CliManagedNpmResolverCreateOptions,
) -> Arc<dyn CliNpmResolver> {
let managed_npm_resolver =
Arc::new(ManagedNpmResolver::<CliSys>::new::<CliSys>(
&options.npm_cache_dir,
&options.npmrc,
options.npm_resolution.clone(),
options.sys.clone(),
options.maybe_node_modules_path,
));
Arc::new(ManagedCliNpmResolver::new(
managed_npm_resolver,
options.npm_cache_dir,
options.npmrc,
options.npm_resolution,
options.sys,
options.npm_system_info,
))
}
#[derive(Debug, Error, Clone, JsError)]
#[error("failed reading lockfile '{}'", lockfile_path.display())]
#[class(inherit)]
pub struct ResolveSnapshotError {
lockfile_path: PathBuf,
#[inherit]
#[source]
source: SnapshotFromLockfileError,
}
impl ResolveSnapshotError {
pub fn maybe_integrity_check_error(
&self,
) -> Option<&deno_npm::resolution::IntegrityCheckFailedError> {
match &self.source {
SnapshotFromLockfileError::SnapshotFromLockfile(
deno_npm::resolution::SnapshotFromLockfileError::IntegrityCheckFailed(
err,
),
) => Some(err),
_ => None,
}
}
}
async fn resolve_snapshot(
registry_info_provider: &Arc<CliNpmRegistryInfoProvider>,
snapshot: CliNpmResolverManagedSnapshotOption,
) -> Result<Option<ValidSerializedNpmResolutionSnapshot>, ResolveSnapshotError>
{
match snapshot {
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(lockfile) => {
if !lockfile.overwrite() {
let snapshot = snapshot_from_lockfile(
lockfile.clone(),
&registry_info_provider.as_npm_registry_api(),
)
.await
.map_err(|source| ResolveSnapshotError {
lockfile_path: lockfile.filename.clone(),
source,
})?;
Ok(Some(snapshot))
} else {
Ok(None)
}
}
CliNpmResolverManagedSnapshotOption::Specified(snapshot) => Ok(snapshot),
}
}
#[derive(Debug, Error, Clone, JsError)]
pub enum SnapshotFromLockfileError {
#[error(transparent)]
#[class(inherit)]
IncompleteError(
#[from] deno_npm::resolution::IncompleteSnapshotFromLockfileError,
),
#[error(transparent)]
#[class(inherit)]
SnapshotFromLockfile(#[from] deno_npm::resolution::SnapshotFromLockfileError),
}
async fn snapshot_from_lockfile(
lockfile: Arc<CliLockfile>,
api: &dyn NpmRegistryApi,
) -> Result<ValidSerializedNpmResolutionSnapshot, SnapshotFromLockfileError> {
let (incomplete_snapshot, skip_integrity_check) = {
let lock = lockfile.lock();
(
deno_npm::resolution::incomplete_snapshot_from_lockfile(&lock)?,
lock.overwrite,
)
};
let snapshot = deno_npm::resolution::snapshot_from_lockfile(
deno_npm::resolution::SnapshotFromLockfileParams {
incomplete_snapshot,
api,
skip_integrity_check,
},
)
.await?;
Ok(snapshot)
}
/// An npm resolver where the resolution is managed by Deno rather than
/// the user bringing their own node_modules (BYONM) on the file system.
pub struct ManagedCliNpmResolver {
managed_npm_resolver: Arc<ManagedNpmResolver<CliSys>>,
npm_cache_dir: Arc<NpmCacheDir>,
npm_rc: Arc<ResolvedNpmRc>,
sys: CliSys,
resolution: Arc<NpmResolutionCell>,
system_info: NpmSystemInfo,
}
impl std::fmt::Debug for ManagedCliNpmResolver {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ManagedCliNpmResolver")
.field("<omitted>", &"<omitted>")
.finish()
}
}
impl ManagedCliNpmResolver {
#[allow(clippy::too_many_arguments)]
pub fn new(
managed_npm_resolver: Arc<ManagedNpmResolver<CliSys>>,
npm_cache_dir: Arc<NpmCacheDir>,
npm_rc: Arc<ResolvedNpmRc>,
resolution: Arc<NpmResolutionCell>,
sys: CliSys,
system_info: NpmSystemInfo,
) -> Self {
Self {
managed_npm_resolver,
npm_cache_dir,
npm_rc,
resolution,
sys,
system_info,
}
}
pub fn resolve_pkg_folder_from_pkg_id(
&self,
pkg_id: &NpmPackageId,
) -> Result<PathBuf, ResolvePkgFolderFromPkgIdError> {
self
.managed_npm_resolver
.resolve_pkg_folder_from_pkg_id(pkg_id)
}
/// Resolves the package id from the provided specifier.
pub fn resolve_pkg_id_from_specifier(
&self,
specifier: &ModuleSpecifier,
) -> Result<Option<NpmPackageId>, ResolvePkgIdFromSpecifierError> {
self
.managed_npm_resolver
.resolve_pkg_id_from_specifier(specifier)
}
pub fn resolve_pkg_reqs_from_pkg_id(
&self,
id: &NpmPackageId,
) -> Vec<PackageReq> {
self.resolution.resolve_pkg_reqs_from_pkg_id(id)
}
pub fn all_system_packages(
&self,
system_info: &NpmSystemInfo,
) -> Vec<NpmResolutionPackage> {
self.resolution.all_system_packages(system_info)
}
/// Checks if the provided package req's folder is cached.
pub fn is_pkg_req_folder_cached(&self, req: &PackageReq) -> bool {
self
.resolve_pkg_id_from_pkg_req(req)
.ok()
.and_then(|id| {
self
.managed_npm_resolver
.resolve_pkg_folder_from_pkg_id(&id)
.ok()
})
.map(|folder| self.sys.fs_exists_no_err(folder))
.unwrap_or(false)
}
pub fn snapshot(&self) -> NpmResolutionSnapshot {
self.resolution.snapshot()
}
pub fn top_package_req_for_name(&self, name: &str) -> Option<PackageReq> {
let package_reqs = self.resolution.package_reqs();
let mut entries = package_reqs
.iter()
.filter(|(_, nv)| nv.name == name)
.collect::<Vec<_>>();
entries.sort_by_key(|(_, nv)| &nv.version);
Some(entries.last()?.0.clone())
}
pub fn serialized_valid_snapshot_for_system(
&self,
system_info: &NpmSystemInfo,
) -> ValidSerializedNpmResolutionSnapshot {
self
.resolution
.serialized_valid_snapshot_for_system(system_info)
}
pub fn resolve_pkg_folder_from_deno_module(
&self,
nv: &PackageNv,
) -> Result<PathBuf, ResolvePkgFolderFromDenoModuleError> {
self
.managed_npm_resolver
.resolve_pkg_folder_from_deno_module(nv)
}
pub fn resolve_pkg_id_from_pkg_req(
&self,
req: &PackageReq,
) -> Result<NpmPackageId, PackageReqNotFoundError> {
self.resolution.resolve_pkg_id_from_pkg_req(req)
}
pub fn maybe_node_modules_path(&self) -> Option<&Path> {
self.managed_npm_resolver.node_modules_path()
}
pub fn global_cache_root_path(&self) -> &Path {
self.npm_cache_dir.root_dir()
}
pub fn global_cache_root_url(&self) -> &Url {
self.npm_cache_dir.root_dir_url()
}
}
pub fn npm_process_state(
snapshot: ValidSerializedNpmResolutionSnapshot,
node_modules_path: Option<&Path>,
) -> String {
serde_json::to_string(&NpmProcessState {
kind: NpmProcessStateKind::Snapshot(snapshot.into_serialized()),
local_node_modules_path: node_modules_path
.map(|p| p.to_string_lossy().to_string()),
})
.unwrap()
}
impl NpmProcessStateProvider for ManagedCliNpmResolver {
fn get_npm_process_state(&self) -> String {
npm_process_state(
self.resolution.serialized_valid_snapshot(),
self.managed_npm_resolver.node_modules_path(),
)
}
}
impl CliNpmResolver for ManagedCliNpmResolver {
fn into_npm_pkg_folder_resolver(
self: Arc<Self>,
) -> Arc<dyn NpmPackageFolderResolver> {
self.managed_npm_resolver.clone()
}
fn into_process_state_provider(
self: Arc<Self>,
) -> Arc<dyn NpmProcessStateProvider> {
self
}
fn into_byonm_or_managed(
self: Arc<Self>,
) -> ByonmOrManagedNpmResolver<CliSys> {
ByonmOrManagedNpmResolver::Managed(self.managed_npm_resolver.clone())
}
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver> {
// create a new snapshotted npm resolution and resolver
let npm_resolution =
Arc::new(NpmResolutionCell::new(self.resolution.snapshot()));
Arc::new(ManagedCliNpmResolver::new(
Arc::new(ManagedNpmResolver::<CliSys>::new::<CliSys>(
&self.npm_cache_dir,
&self.npm_rc,
npm_resolution.clone(),
self.sys.clone(),
self.root_node_modules_path().map(ToOwned::to_owned),
)),
self.npm_cache_dir.clone(),
self.npm_rc.clone(),
npm_resolution,
self.sys.clone(),
self.system_info.clone(),
))
}
fn as_inner(&self) -> InnerCliNpmResolverRef {
InnerCliNpmResolverRef::Managed(self)
}
fn root_node_modules_path(&self) -> Option<&Path> {
self.managed_npm_resolver.node_modules_path()
}
fn check_state_hash(&self) -> Option<u64> {
// We could go further and check all the individual
// npm packages, but that's probably overkill.
let mut package_reqs = self
.resolution
.package_reqs()
.into_iter()
.collect::<Vec<_>>();
package_reqs.sort_by(|a, b| a.0.cmp(&b.0)); // determinism
let mut hasher = FastInsecureHasher::new_without_deno_version();
// ensure the cache gets busted when turning nodeModulesDir on or off
// as this could cause changes in resolution
hasher
.write_hashable(self.managed_npm_resolver.node_modules_path().is_some());
for (pkg_req, pkg_nv) in package_reqs {
hasher.write_hashable(&pkg_req);
hasher.write_hashable(&pkg_nv);
}
Some(hasher.finish())
}
fn resolve_pkg_folder_from_deno_module_req(
&self,
req: &PackageReq,
referrer: &Url,
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
self
.managed_npm_resolver
.resolve_pkg_folder_from_deno_module_req(req, referrer)
.map_err(ResolvePkgFolderFromDenoReqError::Managed)
}
}

View file

@ -1,55 +0,0 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use std::path::PathBuf;
use std::sync::Arc;
use deno_npm::NpmSystemInfo;
use deno_resolver::npm::managed::NpmResolution;
pub use self::common::NpmPackageFsInstaller;
use self::global::GlobalNpmPackageInstaller;
use self::local::LocalNpmPackageInstaller;
use crate::args::LifecycleScriptsConfig;
use crate::args::NpmInstallDepsProvider;
use crate::npm::CliNpmCache;
use crate::npm::CliNpmTarballCache;
use crate::sys::CliSys;
use crate::util::progress_bar::ProgressBar;
mod common;
mod global;
mod local;
#[allow(clippy::too_many_arguments)]
pub fn create_npm_fs_installer(
npm_cache: Arc<CliNpmCache>,
npm_install_deps_provider: &Arc<NpmInstallDepsProvider>,
progress_bar: &ProgressBar,
resolution: Arc<NpmResolution>,
sys: CliSys,
tarball_cache: Arc<CliNpmTarballCache>,
maybe_node_modules_path: Option<PathBuf>,
system_info: NpmSystemInfo,
lifecycle_scripts: LifecycleScriptsConfig,
) -> Arc<dyn NpmPackageFsInstaller> {
match maybe_node_modules_path {
Some(node_modules_folder) => Arc::new(LocalNpmPackageInstaller::new(
npm_cache,
npm_install_deps_provider.clone(),
progress_bar.clone(),
resolution,
sys,
tarball_cache,
node_modules_folder,
system_info,
lifecycle_scripts,
)),
None => Arc::new(GlobalNpmPackageInstaller::new(
npm_cache,
tarball_cache,
resolution,
system_info,
lifecycle_scripts,
)),
}
}

View file

@ -1,768 +0,0 @@
// Copyright 2018-2025 the Deno authors. MIT license.
use std::borrow::Cow;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use deno_ast::ModuleSpecifier;
use deno_cache_dir::npm::NpmCacheDir;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_core::url::Url;
use deno_error::JsErrorBox;
use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::registry::NpmPackageInfo;
use deno_npm::registry::NpmRegistryApi;
use deno_npm::resolution::NpmResolutionSnapshot;
use deno_npm::resolution::PackageReqNotFoundError;
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo;
use deno_npm_cache::NpmCacheSetting;
use deno_resolver::npm::managed::NpmResolution;
use deno_resolver::npm::managed::ResolvePkgFolderFromPkgIdError;
use deno_resolver::npm::ByonmOrManagedNpmResolver;
use deno_resolver::npm::ManagedNpmResolver;
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
use deno_runtime::colors;
use deno_runtime::ops::process::NpmProcessStateProvider;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
use installer::AddPkgReqsResult;
use installer::NpmResolutionInstaller;
use installers::create_npm_fs_installer;
use installers::NpmPackageFsInstaller;
use node_resolver::NpmPackageFolderResolver;
use super::CliNpmCache;
use super::CliNpmCacheHttpClient;
use super::CliNpmRegistryInfoProvider;
use super::CliNpmResolver;
use super::CliNpmTarballCache;
use super::InnerCliNpmResolverRef;
use crate::args::CliLockfile;
use crate::args::LifecycleScriptsConfig;
use crate::args::NpmInstallDepsProvider;
use crate::args::NpmProcessState;
use crate::args::NpmProcessStateKind;
use crate::args::PackageJsonDepValueParseWithLocationError;
use crate::cache::FastInsecureHasher;
use crate::sys::CliSys;
use crate::util::progress_bar::ProgressBar;
use crate::util::sync::AtomicFlag;
mod installer;
mod installers;
pub enum CliNpmResolverManagedSnapshotOption {
ResolveFromLockfile(Arc<CliLockfile>),
Specified(Option<ValidSerializedNpmResolutionSnapshot>),
}
pub struct CliManagedNpmResolverCreateOptions {
pub snapshot: CliNpmResolverManagedSnapshotOption,
pub maybe_lockfile: Option<Arc<CliLockfile>>,
pub http_client_provider: Arc<crate::http_util::HttpClientProvider>,
pub npm_cache_dir: Arc<NpmCacheDir>,
pub sys: CliSys,
pub cache_setting: deno_cache_dir::file_fetcher::CacheSetting,
pub text_only_progress_bar: crate::util::progress_bar::ProgressBar,
pub maybe_node_modules_path: Option<PathBuf>,
pub npm_system_info: NpmSystemInfo,
pub npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
pub npmrc: Arc<ResolvedNpmRc>,
pub lifecycle_scripts: LifecycleScriptsConfig,
}
pub async fn create_managed_npm_resolver_for_lsp(
options: CliManagedNpmResolverCreateOptions,
) -> Arc<dyn CliNpmResolver> {
let npm_cache = create_cache(&options);
let http_client = Arc::new(CliNpmCacheHttpClient::new(
options.http_client_provider.clone(),
options.text_only_progress_bar.clone(),
));
let npm_api = create_api(npm_cache.clone(), http_client.clone(), &options);
// spawn due to the lsp's `Send` requirement
deno_core::unsync::spawn(async move {
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(
http_client,
npm_cache,
options.npm_cache_dir,
options.npm_install_deps_provider,
npm_api,
options.sys,
options.text_only_progress_bar,
options.maybe_lockfile,
options.npmrc,
options.maybe_node_modules_path,
options.npm_system_info,
snapshot,
options.lifecycle_scripts,
)
})
.await
.unwrap()
}
pub async fn create_managed_npm_resolver(
options: CliManagedNpmResolverCreateOptions,
) -> Result<Arc<dyn CliNpmResolver>, AnyError> {
let npm_cache = create_cache(&options);
let http_client = Arc::new(CliNpmCacheHttpClient::new(
options.http_client_provider.clone(),
options.text_only_progress_bar.clone(),
));
let api = create_api(npm_cache.clone(), http_client.clone(), &options);
let snapshot = resolve_snapshot(&api, options.snapshot).await?;
Ok(create_inner(
http_client,
npm_cache,
options.npm_cache_dir,
options.npm_install_deps_provider,
api,
options.sys,
options.text_only_progress_bar,
options.maybe_lockfile,
options.npmrc,
options.maybe_node_modules_path,
options.npm_system_info,
snapshot,
options.lifecycle_scripts,
))
}
#[allow(clippy::too_many_arguments)]
fn create_inner(
http_client: Arc<CliNpmCacheHttpClient>,
npm_cache: Arc<CliNpmCache>,
npm_cache_dir: Arc<NpmCacheDir>,
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
sys: CliSys,
text_only_progress_bar: crate::util::progress_bar::ProgressBar,
maybe_lockfile: Option<Arc<CliLockfile>>,
npm_rc: Arc<ResolvedNpmRc>,
node_modules_dir_path: Option<PathBuf>,
npm_system_info: NpmSystemInfo,
snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
lifecycle_scripts: LifecycleScriptsConfig,
) -> Arc<dyn CliNpmResolver> {
let resolution = Arc::new(NpmResolution::from_serialized(snapshot));
let tarball_cache = Arc::new(CliNpmTarballCache::new(
npm_cache.clone(),
http_client,
sys.clone(),
npm_rc.clone(),
));
let fs_installer = create_npm_fs_installer(
npm_cache.clone(),
&npm_install_deps_provider,
&text_only_progress_bar,
resolution.clone(),
sys.clone(),
tarball_cache.clone(),
node_modules_dir_path.clone(),
npm_system_info.clone(),
lifecycle_scripts.clone(),
);
let managed_npm_resolver =
Arc::new(ManagedNpmResolver::<CliSys>::new::<CliSys>(
&npm_cache_dir,
&npm_rc,
resolution.clone(),
sys.clone(),
node_modules_dir_path,
));
Arc::new(ManagedCliNpmResolver::new(
fs_installer,
maybe_lockfile,
managed_npm_resolver,
npm_cache,
npm_cache_dir,
npm_install_deps_provider,
npm_rc,
registry_info_provider,
resolution,
sys,
tarball_cache,
text_only_progress_bar,
npm_system_info,
lifecycle_scripts,
))
}
fn create_cache(
options: &CliManagedNpmResolverCreateOptions,
) -> Arc<CliNpmCache> {
Arc::new(CliNpmCache::new(
options.npm_cache_dir.clone(),
options.sys.clone(),
NpmCacheSetting::from_cache_setting(&options.cache_setting),
options.npmrc.clone(),
))
}
fn create_api(
cache: Arc<CliNpmCache>,
http_client: Arc<CliNpmCacheHttpClient>,
options: &CliManagedNpmResolverCreateOptions,
) -> Arc<CliNpmRegistryInfoProvider> {
Arc::new(CliNpmRegistryInfoProvider::new(
cache,
http_client,
options.npmrc.clone(),
))
}
async fn resolve_snapshot(
registry_info_provider: &Arc<CliNpmRegistryInfoProvider>,
snapshot: CliNpmResolverManagedSnapshotOption,
) -> Result<Option<ValidSerializedNpmResolutionSnapshot>, AnyError> {
match snapshot {
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(lockfile) => {
if !lockfile.overwrite() {
let snapshot = snapshot_from_lockfile(
lockfile.clone(),
&registry_info_provider.as_npm_registry_api(),
)
.await
.with_context(|| {
format!("failed reading lockfile '{}'", lockfile.filename.display())
})?;
Ok(Some(snapshot))
} else {
Ok(None)
}
}
CliNpmResolverManagedSnapshotOption::Specified(snapshot) => Ok(snapshot),
}
}
async fn snapshot_from_lockfile(
lockfile: Arc<CliLockfile>,
api: &dyn NpmRegistryApi,
) -> Result<ValidSerializedNpmResolutionSnapshot, AnyError> {
let (incomplete_snapshot, skip_integrity_check) = {
let lock = lockfile.lock();
(
deno_npm::resolution::incomplete_snapshot_from_lockfile(&lock)?,
lock.overwrite,
)
};
let snapshot = deno_npm::resolution::snapshot_from_lockfile(
deno_npm::resolution::SnapshotFromLockfileParams {
incomplete_snapshot,
api,
skip_integrity_check,
},
)
.await?;
Ok(snapshot)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PackageCaching<'a> {
Only(Cow<'a, [PackageReq]>),
All,
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum ResolvePkgFolderFromDenoModuleError {
#[class(inherit)]
#[error(transparent)]
PackageNvNotFound(#[from] deno_npm::resolution::PackageNvNotFoundError),
#[class(inherit)]
#[error(transparent)]
ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError),
}
/// An npm resolver where the resolution is managed by Deno rather than
/// the user bringing their own node_modules (BYONM) on the file system.
pub struct ManagedCliNpmResolver {
fs_installer: Arc<dyn NpmPackageFsInstaller>,
maybe_lockfile: Option<Arc<CliLockfile>>,
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
managed_npm_resolver: Arc<ManagedNpmResolver<CliSys>>,
npm_cache: Arc<CliNpmCache>,
npm_cache_dir: Arc<NpmCacheDir>,
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
npm_rc: Arc<ResolvedNpmRc>,
sys: CliSys,
resolution: Arc<NpmResolution>,
resolution_installer: NpmResolutionInstaller,
tarball_cache: Arc<CliNpmTarballCache>,
text_only_progress_bar: ProgressBar,
npm_system_info: NpmSystemInfo,
top_level_install_flag: AtomicFlag,
lifecycle_scripts: LifecycleScriptsConfig,
}
impl std::fmt::Debug for ManagedCliNpmResolver {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ManagedCliNpmResolver")
.field("<omitted>", &"<omitted>")
.finish()
}
}
impl ManagedCliNpmResolver {
#[allow(clippy::too_many_arguments)]
pub fn new(
fs_installer: Arc<dyn NpmPackageFsInstaller>,
maybe_lockfile: Option<Arc<CliLockfile>>,
managed_npm_resolver: Arc<ManagedNpmResolver<CliSys>>,
npm_cache: Arc<CliNpmCache>,
npm_cache_dir: Arc<NpmCacheDir>,
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
npm_rc: Arc<ResolvedNpmRc>,
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
resolution: Arc<NpmResolution>,
sys: CliSys,
tarball_cache: Arc<CliNpmTarballCache>,
text_only_progress_bar: ProgressBar,
npm_system_info: NpmSystemInfo,
lifecycle_scripts: LifecycleScriptsConfig,
) -> Self {
let resolution_installer = NpmResolutionInstaller::new(
registry_info_provider.clone(),
resolution.clone(),
maybe_lockfile.clone(),
);
Self {
fs_installer,
maybe_lockfile,
managed_npm_resolver,
npm_cache,
npm_cache_dir,
npm_install_deps_provider,
npm_rc,
registry_info_provider,
text_only_progress_bar,
resolution,
resolution_installer,
sys,
tarball_cache,
npm_system_info,
top_level_install_flag: Default::default(),
lifecycle_scripts,
}
}
pub fn resolve_pkg_folder_from_pkg_id(
&self,
pkg_id: &NpmPackageId,
) -> Result<PathBuf, ResolvePkgFolderFromPkgIdError> {
self
.managed_npm_resolver
.resolve_pkg_folder_from_pkg_id(pkg_id)
}
/// Resolves the package id from the provided specifier.
pub fn resolve_pkg_id_from_specifier(
&self,
specifier: &ModuleSpecifier,
) -> Result<Option<NpmPackageId>, AnyError> {
let Some(cache_folder_id) = self
.managed_npm_resolver
.resolve_package_cache_folder_id_from_specifier(specifier)?
else {
return Ok(None);
};
Ok(Some(
self
.resolution
.resolve_pkg_id_from_pkg_cache_folder_id(&cache_folder_id)?,
))
}
pub fn resolve_pkg_reqs_from_pkg_id(
&self,
id: &NpmPackageId,
) -> Vec<PackageReq> {
self.resolution.resolve_pkg_reqs_from_pkg_id(id)
}
/// Attempts to get the package size in bytes.
pub fn package_size(
&self,
package_id: &NpmPackageId,
) -> Result<u64, AnyError> {
let package_folder = self
.managed_npm_resolver
.resolve_pkg_folder_from_pkg_id(package_id)?;
Ok(crate::util::fs::dir_size(&package_folder)?)
}
pub fn all_system_packages(
&self,
system_info: &NpmSystemInfo,
) -> Vec<NpmResolutionPackage> {
self.resolution.all_system_packages(system_info)
}
/// Checks if the provided package req's folder is cached.
pub fn is_pkg_req_folder_cached(&self, req: &PackageReq) -> bool {
self
.resolve_pkg_id_from_pkg_req(req)
.ok()
.and_then(|id| {
self
.managed_npm_resolver
.resolve_pkg_folder_from_pkg_id(&id)
.ok()
})
.map(|folder| folder.exists())
.unwrap_or(false)
}
/// Adds package requirements to the resolver and ensures everything is setup.
/// This includes setting up the `node_modules` directory, if applicable.
pub async fn add_and_cache_package_reqs(
&self,
packages: &[PackageReq],
) -> Result<(), JsErrorBox> {
self
.add_package_reqs_raw(
packages,
Some(PackageCaching::Only(packages.into())),
)
.await
.dependencies_result
}
pub async fn add_package_reqs_no_cache(
&self,
packages: &[PackageReq],
) -> Result<(), JsErrorBox> {
self
.add_package_reqs_raw(packages, None)
.await
.dependencies_result
}
pub async fn add_package_reqs(
&self,
packages: &[PackageReq],
caching: PackageCaching<'_>,
) -> Result<(), JsErrorBox> {
self
.add_package_reqs_raw(packages, Some(caching))
.await
.dependencies_result
}
pub async fn add_package_reqs_raw<'a>(
&self,
packages: &[PackageReq],
caching: Option<PackageCaching<'a>>,
) -> AddPkgReqsResult {
if packages.is_empty() {
return AddPkgReqsResult {
dependencies_result: Ok(()),
results: vec![],
};
}
let mut result = self.resolution_installer.add_package_reqs(packages).await;
if result.dependencies_result.is_ok() {
if let Some(lockfile) = self.maybe_lockfile.as_ref() {
result.dependencies_result = lockfile.error_if_changed();
}
}
if result.dependencies_result.is_ok() {
if let Some(caching) = caching {
result.dependencies_result = self.cache_packages(caching).await;
}
}
result
}
/// Sets package requirements to the resolver, removing old requirements and adding new ones.
///
/// This will retrieve and resolve package information, but not cache any package files.
pub async fn set_package_reqs(
&self,
packages: &[PackageReq],
) -> Result<(), AnyError> {
self.resolution_installer.set_package_reqs(packages).await
}
pub fn snapshot(&self) -> NpmResolutionSnapshot {
self.resolution.snapshot()
}
pub fn top_package_req_for_name(&self, name: &str) -> Option<PackageReq> {
let package_reqs = self.resolution.package_reqs();
let mut entries = package_reqs
.iter()
.filter(|(_, nv)| nv.name == name)
.collect::<Vec<_>>();
entries.sort_by_key(|(_, nv)| &nv.version);
Some(entries.last()?.0.clone())
}
pub fn serialized_valid_snapshot_for_system(
&self,
system_info: &NpmSystemInfo,
) -> ValidSerializedNpmResolutionSnapshot {
self
.resolution
.serialized_valid_snapshot_for_system(system_info)
}
pub async fn inject_synthetic_types_node_package(
&self,
) -> Result<(), JsErrorBox> {
let reqs = &[PackageReq::from_str("@types/node").unwrap()];
// add and ensure this isn't added to the lockfile
self
.add_package_reqs(reqs, PackageCaching::Only(reqs.into()))
.await?;
Ok(())
}
pub async fn cache_packages(
&self,
caching: PackageCaching<'_>,
) -> Result<(), JsErrorBox> {
self.fs_installer.cache_packages(caching).await
}
pub fn resolve_pkg_folder_from_deno_module(
&self,
nv: &PackageNv,
) -> Result<PathBuf, ResolvePkgFolderFromDenoModuleError> {
let pkg_id = self.resolution.resolve_pkg_id_from_deno_module(nv)?;
Ok(self.resolve_pkg_folder_from_pkg_id(&pkg_id)?)
}
pub fn resolve_pkg_id_from_pkg_req(
&self,
req: &PackageReq,
) -> Result<NpmPackageId, PackageReqNotFoundError> {
self.resolution.resolve_pkg_id_from_pkg_req(req)
}
pub fn ensure_no_pkg_json_dep_errors(
&self,
) -> Result<(), Box<PackageJsonDepValueParseWithLocationError>> {
for err in self.npm_install_deps_provider.pkg_json_dep_errors() {
match err.source.as_kind() {
deno_package_json::PackageJsonDepValueParseErrorKind::VersionReq(_) => {
return Err(Box::new(err.clone()));
}
deno_package_json::PackageJsonDepValueParseErrorKind::Unsupported {
..
} => {
// only warn for this one
log::warn!(
"{} {}\n at {}",
colors::yellow("Warning"),
err.source,
err.location,
)
}
}
}
Ok(())
}
/// Ensures that the top level `package.json` dependencies are installed.
/// This may set up the `node_modules` directory.
///
/// Returns `true` if the top level packages are already installed. A
/// return value of `false` means that new packages were added to the NPM resolution.
pub async fn ensure_top_level_package_json_install(
&self,
) -> Result<bool, JsErrorBox> {
if !self.top_level_install_flag.raise() {
return Ok(true); // already did this
}
let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs();
if pkg_json_remote_pkgs.is_empty() {
return Ok(true);
}
// check if something needs resolving before bothering to load all
// the package information (which is slow)
if pkg_json_remote_pkgs.iter().all(|pkg| {
self
.resolution
.resolve_pkg_id_from_pkg_req(&pkg.req)
.is_ok()
}) {
log::debug!(
"All package.json deps resolvable. Skipping top level install."
);
return Ok(true); // everything is already resolvable
}
let pkg_reqs = pkg_json_remote_pkgs
.iter()
.map(|pkg| pkg.req.clone())
.collect::<Vec<_>>();
self.add_package_reqs_no_cache(&pkg_reqs).await?;
Ok(false)
}
pub async fn cache_package_info(
&self,
package_name: &str,
) -> Result<Arc<NpmPackageInfo>, AnyError> {
// this will internally cache the package information
self
.registry_info_provider
.package_info(package_name)
.await
.map_err(|err| err.into())
}
pub fn maybe_node_modules_path(&self) -> Option<&Path> {
self.managed_npm_resolver.node_modules_path()
}
pub fn global_cache_root_path(&self) -> &Path {
self.npm_cache_dir.root_dir()
}
pub fn global_cache_root_url(&self) -> &Url {
self.npm_cache_dir.root_dir_url()
}
}
fn npm_process_state(
snapshot: ValidSerializedNpmResolutionSnapshot,
node_modules_path: Option<&Path>,
) -> String {
serde_json::to_string(&NpmProcessState {
kind: NpmProcessStateKind::Snapshot(snapshot.into_serialized()),
local_node_modules_path: node_modules_path
.map(|p| p.to_string_lossy().to_string()),
})
.unwrap()
}
impl NpmProcessStateProvider for ManagedCliNpmResolver {
fn get_npm_process_state(&self) -> String {
npm_process_state(
self.resolution.serialized_valid_snapshot(),
self.managed_npm_resolver.node_modules_path(),
)
}
}
impl CliNpmResolver for ManagedCliNpmResolver {
fn into_npm_pkg_folder_resolver(
self: Arc<Self>,
) -> Arc<dyn NpmPackageFolderResolver> {
self.managed_npm_resolver.clone()
}
fn into_process_state_provider(
self: Arc<Self>,
) -> Arc<dyn NpmProcessStateProvider> {
self
}
fn into_byonm_or_managed(
self: Arc<Self>,
) -> ByonmOrManagedNpmResolver<CliSys> {
ByonmOrManagedNpmResolver::Managed(self.managed_npm_resolver.clone())
}
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver> {
// create a new snapshotted npm resolution and resolver
let npm_resolution =
Arc::new(NpmResolution::new(self.resolution.snapshot()));
Arc::new(ManagedCliNpmResolver::new(
create_npm_fs_installer(
self.npm_cache.clone(),
&self.npm_install_deps_provider,
&self.text_only_progress_bar,
npm_resolution.clone(),
self.sys.clone(),
self.tarball_cache.clone(),
self.root_node_modules_path().map(ToOwned::to_owned),
self.npm_system_info.clone(),
self.lifecycle_scripts.clone(),
),
self.maybe_lockfile.clone(),
Arc::new(ManagedNpmResolver::<CliSys>::new::<CliSys>(
&self.npm_cache_dir,
&self.npm_rc,
npm_resolution.clone(),
self.sys.clone(),
self.root_node_modules_path().map(ToOwned::to_owned),
)),
self.npm_cache.clone(),
self.npm_cache_dir.clone(),
self.npm_install_deps_provider.clone(),
self.npm_rc.clone(),
self.registry_info_provider.clone(),
npm_resolution,
self.sys.clone(),
self.tarball_cache.clone(),
self.text_only_progress_bar.clone(),
self.npm_system_info.clone(),
self.lifecycle_scripts.clone(),
))
}
fn as_inner(&self) -> InnerCliNpmResolverRef {
InnerCliNpmResolverRef::Managed(self)
}
fn root_node_modules_path(&self) -> Option<&Path> {
self.managed_npm_resolver.node_modules_path()
}
fn check_state_hash(&self) -> Option<u64> {
// We could go further and check all the individual
// npm packages, but that's probably overkill.
let mut package_reqs = self
.resolution
.package_reqs()
.into_iter()
.collect::<Vec<_>>();
package_reqs.sort_by(|a, b| a.0.cmp(&b.0)); // determinism
let mut hasher = FastInsecureHasher::new_without_deno_version();
// ensure the cache gets busted when turning nodeModulesDir on or off
// as this could cause changes in resolution
hasher
.write_hashable(self.managed_npm_resolver.node_modules_path().is_some());
for (pkg_req, pkg_nv) in package_reqs {
hasher.write_hashable(&pkg_req);
hasher.write_hashable(&pkg_nv);
}
Some(hasher.finish())
}
fn resolve_pkg_folder_from_deno_module_req(
&self,
req: &PackageReq,
referrer: &Url,
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
self
.managed_npm_resolver
.resolve_pkg_folder_from_deno_module_req(req, referrer)
.map_err(ResolvePkgFolderFromDenoReqError::Managed)
}
}

View file

@ -1,6 +1,7 @@
// Copyright 2018-2025 the Deno authors. MIT license. // Copyright 2018-2025 the Deno authors. MIT license.
mod byonm; mod byonm;
pub mod installer;
mod managed; mod managed;
mod permission_checker; mod permission_checker;
@ -9,7 +10,6 @@ use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use dashmap::DashMap; use dashmap::DashMap;
use deno_core::error::AnyError;
use deno_core::serde_json; use deno_core::serde_json;
use deno_core::url::Url; use deno_core::url::Url;
use deno_error::JsErrorBox; use deno_error::JsErrorBox;
@ -30,8 +30,8 @@ pub use self::byonm::CliByonmNpmResolverCreateOptions;
pub use self::managed::CliManagedNpmResolverCreateOptions; pub use self::managed::CliManagedNpmResolverCreateOptions;
pub use self::managed::CliNpmResolverManagedSnapshotOption; pub use self::managed::CliNpmResolverManagedSnapshotOption;
pub use self::managed::ManagedCliNpmResolver; pub use self::managed::ManagedCliNpmResolver;
pub use self::managed::PackageCaching; pub use self::managed::NpmResolutionInitializer;
pub use self::managed::ResolvePkgFolderFromDenoModuleError; pub use self::managed::ResolveSnapshotError;
pub use self::permission_checker::NpmRegistryReadPermissionChecker; pub use self::permission_checker::NpmRegistryReadPermissionChecker;
pub use self::permission_checker::NpmRegistryReadPermissionCheckerMode; pub use self::permission_checker::NpmRegistryReadPermissionCheckerMode;
use crate::file_fetcher::CliFileFetcher; use crate::file_fetcher::CliFileFetcher;
@ -109,28 +109,16 @@ pub enum CliNpmResolverCreateOptions {
Byonm(CliByonmNpmResolverCreateOptions), Byonm(CliByonmNpmResolverCreateOptions),
} }
pub async fn create_cli_npm_resolver_for_lsp( pub fn create_cli_npm_resolver(
options: CliNpmResolverCreateOptions, options: CliNpmResolverCreateOptions,
) -> Arc<dyn CliNpmResolver> { ) -> Arc<dyn CliNpmResolver> {
use CliNpmResolverCreateOptions::*; use CliNpmResolverCreateOptions::*;
match options { match options {
Managed(options) => { Managed(options) => managed::create_managed_npm_resolver(options),
managed::create_managed_npm_resolver_for_lsp(options).await
}
Byonm(options) => Arc::new(ByonmNpmResolver::new(options)), Byonm(options) => Arc::new(ByonmNpmResolver::new(options)),
} }
} }
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(options) => Ok(Arc::new(ByonmNpmResolver::new(options))),
}
}
pub enum InnerCliNpmResolverRef<'a> { pub enum InnerCliNpmResolverRef<'a> {
Managed(&'a ManagedCliNpmResolver), Managed(&'a ManagedCliNpmResolver),
#[allow(dead_code)] #[allow(dead_code)]

View file

@ -33,8 +33,8 @@ use thiserror::Error;
use crate::args::NpmCachingStrategy; use crate::args::NpmCachingStrategy;
use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS; use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeCodeTranslator;
use crate::npm::CliNpmResolver; use crate::npm::installer::NpmInstaller;
use crate::npm::InnerCliNpmResolverRef; use crate::npm::installer::PackageCaching;
use crate::sys::CliSys; use crate::sys::CliSys;
use crate::util::sync::AtomicFlag; use crate::util::sync::AtomicFlag;
use crate::util::text_encoding::from_utf8_lossy_cow; use crate::util::text_encoding::from_utf8_lossy_cow;
@ -164,48 +164,30 @@ impl NpmModuleLoader {
} }
} }
pub struct CliResolverOptions { #[derive(Debug, Default)]
pub deno_resolver: Arc<CliDenoResolver>, pub struct FoundPackageJsonDepFlag(AtomicFlag);
pub npm_resolver: Option<Arc<dyn CliNpmResolver>>,
pub bare_node_builtins_enabled: bool,
}
/// A resolver that takes care of resolution, taking into account loaded /// A resolver that takes care of resolution, taking into account loaded
/// import map, JSX settings. /// import map, JSX settings.
#[derive(Debug)] #[derive(Debug)]
pub struct CliResolver { pub struct CliResolver {
deno_resolver: Arc<CliDenoResolver>, deno_resolver: Arc<CliDenoResolver>,
npm_resolver: Option<Arc<dyn CliNpmResolver>>, found_package_json_dep_flag: Arc<FoundPackageJsonDepFlag>,
found_package_json_dep_flag: AtomicFlag,
bare_node_builtins_enabled: bool,
warned_pkgs: DashSet<PackageReq>, warned_pkgs: DashSet<PackageReq>,
} }
impl CliResolver { impl CliResolver {
pub fn new(options: CliResolverOptions) -> Self { pub fn new(
deno_resolver: Arc<CliDenoResolver>,
found_package_json_dep_flag: Arc<FoundPackageJsonDepFlag>,
) -> Self {
Self { Self {
deno_resolver: options.deno_resolver, deno_resolver,
npm_resolver: options.npm_resolver, found_package_json_dep_flag,
found_package_json_dep_flag: Default::default(),
bare_node_builtins_enabled: options.bare_node_builtins_enabled,
warned_pkgs: Default::default(), warned_pkgs: Default::default(),
} }
} }
// todo(dsherret): move this off CliResolver as CliResolver is acting
// like a factory by doing this (it's beyond its responsibility)
pub fn create_graph_npm_resolver(
&self,
npm_caching: NpmCachingStrategy,
) -> WorkerCliNpmGraphResolver {
WorkerCliNpmGraphResolver {
npm_resolver: self.npm_resolver.as_ref(),
found_package_json_dep_flag: &self.found_package_json_dep_flag,
bare_node_builtins_enabled: self.bare_node_builtins_enabled,
npm_caching,
}
}
pub fn resolve( pub fn resolve(
&self, &self,
raw_specifier: &str, raw_specifier: &str,
@ -233,7 +215,7 @@ impl CliResolver {
if resolution.found_package_json_dep { if resolution.found_package_json_dep {
// mark that we need to do an "npm install" later // mark that we need to do an "npm install" later
self.found_package_json_dep_flag.raise(); self.found_package_json_dep_flag.0.raise();
} }
if let Some(diagnostic) = resolution.maybe_diagnostic { if let Some(diagnostic) = resolution.maybe_diagnostic {
@ -260,15 +242,31 @@ impl CliResolver {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct WorkerCliNpmGraphResolver<'a> { pub struct CliNpmGraphResolver {
npm_resolver: Option<&'a Arc<dyn CliNpmResolver>>, npm_installer: Option<Arc<NpmInstaller>>,
found_package_json_dep_flag: &'a AtomicFlag, found_package_json_dep_flag: Arc<FoundPackageJsonDepFlag>,
bare_node_builtins_enabled: bool, bare_node_builtins_enabled: bool,
npm_caching: NpmCachingStrategy, npm_caching: NpmCachingStrategy,
} }
impl CliNpmGraphResolver {
pub fn new(
npm_installer: Option<Arc<NpmInstaller>>,
found_package_json_dep_flag: Arc<FoundPackageJsonDepFlag>,
bare_node_builtins_enabled: bool,
npm_caching: NpmCachingStrategy,
) -> Self {
Self {
npm_installer,
found_package_json_dep_flag,
bare_node_builtins_enabled,
npm_caching,
}
}
}
#[async_trait(?Send)] #[async_trait(?Send)]
impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> { impl deno_graph::source::NpmResolver for CliNpmGraphResolver {
fn resolve_builtin_node_module( fn resolve_builtin_node_module(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
@ -298,17 +296,12 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> {
} }
fn load_and_cache_npm_package_info(&self, package_name: &str) { fn load_and_cache_npm_package_info(&self, package_name: &str) {
match self.npm_resolver { if let Some(npm_installer) = &self.npm_installer {
Some(npm_resolver) if npm_resolver.as_managed().is_some() => { let npm_installer = npm_installer.clone();
let npm_resolver = npm_resolver.clone(); let package_name = package_name.to_string();
let package_name = package_name.to_string(); deno_core::unsync::spawn(async move {
deno_core::unsync::spawn(async move { let _ignore = npm_installer.cache_package_info(&package_name).await;
if let Some(managed) = npm_resolver.as_managed() { });
let _ignore = managed.cache_package_info(&package_name).await;
}
});
}
_ => {}
} }
} }
@ -316,17 +309,11 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> {
&self, &self,
package_reqs: &[PackageReq], package_reqs: &[PackageReq],
) -> NpmResolvePkgReqsResult { ) -> NpmResolvePkgReqsResult {
match &self.npm_resolver { match &self.npm_installer {
Some(npm_resolver) => { Some(npm_installer) => {
let npm_resolver = match npm_resolver.as_inner() { let top_level_result = if self.found_package_json_dep_flag.0.is_raised()
InnerCliNpmResolverRef::Managed(npm_resolver) => npm_resolver, {
// if we are using byonm, then this should never be called because npm_installer
// we don't use deno_graph's npm resolution in this case
InnerCliNpmResolverRef::Byonm(_) => unreachable!(),
};
let top_level_result = if self.found_package_json_dep_flag.is_raised() {
npm_resolver
.ensure_top_level_package_json_install() .ensure_top_level_package_json_install()
.await .await
.map(|_| ()) .map(|_| ())
@ -334,15 +321,13 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> {
Ok(()) Ok(())
}; };
let result = npm_resolver let result = npm_installer
.add_package_reqs_raw( .add_package_reqs_raw(
package_reqs, package_reqs,
match self.npm_caching { match self.npm_caching {
NpmCachingStrategy::Eager => { NpmCachingStrategy::Eager => Some(PackageCaching::All),
Some(crate::npm::PackageCaching::All)
}
NpmCachingStrategy::Lazy => { NpmCachingStrategy::Lazy => {
Some(crate::npm::PackageCaching::Only(package_reqs.into())) Some(PackageCaching::Only(package_reqs.into()))
} }
NpmCachingStrategy::Manual => None, NpmCachingStrategy::Manual => None,
}, },

View file

@ -37,10 +37,12 @@ use deno_core::ResolutionKind;
use deno_core::SourceCodeCacheInfo; use deno_core::SourceCodeCacheInfo;
use deno_error::JsErrorBox; use deno_error::JsErrorBox;
use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::resolution::NpmResolutionSnapshot;
use deno_package_json::PackageJsonDepValue; use deno_package_json::PackageJsonDepValue;
use deno_resolver::cjs::IsCjsResolutionMode; use deno_resolver::cjs::IsCjsResolutionMode;
use deno_resolver::npm::create_in_npm_pkg_checker; use deno_resolver::npm::create_in_npm_pkg_checker;
use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions; use deno_resolver::npm::managed::ManagedInNpmPkgCheckerCreateOptions;
use deno_resolver::npm::managed::NpmResolutionCell;
use deno_resolver::npm::CreateInNpmPkgCheckerOptions; use deno_resolver::npm::CreateInNpmPkgCheckerOptions;
use deno_resolver::npm::NpmReqResolverOptions; use deno_resolver::npm::NpmReqResolverOptions;
use deno_runtime::deno_fs; use deno_runtime::deno_fs;
@ -91,6 +93,7 @@ use crate::npm::CliNpmResolverCreateOptions;
use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CliNpmResolverManagedSnapshotOption;
use crate::npm::NpmRegistryReadPermissionChecker; use crate::npm::NpmRegistryReadPermissionChecker;
use crate::npm::NpmRegistryReadPermissionCheckerMode; use crate::npm::NpmRegistryReadPermissionCheckerMode;
use crate::npm::NpmResolutionInitializer;
use crate::resolver::CjsTracker; use crate::resolver::CjsTracker;
use crate::resolver::CliNpmReqResolver; use crate::resolver::CliNpmReqResolver;
use crate::resolver::NpmModuleLoader; use crate::resolver::NpmModuleLoader;
@ -687,18 +690,12 @@ pub async fn run(
ca_data: metadata.ca_data.map(CaData::Bytes), ca_data: metadata.ca_data.map(CaData::Bytes),
cell: Default::default(), cell: Default::default(),
}); });
let progress_bar = ProgressBar::new(ProgressBarStyle::TextOnly);
let http_client_provider = Arc::new(HttpClientProvider::new(
Some(root_cert_store_provider.clone()),
metadata.unsafely_ignore_certificate_errors.clone(),
));
// use a dummy npm registry url // use a dummy npm registry url
let npm_registry_url = ModuleSpecifier::parse("https://localhost/").unwrap(); let npm_registry_url = ModuleSpecifier::parse("https://localhost/").unwrap();
let root_dir_url = let root_dir_url =
Arc::new(ModuleSpecifier::from_directory_path(&root_path).unwrap()); Arc::new(ModuleSpecifier::from_directory_path(&root_path).unwrap());
let main_module = root_dir_url.join(&metadata.entrypoint_key).unwrap(); let main_module = root_dir_url.join(&metadata.entrypoint_key).unwrap();
let npm_global_cache_dir = root_path.join(".deno_compile_node_modules"); let npm_global_cache_dir = root_path.join(".deno_compile_node_modules");
let cache_setting = CacheSetting::Only;
let pkg_json_resolver = Arc::new(CliPackageJsonResolver::new(sys.clone())); let pkg_json_resolver = Arc::new(CliPackageJsonResolver::new(sys.clone()));
let npm_registry_permission_checker = { let npm_registry_permission_checker = {
let mode = match &metadata.node_modules { let mode = match &metadata.node_modules {
@ -743,29 +740,19 @@ pub async fn run(
maybe_node_modules_path: maybe_node_modules_path.as_deref(), maybe_node_modules_path: maybe_node_modules_path.as_deref(),
}, },
)); ));
let npm_resolution =
Arc::new(NpmResolutionCell::new(NpmResolutionSnapshot::new(snapshot)));
let npm_resolver = let npm_resolver =
create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed( create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
CliManagedNpmResolverCreateOptions { CliManagedNpmResolverCreateOptions {
snapshot: CliNpmResolverManagedSnapshotOption::Specified(Some( npm_resolution,
snapshot,
)),
maybe_lockfile: None,
http_client_provider: http_client_provider.clone(),
npm_cache_dir, npm_cache_dir,
npm_install_deps_provider: Arc::new(
// this is only used for installing packages, which isn't necessary with deno compile
NpmInstallDepsProvider::empty(),
),
sys: sys.clone(), sys: sys.clone(),
text_only_progress_bar: progress_bar,
cache_setting,
maybe_node_modules_path, maybe_node_modules_path,
npm_system_info: Default::default(), npm_system_info: Default::default(),
npmrc, npmrc,
lifecycle_scripts: Default::default(),
}, },
)) ));
.await?;
(in_npm_pkg_checker, npm_resolver) (in_npm_pkg_checker, npm_resolver)
} }
Some(binary::NodeModules::Byonm { Some(binary::NodeModules::Byonm {
@ -781,8 +768,7 @@ pub async fn run(
pkg_json_resolver: pkg_json_resolver.clone(), pkg_json_resolver: pkg_json_resolver.clone(),
root_node_modules_dir, root_node_modules_dir,
}), }),
) );
.await?;
(in_npm_pkg_checker, npm_resolver) (in_npm_pkg_checker, npm_resolver)
} }
None => { None => {
@ -801,27 +787,18 @@ pub async fn run(
maybe_node_modules_path: None, maybe_node_modules_path: None,
}, },
)); ));
let npm_resolution = Arc::new(NpmResolutionCell::default());
let npm_resolver = let npm_resolver =
create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed( create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
CliManagedNpmResolverCreateOptions { CliManagedNpmResolverCreateOptions {
snapshot: CliNpmResolverManagedSnapshotOption::Specified(None), npm_resolution,
http_client_provider: http_client_provider.clone(),
npm_install_deps_provider: Arc::new(
// this is only used for installing packages, which isn't necessary with deno compile
NpmInstallDepsProvider::empty(),
),
sys: sys.clone(), sys: sys.clone(),
cache_setting,
text_only_progress_bar: progress_bar,
npm_cache_dir, npm_cache_dir,
maybe_lockfile: None,
maybe_node_modules_path: None, maybe_node_modules_path: None,
npm_system_info: Default::default(), npm_system_info: Default::default(),
npmrc: create_default_npmrc(), npmrc: create_default_npmrc(),
lifecycle_scripts: Default::default(),
}, },
)) ));
.await?;
(in_npm_pkg_checker, npm_resolver) (in_npm_pkg_checker, npm_resolver)
} }
}; };
@ -995,6 +972,7 @@ pub async fn run(
None, None,
Box::new(module_loader_factory), Box::new(module_loader_factory),
node_resolver, node_resolver,
None,
npm_resolver, npm_resolver,
pkg_json_resolver, pkg_json_resolver,
root_cert_store_provider, root_cert_store_provider,

View file

@ -34,6 +34,7 @@ use crate::graph_util::maybe_additional_sloppy_imports_message;
use crate::graph_util::BuildFastCheckGraphOptions; use crate::graph_util::BuildFastCheckGraphOptions;
use crate::graph_util::ModuleGraphBuilder; use crate::graph_util::ModuleGraphBuilder;
use crate::node::CliNodeResolver; use crate::node::CliNodeResolver;
use crate::npm::installer::NpmInstaller;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::sys::CliSys; use crate::sys::CliSys;
use crate::tsc; use crate::tsc;
@ -109,6 +110,7 @@ pub struct TypeChecker {
cjs_tracker: Arc<TypeCheckingCjsTracker>, cjs_tracker: Arc<TypeCheckingCjsTracker>,
cli_options: Arc<CliOptions>, cli_options: Arc<CliOptions>,
module_graph_builder: Arc<ModuleGraphBuilder>, module_graph_builder: Arc<ModuleGraphBuilder>,
npm_installer: Option<Arc<NpmInstaller>>,
node_resolver: Arc<CliNodeResolver>, node_resolver: Arc<CliNodeResolver>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_resolver: Arc<dyn CliNpmResolver>,
sys: CliSys, sys: CliSys,
@ -136,12 +138,14 @@ pub enum CheckError {
} }
impl TypeChecker { impl TypeChecker {
#[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
caches: Arc<Caches>, caches: Arc<Caches>,
cjs_tracker: Arc<TypeCheckingCjsTracker>, cjs_tracker: Arc<TypeCheckingCjsTracker>,
cli_options: Arc<CliOptions>, cli_options: Arc<CliOptions>,
module_graph_builder: Arc<ModuleGraphBuilder>, module_graph_builder: Arc<ModuleGraphBuilder>,
node_resolver: Arc<CliNodeResolver>, node_resolver: Arc<CliNodeResolver>,
npm_installer: Option<Arc<NpmInstaller>>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_resolver: Arc<dyn CliNpmResolver>,
sys: CliSys, sys: CliSys,
) -> Self { ) -> Self {
@ -151,6 +155,7 @@ impl TypeChecker {
cli_options, cli_options,
module_graph_builder, module_graph_builder,
node_resolver, node_resolver,
npm_installer,
npm_resolver, npm_resolver,
sys, sys,
} }
@ -191,9 +196,9 @@ impl TypeChecker {
// node built-in specifiers use the @types/node package to determine // node built-in specifiers use the @types/node package to determine
// types, so inject that now (the caller should do this after the lockfile // types, so inject that now (the caller should do this after the lockfile
// has been written) // has been written)
if let Some(npm_resolver) = self.npm_resolver.as_managed() { if let Some(npm_installer) = &self.npm_installer {
if graph.has_node_specifier { if graph.has_node_specifier {
npm_resolver.inject_synthetic_types_node_package().await?; npm_installer.inject_synthetic_types_node_package().await?;
} }
} }

View file

@ -386,8 +386,11 @@ impl NpmInfo {
npm_snapshot: &'a NpmResolutionSnapshot, npm_snapshot: &'a NpmResolutionSnapshot,
) { ) {
self.packages.insert(package.id.clone(), package.clone()); self.packages.insert(package.id.clone(), package.clone());
if let Ok(size) = npm_resolver.package_size(&package.id) { if let Ok(folder) = npm_resolver.resolve_pkg_folder_from_pkg_id(&package.id)
self.package_sizes.insert(package.id.clone(), size); {
if let Ok(size) = crate::util::fs::dir_size(&folder) {
self.package_sizes.insert(package.id.clone(), size);
}
} }
for id in package.dependencies.values() { for id in package.dependencies.values() {
if !self.packages.contains_key(id) { if !self.packages.contains_key(id) {

View file

@ -300,8 +300,8 @@ async fn install_local(
InstallFlagsLocal::TopLevel => { InstallFlagsLocal::TopLevel => {
let factory = CliFactory::from_flags(flags); let factory = CliFactory::from_flags(flags);
// surface any errors in the package.json // surface any errors in the package.json
if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() { if let Some(npm_installer) = factory.npm_installer_if_managed()? {
npm_resolver.ensure_no_pkg_json_dep_errors()?; npm_installer.ensure_no_pkg_json_dep_errors()?;
} }
crate::tools::registry::cache_top_level_deps(&factory, None).await?; crate::tools::registry::cache_top_level_deps(&factory, None).await?;

View file

@ -67,7 +67,7 @@ pub async fn kernel(
// TODO(bartlomieju): should we run with all permissions? // TODO(bartlomieju): should we run with all permissions?
let permissions = let permissions =
PermissionsContainer::allow_all(factory.permission_desc_parser()?.clone()); PermissionsContainer::allow_all(factory.permission_desc_parser()?.clone());
let npm_resolver = factory.npm_resolver().await?.clone(); let npm_installer = factory.npm_installer_if_managed()?.cloned();
let resolver = factory.resolver().await?.clone(); let resolver = factory.resolver().await?.clone();
let worker_factory = factory.create_cli_main_worker_factory().await?; let worker_factory = factory.create_cli_main_worker_factory().await?;
let (stdio_tx, stdio_rx) = mpsc::unbounded_channel(); let (stdio_tx, stdio_rx) = mpsc::unbounded_channel();
@ -115,7 +115,7 @@ pub async fn kernel(
let worker = worker.into_main_worker(); let worker = worker.into_main_worker();
let mut repl_session = repl::ReplSession::initialize( let mut repl_session = repl::ReplSession::initialize(
cli_options, cli_options,
npm_resolver, npm_installer,
resolver, resolver,
worker, worker,
main_module, main_module,

View file

@ -861,10 +861,8 @@ async fn npm_install_after_modification(
// make a new CliFactory to pick up the updated config file // make a new CliFactory to pick up the updated config file
let cli_factory = CliFactory::from_flags(flags); let cli_factory = CliFactory::from_flags(flags);
// surface any errors in the package.json // surface any errors in the package.json
let npm_resolver = cli_factory.npm_resolver().await?; let npm_installer = cli_factory.npm_installer()?;
if let Some(npm_resolver) = npm_resolver.as_managed() { npm_installer.ensure_no_pkg_json_dep_errors()?;
npm_resolver.ensure_no_pkg_json_dep_errors()?;
}
// npm install // npm install
cache_deps::cache_top_level_deps(&cli_factory, jsr_resolver).await?; cache_deps::cache_top_level_deps(&cli_factory, jsr_resolver).await?;

View file

@ -12,16 +12,19 @@ use crate::factory::CliFactory;
use crate::graph_container::ModuleGraphContainer; use crate::graph_container::ModuleGraphContainer;
use crate::graph_container::ModuleGraphUpdatePermit; use crate::graph_container::ModuleGraphUpdatePermit;
use crate::graph_util::CreateGraphOptions; use crate::graph_util::CreateGraphOptions;
use crate::npm::installer::PackageCaching;
pub async fn cache_top_level_deps( pub async fn cache_top_level_deps(
// todo(dsherret): don't pass the factory into this function. Instead use ctor deps // todo(dsherret): don't pass the factory into this function. Instead use ctor deps
factory: &CliFactory, factory: &CliFactory,
jsr_resolver: Option<Arc<crate::jsr::JsrFetchResolver>>, jsr_resolver: Option<Arc<crate::jsr::JsrFetchResolver>>,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let npm_resolver = factory.npm_resolver().await?; let npm_installer = factory.npm_installer_if_managed()?;
let cli_options = factory.cli_options()?; let cli_options = factory.cli_options()?;
if let Some(npm_resolver) = npm_resolver.as_managed() { if let Some(npm_installer) = &npm_installer {
npm_resolver.ensure_top_level_package_json_install().await?; npm_installer
.ensure_top_level_package_json_install()
.await?;
if let Some(lockfile) = cli_options.maybe_lockfile() { if let Some(lockfile) = cli_options.maybe_lockfile() {
lockfile.error_if_changed()?; lockfile.error_if_changed()?;
} }
@ -138,10 +141,8 @@ pub async fn cache_top_level_deps(
maybe_graph_error = graph_builder.graph_roots_valid(graph, &roots); maybe_graph_error = graph_builder.graph_roots_valid(graph, &roots);
} }
if let Some(npm_resolver) = npm_resolver.as_managed() { if let Some(npm_installer) = &npm_installer {
npm_resolver npm_installer.cache_packages(PackageCaching::All).await?;
.cache_packages(crate::npm::PackageCaching::All)
.await?;
} }
maybe_graph_error?; maybe_graph_error?;

View file

@ -42,6 +42,7 @@ use crate::graph_container::ModuleGraphContainer;
use crate::graph_container::ModuleGraphUpdatePermit; use crate::graph_container::ModuleGraphUpdatePermit;
use crate::jsr::JsrFetchResolver; use crate::jsr::JsrFetchResolver;
use crate::module_loader::ModuleLoadPreparer; use crate::module_loader::ModuleLoadPreparer;
use crate::npm::installer::NpmInstaller;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::npm::NpmFetchResolver; use crate::npm::NpmFetchResolver;
use crate::util::sync::AtomicFlag; use crate::util::sync::AtomicFlag;
@ -451,6 +452,7 @@ pub struct DepManager {
pub(crate) jsr_fetch_resolver: Arc<JsrFetchResolver>, pub(crate) jsr_fetch_resolver: Arc<JsrFetchResolver>,
pub(crate) npm_fetch_resolver: Arc<NpmFetchResolver>, pub(crate) npm_fetch_resolver: Arc<NpmFetchResolver>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_resolver: Arc<dyn CliNpmResolver>,
npm_installer: Arc<NpmInstaller>,
permissions_container: PermissionsContainer, permissions_container: PermissionsContainer,
main_module_graph_container: Arc<MainModuleGraphContainer>, main_module_graph_container: Arc<MainModuleGraphContainer>,
lockfile: Option<Arc<CliLockfile>>, lockfile: Option<Arc<CliLockfile>>,
@ -460,6 +462,7 @@ pub struct DepManagerArgs {
pub module_load_preparer: Arc<ModuleLoadPreparer>, pub module_load_preparer: Arc<ModuleLoadPreparer>,
pub jsr_fetch_resolver: Arc<JsrFetchResolver>, pub jsr_fetch_resolver: Arc<JsrFetchResolver>,
pub npm_fetch_resolver: Arc<NpmFetchResolver>, pub npm_fetch_resolver: Arc<NpmFetchResolver>,
pub npm_installer: Arc<NpmInstaller>,
pub npm_resolver: Arc<dyn CliNpmResolver>, pub npm_resolver: Arc<dyn CliNpmResolver>,
pub permissions_container: PermissionsContainer, pub permissions_container: PermissionsContainer,
pub main_module_graph_container: Arc<MainModuleGraphContainer>, pub main_module_graph_container: Arc<MainModuleGraphContainer>,
@ -477,6 +480,7 @@ impl DepManager {
module_load_preparer, module_load_preparer,
jsr_fetch_resolver, jsr_fetch_resolver,
npm_fetch_resolver, npm_fetch_resolver,
npm_installer,
npm_resolver, npm_resolver,
permissions_container, permissions_container,
main_module_graph_container, main_module_graph_container,
@ -490,6 +494,7 @@ impl DepManager {
dependencies_resolved: AtomicFlag::lowered(), dependencies_resolved: AtomicFlag::lowered(),
module_load_preparer, module_load_preparer,
npm_fetch_resolver, npm_fetch_resolver,
npm_installer,
npm_resolver, npm_resolver,
permissions_container, permissions_container,
main_module_graph_container, main_module_graph_container,
@ -556,7 +561,10 @@ impl DepManager {
return Ok(()); return Ok(());
} }
npm_resolver.ensure_top_level_package_json_install().await?; self
.npm_installer
.ensure_top_level_package_json_install()
.await?;
let mut roots = Vec::new(); let mut roots = Vec::new();
let mut info_futures = FuturesUnordered::new(); let mut info_futures = FuturesUnordered::new();
for dep in &self.deps { for dep in &self.deps {

View file

@ -445,6 +445,7 @@ async fn dep_manager_args(
jsr_fetch_resolver, jsr_fetch_resolver,
npm_fetch_resolver, npm_fetch_resolver,
npm_resolver: factory.npm_resolver().await?.clone(), npm_resolver: factory.npm_resolver().await?.clone(),
npm_installer: factory.npm_installer()?.clone(),
permissions_container: factory.root_permissions_container()?.clone(), permissions_container: factory.root_permissions_container()?.clone(),
main_module_graph_container: factory main_module_graph_container: factory
.main_module_graph_container() .main_module_graph_container()

View file

@ -164,7 +164,7 @@ pub async fn run(
let cli_options = factory.cli_options()?; let cli_options = factory.cli_options()?;
let main_module = cli_options.resolve_main_module()?; let main_module = cli_options.resolve_main_module()?;
let permissions = factory.root_permissions_container()?; let permissions = factory.root_permissions_container()?;
let npm_resolver = factory.npm_resolver().await?.clone(); let npm_installer = factory.npm_installer_if_managed()?.cloned();
let resolver = factory.resolver().await?.clone(); let resolver = factory.resolver().await?.clone();
let file_fetcher = factory.file_fetcher()?; let file_fetcher = factory.file_fetcher()?;
let worker_factory = factory.create_cli_main_worker_factory().await?; let worker_factory = factory.create_cli_main_worker_factory().await?;
@ -187,7 +187,7 @@ pub async fn run(
let worker = worker.into_main_worker(); let worker = worker.into_main_worker();
let session = ReplSession::initialize( let session = ReplSession::initialize(
cli_options, cli_options,
npm_resolver, npm_installer,
resolver, resolver,
worker, worker,
main_module.clone(), main_module.clone(),

View file

@ -45,7 +45,7 @@ use crate::args::CliOptions;
use crate::cdp; use crate::cdp;
use crate::colors; use crate::colors;
use crate::lsp::ReplLanguageServer; use crate::lsp::ReplLanguageServer;
use crate::npm::CliNpmResolver; use crate::npm::installer::NpmInstaller;
use crate::resolver::CliResolver; use crate::resolver::CliResolver;
use crate::tools::test::report_tests; use crate::tools::test::report_tests;
use crate::tools::test::reporters::PrettyTestReporter; use crate::tools::test::reporters::PrettyTestReporter;
@ -181,7 +181,7 @@ struct ReplJsxState {
} }
pub struct ReplSession { pub struct ReplSession {
npm_resolver: Arc<dyn CliNpmResolver>, npm_installer: Option<Arc<NpmInstaller>>,
resolver: Arc<CliResolver>, resolver: Arc<CliResolver>,
pub worker: MainWorker, pub worker: MainWorker,
session: LocalInspectorSession, session: LocalInspectorSession,
@ -200,7 +200,7 @@ pub struct ReplSession {
impl ReplSession { impl ReplSession {
pub async fn initialize( pub async fn initialize(
cli_options: &CliOptions, cli_options: &CliOptions,
npm_resolver: Arc<dyn CliNpmResolver>, npm_installer: Option<Arc<NpmInstaller>>,
resolver: Arc<CliResolver>, resolver: Arc<CliResolver>,
mut worker: MainWorker, mut worker: MainWorker,
main_module: ModuleSpecifier, main_module: ModuleSpecifier,
@ -265,7 +265,7 @@ impl ReplSession {
)?; )?;
let experimental_decorators = transpile_options.use_ts_decorators; let experimental_decorators = transpile_options.use_ts_decorators;
let mut repl_session = ReplSession { let mut repl_session = ReplSession {
npm_resolver, npm_installer,
resolver, resolver,
worker, worker,
session, session,
@ -704,8 +704,8 @@ impl ReplSession {
&mut self, &mut self,
program: &swc_ast::Program, program: &swc_ast::Program,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let Some(npm_resolver) = self.npm_resolver.as_managed() else { let Some(npm_installer) = &self.npm_installer else {
return Ok(()); // don't auto-install for byonm return Ok(());
}; };
let mut collector = ImportCollector::new(); let mut collector = ImportCollector::new();
@ -737,13 +737,13 @@ impl ReplSession {
let has_node_specifier = let has_node_specifier =
resolved_imports.iter().any(|url| url.scheme() == "node"); resolved_imports.iter().any(|url| url.scheme() == "node");
if !npm_imports.is_empty() || has_node_specifier { if !npm_imports.is_empty() || has_node_specifier {
npm_resolver npm_installer
.add_and_cache_package_reqs(&npm_imports) .add_and_cache_package_reqs(&npm_imports)
.await?; .await?;
// prevent messages in the repl about @types/node not being cached // prevent messages in the repl about @types/node not being cached
if has_node_specifier { if has_node_specifier {
npm_resolver.inject_synthetic_types_node_package().await?; npm_installer.inject_synthetic_types_node_package().await?;
} }
} }
Ok(()) Ok(())

View file

@ -12,6 +12,7 @@ use crate::args::EvalFlags;
use crate::args::Flags; use crate::args::Flags;
use crate::args::WatchFlagsWithPaths; use crate::args::WatchFlagsWithPaths;
use crate::factory::CliFactory; use crate::factory::CliFactory;
use crate::npm::installer::PackageCaching;
use crate::util; use crate::util;
use crate::util::file_watcher::WatcherRestartMode; use crate::util::file_watcher::WatcherRestartMode;
@ -202,18 +203,17 @@ pub async fn maybe_npm_install(factory: &CliFactory) -> Result<(), AnyError> {
// ensure an "npm install" is done if the user has explicitly // ensure an "npm install" is done if the user has explicitly
// opted into using a managed node_modules directory // opted into using a managed node_modules directory
if cli_options.node_modules_dir()? == Some(NodeModulesDirMode::Auto) { if cli_options.node_modules_dir()? == Some(NodeModulesDirMode::Auto) {
if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() { if let Some(npm_installer) = factory.npm_installer_if_managed()? {
let already_done = let already_done = npm_installer
npm_resolver.ensure_top_level_package_json_install().await?; .ensure_top_level_package_json_install()
.await?;
if !already_done if !already_done
&& matches!( && matches!(
cli_options.default_npm_caching_strategy(), cli_options.default_npm_caching_strategy(),
crate::graph_util::NpmCachingStrategy::Eager crate::graph_util::NpmCachingStrategy::Eager
) )
{ {
npm_resolver npm_installer.cache_packages(PackageCaching::All).await?;
.cache_packages(crate::npm::PackageCaching::All)
.await?;
} }
} }
} }

View file

@ -36,6 +36,8 @@ use crate::args::TaskFlags;
use crate::colors; use crate::colors;
use crate::factory::CliFactory; use crate::factory::CliFactory;
use crate::node::CliNodeResolver; use crate::node::CliNodeResolver;
use crate::npm::installer::NpmInstaller;
use crate::npm::installer::PackageCaching;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::task_runner; use crate::task_runner;
use crate::task_runner::run_future_forwarding_signals; use crate::task_runner::run_future_forwarding_signals;
@ -203,6 +205,7 @@ pub async fn execute_script(
}] }]
}; };
let npm_installer = factory.npm_installer_if_managed()?;
let npm_resolver = factory.npm_resolver().await?; let npm_resolver = factory.npm_resolver().await?;
let node_resolver = factory.node_resolver().await?; let node_resolver = factory.node_resolver().await?;
let env_vars = task_runner::real_env_vars(); let env_vars = task_runner::real_env_vars();
@ -216,6 +219,7 @@ pub async fn execute_script(
let task_runner = TaskRunner { let task_runner = TaskRunner {
task_flags: &task_flags, task_flags: &task_flags,
npm_installer: npm_installer.map(|n| n.as_ref()),
npm_resolver: npm_resolver.as_ref(), npm_resolver: npm_resolver.as_ref(),
node_resolver: node_resolver.as_ref(), node_resolver: node_resolver.as_ref(),
env_vars, env_vars,
@ -266,6 +270,7 @@ struct RunSingleOptions<'a> {
struct TaskRunner<'a> { struct TaskRunner<'a> {
task_flags: &'a TaskFlags, task_flags: &'a TaskFlags,
npm_installer: Option<&'a NpmInstaller>,
npm_resolver: &'a dyn CliNpmResolver, npm_resolver: &'a dyn CliNpmResolver,
node_resolver: &'a CliNodeResolver, node_resolver: &'a CliNodeResolver,
env_vars: HashMap<String, String>, env_vars: HashMap<String, String>,
@ -458,11 +463,11 @@ impl<'a> TaskRunner<'a> {
return Ok(0); return Ok(0);
}; };
if let Some(npm_resolver) = self.npm_resolver.as_managed() { if let Some(npm_installer) = self.npm_installer {
npm_resolver.ensure_top_level_package_json_install().await?; npm_installer
npm_resolver .ensure_top_level_package_json_install()
.cache_packages(crate::npm::PackageCaching::All)
.await?; .await?;
npm_installer.cache_packages(PackageCaching::All).await?;
} }
let cwd = match &self.task_flags.cwd { let cwd = match &self.task_flags.cwd {
@ -497,11 +502,11 @@ impl<'a> TaskRunner<'a> {
argv: &[String], argv: &[String],
) -> Result<i32, deno_core::anyhow::Error> { ) -> Result<i32, deno_core::anyhow::Error> {
// ensure the npm packages are installed if using a managed resolver // ensure the npm packages are installed if using a managed resolver
if let Some(npm_resolver) = self.npm_resolver.as_managed() { if let Some(npm_installer) = self.npm_installer {
npm_resolver.ensure_top_level_package_json_install().await?; npm_installer
npm_resolver .ensure_top_level_package_json_install()
.cache_packages(crate::npm::PackageCaching::All)
.await?; .await?;
npm_installer.cache_packages(PackageCaching::All).await?;
} }
let cwd = match &self.task_flags.cwd { let cwd = match &self.task_flags.cwd {

View file

@ -28,6 +28,7 @@ use deno_graph::GraphKind;
use deno_graph::Module; use deno_graph::Module;
use deno_graph::ModuleGraph; use deno_graph::ModuleGraph;
use deno_graph::ResolutionResolved; use deno_graph::ResolutionResolved;
use deno_resolver::npm::managed::ResolvePkgFolderFromDenoModuleError;
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError; use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
use node_resolver::errors::NodeJsErrorCode; use node_resolver::errors::NodeJsErrorCode;
@ -709,9 +710,7 @@ pub enum ResolveError {
PackageSubpathResolve(PackageSubpathResolveError), PackageSubpathResolve(PackageSubpathResolveError),
#[class(inherit)] #[class(inherit)]
#[error("{0}")] #[error("{0}")]
ResolvePkgFolderFromDenoModule( ResolvePkgFolderFromDenoModule(#[from] ResolvePkgFolderFromDenoModuleError),
#[from] crate::npm::ResolvePkgFolderFromDenoModuleError,
),
#[class(inherit)] #[class(inherit)]
#[error("{0}")] #[error("{0}")]
ResolveNonGraphSpecifierTypes(#[from] ResolveNonGraphSpecifierTypesError), ResolveNonGraphSpecifierTypes(#[from] ResolveNonGraphSpecifierTypesError),

View file

@ -54,6 +54,8 @@ use crate::args::NpmCachingStrategy;
use crate::args::StorageKeyResolver; use crate::args::StorageKeyResolver;
use crate::node::CliNodeResolver; use crate::node::CliNodeResolver;
use crate::node::CliPackageJsonResolver; use crate::node::CliPackageJsonResolver;
use crate::npm::installer::NpmInstaller;
use crate::npm::installer::PackageCaching;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::sys::CliSys; use crate::sys::CliSys;
use crate::util::checksum; use crate::util::checksum;
@ -148,6 +150,7 @@ struct SharedWorkerState {
maybe_lockfile: Option<Arc<CliLockfile>>, maybe_lockfile: Option<Arc<CliLockfile>>,
module_loader_factory: Box<dyn ModuleLoaderFactory>, module_loader_factory: Box<dyn ModuleLoaderFactory>,
node_resolver: Arc<CliNodeResolver>, node_resolver: Arc<CliNodeResolver>,
npm_installer: Option<Arc<NpmInstaller>>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_resolver: Arc<dyn CliNpmResolver>,
pkg_json_resolver: Arc<CliPackageJsonResolver>, pkg_json_resolver: Arc<CliPackageJsonResolver>,
root_cert_store_provider: Arc<dyn RootCertStoreProvider>, root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
@ -423,6 +426,7 @@ impl CliMainWorkerFactory {
maybe_lockfile: Option<Arc<CliLockfile>>, maybe_lockfile: Option<Arc<CliLockfile>>,
module_loader_factory: Box<dyn ModuleLoaderFactory>, module_loader_factory: Box<dyn ModuleLoaderFactory>,
node_resolver: Arc<CliNodeResolver>, node_resolver: Arc<CliNodeResolver>,
npm_installer: Option<Arc<NpmInstaller>>,
npm_resolver: Arc<dyn CliNpmResolver>, npm_resolver: Arc<dyn CliNpmResolver>,
pkg_json_resolver: Arc<CliPackageJsonResolver>, pkg_json_resolver: Arc<CliPackageJsonResolver>,
root_cert_store_provider: Arc<dyn RootCertStoreProvider>, root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
@ -447,6 +451,7 @@ impl CliMainWorkerFactory {
maybe_lockfile, maybe_lockfile,
module_loader_factory, module_loader_factory,
node_resolver, node_resolver,
npm_installer,
npm_resolver, npm_resolver,
pkg_json_resolver, pkg_json_resolver,
root_cert_store_provider, root_cert_store_provider,
@ -496,18 +501,18 @@ impl CliMainWorkerFactory {
let main_module = if let Ok(package_ref) = let main_module = if let Ok(package_ref) =
NpmPackageReqReference::from_specifier(&main_module) NpmPackageReqReference::from_specifier(&main_module)
{ {
if let Some(npm_resolver) = shared.npm_resolver.as_managed() { if let Some(npm_installer) = &shared.npm_installer {
let reqs = &[package_ref.req().clone()]; let reqs = &[package_ref.req().clone()];
npm_resolver npm_installer
.add_package_reqs( .add_package_reqs(
reqs, reqs,
if matches!( if matches!(
shared.default_npm_caching_strategy, shared.default_npm_caching_strategy,
NpmCachingStrategy::Lazy NpmCachingStrategy::Lazy
) { ) {
crate::npm::PackageCaching::Only(reqs.into()) PackageCaching::Only(reqs.into())
} else { } else {
crate::npm::PackageCaching::All PackageCaching::All
}, },
) )
.await?; .await?;

View file

@ -16,7 +16,7 @@ use node_resolver::errors::ReferrerNotFoundError;
use node_resolver::NpmPackageFolderResolver; use node_resolver::NpmPackageFolderResolver;
use url::Url; use url::Url;
use super::resolution::NpmResolutionRc; use super::resolution::NpmResolutionCellRc;
use super::NpmCacheDirRc; use super::NpmCacheDirRc;
use super::NpmPackageFsResolver; use super::NpmPackageFsResolver;
use crate::ResolvedNpmRcRc; use crate::ResolvedNpmRcRc;
@ -26,14 +26,14 @@ use crate::ResolvedNpmRcRc;
pub struct GlobalNpmPackageResolver { pub struct GlobalNpmPackageResolver {
cache: NpmCacheDirRc, cache: NpmCacheDirRc,
npm_rc: ResolvedNpmRcRc, npm_rc: ResolvedNpmRcRc,
resolution: NpmResolutionRc, resolution: NpmResolutionCellRc,
} }
impl GlobalNpmPackageResolver { impl GlobalNpmPackageResolver {
pub fn new( pub fn new(
cache: NpmCacheDirRc, cache: NpmCacheDirRc,
npm_rc: ResolvedNpmRcRc, npm_rc: ResolvedNpmRcRc,
resolution: NpmResolutionRc, resolution: NpmResolutionCellRc,
) -> Self { ) -> Self {
Self { Self {
cache, cache,

View file

@ -19,7 +19,7 @@ use sys_traits::FsCanonicalize;
use sys_traits::FsMetadata; use sys_traits::FsMetadata;
use url::Url; use url::Url;
use super::resolution::NpmResolutionRc; use super::resolution::NpmResolutionCellRc;
use super::NpmPackageFsResolver; use super::NpmPackageFsResolver;
use crate::npm::local::get_package_folder_id_folder_name_from_parts; use crate::npm::local::get_package_folder_id_folder_name_from_parts;
use crate::npm::local::get_package_folder_id_from_folder_name; use crate::npm::local::get_package_folder_id_from_folder_name;
@ -32,7 +32,7 @@ use crate::sync::MaybeSync;
pub struct LocalNpmPackageResolver< pub struct LocalNpmPackageResolver<
TSys: FsCanonicalize + FsMetadata + MaybeSend + MaybeSync, TSys: FsCanonicalize + FsMetadata + MaybeSend + MaybeSync,
> { > {
resolution: NpmResolutionRc, resolution: NpmResolutionCellRc,
sys: TSys, sys: TSys,
root_node_modules_path: PathBuf, root_node_modules_path: PathBuf,
root_node_modules_url: Url, root_node_modules_url: Url,
@ -43,7 +43,7 @@ impl<TSys: FsCanonicalize + FsMetadata + MaybeSend + MaybeSync>
{ {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
resolution: NpmResolutionRc, resolution: NpmResolutionCellRc,
sys: TSys, sys: TSys,
node_modules_folder: PathBuf, node_modules_folder: PathBuf,
) -> Self { ) -> Self {

View file

@ -8,10 +8,13 @@ mod resolution;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use deno_npm::resolution::PackageCacheFolderIdNotFoundError;
use deno_npm::resolution::PackageNvNotFoundError;
use deno_npm::resolution::PackageReqNotFoundError; use deno_npm::resolution::PackageReqNotFoundError;
use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageCacheFolderId;
use deno_npm::NpmPackageId; use deno_npm::NpmPackageId;
use deno_path_util::fs::canonicalize_path_maybe_not_exists; use deno_path_util::fs::canonicalize_path_maybe_not_exists;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use node_resolver::InNpmPackageChecker; use node_resolver::InNpmPackageChecker;
use node_resolver::NpmPackageFolderResolver; use node_resolver::NpmPackageFolderResolver;
@ -23,14 +26,24 @@ use self::common::NpmPackageFsResolver;
use self::common::NpmPackageFsResolverRc; use self::common::NpmPackageFsResolverRc;
use self::global::GlobalNpmPackageResolver; use self::global::GlobalNpmPackageResolver;
use self::local::LocalNpmPackageResolver; use self::local::LocalNpmPackageResolver;
pub use self::resolution::NpmResolution; pub use self::resolution::NpmResolutionCell;
pub use self::resolution::NpmResolutionRc; pub use self::resolution::NpmResolutionCellRc;
use crate::sync::new_rc; use crate::sync::new_rc;
use crate::sync::MaybeSend; use crate::sync::MaybeSend;
use crate::sync::MaybeSync; use crate::sync::MaybeSync;
use crate::NpmCacheDirRc; use crate::NpmCacheDirRc;
use crate::ResolvedNpmRcRc; use crate::ResolvedNpmRcRc;
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum ResolvePkgFolderFromDenoModuleError {
#[class(inherit)]
#[error(transparent)]
PackageNvNotFound(#[from] PackageNvNotFoundError),
#[class(inherit)]
#[error(transparent)]
ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError),
}
#[derive(Debug, thiserror::Error, deno_error::JsError)] #[derive(Debug, thiserror::Error, deno_error::JsError)]
#[error(transparent)] #[error(transparent)]
pub enum ResolvePkgFolderFromPkgIdError { pub enum ResolvePkgFolderFromPkgIdError {
@ -66,6 +79,16 @@ pub enum ManagedResolvePkgFolderFromDenoReqError {
ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError), ResolvePkgFolderFromPkgId(#[from] ResolvePkgFolderFromPkgIdError),
} }
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum ResolvePkgIdFromSpecifierError {
#[class(inherit)]
#[error(transparent)]
Io(#[from] std::io::Error),
#[class(inherit)]
#[error(transparent)]
NotFound(#[from] PackageCacheFolderIdNotFoundError),
}
#[allow(clippy::disallowed_types)] #[allow(clippy::disallowed_types)]
pub type ManagedNpmResolverRc<TSys> = pub type ManagedNpmResolverRc<TSys> =
crate::sync::MaybeArc<ManagedNpmResolver<TSys>>; crate::sync::MaybeArc<ManagedNpmResolver<TSys>>;
@ -73,7 +96,7 @@ pub type ManagedNpmResolverRc<TSys> =
#[derive(Debug)] #[derive(Debug)]
pub struct ManagedNpmResolver<TSys: FsCanonicalize> { pub struct ManagedNpmResolver<TSys: FsCanonicalize> {
fs_resolver: NpmPackageFsResolverRc, fs_resolver: NpmPackageFsResolverRc,
resolution: NpmResolutionRc, resolution: NpmResolutionCellRc,
sys: TSys, sys: TSys,
} }
@ -89,7 +112,7 @@ impl<TSys: FsCanonicalize> ManagedNpmResolver<TSys> {
>( >(
npm_cache_dir: &NpmCacheDirRc, npm_cache_dir: &NpmCacheDirRc,
npm_rc: &ResolvedNpmRcRc, npm_rc: &ResolvedNpmRcRc,
resolution: NpmResolutionRc, resolution: NpmResolutionCellRc,
sys: TCreateSys, sys: TCreateSys,
maybe_node_modules_path: Option<PathBuf>, maybe_node_modules_path: Option<PathBuf>,
) -> ManagedNpmResolver<TCreateSys> { ) -> ManagedNpmResolver<TCreateSys> {
@ -144,6 +167,14 @@ impl<TSys: FsCanonicalize> ManagedNpmResolver<TSys> {
Ok(path) Ok(path)
} }
pub fn resolve_pkg_folder_from_deno_module(
&self,
nv: &PackageNv,
) -> Result<PathBuf, ResolvePkgFolderFromDenoModuleError> {
let pkg_id = self.resolution.resolve_pkg_id_from_deno_module(nv)?;
Ok(self.resolve_pkg_folder_from_pkg_id(&pkg_id)?)
}
pub fn resolve_pkg_folder_from_deno_module_req( pub fn resolve_pkg_folder_from_deno_module_req(
&self, &self,
req: &PackageReq, req: &PackageReq,
@ -162,6 +193,24 @@ impl<TSys: FsCanonicalize> ManagedNpmResolver<TSys> {
.fs_resolver .fs_resolver
.resolve_package_cache_folder_id_from_specifier(specifier) .resolve_package_cache_folder_id_from_specifier(specifier)
} }
/// Resolves the package id from the provided specifier.
pub fn resolve_pkg_id_from_specifier(
&self,
specifier: &Url,
) -> Result<Option<NpmPackageId>, ResolvePkgIdFromSpecifierError> {
let Some(cache_folder_id) = self
.fs_resolver
.resolve_package_cache_folder_id_from_specifier(specifier)?
else {
return Ok(None);
};
Ok(Some(
self
.resolution
.resolve_pkg_id_from_pkg_cache_folder_id(&cache_folder_id)?,
))
}
} }
impl<TSys: FsCanonicalize + std::fmt::Debug + MaybeSend + MaybeSync> impl<TSys: FsCanonicalize + std::fmt::Debug + MaybeSend + MaybeSync>

View file

@ -18,16 +18,17 @@ use deno_semver::package::PackageReq;
use parking_lot::RwLock; use parking_lot::RwLock;
#[allow(clippy::disallowed_types)] #[allow(clippy::disallowed_types)]
pub type NpmResolutionRc = crate::sync::MaybeArc<NpmResolution>; pub type NpmResolutionCellRc = crate::sync::MaybeArc<NpmResolutionCell>;
/// Handles updating and storing npm resolution in memory. /// Handles updating and storing npm resolution in memory.
/// ///
/// This does not interact with the file system. /// This does not interact with the file system.
pub struct NpmResolution { #[derive(Default)]
pub struct NpmResolutionCell {
snapshot: RwLock<NpmResolutionSnapshot>, snapshot: RwLock<NpmResolutionSnapshot>,
} }
impl std::fmt::Debug for NpmResolution { impl std::fmt::Debug for NpmResolutionCell {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let snapshot = self.snapshot.read(); let snapshot = self.snapshot.read();
f.debug_struct("NpmResolution") f.debug_struct("NpmResolution")
@ -36,7 +37,7 @@ impl std::fmt::Debug for NpmResolution {
} }
} }
impl NpmResolution { impl NpmResolutionCell {
pub fn from_serialized( pub fn from_serialized(
initial_snapshot: Option<ValidSerializedNpmResolutionSnapshot>, initial_snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
) -> Self { ) -> Self {

View file

@ -140,7 +140,9 @@ pub trait NpmProcessStateProvider:
#[derive(Debug)] #[derive(Debug)]
pub struct EmptyNpmProcessStateProvider; pub struct EmptyNpmProcessStateProvider;
impl NpmProcessStateProvider for EmptyNpmProcessStateProvider {} impl NpmProcessStateProvider for EmptyNpmProcessStateProvider {}
deno_core::extension!( deno_core::extension!(
deno_process, deno_process,
ops = [ ops = [