1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

fix(byonm): resolve npm deps of jsr deps (#25399)

This allows using npm deps of jsr deps without having to add them to the
root package.json.

Works by taking the package requirement and scanning the
`node_modules/.deno` directory for the best matching package, so it
relies on deno's node_modules structure.

Additionally to make the transition from package.json to deno.json
easier, Deno now:

1. Installs npm deps in a deno.json at the same time as installing npm
deps from a package.json.
2. Uses the alias in the import map for `node_modules/<alias>` for
better package.json compatiblity.
This commit is contained in:
David Sherret 2024-09-04 16:00:44 +02:00 committed by GitHub
parent 13911eb8ef
commit c6d1b0a1cc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
49 changed files with 504 additions and 145 deletions

View file

@ -44,7 +44,7 @@ pub use deno_config::deno_json::TsTypeLib;
pub use deno_config::glob::FilePatterns; pub use deno_config::glob::FilePatterns;
pub use flags::*; pub use flags::*;
pub use lockfile::CliLockfile; pub use lockfile::CliLockfile;
pub use package_json::PackageJsonInstallDepsProvider; pub use package_json::NpmInstallDepsProvider;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_core::anyhow::bail; use deno_core::anyhow::bail;

View file

@ -1,17 +1,20 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::collections::HashSet;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use deno_config::workspace::Workspace; use deno_config::workspace::Workspace;
use deno_core::serde_json;
use deno_package_json::PackageJsonDepValue; use deno_package_json::PackageJsonDepValue;
use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use crate::util::path::is_banned_path_char;
#[derive(Debug)] #[derive(Debug)]
pub struct InstallNpmRemotePkg { pub struct InstallNpmRemotePkg {
pub alias: String, pub alias: String,
// todo(24419): use this when setting up the node_modules dir
#[allow(dead_code)]
pub base_dir: PathBuf, pub base_dir: PathBuf,
pub req: PackageReq, pub req: PackageReq,
} }
@ -19,74 +22,126 @@ pub struct InstallNpmRemotePkg {
#[derive(Debug)] #[derive(Debug)]
pub struct InstallNpmWorkspacePkg { pub struct InstallNpmWorkspacePkg {
pub alias: String, pub alias: String,
// todo(24419): use this when setting up the node_modules dir
#[allow(dead_code)]
pub base_dir: PathBuf,
pub target_dir: PathBuf, pub target_dir: PathBuf,
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct PackageJsonInstallDepsProvider { pub struct NpmInstallDepsProvider {
remote_pkgs: Vec<InstallNpmRemotePkg>, remote_pkgs: Vec<InstallNpmRemotePkg>,
workspace_pkgs: Vec<InstallNpmWorkspacePkg>, workspace_pkgs: Vec<InstallNpmWorkspacePkg>,
} }
impl PackageJsonInstallDepsProvider { impl NpmInstallDepsProvider {
pub fn empty() -> Self { pub fn empty() -> Self {
Self::default() Self::default()
} }
pub fn from_workspace(workspace: &Arc<Workspace>) -> Self { pub fn from_workspace(workspace: &Arc<Workspace>) -> Self {
// todo(dsherret): estimate capacity?
let mut workspace_pkgs = Vec::new(); let mut workspace_pkgs = Vec::new();
let mut remote_pkgs = Vec::new(); let mut remote_pkgs = Vec::new();
let workspace_npm_pkgs = workspace.npm_packages(); let workspace_npm_pkgs = workspace.npm_packages();
for pkg_json in workspace.package_jsons() {
let deps = pkg_json.resolve_local_package_json_deps(); for (_, folder) in workspace.config_folders() {
let mut pkg_pkgs = Vec::with_capacity(deps.len()); let mut deno_json_aliases = HashSet::new();
for (alias, dep) in deps {
let Ok(dep) = dep else { // deal with the deno.json first because it takes precedence during resolution
continue; if let Some(deno_json) = &folder.deno_json {
}; // don't bother with externally referenced import maps as users
match dep { // should inline their import map to get this behaviour
PackageJsonDepValue::Req(pkg_req) => { if let Some(serde_json::Value::Object(obj)) = &deno_json.json.imports {
let workspace_pkg = workspace_npm_pkgs.iter().find(|pkg| { deno_json_aliases.reserve(obj.len());
pkg.matches_req(&pkg_req) let mut pkg_pkgs = Vec::with_capacity(obj.len());
// do not resolve to the current package for (alias, value) in obj {
&& pkg.pkg_json.path != pkg_json.path let serde_json::Value::String(specifier) = value else {
}); continue;
};
let Ok(npm_req_ref) = NpmPackageReqReference::from_str(specifier)
else {
continue;
};
// skip any aliases with banned characters
if alias.chars().any(|c| c == '\\' || is_banned_path_char(c)) {
continue;
}
deno_json_aliases.insert(alias.to_lowercase());
let pkg_req = npm_req_ref.into_inner().req;
let workspace_pkg = workspace_npm_pkgs
.iter()
.find(|pkg| pkg.matches_req(&pkg_req));
if let Some(pkg) = workspace_pkg { if let Some(pkg) = workspace_pkg {
workspace_pkgs.push(InstallNpmWorkspacePkg { workspace_pkgs.push(InstallNpmWorkspacePkg {
alias, alias: alias.to_string(),
base_dir: pkg_json.dir_path().to_path_buf(),
target_dir: pkg.pkg_json.dir_path().to_path_buf(), target_dir: pkg.pkg_json.dir_path().to_path_buf(),
}); });
} else { } else {
pkg_pkgs.push(InstallNpmRemotePkg { pkg_pkgs.push(InstallNpmRemotePkg {
alias, alias: alias.to_string(),
base_dir: pkg_json.dir_path().to_path_buf(), base_dir: deno_json.dir_path(),
req: pkg_req, req: pkg_req,
}); });
} }
} }
PackageJsonDepValue::Workspace(version_req) => {
if let Some(pkg) = workspace_npm_pkgs.iter().find(|pkg| { // sort within each package (more like npm resolution)
pkg.matches_name_and_version_req(&alias, &version_req) pkg_pkgs.sort_by(|a, b| a.alias.cmp(&b.alias));
}) { remote_pkgs.extend(pkg_pkgs);
workspace_pkgs.push(InstallNpmWorkspacePkg { }
alias, }
base_dir: pkg_json.dir_path().to_path_buf(),
target_dir: pkg.pkg_json.dir_path().to_path_buf(), if let Some(pkg_json) = &folder.pkg_json {
let deps = pkg_json.resolve_local_package_json_deps();
let mut pkg_pkgs = Vec::with_capacity(deps.len());
for (alias, dep) in deps {
let Ok(dep) = dep else {
continue;
};
if deno_json_aliases.contains(&alias.to_lowercase()) {
// aliases in deno.json take precedence over package.json, so
// since this can't be resolved don't bother installing it
continue;
}
match dep {
PackageJsonDepValue::Req(pkg_req) => {
let workspace_pkg = workspace_npm_pkgs.iter().find(|pkg| {
pkg.matches_req(&pkg_req)
// do not resolve to the current package
&& pkg.pkg_json.path != pkg_json.path
}); });
if let Some(pkg) = workspace_pkg {
workspace_pkgs.push(InstallNpmWorkspacePkg {
alias,
target_dir: pkg.pkg_json.dir_path().to_path_buf(),
});
} else {
pkg_pkgs.push(InstallNpmRemotePkg {
alias,
base_dir: pkg_json.dir_path().to_path_buf(),
req: pkg_req,
});
}
}
PackageJsonDepValue::Workspace(version_req) => {
if let Some(pkg) = workspace_npm_pkgs.iter().find(|pkg| {
pkg.matches_name_and_version_req(&alias, &version_req)
}) {
workspace_pkgs.push(InstallNpmWorkspacePkg {
alias,
target_dir: pkg.pkg_json.dir_path().to_path_buf(),
});
}
} }
} }
} }
}
// sort within each package
pkg_pkgs.sort_by(|a, b| a.alias.cmp(&b.alias));
remote_pkgs.extend(pkg_pkgs); // sort within each package as npm does
pkg_pkgs.sort_by(|a, b| a.alias.cmp(&b.alias));
remote_pkgs.extend(pkg_pkgs);
}
} }
remote_pkgs.shrink_to_fit(); remote_pkgs.shrink_to_fit();
workspace_pkgs.shrink_to_fit(); workspace_pkgs.shrink_to_fit();
Self { Self {

View file

@ -5,7 +5,7 @@ use crate::args::CaData;
use crate::args::CliOptions; use crate::args::CliOptions;
use crate::args::DenoSubcommand; use crate::args::DenoSubcommand;
use crate::args::Flags; use crate::args::Flags;
use crate::args::PackageJsonInstallDepsProvider; use crate::args::NpmInstallDepsProvider;
use crate::args::StorageKeyResolver; use crate::args::StorageKeyResolver;
use crate::args::TsConfigType; use crate::args::TsConfigType;
use crate::cache::Caches; use crate::cache::Caches;
@ -386,9 +386,7 @@ impl CliFactory {
cache_setting: cli_options.cache_setting(), cache_setting: cli_options.cache_setting(),
text_only_progress_bar: self.text_only_progress_bar().clone(), text_only_progress_bar: self.text_only_progress_bar().clone(),
maybe_node_modules_path: cli_options.node_modules_dir_path().cloned(), maybe_node_modules_path: cli_options.node_modules_dir_path().cloned(),
package_json_deps_provider: Arc::new(PackageJsonInstallDepsProvider::from_workspace( npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::from_workspace(cli_options.workspace())),
cli_options.workspace(),
)),
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(), lifecycle_scripts: cli_options.lifecycle_scripts_config(),

View file

@ -1251,7 +1251,7 @@ impl Documents {
/// tsc when type checking. /// tsc when type checking.
pub fn resolve( pub fn resolve(
&self, &self,
specifiers: &[String], raw_specifiers: &[String],
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
file_referrer: Option<&ModuleSpecifier>, file_referrer: Option<&ModuleSpecifier>,
) -> Vec<Option<(ModuleSpecifier, MediaType)>> { ) -> Vec<Option<(ModuleSpecifier, MediaType)>> {
@ -1262,16 +1262,16 @@ impl Documents {
.or(file_referrer); .or(file_referrer);
let dependencies = document.as_ref().map(|d| d.dependencies()); let dependencies = document.as_ref().map(|d| d.dependencies());
let mut results = Vec::new(); let mut results = Vec::new();
for specifier in specifiers { for raw_specifier in raw_specifiers {
if specifier.starts_with("asset:") { if raw_specifier.starts_with("asset:") {
if let Ok(specifier) = ModuleSpecifier::parse(specifier) { if let Ok(specifier) = ModuleSpecifier::parse(raw_specifier) {
let media_type = MediaType::from_specifier(&specifier); let media_type = MediaType::from_specifier(&specifier);
results.push(Some((specifier, media_type))); results.push(Some((specifier, media_type)));
} else { } else {
results.push(None); results.push(None);
} }
} else if let Some(dep) = } else if let Some(dep) =
dependencies.as_ref().and_then(|d| d.get(specifier)) dependencies.as_ref().and_then(|d| d.get(raw_specifier))
{ {
if let Some(specifier) = dep.maybe_type.maybe_specifier() { if let Some(specifier) = dep.maybe_type.maybe_specifier() {
results.push(self.resolve_dependency( results.push(self.resolve_dependency(
@ -1290,7 +1290,7 @@ impl Documents {
} }
} else if let Ok(specifier) = } else if let Ok(specifier) =
self.resolver.as_graph_resolver(file_referrer).resolve( self.resolver.as_graph_resolver(file_referrer).resolve(
specifier, raw_specifier,
&deno_graph::Range { &deno_graph::Range {
specifier: referrer.clone(), specifier: referrer.clone(),
start: deno_graph::Position::zeroed(), start: deno_graph::Position::zeroed(),

View file

@ -3,7 +3,7 @@
use crate::args::create_default_npmrc; use crate::args::create_default_npmrc;
use crate::args::CacheSetting; use crate::args::CacheSetting;
use crate::args::CliLockfile; use crate::args::CliLockfile;
use crate::args::PackageJsonInstallDepsProvider; use crate::args::NpmInstallDepsProvider;
use crate::graph_util::CliJsrUrlProvider; use crate::graph_util::CliJsrUrlProvider;
use crate::http_util::HttpClientProvider; use crate::http_util::HttpClientProvider;
use crate::lsp::config::Config; use crate::lsp::config::Config;
@ -474,9 +474,7 @@ async fn create_npm_resolver(
maybe_node_modules_path: config_data maybe_node_modules_path: config_data
.and_then(|d| d.node_modules_dir.clone()), .and_then(|d| d.node_modules_dir.clone()),
// only used for top level install, so we can ignore this // only used for top level install, so we can ignore this
package_json_deps_provider: Arc::new( npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()),
PackageJsonInstallDepsProvider::empty(),
),
npmrc: config_data npmrc: config_data
.and_then(|d| d.npmrc.clone()) .and_then(|d| d.npmrc.clone())
.unwrap_or_else(create_default_npmrc), .unwrap_or_else(create_default_npmrc),

View file

@ -401,7 +401,7 @@ impl<TGraphContainer: ModuleGraphContainer>
fn inner_resolve( fn inner_resolve(
&self, &self,
specifier: &str, raw_specifier: &str,
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
) -> Result<ModuleSpecifier, AnyError> { ) -> Result<ModuleSpecifier, AnyError> {
if self.shared.node_resolver.in_npm_package(referrer) { if self.shared.node_resolver.in_npm_package(referrer) {
@ -409,7 +409,7 @@ impl<TGraphContainer: ModuleGraphContainer>
self self
.shared .shared
.node_resolver .node_resolver
.resolve(specifier, referrer, NodeResolutionMode::Execution)? .resolve(raw_specifier, referrer, NodeResolutionMode::Execution)?
.into_url(), .into_url(),
); );
} }
@ -418,7 +418,7 @@ impl<TGraphContainer: ModuleGraphContainer>
let resolution = match graph.get(referrer) { let resolution = match graph.get(referrer) {
Some(Module::Js(module)) => module Some(Module::Js(module)) => module
.dependencies .dependencies
.get(specifier) .get(raw_specifier)
.map(|d| &d.maybe_code) .map(|d| &d.maybe_code)
.unwrap_or(&Resolution::None), .unwrap_or(&Resolution::None),
_ => &Resolution::None, _ => &Resolution::None,
@ -433,7 +433,7 @@ impl<TGraphContainer: ModuleGraphContainer>
)); ));
} }
Resolution::None => Cow::Owned(self.shared.resolver.resolve( Resolution::None => Cow::Owned(self.shared.resolver.resolve(
specifier, raw_specifier,
&deno_graph::Range { &deno_graph::Range {
specifier: referrer.clone(), specifier: referrer.clone(),
start: deno_graph::Position::zeroed(), start: deno_graph::Position::zeroed(),

View file

@ -17,6 +17,7 @@ use deno_runtime::deno_node::NodeRequireResolver;
use deno_runtime::deno_node::NpmProcessStateProvider; use deno_runtime::deno_node::NpmProcessStateProvider;
use deno_runtime::deno_node::PackageJson; use deno_runtime::deno_node::PackageJson;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use deno_semver::Version;
use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageFolderResolveError;
use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::errors::PackageFolderResolveIoError;
use node_resolver::errors::PackageJsonLoadError; use node_resolver::errors::PackageJsonLoadError;
@ -29,6 +30,7 @@ use crate::args::NpmProcessStateKind;
use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs; use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
use deno_runtime::fs_util::specifier_to_file_path; use deno_runtime::fs_util::specifier_to_file_path;
use super::managed::normalize_pkg_name_for_node_modules_deno_folder;
use super::CliNpmResolver; use super::CliNpmResolver;
use super::InnerCliNpmResolverRef; use super::InnerCliNpmResolverRef;
@ -60,9 +62,7 @@ impl ByonmCliNpmResolver {
) -> Result<Option<Arc<PackageJson>>, PackageJsonLoadError> { ) -> Result<Option<Arc<PackageJson>>, PackageJsonLoadError> {
load_pkg_json(&DenoPkgJsonFsAdapter(self.fs.as_ref()), path) load_pkg_json(&DenoPkgJsonFsAdapter(self.fs.as_ref()), path)
} }
}
impl ByonmCliNpmResolver {
/// Finds the ancestor package.json that contains the specified dependency. /// Finds the ancestor package.json that contains the specified dependency.
pub fn find_ancestor_package_json_with_dep( pub fn find_ancestor_package_json_with_dep(
&self, &self,
@ -98,7 +98,7 @@ impl ByonmCliNpmResolver {
&self, &self,
req: &PackageReq, req: &PackageReq,
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
) -> Result<(Arc<PackageJson>, String), AnyError> { ) -> Result<Option<(Arc<PackageJson>, String)>, AnyError> {
fn resolve_alias_from_pkg_json( fn resolve_alias_from_pkg_json(
req: &PackageReq, req: &PackageReq,
pkg_json: &PackageJson, pkg_json: &PackageJson,
@ -134,7 +134,7 @@ impl ByonmCliNpmResolver {
if let Some(alias) = if let Some(alias) =
resolve_alias_from_pkg_json(req, pkg_json.as_ref()) resolve_alias_from_pkg_json(req, pkg_json.as_ref())
{ {
return Ok((pkg_json, alias)); return Ok(Some((pkg_json, alias)));
} }
} }
current_path = dir_path; current_path = dir_path;
@ -148,19 +148,65 @@ impl ByonmCliNpmResolver {
if let Some(pkg_json) = self.load_pkg_json(&root_pkg_json_path)? { if let Some(pkg_json) = self.load_pkg_json(&root_pkg_json_path)? {
if let Some(alias) = resolve_alias_from_pkg_json(req, pkg_json.as_ref()) if let Some(alias) = resolve_alias_from_pkg_json(req, pkg_json.as_ref())
{ {
return Ok((pkg_json, alias)); return Ok(Some((pkg_json, alias)));
} }
} }
} }
bail!( Ok(None)
concat!( }
"Could not find a matching package for 'npm:{}' in a package.json file. ",
"You must specify this as a package.json dependency when the ", fn resolve_folder_in_root_node_modules(
"node_modules folder is not managed by Deno.", &self,
), req: &PackageReq,
req, ) -> Option<PathBuf> {
// now check if node_modules/.deno/ matches this constraint
let root_node_modules_dir = self.root_node_modules_dir.as_ref()?;
let node_modules_deno_dir = root_node_modules_dir.join(".deno");
let Ok(entries) = self.fs.read_dir_sync(&node_modules_deno_dir) else {
return None;
};
let search_prefix = format!(
"{}@",
normalize_pkg_name_for_node_modules_deno_folder(&req.name)
); );
let mut best_version = None;
// example entries:
// - @denotest+add@1.0.0
// - @denotest+add@1.0.0_1
for entry in entries {
if !entry.is_directory {
continue;
}
let Some(version_and_copy_idx) = entry.name.strip_prefix(&search_prefix)
else {
continue;
};
let version = version_and_copy_idx
.rsplit_once('_')
.map(|(v, _)| v)
.unwrap_or(version_and_copy_idx);
let Ok(version) = Version::parse_from_npm(version) else {
continue;
};
if req.version_req.matches(&version) {
if let Some((best_version_version, _)) = &best_version {
if version > *best_version_version {
best_version = Some((version, entry.name));
}
} else {
best_version = Some((version, entry.name));
}
}
}
best_version.map(|(_version, entry_name)| {
join_package_name(
&node_modules_deno_dir.join(entry_name).join("node_modules"),
&req.name,
)
})
} }
} }
@ -288,29 +334,62 @@ impl CliNpmResolver for ByonmCliNpmResolver {
req: &PackageReq, req: &PackageReq,
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
) -> Result<PathBuf, AnyError> { ) -> Result<PathBuf, AnyError> {
// resolve the pkg json and alias fn node_resolve_dir(
let (pkg_json, alias) = fs: &dyn FileSystem,
self.resolve_pkg_json_and_alias_for_req(req, referrer)?; alias: &str,
// now try node resolution start_dir: &Path,
for ancestor in pkg_json.path.parent().unwrap().ancestors() { ) -> Result<Option<PathBuf>, AnyError> {
let node_modules_folder = ancestor.join("node_modules"); for ancestor in start_dir.ancestors() {
let sub_dir = join_package_name(&node_modules_folder, &alias); let node_modules_folder = ancestor.join("node_modules");
if self.fs.is_dir_sync(&sub_dir) { let sub_dir = join_package_name(&node_modules_folder, alias);
return Ok(canonicalize_path_maybe_not_exists_with_fs( if fs.is_dir_sync(&sub_dir) {
&sub_dir, return Ok(Some(canonicalize_path_maybe_not_exists_with_fs(
self.fs.as_ref(), &sub_dir, fs,
)?); )?));
}
} }
Ok(None)
} }
bail!( // now attempt to resolve if it's found in any package.json
concat!( let maybe_pkg_json_and_alias =
"Could not find \"{}\" in a node_modules folder. ", self.resolve_pkg_json_and_alias_for_req(req, referrer)?;
"Deno expects the node_modules/ directory to be up to date. ", match maybe_pkg_json_and_alias {
"Did you forget to run `deno install`?" Some((pkg_json, alias)) => {
), // now try node resolution
alias, if let Some(resolved) =
); node_resolve_dir(self.fs.as_ref(), &alias, pkg_json.dir_path())?
{
return Ok(resolved);
}
bail!(
concat!(
"Could not find \"{}\" in a node_modules folder. ",
"Deno expects the node_modules/ directory to be up to date. ",
"Did you forget to run `deno install`?"
),
alias,
);
}
None => {
// now check if node_modules/.deno/ matches this constraint
if let Some(folder) = self.resolve_folder_in_root_node_modules(req) {
return Ok(folder);
}
bail!(
concat!(
"Could not find a matching package for 'npm:{}' in the node_modules ",
"directory. Ensure you have all your JSR and npm dependencies listed ",
"in your deno.json or package.json, then run `deno install`. Alternatively, ",
r#"turn on auto-install by specifying `"nodeModulesDir": "auto"` in your "#,
"deno.json file."
),
req,
);
}
}
} }
fn check_state_hash(&self) -> Option<u64> { fn check_state_hash(&self) -> Option<u64> {

View file

@ -32,9 +32,9 @@ use resolution::AddPkgReqsResult;
use crate::args::CliLockfile; use crate::args::CliLockfile;
use crate::args::LifecycleScriptsConfig; use crate::args::LifecycleScriptsConfig;
use crate::args::NpmInstallDepsProvider;
use crate::args::NpmProcessState; use crate::args::NpmProcessState;
use crate::args::NpmProcessStateKind; use crate::args::NpmProcessStateKind;
use crate::args::PackageJsonInstallDepsProvider;
use crate::cache::FastInsecureHasher; use crate::cache::FastInsecureHasher;
use crate::http_util::HttpClientProvider; use crate::http_util::HttpClientProvider;
use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs; use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
@ -45,6 +45,7 @@ use self::cache::NpmCache;
use self::registry::CliNpmRegistryApi; use self::registry::CliNpmRegistryApi;
use self::resolution::NpmResolution; use self::resolution::NpmResolution;
use self::resolvers::create_npm_fs_resolver; use self::resolvers::create_npm_fs_resolver;
pub use self::resolvers::normalize_pkg_name_for_node_modules_deno_folder;
use self::resolvers::NpmPackageFsResolver; use self::resolvers::NpmPackageFsResolver;
use super::CliNpmResolver; use super::CliNpmResolver;
@ -71,7 +72,7 @@ pub struct CliNpmResolverManagedCreateOptions {
pub text_only_progress_bar: crate::util::progress_bar::ProgressBar, pub text_only_progress_bar: crate::util::progress_bar::ProgressBar,
pub maybe_node_modules_path: Option<PathBuf>, pub maybe_node_modules_path: Option<PathBuf>,
pub npm_system_info: NpmSystemInfo, pub npm_system_info: NpmSystemInfo,
pub package_json_deps_provider: Arc<PackageJsonInstallDepsProvider>, pub npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
pub npmrc: Arc<ResolvedNpmRc>, pub npmrc: Arc<ResolvedNpmRc>,
pub lifecycle_scripts: LifecycleScriptsConfig, pub lifecycle_scripts: LifecycleScriptsConfig,
} }
@ -97,7 +98,7 @@ pub async fn create_managed_npm_resolver_for_lsp(
npm_api, npm_api,
npm_cache, npm_cache,
options.npmrc, options.npmrc,
options.package_json_deps_provider, options.npm_install_deps_provider,
options.text_only_progress_bar, options.text_only_progress_bar,
options.maybe_node_modules_path, options.maybe_node_modules_path,
options.npm_system_info, options.npm_system_info,
@ -122,7 +123,7 @@ pub async fn create_managed_npm_resolver(
npm_api, npm_api,
npm_cache, npm_cache,
options.npmrc, options.npmrc,
options.package_json_deps_provider, options.npm_install_deps_provider,
options.text_only_progress_bar, options.text_only_progress_bar,
options.maybe_node_modules_path, options.maybe_node_modules_path,
options.npm_system_info, options.npm_system_info,
@ -139,7 +140,7 @@ fn create_inner(
npm_api: Arc<CliNpmRegistryApi>, npm_api: Arc<CliNpmRegistryApi>,
npm_cache: Arc<NpmCache>, npm_cache: Arc<NpmCache>,
npm_rc: Arc<ResolvedNpmRc>, npm_rc: Arc<ResolvedNpmRc>,
package_json_deps_provider: Arc<PackageJsonInstallDepsProvider>, npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
text_only_progress_bar: crate::util::progress_bar::ProgressBar, text_only_progress_bar: crate::util::progress_bar::ProgressBar,
node_modules_dir_path: Option<PathBuf>, node_modules_dir_path: Option<PathBuf>,
npm_system_info: NpmSystemInfo, npm_system_info: NpmSystemInfo,
@ -161,7 +162,7 @@ fn create_inner(
let fs_resolver = create_npm_fs_resolver( let fs_resolver = create_npm_fs_resolver(
fs.clone(), fs.clone(),
npm_cache.clone(), npm_cache.clone(),
&package_json_deps_provider, &npm_install_deps_provider,
&text_only_progress_bar, &text_only_progress_bar,
resolution.clone(), resolution.clone(),
tarball_cache.clone(), tarball_cache.clone(),
@ -175,7 +176,7 @@ fn create_inner(
maybe_lockfile, maybe_lockfile,
npm_api, npm_api,
npm_cache, npm_cache,
package_json_deps_provider, npm_install_deps_provider,
resolution, resolution,
tarball_cache, tarball_cache,
text_only_progress_bar, text_only_progress_bar,
@ -261,7 +262,7 @@ pub struct ManagedCliNpmResolver {
maybe_lockfile: Option<Arc<CliLockfile>>, maybe_lockfile: Option<Arc<CliLockfile>>,
npm_api: Arc<CliNpmRegistryApi>, npm_api: Arc<CliNpmRegistryApi>,
npm_cache: Arc<NpmCache>, npm_cache: Arc<NpmCache>,
package_json_deps_provider: Arc<PackageJsonInstallDepsProvider>, npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
resolution: Arc<NpmResolution>, resolution: Arc<NpmResolution>,
tarball_cache: Arc<TarballCache>, tarball_cache: Arc<TarballCache>,
text_only_progress_bar: ProgressBar, text_only_progress_bar: ProgressBar,
@ -286,7 +287,7 @@ impl ManagedCliNpmResolver {
maybe_lockfile: Option<Arc<CliLockfile>>, maybe_lockfile: Option<Arc<CliLockfile>>,
npm_api: Arc<CliNpmRegistryApi>, npm_api: Arc<CliNpmRegistryApi>,
npm_cache: Arc<NpmCache>, npm_cache: Arc<NpmCache>,
package_json_deps_provider: Arc<PackageJsonInstallDepsProvider>, npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
resolution: Arc<NpmResolution>, resolution: Arc<NpmResolution>,
tarball_cache: Arc<TarballCache>, tarball_cache: Arc<TarballCache>,
text_only_progress_bar: ProgressBar, text_only_progress_bar: ProgressBar,
@ -299,7 +300,7 @@ impl ManagedCliNpmResolver {
maybe_lockfile, maybe_lockfile,
npm_api, npm_api,
npm_cache, npm_cache,
package_json_deps_provider, npm_install_deps_provider,
text_only_progress_bar, text_only_progress_bar,
resolution, resolution,
tarball_cache, tarball_cache,
@ -476,7 +477,7 @@ impl ManagedCliNpmResolver {
if !self.top_level_install_flag.raise() { if !self.top_level_install_flag.raise() {
return Ok(false); // already did this return Ok(false); // already did this
} }
let pkg_json_remote_pkgs = self.package_json_deps_provider.remote_pkgs(); let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs();
if pkg_json_remote_pkgs.is_empty() { if pkg_json_remote_pkgs.is_empty() {
return Ok(false); return Ok(false);
} }
@ -605,7 +606,7 @@ impl CliNpmResolver for ManagedCliNpmResolver {
create_npm_fs_resolver( create_npm_fs_resolver(
self.fs.clone(), self.fs.clone(),
self.npm_cache.clone(), self.npm_cache.clone(),
&self.package_json_deps_provider, &self.npm_install_deps_provider,
&self.text_only_progress_bar, &self.text_only_progress_bar,
npm_resolution.clone(), npm_resolution.clone(),
self.tarball_cache.clone(), self.tarball_cache.clone(),
@ -616,7 +617,7 @@ impl CliNpmResolver for ManagedCliNpmResolver {
self.maybe_lockfile.clone(), self.maybe_lockfile.clone(),
self.npm_api.clone(), self.npm_api.clone(),
self.npm_cache.clone(), self.npm_cache.clone(),
self.package_json_deps_provider.clone(), self.npm_install_deps_provider.clone(),
npm_resolution, npm_resolution,
self.tarball_cache.clone(), self.tarball_cache.clone(),
self.text_only_progress_bar.clone(), self.text_only_progress_bar.clone(),

View file

@ -41,7 +41,7 @@ use node_resolver::errors::ReferrerNotFoundError;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use crate::args::PackageJsonInstallDepsProvider; use crate::args::NpmInstallDepsProvider;
use crate::cache::CACHE_PERM; use crate::cache::CACHE_PERM;
use crate::npm::cache_dir::mixed_case_package_name_decode; use crate::npm::cache_dir::mixed_case_package_name_decode;
use crate::npm::cache_dir::mixed_case_package_name_encode; use crate::npm::cache_dir::mixed_case_package_name_encode;
@ -65,7 +65,7 @@ use super::common::RegistryReadPermissionChecker;
pub struct LocalNpmPackageResolver { pub struct LocalNpmPackageResolver {
cache: Arc<NpmCache>, cache: Arc<NpmCache>,
fs: Arc<dyn deno_fs::FileSystem>, fs: Arc<dyn deno_fs::FileSystem>,
pkg_json_deps_provider: Arc<PackageJsonInstallDepsProvider>, npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
progress_bar: ProgressBar, progress_bar: ProgressBar,
resolution: Arc<NpmResolution>, resolution: Arc<NpmResolution>,
tarball_cache: Arc<TarballCache>, tarball_cache: Arc<TarballCache>,
@ -81,7 +81,7 @@ impl LocalNpmPackageResolver {
pub fn new( pub fn new(
cache: Arc<NpmCache>, cache: Arc<NpmCache>,
fs: Arc<dyn deno_fs::FileSystem>, fs: Arc<dyn deno_fs::FileSystem>,
pkg_json_deps_provider: Arc<PackageJsonInstallDepsProvider>, npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
progress_bar: ProgressBar, progress_bar: ProgressBar,
resolution: Arc<NpmResolution>, resolution: Arc<NpmResolution>,
tarball_cache: Arc<TarballCache>, tarball_cache: Arc<TarballCache>,
@ -92,7 +92,7 @@ impl LocalNpmPackageResolver {
Self { Self {
cache, cache,
fs: fs.clone(), fs: fs.clone(),
pkg_json_deps_provider, npm_install_deps_provider,
progress_bar, progress_bar,
resolution, resolution,
tarball_cache, tarball_cache,
@ -248,7 +248,7 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver {
sync_resolution_with_fs( sync_resolution_with_fs(
&self.resolution.snapshot(), &self.resolution.snapshot(),
&self.cache, &self.cache,
&self.pkg_json_deps_provider, &self.npm_install_deps_provider,
&self.progress_bar, &self.progress_bar,
&self.tarball_cache, &self.tarball_cache,
&self.root_node_modules_path, &self.root_node_modules_path,
@ -412,14 +412,16 @@ fn has_lifecycle_scripts(
async fn sync_resolution_with_fs( async fn sync_resolution_with_fs(
snapshot: &NpmResolutionSnapshot, snapshot: &NpmResolutionSnapshot,
cache: &Arc<NpmCache>, cache: &Arc<NpmCache>,
pkg_json_deps_provider: &PackageJsonInstallDepsProvider, npm_install_deps_provider: &NpmInstallDepsProvider,
progress_bar: &ProgressBar, progress_bar: &ProgressBar,
tarball_cache: &Arc<TarballCache>, tarball_cache: &Arc<TarballCache>,
root_node_modules_dir_path: &Path, root_node_modules_dir_path: &Path,
system_info: &NpmSystemInfo, system_info: &NpmSystemInfo,
lifecycle_scripts: &LifecycleScriptsConfig, lifecycle_scripts: &LifecycleScriptsConfig,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
if snapshot.is_empty() && pkg_json_deps_provider.workspace_pkgs().is_empty() { if snapshot.is_empty()
&& npm_install_deps_provider.workspace_pkgs().is_empty()
{
return Ok(()); // don't create the directory return Ok(()); // don't create the directory
} }
@ -620,7 +622,7 @@ async fn sync_resolution_with_fs(
// 4. Create symlinks for package json dependencies // 4. Create symlinks for package json dependencies
{ {
for remote in pkg_json_deps_provider.remote_pkgs() { for remote in npm_install_deps_provider.remote_pkgs() {
let remote_pkg = if let Ok(remote_pkg) = let remote_pkg = if let Ok(remote_pkg) =
snapshot.resolve_pkg_from_pkg_req(&remote.req) snapshot.resolve_pkg_from_pkg_req(&remote.req)
{ {
@ -684,7 +686,7 @@ async fn sync_resolution_with_fs(
} }
// 5. Create symlinks for the remaining top level packages in the node_modules folder. // 5. Create symlinks for the remaining top level packages in the node_modules folder.
// (These may be present if they are not in the package.json dependencies, such as ) // (These may be present if they are not in the package.json dependencies)
// Symlink node_modules/.deno/<package_id>/node_modules/<package_name> to // Symlink node_modules/.deno/<package_id>/node_modules/<package_name> to
// node_modules/<package_name> // node_modules/<package_name>
let mut ids = snapshot let mut ids = snapshot
@ -757,10 +759,10 @@ async fn sync_resolution_with_fs(
// 8. Create symlinks for the workspace packages // 8. Create symlinks for the workspace packages
{ {
// todo(#24419): this is not exactly correct because it should // todo(dsherret): this is not exactly correct because it should
// install correctly for a workspace (potentially in sub directories), // install correctly for a workspace (potentially in sub directories),
// but this is good enough for a first pass // but this is good enough for a first pass
for workspace in pkg_json_deps_provider.workspace_pkgs() { for workspace in npm_install_deps_provider.workspace_pkgs() {
symlink_package_dir( symlink_package_dir(
&workspace.target_dir, &workspace.target_dir,
&root_node_modules_dir_path.join(&workspace.alias), &root_node_modules_dir_path.join(&workspace.alias),
@ -985,21 +987,31 @@ impl SetupCache {
} }
} }
/// Normalizes a package name for use at `node_modules/.deno/<pkg-name>@<version>[_<copy_index>]`
pub fn normalize_pkg_name_for_node_modules_deno_folder(name: &str) -> Cow<str> {
let name = if name.to_lowercase() == name {
Cow::Borrowed(name)
} else {
Cow::Owned(format!("_{}", mixed_case_package_name_encode(name)))
};
if name.starts_with('@') {
name.replace('/', "+").into()
} else {
name
}
}
fn get_package_folder_id_folder_name( fn get_package_folder_id_folder_name(
folder_id: &NpmPackageCacheFolderId, folder_id: &NpmPackageCacheFolderId,
) -> String { ) -> String {
let copy_str = if folder_id.copy_index == 0 { let copy_str = if folder_id.copy_index == 0 {
"".to_string() Cow::Borrowed("")
} else { } else {
format!("_{}", folder_id.copy_index) Cow::Owned(format!("_{}", folder_id.copy_index))
}; };
let nv = &folder_id.nv; let nv = &folder_id.nv;
let name = if nv.name.to_lowercase() == nv.name { let name = normalize_pkg_name_for_node_modules_deno_folder(&nv.name);
Cow::Borrowed(&nv.name) format!("{}@{}{}", name, nv.version, copy_str)
} else {
Cow::Owned(format!("_{}", mixed_case_package_name_encode(&nv.name)))
};
format!("{}@{}{}", name, nv.version, copy_str).replace('/', "+")
} }
fn get_package_folder_id_from_folder_name( fn get_package_folder_id_from_folder_name(

View file

@ -11,10 +11,11 @@ use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_fs::FileSystem;
use crate::args::LifecycleScriptsConfig; use crate::args::LifecycleScriptsConfig;
use crate::args::PackageJsonInstallDepsProvider; use crate::args::NpmInstallDepsProvider;
use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBar;
pub use self::common::NpmPackageFsResolver; pub use self::common::NpmPackageFsResolver;
pub use self::local::normalize_pkg_name_for_node_modules_deno_folder;
use self::global::GlobalNpmPackageResolver; use self::global::GlobalNpmPackageResolver;
use self::local::LocalNpmPackageResolver; use self::local::LocalNpmPackageResolver;
@ -27,7 +28,7 @@ use super::resolution::NpmResolution;
pub fn create_npm_fs_resolver( pub fn create_npm_fs_resolver(
fs: Arc<dyn FileSystem>, fs: Arc<dyn FileSystem>,
npm_cache: Arc<NpmCache>, npm_cache: Arc<NpmCache>,
pkg_json_deps_provider: &Arc<PackageJsonInstallDepsProvider>, npm_install_deps_provider: &Arc<NpmInstallDepsProvider>,
progress_bar: &ProgressBar, progress_bar: &ProgressBar,
resolution: Arc<NpmResolution>, resolution: Arc<NpmResolution>,
tarball_cache: Arc<TarballCache>, tarball_cache: Arc<TarballCache>,
@ -39,7 +40,7 @@ pub fn create_npm_fs_resolver(
Some(node_modules_folder) => Arc::new(LocalNpmPackageResolver::new( Some(node_modules_folder) => Arc::new(LocalNpmPackageResolver::new(
npm_cache, npm_cache,
fs, fs,
pkg_json_deps_provider.clone(), npm_install_deps_provider.clone(),
progress_bar.clone(), progress_bar.clone(),
resolution, resolution,
tarball_cache, tarball_cache,

View file

@ -502,7 +502,7 @@ impl Resolver for CliGraphResolver {
fn resolve( fn resolve(
&self, &self,
specifier: &str, raw_specifier: &str,
referrer_range: &deno_graph::Range, referrer_range: &deno_graph::Range,
mode: ResolutionMode, mode: ResolutionMode,
) -> Result<ModuleSpecifier, ResolveError> { ) -> Result<ModuleSpecifier, ResolveError> {
@ -519,7 +519,7 @@ impl Resolver for CliGraphResolver {
if let Some(node_resolver) = self.node_resolver.as_ref() { if let Some(node_resolver) = self.node_resolver.as_ref() {
if referrer.scheme() == "file" && node_resolver.in_npm_package(referrer) { if referrer.scheme() == "file" && node_resolver.in_npm_package(referrer) {
return node_resolver return node_resolver
.resolve(specifier, referrer, to_node_mode(mode)) .resolve(raw_specifier, referrer, to_node_mode(mode))
.map(|res| res.into_url()) .map(|res| res.into_url())
.map_err(|e| ResolveError::Other(e.into())); .map_err(|e| ResolveError::Other(e.into()));
} }
@ -528,7 +528,7 @@ impl Resolver for CliGraphResolver {
// Attempt to resolve with the workspace resolver // Attempt to resolve with the workspace resolver
let result: Result<_, ResolveError> = self let result: Result<_, ResolveError> = self
.workspace_resolver .workspace_resolver
.resolve(specifier, referrer) .resolve(raw_specifier, referrer)
.map_err(|err| match err { .map_err(|err| match err {
MappedResolutionError::Specifier(err) => ResolveError::Specifier(err), MappedResolutionError::Specifier(err) => ResolveError::Specifier(err),
MappedResolutionError::ImportMap(err) => { MappedResolutionError::ImportMap(err) => {
@ -700,7 +700,7 @@ impl Resolver for CliGraphResolver {
// If byonm, check if the bare specifier resolves to an npm package // If byonm, check if the bare specifier resolves to an npm package
if is_byonm && referrer.scheme() == "file" { if is_byonm && referrer.scheme() == "file" {
let maybe_resolution = node_resolver let maybe_resolution = node_resolver
.resolve_if_for_npm_pkg(specifier, referrer, to_node_mode(mode)) .resolve_if_for_npm_pkg(raw_specifier, referrer, to_node_mode(mode))
.map_err(ResolveError::Other)?; .map_err(ResolveError::Other)?;
if let Some(res) = maybe_resolution { if let Some(res) = maybe_resolution {
return Ok(res.into_url()); return Ok(res.into_url());

View file

@ -45,7 +45,7 @@ use serde::Serialize;
use crate::args::CaData; use crate::args::CaData;
use crate::args::CliOptions; use crate::args::CliOptions;
use crate::args::CompileFlags; use crate::args::CompileFlags;
use crate::args::PackageJsonInstallDepsProvider; use crate::args::NpmInstallDepsProvider;
use crate::args::PermissionFlags; use crate::args::PermissionFlags;
use crate::args::UnstableConfig; use crate::args::UnstableConfig;
use crate::cache::DenoDir; use crate::cache::DenoDir;

View file

@ -48,7 +48,7 @@ use crate::args::get_root_cert_store;
use crate::args::npm_pkg_req_ref_to_binary_command; use crate::args::npm_pkg_req_ref_to_binary_command;
use crate::args::CaData; use crate::args::CaData;
use crate::args::CacheSetting; use crate::args::CacheSetting;
use crate::args::PackageJsonInstallDepsProvider; use crate::args::NpmInstallDepsProvider;
use crate::args::StorageKeyResolver; use crate::args::StorageKeyResolver;
use crate::cache::Caches; use crate::cache::Caches;
use crate::cache::DenoDirProvider; use crate::cache::DenoDirProvider;
@ -138,7 +138,7 @@ pub const UNSUPPORTED_SCHEME: &str = "Unsupported scheme";
impl ModuleLoader for EmbeddedModuleLoader { impl ModuleLoader for EmbeddedModuleLoader {
fn resolve( fn resolve(
&self, &self,
specifier: &str, raw_specifier: &str,
referrer: &str, referrer: &str,
kind: ResolutionKind, kind: ResolutionKind,
) -> Result<ModuleSpecifier, AnyError> { ) -> Result<ModuleSpecifier, AnyError> {
@ -162,13 +162,15 @@ impl ModuleLoader for EmbeddedModuleLoader {
self self
.shared .shared
.node_resolver .node_resolver
.resolve(specifier, &referrer, NodeResolutionMode::Execution)? .resolve(raw_specifier, &referrer, NodeResolutionMode::Execution)?
.into_url(), .into_url(),
); );
} }
let mapped_resolution = let mapped_resolution = self
self.shared.workspace_resolver.resolve(specifier, &referrer); .shared
.workspace_resolver
.resolve(raw_specifier, &referrer);
match mapped_resolution { match mapped_resolution {
Ok(MappedResolution::WorkspaceJsrPackage { specifier, .. }) => { Ok(MappedResolution::WorkspaceJsrPackage { specifier, .. }) => {
@ -262,7 +264,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
if err.is_unmapped_bare_specifier() && referrer.scheme() == "file" => if err.is_unmapped_bare_specifier() && referrer.scheme() == "file" =>
{ {
let maybe_res = self.shared.node_resolver.resolve_if_for_npm_pkg( let maybe_res = self.shared.node_resolver.resolve_if_for_npm_pkg(
specifier, raw_specifier,
&referrer, &referrer,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
)?; )?;
@ -502,9 +504,9 @@ pub async fn run(
text_only_progress_bar: progress_bar, text_only_progress_bar: progress_bar,
maybe_node_modules_path, maybe_node_modules_path,
npm_system_info: Default::default(), npm_system_info: Default::default(),
package_json_deps_provider: Arc::new( npm_install_deps_provider: Arc::new(
// this is only used for installing packages, which isn't necessary with deno compile // this is only used for installing packages, which isn't necessary with deno compile
PackageJsonInstallDepsProvider::empty(), NpmInstallDepsProvider::empty(),
), ),
// create an npmrc that uses the fake npm_registry_url to resolve packages // create an npmrc that uses the fake npm_registry_url to resolve packages
npmrc: Arc::new(ResolvedNpmRc { npmrc: Arc::new(ResolvedNpmRc {
@ -554,9 +556,9 @@ pub async fn run(
text_only_progress_bar: progress_bar, text_only_progress_bar: progress_bar,
maybe_node_modules_path: None, maybe_node_modules_path: None,
npm_system_info: Default::default(), npm_system_info: Default::default(),
package_json_deps_provider: Arc::new( npm_install_deps_provider: Arc::new(
// this is only used for installing packages, which isn't necessary with deno compile // this is only used for installing packages, which isn't necessary with deno compile
PackageJsonInstallDepsProvider::empty(), NpmInstallDepsProvider::empty(),
), ),
// Packages from different registries are already inlined in the ESZip, // Packages from different registries are already inlined in the ESZip,
// so no need to create actual `.npmrc` configuration. // so no need to create actual `.npmrc` configuration.

View file

@ -795,7 +795,7 @@ fn resolve_graph_specifier_types(
} }
fn resolve_non_graph_specifier_types( fn resolve_non_graph_specifier_types(
specifier: &str, raw_specifier: &str,
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
referrer_kind: NodeModuleKind, referrer_kind: NodeModuleKind,
state: &State, state: &State,
@ -810,14 +810,16 @@ fn resolve_non_graph_specifier_types(
Ok(Some(NodeResolution::into_specifier_and_media_type( Ok(Some(NodeResolution::into_specifier_and_media_type(
node_resolver node_resolver
.resolve( .resolve(
specifier, raw_specifier,
referrer, referrer,
referrer_kind, referrer_kind,
NodeResolutionMode::Types, NodeResolutionMode::Types,
) )
.ok(), .ok(),
))) )))
} else if let Ok(npm_req_ref) = NpmPackageReqReference::from_str(specifier) { } else if let Ok(npm_req_ref) =
NpmPackageReqReference::from_str(raw_specifier)
{
debug_assert_eq!(referrer_kind, NodeModuleKind::Esm); debug_assert_eq!(referrer_kind, NodeModuleKind::Esm);
// todo(dsherret): add support for injecting this in the graph so // todo(dsherret): add support for injecting this in the graph so
// we don't need this special code here. // we don't need this special code here.

View file

@ -18,6 +18,7 @@ pub use npm::NpmResolverRc;
pub use package_json::load_pkg_json; pub use package_json::load_pkg_json;
pub use package_json::PackageJsonThreadLocalCache; pub use package_json::PackageJsonThreadLocalCache;
pub use path::PathClean; pub use path::PathClean;
pub use resolution::parse_npm_pkg_name;
pub use resolution::NodeModuleKind; pub use resolution::NodeModuleKind;
pub use resolution::NodeResolution; pub use resolution::NodeResolution;
pub use resolution::NodeResolutionMode; pub use resolution::NodeResolutionMode;

View file

@ -14797,7 +14797,7 @@ fn lsp_byonm() {
"severity": 1, "severity": 1,
"code": "resolver-error", "code": "resolver-error",
"source": "deno", "source": "deno",
"message": "Could not find a matching package for 'npm:chalk' in a package.json file. You must specify this as a package.json dependency when the node_modules folder is not managed by Deno.", "message": "Could not find a matching package for 'npm:chalk' in the node_modules directory. Ensure you have all your JSR and npm dependencies listed in your deno.json or package.json, then run `deno install`. Alternatively, turn on auto-install by specifying `\"nodeModulesDir\": \"auto\"` in your deno.json file.",
}, },
{ {
"range": { "range": {
@ -14842,7 +14842,7 @@ fn lsp_byonm() {
"severity": 1, "severity": 1,
"code": "resolver-error", "code": "resolver-error",
"source": "deno", "source": "deno",
"message": "Could not find a matching package for 'npm:chalk' in a package.json file. You must specify this as a package.json dependency when the node_modules folder is not managed by Deno.", "message": "Could not find a matching package for 'npm:chalk' in the node_modules directory. Ensure you have all your JSR and npm dependencies listed in your deno.json or package.json, then run `deno install`. Alternatively, turn on auto-install by specifying `\"nodeModulesDir\": \"auto\"` in your deno.json file.",
}, },
]) ])
); );

View file

@ -2195,7 +2195,7 @@ console.log(getKind());
.args("run --allow-read chalk.ts") .args("run --allow-read chalk.ts")
.run(); .run();
output.assert_matches_text( output.assert_matches_text(
r#"error: Could not find a matching package for 'npm:chalk@5' in a package.json file. You must specify this as a package.json dependency when the node_modules folder is not managed by Deno. r#"error: Could not find a matching package for 'npm:chalk@5' in the node_modules directory. Ensure you have all your JSR and npm dependencies listed in your deno.json or package.json, then run `deno install`. Alternatively, turn on auto-install by specifying `"nodeModulesDir": "auto"` in your deno.json file.
at file:///[WILDCARD]chalk.ts:1:19 at file:///[WILDCARD]chalk.ts:1:19
"#); "#);
output.assert_exit_code(1); output.assert_exit_code(1);
@ -2277,7 +2277,7 @@ console.log(getKind());
.args("run --allow-read chalk.ts") .args("run --allow-read chalk.ts")
.run(); .run();
output.assert_matches_text( output.assert_matches_text(
r#"error: Could not find a matching package for 'npm:chalk@5' in a package.json file. You must specify this as a package.json dependency when the node_modules folder is not managed by Deno. r#"error: Could not find a matching package for 'npm:chalk@5' in the node_modules directory. Ensure you have all your JSR and npm dependencies listed in your deno.json or package.json, then run `deno install`. Alternatively, turn on auto-install by specifying `"nodeModulesDir": "auto"` in your deno.json file.
at file:///[WILDCARD]chalk.ts:1:19 at file:///[WILDCARD]chalk.ts:1:19
"#); "#);
output.assert_exit_code(1); output.assert_exit_code(1);

View file

@ -0,0 +1,5 @@
import * as npmAdd from "npm:@denotest/add@0.5";
export function sum(a: number, b: number): number {
return npmAdd.sum(a, b);
}

View file

@ -0,0 +1,5 @@
{
"exports": {
".": "./mod.ts"
}
}

View file

@ -0,0 +1,5 @@
import * as npmAdd from "npm:@denotest/add@1";
export function add(a: number, b: number): number {
return npmAdd.add(a, b);
}

View file

@ -0,0 +1,5 @@
{
"exports": {
".": "./mod.ts"
}
}

View file

@ -0,0 +1,6 @@
{
"versions": {
"0.5.0": {},
"1.0.0": {}
}
}

View file

@ -0,0 +1,10 @@
{
"tempDir": true,
"steps": [{
"args": "install",
"output": "[WILDCARD]"
}, {
"args": "run --allow-read=. verify.ts",
"output": "true\n"
}]
}

View file

@ -0,0 +1,5 @@
{
"imports": {
"alias": "npm:@denotest/add"
}
}

View file

@ -0,0 +1,2 @@
{
}

View file

@ -0,0 +1,2 @@
const stat = Deno.statSync(new URL("./node_modules/alias", import.meta.url));
console.log(stat.isDirectory);

View file

@ -0,0 +1,10 @@
{
"tempDir": true,
"steps": [{
"args": "install",
"output": "[WILDCARD]"
}, {
"args": "run --allow-read=. verify.ts",
"output": ".bin\n.deno\n@denotest\n"
}]
}

View file

@ -0,0 +1,7 @@
{
"imports": {
// alias*test is an invalid path char on windows, so
// don't create an alias for this
"alias*test": "npm:@denotest/add"
}
}

View file

@ -0,0 +1,2 @@
{
}

View file

@ -0,0 +1,10 @@
const entries = Array.from(
Deno.readDirSync(new URL("./node_modules", import.meta.url)),
);
const names = entries.map((entry) => entry.name);
names.sort();
// won't have the invalid path alias
for (const name of names) {
console.log(name);
}

View file

@ -0,0 +1,10 @@
{
"tempDir": true,
"steps": [{
"args": "install",
"output": "[WILDCARD]"
}, {
"args": "run --allow-read=. verify.ts",
"output": "verify.out"
}]
}

View file

@ -0,0 +1,5 @@
{
"imports": {
"alias": "jsr:@denotest/add"
}
}

View file

@ -0,0 +1,5 @@
{
"dependencies": {
"alias": "npm:@denotest/esm-basic"
}
}

View file

@ -0,0 +1,2 @@
@denotest/esm-basic
[Module: null prototype] { add: [Function: add] }

View file

@ -0,0 +1,13 @@
import * as mod from "alias";
const data = JSON.parse(
Deno.readTextFileSync(
new URL("./node_modules/alias/package.json", import.meta.url),
),
);
// this should just setup the npm package anyway, even though the alias
// will resolve to the jsr package
console.log(data.name);
console.log(mod);

View file

@ -0,0 +1,10 @@
{
"tempDir": true,
"steps": [{
"args": "install",
"output": "[WILDCARD]"
}, {
"args": "run --allow-read=. verify.ts",
"output": "verify.out"
}]
}

View file

@ -0,0 +1,5 @@
{
"imports": {
"alias": "npm:@denotest/add"
}
}

View file

@ -0,0 +1,5 @@
{
"dependencies": {
"alias": "npm:@denotest/esm-basic"
}
}

View file

@ -0,0 +1,5 @@
@denotest/add
[Module: null prototype] {
add: [Function (anonymous)],
default: { add: [Function (anonymous)] }
}

View file

@ -0,0 +1,10 @@
import * as mod from "alias";
const data = JSON.parse(
Deno.readTextFileSync(
new URL("./node_modules/alias/package.json", import.meta.url),
),
);
console.log(data.name);
console.log(mod);

View file

@ -0,0 +1,10 @@
{
"tempDir": true,
"steps": [{
"args": "install",
"output": "[WILDCARD]"
}, {
"args": "run main.ts",
"output": "3\n4\n"
}]
}

View file

@ -0,0 +1,6 @@
{
"imports": {
"@denotest/npm-add": "jsr:@denotest/npm-add@1",
"@denotest/npm-add-0-5": "jsr:@denotest/npm-add@0.5"
}
}

View file

@ -0,0 +1,5 @@
import { add } from "@denotest/npm-add";
import { sum } from "@denotest/npm-add-0-5";
console.log(add(1, 2));
console.log(sum(2, 2));

View file

@ -0,0 +1,2 @@
{
}

View file

@ -0,0 +1,13 @@
{
"tempDir": true,
"steps": [{
"args": "install",
"output": "[WILDCARD]"
}, {
"args": "run --allow-read main.js",
"output": "4\n0.5.0\n"
}, {
"args": "run --allow-read member/main.js",
"output": "3\n1.0.0\n"
}]
}

View file

@ -0,0 +1,7 @@
{
"nodeModulesDir": "manual",
"workspace": ["./member"],
"imports": {
"@denotest/add": "npm:@denotest/add@0.5"
}
}

View file

@ -0,0 +1,9 @@
import { sum } from "@denotest/add";
console.log(sum(2, 2));
console.log(
JSON.parse(Deno.readTextFileSync(
new URL("node_modules/@denotest/add/package.json", import.meta.url),
)).version,
);

View file

@ -0,0 +1,5 @@
{
"imports": {
"@denotest/add": "npm:@denotest/add@1"
}
}

View file

@ -0,0 +1,9 @@
import { add } from "@denotest/add";
console.log(add(1, 2));
console.log(
JSON.parse(Deno.readTextFileSync(
new URL("node_modules/@denotest/add/package.json", import.meta.url),
)).version,
);