mod clever_merge;
mod value_type;
use std::borrow::Cow;
use rspack_cacheable::{
cacheable,
with::{AsCacheable, AsMap, AsPreset, AsRefStr, AsTuple2, AsVec},
};
use rspack_paths::Utf8PathBuf;
use rspack_regex::RspackRegex;
use rspack_util::fx_hash::FxLinkedHashMap;
use crate::DependencyCategory;
pub type AliasMap = rspack_resolver::AliasValue;
#[cacheable]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum Alias {
OverwriteToNoAlias,
MergeAlias(
#[cacheable(with=AsVec<AsTuple2<AsCacheable, AsVec<AsPreset>>>)] rspack_resolver::Alias,
),
}
impl Default for Alias {
fn default() -> Self {
Self::MergeAlias(rspack_resolver::Alias::default())
}
}
impl From<rspack_resolver::Alias> for Alias {
fn from(value: rspack_resolver::Alias) -> Alias {
Alias::MergeAlias(value)
}
}
impl value_type::GetValueType for Alias {
fn get_value_type(&self) -> value_type::ValueType {
match self {
Alias::OverwriteToNoAlias => value_type::ValueType::Atom,
Alias::MergeAlias(_) => value_type::ValueType::Other,
}
}
}
#[cacheable]
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum Restriction {
Path(String),
Regex(RspackRegex),
}
pub(super) type Extensions = Vec<String>;
pub(super) type PreferRelative = bool;
pub(super) type PreferAbsolute = bool;
pub(super) type Symlink = bool;
pub(super) type MainFiles = Vec<String>;
pub(super) type MainFields = Vec<String>;
pub(super) type DescriptionFiles = Vec<String>;
pub(super) type AliasFields = Vec<Vec<String>>;
pub(super) type ConditionNames = Vec<String>;
pub(super) type Fallback = Alias;
pub(super) type FullySpecified = bool;
pub(super) type EnforceExtension = bool;
pub(super) type ExportsFields = Vec<Vec<String>>;
pub(super) type ImportsFields = Vec<Vec<String>>;
pub(super) type ExtensionAlias = Vec<(String, Vec<String>)>;
pub(super) type Modules = Vec<String>;
pub(super) type Roots = Vec<String>;
pub(super) type Restrictions = Vec<Restriction>;
#[cacheable]
#[derive(Debug, Clone, Default, Hash, PartialEq, Eq)]
pub struct Resolve {
pub extensions: Option<Extensions>,
pub alias: Option<Alias>,
pub prefer_relative: Option<PreferRelative>,
pub prefer_absolute: Option<PreferAbsolute>,
pub symlinks: Option<Symlink>,
pub main_files: Option<MainFiles>,
pub main_fields: Option<MainFields>,
pub condition_names: Option<ConditionNames>,
pub tsconfig: Option<TsconfigOptions>,
pub modules: Option<Modules>,
pub fallback: Option<Fallback>,
pub fully_specified: Option<FullySpecified>,
pub exports_fields: Option<ExportsFields>,
pub extension_alias: Option<ExtensionAlias>,
pub alias_fields: Option<AliasFields>,
pub roots: Option<Roots>,
pub restrictions: Option<Restrictions>,
pub imports_fields: Option<ImportsFields>,
#[cacheable(omit_bounds)]
pub by_dependency: Option<ByDependency>,
pub description_files: Option<DescriptionFiles>,
pub enforce_extension: Option<EnforceExtension>,
pub pnp: Option<bool>,
pub builtin_modules: bool,
}
#[cacheable]
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default)]
pub struct TsconfigOptions {
#[cacheable(with=AsPreset)]
pub config_file: Utf8PathBuf,
pub references: TsconfigReferences,
}
impl From<TsconfigOptions> for rspack_resolver::TsconfigOptions {
fn from(val: TsconfigOptions) -> Self {
rspack_resolver::TsconfigOptions {
config_file: val.config_file.into(),
references: val.references.into(),
}
}
}
#[cacheable]
#[derive(Debug, Clone, Hash, PartialEq, Eq, Default)]
pub enum TsconfigReferences {
#[default]
Disabled,
Auto,
Paths(#[cacheable(with=AsVec<AsPreset>)] Vec<Utf8PathBuf>),
}
impl From<TsconfigReferences> for rspack_resolver::TsconfigReferences {
fn from(val: TsconfigReferences) -> Self {
match val {
TsconfigReferences::Disabled => rspack_resolver::TsconfigReferences::Disabled,
TsconfigReferences::Auto => rspack_resolver::TsconfigReferences::Auto,
TsconfigReferences::Paths(paths) => {
rspack_resolver::TsconfigReferences::Paths(paths.into_iter().map(Into::into).collect())
}
}
}
}
macro_rules! impl_resolve_by_dependency {
($ident:ident) => {
pub fn $ident(&self, cat: Option<&DependencyCategory>) -> Option<bool> {
cat
.and_then(|cat| {
self
.by_dependency
.as_ref()
.and_then(|by_dep| by_dep.get(cat).and_then(|d| d.$ident))
})
.or(self.$ident)
}
};
}
impl Resolve {
pub fn merge_by_dependency(mut self, dependency_type: DependencyCategory) -> Self {
let Some(mut by_dependency) = self.by_dependency.as_mut().map(std::mem::take) else {
return self;
};
let Some(by_value) = by_dependency
.take(&dependency_type)
.or_else(|| by_dependency.take_default())
else {
return self;
};
Self::merge(self, by_value)
}
pub fn merge(self, value: Self) -> Self {
clever_merge::merge_resolve(self, value)
}
impl_resolve_by_dependency!(fully_specified);
impl_resolve_by_dependency!(prefer_relative);
}
type DependencyCategoryStr = Cow<'static, str>;
#[cacheable]
#[derive(Debug, Clone, Default, Hash, PartialEq, Eq)]
pub struct ByDependency(
#[cacheable(with=AsMap<AsRefStr>)] FxLinkedHashMap<DependencyCategoryStr, Resolve>,
);
impl FromIterator<(DependencyCategoryStr, Resolve)> for ByDependency {
fn from_iter<I: IntoIterator<Item = (DependencyCategoryStr, Resolve)>>(i: I) -> Self {
Self(FxLinkedHashMap::from_iter(i))
}
}
impl ByDependency {
pub fn get(&self, k: &DependencyCategory) -> Option<&Resolve> {
self.0.get(k.as_str())
}
pub fn take(&mut self, k: &DependencyCategory) -> Option<Resolve> {
self.0.get_mut(k.as_str()).map(std::mem::take)
}
pub fn take_default(&mut self) -> Option<Resolve> {
self.0.get_mut("default").map(std::mem::take)
}
}