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

fix(install): surface package.json dependency errors (#26023)

This commit is contained in:
David Sherret 2024-10-04 08:52:00 +01:00 committed by GitHub
parent b8a9a4a862
commit edac916604
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 96 additions and 52 deletions

View file

@ -2910,6 +2910,7 @@ List all available tasks:
.help("Specify the directory to run the task in") .help("Specify the directory to run the task in")
.value_hint(ValueHint::DirPath), .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); .unwrap_or(ConfigFlag::Discover);
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime); unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime);
node_modules_arg_parse(flags, matches);
let mut task_flags = TaskFlags { let mut task_flags = TaskFlags {
cwd: matches.remove_one::<String>("cwd"), cwd: matches.remove_one::<String>("cwd"),

View file

@ -6,6 +6,7 @@ use std::sync::Arc;
use deno_config::workspace::Workspace; use deno_config::workspace::Workspace;
use deno_core::serde_json; use deno_core::serde_json;
use deno_package_json::PackageJsonDepValue; use deno_package_json::PackageJsonDepValue;
use deno_package_json::PackageJsonDepValueParseError;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
@ -26,6 +27,7 @@ pub struct InstallNpmWorkspacePkg {
pub struct NpmInstallDepsProvider { pub struct NpmInstallDepsProvider {
remote_pkgs: Vec<InstallNpmRemotePkg>, remote_pkgs: Vec<InstallNpmRemotePkg>,
workspace_pkgs: Vec<InstallNpmWorkspacePkg>, workspace_pkgs: Vec<InstallNpmWorkspacePkg>,
pkg_json_dep_errors: Vec<PackageJsonDepValueParseError>,
} }
impl NpmInstallDepsProvider { impl NpmInstallDepsProvider {
@ -37,6 +39,7 @@ impl NpmInstallDepsProvider {
// todo(dsherret): estimate capacity? // 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 mut pkg_json_dep_errors = Vec::new();
let workspace_npm_pkgs = workspace.npm_packages(); let workspace_npm_pkgs = workspace.npm_packages();
for (_, folder) in workspace.config_folders() { for (_, folder) in workspace.config_folders() {
@ -83,8 +86,12 @@ impl NpmInstallDepsProvider {
let deps = pkg_json.resolve_local_package_json_deps(); let deps = pkg_json.resolve_local_package_json_deps();
let mut pkg_pkgs = Vec::with_capacity(deps.len()); let mut pkg_pkgs = Vec::with_capacity(deps.len());
for (alias, dep) in deps { for (alias, dep) in deps {
let Ok(dep) = dep else { let dep = match dep {
continue; Ok(dep) => dep,
Err(err) => {
pkg_json_dep_errors.push(err);
continue;
}
}; };
match dep { match dep {
PackageJsonDepValue::Req(pkg_req) => { PackageJsonDepValue::Req(pkg_req) => {
@ -131,14 +138,19 @@ impl NpmInstallDepsProvider {
Self { Self {
remote_pkgs, remote_pkgs,
workspace_pkgs, workspace_pkgs,
pkg_json_dep_errors,
} }
} }
pub fn remote_pkgs(&self) -> &Vec<InstallNpmRemotePkg> { pub fn remote_pkgs(&self) -> &[InstallNpmRemotePkg] {
&self.remote_pkgs &self.remote_pkgs
} }
pub fn workspace_pkgs(&self) -> &Vec<InstallNpmWorkspacePkg> { pub fn workspace_pkgs(&self) -> &[InstallNpmWorkspacePkg] {
&self.workspace_pkgs &self.workspace_pkgs
} }
pub fn pkg_json_dep_errors(&self) -> &[PackageJsonDepValueParseError] {
&self.pkg_json_dep_errors
}
} }

View file

@ -20,6 +20,7 @@ use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmPackageId; use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage; use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo; use deno_npm::NpmSystemInfo;
use deno_runtime::colors;
use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NodeRequireResolver; use deno_runtime::deno_node::NodeRequireResolver;
@ -478,6 +479,25 @@ impl ManagedCliNpmResolver {
self.resolution.resolve_pkg_id_from_pkg_req(req) 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. /// Ensures that the top level `package.json` dependencies are installed.
/// This may set up the `node_modules` directory. /// This may set up the `node_modules` directory.
/// ///
@ -489,6 +509,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.npm_install_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);

View file

@ -298,6 +298,10 @@ async fn install_local(
} }
InstallFlagsLocal::TopLevel => { InstallFlagsLocal::TopLevel => {
let factory = CliFactory::from_flags(flags); 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?; crate::tools::registry::cache_top_level_deps(&factory, None).await?;
if let Some(lockfile) = factory.cli_options()?.maybe_lockfile() { if let Some(lockfile) = factory.cli_options()?.maybe_lockfile() {

View file

@ -558,12 +558,7 @@ pub async fn add(
result.context("Failed to update configuration file")?; result.context("Failed to update configuration file")?;
} }
// clear the previously cached package.json from memory before reloading it npm_install_after_modification(flags, Some(jsr_resolver)).await?;
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?;
Ok(()) Ok(())
} }
@ -786,15 +781,33 @@ pub async fn remove(
config.commit().await?; config.commit().await?;
} }
// Update deno.lock npm_install_after_modification(flags, None).await?;
node_resolver::PackageJsonThreadLocalCache::clear();
let cli_factory = CliFactory::from_flags(flags);
cache_deps::cache_top_level_deps(&cli_factory, None).await?;
} }
Ok(()) 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(())
}
fn update_config_file_content< fn update_config_file_content<
I: IntoIterator<Item = (&'static str, Option<String>)>, I: IntoIterator<Item = (&'static str, Option<String>)>,
>( >(

View file

@ -11,6 +11,7 @@ use deno_core::futures::StreamExt;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
pub async fn cache_top_level_deps( pub async fn cache_top_level_deps(
// todo(dsherret): don't pass the factory into this function. Instead use ctor deps
factory: &CliFactory, factory: &CliFactory,
jsr_resolver: Option<Arc<crate::jsr::JsrFetchResolver>>, jsr_resolver: Option<Arc<crate::jsr::JsrFetchResolver>>,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {

View file

@ -6,49 +6,25 @@
"args": "run --quiet --node-modules-dir=auto ok.ts", "args": "run --quiet --node-modules-dir=auto ok.ts",
"output": "ok.ts.out" "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 // should fail when referencing a failing dep entry
"run_error_auto": { "run_error_auto": {
"args": "run --node-modules-dir=auto error.ts", "args": "run --node-modules-dir=auto error.ts",
"exitCode": 1, "exitCode": 1,
"output": "error_auto.out" "output": "error_auto.out"
}, },
"run_error_byonm": { "install_error_byonm": {
"steps": [ "args": "install",
{ "output": "install.out",
"args": "install", "exitCode": 1
"output": "install.out" },
}, "add_error_byonm": {
{ "args": "add npm:cowsay",
"args": "run error.ts", "output": "add.out",
"exitCode": 1, "exitCode": 1
"output": "error.out"
}
]
}, },
// should output a warning about the failing dep entry
"task_test": { "task_test": {
"steps": [ "args": "task --node-modules-dir=auto test",
{ "output": "task.out"
"args": "install",
"output": "install.out"
},
{
"args": "task test",
"output": "task.out"
}
]
} }
} }
} }

View file

@ -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
~

View file

@ -1,3 +1,7 @@
Download http://localhost:4260/@denotest/esm-basic error: Failed to install from package.json
Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz
Initialize @denotest/esm-basic@1.0.0 Caused by:
0: Invalid version requirement
1: Unexpected character.
invalid stuff that won't parse
~

View file

@ -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 Task test echo 1
1 1