fix(pacquet): accept string libc in PackageMetadata (#11880)

This commit is contained in:
kimulaco
2026-05-24 03:40:25 +09:00
committed by GitHub
parent 508e6d800b
commit 43e06bb2ae
2 changed files with 77 additions and 2 deletions

View File

@@ -1,5 +1,5 @@
use crate::LockfileResolution;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use std::collections::HashMap;
/// Metadata for one resolved package version, as stored in the v9
@@ -19,7 +19,11 @@ pub struct PackageMetadata {
pub cpu: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub os: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_string_or_vec"
)]
pub libc: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub deprecated: Option<String>,
@@ -36,7 +40,34 @@ pub struct PackageMetadata {
pub peer_dependencies_meta: Option<HashMap<String, PeerDependencyMeta>>,
}
// Some packages declare `libc` as a plain string in `package.json`; pnpm writes
// that string as-is into the lockfile. Accepts both string and array forms,
// normalizing to `Vec<Value>`.
fn deserialize_string_or_vec<'de, Value, Deser>(
deserializer: Deser,
) -> Result<Option<Vec<Value>>, Deser::Error>
where
Value: Deserialize<'de>,
Deser: Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum StringOrVec<Value> {
String(Value),
Vec(Vec<Value>),
}
let opt = Option::<StringOrVec<Value>>::deserialize(deserializer)?;
Ok(opt.map(|value| match value {
StringOrVec::String(item) => vec![item],
StringOrVec::Vec(items) => items,
}))
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PeerDependencyMeta {
pub optional: bool,
}
#[cfg(test)]
mod tests;

View File

@@ -0,0 +1,44 @@
use super::PackageMetadata;
use crate::serialize_yaml;
use text_block_macros::text_block;
fn make_metadata(libc_yaml: &str) -> String {
let base = text_block! {
"resolution:"
" integrity: sha512-abc123"
" tarball: https://registry.npmjs.org/foo/-/foo-1.0.0.tgz"
"cpu: [arm64]"
"os: [linux]"
};
format!("{base}\n{libc_yaml}")
}
#[test]
fn libc_as_string() {
let yaml = make_metadata("libc: glibc\n");
let metadata: PackageMetadata = serde_saphyr::from_str(&yaml).unwrap();
assert_eq!(metadata.libc, Some(vec!["glibc".to_string()]));
}
#[test]
fn libc_as_array() {
let yaml = make_metadata("libc: [glibc]\n");
let metadata: PackageMetadata = serde_saphyr::from_str(&yaml).unwrap();
assert_eq!(metadata.libc, Some(vec!["glibc".to_string()]));
}
#[test]
fn libc_absent() {
let yaml = make_metadata("");
let metadata: PackageMetadata = serde_saphyr::from_str(&yaml).unwrap();
assert_eq!(metadata.libc, None);
}
#[test]
fn libc_string_roundtrip() {
let yaml = make_metadata("libc: glibc\n");
let metadata: PackageMetadata = serde_saphyr::from_str(&yaml).unwrap();
let serialized = serialize_yaml::to_string(&metadata).unwrap();
let reparsed: PackageMetadata = serde_saphyr::from_str(&serialized).unwrap();
assert_eq!(metadata.libc, reparsed.libc);
}