summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2023-09-30 12:06:38 -0400
committerGitHub <noreply@github.com>2023-09-30 12:06:38 -0400
commit8d24be1a59714761665516e0d78d25059608c29b (patch)
treeaed0140b63441008cb9b549d44948f7a36a4f5f1
parent1cda3840ff673512f7c6d58fa8402c35c760bc3b (diff)
refactor(npm): create `cli::npm::managed` module (#20740)
Creates the `cli::npm::managed` module and starts moving more functionality into it.
-rw-r--r--cli/factory.rs11
-rw-r--r--cli/graph_util.rs11
-rw-r--r--cli/lsp/analysis.rs7
-rw-r--r--cli/lsp/documents.rs37
-rw-r--r--cli/lsp/language_server.rs11
-rw-r--r--cli/main.rs5
-rw-r--r--cli/npm/managed/installer.rs (renamed from cli/npm/installer.rs)2
-rw-r--r--cli/npm/managed/mod.rs (renamed from cli/npm/resolvers/mod.rs)174
-rw-r--r--cli/npm/managed/resolution.rs (renamed from cli/npm/resolution.rs)10
-rw-r--r--cli/npm/managed/resolvers/common.rs (renamed from cli/npm/resolvers/common.rs)0
-rw-r--r--cli/npm/managed/resolvers/global.rs (renamed from cli/npm/resolvers/global.rs)4
-rw-r--r--cli/npm/managed/resolvers/local.rs (renamed from cli/npm/resolvers/local.rs)2
-rw-r--r--cli/npm/managed/resolvers/mod.rs50
-rw-r--r--cli/npm/mod.rs102
-rw-r--r--cli/npm/registry.rs6
-rw-r--r--cli/resolver.rs117
-rw-r--r--cli/standalone/binary.rs4
-rw-r--r--cli/standalone/mod.rs18
-rw-r--r--cli/tools/run.rs10
-rw-r--r--cli/tools/vendor/test.rs13
20 files changed, 316 insertions, 278 deletions
diff --git a/cli/factory.rs b/cli/factory.rs
index c56f64139..8a58062c7 100644
--- a/cli/factory.rs
+++ b/cli/factory.rs
@@ -354,10 +354,12 @@ impl CliFactory {
self.options.npm_system_info(),
);
Ok(Arc::new(ManagedCliNpmResolver::new(
+ self.npm_api()?.clone(),
fs.clone(),
npm_resolution.clone(),
npm_fs_resolver,
self.maybe_lockfile().as_ref().cloned(),
+ self.package_json_deps_installer().await?.clone(),
)) as Arc<dyn CliNpmResolver>)
})
.await
@@ -428,17 +430,18 @@ impl CliFactory {
.resolver
.get_or_try_init_async(async {
Ok(Arc::new(CliGraphResolver::new(
- self.npm_api()?.clone(),
- self.npm_resolution().await?.clone(),
+ if self.options.no_npm() {
+ None
+ } else {
+ Some(self.npm_resolver().await?.clone())
+ },
self.package_json_deps_provider().clone(),
- self.package_json_deps_installer().await?.clone(),
CliGraphResolverOptions {
maybe_jsx_import_source_config: self
.options
.to_maybe_jsx_import_source_config()?,
maybe_import_map: self.maybe_import_map().await?.clone(),
maybe_vendor_dir: self.options.vendor_dir_path(),
- no_npm: self.options.no_npm(),
},
)))
})
diff --git a/cli/graph_util.rs b/cli/graph_util.rs
index a4a5dcab1..17437ca99 100644
--- a/cli/graph_util.rs
+++ b/cli/graph_util.rs
@@ -322,7 +322,9 @@ impl ModuleGraphBuilder {
// ensure an "npm install" is done if the user has explicitly
// opted into using a node_modules directory
if self.options.node_modules_dir_enablement() == Some(true) {
- self.resolver.force_top_level_package_json_install().await?;
+ if let Some(npm_resolver) = self.npm_resolver.as_managed() {
+ npm_resolver.ensure_top_level_package_json_install().await?;
+ }
}
// add the lockfile redirects to the graph if it's the first time executing
@@ -393,10 +395,9 @@ impl ModuleGraphBuilder {
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
// ensure that the top level package.json is installed if a
// specifier was matched in the package.json
- self
- .resolver
- .top_level_package_json_install_if_necessary()
- .await?;
+ if self.resolver.found_package_json_dep() {
+ npm_resolver.ensure_top_level_package_json_install().await?;
+ }
// resolve the dependencies of any pending dependencies
// that were inserted by building the graph
diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs
index 1f12fb76b..8b2560f16 100644
--- a/cli/lsp/analysis.rs
+++ b/cli/lsp/analysis.rs
@@ -7,7 +7,6 @@ use super::language_server;
use super::tsc;
use crate::npm::CliNpmResolver;
-use crate::npm::NpmResolution;
use crate::tools::lint::create_linter;
use deno_ast::SourceRange;
@@ -162,7 +161,6 @@ fn code_as_string(code: &Option<lsp::NumberOrString>) -> String {
pub struct TsResponseImportMapper<'a> {
documents: &'a Documents,
maybe_import_map: Option<&'a ImportMap>,
- npm_resolution: &'a NpmResolution,
npm_resolver: &'a dyn CliNpmResolver,
}
@@ -170,13 +168,11 @@ impl<'a> TsResponseImportMapper<'a> {
pub fn new(
documents: &'a Documents,
maybe_import_map: Option<&'a ImportMap>,
- npm_resolution: &'a NpmResolution,
npm_resolver: &'a dyn CliNpmResolver,
) -> Self {
Self {
documents,
maybe_import_map,
- npm_resolution,
npm_resolver,
}
}
@@ -203,8 +199,7 @@ impl<'a> TsResponseImportMapper<'a> {
if let Ok(Some(pkg_id)) =
npm_resolver.resolve_pkg_id_from_specifier(specifier)
{
- let pkg_reqs =
- self.npm_resolution.resolve_pkg_reqs_from_pkg_id(&pkg_id);
+ let pkg_reqs = npm_resolver.resolve_pkg_reqs_from_pkg_id(&pkg_id);
// check if any pkg reqs match what is found in an import map
if !pkg_reqs.is_empty() {
let sub_path = self.resolve_package_path(specifier);
diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs
index 14c01d13b..30f419249 100644
--- a/cli/lsp/documents.rs
+++ b/cli/lsp/documents.rs
@@ -17,9 +17,7 @@ use crate::file_fetcher::get_source_from_bytes;
use crate::file_fetcher::get_source_from_data_url;
use crate::file_fetcher::map_content_type;
use crate::lsp::logging::lsp_warn;
-use crate::npm::CliNpmRegistryApi;
-use crate::npm::NpmResolution;
-use crate::npm::PackageJsonDepsInstaller;
+use crate::npm::CliNpmResolver;
use crate::resolver::CliGraphResolver;
use crate::resolver::CliGraphResolverOptions;
use crate::util::glob;
@@ -860,8 +858,7 @@ pub struct UpdateDocumentConfigOptions<'a> {
pub maybe_import_map: Option<Arc<import_map::ImportMap>>,
pub maybe_config_file: Option<&'a ConfigFile>,
pub maybe_package_json: Option<&'a PackageJson>,
- pub npm_registry_api: Arc<CliNpmRegistryApi>,
- pub npm_resolution: Arc<NpmResolution>,
+ pub npm_resolver: Option<Arc<dyn CliNpmResolver>>,
}
/// Specify the documents to include on a `documents.documents(...)` call.
@@ -917,7 +914,15 @@ impl Documents {
file_system_docs: Default::default(),
resolver_config_hash: 0,
imports: Default::default(),
- resolver: Default::default(),
+ resolver: Arc::new(CliGraphResolver::new(
+ None,
+ Arc::new(PackageJsonDepsProvider::default()),
+ CliGraphResolverOptions {
+ maybe_jsx_import_source_config: None,
+ maybe_import_map: None,
+ maybe_vendor_dir: None,
+ },
+ )),
npm_specifier_reqs: Default::default(),
has_injected_types_node_package: false,
specifier_resolver: Arc::new(SpecifierResolver::new(cache)),
@@ -1293,12 +1298,9 @@ impl Documents {
);
let deps_provider =
Arc::new(PackageJsonDepsProvider::new(maybe_package_json_deps));
- let deps_installer = Arc::new(PackageJsonDepsInstaller::no_op());
self.resolver = Arc::new(CliGraphResolver::new(
- options.npm_registry_api,
- options.npm_resolution,
+ options.npm_resolver,
deps_provider,
- deps_installer,
CliGraphResolverOptions {
maybe_jsx_import_source_config: maybe_jsx_config,
maybe_import_map: options.maybe_import_map,
@@ -1306,7 +1308,6 @@ impl Documents {
.maybe_config_file
.and_then(|c| c.vendor_dir_path())
.as_ref(),
- no_npm: false,
},
));
self.imports = Arc::new(
@@ -1938,7 +1939,6 @@ fn sort_and_remove_non_leaf_dirs(mut dirs: Vec<PathBuf>) -> Vec<PathBuf> {
mod tests {
use crate::cache::GlobalHttpCache;
use crate::cache::RealDenoCacheEnv;
- use crate::npm::NpmResolution;
use super::*;
use import_map::ImportMap;
@@ -2046,13 +2046,6 @@ console.log(b, "hello deno");
#[test]
fn test_documents_refresh_dependencies_config_change() {
- let npm_registry_api = Arc::new(CliNpmRegistryApi::new_uninitialized());
- let npm_resolution = Arc::new(NpmResolution::from_serialized(
- npm_registry_api.clone(),
- None,
- None,
- ));
-
// it should never happen that a user of this API causes this to happen,
// but we'll guard against it anyway
let temp_dir = TempDir::new();
@@ -2089,8 +2082,7 @@ console.log(b, "hello deno");
maybe_import_map: Some(Arc::new(import_map)),
maybe_config_file: None,
maybe_package_json: None,
- npm_registry_api: npm_registry_api.clone(),
- npm_resolution: npm_resolution.clone(),
+ npm_resolver: None,
});
// open the document
@@ -2131,8 +2123,7 @@ console.log(b, "hello deno");
maybe_import_map: Some(Arc::new(import_map)),
maybe_config_file: None,
maybe_package_json: None,
- npm_registry_api,
- npm_resolution,
+ npm_resolver: None,
});
// check the document's dependencies
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 9b3500361..7bed7b1bf 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -109,6 +109,7 @@ use crate::npm::ManagedCliNpmResolver;
use crate::npm::NpmCache;
use crate::npm::NpmCacheDir;
use crate::npm::NpmResolution;
+use crate::npm::PackageJsonDepsInstaller;
use crate::tools::fmt::format_file;
use crate::tools::fmt::format_parsed_source;
use crate::util::fs::remove_dir_all_if_exists;
@@ -509,7 +510,7 @@ fn create_npm_resolver_and_resolution(
maybe_snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
) -> (Arc<dyn CliNpmResolver>, Arc<NpmResolution>) {
let resolution = Arc::new(NpmResolution::from_serialized(
- api,
+ api.clone(),
maybe_snapshot,
// Don't provide the lockfile. We don't want these resolvers
// updating it. Only the cache request should update the lockfile.
@@ -527,12 +528,14 @@ fn create_npm_resolver_and_resolution(
);
(
Arc::new(ManagedCliNpmResolver::new(
+ api,
fs,
resolution.clone(),
fs_resolver,
// Don't provide the lockfile. We don't want these resolvers
// updating it. Only the cache request should update the lockfile.
None,
+ Arc::new(PackageJsonDepsInstaller::no_op()),
)),
resolution,
)
@@ -804,6 +807,7 @@ impl Inner {
));
let node_fs = Arc::new(deno_fs::RealFs);
let npm_resolver = Arc::new(ManagedCliNpmResolver::new(
+ self.npm.api.clone(),
node_fs.clone(),
npm_resolution.clone(),
create_npm_fs_resolver(
@@ -816,6 +820,7 @@ impl Inner {
NpmSystemInfo::default(),
),
self.config.maybe_lockfile().cloned(),
+ Arc::new(PackageJsonDepsInstaller::no_op()),
));
let node_resolver =
Arc::new(NodeResolver::new(node_fs, npm_resolver.clone()));
@@ -1366,8 +1371,7 @@ impl Inner {
maybe_import_map: self.maybe_import_map.clone(),
maybe_config_file: self.config.maybe_config_file(),
maybe_package_json: self.maybe_package_json.as_ref(),
- npm_registry_api: self.npm.api.clone(),
- npm_resolution: self.npm.resolution.clone(),
+ npm_resolver: Some(self.npm.resolver.clone()),
});
// refresh the npm specifiers because it might have discovered
@@ -2161,7 +2165,6 @@ impl Inner {
TsResponseImportMapper::new(
&self.documents,
self.maybe_import_map.as_deref(),
- &self.npm.resolution,
self.npm.resolver.as_ref(),
)
}
diff --git a/cli/main.rs b/cli/main.rs
index 98a2e5d48..1ccd694ee 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -76,7 +76,10 @@ impl SubcommandOutput for Result<(), std::io::Error> {
fn spawn_subcommand<F: Future<Output = T> + 'static, T: SubcommandOutput>(
f: F,
) -> JoinHandle<Result<i32, AnyError>> {
- deno_core::unsync::spawn(f.map(|r| r.output()))
+ // the boxed_local() is important in order to get windows to not blow the stack in debug
+ deno_core::unsync::spawn(
+ async move { f.map(|r| r.output()).await }.boxed_local(),
+ )
}
async fn run_subcommand(flags: Flags) -> Result<i32, AnyError> {
diff --git a/cli/npm/installer.rs b/cli/npm/managed/installer.rs
index 8f3db0531..21285c3d7 100644
--- a/cli/npm/installer.rs
+++ b/cli/npm/managed/installer.rs
@@ -13,7 +13,7 @@ use deno_semver::package::PackageReq;
use crate::args::PackageJsonDepsProvider;
use crate::util::sync::AtomicFlag;
-use super::CliNpmRegistryApi;
+use super::super::CliNpmRegistryApi;
use super::NpmResolution;
#[derive(Debug)]
diff --git a/cli/npm/resolvers/mod.rs b/cli/npm/managed/mod.rs
index 07a122a3e..c5ba3d3af 100644
--- a/cli/npm/resolvers/mod.rs
+++ b/cli/npm/managed/mod.rs
@@ -1,9 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-mod common;
-mod global;
-mod local;
-
use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
@@ -14,10 +10,13 @@ use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
use deno_core::serde_json;
use deno_core::url::Url;
+use deno_graph::NpmPackageReqResolution;
+use deno_npm::registry::NpmRegistryApi;
use deno_npm::resolution::NpmResolutionSnapshot;
use deno_npm::resolution::PackageReqNotFoundError;
use deno_npm::resolution::SerializedNpmResolutionSnapshot;
use deno_npm::NpmPackageId;
+use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::NodePermissions;
@@ -28,19 +27,24 @@ use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageNvReference;
use deno_semver::package::PackageReq;
-use global::GlobalNpmPackageResolver;
use serde::Deserialize;
use serde::Serialize;
use crate::args::Lockfile;
use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
-use crate::util::progress_bar::ProgressBar;
-use self::local::LocalNpmPackageResolver;
-use super::resolution::NpmResolution;
-use super::NpmCache;
+use super::CliNpmRegistryApi;
+use super::CliNpmResolver;
+use super::InnerCliNpmResolverRef;
+
+pub use self::installer::PackageJsonDepsInstaller;
+pub use self::resolution::NpmResolution;
+pub use self::resolvers::create_npm_fs_resolver;
+pub use self::resolvers::NpmPackageFsResolver;
-pub use self::common::NpmPackageFsResolver;
+mod installer;
+mod resolution;
+mod resolvers;
/// State provided to the process via an environment variable.
#[derive(Clone, Debug, Serialize, Deserialize)]
@@ -49,97 +53,46 @@ pub struct NpmProcessState {
pub local_node_modules_path: Option<String>,
}
-pub enum InnerCliNpmResolverRef<'a> {
- Managed(&'a ManagedCliNpmResolver),
- #[allow(dead_code)]
- Byonm(&'a ByonmCliNpmResolver),
-}
-
-pub trait CliNpmResolver: NpmResolver {
- fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver>;
-
- fn root_dir_url(&self) -> &Url;
-
- fn as_inner(&self) -> InnerCliNpmResolverRef;
-
- fn as_managed(&self) -> Option<&ManagedCliNpmResolver> {
- match self.as_inner() {
- InnerCliNpmResolverRef::Managed(inner) => Some(inner),
- InnerCliNpmResolverRef::Byonm(_) => None,
- }
- }
-
- fn node_modules_path(&self) -> Option<PathBuf>;
-
- /// Checks if the provided package req's folder is cached.
- fn is_pkg_req_folder_cached(&self, req: &PackageReq) -> bool;
-
- fn resolve_pkg_nv_ref_from_pkg_req_ref(
- &self,
- req_ref: &NpmPackageReqReference,
- ) -> Result<NpmPackageNvReference, PackageReqNotFoundError>;
-
- /// Resolve the root folder of the package the provided specifier is in.
- ///
- /// This will error when the provided specifier is not in an npm package.
- fn resolve_pkg_folder_from_specifier(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Result<Option<PathBuf>, AnyError>;
-
- fn resolve_pkg_folder_from_deno_module_req(
- &self,
- req: &PackageReq,
- ) -> Result<PathBuf, AnyError>;
-
- fn resolve_pkg_folder_from_deno_module(
- &self,
- nv: &PackageNv,
- ) -> Result<PathBuf, AnyError>;
-
- /// Gets the state of npm for the process.
- fn get_npm_process_state(&self) -> String;
-
- // todo(#18967): should instead return a hash state of the resolver
- // or perhaps this could be non-BYONM only and byonm always runs deno check
- fn package_reqs(&self) -> HashMap<PackageReq, PackageNv>;
-}
-
-// todo(dsherret): implement this
-pub struct ByonmCliNpmResolver;
-
/// 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 {
+ api: Arc<CliNpmRegistryApi>,
fs: Arc<dyn FileSystem>,
fs_resolver: Arc<dyn NpmPackageFsResolver>,
resolution: Arc<NpmResolution>,
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
+ package_json_deps_installer: Arc<PackageJsonDepsInstaller>,
}
impl std::fmt::Debug for ManagedCliNpmResolver {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ManagedNpmResolver")
+ .field("api", &"<omitted>")
.field("fs", &"<omitted>")
.field("fs_resolver", &"<omitted>")
.field("resolution", &"<omitted>")
.field("maybe_lockfile", &"<omitted>")
+ .field("package_json_deps_installer", &"<omitted>")
.finish()
}
}
impl ManagedCliNpmResolver {
pub fn new(
+ api: Arc<CliNpmRegistryApi>,
fs: Arc<dyn FileSystem>,
resolution: Arc<NpmResolution>,
fs_resolver: Arc<dyn NpmPackageFsResolver>,
maybe_lockfile: Option<Arc<Mutex<Lockfile>>>,
+ package_json_deps_installer: Arc<PackageJsonDepsInstaller>,
) -> Self {
Self {
+ api,
fs,
fs_resolver,
resolution,
maybe_lockfile,
+ package_json_deps_installer,
}
}
@@ -180,6 +133,13 @@ impl ManagedCliNpmResolver {
))
}
+ 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,
@@ -189,6 +149,13 @@ impl ManagedCliNpmResolver {
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)
+ }
+
/// Adds package requirements to the resolver and ensures everything is setup.
pub async fn add_package_reqs(
&self,
@@ -251,6 +218,28 @@ impl ManagedCliNpmResolver {
) -> Result<NpmPackageId, PackageReqNotFoundError> {
self.resolution.resolve_pkg_id_from_pkg_req(req)
}
+
+ pub async fn ensure_top_level_package_json_install(
+ &self,
+ ) -> Result<(), AnyError> {
+ self
+ .package_json_deps_installer
+ .ensure_top_level_install()
+ .await
+ }
+
+ pub async fn cache_package_info(
+ &self,
+ package_name: &str,
+ ) -> Result<(), AnyError> {
+ // this will internally cache the package information
+ self
+ .api
+ .package_info(package_name)
+ .await
+ .map(|_| ())
+ .map_err(|err| err.into())
+ }
}
impl NpmResolver for ManagedCliNpmResolver {
@@ -316,6 +305,24 @@ impl CliNpmResolver for ManagedCliNpmResolver {
.unwrap_or(false)
}
+ fn resolve_npm_for_deno_graph(
+ &self,
+ pkg_req: &PackageReq,
+ ) -> NpmPackageReqResolution {
+ let result = self.resolution.resolve_pkg_req_as_pending(pkg_req);
+ match result {
+ Ok(nv) => NpmPackageReqResolution::Ok(nv),
+ Err(err) => {
+ if self.api.mark_force_reload() {
+ log::debug!("Restarting npm specifier resolution to check for new registry information. Error: {:#}", err);
+ NpmPackageReqResolution::ReloadRegistryInfo(err.into())
+ } else {
+ NpmPackageReqResolution::Err(err.into())
+ }
+ }
+ }
+ }
+
fn resolve_pkg_nv_ref_from_pkg_req_ref(
&self,
req_ref: &NpmPackageReqReference,
@@ -385,32 +392,3 @@ impl CliNpmResolver for ManagedCliNpmResolver {
self.resolution.package_reqs()
}
}
-
-pub fn create_npm_fs_resolver(
- fs: Arc<dyn FileSystem>,
- cache: Arc<NpmCache>,
- progress_bar: &ProgressBar,
- registry_url: Url,
- resolution: Arc<NpmResolution>,
- maybe_node_modules_path: Option<PathBuf>,
- system_info: NpmSystemInfo,
-) -> Arc<dyn NpmPackageFsResolver> {
- match maybe_node_modules_path {
- Some(node_modules_folder) => Arc::new(LocalNpmPackageResolver::new(
- fs,
- cache,
- progress_bar.clone(),
- registry_url,
- node_modules_folder,
- resolution,
- system_info,
- )),
- None => Arc::new(GlobalNpmPackageResolver::new(
- fs,
- cache,
- registry_url,
- resolution,
- system_info,
- )),
- }
-}
diff --git a/cli/npm/resolution.rs b/cli/npm/managed/resolution.rs
index 3e76d5e85..05c1227a7 100644
--- a/cli/npm/resolution.rs
+++ b/cli/npm/managed/resolution.rs
@@ -34,7 +34,7 @@ use deno_semver::VersionReq;
use crate::args::Lockfile;
use crate::util::sync::TaskQueue;
-use super::registry::CliNpmRegistryApi;
+use super::super::registry::CliNpmRegistryApi;
/// Handles updating and storing npm resolution in memory where the underlying
/// snapshot can be updated concurrently. Additionally handles updating the lockfile
@@ -221,6 +221,8 @@ impl NpmResolution {
.map(|pkg| pkg.id.clone())
}
+ // todo: NEXT
+
/// Resolves a package requirement for deno graph. This should only be
/// called by deno_graph's NpmResolver or for resolving packages in
/// a package.json
@@ -273,10 +275,14 @@ impl NpmResolution {
.all_system_packages_partitioned(system_info)
}
+ // todo: NEXT
+
pub fn has_packages(&self) -> bool {
!self.snapshot.read().is_empty()
}
+ // todo: NEXT
+
pub fn snapshot(&self) -> NpmResolutionSnapshot {
self.snapshot.read().clone()
}
@@ -287,6 +293,8 @@ impl NpmResolution {
self.snapshot.read().as_valid_serialized()
}
+ // todo: NEXT
+
pub fn serialized_valid_snapshot_for_system(
&self,
system_info: &NpmSystemInfo,
diff --git a/cli/npm/resolvers/common.rs b/cli/npm/managed/resolvers/common.rs
index 4076579bf..4076579bf 100644
--- a/cli/npm/resolvers/common.rs
+++ b/cli/npm/managed/resolvers/common.rs
diff --git a/cli/npm/resolvers/global.rs b/cli/npm/managed/resolvers/global.rs
index e1144f610..25db62f73 100644
--- a/cli/npm/resolvers/global.rs
+++ b/cli/npm/managed/resolvers/global.rs
@@ -20,10 +20,10 @@ use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NodeResolutionMode;
-use crate::npm::resolution::NpmResolution;
-use crate::npm::resolvers::common::cache_packages;
use crate::npm::NpmCache;
+use super::super::resolution::NpmResolution;
+use super::common::cache_packages;
use super::common::types_package_name;
use super::common::NpmPackageFsResolver;
use super::common::RegistryReadPermissionChecker;
diff --git a/cli/npm/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs
index afa95e756..57170eccd 100644
--- a/cli/npm/resolvers/local.rs
+++ b/cli/npm/managed/resolvers/local.rs
@@ -42,11 +42,11 @@ use serde::Deserialize;
use serde::Serialize;
use crate::npm::cache::mixed_case_package_name_encode;
-use crate::npm::resolution::NpmResolution;
use crate::npm::NpmCache;
use crate::util::fs::copy_dir_recursive;
use crate::util::fs::hard_link_dir_recursive;
+use super::super::resolution::NpmResolution;
use super::common::types_package_name;
use super::common::NpmPackageFsResolver;
use super::common::RegistryReadPermissionChecker;
diff --git a/cli/npm/managed/resolvers/mod.rs b/cli/npm/managed/resolvers/mod.rs
new file mode 100644
index 000000000..b6d96c4af
--- /dev/null
+++ b/cli/npm/managed/resolvers/mod.rs
@@ -0,0 +1,50 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+mod common;
+mod global;
+mod local;
+
+use std::path::PathBuf;
+use std::sync::Arc;
+
+use deno_core::url::Url;
+use deno_npm::NpmSystemInfo;
+use deno_runtime::deno_fs::FileSystem;
+
+use crate::npm::NpmCache;
+use crate::util::progress_bar::ProgressBar;
+
+pub use self::common::NpmPackageFsResolver;
+use self::global::GlobalNpmPackageResolver;
+use self::local::LocalNpmPackageResolver;
+
+use super::NpmResolution;
+
+pub fn create_npm_fs_resolver(
+ fs: Arc<dyn FileSystem>,
+ cache: Arc<NpmCache>,
+ progress_bar: &ProgressBar,
+ registry_url: Url,
+ resolution: Arc<NpmResolution>,
+ maybe_node_modules_path: Option<PathBuf>,
+ system_info: NpmSystemInfo,
+) -> Arc<dyn NpmPackageFsResolver> {
+ match maybe_node_modules_path {
+ Some(node_modules_folder) => Arc::new(LocalNpmPackageResolver::new(
+ fs,
+ cache,
+ progress_bar.clone(),
+ registry_url,
+ node_modules_folder,
+ resolution,
+ system_info,
+ )),
+ None => Arc::new(GlobalNpmPackageResolver::new(
+ fs,
+ cache,
+ registry_url,
+ resolution,
+ system_info,
+ )),
+ }
+}
diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs
index 1b6ec243c..114bf15f2 100644
--- a/cli/npm/mod.rs
+++ b/cli/npm/mod.rs
@@ -1,20 +1,100 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+mod managed;
+
+// todo(#18967): move the cache, registry, and tarball into the managed folder
mod cache;
-mod installer;
mod registry;
-mod resolution;
-mod resolvers;
mod tarball;
+use std::collections::HashMap;
+use std::path::PathBuf;
+use std::sync::Arc;
+
+use deno_ast::ModuleSpecifier;
+use deno_core::error::AnyError;
+use deno_core::url::Url;
+use deno_graph::NpmPackageReqResolution;
+use deno_npm::resolution::PackageReqNotFoundError;
+use deno_runtime::deno_node::NpmResolver;
+
pub use cache::NpmCache;
pub use cache::NpmCacheDir;
-pub use installer::PackageJsonDepsInstaller;
+use deno_semver::npm::NpmPackageNvReference;
+use deno_semver::npm::NpmPackageReqReference;
+use deno_semver::package::PackageNv;
+use deno_semver::package::PackageReq;
+pub use managed::create_npm_fs_resolver;
+pub use managed::ManagedCliNpmResolver;
+pub use managed::NpmPackageFsResolver;
+pub use managed::NpmProcessState;
+pub use managed::NpmResolution;
+pub use managed::PackageJsonDepsInstaller;
pub use registry::CliNpmRegistryApi;
-pub use resolution::NpmResolution;
-pub use resolvers::create_npm_fs_resolver;
-pub use resolvers::CliNpmResolver;
-pub use resolvers::InnerCliNpmResolverRef;
-pub use resolvers::ManagedCliNpmResolver;
-pub use resolvers::NpmPackageFsResolver;
-pub use resolvers::NpmProcessState;
+
+pub enum InnerCliNpmResolverRef<'a> {
+ Managed(&'a ManagedCliNpmResolver),
+ #[allow(dead_code)]
+ Byonm(&'a ByonmCliNpmResolver),
+}
+
+pub trait CliNpmResolver: NpmResolver {
+ fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver>;
+
+ fn root_dir_url(&self) -> &Url;
+
+ fn as_inner(&self) -> InnerCliNpmResolverRef;
+
+ fn as_managed(&self) -> Option<&ManagedCliNpmResolver> {
+ match self.as_inner() {
+ InnerCliNpmResolverRef::Managed(inner) => Some(inner),
+ InnerCliNpmResolverRef::Byonm(_) => None,
+ }
+ }
+
+ fn node_modules_path(&self) -> Option<PathBuf>;
+
+ /// Checks if the provided package req's folder is cached.
+ fn is_pkg_req_folder_cached(&self, req: &PackageReq) -> bool;
+
+ /// Resolves a package requirement for deno graph. This should only be
+ /// called by deno_graph's NpmResolver or for resolving packages in
+ /// a package.json
+ fn resolve_npm_for_deno_graph(
+ &self,
+ pkg_req: &PackageReq,
+ ) -> NpmPackageReqResolution;
+
+ fn resolve_pkg_nv_ref_from_pkg_req_ref(
+ &self,
+ req_ref: &NpmPackageReqReference,
+ ) -> Result<NpmPackageNvReference, PackageReqNotFoundError>;
+
+ /// Resolve the root folder of the package the provided specifier is in.
+ ///
+ /// This will error when the provided specifier is not in an npm package.
+ fn resolve_pkg_folder_from_specifier(
+ &self,
+ specifier: &ModuleSpecifier,
+ ) -> Result<Option<PathBuf>, AnyError>;
+
+ fn resolve_pkg_folder_from_deno_module_req(
+ &self,
+ req: &PackageReq,
+ ) -> Result<PathBuf, AnyError>;
+
+ fn resolve_pkg_folder_from_deno_module(
+ &self,
+ nv: &PackageNv,
+ ) -> Result<PathBuf, AnyError>;
+
+ /// Gets the state of npm for the process.
+ fn get_npm_process_state(&self) -> String;
+
+ // todo(#18967): should instead return a hash state of the resolver
+ // or perhaps this could be non-BYONM only and byonm always runs deno check
+ fn package_reqs(&self) -> HashMap<PackageReq, PackageNv>;
+}
+
+// todo(#18967): implement this
+pub struct ByonmCliNpmResolver;
diff --git a/cli/npm/registry.rs b/cli/npm/registry.rs
index e960d926f..61eb4123d 100644
--- a/cli/npm/registry.rs
+++ b/cli/npm/registry.rs
@@ -75,12 +75,6 @@ impl CliNpmRegistryApi {
})))
}
- /// Creates an npm registry API that will be uninitialized. This is
- /// useful for tests or for initializing the LSP.
- pub fn new_uninitialized() -> Self {
- Self(None)
- }
-
/// Clears the internal memory cache.
pub fn clear_memory_cache(&self) {
self.inner().clear_memory_cache();
diff --git a/cli/resolver.rs b/cli/resolver.rs
index 1cbd3356e..cc1059aed 100644
--- a/cli/resolver.rs
+++ b/cli/resolver.rs
@@ -12,7 +12,6 @@ use deno_graph::source::NpmResolver;
use deno_graph::source::Resolver;
use deno_graph::source::UnknownBuiltInNodeModuleError;
use deno_graph::source::DEFAULT_JSX_IMPORT_SOURCE_MODULE;
-use deno_npm::registry::NpmRegistryApi;
use deno_runtime::deno_node::is_builtin_node_module;
use deno_semver::package::PackageReq;
use import_map::ImportMap;
@@ -22,9 +21,7 @@ use std::sync::Arc;
use crate::args::package_json::PackageJsonDeps;
use crate::args::JsxImportSourceConfig;
use crate::args::PackageJsonDepsProvider;
-use crate::npm::CliNpmRegistryApi;
-use crate::npm::NpmResolution;
-use crate::npm::PackageJsonDepsInstaller;
+use crate::npm::CliNpmResolver;
use crate::util::sync::AtomicFlag;
/// Result of checking if a specifier is mapped via
@@ -104,53 +101,20 @@ pub struct CliGraphResolver {
maybe_default_jsx_import_source: Option<String>,
maybe_jsx_import_source_module: Option<String>,
maybe_vendor_specifier: Option<ModuleSpecifier>,
- no_npm: bool,
- npm_registry_api: Arc<CliNpmRegistryApi>,
- npm_resolution: Arc<NpmResolution>,
- package_json_deps_installer: Arc<PackageJsonDepsInstaller>,
+ npm_resolver: Option<Arc<dyn CliNpmResolver>>,
found_package_json_dep_flag: Arc<AtomicFlag>,
}
-impl Default for CliGraphResolver {
- fn default() -> Self {
- // This is not ideal, but necessary for the LSP. In the future, we should
- // refactor the LSP and force this to be initialized.
- let npm_registry_api = Arc::new(CliNpmRegistryApi::new_uninitialized());
- let npm_resolution = Arc::new(NpmResolution::from_serialized(
- npm_registry_api.clone(),
- None,
- None,
- ));
- Self {
- mapped_specifier_resolver: MappedSpecifierResolver {
- maybe_import_map: Default::default(),
- package_json_deps_provider: Default::default(),
- },
- maybe_default_jsx_import_source: None,
- maybe_jsx_import_source_module: None,
- maybe_vendor_specifier: None,
- no_npm: false,
- npm_registry_api,
- npm_resolution,
- package_json_deps_installer: Default::default(),
- found_package_json_dep_flag: Default::default(),
- }
- }
-}
-
pub struct CliGraphResolverOptions<'a> {
pub maybe_jsx_import_source_config: Option<JsxImportSourceConfig>,
pub maybe_import_map: Option<Arc<ImportMap>>,
pub maybe_vendor_dir: Option<&'a PathBuf>,
- pub no_npm: bool,
}
impl CliGraphResolver {
pub fn new(
- npm_registry_api: Arc<CliNpmRegistryApi>,
- npm_resolution: Arc<NpmResolution>,
+ npm_resolver: Option<Arc<dyn CliNpmResolver>>,
package_json_deps_provider: Arc<PackageJsonDepsProvider>,
- package_json_deps_installer: Arc<PackageJsonDepsInstaller>,
options: CliGraphResolverOptions,
) -> Self {
Self {
@@ -168,10 +132,7 @@ impl CliGraphResolver {
maybe_vendor_specifier: options
.maybe_vendor_dir
.and_then(|v| ModuleSpecifier::from_directory_path(v).ok()),
- no_npm: options.no_npm,
- npm_registry_api,
- npm_resolution,
- package_json_deps_installer,
+ npm_resolver,
found_package_json_dep_flag: Default::default(),
}
}
@@ -184,22 +145,8 @@ impl CliGraphResolver {
self
}
- pub async fn force_top_level_package_json_install(
- &self,
- ) -> Result<(), AnyError> {
- self
- .package_json_deps_installer
- .ensure_top_level_install()
- .await
- }
-
- pub async fn top_level_package_json_install_if_necessary(
- &self,
- ) -> Result<(), AnyError> {
- if self.found_package_json_dep_flag.is_raised() {
- self.force_top_level_package_json_install().await?;
- }
- Ok(())
+ pub fn found_package_json_dep(&self) -> bool {
+ self.found_package_json_dep_flag.is_raised()
}
}
@@ -295,41 +242,33 @@ impl NpmResolver for CliGraphResolver {
&self,
package_name: &str,
) -> LocalBoxFuture<'static, Result<(), AnyError>> {
- if self.no_npm {
- // return it succeeded and error at the import site below
- return Box::pin(future::ready(Ok(())));
- }
- // this will internally cache the package information
- let package_name = package_name.to_string();
- let api = self.npm_registry_api.clone();
- async move {
- api
- .package_info(&package_name)
- .await
- .map(|_| ())
- .map_err(|err| err.into())
+ match &self.npm_resolver {
+ Some(npm_resolver) if npm_resolver.as_managed().is_some() => {
+ let package_name = package_name.to_string();
+ let npm_resolver = npm_resolver.clone();
+ async move {
+ if let Some(managed) = npm_resolver.as_managed() {
+ managed.cache_package_info(&package_name).await?;
+ }
+ Ok(())
+ }
+ .boxed()
+ }
+ _ => {
+ // return it succeeded and error at the import site below
+ Box::pin(future::ready(Ok(())))
+ }
}
- .boxed()
}
fn resolve_npm(&self, package_req: &PackageReq) -> NpmPackageReqResolution {
- if self.no_npm {
- return NpmPackageReqResolution::Err(anyhow!(
- "npm specifiers were requested; but --no-npm is specified"
- ));
- }
-
- let result = self.npm_resolution.resolve_pkg_req_as_pending(package_req);
- match result {
- Ok(nv) => NpmPackageReqResolution::Ok(nv),
- Err(err) => {
- if self.npm_registry_api.mark_force_reload() {
- log::debug!("Restarting npm specifier resolution to check for new registry information. Error: {:#}", err);
- NpmPackageReqResolution::ReloadRegistryInfo(err.into())
- } else {
- NpmPackageReqResolution::Err(err.into())
- }
+ match &self.npm_resolver {
+ Some(npm_resolver) => {
+ npm_resolver.resolve_npm_for_deno_graph(package_req)
}
+ None => NpmPackageReqResolution::Err(anyhow!(
+ "npm specifiers were requested; but --no-npm is specified"
+ )),
}
}
}
diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs
index 38fb3b08f..afe5a1b57 100644
--- a/cli/standalone/binary.rs
+++ b/cli/standalone/binary.rs
@@ -558,9 +558,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
let registry_url = self.npm_api.base_url();
let root_path = self.npm_cache.registry_folder(registry_url);
let mut builder = VfsBuilder::new(root_path)?;
- for package in self
- .npm_resolution
- .all_system_packages(&self.npm_system_info)
+ for package in npm_resolver.all_system_packages(&self.npm_system_info)
{
let folder =
npm_resolver.resolve_pkg_folder_from_pkg_id(&package.id)?;
diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs
index 30bbd7f8b..442334e39 100644
--- a/cli/standalone/mod.rs
+++ b/cli/standalone/mod.rs
@@ -21,6 +21,7 @@ use crate::npm::ManagedCliNpmResolver;
use crate::npm::NpmCache;
use crate::npm::NpmCacheDir;
use crate::npm::NpmResolution;
+use crate::npm::PackageJsonDepsInstaller;
use crate::resolver::MappedSpecifierResolver;
use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle;
@@ -366,11 +367,23 @@ pub async fn run(
node_modules_path,
NpmSystemInfo::default(),
);
+ let package_json_deps_provider = Arc::new(PackageJsonDepsProvider::new(
+ metadata
+ .package_json_deps
+ .map(|serialized| serialized.into_deps()),
+ ));
+ let package_json_installer = Arc::new(PackageJsonDepsInstaller::new(
+ package_json_deps_provider.clone(),
+ npm_api.clone(),
+ npm_resolution.clone(),
+ ));
let npm_resolver = Arc::new(ManagedCliNpmResolver::new(
+ npm_api.clone(),
fs.clone(),
npm_resolution.clone(),
npm_fs_resolver,
None,
+ package_json_installer,
)) as Arc<dyn CliNpmResolver>;
let node_resolver = Arc::new(NodeResolver::new(
fs.clone(),
@@ -387,11 +400,6 @@ pub async fn run(
node_resolver.clone(),
npm_resolver.clone().into_npm_resolver(),
));
- let package_json_deps_provider = Arc::new(PackageJsonDepsProvider::new(
- metadata
- .package_json_deps
- .map(|serialized| serialized.into_deps()),
- ));
let maybe_import_map = metadata.maybe_import_map.map(|(base, source)| {
Arc::new(parse_from_json(&base, &source).unwrap().import_map)
});
diff --git a/cli/tools/run.rs b/cli/tools/run.rs
index 6ded628ea..5fb31a4ad 100644
--- a/cli/tools/run.rs
+++ b/cli/tools/run.rs
@@ -186,13 +186,11 @@ pub async fn eval_command(
async fn maybe_npm_install(factory: &CliFactory) -> Result<(), AnyError> {
// ensure an "npm install" is done if the user has explicitly
- // opted into using a node_modules directory
+ // opted into using a managed node_modules directory
if factory.cli_options().node_modules_dir_enablement() == Some(true) {
- factory
- .package_json_deps_installer()
- .await?
- .ensure_top_level_install()
- .await?;
+ if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() {
+ npm_resolver.ensure_top_level_package_json_install().await?;
+ }
}
Ok(())
}
diff --git a/cli/tools/vendor/test.rs b/cli/tools/vendor/test.rs
index a8b83bb91..e13b8579b 100644
--- a/cli/tools/vendor/test.rs
+++ b/cli/tools/vendor/test.rs
@@ -23,8 +23,6 @@ use import_map::ImportMap;
use crate::args::JsxImportSourceConfig;
use crate::cache::ParsedSourceCache;
-use crate::npm::CliNpmRegistryApi;
-use crate::npm::NpmResolution;
use crate::resolver::CliGraphResolver;
use crate::resolver::CliGraphResolverOptions;
@@ -295,22 +293,13 @@ fn build_resolver(
maybe_jsx_import_source_config: Option<JsxImportSourceConfig>,
original_import_map: Option<ImportMap>,
) -> CliGraphResolver {
- let npm_registry_api = Arc::new(CliNpmRegistryApi::new_uninitialized());
- let npm_resolution = Arc::new(NpmResolution::from_serialized(
- npm_registry_api.clone(),
- None,
- None,
- ));
CliGraphResolver::new(
- npm_registry_api,
- npm_resolution,
- Default::default(),
+ None,
Default::default(),
CliGraphResolverOptions {
maybe_jsx_import_source_config,
maybe_import_map: original_import_map.map(Arc::new),
maybe_vendor_dir: None,
- no_npm: false,
},
)
}