perf: use simd-json for package.json parsing (#761)

## Summary

Replace `serde_json` with `simd-json` using `BorrowedValue` for zero-copy JSON parsing. Uses `self_cell` to manage the self-referential structure where the parsed JSON borrows from the original string.

Breaking change:

* raw API with `package_json_raw_json_api` feature is removed.
* Returned error no longer has line and column information.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
Boshen
2025-10-17 04:24:20 +00:00
co-authored by Hanzo Dev
parent 3461590524
commit e9ba7c3a0e
13 changed files with 575 additions and 452 deletions
Generated
+87
View File
@@ -17,6 +17,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "anes"
version = "0.2.1"
@@ -380,6 +386,21 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
[[package]]
name = "float-cmp"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8"
dependencies = [
"num-traits",
]
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "form_urlencoded"
version = "1.2.2"
@@ -429,11 +450,25 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "halfbrown"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7ed2f2edad8a14c8186b847909a41fbb9c3eafa44f88bd891114ed5019da09"
dependencies = [
"hashbrown",
]
[[package]]
name = "hashbrown"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash",
]
[[package]]
name = "icu_collections"
@@ -795,8 +830,10 @@ dependencies = [
"pnp",
"rayon",
"rustc-hash",
"self_cell",
"serde",
"serde_json",
"simd-json",
"simdutf8",
"thiserror",
"tracing",
@@ -955,6 +992,26 @@ dependencies = [
"thiserror",
]
[[package]]
name = "ref-cast"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d"
dependencies = [
"ref-cast-impl",
]
[[package]]
name = "ref-cast-impl"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "regex-automata"
version = "0.4.12"
@@ -1009,6 +1066,12 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "self_cell"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749"
[[package]]
name = "semver"
version = "1.0.27"
@@ -1074,6 +1137,18 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "simd-json"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4255126f310d2ba20048db6321c81ab376f6a6735608bf11f0785c41f01f64e3"
dependencies = [
"halfbrown",
"ref-cast",
"simdutf8",
"value-trait",
]
[[package]]
name = "simdutf8"
version = "0.1.5"
@@ -1246,6 +1321,18 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "value-trait"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e80f0c733af0720a501b3905d22e2f97662d8eacfe082a75ed7ffb5ab08cb59"
dependencies = [
"float-cmp",
"halfbrown",
"itoa",
"ryu",
]
[[package]]
name = "vfs"
version = "0.12.2"
+3 -3
View File
@@ -83,8 +83,11 @@ json-strip-comments = "3"
once_cell = "1" # Use `std::sync::OnceLock::get_or_try_init` when it is stable.
papaya = "0.2"
rustc-hash = { version = "2" }
self_cell = "1"
serde = { version = "1", features = ["derive"] } # derive for Deserialize from package.json
serde_json = { version = "1", features = ["preserve_order"] } # preserve_order: package_json.exports requires order such as `["require", "import", "default"]`
# Omit serde_impl and swar-number-parsing (package.json seldom has floats).
simd-json = { version = "0.17.0", default-features = false, features = ["runtime-detection"] }
simdutf8 = { version = "0.1" }
thiserror = "2"
tracing = "0.1"
@@ -115,9 +118,6 @@ windows-sys = { version = "0.61.0", features = ["Win32_Storage", "Win32_Storage_
[features]
default = []
## Enables the [PackageJson::raw_json] API,
## which returns the `package.json` with `serde_json::Value`.
package_json_raw_json_api = []
## [Yarn Plug'n'Play](https://yarnpkg.com/features/pnp)
yarn_pnp = ["pnp"]
# For codspeed benchmark
+16 -25
View File
@@ -283,33 +283,24 @@ fn bench_package_json_deserialization(c: &mut Criterion) {
let test_path = PathBuf::from("/test/package.json");
let test_realpath = test_path.clone();
group.bench_function("small", |b| {
b.iter(|| {
PackageJson::parse(test_path.clone(), test_realpath.clone(), small_json)
.expect("Failed to parse small JSON");
});
});
let data = [
("small", small_json.to_string()),
("medium", medium_json.to_string()),
("large", large_json.to_string()),
("complex_real", complex_json),
];
group.bench_function("medium", |b| {
b.iter(|| {
PackageJson::parse(test_path.clone(), test_realpath.clone(), medium_json)
.expect("Failed to parse medium JSON");
for (name, json) in data {
group.bench_function(name, |b| {
b.iter_with_setup_wrapper(|runner| {
let json = json.clone();
runner.run(|| {
PackageJson::parse(test_path.clone(), test_realpath.clone(), json)
.expect("Failed to parse JSON");
});
});
});
});
group.bench_function("large", |b| {
b.iter(|| {
PackageJson::parse(test_path.clone(), test_realpath.clone(), large_json)
.expect("Failed to parse large JSON");
});
});
group.bench_function("complex_real", |b| {
b.iter(|| {
PackageJson::parse(test_path.clone(), test_realpath.clone(), &complex_json)
.expect("Failed to parse complex JSON");
});
});
}
group.finish();
}
+1
View File
@@ -96,6 +96,7 @@ allow = [
"Unicode-3.0",
"BSD-2-Clause",
"MPL-2.0",
"Zlib",
# "Apache-2.0 WITH LLVM-exception",
]
# The confidence threshold for detecting a license from license text.
+2 -2
View File
@@ -120,9 +120,9 @@ impl<Fs: FileSystem> Cache<Fs> {
} else {
package_json_path.clone()
};
PackageJson::parse(package_json_path.clone(), real_path, &package_json_string)
PackageJson::parse(package_json_path.clone(), real_path, package_json_string)
.map(|package_json| Some(Arc::new(package_json)))
.map_err(|error| ResolveError::from_serde_json_error(package_json_path, &error))
.map_err(|error| ResolveError::from_simd_json_error(package_json_path, &error))
})
.cloned();
// https://github.com/webpack/enhanced-resolve/blob/58464fc7cb56673c9aa849e68e6300239601e615/lib/DescriptionFileUtils.js#L68-L82
+11
View File
@@ -135,6 +135,17 @@ impl ResolveError {
column: error.column(),
})
}
#[cold]
#[must_use]
pub fn from_simd_json_error(path: PathBuf, error: &simd_json::Error) -> Self {
Self::Json(JSONError {
path,
message: error.to_string(),
line: 0, // simd_json doesn't provide line/column info
column: 0,
})
}
}
/// Error for [ResolveError::Specifier]
+1 -1
View File
@@ -75,7 +75,7 @@ pub use crate::{
},
package_json::{
ImportsExportsArray, ImportsExportsEntry, ImportsExportsKind, ImportsExportsMap,
PackageJson, PackageType,
PackageJson, PackageType, SideEffects,
},
path::PathUtil,
resolution::{ModuleType, Resolution},
+165 -125
View File
@@ -6,19 +6,29 @@ use std::{
path::{Path, PathBuf},
};
use serde_json::Value as JSONValue;
use simd_json::{BorrowedValue, prelude::*};
use crate::{ResolveError, path::PathUtil};
pub type JSONMap = serde_json::Map<String, JSONValue>;
// Use simd_json's Object type which handles the hasher correctly based on features
type BorrowedObject<'a> = simd_json::value::borrowed::Object<'a>;
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "lowercase")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PackageType {
CommonJs,
Module,
}
impl PackageType {
fn from_str(s: &str) -> Option<Self> {
match s {
"commonjs" => Some(Self::CommonJs),
"module" => Some(Self::Module),
_ => None,
}
}
}
impl fmt::Display for PackageType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
@@ -36,11 +46,26 @@ pub enum ImportsExportsKind {
Invalid,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SideEffects<'a> {
Bool(bool),
String(&'a str),
Array(Vec<&'a str>),
}
self_cell::self_cell! {
struct PackageJsonCell {
owner: Vec<u8>,
#[covariant]
dependent: BorrowedValue,
}
}
/// Serde implementation for the deserialized `package.json`.
///
/// This implementation is used by the [crate::Cache] and enabled through the
/// `fs_cache` feature.
#[derive(Debug, Default)]
pub struct PackageJson {
/// Path to `package.json`. Contains the `package.json` filename.
pub path: PathBuf,
@@ -48,20 +73,18 @@ pub struct PackageJson {
/// Realpath to `package.json`. Contains the `package.json` filename.
pub realpath: PathBuf,
/// Name of the package.
pub name: Option<String>,
cell: PackageJsonCell,
}
/// The "type" field.
///
/// <https://nodejs.org/api/packages.html#type>
pub r#type: Option<PackageType>,
/// The "sideEffects" field.
///
/// <https://webpack.js.org/guides/tree-shaking>
pub side_effects: Option<JSONValue>,
raw_json: std::sync::Arc<JSONValue>,
impl fmt::Debug for PackageJson {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PackageJson")
.field("path", &self.path)
.field("realpath", &self.realpath)
.field("name", &self.name())
.field("type", &self.r#type())
.finish_non_exhaustive()
}
}
impl PackageJson {
@@ -106,7 +129,23 @@ impl PackageJson {
/// <https://nodejs.org/api/packages.html#name>
#[must_use]
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
self.cell
.borrow_dependent()
.as_object()
.and_then(|obj| obj.get("name"))
.and_then(|v| v.as_str())
}
/// Version of the package.
///
/// <https://nodejs.org/api/packages.html#name>
#[must_use]
pub fn version(&self) -> Option<&str> {
self.cell
.borrow_dependent()
.as_object()
.and_then(|obj| obj.get("version"))
.and_then(|v| v.as_str())
}
/// Returns the package type, if one is configured in the `package.json`.
@@ -114,7 +153,44 @@ impl PackageJson {
/// <https://nodejs.org/api/packages.html#type>
#[must_use]
pub fn r#type(&self) -> Option<PackageType> {
self.r#type
self.cell
.borrow_dependent()
.as_object()
.and_then(|obj| obj.get("type"))
.and_then(|v| v.as_str())
.and_then(PackageType::from_str)
}
/// The "sideEffects" field.
///
/// <https://webpack.js.org/guides/tree-shaking>
#[must_use]
pub fn side_effects(&self) -> Option<SideEffects<'_>> {
self.cell.borrow_dependent().as_object().and_then(|obj| obj.get("sideEffects")).and_then(
|value| match value {
BorrowedValue::Static(simd_json::StaticNode::Bool(b)) => {
Some(SideEffects::Bool(*b))
}
BorrowedValue::String(s) => Some(SideEffects::String(s.as_ref())),
BorrowedValue::Array(arr) => {
let strings: Vec<&str> = arr.iter().filter_map(|v| v.as_str()).collect();
Some(SideEffects::Array(strings))
}
_ => None,
},
)
}
/// The "exports" field allows defining the entry points of a package.
///
/// <https://nodejs.org/api/packages.html#exports>
#[must_use]
pub fn exports(&self) -> Option<ImportsExportsEntry<'_>> {
self.cell
.borrow_dependent()
.as_object()
.and_then(|obj| obj.get("exports"))
.map(ImportsExportsEntry)
}
/// The "main" field defines the entry point of a package when imported by
@@ -130,10 +206,13 @@ impl PackageJson {
&'a self,
main_fields: &'a [String],
) -> impl Iterator<Item = &'a str> + 'a {
let json_value = self.cell.borrow_dependent();
let json_object = json_value.as_object();
main_fields
.iter()
.filter_map(|main_field| self.raw_json.get(main_field))
.filter_map(JSONValue::as_str)
.filter_map(move |main_field| json_object.and_then(|obj| obj.get(main_field.as_str())))
.filter_map(|v| v.as_str())
}
/// The "exports" field allows defining the entry points of a package when
@@ -145,10 +224,12 @@ impl PackageJson {
&'a self,
exports_fields: &'a [Vec<String>],
) -> impl Iterator<Item = ImportsExportsEntry<'a>> + 'a {
let json_value = self.cell.borrow_dependent();
exports_fields
.iter()
.filter_map(|object_path| {
self.raw_json
.filter_map(move |object_path| {
json_value
.as_object()
.and_then(|json_object| Self::get_value_by_path(json_object, object_path))
})
@@ -164,13 +245,15 @@ impl PackageJson {
&'a self,
imports_fields: &'a [Vec<String>],
) -> impl Iterator<Item = ImportsExportsMap<'a>> + 'a {
let json_value = self.cell.borrow_dependent();
imports_fields
.iter()
.filter_map(|object_path| {
self.raw_json
.filter_map(move |object_path| {
json_value
.as_object()
.and_then(|json_object| Self::get_value_by_path(json_object, object_path))
.and_then(JSONValue::as_object)
.and_then(|v| v.as_object())
})
.map(ImportsExportsMap)
}
@@ -187,13 +270,14 @@ impl PackageJson {
) -> Result<Option<&'a str>, ResolveError> {
for object in self.browser_fields(alias_fields) {
if let Some(request) = request {
// Find matching key in object
if let Some(value) = object.get(request) {
return Self::alias_value(path, value);
}
} else {
let dir = self.path.parent().unwrap();
for (key, value) in object {
let joined = dir.normalize_with(key);
let joined = dir.normalize_with(key.as_ref());
if joined == path {
return Self::alias_value(path, value);
}
@@ -207,49 +291,40 @@ impl PackageJson {
///
/// # Panics
/// # Errors
pub fn parse(path: PathBuf, realpath: PathBuf, json: &str) -> Result<Self, serde_json::Error> {
let json = json.trim_start_matches("\u{feff}"); // strip bom
let mut raw_json: JSONValue = serde_json::from_str(json)?;
let mut package_json = Self::default();
if let Some(json_object) = raw_json.as_object_mut() {
// Remove large fields that are useless for pragmatic use.
#[cfg(feature = "package_json_raw_json_api")]
{
json_object.remove("description");
json_object.remove("keywords");
json_object.remove("scripts");
json_object.remove("dependencies");
json_object.remove("devDependencies");
json_object.remove("peerDependencies");
json_object.remove("optionalDependencies");
}
// Add name, type and sideEffects.
package_json.name =
json_object.get("name").and_then(|field| field.as_str()).map(ToString::to_string);
package_json.r#type =
json_object.get("type").and_then(|ty| serde_json::from_value(ty.clone()).ok());
package_json.side_effects = json_object.get("sideEffects").cloned();
pub fn parse(path: PathBuf, realpath: PathBuf, json: String) -> Result<Self, simd_json::Error> {
// Strip BOM in place by replacing with spaces (no reallocation)
let mut json_bytes = json.into_bytes();
if json_bytes.starts_with(b"\xEF\xBB\xBF") {
json_bytes[0] = b' ';
json_bytes[1] = b' ';
json_bytes[2] = b' ';
}
package_json.path = path;
package_json.realpath = realpath;
package_json.raw_json = std::sync::Arc::new(raw_json);
Ok(package_json)
// Create the self-cell with the JSON bytes and parsed BorrowedValue
let cell = PackageJsonCell::try_new(json_bytes.clone(), |bytes| {
// We need a mutable slice from our owned data
// SAFETY: We're creating a mutable reference to the owned data.
// The self_cell ensures this reference is valid for the lifetime of the cell.
let slice =
unsafe { std::slice::from_raw_parts_mut(bytes.as_ptr().cast_mut(), bytes.len()) };
simd_json::to_borrowed_value(slice)
})?;
Ok(Self { path, realpath, cell })
}
fn get_value_by_path<'a>(
fields: &'a serde_json::Map<String, JSONValue>,
fields: &'a BorrowedObject<'a>,
path: &[String],
) -> Option<&'a JSONValue> {
) -> Option<&'a BorrowedValue<'a>> {
if path.is_empty() {
return None;
}
let mut value = fields.get(&path[0])?;
let mut value = fields.get(path[0].as_str())?;
for key in path.iter().skip(1) {
if let Some(inner_value) = value.as_object().and_then(|o| o.get(key)) {
value = inner_value;
if let Some(obj) = value.as_object() {
value = obj.get(key.as_str())?;
} else {
return None;
}
@@ -257,21 +332,6 @@ impl PackageJson {
Some(value)
}
/// Raw serde json value of `package.json`.
///
/// This is currently used in Rspack for:
/// * getting the `sideEffects` field
/// * query in <https://www.rspack.dev/config/module.html#ruledescriptiondata> - search on GitHub indicates query on the `type` field.
///
/// To reduce overall memory consumption, large fields that useless for pragmatic use are removed.
/// They are: `description`, `keywords`, `scripts`,
/// `dependencies` and `devDependencies`, `peerDependencies`, `optionalDependencies`.
#[cfg(feature = "package_json_raw_json_api")]
#[must_use]
pub const fn raw_json(&self) -> &std::sync::Arc<JSONValue> {
&self.raw_json
}
/// The "browser" field is provided by a module author as a hint to javascript bundlers or component tools when packaging modules for client side use.
/// Multiple values are configured by [ResolveOptions::alias_fields].
///
@@ -279,9 +339,11 @@ impl PackageJson {
pub(crate) fn browser_fields<'a>(
&'a self,
alias_fields: &'a [Vec<String>],
) -> impl Iterator<Item = &'a JSONMap> + 'a {
alias_fields.iter().filter_map(|object_path| {
self.raw_json
) -> impl Iterator<Item = &'a BorrowedObject<'a>> + 'a {
let json_value = self.cell.borrow_dependent();
alias_fields.iter().filter_map(move |object_path| {
json_value
.as_object()
.and_then(|json_object| Self::get_value_by_path(json_object, object_path))
// Only object is valid, all other types are invalid
@@ -292,57 +354,59 @@ impl PackageJson {
pub(crate) fn alias_value<'a>(
key: &Path,
value: &'a JSONValue,
value: &'a BorrowedValue<'a>,
) -> Result<Option<&'a str>, ResolveError> {
match value {
JSONValue::String(value) => Ok(Some(value.as_str())),
JSONValue::Bool(b) if !b => Err(ResolveError::Ignored(key.to_path_buf())),
BorrowedValue::String(s) => Ok(Some(s.as_ref())),
BorrowedValue::Static(simd_json::StaticNode::Bool(false)) => {
Err(ResolveError::Ignored(key.to_path_buf()))
}
_ => Ok(None),
}
}
}
#[derive(Clone)]
pub struct ImportsExportsEntry<'a>(pub(crate) &'a JSONValue);
pub struct ImportsExportsEntry<'a>(pub(crate) &'a BorrowedValue<'a>);
impl<'a> ImportsExportsEntry<'a> {
#[must_use]
pub fn kind(&self) -> ImportsExportsKind {
match &self.0 {
JSONValue::String(_) => ImportsExportsKind::String,
JSONValue::Array(_) => ImportsExportsKind::Array,
JSONValue::Object(_) => ImportsExportsKind::Map,
_ => ImportsExportsKind::Invalid,
match self.0 {
BorrowedValue::String(_) => ImportsExportsKind::String,
BorrowedValue::Array(_) => ImportsExportsKind::Array,
BorrowedValue::Object(_) => ImportsExportsKind::Map,
BorrowedValue::Static(_) => ImportsExportsKind::Invalid,
}
}
#[must_use]
pub fn as_string(&self) -> Option<&'a str> {
match &self.0 {
JSONValue::String(string) => Some(string),
match self.0 {
BorrowedValue::String(s) => Some(s.as_ref()),
_ => None,
}
}
#[must_use]
pub fn as_array(&self) -> Option<ImportsExportsArray<'a>> {
match &self.0 {
JSONValue::Array(vec) => Some(ImportsExportsArray(vec)),
match self.0 {
BorrowedValue::Array(arr) => Some(ImportsExportsArray(arr)),
_ => None,
}
}
#[must_use]
pub fn as_map(&self) -> Option<ImportsExportsMap<'a>> {
match &self.0 {
JSONValue::Object(map) => Some(ImportsExportsMap(map)),
match self.0 {
BorrowedValue::Object(obj) => Some(ImportsExportsMap(obj)),
_ => None,
}
}
}
#[derive(Clone)]
pub struct ImportsExportsArray<'a>(&'a Vec<JSONValue>);
pub struct ImportsExportsArray<'a>(&'a [BorrowedValue<'a>]);
impl<'a> ImportsExportsArray<'a> {
#[must_use]
@@ -356,12 +420,12 @@ impl<'a> ImportsExportsArray<'a> {
}
pub fn iter(&self) -> impl Iterator<Item = ImportsExportsEntry<'a>> {
ImportsExportsArrayIter { vec: self.0, index: 0 }
ImportsExportsArrayIter { slice: self.0, index: 0 }
}
}
struct ImportsExportsArrayIter<'a> {
vec: &'a Vec<JSONValue>,
slice: &'a [BorrowedValue<'a>],
index: usize,
}
@@ -369,7 +433,7 @@ impl<'a> Iterator for ImportsExportsArrayIter<'a> {
type Item = ImportsExportsEntry<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.vec.get(self.index).map(|value| {
self.slice.get(self.index).map(|value| {
self.index += 1;
ImportsExportsEntry(value)
})
@@ -377,7 +441,7 @@ impl<'a> Iterator for ImportsExportsArrayIter<'a> {
}
#[derive(Clone)]
pub struct ImportsExportsMap<'a>(pub(crate) &'a JSONMap);
pub struct ImportsExportsMap<'a>(pub(crate) &'a BorrowedObject<'a>);
impl<'a> ImportsExportsMap<'a> {
pub fn get(&self, key: &str) -> Option<ImportsExportsEntry<'a>> {
@@ -385,34 +449,10 @@ impl<'a> ImportsExportsMap<'a> {
}
pub fn keys(&self) -> impl Iterator<Item = &'a str> {
ImportsExportsMapKeysIter { inner: self.0.keys() }
self.0.keys().map(std::convert::AsRef::as_ref)
}
pub fn iter(&self) -> impl Iterator<Item = (&'a str, ImportsExportsEntry<'a>)> {
ImportsExportsMapIter { inner: self.0.iter() }
}
}
struct ImportsExportsMapIter<'a> {
inner: serde_json::map::Iter<'a>,
}
impl<'a> Iterator for ImportsExportsMapIter<'a> {
type Item = (&'a str, ImportsExportsEntry<'a>);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(key, value)| (key.as_str(), ImportsExportsEntry(value)))
}
}
struct ImportsExportsMapKeysIter<'a> {
inner: serde_json::map::Keys<'a>,
}
impl<'a> Iterator for ImportsExportsMapKeysIter<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(String::as_str)
self.0.iter().map(|(k, v)| (k.as_ref(), ImportsExportsEntry(v)))
}
}
+180 -177
View File
File diff suppressed because it is too large Load Diff
+95 -92
View File
@@ -88,7 +88,7 @@ fn shared_resolvers() {
// } else {
// console.log(`expect: Some(vec!${JSON.stringify(c.expect)}),`)
// }
// console.log(`imports_field: imports_field(json!(${JSON.stringify(c.suite[0], null, 2)})),`)
// console.log(`imports_field: imports_field(&json!(${JSON.stringify(c.suite[0], null, 2)})),`)
// console.log(`request: "${c.suite[1]}",`)
// console.log(`condition_names: vec!${JSON.stringify(c.suite[2])},`)
// console.log("},")
@@ -102,11 +102,14 @@ struct TestCase {
condition_names: Vec<&'static str>,
}
fn imports_field(value: serde_json::Value) -> ImportsExportsMap<'static> {
let serde_json::Value::Object(map) = value else {
fn imports_field(value: &serde_json::Value) -> ImportsExportsMap<'static> {
// Serialize back to JSON string and parse with simd_json
let json_str = serde_json::to_string(value).unwrap();
let bytes = Box::leak::<'static>(Box::new(json_str.into_bytes()));
let borrowed = simd_json::to_borrowed_value(bytes).unwrap();
let simd_json::BorrowedValue::Object(map) = borrowed else {
panic!("Expected an object");
};
// Don't do this at home:
let map = Box::leak::<'static>(Box::new(map));
ImportsExportsMap(map)
}
@@ -118,7 +121,7 @@ fn test_cases() {
TestCase {
name: "sample #1",
expect: Some(vec!["./dist/test/file.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#abc/": {
"import": [
"./dist/",
@@ -135,7 +138,7 @@ fn test_cases() {
TestCase {
name: "sample #1",
expect: Some(vec!["./src/test/file.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#abc/": {
"import": [
"./src/"
@@ -150,7 +153,7 @@ fn test_cases() {
TestCase {
name: "sample #2",
expect: Some(vec!["./data/timezones/pdt.mjs"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#1/timezones/": "./data/timezones/"
})),
request: "#1/timezones/pdt.mjs",
@@ -159,7 +162,7 @@ fn test_cases() {
TestCase {
name: "sample #3",
expect: Some(vec!["./data/timezones/timezones/pdt.mjs"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#aaa/": "./data/timezones/",
"#a/": "./data/timezones/"
})),
@@ -169,7 +172,7 @@ fn test_cases() {
TestCase {
name: "sample #4",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/lib/": {
"browser": [
"./browser/"
@@ -185,7 +188,7 @@ fn test_cases() {
TestCase {
name: "sample #5",
expect: Some(vec!["./browser/index.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/lib/": {
"browser": [
"./browser/"
@@ -202,7 +205,7 @@ fn test_cases() {
TestCase {
name: "sample #6",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/dist/a": "./dist/index.js"
})),
request: "#a/dist/aaa",
@@ -211,7 +214,7 @@ fn test_cases() {
TestCase {
name: "sample #7",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/a/a/": "./dist/index.js"
})),
request: "#a/a/a",
@@ -220,7 +223,7 @@ fn test_cases() {
TestCase {
name: "sample #8",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": "./index.js"
})),
request: "#a/timezones/pdt.mjs",
@@ -229,7 +232,7 @@ fn test_cases() {
TestCase {
name: "sample #9",
expect: Some(vec!["./main.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/index.js": "./main.js"
})),
request: "#a/index.js",
@@ -238,7 +241,7 @@ fn test_cases() {
TestCase {
name: "sample #10",
expect: Some(vec!["./ok.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/#foo": "./ok.js",
"#a/module": "./ok.js",
"#a/🎉": "./ok.js",
@@ -252,7 +255,7 @@ fn test_cases() {
TestCase {
name: "sample #11",
expect: Some(vec!["./ok.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/#foo": "./ok.js",
"#a/module": "./ok.js",
"#a/🎉": "./ok.js",
@@ -266,7 +269,7 @@ fn test_cases() {
TestCase {
name: "sample #12",
expect: Some(vec!["./ok.js#abc"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/#foo": "./ok.js",
"#a/module": "./ok.js",
"#a/🎉": "./ok.js",
@@ -280,7 +283,7 @@ fn test_cases() {
TestCase {
name: "sample #13",
expect: Some(vec!["./ok.js?abc"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/#foo": "./ok.js",
"#a/module": "./ok.js",
"#a/🎉": "./ok.js",
@@ -294,7 +297,7 @@ fn test_cases() {
TestCase {
name: "sample #14",
expect: Some(vec!["./🎉.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/#foo": "./ok.js",
"#a/module": "./ok.js",
"#a/🎉": "./ok.js",
@@ -308,7 +311,7 @@ fn test_cases() {
TestCase {
name: "sample #15",
expect: Some(vec!["./%F0%9F%8E%89.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/#foo": "./ok.js",
"#a/module": "./ok.js",
"#a/🎉": "./ok.js",
@@ -322,7 +325,7 @@ fn test_cases() {
TestCase {
name: "sample #16",
expect: Some(vec!["./ok.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/#foo": "./ok.js",
"#a/module": "./ok.js",
"#a/🎉": "./ok.js",
@@ -336,7 +339,7 @@ fn test_cases() {
TestCase {
name: "sample #17",
expect: Some(vec!["./other.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/#foo": "./ok.js",
"#a/module": "./ok.js",
"#a/🎉": "./ok.js",
@@ -350,7 +353,7 @@ fn test_cases() {
TestCase {
name: "sample #18",
expect: Some(vec!["./ok.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/#foo": "./ok.js",
"#a/module": "./ok.js",
"#a/🎉": "./ok.js",
@@ -364,7 +367,7 @@ fn test_cases() {
TestCase {
name: "sample #19",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/#foo": "./ok.js",
"#a/module": "./ok.js",
"#a/🎉": "./ok.js",
@@ -378,7 +381,7 @@ fn test_cases() {
TestCase {
name: "sample #20",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/#foo": "./ok.js",
"#a/module": "./ok.js",
"#a/🎉": "./ok.js",
@@ -392,7 +395,7 @@ fn test_cases() {
TestCase {
name: "sample #21",
expect: Some(vec!["./d?e?f"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/a?b?c/": "./"
})),
request: "#a/a?b?c/d?e?f",
@@ -403,7 +406,7 @@ fn test_cases() {
// We throw InvalidPackageTarget
expect: None,
// expect: Some(vec!["/user/a/index"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": "/user/a/"
})),
request: "#a/index",
@@ -412,7 +415,7 @@ fn test_cases() {
TestCase {
name: "path tree edge case #1",
expect: Some(vec!["./A/b/d.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": "./A/",
"#a/b/c": "./c.js"
})),
@@ -422,7 +425,7 @@ fn test_cases() {
TestCase {
name: "path tree edge case #2",
expect: Some(vec!["./A/c.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": "./A/",
"#a/b": "./b.js"
})),
@@ -432,7 +435,7 @@ fn test_cases() {
TestCase {
name: "path tree edge case #3",
expect: Some(vec!["./A/b/c/d.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": "./A/",
"#a/b/c/d": "./c.js"
})),
@@ -442,7 +445,7 @@ fn test_cases() {
TestCase {
name: "Direct mapping #1",
expect: Some(vec!["./dist/index.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": "./dist/index.js"
})),
request: "#a",
@@ -451,7 +454,7 @@ fn test_cases() {
TestCase {
name: "Direct mapping #2",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": "./"
})),
request: "#a",
@@ -460,7 +463,7 @@ fn test_cases() {
TestCase {
name: "Direct mapping #3",
expect: Some(vec!["./dist/a.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": "./dist/",
"#a/index.js": "./dist/a.js"
})),
@@ -470,7 +473,7 @@ fn test_cases() {
TestCase {
name: "Direct mapping #4",
expect: Some(vec!["./index.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": [
"./browser/"
@@ -486,7 +489,7 @@ fn test_cases() {
TestCase {
name: "Direct mapping #5",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": [
"./browser/"
@@ -502,7 +505,7 @@ fn test_cases() {
TestCase {
name: "Direct mapping #6",
expect: Some(vec!["./index.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": {
"browser": "./index.js",
"node": "./src/node/index.js",
@@ -515,7 +518,7 @@ fn test_cases() {
TestCase {
name: "Direct mapping #7",
expect: Some(vec!["./src/index.js"]), // `enhanced_resolve` is `None`
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": {
"default": "./src/index.js",
"browser": "./index.js",
@@ -528,7 +531,7 @@ fn test_cases() {
TestCase {
name: "Direct mapping #8",
expect: Some(vec!["./src/index.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": {
"browser": "./index.js",
"node": "./src/node/index.js",
@@ -541,7 +544,7 @@ fn test_cases() {
TestCase {
name: "Direct mapping #9",
expect: Some(vec!["./index"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": "./index"
})),
request: "#a",
@@ -550,7 +553,7 @@ fn test_cases() {
TestCase {
name: "Direct mapping #10",
expect: Some(vec!["./index.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/index": "./index.js"
})),
request: "#a/index",
@@ -561,7 +564,7 @@ fn test_cases() {
// We throw InvalidPackageTarget
// expect: Some(vec!["b"]),
expect: None,
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": "b"
})),
request: "#a",
@@ -572,7 +575,7 @@ fn test_cases() {
// We throw InvalidPackageTarget
// expect: Some(vec!["b/index"]),
expect: None,
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": "b/"
})),
request: "#a/index",
@@ -583,7 +586,7 @@ fn test_cases() {
// We throw InvalidPackageTarget
// expect: Some(vec!["b#anotherhashishere"]),
expect: None,
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a?q=a#hashishere": "b#anotherhashishere"
})),
request: "#a?q=a#hashishere",
@@ -592,7 +595,7 @@ fn test_cases() {
TestCase {
name: "Direct and conditional mapping #1",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": [
{
"browser": "./browser.js"
@@ -611,7 +614,7 @@ fn test_cases() {
TestCase {
name: "Direct and conditional mapping #2",
expect: Some(vec!["./import.mjs"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": [
{
"browser": "./browser.js"
@@ -630,7 +633,7 @@ fn test_cases() {
TestCase {
name: "Direct and conditional mapping #3",
expect: Some(vec!["./require.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": [
{
"browser": "./browser.js"
@@ -650,7 +653,7 @@ fn test_cases() {
TestCase {
name: "Direct and conditional mapping #3",
expect: Some(vec!["./import.mjs"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": [
{
"browser": "./browser.js"
@@ -666,7 +669,7 @@ fn test_cases() {
TestCase {
name: "Direct and conditional mapping #4",
expect: Some(vec!["./require.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": [
{
"browser": "./browser.js"
@@ -691,7 +694,7 @@ fn test_cases() {
TestCase {
name: "Direct and conditional mapping #4",
expect: Some(vec!["./import.mjs"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": [
{
"browser": "./browser.js"
@@ -711,7 +714,7 @@ fn test_cases() {
TestCase {
name: "Direct and conditional mapping #4",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": [
{
"browser": "./browser.js"
@@ -729,7 +732,7 @@ fn test_cases() {
TestCase {
name: "mapping to a folder root #1",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#timezones": "./data/timezones/"
})),
request: "#timezones/pdt.mjs",
@@ -738,7 +741,7 @@ fn test_cases() {
TestCase {
name: "mapping to a folder root #2",
expect: None,
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#timezones/": "./data/timezones"
})),
request: "#timezones/pdt.mjs",
@@ -747,7 +750,7 @@ fn test_cases() {
TestCase {
name: "mapping to a folder root #3",
expect: Some(vec!["./data/timezones/pdt/index.mjs"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#timezones/pdt/": "./data/timezones/pdt/"
})),
request: "#timezones/pdt/index.mjs",
@@ -756,7 +759,7 @@ fn test_cases() {
TestCase {
name: "mapping to a folder root #4",
expect: Some(vec!["./timezones/pdt.mjs"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": "./timezones/"
})),
request: "#a/pdt.mjs",
@@ -765,7 +768,7 @@ fn test_cases() {
TestCase {
name: "mapping to a folder root #5",
expect: Some(vec!["./timezones/pdt.mjs"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": "./"
})),
request: "#a/timezones/pdt.mjs",
@@ -774,7 +777,7 @@ fn test_cases() {
TestCase {
name: "mapping to a folder root #6",
expect: None,
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": "."
})),
request: "#a/timezones/pdt.mjs",
@@ -783,7 +786,7 @@ fn test_cases() {
TestCase {
name: "mapping to a folder root #7",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": "./"
})),
request: "#a/timezones/pdt.mjs",
@@ -792,7 +795,7 @@ fn test_cases() {
TestCase {
name: "the longest matching path prefix is prioritized #1",
expect: Some(vec!["./lib/index.mjs"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": "./",
"#a/dist/": "./lib/"
})),
@@ -802,7 +805,7 @@ fn test_cases() {
TestCase {
name: "the longest matching path prefix is prioritized #2",
expect: Some(vec!["./dist/utils/index.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/dist/utils/": "./dist/utils/",
"#a/dist/": "./lib/"
})),
@@ -812,7 +815,7 @@ fn test_cases() {
TestCase {
name: "the longest matching path prefix is prioritized #3",
expect: Some(vec!["./dist/utils/index.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/dist/utils/index.js": "./dist/utils/index.js",
"#a/dist/utils/": "./dist/utils/index.mjs",
"#a/dist/": "./lib/"
@@ -823,7 +826,7 @@ fn test_cases() {
TestCase {
name: "the longest matching path prefix is prioritized #4",
expect: Some(vec!["./lib/index.mjs"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": "./browser/"
},
@@ -837,7 +840,7 @@ fn test_cases() {
// This behaves differently from enhanced_resolve, because `lodash/` is an an InvalidPackageConfig
// expect: Some(vec!["lodash/index.js"]),
expect: Some(vec!["./utils/index.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": [
"lodash/",
@@ -855,7 +858,7 @@ fn test_cases() {
TestCase {
name: "conditional mapping folder #1",
expect: Some(vec!["./utils/index.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": [
"./utils/"
@@ -871,7 +874,7 @@ fn test_cases() {
TestCase {
name: "conditional mapping folder #2",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"webpack": "./wpk/",
"browser": [
@@ -889,7 +892,7 @@ fn test_cases() {
TestCase {
name: "conditional mapping folder #3",
expect: Some(vec!["./wpk/index.mjs"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"webpack": "./wpk/",
"browser": [
@@ -909,7 +912,7 @@ fn test_cases() {
// We throw `PackageImportNotDefined`
// expect: None,
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"/utils/": "./a/"
})),
request: "#a/index.mjs",
@@ -920,7 +923,7 @@ fn test_cases() {
// We throw `PackageImportNotDefined`
// expect: None,
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"/utils/": {
"browser": "./a/",
"default": "./b/"
@@ -932,7 +935,7 @@ fn test_cases() {
TestCase {
name: "incorrect exports field #3",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/index": "./a/index.js"
})),
request: "#a/index.mjs",
@@ -941,7 +944,7 @@ fn test_cases() {
TestCase {
name: "incorrect exports field #4",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/index.mjs": "./a/index.js"
})),
request: "#a/index",
@@ -950,7 +953,7 @@ fn test_cases() {
TestCase {
name: "incorrect exports field #5",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/index": {
"browser": "./a/index.js",
"default": "./b/index.js"
@@ -962,7 +965,7 @@ fn test_cases() {
TestCase {
name: "incorrect exports field #6",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/index.mjs": {
"browser": "./a/index.js",
"default": "./b/index.js"
@@ -976,7 +979,7 @@ fn test_cases() {
// We don't throw in `package_imports_exports_resolve`
// expect: None,
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": "./a/"
})),
request: "/utils/index.mjs",
@@ -987,7 +990,7 @@ fn test_cases() {
// We don't throw in `package_imports_exports_resolve`
// expect: None,
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": "./a/",
"default": "./b/"
@@ -1001,7 +1004,7 @@ fn test_cases() {
// We don't throw in `package_imports_exports_resolve`, it's thrown in `package_imports_resolve`
// expect: None,
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": "./a/",
"default": "./b/"
@@ -1015,7 +1018,7 @@ fn test_cases() {
// We don't throw in `package_imports_exports_resolve`, it's thrown in `package_imports_resolve`
// expect: None,
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": "./a/",
"default": "./b/"
@@ -1028,7 +1031,7 @@ fn test_cases() {
name: "incorrect request #5",
// expect: None,
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": "./a/",
"default": "./b/"
@@ -1041,7 +1044,7 @@ fn test_cases() {
name: "backtracking package base #1",
// expect: Some(vec!["./dist/index"]),
expect: Some(vec!["dist/index"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/../../utils/": "./dist/"
})),
request: "#a/../../utils/index",
@@ -1052,7 +1055,7 @@ fn test_cases() {
// We throw InvalidPackageTarget
// expect: Some(vec!["./dist/../../utils/index"]),
expect: None,
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": "./dist/"
})),
request: "#a/../../utils/index",
@@ -1063,7 +1066,7 @@ fn test_cases() {
// We throw InvalidPackageTarget
// expect: Some(vec!["../src/index"]),
expect: None,
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": "../src/"
})),
request: "#a/index",
@@ -1074,7 +1077,7 @@ fn test_cases() {
// We throw InvalidPackageTarget
// expect: Some(vec!["./utils/../../../index"]),
expect: None,
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": "./utils/../../../"
}
@@ -1086,7 +1089,7 @@ fn test_cases() {
name: "nested node_modules path #1",
// expect: Some(vec!["moment/node_modules/lodash/dist/index.js"]),
expect: None,
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": "moment/node_modules/"
}
@@ -1099,7 +1102,7 @@ fn test_cases() {
// We throw InvalidPackageTarget
// expect: Some(vec!["../node_modules/lodash/dist/index.js"]),
expect: None,
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": "../node_modules/"
})),
request: "#a/lodash/dist/index.js",
@@ -1108,7 +1111,7 @@ fn test_cases() {
TestCase {
name: "nested mapping #1",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": {
"webpack": "./",
@@ -1124,7 +1127,7 @@ fn test_cases() {
TestCase {
name: "nested mapping #2",
expect: Some(vec!["./index.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": {
"webpack": [
@@ -1144,7 +1147,7 @@ fn test_cases() {
TestCase {
name: "nested mapping #2",
expect: Some(vec!["./node/index.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": {
"webpack": [
@@ -1162,7 +1165,7 @@ fn test_cases() {
TestCase {
name: "nested mapping #3",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": {
"webpack": [
@@ -1183,7 +1186,7 @@ fn test_cases() {
// We throw NotFound
// expect: Some(vec!["moment/node/index.js"]),
expect: None,
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": {
"webpack": [
@@ -1202,7 +1205,7 @@ fn test_cases() {
TestCase {
name: "nested mapping #5",
expect: Some(vec![]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": {
"webpack": [
@@ -1225,7 +1228,7 @@ fn test_cases() {
TestCase {
name: "nested mapping #6",
expect: Some(vec!["./index.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": {
"webpack": [
@@ -1249,7 +1252,7 @@ fn test_cases() {
TestCase {
name: "nested mapping #6",
expect: Some(vec!["./node/index.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a/": {
"browser": {
"webpack": [
@@ -1271,7 +1274,7 @@ fn test_cases() {
TestCase {
name: "nested mapping #7",
expect: Some(vec!["./y.js"]),
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": {
"abc": {
"def": "./x.js"
@@ -1287,7 +1290,7 @@ fn test_cases() {
// We throw PackageImportNotDefined
// expect: Some(vec![]),
expect: None,
imports_field: imports_field(json!({
imports_field: imports_field(&json!({
"#a": {
"abc": {
"def": "./x.js",
+11 -10
View File
@@ -9,14 +9,14 @@ use crate::{JSONError, ResolveContext, ResolveError, Resolver};
fn incorrect_description_file_1() {
let f = super::fixture().join("incorrect-package");
let mut ctx = ResolveContext::default();
let resolution = Resolver::default().resolve_with_context(f.join("pack1"), ".", &mut ctx);
let error = ResolveError::Json(JSONError {
path: f.join("pack1/package.json"),
message: String::from("EOF while parsing a value at line 3 column 0"),
line: 3,
column: 0,
});
assert_eq!(resolution, Err(error));
let error =
Resolver::default().resolve_with_context(f.join("pack1"), ".", &mut ctx).unwrap_err();
match error {
ResolveError::Json(e) => {
assert_eq!(e.path, f.join("pack1/package.json"));
}
_ => panic!("must be a json error."),
}
assert_eq!(ctx.file_dependencies, FxHashSet::from_iter([f.join("pack1/package.json")]));
assert!(ctx.missing_dependencies.is_empty());
}
@@ -26,10 +26,11 @@ fn incorrect_description_file_1() {
fn incorrect_description_file_2() {
let f = super::fixture().join("incorrect-package");
let resolution = Resolver::default().resolve(f.join("pack2"), ".");
// simd_json has different error messages than serde_json
let error = ResolveError::Json(JSONError {
path: f.join("pack2/package.json"),
message: String::from("EOF while parsing a value at line 1 column 0"),
line: 1,
message: String::from("Eof at character 0"),
line: 0, // simd_json doesn't provide line/column info
column: 0,
});
assert_eq!(resolution, Err(error));
+2 -2
View File
@@ -21,7 +21,7 @@ fn test() {
let package_json =
resolver.resolve(&path, request).ok().and_then(|f| f.package_json().cloned());
let package_json_path = package_json.as_ref().map(|p| &p.path);
let package_json_name = package_json.as_ref().and_then(|p| p.name.as_deref());
let package_json_name = package_json.as_ref().and_then(|p| p.name());
assert_eq!(package_json_path, Some(&resolved_package_json_path), "{path:?} {request}");
assert_eq!(package_json_name, Some("package-json-nested"), "{path:?} {request}");
}
@@ -42,7 +42,7 @@ fn adjacent_to_node_modules() {
let package_json = resolver.resolve(&path, request).unwrap().package_json().cloned();
let package_json_path = package_json.as_ref().map(|p| &p.path);
let package_json_name = package_json.as_ref().and_then(|p| p.name.as_deref());
let package_json_name = package_json.as_ref().and_then(|p| p.name());
assert_eq!(package_json_path, Some(&resolved_package_json_path));
assert_eq!(package_json_name, Some("misc"));
}
+1 -15
View File
@@ -40,7 +40,7 @@ fn package_json() {
let package_json = resolution.package_json().unwrap();
assert_eq!(package_json.name().unwrap(), "name");
assert_eq!(package_json.r#type().unwrap().to_string(), "module".to_string());
assert!(package_json.side_effects.as_ref().unwrap().is_object());
assert_eq!(package_json.side_effects(), None);
}
#[test]
@@ -84,20 +84,6 @@ fn tsconfig_extends_circular_reference() {
);
}
#[cfg(feature = "package_json_raw_json_api")]
#[test]
fn package_json_raw_json_api() {
let resolution = resolve("./tests/package.json");
assert!(
resolution
.package_json()
.unwrap()
.raw_json()
.get("name")
.is_some_and(|name| name == "name")
);
}
#[test]
fn clear_cache() {
let resolver = Resolver::new(ResolveOptions::default());