diff --git a/crates/rspack_cacheable/src/with/as_string.rs b/crates/rspack_cacheable/src/with/as_string.rs index 0063f77c2a6e..17dd1e53e875 100644 --- a/crates/rspack_cacheable/src/with/as_string.rs +++ b/crates/rspack_cacheable/src/with/as_string.rs @@ -59,17 +59,3 @@ where AsStringConverter::from_str(field.as_str()) } } - -// for pathbuf -use std::path::PathBuf; -impl AsStringConverter for PathBuf { - fn to_string(&self) -> Result { - Ok(self.to_string_lossy().to_string()) - } - fn from_str(s: &str) -> Result - where - Self: Sized, - { - Ok(PathBuf::from(s)) - } -} diff --git a/crates/rspack_cacheable/src/with/custom.rs b/crates/rspack_cacheable/src/with/custom.rs index 8d871bc3a1c7..eaeec7f0ca24 100644 --- a/crates/rspack_cacheable/src/with/custom.rs +++ b/crates/rspack_cacheable/src/with/custom.rs @@ -1,3 +1,5 @@ +use std::hash::Hash; + use rkyv::{ Archive, Deserialize, Place, Serialize, de::Pooling, @@ -5,8 +7,9 @@ use rkyv::{ ser::Sharing, with::{ArchiveWith, DeserializeWith, SerializeWith}, }; +use rspack_cacheable_macros::enable_cacheable as cacheable; -use crate::{ContextGuard, Error, Result, cacheable}; +use crate::{ContextGuard, Error, Result}; /// A trait for writing custom serialization and deserialization. /// @@ -27,6 +30,32 @@ pub struct Custom; #[cacheable(crate=crate)] pub struct DataBox(T); +// impl hashable for ArchivedDataBox +impl Hash for ArchivedDataBox +where + T: Archive, + T::Archived: Hash, +{ + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} +impl PartialEq for ArchivedDataBox +where + T: Archive, + T::Archived: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} +impl Eq for ArchivedDataBox +where + T: Archive, + T::Archived: Eq, +{ +} + pub struct CustomResolver { resolver: DataBoxResolver, value: DataBox, diff --git a/crates/rspack_collections/src/identifier.rs b/crates/rspack_collections/src/identifier.rs index 3cec6a0ac319..9b5cfea1a958 100644 --- a/crates/rspack_collections/src/identifier.rs +++ b/crates/rspack_collections/src/identifier.rs @@ -9,7 +9,11 @@ use std::{ use dashmap::{DashMap, DashSet}; use hashlink::{LinkedHashMap, LinkedHashSet}; use indexmap::{IndexMap, IndexSet}; -use rspack_cacheable::{cacheable, with, with::AsPreset}; +use rspack_cacheable::{ + ContextGuard, Error as CacheableError, cacheable, + utils::PortableString, + with::{Custom, CustomConverter}, +}; use serde::Serialize; use ustr::Ustr; @@ -34,10 +38,10 @@ pub type IdentifierIndexSet = IndexSet>; pub type IdentifierLinkedSet = LinkedHashSet>; -#[cacheable(hashable)] +#[cacheable(with=Custom, hashable)] #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)] #[cfg_attr(allocative, derive(allocative::Allocative))] -pub struct Identifier(#[cacheable(with=AsPreset)] Ustr); +pub struct Identifier(Ustr); impl Deref for Identifier { type Target = Ustr; @@ -99,14 +103,12 @@ impl fmt::Display for Identifier { } // for Identifier -impl with::AsRefStrConverter for Identifier { - fn as_str(&self) -> &str { - self.0.as_str() +impl CustomConverter for Identifier { + type Target = PortableString; + fn serialize(&self, guard: &ContextGuard) -> Result { + Ok(PortableString::new(self.as_str(), guard.project_root())) } - fn from_str(s: &str) -> Self - where - Self: Sized, - { - s.into() + fn deserialize(data: Self::Target, guard: &ContextGuard) -> Result { + Ok(Self::from(data.into_path_string(guard.project_root()))) } } diff --git a/crates/rspack_core/src/cache/persistent/build_dependencies/mod.rs b/crates/rspack_core/src/cache/persistent/build_dependencies/mod.rs index 1b3622c1e63f..35aa5123d46b 100644 --- a/crates/rspack_core/src/cache/persistent/build_dependencies/mod.rs +++ b/crates/rspack_core/src/cache/persistent/build_dependencies/mod.rs @@ -9,6 +9,7 @@ use rustc_hash::FxHashSet as HashSet; use self::helper::{Helper, is_node_package_path}; use super::{ + codec::CacheCodec, snapshot::{Snapshot, SnapshotOptions}, storage::Storage, }; @@ -40,6 +41,7 @@ impl BuildDeps { snapshot_options: &SnapshotOptions, fs: Arc, storage: Arc, + codec: Arc, ) -> Self { Self { added: Default::default(), @@ -49,6 +51,7 @@ impl BuildDeps { snapshot_options.clone(), fs.clone(), storage.clone(), + codec, ), storage, fs, @@ -112,7 +115,10 @@ mod test { use rspack_fs::{MemoryFileSystem, WritableFileSystem}; use rspack_storage::Storage; - use super::{super::storage::MemoryStorage, BuildDeps, SCOPE, SnapshotOptions}; + use super::{ + super::{codec::CacheCodec, storage::MemoryStorage}, + BuildDeps, SCOPE, SnapshotOptions, + }; #[tokio::test] async fn build_dependencies_test() { let fs = Arc::new(MemoryFileSystem::default()); @@ -151,13 +157,26 @@ mod test { let options = vec![PathBuf::from("/index.js"), PathBuf::from("/configs")]; let snapshot_options = SnapshotOptions::default(); let storage = Arc::new(MemoryStorage::default()); - let mut build_deps = BuildDeps::new(&options, &snapshot_options, fs.clone(), storage.clone()); + let codec = Arc::new(CacheCodec::new(None)); + let mut build_deps = BuildDeps::new( + &options, + &snapshot_options, + fs.clone(), + storage.clone(), + codec.clone(), + ); let warnings = build_deps.add(vec![].into_iter()).await; assert_eq!(warnings.len(), 1); let data = storage.load(SCOPE).await.expect("should load success"); assert_eq!(data.len(), 9); - let mut build_deps = BuildDeps::new(&options, &snapshot_options, fs.clone(), storage.clone()); + let mut build_deps = BuildDeps::new( + &options, + &snapshot_options, + fs.clone(), + storage.clone(), + codec, + ); fs.write("/b.js".into(), r#"require("./c")"#.as_bytes()) .await .unwrap(); diff --git a/crates/rspack_core/src/cache/persistent/cacheable_context.rs b/crates/rspack_core/src/cache/persistent/cacheable_context.rs deleted file mode 100644 index b2e919abb23d..000000000000 --- a/crates/rspack_core/src/cache/persistent/cacheable_context.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[derive(Debug)] -pub struct CacheableContext; - -impl rspack_cacheable::CacheableContext for CacheableContext { - fn project_root(&self) -> Option<&std::path::Path> { - None - } -} diff --git a/crates/rspack_core/src/cache/persistent/codec.rs b/crates/rspack_core/src/cache/persistent/codec.rs new file mode 100644 index 000000000000..24791964c3d7 --- /dev/null +++ b/crates/rspack_core/src/cache/persistent/codec.rs @@ -0,0 +1,64 @@ +use std::path::Path; + +use rspack_cacheable::{ + __private::rkyv::{Archive, Deserialize, Serialize, bytecheck::CheckBytes}, + Deserializer, Serializer, Validator, from_bytes, to_bytes, +}; +use rspack_error::Result; +use rspack_paths::Utf8PathBuf; + +/// Internal cacheable context for serialization +#[derive(Debug, Clone)] +struct Context { + project_path: Option, +} + +impl rspack_cacheable::CacheableContext for Context { + fn project_root(&self) -> Option<&Path> { + self.project_path.as_ref().map(|p| p.as_std_path()) + } +} + +/// Cache codec for encoding and decoding cacheable data +/// +/// This struct encapsulates the serialization and deserialization logic, +/// automatically passing the project context to rspack_cacheable's to_bytes and from_bytes. +/// +/// # Example +/// +/// ```ignore +/// let codec = CacheCodec::new(project_path); +/// +/// // Encode data to bytes +/// let bytes = codec.encode(&my_data)?; +/// +/// // Decode bytes back to data +/// let my_data: MyType = codec.decode(&bytes)?; +/// ``` +#[derive(Debug, Clone)] +pub struct CacheCodec { + context: Context, +} + +impl CacheCodec { + pub fn new(project_path: Option) -> Self { + Self { + context: Context { project_path }, + } + } + + pub fn encode(&self, data: &T) -> Result> + where + T: for<'a> Serialize>, + { + to_bytes(data, &self.context).map_err(|e| rspack_error::error!(e.to_string())) + } + + pub fn decode(&self, bytes: &[u8]) -> Result + where + T: Archive, + T::Archived: for<'a> CheckBytes> + Deserialize, + { + from_bytes(bytes, &self.context).map_err(|e| rspack_error::error!(e.to_string())) + } +} diff --git a/crates/rspack_core/src/cache/persistent/mod.rs b/crates/rspack_core/src/cache/persistent/mod.rs index 6dde42607888..6d5558feecf5 100644 --- a/crates/rspack_core/src/cache/persistent/mod.rs +++ b/crates/rspack_core/src/cache/persistent/mod.rs @@ -1,5 +1,5 @@ pub mod build_dependencies; -mod cacheable_context; +pub mod codec; pub mod occasion; pub mod snapshot; pub mod storage; @@ -9,13 +9,18 @@ use std::{ sync::Arc, }; -pub use cacheable_context::CacheableContext; +use rspack_cacheable::{ + cacheable, + utils::PortablePath, + with::{As, AsVec}, +}; use rspack_fs::{IntermediateFileSystem, ReadableFileSystem}; use rspack_paths::ArcPathSet; use rspack_workspace::rspack_pkg_version; use self::{ build_dependencies::{BuildDeps, BuildDepsOptions}, + codec::CacheCodec, occasion::{MakeOccasion, MetaOccasion}, snapshot::{Snapshot, SnapshotOptions}, storage::{Storage, StorageOptions, create_storage}, @@ -26,8 +31,10 @@ use crate::{ compilation::build_module_graph::{BuildModuleGraphArtifact, BuildModuleGraphArtifactState}, }; +#[cacheable] #[derive(Debug, Clone, Hash)] pub struct PersistentCacheOptions { + #[cacheable(with=AsVec>)] pub build_dependencies: BuildDepsOptions, pub version: String, pub snapshot: SnapshotOptions, @@ -40,10 +47,10 @@ pub struct PersistentCache { initialized: bool, build_deps: BuildDeps, snapshot: Snapshot, - storage: Arc, make_occasion: MakeOccasion, meta_occasion: MetaOccasion, async_mode: bool, + storage: Arc, // TODO replace to logger and output warnings directly. warnings: Vec, } @@ -57,19 +64,23 @@ impl PersistentCache { intermediate_filesystem: Arc, ) -> Self { let async_mode = compiler_options.mode.is_development(); + let codec = Arc::new(CacheCodec::new(None)); + // use codec.encode to transform the absolute path in option, + // it will ensure that same project in different directory have the same version. + let option_bytes = codec + .encode(option) + .expect("should persistent cache options can be serialized"); let version = { let mut hasher = DefaultHasher::new(); compiler_path.hash(&mut hasher); - option.hash(&mut hasher); + option_bytes.hash(&mut hasher); rspack_pkg_version!().hash(&mut hasher); compiler_options.name.hash(&mut hasher); compiler_options.mode.hash(&mut hasher); hex::encode(hasher.finish().to_ne_bytes()) }; let storage = create_storage(option.storage.clone(), version, intermediate_filesystem); - let context = Arc::new(CacheableContext); - let make_occasion = MakeOccasion::new(storage.clone(), context); - let meta_occasion = MetaOccasion::new(storage.clone()); + Self { initialized: false, build_deps: BuildDeps::new( @@ -77,13 +88,19 @@ impl PersistentCache { &option.snapshot, input_filesystem.clone(), storage.clone(), + codec.clone(), ), - snapshot: Snapshot::new(option.snapshot.clone(), input_filesystem, storage.clone()), - storage, - make_occasion, - meta_occasion, - async_mode, + snapshot: Snapshot::new( + option.snapshot.clone(), + input_filesystem, + storage.clone(), + codec.clone(), + ), + make_occasion: MakeOccasion::new(storage.clone(), codec.clone()), + meta_occasion: MetaOccasion::new(storage.clone(), codec), warnings: Default::default(), + async_mode, + storage, } } diff --git a/crates/rspack_core/src/cache/persistent/occasion/make/mod.rs b/crates/rspack_core/src/cache/persistent/occasion/make/mod.rs index 7205525aa1f8..fc1cceeecaa0 100644 --- a/crates/rspack_core/src/cache/persistent/occasion/make/mod.rs +++ b/crates/rspack_core/src/cache/persistent/occasion/make/mod.rs @@ -8,7 +8,7 @@ use rspack_collections::IdentifierSet; use rspack_error::Result; use rustc_hash::FxHashSet; -use super::super::{Storage, cacheable_context::CacheableContext}; +use super::super::{Storage, codec::CacheCodec}; use crate::{ FactorizeInfo, compilation::build_module_graph::{BuildModuleGraphArtifact, BuildModuleGraphArtifactState}, @@ -18,13 +18,13 @@ use crate::{ /// Make Occasion is used to save MakeArtifact #[derive(Debug)] pub struct MakeOccasion { - context: Arc, + codec: Arc, storage: Arc, } impl MakeOccasion { - pub fn new(storage: Arc, context: Arc) -> Self { - Self { storage, context } + pub fn new(storage: Arc, codec: Arc) -> Self { + Self { storage, codec } } #[tracing::instrument(name = "Cache::Occasion::Make::save", skip_all)] @@ -65,14 +65,14 @@ impl MakeOccasion { affected_modules.removed(), &need_update_modules, &self.storage, - &self.context, + &self.codec, ); } #[tracing::instrument(name = "Cache::Occasion::Make::recovery", skip_all)] pub async fn recovery(&self) -> Result { let (mg, module_to_lazy_make, entry_dependencies) = - module_graph::recovery_module_graph(&self.storage, &self.context).await?; + module_graph::recovery_module_graph(&self.storage, &self.codec).await?; // regenerate statistical data // recovery make_failed_module diff --git a/crates/rspack_core/src/cache/persistent/occasion/make/module_graph.rs b/crates/rspack_core/src/cache/persistent/occasion/make/module_graph.rs index 6720d58ecf98..00e798d5e1ae 100644 --- a/crates/rspack_core/src/cache/persistent/occasion/make/module_graph.rs +++ b/crates/rspack_core/src/cache/persistent/occasion/make/module_graph.rs @@ -1,9 +1,7 @@ use std::sync::Arc; use rayon::prelude::*; -use rspack_cacheable::{ - Error as CacheableError, cacheable, from_bytes, to_bytes, utils::OwnedOrRef, -}; +use rspack_cacheable::{cacheable, utils::OwnedOrRef}; use rspack_collections::IdentifierSet; use rspack_error::Result; use rustc_hash::FxHashSet as HashSet; @@ -16,7 +14,7 @@ use crate::{ AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, BoxDependency, BoxModule, Dependency, DependencyId, DependencyParents, ExportsInfoData, ModuleGraph, ModuleGraphConnection, ModuleGraphModule, ModuleIdentifier, RayonConsumer, - cache::persistent::cacheable_context::CacheableContext, + cache::persistent::codec::CacheCodec, compilation::build_module_graph::{LazyDependencies, ModuleToLazyMake}, }; @@ -43,7 +41,7 @@ pub fn save_module_graph( removed_modules: &IdentifierSet, need_update_modules: &IdentifierSet, storage: &Arc, - context: &CacheableContext, + codec: &CacheCodec, ) { for identifier in removed_modules { storage.remove(SCOPE, identifier.as_bytes()); @@ -94,9 +92,9 @@ pub fn save_module_graph( blocks, lazy_info, }; - match to_bytes(&node, context) { + match codec.encode(&node) { Ok(bytes) => (identifier.as_bytes().to_vec(), bytes), - Err(err @ CacheableError::UnsupportedField) => { + Err(err) if err.to_string().contains("unsupported field") => { tracing::warn!("to bytes failed {:?}", err); // try use alternatives node.module = TempModule::transform_from(node.module); @@ -106,7 +104,7 @@ pub fn save_module_graph( .map(|(dep, _)| (TempDependency::transform_from(dep), None)) .collect(); node.blocks = vec![]; - if let Ok(bytes) = to_bytes(&node, context) { + if let Ok(bytes) = codec.encode(&node) { (identifier.as_bytes().to_vec(), bytes) } else { panic!("alternatives serialize failed") @@ -129,7 +127,7 @@ pub fn save_module_graph( #[tracing::instrument("Cache::Occasion::Make::ModuleGraph::recovery", skip_all)] pub async fn recovery_module_graph( storage: &Arc, - context: &CacheableContext, + codec: &CacheCodec, ) -> Result<(ModuleGraph, ModuleToLazyMake, HashSet)> { let mut need_check_dep = vec![]; let mut mg = ModuleGraph::default(); @@ -139,7 +137,8 @@ pub async fn recovery_module_graph( .await? .into_par_iter() .map(|(_, v)| { - from_bytes::(&v, context) + codec + .decode::(&v) .expect("unexpected module graph deserialize failed") }) .with_max_len(1) diff --git a/crates/rspack_core/src/cache/persistent/occasion/meta/mod.rs b/crates/rspack_core/src/cache/persistent/occasion/meta/mod.rs index da6a84e2a30c..41afc8a3b99a 100644 --- a/crates/rspack_core/src/cache/persistent/occasion/meta/mod.rs +++ b/crates/rspack_core/src/cache/persistent/occasion/meta/mod.rs @@ -1,10 +1,10 @@ use std::sync::Arc; -use rspack_cacheable::{cacheable, from_bytes, to_bytes}; +use rspack_cacheable::cacheable; use rspack_error::Result; use rspack_tasks::{get_current_dependency_id, set_current_dependency_id}; -use super::super::Storage; +use super::super::{Storage, codec::CacheCodec}; pub const SCOPE: &str = "meta"; @@ -18,11 +18,12 @@ struct Meta { #[derive(Debug)] pub struct MetaOccasion { storage: Arc, + codec: Arc, } impl MetaOccasion { - pub fn new(storage: Arc) -> Self { - Self { storage } + pub fn new(storage: Arc, codec: Arc) -> Self { + Self { storage, codec } } #[tracing::instrument("Cache::Occasion::Meta::save", skip_all)] @@ -33,7 +34,7 @@ impl MetaOccasion { self.storage.set( SCOPE, "default".as_bytes().to_vec(), - to_bytes(&meta, &()).expect("should to bytes success"), + self.codec.encode(&meta).expect("should encode success"), ); } @@ -43,7 +44,7 @@ impl MetaOccasion { return Ok(()); }; - let meta: Meta = from_bytes(&value, &()).expect("should from bytes success"); + let meta: Meta = self.codec.decode(&value).expect("should decode success"); if get_current_dependency_id() != 0 { panic!("The global dependency id generator is not 0 when the persistent cache is restored."); } diff --git a/crates/rspack_core/src/cache/persistent/snapshot/mod.rs b/crates/rspack_core/src/cache/persistent/snapshot/mod.rs index 6a6007402a3e..c0ef9a5bddc2 100644 --- a/crates/rspack_core/src/cache/persistent/snapshot/mod.rs +++ b/crates/rspack_core/src/cache/persistent/snapshot/mod.rs @@ -1,9 +1,8 @@ mod option; mod strategy; -use std::{path::Path, sync::Arc}; +use std::sync::Arc; -use rspack_cacheable::{from_bytes, to_bytes}; use rspack_error::Result; use rspack_fs::ReadableFileSystem; use rspack_paths::{ArcPath, ArcPathSet}; @@ -13,7 +12,7 @@ pub use self::{ option::{PathMatcher, SnapshotOptions}, strategy::Strategy, }; -use super::storage::Storage; +use super::{codec::CacheCodec, storage::Storage}; use crate::FutureConsumer; pub const SCOPE: &str = "snapshot"; @@ -28,6 +27,7 @@ pub struct Snapshot { options: Arc, fs: Arc, storage: Arc, + codec: Arc, } impl Snapshot { @@ -35,12 +35,14 @@ impl Snapshot { options: SnapshotOptions, fs: Arc, storage: Arc, + codec: Arc, ) -> Self { Self { scope: SCOPE, options: Arc::new(options), fs, storage, + codec, } } @@ -49,12 +51,14 @@ impl Snapshot { options: SnapshotOptions, fs: Arc, storage: Arc, + codec: Arc, ) -> Self { Self { scope, options: Arc::new(options), fs, storage, + codec, } } @@ -81,16 +85,18 @@ impl Snapshot { #[tracing::instrument("Cache::Snapshot::add", skip_all)] pub async fn add(&self, paths: impl Iterator) { let helper = Arc::new(StrategyHelper::new(self.fs.clone())); + let codec = self.codec.clone(); // TODO merge package version file paths .map(|path| { let helper = helper.clone(); let options = self.options.clone(); + let codec = codec.clone(); async move { let strategy = Self::calc_strategy(&options, &helper, &path).await?; Some(( - path.as_os_str().as_encoded_bytes().to_vec(), - to_bytes::<_, ()>(&strategy, &()).expect("should to bytes success"), + codec.encode(&path).expect("should encode success"), + codec.encode(&strategy).expect("should encode success"), )) } }) @@ -117,6 +123,7 @@ impl Snapshot { let mut deleted_path = ArcPathSet::default(); let mut no_change_path = ArcPathSet::default(); let helper = Arc::new(StrategyHelper::new(self.fs.clone())); + let codec = self.codec.clone(); let data = self.storage.load(self.scope).await?; let is_hot_start = !data.is_empty(); @@ -124,10 +131,10 @@ impl Snapshot { .into_iter() .map(|(key, value)| { let helper = helper.clone(); + let codec = codec.clone(); async move { - let path: ArcPath = Path::new(&*String::from_utf8_lossy(&key)).into(); - let strategy: Strategy = - from_bytes::(&value, &()).expect("should from bytes success"); + let path: ArcPath = codec.decode(&key).expect("should decode success"); + let strategy: Strategy = codec.decode(&value).expect("should decode success"); let validate = helper.validate(&path, &strategy).await; (path, validate) } @@ -156,7 +163,10 @@ mod tests { use rspack_fs::{MemoryFileSystem, WritableFileSystem}; use rspack_paths::ArcPath; - use super::{super::storage::MemoryStorage, PathMatcher, Snapshot, SnapshotOptions}; + use super::{ + super::{codec::CacheCodec, storage::MemoryStorage}, + PathMatcher, Snapshot, SnapshotOptions, + }; macro_rules! p { ($tt:tt) => { @@ -168,6 +178,7 @@ mod tests { async fn should_snapshot_work() { let fs = Arc::new(MemoryFileSystem::default()); let storage = Arc::new(MemoryStorage::default()); + let codec = Arc::new(CacheCodec::new(None)); let options = SnapshotOptions::new( vec![PathMatcher::String("constant".into())], vec![PathMatcher::String("node_modules/project".into())], @@ -201,7 +212,7 @@ mod tests { .await .unwrap(); - let snapshot = Snapshot::new(options, fs.clone(), storage); + let snapshot = Snapshot::new(options, fs.clone(), storage, codec); snapshot .add( diff --git a/crates/rspack_core/src/cache/persistent/snapshot/option.rs b/crates/rspack_core/src/cache/persistent/snapshot/option.rs index 6fdca9f6e6e5..c1bd94065e64 100644 --- a/crates/rspack_core/src/cache/persistent/snapshot/option.rs +++ b/crates/rspack_core/src/cache/persistent/snapshot/option.rs @@ -1,9 +1,11 @@ +use rspack_cacheable::{cacheable, utils::PortablePath, with::As}; use rspack_regex::RspackRegex; /// Use string or regex to match path +#[cacheable] #[derive(Debug, Clone, Hash)] pub enum PathMatcher { - String(String), + String(#[cacheable(with=As)] String), Regexp(RspackRegex), } @@ -17,6 +19,7 @@ impl PathMatcher { } /// Snapshot options +#[cacheable] #[derive(Debug, Default, Clone, Hash)] pub struct SnapshotOptions { /// immutable paths, snapshot will ignore them diff --git a/crates/rspack_core/src/cache/persistent/storage/mod.rs b/crates/rspack_core/src/cache/persistent/storage/mod.rs index d55fac9e6989..fce42c9e3c2a 100644 --- a/crates/rspack_core/src/cache/persistent/storage/mod.rs +++ b/crates/rspack_core/src/cache/persistent/storage/mod.rs @@ -4,6 +4,7 @@ mod memory; use std::{path::PathBuf, sync::Arc}; pub use memory::MemoryStorage; +use rspack_cacheable::{cacheable, utils::PortablePath, with::As}; use rspack_fs::IntermediateFileSystem; pub use rspack_storage::Storage; use rspack_storage::{BridgeFileSystem, PackStorage, PackStorageOptions}; @@ -12,9 +13,13 @@ use rspack_storage::{BridgeFileSystem, PackStorage, PackStorageOptions}; /// /// This enum contains all of supported storage options. /// Since MemoryStorage is only used in unit test, there is no need to add it here. +#[cacheable] #[derive(Debug, Clone, Hash)] pub enum StorageOptions { - FileSystem { directory: PathBuf }, + FileSystem { + #[cacheable(with=As)] + directory: PathBuf, + }, } pub fn create_storage( diff --git a/crates/rspack_loader_runner/src/content.rs b/crates/rspack_loader_runner/src/content.rs index 2c19e9c92ba3..9c770cb7db5d 100644 --- a/crates/rspack_loader_runner/src/content.rs +++ b/crates/rspack_loader_runner/src/content.rs @@ -8,7 +8,8 @@ use anymap::CloneAny; use once_cell::sync::OnceCell; use rspack_cacheable::{ cacheable, - with::{AsInner, AsOption, AsPreset, AsString}, + utils::PortablePath, + with::{As, AsInner, AsOption, AsPreset}, }; use rspack_error::{Error, Result, ToStringResultToRspackResultExt}; use rspack_paths::{Utf8Path, Utf8PathBuf}; @@ -307,7 +308,7 @@ impl ResourceData { #[derive(Debug, Clone)] pub struct DescriptionData { /// Path to package.json - #[cacheable(with=AsString)] + #[cacheable(with=As)] path: PathBuf, /// Raw package.json diff --git a/crates/rspack_paths/src/lib.rs b/crates/rspack_paths/src/lib.rs index b3d082b7de3f..0331fc6e47ae 100644 --- a/crates/rspack_paths/src/lib.rs +++ b/crates/rspack_paths/src/lib.rs @@ -11,8 +11,9 @@ pub use camino::{Utf8Component, Utf8Components, Utf8Path, Utf8PathBuf, Utf8Prefi use dashmap::{DashMap, DashSet}; use indexmap::IndexSet; use rspack_cacheable::{ - cacheable, - with::{AsRefStr, AsRefStrConverter}, + ContextGuard, Error as CacheableError, cacheable, + utils::PortablePath, + with::{Custom, CustomConverter}, }; use rustc_hash::FxHasher; use ustr::IdentityHasher; @@ -52,7 +53,7 @@ impl<'a> AssertUtf8 for &'a Path { } } -#[cacheable(with=AsRefStr, hashable)] +#[cacheable(with=Custom)] #[derive(Clone, PartialEq, Eq)] pub struct ArcPath { path: Arc, @@ -126,12 +127,15 @@ impl From<&str> for ArcPath { } } -impl AsRefStrConverter for ArcPath { - fn as_str(&self) -> &str { - self.path.to_str().expect("expect utf8 str") +impl CustomConverter for ArcPath { + type Target = PortablePath; + fn serialize(&self, guard: &ContextGuard) -> Result { + Ok(PortablePath::new(&self.path, guard.project_root())) } - fn from_str(s: &str) -> Self { - Self::from(Path::new(s)) + fn deserialize(data: Self::Target, guard: &ContextGuard) -> Result { + Ok(Self::from(PathBuf::from( + data.into_path_string(guard.project_root()), + ))) } } diff --git a/crates/rspack_tools/src/compare/occasion/make.rs b/crates/rspack_tools/src/compare/occasion/make.rs index 6d196c0fcab3..433c61786479 100644 --- a/crates/rspack_tools/src/compare/occasion/make.rs +++ b/crates/rspack_tools/src/compare/occasion/make.rs @@ -4,9 +4,10 @@ pub use rspack_core::cache::persistent::occasion::make::SCOPE; use rspack_core::{ DependencyId, build_module_graph::BuildModuleGraphArtifact, - cache::persistent::{CacheableContext, occasion::make::MakeOccasion, storage::Storage}, + cache::persistent::{codec::CacheCodec, occasion::make::MakeOccasion, storage::Storage}, }; use rspack_error::Result; +use rspack_paths::Utf8PathBuf; use rustc_hash::FxHashMap as HashMap; use crate::{debug_info::DebugInfo, utils::ensure_iter_equal}; @@ -29,9 +30,10 @@ pub async fn compare( ensure_iter_equal("Make module key", map1.keys(), map2.keys(), &debug_info)?; // Convert stored data to BuildModuleGraphArtifact using MakeOccasion's recovery ability - let context = Arc::new(CacheableContext); - let occasion1 = MakeOccasion::new(storage1.clone(), context.clone()); - let occasion2 = MakeOccasion::new(storage2.clone(), context.clone()); + // Use a dummy path for codec since we're only deserializing + let codec = Arc::new(CacheCodec::new(Some(Utf8PathBuf::from("/")))); + let occasion1 = MakeOccasion::new(storage1.clone(), codec.clone()); + let occasion2 = MakeOccasion::new(storage2.clone(), codec.clone()); let artifact1 = occasion1.recovery().await?; let artifact2 = occasion2.recovery().await?; diff --git a/tests/rspack-test/cacheCases/portable/basic/index.js b/tests/rspack-test/cacheCases/portable/basic/index.js index bbd66bf5780c..3363d4d4ead7 100644 --- a/tests/rspack-test/cacheCases/portable/basic/index.js +++ b/tests/rspack-test/cacheCases/portable/basic/index.js @@ -1,17 +1,17 @@ -import value from "./file"; +import value from './file'; -it("should basic test work", async () => { - if (COMPILER_INDEX == 0) { - expect(value).toBe(1); - await NEXT_START(); - } - if (COMPILER_INDEX == 1) { - expect(value).toBe(1); - await NEXT_MOVE_DIR_START(); - } - if (COMPILER_INDEX == 2) { - expect(value).toBe(3); - } +it('should basic test work', async () => { + if (COMPILER_INDEX == 0) { + expect(value).toBe(1); + await NEXT_START(); + } + if (COMPILER_INDEX == 1) { + expect(value).toBe(1); + await NEXT_MOVE_DIR_START(); + } + if (COMPILER_INDEX == 2) { + expect(value).toBe(3); + } }); -module.hot.accept("./file"); +module.hot.accept('./file');