summaryrefslogtreecommitdiff
path: root/cli/args/package_json.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2024-09-04 16:00:44 +0200
committerGitHub <noreply@github.com>2024-09-04 14:00:44 +0000
commitc6d1b0a1ccf45b7819b1e6f1efe8687b240f495a (patch)
tree6be2c0c611e4aee950402a34aaedd1c9b6bcaac3 /cli/args/package_json.rs
parent13911eb8efb77bd14a80412072aecb664aa55fd5 (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.rs129
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 {