diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2024-09-04 16:00:44 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-04 14:00:44 +0000 |
commit | c6d1b0a1ccf45b7819b1e6f1efe8687b240f495a (patch) | |
tree | 6be2c0c611e4aee950402a34aaedd1c9b6bcaac3 /cli/args/package_json.rs | |
parent | 13911eb8efb77bd14a80412072aecb664aa55fd5 (diff) |
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.
Diffstat (limited to 'cli/args/package_json.rs')
-rw-r--r-- | cli/args/package_json.rs | 129 |
1 files changed, 92 insertions, 37 deletions
diff --git a/cli/args/package_json.rs b/cli/args/package_json.rs index eedd0a194..b9f0919d5 100644 --- a/cli/args/package_json.rs +++ b/cli/args/package_json.rs @@ -1,17 +1,20 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::collections::HashSet; use std::path::PathBuf; use std::sync::Arc; use deno_config::workspace::Workspace; +use deno_core::serde_json; use deno_package_json::PackageJsonDepValue; +use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageReq; +use crate::util::path::is_banned_path_char; + #[derive(Debug)] pub struct InstallNpmRemotePkg { pub alias: String, - // todo(24419): use this when setting up the node_modules dir - #[allow(dead_code)] pub base_dir: PathBuf, pub req: PackageReq, } @@ -19,74 +22,126 @@ pub struct InstallNpmRemotePkg { #[derive(Debug)] pub struct InstallNpmWorkspacePkg { 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, } #[derive(Debug, Default)] -pub struct PackageJsonInstallDepsProvider { +pub struct NpmInstallDepsProvider { remote_pkgs: Vec<InstallNpmRemotePkg>, workspace_pkgs: Vec<InstallNpmWorkspacePkg>, } -impl PackageJsonInstallDepsProvider { +impl NpmInstallDepsProvider { pub fn empty() -> Self { Self::default() } pub fn from_workspace(workspace: &Arc<Workspace>) -> Self { + // todo(dsherret): estimate capacity? let mut workspace_pkgs = Vec::new(); let mut remote_pkgs = Vec::new(); let workspace_npm_pkgs = workspace.npm_packages(); - for pkg_json in workspace.package_jsons() { - 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; - }; - 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 - }); + + for (_, folder) in workspace.config_folders() { + let mut deno_json_aliases = HashSet::new(); + + // deal with the deno.json first because it takes precedence during resolution + if let Some(deno_json) = &folder.deno_json { + // don't bother with externally referenced import maps as users + // should inline their import map to get this behaviour + if let Some(serde_json::Value::Object(obj)) = &deno_json.json.imports { + deno_json_aliases.reserve(obj.len()); + let mut pkg_pkgs = Vec::with_capacity(obj.len()); + for (alias, value) in obj { + 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 { workspace_pkgs.push(InstallNpmWorkspacePkg { - alias, - base_dir: pkg_json.dir_path().to_path_buf(), + alias: alias.to_string(), 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(), + alias: alias.to_string(), + base_dir: deno_json.dir_path(), 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, - base_dir: pkg_json.dir_path().to_path_buf(), - target_dir: pkg.pkg_json.dir_path().to_path_buf(), + + // sort within each package (more like npm resolution) + pkg_pkgs.sort_by(|a, b| a.alias.cmp(&b.alias)); + remote_pkgs.extend(pkg_pkgs); + } + } + + 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(); workspace_pkgs.shrink_to_fit(); Self { |