mirror of
https://github.com/denoland/deno.git
synced 2024-11-25 15:29:32 -05:00
fix(install): surface package.json dependency errors (#26023)
This commit is contained in:
parent
b8a9a4a862
commit
edac916604
10 changed files with 96 additions and 52 deletions
|
@ -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"),
|
||||||
|
|
|
@ -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 {
|
||||||
|
Ok(dep) => dep,
|
||||||
|
Err(err) => {
|
||||||
|
pkg_json_dep_errors.push(err);
|
||||||
continue;
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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>)>,
|
||||||
>(
|
>(
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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",
|
"args": "install",
|
||||||
"output": "install.out"
|
"output": "install.out",
|
||||||
|
"exitCode": 1
|
||||||
},
|
},
|
||||||
{
|
"add_error_byonm": {
|
||||||
"args": "run error.ts",
|
"args": "add npm:cowsay",
|
||||||
"exitCode": 1,
|
"output": "add.out",
|
||||||
"output": "error.out"
|
"exitCode": 1
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
// should output a warning about the failing dep entry
|
|
||||||
"task_test": {
|
"task_test": {
|
||||||
"steps": [
|
"args": "task --node-modules-dir=auto test",
|
||||||
{
|
|
||||||
"args": "install",
|
|
||||||
"output": "install.out"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"args": "task test",
|
|
||||||
"output": "task.out"
|
"output": "task.out"
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
8
tests/specs/run/package_json/invalid_value/add.out
Normal file
8
tests/specs/run/package_json/invalid_value/add.out
Normal 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
|
||||||
|
~
|
|
@ -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
|
||||||
|
~
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue