mirror of
https://github.com/denoland/deno.git
synced 2025-09-28 05:04:48 +00:00
fix(install): handle when bin entry info isn't present in package.json but is in registry (#28822)
Apparently things like the `bin` field can appear in the version info from the registry, but not the package's `package.json`. I'm still not sure how you actually achieve this, but it's the case for `esbuild-wasm`. This fixes the following panic: ``` ❯ deno i --node-modules-dir npm:esbuild-wasm Add npm:esbuild-wasm@0.25.2 Initialize ⣯ [00:00] - esbuild-wasm@0.25.2 ============================================================ Deno has panicked. This is a bug in Deno. Please report this at https://github.com/denoland/deno/issues/new. If you can reliably reproduce this panic, include the reproduction steps and re-run with the RUST_BACKTRACE=1 env var set and include the backtrace in your report. Platform: macos aarch64 Version: 2.2.8+58c6c0b Args: ["deno", "i", "--node-modules-dir", "npm:esbuild-wasm"] View stack trace at: https://panic.deno.com/v2.2.8+58c6c0bc9c1b4ee08645be936ff9268f17028f0f/aarch64-apple-darwin/g4h6Jo393pB4k4kqBo-3kqBg6klqBogtyLg13yLw_t0Lw549Hgj8-Hgw__H428-F4yv_HgjkpKww7gIon4gIw54rKwi5MorzMw5y7G42g7Iw---I40s-I4vu4Jw2rEw8z7Dwnr6J4tp7Bo_vvK thread 'main' panicked at cli/npm/installer/common/bin_entries.rs:108:30: called `Option::unwrap()` on a `None` value note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ```
This commit is contained in:
parent
ac5c6018a8
commit
ce5b9da11b
14 changed files with 226 additions and 38 deletions
|
@ -1225,7 +1225,15 @@ impl CliOptions {
|
|||
}
|
||||
|
||||
pub fn default_npm_caching_strategy(&self) -> NpmCachingStrategy {
|
||||
if self.flags.unstable_config.npm_lazy_caching {
|
||||
if matches!(
|
||||
self.sub_command(),
|
||||
DenoSubcommand::Install(InstallFlags::Local(
|
||||
InstallFlagsLocal::TopLevel | InstallFlagsLocal::Add(_)
|
||||
)) | DenoSubcommand::Add(_)
|
||||
| DenoSubcommand::Outdated(_)
|
||||
) {
|
||||
NpmCachingStrategy::Manual
|
||||
} else if self.flags.unstable_config.npm_lazy_caching {
|
||||
NpmCachingStrategy::Lazy
|
||||
} else {
|
||||
NpmCachingStrategy::Eager
|
||||
|
|
|
@ -437,7 +437,10 @@ async fn resolve_custom_commands_from_packages<
|
|||
extra.clone()
|
||||
} else {
|
||||
let Ok(extra) = provider
|
||||
.get_package_extra_info(&package.id.nv, package.is_deprecated)
|
||||
.get_package_extra_info(
|
||||
&package.id.nv,
|
||||
super::ExpectedExtraInfo::from_package(package),
|
||||
)
|
||||
.await
|
||||
else {
|
||||
continue;
|
||||
|
|
|
@ -8,6 +8,7 @@ use deno_core::parking_lot::RwLock;
|
|||
use deno_error::JsErrorBox;
|
||||
use deno_npm::registry::NpmRegistryApi;
|
||||
use deno_npm::NpmPackageExtraInfo;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
use deno_semver::package::PackageNv;
|
||||
|
||||
use super::PackageCaching;
|
||||
|
@ -29,7 +30,7 @@ pub trait NpmPackageExtraInfoProvider: std::fmt::Debug + Send + Sync {
|
|||
async fn get_package_extra_info(
|
||||
&self,
|
||||
package_id: &PackageNv,
|
||||
is_deprecated: bool,
|
||||
expected: ExpectedExtraInfo,
|
||||
) -> Result<deno_npm::NpmPackageExtraInfo, JsErrorBox>;
|
||||
}
|
||||
|
||||
|
@ -39,11 +40,11 @@ impl<T: NpmPackageExtraInfoProvider + ?Sized> NpmPackageExtraInfoProvider
|
|||
async fn get_package_extra_info(
|
||||
&self,
|
||||
package_id: &PackageNv,
|
||||
is_deprecated: bool,
|
||||
expected: ExpectedExtraInfo,
|
||||
) -> Result<deno_npm::NpmPackageExtraInfo, JsErrorBox> {
|
||||
self
|
||||
.as_ref()
|
||||
.get_package_extra_info(package_id, is_deprecated)
|
||||
.get_package_extra_info(package_id, expected)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
@ -76,19 +77,28 @@ impl ExtraInfoProvider {
|
|||
}
|
||||
}
|
||||
|
||||
impl super::common::NpmPackageExtraInfoProvider for ExtraInfoProvider {
|
||||
async fn get_package_extra_info(
|
||||
&self,
|
||||
package_nv: &PackageNv,
|
||||
deprecated: bool,
|
||||
) -> Result<NpmPackageExtraInfo, JsErrorBox> {
|
||||
if let Some(extra_info) = self.cache.read().get(package_nv) {
|
||||
return Ok(extra_info.clone());
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct ExpectedExtraInfo {
|
||||
pub deprecated: bool,
|
||||
pub bin: bool,
|
||||
pub scripts: bool,
|
||||
}
|
||||
|
||||
if deprecated {
|
||||
// we need the registry version info to get the deprecated string, since it's not in the
|
||||
// package's package.json
|
||||
impl ExpectedExtraInfo {
|
||||
pub fn from_package(package: &NpmResolutionPackage) -> Self {
|
||||
Self {
|
||||
deprecated: package.is_deprecated,
|
||||
bin: package.has_bin,
|
||||
scripts: package.has_scripts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtraInfoProvider {
|
||||
async fn fetch_from_registry(
|
||||
&self,
|
||||
package_nv: &PackageNv,
|
||||
) -> Result<NpmPackageExtraInfo, JsErrorBox> {
|
||||
let package_info = self
|
||||
.npm_registry_info_provider
|
||||
.package_info(&package_nv.name)
|
||||
|
@ -103,14 +113,56 @@ impl super::common::NpmPackageExtraInfoProvider for ExtraInfoProvider {
|
|||
bin: version_info.bin.clone(),
|
||||
scripts: version_info.scripts.clone(),
|
||||
})
|
||||
} else {
|
||||
}
|
||||
|
||||
async fn fetch_from_package_json(
|
||||
&self,
|
||||
package_nv: &PackageNv,
|
||||
) -> Result<NpmPackageExtraInfo, JsErrorBox> {
|
||||
let folder_path = self.npm_cache.package_folder_for_nv(package_nv);
|
||||
let package_json_path = folder_path.join("package.json");
|
||||
let extra_info: NpmPackageExtraInfo =
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let package_json = std::fs::read_to_string(&package_json_path)
|
||||
.map_err(JsErrorBox::from_err)?;
|
||||
let extra_info: NpmPackageExtraInfo =
|
||||
deno_core::serde_json::from_str(&package_json)
|
||||
.map_err(JsErrorBox::from_err)?;
|
||||
|
||||
Ok::<_, JsErrorBox>(extra_info)
|
||||
})
|
||||
.await
|
||||
.map_err(JsErrorBox::from_err)??;
|
||||
Ok(extra_info)
|
||||
}
|
||||
}
|
||||
|
||||
impl super::common::NpmPackageExtraInfoProvider for ExtraInfoProvider {
|
||||
async fn get_package_extra_info(
|
||||
&self,
|
||||
package_nv: &PackageNv,
|
||||
expected: ExpectedExtraInfo,
|
||||
) -> Result<NpmPackageExtraInfo, JsErrorBox> {
|
||||
if let Some(extra_info) = self.cache.read().get(package_nv) {
|
||||
return Ok(extra_info.clone());
|
||||
}
|
||||
|
||||
let extra_info = if expected.deprecated {
|
||||
// we need the registry version info to get the deprecated string, since it's not in the
|
||||
// package's package.json
|
||||
self.fetch_from_registry(package_nv).await?
|
||||
} else {
|
||||
let extra_info = self.fetch_from_package_json(package_nv).await?;
|
||||
// some packages have bin in registry but not in package.json (e.g. esbuild-wasm)
|
||||
// still not sure how that happens
|
||||
if (expected.bin && extra_info.bin.is_none())
|
||||
|| (expected.scripts && extra_info.scripts.is_empty())
|
||||
{
|
||||
self.fetch_from_registry(package_nv).await?
|
||||
} else {
|
||||
extra_info
|
||||
}
|
||||
};
|
||||
self
|
||||
.cache
|
||||
.write()
|
||||
|
@ -118,4 +170,3 @@ impl super::common::NpmPackageExtraInfoProvider for ExtraInfoProvider {
|
|||
Ok(extra_info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -414,12 +414,16 @@ async fn sync_resolution_with_fs(
|
|||
Ok::<_, SyncResolutionWithFsError>(())
|
||||
}
|
||||
});
|
||||
let extra_fut = if package.has_bin
|
||||
let extra_fut = if (package.has_bin
|
||||
|| package.has_scripts
|
||||
|| package.is_deprecated && package.extra.is_none()
|
||||
|| package.is_deprecated)
|
||||
&& package.extra.is_none()
|
||||
{
|
||||
extra_info_provider
|
||||
.get_package_extra_info(&package.id.nv, package.is_deprecated)
|
||||
.get_package_extra_info(
|
||||
&package.id.nv,
|
||||
super::common::ExpectedExtraInfo::from_package(package),
|
||||
)
|
||||
.boxed_local()
|
||||
} else {
|
||||
std::future::ready(Ok(package.extra.clone().unwrap_or_default()))
|
||||
|
@ -465,7 +469,10 @@ async fn sync_resolution_with_fs(
|
|||
cache_futures.push(
|
||||
async move {
|
||||
let extra = extra_info_provider
|
||||
.get_package_extra_info(&package.id.nv, package.is_deprecated)
|
||||
.get_package_extra_info(
|
||||
&package.id.nv,
|
||||
super::common::ExpectedExtraInfo::from_package(package),
|
||||
)
|
||||
.await
|
||||
.map_err(JsErrorBox::from_err)?;
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"name": "denotest-packagejson-missing-info",
|
||||
"dist-tags": {
|
||||
"latest": "1.0.0-missingbin"
|
||||
},
|
||||
"versions": {
|
||||
"1.0.0-missingbin": {
|
||||
"name": "denotest-packagejson-missing-info",
|
||||
"version": "1.0.0-missingbin",
|
||||
"bin": {
|
||||
"denotest": "./bin/denotest.js"
|
||||
},
|
||||
"dist": {
|
||||
"tarball": "http://localhost:4260/denotest-packagejson-missing-info/denotest-packagejson-missing-info-1.0.0-missingbin.tgz"
|
||||
}
|
||||
},
|
||||
"0.5.0-missingscripts": {
|
||||
"name": "denotest-packagejson-missing-info",
|
||||
"version": "0.5.0-missingscripts",
|
||||
"scripts": {
|
||||
"postinstall": "echo 'postinstall'"
|
||||
},
|
||||
"dist": {
|
||||
"tarball": "http://localhost:4260/denotest-packagejson-missing-info/denotest-packagejson-missing-info-0.5.0-missingscripts.tgz"
|
||||
}
|
||||
},
|
||||
"0.2.5-missingdeprecated": {
|
||||
"name": "denotest-packagejson-missing-info",
|
||||
"version": "0.2.5-missingdeprecated",
|
||||
"deprecated": "Use 0.5.0 or later",
|
||||
"dist": {
|
||||
"tarball": "http://localhost:4260/denotest-packagejson-missing-info/denotest-packagejson-missing-info-0.2.5-missingdeprecated.tgz"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
tests/specs/install/packagejson_missing_extra/__test__.jsonc
Normal file
45
tests/specs/install/packagejson_missing_extra/__test__.jsonc
Normal file
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"tempDir": true,
|
||||
"tests": {
|
||||
"missing_bin": {
|
||||
"steps": [
|
||||
{
|
||||
"args": "install npm:denotest-packagejson-missing-info@1.0.0-missingbin",
|
||||
"output": "missingbin.out"
|
||||
},
|
||||
{
|
||||
"args": "run -A ./has-bin.ts denotest",
|
||||
"output": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"missing_scripts": {
|
||||
"steps": [
|
||||
{
|
||||
"args": "install npm:denotest-packagejson-missing-info@0.5.0-missingscripts",
|
||||
"output": "missingscripts.out"
|
||||
},
|
||||
{
|
||||
"args": "install --allow-scripts",
|
||||
"output": "[WILDCARD]running 'postinstall' script\n"
|
||||
}
|
||||
]
|
||||
},
|
||||
"missing_deprecated": {
|
||||
"steps": [
|
||||
{
|
||||
"args": "install npm:denotest-packagejson-missing-info@0.2.5-missingdeprecated",
|
||||
"output": "missingdeprecated.out"
|
||||
}
|
||||
]
|
||||
},
|
||||
"missing_deprecated_with_lazy_caching": {
|
||||
"steps": [
|
||||
{
|
||||
"args": "install --unstable-npm-lazy-caching npm:denotest-packagejson-missing-info@0.2.5-missingdeprecated",
|
||||
"output": "missingdeprecated.out"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
3
tests/specs/install/packagejson_missing_extra/deno.json
Normal file
3
tests/specs/install/packagejson_missing_extra/deno.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"nodeModulesDir": "auto"
|
||||
}
|
20
tests/specs/install/packagejson_missing_extra/has-bin.ts
Normal file
20
tests/specs/install/packagejson_missing_extra/has-bin.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
const name = Deno.args[0].trim();
|
||||
|
||||
function exists(path: string) {
|
||||
try {
|
||||
Deno.statSync(path);
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!(exists(`node_modules/.bin/${name}`) ||
|
||||
exists(`node_modules/.bin/${name}.cmd`))
|
||||
) {
|
||||
console.log("missing bin");
|
||||
console.log(`node_modules/.bin/${name}`);
|
||||
console.log(Deno.readDirSync("node_modules/.bin").toArray());
|
||||
Deno.exit(1);
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
Add npm:denotest-packagejson-missing-info@1.0.0-missingbin
|
||||
Download http://localhost:4260/denotest-packagejson-missing-info
|
||||
Download http://localhost:4260/denotest-packagejson-missing-info/denotest-packagejson-missing-info-1.0.0-missingbin.tgz
|
||||
Initialize denotest-packagejson-missing-info@1.0.0-missingbin
|
|
@ -0,0 +1,6 @@
|
|||
Add npm:denotest-packagejson-missing-info@0.2.5-missingdeprecated
|
||||
Download http://localhost:4260/denotest-packagejson-missing-info
|
||||
Download http://localhost:4260/denotest-packagejson-missing-info/denotest-packagejson-missing-info-0.2.5-missingdeprecated.tgz
|
||||
Initialize denotest-packagejson-missing-info@0.2.5-missingdeprecated
|
||||
Warning The following packages are deprecated:
|
||||
┖─ npm:denotest-packagejson-missing-info@0.2.5-missingdeprecated (Use 0.5.0 or later)
|
|
@ -0,0 +1,5 @@
|
|||
Add npm:denotest-packagejson-missing-info@0.5.0-missingscripts
|
||||
Download http://localhost:4260/denotest-packagejson-missing-info
|
||||
Download http://localhost:4260/denotest-packagejson-missing-info/denotest-packagejson-missing-info-0.5.0-missingscripts.tgz
|
||||
Initialize denotest-packagejson-missing-info@0.5.0-missingscripts
|
||||
Warning The following packages contained npm lifecycle scripts (preinstall/install/postinstall) that were not executed:[WILDCARD]
|
Loading…
Add table
Add a link
Reference in a new issue