#[cfg(target_endian = "big")]
mod serde;
#[cfg(target_endian = "little")]
mod simd;
#[cfg(target_endian = "big")]
pub use serde::*;
#[cfg(target_endian = "little")]
pub use simd::*;
use std::{
fmt,
path::{Path, PathBuf},
};
use crate::{JSONError, ResolveError, path::PathUtil};
fn check_if_empty(json_bytes: &[u8], path: &Path) -> Result<(), JSONError> {
if json_bytes.iter().all(|&b| b.is_ascii_whitespace()) {
return Err(JSONError {
path: path.to_path_buf(),
message: "File is empty".to_string(),
line: 0,
column: 0,
});
}
Ok(())
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PackageType {
CommonJs,
Module,
}
impl PackageType {
pub(super) 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 {
Self::CommonJs => f.write_str("commonjs"),
Self::Module => f.write_str("module"),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ImportsExportsKind {
String,
Array,
Map,
Invalid,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SideEffects<'a> {
Bool(bool),
String(&'a str),
Array(Vec<&'a str>),
}
pub trait JsonValue: Sized {
type Object: JsonObject<Value = Self>;
fn as_str(&self) -> Option<&str>;
fn as_bool(&self) -> Option<bool>;
fn as_slice(&self) -> Option<&[Self]>;
fn as_object(&self) -> Option<&Self::Object>;
fn entry_kind(&self) -> ImportsExportsKind;
}
pub trait JsonObject {
type Value: JsonValue<Object = Self>;
fn get(&self, key: &str) -> Option<&Self::Value>;
fn iter(&self) -> impl Iterator<Item = (&str, &Self::Value)>;
}
pub trait PackageJsonBackend {
type Value<'a>: JsonValue
where
Self: 'a;
fn root(&self) -> &Self::Value<'_>;
}
fn get_value_by_path<'a, O: JsonObject>(fields: &'a O, path: &[String]) -> Option<&'a O::Value> {
let mut value = fields.get(path.first()?.as_str())?;
for key in &path[1..] {
value = value.as_object()?.get(key.as_str())?;
}
Some(value)
}
fn alias_value<'a, V: JsonValue>(
key: &Path,
value: &'a V,
) -> Result<Option<&'a str>, ResolveError> {
if let Some(s) = value.as_str() {
return Ok(Some(s));
}
if value.as_bool() == Some(false) {
return Err(ResolveError::Ignored(key.to_path_buf()));
}
Ok(None)
}
pub struct PackageJsonGeneric<S> {
pub path: PathBuf,
pub realpath: PathBuf,
pub(crate) store: S,
}
impl<S: PackageJsonBackend> fmt::Debug for PackageJsonGeneric<S> {
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<S: PackageJsonBackend> PackageJsonGeneric<S> {
#[must_use]
pub fn path(&self) -> &Path {
&self.path
}
#[must_use]
pub fn realpath(&self) -> &Path {
&self.realpath
}
#[must_use]
pub fn directory(&self) -> &Path {
debug_assert!(self.realpath.file_name().is_some_and(|x| x == "package.json"));
self.realpath.parent().unwrap()
}
fn field(&self, key: &str) -> Option<&S::Value<'_>> {
self.store.root().as_object()?.get(key)
}
#[must_use]
pub fn name(&self) -> Option<&str> {
self.field("name")?.as_str()
}
#[must_use]
pub fn version(&self) -> Option<&str> {
self.field("version")?.as_str()
}
#[must_use]
pub fn r#type(&self) -> Option<PackageType> {
PackageType::from_str(self.field("type")?.as_str()?)
}
#[must_use]
pub fn side_effects(&self) -> Option<SideEffects<'_>> {
let value = self.field("sideEffects")?;
if let Some(b) = value.as_bool() {
return Some(SideEffects::Bool(b));
}
if let Some(s) = value.as_str() {
return Some(SideEffects::String(s));
}
value
.as_slice()
.map(|arr| SideEffects::Array(arr.iter().filter_map(JsonValue::as_str).collect()))
}
#[must_use]
pub fn exports(&self) -> Option<ImportsExportsEntryGeneric<'_, S::Value<'_>>> {
Some(ImportsExportsEntryGeneric(self.field("exports")?))
}
#[must_use]
pub fn types(&self) -> Option<&str> {
self.field("types")?.as_str()
}
#[must_use]
pub fn typings(&self) -> Option<&str> {
self.field("typings")?.as_str()
}
pub(crate) fn types_versions(
&self,
) -> Option<ImportsExportsMapGeneric<'_, <S::Value<'_> as JsonValue>::Object>> {
Some(ImportsExportsMapGeneric(self.field("typesVersions")?.as_object()?))
}
pub(crate) fn main_fields<'a>(
&'a self,
main_fields: &'a [String],
) -> impl Iterator<Item = &'a str> + 'a {
let object = self.store.root().as_object();
main_fields
.iter()
.filter_map(move |main_field| object?.get(main_field.as_str()))
.filter_map(JsonValue::as_str)
}
pub(crate) fn exports_fields<'a>(
&'a self,
exports_fields: &'a [Vec<String>],
) -> impl Iterator<Item = ImportsExportsEntryGeneric<'a, S::Value<'a>>> + 'a {
let object = self.store.root().as_object();
exports_fields
.iter()
.filter_map(move |object_path| get_value_by_path(object?, object_path))
.map(ImportsExportsEntryGeneric)
}
pub(crate) fn imports_fields<'a>(
&'a self,
imports_fields: &'a [Vec<String>],
) -> impl Iterator<Item = ImportsExportsMapGeneric<'a, <S::Value<'a> as JsonValue>::Object>> + 'a
{
let object = self.store.root().as_object();
imports_fields
.iter()
.filter_map(move |object_path| get_value_by_path(object?, object_path))
.filter_map(JsonValue::as_object)
.map(ImportsExportsMapGeneric)
}
fn browser_fields<'a>(
&'a self,
alias_fields: &'a [Vec<String>],
) -> impl Iterator<Item = &'a <S::Value<'a> as JsonValue>::Object> + 'a {
let object = self.store.root().as_object();
alias_fields
.iter()
.filter_map(move |object_path| get_value_by_path(object?, object_path))
.filter_map(JsonValue::as_object)
}
pub(crate) fn resolve_browser_field<'a>(
&'a self,
path: &Path,
request: Option<&str>,
alias_fields: &'a [Vec<String>],
) -> Result<Option<&'a str>, ResolveError> {
for object in self.browser_fields(alias_fields) {
if let Some(request) = request {
if let Some(value) = object.get(request) {
return alias_value(path, value);
}
} else {
let dir = self.path.parent().unwrap();
let path_file_name = path.file_name();
for (key, value) in object.iter() {
if let Some(key_file_name) = Path::new(key).file_name()
&& Some(key_file_name) != path_file_name
{
continue;
}
let joined = dir.normalize_with(key);
if joined == path {
return alias_value(path, value);
}
}
}
}
Ok(None)
}
}
#[derive(Clone)]
pub struct ImportsExportsEntryGeneric<'a, V>(pub(crate) &'a V);
impl<'a, V: JsonValue> ImportsExportsEntryGeneric<'a, V> {
#[must_use]
pub fn kind(&self) -> ImportsExportsKind {
self.0.entry_kind()
}
#[must_use]
pub fn as_string(&self) -> Option<&'a str> {
self.0.as_str()
}
#[must_use]
pub fn as_array(&self) -> Option<ImportsExportsArrayGeneric<'a, V>> {
self.0.as_slice().map(ImportsExportsArrayGeneric)
}
#[must_use]
pub fn as_map(&self) -> Option<ImportsExportsMapGeneric<'a, V::Object>> {
self.0.as_object().map(ImportsExportsMapGeneric)
}
}
#[derive(Clone)]
pub struct ImportsExportsArrayGeneric<'a, V>(&'a [V]);
impl<'a, V: JsonValue> ImportsExportsArrayGeneric<'a, V> {
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
pub fn iter(&self) -> impl Iterator<Item = ImportsExportsEntryGeneric<'a, V>> {
self.0.iter().map(ImportsExportsEntryGeneric)
}
}
#[derive(Clone)]
pub struct ImportsExportsMapGeneric<'a, O>(pub(crate) &'a O);
impl<'a, O: JsonObject> ImportsExportsMapGeneric<'a, O> {
pub fn get(&self, key: &str) -> Option<ImportsExportsEntryGeneric<'a, O::Value>> {
self.0.get(key).map(ImportsExportsEntryGeneric)
}
pub fn keys(&self) -> impl Iterator<Item = &'a str> {
self.0.iter().map(|(key, _)| key)
}
pub fn iter(
&self,
) -> impl Iterator<Item = (&'a str, ImportsExportsEntryGeneric<'a, O::Value>)> {
self.0.iter().map(|(key, value)| (key, ImportsExportsEntryGeneric(value)))
}
}