summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2024-10-04 08:52:00 +0100
committerGitHub <noreply@github.com>2024-10-04 07:52:00 +0000
commitedac9166040dc09674072ce57af6a9c5ea958d85 (patch)
tree0a54e2946b8d6146e3620b4859c6d609d1c657ee
parentb8a9a4a862e4d61630c5bc8089261c7a177ec97a (diff)
fix(install): surface package.json dependency errors (#26023)
-rw-r--r--cli/args/flags.rs2
-rw-r--r--cli/args/package_json.rs20
-rw-r--r--cli/npm/managed/mod.rs21
-rw-r--r--cli/tools/installer.rs4
-rw-r--r--cli/tools/registry/pm.rs33
-rw-r--r--cli/tools/registry/pm/cache_deps.rs1
-rw-r--r--tests/specs/run/package_json/invalid_value/__test__.jsonc46
-rw-r--r--tests/specs/run/package_json/invalid_value/add.out8
-rw-r--r--tests/specs/run/package_json/invalid_value/install.out10
-rw-r--r--tests/specs/run/package_json/invalid_value/task.out3
10 files changed, 96 insertions, 52 deletions
diff --git a/cli/args/flags.rs b/cli/args/flags.rs
index 06ce50783..258712ca9 100644
--- a/cli/args/flags.rs
+++ b/cli/args/flags.rs
@@ -2910,6 +2910,7 @@ List all available tasks:
.help("Specify the directory to run the task in")
.value_hint(ValueHint::DirPath),
)
+ .arg(node_modules_dir_arg())
})
}
@@ -4974,6 +4975,7 @@ fn task_parse(flags: &mut Flags, matches: &mut ArgMatches) {
.unwrap_or(ConfigFlag::Discover);
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime);
+ node_modules_arg_parse(flags, matches);
let mut task_flags = TaskFlags {
cwd: matches.remove_one::<String>("cwd"),
diff --git a/cli/args/package_json.rs b/cli/args/package_json.rs
index 2529e54fd..2ef39a30d 100644
--- a/cli/args/package_json.rs
+++ b/cli/args/package_json.rs
@@ -6,6 +6,7 @@ use std::sync::Arc;
use deno_config::workspace::Workspace;
use deno_core::serde_json;
use deno_package_json::PackageJsonDepValue;
+use deno_package_json::PackageJsonDepValueParseError;
use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageReq;
@@ -26,6 +27,7 @@ pub struct InstallNpmWorkspacePkg {
pub struct NpmInstallDepsProvider {
remote_pkgs: Vec<InstallNpmRemotePkg>,
workspace_pkgs: Vec<InstallNpmWorkspacePkg>,
+ pkg_json_dep_errors: Vec<PackageJsonDepValueParseError>,
}
impl NpmInstallDepsProvider {
@@ -37,6 +39,7 @@ impl NpmInstallDepsProvider {
// todo(dsherret): estimate capacity?
let mut workspace_pkgs = Vec::new();
let mut remote_pkgs = Vec::new();
+ let mut pkg_json_dep_errors = Vec::new();
let workspace_npm_pkgs = workspace.npm_packages();
for (_, folder) in workspace.config_folders() {
@@ -83,8 +86,12 @@ impl NpmInstallDepsProvider {
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;
+ let dep = match dep {
+ Ok(dep) => dep,
+ Err(err) => {
+ pkg_json_dep_errors.push(err);
+ continue;
+ }
};
match dep {
PackageJsonDepValue::Req(pkg_req) => {
@@ -131,14 +138,19 @@ impl NpmInstallDepsProvider {
Self {
remote_pkgs,
workspace_pkgs,
+ pkg_json_dep_errors,
}
}
- pub fn remote_pkgs(&self) -> &Vec<InstallNpmRemotePkg> {
+ pub fn remote_pkgs(&self) -> &[InstallNpmRemotePkg] {
&self.remote_pkgs
}
- pub fn workspace_pkgs(&self) -> &Vec<InstallNpmWorkspacePkg> {
+ pub fn workspace_pkgs(&self) -> &[InstallNpmWorkspacePkg] {
&self.workspace_pkgs
}
+
+ pub fn pkg_json_dep_errors(&self) -> &[PackageJsonDepValueParseError] {
+ &self.pkg_json_dep_errors
+ }
}
diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs
index 7bb254cb5..225cd6c29 100644
--- a/cli/npm/managed/mod.rs
+++ b/cli/npm/managed/mod.rs
@@ -20,6 +20,7 @@ use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo;
+use deno_runtime::colors;
use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NodeRequireResolver;
@@ -478,6 +479,25 @@ impl ManagedCliNpmResolver {
self.resolution.resolve_pkg_id_from_pkg_req(req)
}
+ pub fn ensure_no_pkg_json_dep_errors(&self) -> Result<(), AnyError> {
+ for err in self.npm_install_deps_provider.pkg_json_dep_errors() {
+ match err {
+ deno_package_json::PackageJsonDepValueParseError::VersionReq(_) => {
+ return Err(
+ AnyError::from(err.clone())
+ .context("Failed to install from package.json"),
+ );
+ }
+ deno_package_json::PackageJsonDepValueParseError::Unsupported {
+ ..
+ } => {
+ log::warn!("{} {} in package.json", colors::yellow("Warning"), err)
+ }
+ }
+ }
+ Ok(())
+ }
+
/// Ensures that the top level `package.json` dependencies are installed.
/// This may set up the `node_modules` directory.
///
@@ -489,6 +509,7 @@ impl ManagedCliNpmResolver {
if !self.top_level_install_flag.raise() {
return Ok(false); // already did this
}
+
let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs();
if pkg_json_remote_pkgs.is_empty() {
return Ok(false);
diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs
index bc8769de1..ed86e86c7 100644
--- a/cli/tools/installer.rs
+++ b/cli/tools/installer.rs
@@ -298,6 +298,10 @@ async fn install_local(
}
InstallFlagsLocal::TopLevel => {
let factory = CliFactory::from_flags(flags);
+ // surface any errors in the package.json
+ if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() {
+ npm_resolver.ensure_no_pkg_json_dep_errors()?;
+ }
crate::tools::registry::cache_top_level_deps(&factory, None).await?;
if let Some(lockfile) = factory.cli_options()?.maybe_lockfile() {
diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs
index c92710f46..f56774e8e 100644
--- a/cli/tools/registry/pm.rs
+++ b/cli/tools/registry/pm.rs
@@ -558,12 +558,7 @@ pub async fn add(
result.context("Failed to update configuration file")?;
}
- // clear the previously cached package.json from memory before reloading it
- node_resolver::PackageJsonThreadLocalCache::clear();
- // make a new CliFactory to pick up the updated config file
- let cli_factory = CliFactory::from_flags(flags);
- // cache deps
- cache_deps::cache_top_level_deps(&cli_factory, Some(jsr_resolver)).await?;
+ npm_install_after_modification(flags, Some(jsr_resolver)).await?;
Ok(())
}
@@ -786,11 +781,29 @@ pub async fn remove(
config.commit().await?;
}
- // Update deno.lock
- node_resolver::PackageJsonThreadLocalCache::clear();
- let cli_factory = CliFactory::from_flags(flags);
- cache_deps::cache_top_level_deps(&cli_factory, None).await?;
+ npm_install_after_modification(flags, None).await?;
+ }
+
+ Ok(())
+}
+
+async fn npm_install_after_modification(
+ flags: Arc<Flags>,
+ // explicitly provided to prevent redownloading
+ jsr_resolver: Option<Arc<crate::jsr::JsrFetchResolver>>,
+) -> Result<(), AnyError> {
+ // clear the previously cached package.json from memory before reloading it
+ node_resolver::PackageJsonThreadLocalCache::clear();
+
+ // make a new CliFactory to pick up the updated config file
+ let cli_factory = CliFactory::from_flags(flags);
+ // surface any errors in the package.json
+ let npm_resolver = cli_factory.npm_resolver().await?;
+ if let Some(npm_resolver) = npm_resolver.as_managed() {
+ npm_resolver.ensure_no_pkg_json_dep_errors()?;
}
+ // npm install
+ cache_deps::cache_top_level_deps(&cli_factory, jsr_resolver).await?;
Ok(())
}
diff --git a/cli/tools/registry/pm/cache_deps.rs b/cli/tools/registry/pm/cache_deps.rs
index 7d1773b34..c8258e600 100644
--- a/cli/tools/registry/pm/cache_deps.rs
+++ b/cli/tools/registry/pm/cache_deps.rs
@@ -11,6 +11,7 @@ use deno_core::futures::StreamExt;
use deno_semver::package::PackageReq;
pub async fn cache_top_level_deps(
+ // todo(dsherret): don't pass the factory into this function. Instead use ctor deps
factory: &CliFactory,
jsr_resolver: Option<Arc<crate::jsr::JsrFetchResolver>>,
) -> Result<(), AnyError> {
diff --git a/tests/specs/run/package_json/invalid_value/__test__.jsonc b/tests/specs/run/package_json/invalid_value/__test__.jsonc
index 0b3c63384..195734d95 100644
--- a/tests/specs/run/package_json/invalid_value/__test__.jsonc
+++ b/tests/specs/run/package_json/invalid_value/__test__.jsonc
@@ -6,49 +6,25 @@
"args": "run --quiet --node-modules-dir=auto ok.ts",
"output": "ok.ts.out"
},
- "run_ok_byonm": {
- "steps": [
- {
- "args": "install",
- "output": "install.out"
- },
- {
- "args": "run ok.ts",
- "output": "ok.ts.out"
- }
- ]
- },
// should fail when referencing a failing dep entry
"run_error_auto": {
"args": "run --node-modules-dir=auto error.ts",
"exitCode": 1,
"output": "error_auto.out"
},
- "run_error_byonm": {
- "steps": [
- {
- "args": "install",
- "output": "install.out"
- },
- {
- "args": "run error.ts",
- "exitCode": 1,
- "output": "error.out"
- }
- ]
+ "install_error_byonm": {
+ "args": "install",
+ "output": "install.out",
+ "exitCode": 1
+ },
+ "add_error_byonm": {
+ "args": "add npm:cowsay",
+ "output": "add.out",
+ "exitCode": 1
},
- // should output a warning about the failing dep entry
"task_test": {
- "steps": [
- {
- "args": "install",
- "output": "install.out"
- },
- {
- "args": "task test",
- "output": "task.out"
- }
- ]
+ "args": "task --node-modules-dir=auto test",
+ "output": "task.out"
}
}
}
diff --git a/tests/specs/run/package_json/invalid_value/add.out b/tests/specs/run/package_json/invalid_value/add.out
new file mode 100644
index 000000000..9b7493c1a
--- /dev/null
+++ b/tests/specs/run/package_json/invalid_value/add.out
@@ -0,0 +1,8 @@
+Add npm:cowsay@1.5.0
+error: Failed to install from package.json
+
+Caused by:
+ 0: Invalid version requirement
+ 1: Unexpected character.
+ invalid stuff that won't parse
+ ~
diff --git a/tests/specs/run/package_json/invalid_value/install.out b/tests/specs/run/package_json/invalid_value/install.out
index b8114c12a..cc82b345b 100644
--- a/tests/specs/run/package_json/invalid_value/install.out
+++ b/tests/specs/run/package_json/invalid_value/install.out
@@ -1,3 +1,7 @@
-Download http://localhost:4260/@denotest/esm-basic
-Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz
-Initialize @denotest/esm-basic@1.0.0
+error: Failed to install from package.json
+
+Caused by:
+ 0: Invalid version requirement
+ 1: Unexpected character.
+ invalid stuff that won't parse
+ ~
diff --git a/tests/specs/run/package_json/invalid_value/task.out b/tests/specs/run/package_json/invalid_value/task.out
index 79249d175..d0adb0525 100644
--- a/tests/specs/run/package_json/invalid_value/task.out
+++ b/tests/specs/run/package_json/invalid_value/task.out
@@ -1,2 +1,5 @@
+Download http://localhost:4260/@denotest/esm-basic
+Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz
+Initialize @denotest/esm-basic@1.0.0
Task test echo 1
1