diff --git a/Cargo.lock b/Cargo.lock index 9269e1614b98..0d9e34eb86f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,6 +388,12 @@ dependencies = [ "serde", ] +[[package]] +name = "camino" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3054fea8a20d8ff3968d5b22cc27501d2b08dc4decdb31b184323f00c5ef23bb" + [[package]] name = "cargo_toml" version = "0.20.4" @@ -2343,6 +2349,9 @@ name = "pathdiff" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +dependencies = [ + "camino", +] [[package]] name = "percent-encoding" @@ -2932,6 +2941,7 @@ dependencies = [ "rspack_loader_testing", "rspack_napi", "rspack_napi_macros", + "rspack_paths", "rspack_plugin_asset", "rspack_plugin_banner", "rspack_plugin_copy", @@ -3042,6 +3052,7 @@ dependencies = [ "rspack_hook", "rspack_loader_runner", "rspack_macros", + "rspack_paths", "rspack_regex", "rspack_resolver", "rspack_sources", @@ -3069,6 +3080,7 @@ dependencies = [ "once_cell", "owo-colors 3.5.0", "rspack_collections", + "rspack_paths", "swc_core", "termcolor", "textwrap 0.15.2", @@ -3082,6 +3094,7 @@ version = "0.1.0" dependencies = [ "futures", "rspack_error", + "rspack_paths", "tokio", ] @@ -3095,6 +3108,7 @@ dependencies = [ "napi-h", "rspack_fs", "rspack_napi", + "rspack_paths", ] [[package]] @@ -3185,6 +3199,7 @@ dependencies = [ "regex", "rspack_collections", "rspack_error", + "rspack_paths", "rspack_sources", "rspack_util", "rustc-hash 1.1.0", @@ -3294,12 +3309,20 @@ dependencies = [ "rspack_hash", "rspack_hook", "rspack_napi", + "rspack_paths", "rspack_plugin_javascript", "rspack_tracing", "tokio", "tracing", ] +[[package]] +name = "rspack_paths" +version = "0.1.0" +dependencies = [ + "camino", +] + [[package]] name = "rspack_plugin_asset" version = "0.1.0" @@ -3349,6 +3372,7 @@ dependencies = [ "rspack_futures", "rspack_hash", "rspack_hook", + "rspack_paths", "rspack_util", "rustc-hash 1.1.0", "sugar_path", @@ -3503,6 +3527,7 @@ dependencies = [ "rspack_core", "rspack_error", "rspack_hook", + "rspack_paths", "rspack_util", "schemars", "serde", @@ -3554,6 +3579,7 @@ dependencies = [ "rspack_hash", "rspack_hook", "rspack_ids", + "rspack_paths", "rspack_regex", "rspack_util", "rustc-hash 1.1.0", @@ -3763,6 +3789,7 @@ dependencies = [ "rspack_core", "rspack_error", "rspack_hook", + "rspack_paths", "tracing", "url", "urlencoding", diff --git a/Cargo.toml b/Cargo.toml index b2dbc5640ca0..9bcebb14685b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ async-recursion = { version = "1.1.0" } async-scoped = { version = "0.9.0" } async-trait = { version = "0.1.79" } bitflags = { version = "2.5.0" } +camino = { version = "1.1.8" } concat-string = "1.0.1" css-module-lexer = "0.0.14" dashmap = { version = "5.5.3" } diff --git a/crates/node_binding/Cargo.toml b/crates/node_binding/Cargo.toml index a4b7697c25cf..6cd525a3d3a2 100644 --- a/crates/node_binding/Cargo.toml +++ b/crates/node_binding/Cargo.toml @@ -24,9 +24,11 @@ rspack_fs_node = { version = "0.1.0", path = "../rspack_fs_node" } rspack_hash = { version = "0.1.0", path = "../rspack_hash" } rspack_hook = { version = "0.1.0", path = "../rspack_hook" } rspack_napi = { version = "0.1.0", path = "../rspack_napi" } +rspack_paths = { version = "0.1.0", path = "../rspack_paths" } rspack_plugin_javascript = { version = "0.1.0", path = "../rspack_plugin_javascript" } -rspack_tracing = { version = "0.1.0", path = "../rspack_tracing" } -tokio = { workspace = true, features = ["rt", "rt-multi-thread"] } + +rspack_tracing = { version = "0.1.0", path = "../rspack_tracing" } +tokio = { workspace = true, features = ["rt", "rt-multi-thread"] } async-trait = { workspace = true } once_cell = { workspace = true } diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index bfae6c9962e2..1c39cfcfae17 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -1733,7 +1733,7 @@ export interface RawSwcJsMinimizerRspackPluginOptions { export interface RawToOptions { context: string - absoluteFilename?: string + absoluteFilename: string } export interface RawTrustedTypes { diff --git a/crates/node_binding/src/plugins/interceptor.rs b/crates/node_binding/src/plugins/interceptor.rs index 4c4044592b22..4ed6beb8d932 100644 --- a/crates/node_binding/src/plugins/interceptor.rs +++ b/crates/node_binding/src/plugins/interceptor.rs @@ -1,7 +1,6 @@ use std::{ borrow::Cow, hash::Hash, - path::PathBuf, sync::{Arc, RwLock}, }; @@ -56,6 +55,7 @@ use rspack_core::{ use rspack_hash::RspackHash; use rspack_hook::{Hook, Interceptor}; use rspack_napi::threadsafe_function::ThreadsafeFunction; +use rspack_paths::Utf8PathBuf; use rspack_plugin_javascript::{JavascriptModulesChunkHash, JavascriptModulesChunkHashHook}; #[napi(object)] @@ -881,8 +881,8 @@ impl CompilerAssetEmitted for CompilerAssetEmittedTap { .function .call_with_promise(JsAssetEmittedArgs { filename: filename.to_string(), - output_path: info.output_path.to_string_lossy().into_owned(), - target_path: info.target_path.to_string_lossy().into_owned(), + output_path: info.output_path.as_str().to_owned(), + target_path: info.target_path.as_str().to_owned(), }) .await } @@ -1290,7 +1290,7 @@ impl NormalModuleFactoryResolveForScheme for NormalModuleFactoryResolveForScheme }) .await?; resource_data.set_resource(new_resource_data.resource); - resource_data.set_path_optional(new_resource_data.path.map(PathBuf::from)); + resource_data.set_path_optional(new_resource_data.path.map(Utf8PathBuf::from)); resource_data.set_query_optional(new_resource_data.query); resource_data.set_fragment_optional(new_resource_data.fragment); Ok(bail) @@ -1440,7 +1440,7 @@ impl ContextModuleFactoryAfterResolve for ContextModuleFactoryAfterResolveTap { AfterResolveResult::Ignored => JsContextModuleFactoryAfterResolveResult::A(false), AfterResolveResult::Data(d) => { JsContextModuleFactoryAfterResolveResult::B(JsContextModuleFactoryAfterResolveData { - resource: d.resource.to_owned(), + resource: d.resource.as_str().to_owned(), context: d.context.to_owned(), request: d.request.to_owned(), reg_exp: d.reg_exp.clone().map(|r| r.into()), @@ -1451,7 +1451,7 @@ impl ContextModuleFactoryAfterResolve for ContextModuleFactoryAfterResolveTap { napi::Either::A(_) => Ok(AfterResolveResult::Ignored), napi::Either::B(d) => { let data = AfterResolveData { - resource: d.resource, + resource: d.resource.into(), context: d.context, request: d.request, reg_exp: match d.reg_exp { diff --git a/crates/rspack_allocator/LICENSE b/crates/rspack_allocator/LICENSE new file mode 100644 index 000000000000..46310101ad8a --- /dev/null +++ b/crates/rspack_allocator/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2022-present Bytedance, Inc. and its affiliates. + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/rspack_binding_options/Cargo.toml b/crates/rspack_binding_options/Cargo.toml index 5694662c4fc8..c84ccb061435 100644 --- a/crates/rspack_binding_options/Cargo.toml +++ b/crates/rspack_binding_options/Cargo.toml @@ -32,6 +32,7 @@ rspack_loader_swc = { version = "0.1.0", path = "../rspack_l rspack_loader_testing = { version = "0.1.0", path = "../rspack_loader_testing" } rspack_napi = { version = "0.1.0", path = "../rspack_napi" } rspack_napi_macros = { version = "0.1.0", path = "../rspack_napi_macros" } +rspack_paths = { version = "0.1.0", path = "../rspack_paths" } rspack_plugin_asset = { version = "0.1.0", path = "../rspack_plugin_asset" } rspack_plugin_banner = { version = "0.1.0", path = "../rspack_plugin_banner" } rspack_plugin_copy = { version = "0.1.0", path = "../rspack_plugin_copy" } diff --git a/crates/rspack_binding_options/src/options/raw_builtins/raw_copy.rs b/crates/rspack_binding_options/src/options/raw_builtins/raw_copy.rs index f15173ec4e5c..59eac2ee7dd7 100644 --- a/crates/rspack_binding_options/src/options/raw_builtins/raw_copy.rs +++ b/crates/rspack_binding_options/src/options/raw_builtins/raw_copy.rs @@ -1,5 +1,3 @@ -use std::path::PathBuf; - use derivative::Derivative; use napi::{bindgen_prelude::Buffer, Either}; use napi_derive::napi; @@ -20,7 +18,7 @@ type RawTo = Either; #[napi(object)] pub struct RawToOptions { pub context: String, - pub absolute_filename: Option, + pub absolute_filename: String, } #[derive(Derivative)] @@ -103,14 +101,14 @@ impl From for CopyPattern { let f = f.clone(); Box::pin(async move { f.call(RawToOptions { - context: ctx.context.to_owned(), - absolute_filename: ctx.absolute_filename.map(|filename| filename.to_owned()), + context: ctx.context.as_str().to_owned(), + absolute_filename: ctx.absolute_filename.as_str().to_owned(), }) .await }) })), }), - context: context.map(PathBuf::from), + context: context.map(Into::into), to_type: if let Some(to_type) = to_type { match to_type.to_lowercase().as_str() { "dir" => Some(ToType::Dir), diff --git a/crates/rspack_binding_options/src/plugins/js_loader/resolver.rs b/crates/rspack_binding_options/src/plugins/js_loader/resolver.rs index 987ce00b92fa..b16985321c68 100644 --- a/crates/rspack_binding_options/src/plugins/js_loader/resolver.rs +++ b/crates/rspack_binding_options/src/plugins/js_loader/resolver.rs @@ -1,4 +1,4 @@ -use std::{path::Path, sync::Arc}; +use std::sync::Arc; use rspack_collections::{Identifiable, Identifier}; use rspack_core::{ @@ -11,6 +11,7 @@ use rspack_loader_lightningcss::LIGHTNINGCSS_LOADER_IDENTIFIER; use rspack_loader_preact_refresh::PREACT_REFRESH_LOADER_IDENTIFIER; use rspack_loader_react_refresh::REACT_REFRESH_LOADER_IDENTIFIER; use rspack_loader_swc::SWC_LOADER_IDENTIFIER; +use rspack_paths::Utf8Path; use super::{JsLoaderRspackPlugin, JsLoaderRspackPluginInner}; @@ -81,15 +82,15 @@ pub(crate) async fn resolve_loader( resolver: &Resolver, l: &ModuleRuleUseLoader, ) -> Result> { - let context = context.as_ref(); + let context = context.as_path(); let loader_request = &l.loader; let loader_options = l.options.as_deref(); let mut rest = None; let prev = if let Some(index) = loader_request.find('?') { rest = Some(&loader_request[index..]); - Path::new(&loader_request[0..index]) + Utf8Path::new(&loader_request[0..index]) } else { - Path::new(loader_request) + Utf8Path::new(loader_request) }; // FIXME: not belong to napi @@ -98,16 +99,12 @@ pub(crate) async fn resolve_loader( } let resolve_result = resolver - .resolve(context, &prev.to_string_lossy()) - .map_err(|err| { - let loader_request = prev.display(); - let context = context.display(); - error!("Failed to resolve loader: {loader_request} in {context} {err:?}") - })?; + .resolve(context.as_std_path(), prev.as_str()) + .map_err(|err| error!("Failed to resolve loader: {prev} in {context}, error: {err:?}"))?; match resolve_result { ResolveResult::Resource(resource) => { - let path = resource.path.to_string_lossy().to_ascii_lowercase(); + let path = resource.path.as_str(); let r#type = if path.ends_with(".mjs") { Some("module") } else if path.ends_with(".cjs") { @@ -119,7 +116,7 @@ pub(crate) async fn resolve_loader( .and_then(|data| data.json().get("type").and_then(|t| t.as_str())) }; // TODO: Should move this logic to `resolver`, since `resolve.alias` may contain query or fragment too. - let resource = resource.path.to_string_lossy().to_string() + rest.unwrap_or_default(); + let resource = resource.path.as_str().to_owned() + rest.unwrap_or_default(); let ident = if let Some(ty) = r#type { format!("{ty}|{resource}") } else { @@ -127,12 +124,8 @@ pub(crate) async fn resolve_loader( }; Ok(Some(Arc::new(JsLoader(ident.into())))) } - ResolveResult::Ignored => { - let loader_request = prev.display(); - let context = context.to_string_lossy(); - Err(error!( - "Failed to resolve loader: loader_request={loader_request}, context={context}" - )) - } + ResolveResult::Ignored => Err(error!( + "Failed to resolve loader: loader_request={prev}, context={context}" + )), } } diff --git a/crates/rspack_binding_options/src/plugins/js_loader/scheduler.rs b/crates/rspack_binding_options/src/plugins/js_loader/scheduler.rs index ce1739519d56..b3d4046b5fef 100644 --- a/crates/rspack_binding_options/src/plugins/js_loader/scheduler.rs +++ b/crates/rspack_binding_options/src/plugins/js_loader/scheduler.rs @@ -50,25 +50,21 @@ pub(crate) fn merge_loader_context( to.additional_data.insert(data.clone()); } to.cacheable = from.cacheable; - to.file_dependencies = from - .file_dependencies - .into_iter() - .map(std::path::PathBuf::from) - .collect(); + to.file_dependencies = from.file_dependencies.into_iter().map(Into::into).collect(); to.context_dependencies = from .context_dependencies .into_iter() - .map(std::path::PathBuf::from) + .map(Into::into) .collect(); to.missing_dependencies = from .missing_dependencies .into_iter() - .map(std::path::PathBuf::from) + .map(Into::into) .collect(); to.build_dependencies = from .build_dependencies .into_iter() - .map(std::path::PathBuf::from) + .map(Into::into) .collect(); to.content = match from.content { Either::A(_) => None, diff --git a/crates/rspack_binding_values/src/compilation/mod.rs b/crates/rspack_binding_values/src/compilation/mod.rs index e3662a250b62..ff77909593a8 100644 --- a/crates/rspack_binding_values/src/compilation/mod.rs +++ b/crates/rspack_binding_values/src/compilation/mod.rs @@ -5,7 +5,6 @@ use std::cell::RefCell; use std::collections::HashMap; use std::ops::Deref; use std::ops::DerefMut; -use std::path::PathBuf; use dependencies::DependenciesDTO; use entries::JsEntries; @@ -412,7 +411,7 @@ impl JsCompilation { self .0 .file_dependencies - .extend(deps.into_iter().map(PathBuf::from)) + .extend(deps.into_iter().map(Into::into)) } #[napi] @@ -420,7 +419,7 @@ impl JsCompilation { self .0 .context_dependencies - .extend(deps.into_iter().map(PathBuf::from)) + .extend(deps.into_iter().map(Into::into)) } #[napi] @@ -428,7 +427,7 @@ impl JsCompilation { self .0 .missing_dependencies - .extend(deps.into_iter().map(PathBuf::from)) + .extend(deps.into_iter().map(Into::into)) } #[napi] @@ -436,7 +435,7 @@ impl JsCompilation { self .0 .build_dependencies - .extend(deps.into_iter().map(PathBuf::from)) + .extend(deps.into_iter().map(Into::into)) } #[napi] diff --git a/crates/rspack_binding_values/src/options/raw_resolve.rs b/crates/rspack_binding_values/src/options/raw_resolve.rs index b1a5b012ce7d..608aef2e2478 100644 --- a/crates/rspack_binding_values/src/options/raw_resolve.rs +++ b/crates/rspack_binding_values/src/options/raw_resolve.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, path::PathBuf}; +use std::collections::HashMap; use napi_derive::napi; use rspack_core::{ @@ -158,7 +158,7 @@ impl TryFrom for TsconfigOptions { type Error = rspack_error::Error; fn try_from(value: RawResolveTsconfigOptions) -> Result { let references = match value.references_type.as_str() { - "manual" => TsconfigReferences::Paths(value.references.unwrap_or_default().into_iter().map(PathBuf::from).collect()), + "manual" => TsconfigReferences::Paths(value.references.unwrap_or_default().into_iter().map(Into::into).collect()), "auto" => TsconfigReferences::Auto, "disabled" => TsconfigReferences::Disabled, _ => panic!( @@ -167,7 +167,7 @@ impl TryFrom for TsconfigOptions { ) }; Ok(TsconfigOptions { - config_file: PathBuf::from(value.config_file), + config_file: value.config_file.into(), references, }) } diff --git a/crates/rspack_binding_values/src/resource_data.rs b/crates/rspack_binding_values/src/resource_data.rs index 4d45e5bcf95d..641cedffa2c2 100644 --- a/crates/rspack_binding_values/src/resource_data.rs +++ b/crates/rspack_binding_values/src/resource_data.rs @@ -17,7 +17,7 @@ impl From for JsResourceData { fn from(value: ResourceData) -> Self { Self { resource: value.resource, - path: value.resource_path.map(|p| p.to_string_lossy().to_string()), + path: value.resource_path.map(|p| p.as_str().to_string()), query: value.resource_query, fragment: value.resource_fragment, } @@ -28,10 +28,7 @@ impl From<&ResourceData> for JsResourceData { fn from(value: &ResourceData) -> Self { Self { resource: value.resource.to_owned(), - path: value - .resource_path - .as_ref() - .map(|p| p.to_string_lossy().to_string()), + path: value.resource_path.as_ref().map(|p| p.as_str().to_string()), fragment: value.resource_fragment.as_ref().map(|r| r.to_owned()), query: value.resource_query.as_ref().map(|r| r.to_owned()), } diff --git a/crates/rspack_binding_values/src/rspack_error.rs b/crates/rspack_binding_values/src/rspack_error.rs index e7e41e524532..679fb41a1a75 100644 --- a/crates/rspack_binding_values/src/rspack_error.rs +++ b/crates/rspack_binding_values/src/rspack_error.rs @@ -52,7 +52,7 @@ impl JsRspackError { message: diagnostic.render_report(colored)?, module_identifier: diagnostic.module_identifier().map(|d| d.to_string()), loc: diagnostic.loc(), - file: diagnostic.file().map(|f| f.to_string_lossy().to_string()), + file: diagnostic.file().map(|f| f.as_str().to_string()), stack: diagnostic.stack(), hide_stack: diagnostic.hide_stack(), }) diff --git a/crates/rspack_binding_values/src/stats.rs b/crates/rspack_binding_values/src/stats.rs index a4a6226ad864..a5c817f55f48 100644 --- a/crates/rspack_binding_values/src/stats.rs +++ b/crates/rspack_binding_values/src/stats.rs @@ -112,7 +112,7 @@ impl From> for JsStatsError { }), message: stats.message, loc: stats.loc, - file: stats.file.map(|f| f.to_string_lossy().to_string()), + file: stats.file.map(|f| f.as_str().to_string()), chunk_name: stats.chunk_name, chunk_entry: stats.chunk_entry, chunk_initial: stats.chunk_initial, @@ -164,7 +164,7 @@ impl From> for JsStatsWarning { .into() }), message: stats.message, - file: stats.file.map(|f| f.to_string_lossy().to_string()), + file: stats.file.map(|f| f.as_str().to_string()), chunk_name: stats.chunk_name, chunk_entry: stats.chunk_entry, chunk_initial: stats.chunk_initial, diff --git a/crates/rspack_core/Cargo.toml b/crates/rspack_core/Cargo.toml index 34ebfbf3c4e4..cbf8d55723a9 100644 --- a/crates/rspack_core/Cargo.toml +++ b/crates/rspack_core/Cargo.toml @@ -36,6 +36,7 @@ rspack_hash = { version = "0.1.0", path = "../rspack_hash" } rspack_hook = { version = "0.1.0", path = "../rspack_hook" } rspack_loader_runner = { version = "0.1.0", path = "../rspack_loader_runner" } rspack_macros = { version = "0.1.0", path = "../rspack_macros" } +rspack_paths = { version = "0.1.0", path = "../rspack_paths" } rspack_regex = { version = "0.1.0", path = "../rspack_regex" } rspack_resolver = { version = "0.1.0", features = ["package_json_raw_json_api"] } rspack_sources = { workspace = true } diff --git a/crates/rspack_core/src/compiler/make/repair/factorize.rs b/crates/rspack_core/src/compiler/make/repair/factorize.rs index 6ea6e013daa4..55c80a8f44a7 100644 --- a/crates/rspack_core/src/compiler/make/repair/factorize.rs +++ b/crates/rspack_core/src/compiler/make/repair/factorize.rs @@ -1,5 +1,4 @@ -use std::path::PathBuf; -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; use rspack_error::Diagnostic; use rspack_sources::BoxSource; diff --git a/crates/rspack_core/src/compiler/mod.rs b/crates/rspack_core/src/compiler/mod.rs index d4b7e6ead8bf..9fd07e875993 100644 --- a/crates/rspack_core/src/compiler/mod.rs +++ b/crates/rspack_core/src/compiler/mod.rs @@ -3,13 +3,13 @@ mod hmr; mod make; mod module_executor; -use std::path::{Path, PathBuf}; use std::sync::Arc; use rspack_error::Result; use rspack_fs::AsyncWritableFileSystem; use rspack_futures::FuturesResults; use rspack_hook::define_hook; +use rspack_paths::{Utf8Path, Utf8PathBuf}; use rspack_sources::BoxSource; use rustc_hash::FxHashMap as HashMap; use tracing::instrument; @@ -249,8 +249,11 @@ where .iter() .filter_map(|(filename, _version)| { if !assets.contains_key(filename) { - let file_path = Path::new(&self.options.output.path).join(filename); - Some(self.output_filesystem.remove_file(&file_path)) + let filename = filename.to_owned(); + Some(async { + let filename = Utf8Path::new(&self.options.output.path).join(filename); + let _ = self.output_filesystem.remove_file(&filename).await; + }) } else { None } @@ -303,7 +306,7 @@ where async fn emit_asset( &self, - output_path: &Path, + output_path: &Utf8Path, filename: &str, asset: &CompilationAsset, ) -> Result<()> { @@ -312,13 +315,13 @@ where .split_once('?') .map(|(filename, _query)| filename) .unwrap_or(filename); - let file_path = Path::new(&output_path).join(filename); + let file_path = output_path.join(filename); self .output_filesystem .create_dir_all( file_path .parent() - .unwrap_or_else(|| panic!("The parent of {} can't found", file_path.display())), + .unwrap_or_else(|| panic!("The parent of {file_path} can't found")), ) .await?; @@ -369,6 +372,6 @@ pub struct CompilationParams { #[derive(Debug)] pub struct AssetEmittedInfo { pub source: BoxSource, - pub output_path: PathBuf, - pub target_path: PathBuf, + pub output_path: Utf8PathBuf, + pub target_path: Utf8PathBuf, } diff --git a/crates/rspack_core/src/compiler/module_executor/execute.rs b/crates/rspack_core/src/compiler/module_executor/execute.rs index 425189f0bdce..16c286a2e785 100644 --- a/crates/rspack_core/src/compiler/module_executor/execute.rs +++ b/crates/rspack_core/src/compiler/module_executor/execute.rs @@ -1,3 +1,4 @@ +use std::path::PathBuf; use std::{iter::once, sync::atomic::AtomicU32}; use itertools::Itertools; @@ -33,10 +34,10 @@ pub type ExecuteModuleId = u32; #[derive(Debug, Default)] pub struct ExecuteModuleResult { pub cacheable: bool, - pub file_dependencies: HashSet, - pub context_dependencies: HashSet, - pub missing_dependencies: HashSet, - pub build_dependencies: HashSet, + pub file_dependencies: HashSet, + pub context_dependencies: HashSet, + pub missing_dependencies: HashSet, + pub build_dependencies: HashSet, pub code_generated_modules: IdentifierSet, pub assets: HashSet, pub id: ExecuteModuleId, diff --git a/crates/rspack_core/src/context_module.rs b/crates/rspack_core/src/context_module.rs index 8c7b6a86f989..8b156c19554b 100644 --- a/crates/rspack_core/src/context_module.rs +++ b/crates/rspack_core/src/context_module.rs @@ -1,11 +1,6 @@ +use std::path::PathBuf; use std::sync::LazyLock; -use std::{ - borrow::Cow, - fs, - hash::Hash, - path::{Path, PathBuf}, - sync::Arc, -}; +use std::{borrow::Cow, fs, hash::Hash, sync::Arc}; use indoc::formatdoc; use itertools::Itertools; @@ -13,6 +8,7 @@ use regex::{Captures, Regex}; use rspack_collections::{Identifiable, Identifier}; use rspack_error::{impl_empty_diagnosable_trait, miette::IntoDiagnostic, Diagnostic, Result}; use rspack_macros::impl_source_map_config; +use rspack_paths::{AssertUtf8, Utf8Path, Utf8PathBuf}; use rspack_regex::RspackRegex; use rspack_sources::{BoxSource, ConcatSource, RawSource, SourceExt}; use rspack_util::{fx_hash::FxIndexMap, json_stringify, source_map::SourceMapKind}; @@ -143,7 +139,7 @@ pub struct ContextOptions { #[derive(Debug, Clone)] pub struct ContextModuleOptions { pub addon: String, - pub resource: String, + pub resource: Utf8PathBuf, pub resource_query: String, pub resource_fragment: String, pub context_options: ContextOptions, @@ -856,7 +852,7 @@ impl Module for ContextModule { id += layer; id += ")/"; } - id += &contextify(options.context, &self.options.resource); + id += &contextify(options.context, self.options.resource.as_str()); id.push(' '); id.push_str(self.options.context_options.mode.as_str()); if self.options.context_options.recursive { @@ -877,7 +873,7 @@ impl Module for ContextModule { let (dependencies, blocks) = self.resolve_dependencies()?; let mut context_dependencies: HashSet = Default::default(); - context_dependencies.insert(PathBuf::from(&self.options.resource)); + context_dependencies.insert(self.options.resource.clone().into_std_path_buf()); let build_info = BuildInfo { context_dependencies, @@ -990,7 +986,7 @@ static WEBPACK_CHUNK_NAME_REQUEST_PLACEHOLDER: LazyLock = impl ContextModule { fn visit_dirs( ctx: &str, - dir: &Path, + dir: &Utf8Path, dependencies: &mut Vec, options: &ContextModuleOptions, resolve_options: &ResolveInnerOptions, @@ -1001,11 +997,11 @@ impl ContextModule { let include = &options.context_options.include; let exclude = &options.context_options.exclude; for entry in fs::read_dir(dir).into_diagnostic()? { - let path = entry.into_diagnostic()?.path(); - let path_str = path.to_string_lossy().to_string(); + let path = entry.into_diagnostic()?.path().assert_utf8(); + let path_str = path.as_str(); if let Some(exclude) = exclude - && exclude.test(&path_str) + && exclude.test(path_str) { // ignore excluded files continue; @@ -1015,15 +1011,12 @@ impl ContextModule { if options.context_options.recursive { Self::visit_dirs(ctx, &path, dependencies, options, resolve_options)?; } - } else if path - .file_name() - .map_or(false, |name| name.to_string_lossy().starts_with('.')) - { + } else if path.file_name().map_or(false, |name| name.starts_with('.')) { // ignore hidden files continue; } else { if let Some(include) = include - && !include.test(&path_str) + && !include.test(path_str) { // ignore not included files continue; @@ -1031,8 +1024,8 @@ impl ContextModule { // FIXME: nodejs resolver return path of context, sometimes is '/a/b', sometimes is '/a/b/' let relative_path = { + let mut path_str = path_str.to_owned(); let p = path_str - .clone() .drain(ctx.len()..) .collect::() .replace('\\', "/"); @@ -1071,7 +1064,7 @@ impl ContextModule { layer: options.layer.clone(), options: options.context_options.clone(), resource_identifier: ContextElementDependency::create_resource_identifier( - &options.resource, + options.resource.as_str(), &path, options.context_options.attributes.as_ref(), ), @@ -1099,8 +1092,8 @@ impl ContextModule { let mut context_element_dependencies = vec![]; Self::visit_dirs( + self.options.resource.as_str(), &self.options.resource, - Path::new(&self.options.resource), &mut context_element_dependencies, &self.options, &resolver.options(), @@ -1192,7 +1185,7 @@ impl ContextModule { } fn create_identifier(options: &ContextModuleOptions) -> Identifier { - let mut id = String::from(&options.resource); + let mut id = options.resource.as_str().to_owned(); if !options.resource_query.is_empty() { id += "|"; id += &options.resource_query; diff --git a/crates/rspack_core/src/context_module_factory.rs b/crates/rspack_core/src/context_module_factory.rs index e0e2c7fc943e..15ba7a17b003 100644 --- a/crates/rspack_core/src/context_module_factory.rs +++ b/crates/rspack_core/src/context_module_factory.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use rspack_error::{error, Result}; use rspack_hook::define_hook; +use rspack_paths::Utf8PathBuf; use rspack_regex::RspackRegex; use tracing::instrument; @@ -41,7 +42,7 @@ pub enum AfterResolveResult { #[derive(Clone)] pub struct AfterResolveData { - pub resource: String, + pub resource: Utf8PathBuf, pub context: String, // dependencies // layer @@ -234,7 +235,7 @@ impl ContextModuleFactory { Ok(ResolveResult::Resource(resource)) => { let options = ContextModuleOptions { addon: loader_request.to_string(), - resource: resource.path.to_string_lossy().to_string(), + resource: resource.path, resource_query: resource.query, resource_fragment: resource.fragment, layer: data.issuer_layer.clone(), diff --git a/crates/rspack_core/src/dependency/context_element_dependency.rs b/crates/rspack_core/src/dependency/context_element_dependency.rs index d0c61ca6a064..18b5254bafb2 100644 --- a/crates/rspack_core/src/dependency/context_element_dependency.rs +++ b/crates/rspack_core/src/dependency/context_element_dependency.rs @@ -1,6 +1,5 @@ -use std::path::Path; - use itertools::Itertools; +use rspack_paths::Utf8Path; use rspack_util::json_stringify; use swc_core::ecma::atoms::Atom; @@ -32,10 +31,10 @@ pub struct ContextElementDependency { impl ContextElementDependency { pub fn create_resource_identifier( resource: &str, - path: &Path, + path: &Utf8Path, attributes: Option<&ImportAttributes>, ) -> String { - let mut ident = format!("context{}|{}", resource, path.display()); + let mut ident = format!("context{}|{}", resource, path); if let Some(attributes) = attributes { ident += &json_stringify(&attributes); } diff --git a/crates/rspack_core/src/dependency/runtime_template.rs b/crates/rspack_core/src/dependency/runtime_template.rs index 2c172f1570e4..4491f5e46832 100644 --- a/crates/rspack_core/src/dependency/runtime_template.rs +++ b/crates/rspack_core/src/dependency/runtime_template.rs @@ -2,7 +2,6 @@ use std::borrow::Cow; use rustc_hash::FxHashSet as HashSet; use serde_json::json; -use sugar_path::SugarPath; use swc_core::ecma::atoms::Atom; use crate::{ diff --git a/crates/rspack_core/src/module_factory.rs b/crates/rspack_core/src/module_factory.rs index 347f043e225b..4a6450995c4e 100644 --- a/crates/rspack_core/src/module_factory.rs +++ b/crates/rspack_core/src/module_factory.rs @@ -33,9 +33,7 @@ impl ModuleFactoryCreateData { } pub fn add_file_dependency(&mut self, file: PathBuf) { - if file.is_absolute() { - self.file_dependencies.insert(file); - } + self.file_dependencies.insert(file); } pub fn add_file_dependencies(&mut self, files: impl IntoIterator) { diff --git a/crates/rspack_core/src/normal_module_factory.rs b/crates/rspack_core/src/normal_module_factory.rs index 0639d4eb9511..375ceff4a2bd 100644 --- a/crates/rspack_core/src/normal_module_factory.rs +++ b/crates/rspack_core/src/normal_module_factory.rs @@ -5,6 +5,7 @@ use regex::Regex; use rspack_error::{error, Result}; use rspack_hook::define_hook; use rspack_loader_runner::{get_scheme, Loader, Scheme}; +use rspack_paths::Utf8PathBuf; use rspack_util::MergeFrom; use sugar_path::SugarPath; use swc_core::common::Span; @@ -182,9 +183,10 @@ impl NormalModuleFactory { .context .as_path() .join(resource) + .as_std_path() .absolutize() .to_string_lossy() - .to_string() + .into_owned() } else { resource.to_owned() } @@ -311,7 +313,7 @@ impl NormalModuleFactory { } else { // resource without scheme and without path if resource.is_empty() || resource.starts_with(QUESTION_MARK) { - ResourceData::new(resource.to_owned()).path("".into()) + ResourceData::new(resource.to_owned()).path(Utf8PathBuf::from("")) } else { // resource without scheme and with path let resolve_args = ResolveArgs { @@ -609,7 +611,7 @@ impl NormalModuleFactory { .await?; if let Some(file_dependency) = file_dependency { - data.add_file_dependency(file_dependency); + data.add_file_dependency(file_dependency.into_std_path_buf()); } data.add_file_dependencies(file_dependencies); data.add_missing_dependencies(missing_dependencies); diff --git a/crates/rspack_core/src/options/context.rs b/crates/rspack_core/src/options/context.rs index e15c8e63a9b1..09f27be7b4bb 100644 --- a/crates/rspack_core/src/options/context.rs +++ b/crates/rspack_core/src/options/context.rs @@ -1,10 +1,7 @@ -use std::{ - fmt, - ops::Deref, - path::{Path, PathBuf}, -}; +use std::{fmt, ops::Deref, path::Path}; use rspack_loader_runner::ResourceData; +use rspack_paths::{Utf8Path, Utf8PathBuf}; use rspack_util::atom::Atom; use crate::{contextify, parse_resource}; @@ -22,6 +19,16 @@ impl Context { pub fn as_str(&self) -> &str { self.as_ref() } + + pub fn as_path(&self) -> &Utf8Path { + Utf8Path::new(self.as_str()) + } +} + +impl AsRef for Context { + fn as_ref(&self) -> &Utf8Path { + Utf8Path::new(self.inner.as_str()) + } } impl AsRef for Context { @@ -56,18 +63,18 @@ impl From<&str> for Context { } } -impl From for Context { - fn from(v: PathBuf) -> Self { +impl From for Context { + fn from(v: Utf8PathBuf) -> Self { Self { - inner: v.to_string_lossy().into(), + inner: v.as_str().into(), } } } -impl From<&Path> for Context { - fn from(v: &Path) -> Self { +impl From<&Utf8Path> for Context { + fn from(v: &Utf8Path) -> Self { Self { - inner: v.to_string_lossy().into(), + inner: v.as_str().into(), } } } @@ -117,7 +124,7 @@ pub fn get_context(resource_data: &ResourceData) -> Context { { dirname.into() } else if let Some(parsed) = parse_resource(&resource_data.resource) { - dirname(&parsed.path.to_string_lossy()).into() + dirname(parsed.path.as_str()).into() } else { Context::from("") } diff --git a/crates/rspack_core/src/options/filename.rs b/crates/rspack_core/src/options/filename.rs index a6e78d8ec1bb..bfa08708aaf9 100644 --- a/crates/rspack_core/src/options/filename.rs +++ b/crates/rspack_core/src/options/filename.rs @@ -247,24 +247,24 @@ fn render_template( }) = parse_resource(filename) { t = t - .map(|t| FILE_PLACEHOLDER.replace_all(t, NoExpand(&file.to_string_lossy()))) + .map(|t| FILE_PLACEHOLDER.replace_all(t, NoExpand(file.as_str()))) .map(|t| { EXT_PLACEHOLDER.replace_all( t, NoExpand( &file .extension() - .map(|p| format!(".{}", p.to_string_lossy())) + .map(|p| format!(".{p}")) .unwrap_or_default(), ), ) }); - if let Some(base) = file.file_name().map(|p| p.to_string_lossy()) { - t = t.map(|t| BASE_PLACEHOLDER.replace_all(t, NoExpand(&base))); + if let Some(base) = file.file_name() { + t = t.map(|t| BASE_PLACEHOLDER.replace_all(t, NoExpand(base))); } - if let Some(name) = file.file_stem().map(|p| p.to_string_lossy()) { - t = t.map(|t| NAME_PLACEHOLDER.replace_all(t, NoExpand(&name))); + if let Some(name) = file.file_stem() { + t = t.map(|t| NAME_PLACEHOLDER.replace_all(t, NoExpand(name))); } t = t .map(|t| { @@ -273,10 +273,9 @@ fn render_template( NoExpand( &file .parent() - .map(|p| p.to_string_lossy()) // "" -> "", "folder" -> "folder/" - .filter(|p| !p.is_empty()) - .map(|p| p + "/") + .filter(|p| !p.as_str().is_empty()) + .map(|p| p.as_str().to_owned() + "/") .unwrap_or_default(), ), ) diff --git a/crates/rspack_core/src/options/module.rs b/crates/rspack_core/src/options/module.rs index 3318d43c20db..d10cef2023ad 100644 --- a/crates/rspack_core/src/options/module.rs +++ b/crates/rspack_core/src/options/module.rs @@ -1,5 +1,4 @@ use std::{ - borrow::{Borrow, Cow}, fmt::{self, Debug}, sync::Arc, }; @@ -542,12 +541,6 @@ impl<'s> From<&'s str> for DataRef<'s> { } } -impl<'s> From<&'s Cow<'_, str>> for DataRef<'s> { - fn from(value: &'s Cow<'_, str>) -> Self { - Self::Str(value.borrow()) - } -} - impl<'s> From<&'s serde_json::Value> for DataRef<'s> { fn from(value: &'s serde_json::Value) -> Self { Self::Value(value) diff --git a/crates/rspack_core/src/options/output.rs b/crates/rspack_core/src/options/output.rs index 53f9fc9dad34..e4158b662b76 100644 --- a/crates/rspack_core/src/options/output.rs +++ b/crates/rspack_core/src/options/output.rs @@ -1,18 +1,12 @@ use std::sync::LazyLock; -use std::{ - borrow::Cow, - fmt::Debug, - hash::Hash, - path::{Path, PathBuf}, - str::FromStr, - string::ParseError, -}; +use std::{borrow::Cow, fmt::Debug, hash::Hash, str::FromStr, string::ParseError}; use derivative::Derivative; use regex::Regex; use rspack_hash::RspackHash; pub use rspack_hash::{HashDigest, HashFunction, HashSalt}; use rspack_macros::MergeFrom; +use rspack_paths::{AssertUtf8, Utf8Path, Utf8PathBuf}; use sugar_path::SugarPath; use crate::{ @@ -28,7 +22,7 @@ pub enum PathInfo { #[derive(Debug)] pub struct OutputOptions { - pub path: PathBuf, + pub path: Utf8PathBuf, pub pathinfo: PathInfo, pub clean: bool, pub public_path: PublicPath, @@ -322,15 +316,25 @@ impl PublicPath { } pub fn render_auto_public_path(compilation: &Compilation, filename: &str) -> String { - let public_path = match Path::new(filename).parent() { + let public_path = match Utf8Path::new(filename).parent() { None => "".to_string(), Some(dirname) => compilation .options .output .path - .relative(compilation.options.output.path.join(dirname).absolutize()) - .to_string_lossy() - .to_string(), + .as_std_path() + .relative( + compilation + .options + .output + .path + .join(dirname) + .into_std_path_buf() + .absolutize(), + ) + .assert_utf8() + .as_str() + .to_owned(), }; Self::ensure_ends_with_slash(public_path) } diff --git a/crates/rspack_core/src/options/resolve/mod.rs b/crates/rspack_core/src/options/resolve/mod.rs index e3b227fcb6d4..8eb4cb735d05 100644 --- a/crates/rspack_core/src/options/resolve/mod.rs +++ b/crates/rspack_core/src/options/resolve/mod.rs @@ -1,9 +1,10 @@ mod clever_merge; mod value_type; -use std::{borrow::Cow, path::PathBuf}; +use std::borrow::Cow; use hashlink::LinkedHashMap; +use rspack_paths::Utf8PathBuf; use crate::DependencyCategory; @@ -97,7 +98,7 @@ pub struct TsconfigOptions { /// You may provide /// * a relative path to the configuration file. It will be resolved relative to cwd. /// * an absolute path to the configuration file. - pub config_file: PathBuf, + pub config_file: Utf8PathBuf, /// Support for Typescript Project References. pub references: TsconfigReferences, @@ -106,7 +107,7 @@ pub struct TsconfigOptions { impl From for rspack_resolver::TsconfigOptions { fn from(val: TsconfigOptions) -> Self { rspack_resolver::TsconfigOptions { - config_file: val.config_file, + config_file: val.config_file.into(), references: val.references.into(), } } @@ -119,7 +120,7 @@ pub enum TsconfigReferences { /// Use the `references` field from tsconfig read from `config_file`. Auto, /// Manually provided relative or absolute path. - Paths(Vec), + Paths(Vec), } impl From for rspack_resolver::TsconfigReferences { @@ -127,7 +128,9 @@ impl From for rspack_resolver::TsconfigReferences { match val { TsconfigReferences::Disabled => rspack_resolver::TsconfigReferences::Disabled, TsconfigReferences::Auto => rspack_resolver::TsconfigReferences::Auto, - TsconfigReferences::Paths(paths) => rspack_resolver::TsconfigReferences::Paths(paths), + TsconfigReferences::Paths(paths) => { + rspack_resolver::TsconfigReferences::Paths(paths.into_iter().map(Into::into).collect()) + } } } } diff --git a/crates/rspack_core/src/resolver/mod.rs b/crates/rspack_core/src/resolver/mod.rs index acaed5b5985e..694c61f942b1 100644 --- a/crates/rspack_core/src/resolver/mod.rs +++ b/crates/rspack_core/src/resolver/mod.rs @@ -1,13 +1,16 @@ mod factory; mod resolver_impl; use std::borrow::Borrow; +use std::fmt; use std::fs; +use std::path::PathBuf; use std::sync::LazyLock; -use std::{fmt, path::PathBuf}; use regex::Regex; use rspack_error::{Error, MietteExt}; use rspack_loader_runner::DescriptionData; +use rspack_paths::AssertUtf8; +use rspack_paths::Utf8PathBuf; use rspack_util::identifier::insert_zero_width_space_for_fragment; use rustc_hash::FxHashSet; use sugar_path::SugarPath; @@ -56,7 +59,7 @@ pub enum ResolveResult { /// Contains the raw `package.json` value if there is one. #[derive(Clone)] pub struct Resource { - pub path: PathBuf, + pub path: Utf8PathBuf, pub query: String, pub fragment: String, pub description_data: Option, @@ -78,8 +81,7 @@ impl Eq for Resource {} impl Resource { /// Get the full path with query and fragment attached. pub fn full_path(&self) -> String { - let mut buf = - insert_zero_width_space_for_fragment(&self.path.display().to_string()).into_owned(); + let mut buf = insert_zero_width_space_for_fragment(self.path.as_str()).into_owned(); buf.push_str(&insert_zero_width_space_for_fragment(&self.query)); buf.push_str(&self.fragment); buf @@ -120,7 +122,11 @@ pub fn resolve_for_error_hints( }); let resolver = plugin_driver.resolver_factory.get(dep); if let Ok(ResolveResult::Resource(resource)) = resolver.resolve(base_dir, args.specifier) { - let relative_path = resource.path.relative(args.context); + let relative_path = resource + .path + .as_std_path() + .relative(args.context) + .assert_utf8(); let suggestion = if let Some((_, [prefix])) = CURRENT_DIR_REGEX .captures_iter(args.specifier) .next() @@ -128,15 +134,15 @@ pub fn resolve_for_error_hints( { // If the specifier is a relative path pointing to the current directory, // we can suggest the path relative to the current directory. - format!("{}{}", prefix, relative_path.to_string_lossy()) + format!("{}{}", prefix, relative_path) } else if PARENT_PATH_REGEX.is_match(args.specifier) { // If the specifier is a relative path to which the parent directory is, // then we return the relative path directly. - relative_path.to_string_lossy().to_string() + relative_path.as_str().to_string() } else { // If the specifier is a package name like or some arbitrary alias, // then we return the full path. - resource.path.to_string_lossy().to_string() + resource.path.as_str().to_string() }; return Some(format!("Did you mean '{}'? @@ -231,10 +237,10 @@ which tries to resolve these kind of requests in the current directory too.", file.ok().and_then(|file| { file.path().file_stem().and_then(|file_stem| { if requested_names.contains(&file_stem.to_string_lossy().to_string()) { - let mut suggestion = file.path().relative(&args.context); + let mut suggestion = file.path().relative(&args.context).assert_utf8(); - if !suggestion.to_string_lossy().starts_with('.') { - suggestion = PathBuf::from(format!("./{}", suggestion.to_string_lossy())); + if !suggestion.as_str().starts_with('.') { + suggestion = Utf8PathBuf::from(format!("./{}", suggestion)); } Some(suggestion) } else { @@ -251,19 +257,15 @@ which tries to resolve these kind of requests in the current directory too.", let mut hint: Vec = vec![]; for suggestion in suggestions { - let suggestion_ext = suggestion - .extension() - .map(|e| e.to_string_lossy()) - .unwrap_or_default(); - let suggestion_path = suggestion.to_string_lossy(); + let suggestion_ext = suggestion.extension().unwrap_or_default(); let specifier = args.specifier; hint.push(format!( - "Found module '{suggestion_path}'. However, it's not possible to request this module without the extension + "Found module '{suggestion}'. However, it's not possible to request this module without the extension if its extension was not listed in the `resolve.extensions`. Here're some possible solutions: 1. add the extension `\".{suggestion_ext}\"` to `resolve.extensions` in your rspack configuration -2. use '{suggestion_path}' instead of '{specifier}' +2. use '{suggestion}' instead of '{specifier}' ")); } diff --git a/crates/rspack_core/src/resolver/resolver_impl.rs b/crates/rspack_core/src/resolver/resolver_impl.rs index 670bca2fe5d8..3aad3b61c447 100644 --- a/crates/rspack_core/src/resolver/resolver_impl.rs +++ b/crates/rspack_core/src/resolver/resolver_impl.rs @@ -9,6 +9,7 @@ use rspack_error::{ DiagnosticExt, Severity, TraceableError, }; use rspack_loader_runner::DescriptionData; +use rspack_paths::AssertUtf8; use rustc_hash::FxHashSet as HashSet; use super::{ResolveResult, Resource}; @@ -129,7 +130,7 @@ impl Resolver { match self { Self::RspackResolver(resolver) => match resolver.resolve(path, request) { Ok(r) => Ok(ResolveResult::Resource(Resource { - path: r.path().to_path_buf(), + path: r.path().to_path_buf().assert_utf8(), query: r.query().unwrap_or_default().to_string(), fragment: r.fragment().unwrap_or_default().to_string(), description_data: r @@ -161,7 +162,7 @@ impl Resolver { .extend(context.missing_dependencies); match result { Ok(r) => Ok(ResolveResult::Resource(Resource { - path: r.path().to_path_buf(), + path: r.path().to_path_buf().assert_utf8(), query: r.query().unwrap_or_default().to_string(), fragment: r.fragment().unwrap_or_default().to_string(), description_data: r @@ -262,7 +263,7 @@ fn to_rspack_resolver_options( .restrictions .unwrap_or_default() .into_iter() - .map(|s| rspack_resolver::Restriction::Path(PathBuf::from(s))) + .map(|s| rspack_resolver::Restriction::Path(s.into())) .collect(); let roots = options .roots diff --git a/crates/rspack_core/src/stats/struct.rs b/crates/rspack_core/src/stats/struct.rs index 9deaba65260b..31a9631e9c51 100644 --- a/crates/rspack_core/src/stats/struct.rs +++ b/crates/rspack_core/src/stats/struct.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::fmt::Debug; -use std::path::PathBuf; +use rspack_paths::Utf8PathBuf; use rspack_sources::Source; use rspack_util::atom::Atom; use rustc_hash::FxHashMap as HashMap; @@ -45,7 +45,7 @@ pub struct StatsError<'s> { pub module_name: Option>, pub module_id: Option<&'s str>, pub loc: Option, - pub file: Option, + pub file: Option, pub chunk_name: Option, pub chunk_entry: Option, @@ -63,7 +63,7 @@ pub struct StatsWarning<'s> { pub module_name: Option>, pub module_id: Option<&'s str>, pub loc: Option, - pub file: Option, + pub file: Option, pub chunk_name: Option, pub chunk_entry: Option, diff --git a/crates/rspack_core/src/utils/file_counter/incremental_info.rs b/crates/rspack_core/src/utils/file_counter/incremental_info.rs index c84e9b9a1d4a..d0ad7551ed0b 100644 --- a/crates/rspack_core/src/utils/file_counter/incremental_info.rs +++ b/crates/rspack_core/src/utils/file_counter/incremental_info.rs @@ -43,11 +43,13 @@ impl IncrementalInfo { #[cfg(test)] mod test { + use std::path::PathBuf; + use super::IncrementalInfo; #[test] fn incremental_info_is_available() { let mut info = IncrementalInfo::default(); - let file_a = std::path::PathBuf::from("/a"); + let file_a = PathBuf::from("/a"); info.add(&file_a); info.add(&file_a); diff --git a/crates/rspack_core/src/utils/file_counter/mod.rs b/crates/rspack_core/src/utils/file_counter/mod.rs index c5e62500f731..25f1b9d87fcd 100644 --- a/crates/rspack_core/src/utils/file_counter/mod.rs +++ b/crates/rspack_core/src/utils/file_counter/mod.rs @@ -13,9 +13,9 @@ pub struct FileCounter { } impl FileCounter { - /// Add a pathbuf to counter + /// Add a [`PathBuf``] to counter /// - /// It will +1 to the pathbuf in inner hashmap + /// It will +1 to the PathBuf in inner hashmap fn add_file(&mut self, path: &PathBuf) { if let Some(value) = self.inner.get_mut(path) { *value += 1; @@ -25,12 +25,12 @@ impl FileCounter { } } - /// Remove a pathbuf from counter + /// Remove a [`PathBuf`] from counter /// - /// It will -1 to the pathbuf in inner hashmap + /// It will -1 to the PathBuf in inner hashmap /// - /// If the pathbuf usage is 0 after reduction, the record will be deleted - /// If pathbuf does not exist, panic will occur. + /// If the PathBuf usage is 0 after reduction, the record will be deleted + /// If PathBuf does not exist, panic will occur. fn remove_file(&mut self, path: &PathBuf) { if let Some(value) = self.inner.get_mut(path) { *value -= 1; @@ -43,14 +43,14 @@ impl FileCounter { } } - /// Add batch pathbuf to counter + /// Add batch [`PathBuf``] to counter pub fn add_batch_file(&mut self, paths: &HashSet) { for path in paths { self.add_file(path); } } - /// Remove batch pathbuf to counter + /// Remove batch [`PathBuf`] to counter pub fn remove_batch_file(&mut self, paths: &HashSet) { for path in paths { self.remove_file(path); diff --git a/crates/rspack_core/src/utils/identifier.rs b/crates/rspack_core/src/utils/identifier.rs index 51e69e2b15b7..baf3fc707552 100644 --- a/crates/rspack_core/src/utils/identifier.rs +++ b/crates/rspack_core/src/utils/identifier.rs @@ -1,16 +1,17 @@ +use std::borrow::Cow; use std::sync::LazyLock; -use std::{borrow::Cow, path::Path}; use regex::Regex; +use rspack_paths::Utf8Path; use rspack_util::identifier::absolute_to_request; use crate::ModuleRuleUseLoader; -pub fn contextify(context: impl AsRef, request: &str) -> String { +pub fn contextify(context: impl AsRef, request: &str) -> String { let context = context.as_ref(); request .split('!') - .map(|r| absolute_to_request(&context.to_string_lossy(), r)) + .map(|r| absolute_to_request(context.as_str(), r)) .collect::>>() .join("!") } diff --git a/crates/rspack_core/src/utils/module_rules.rs b/crates/rspack_core/src/utils/module_rules.rs index ada9c92903ae..1337faaf8ef0 100644 --- a/crates/rspack_core/src/utils/module_rules.rs +++ b/crates/rspack_core/src/utils/module_rules.rs @@ -1,8 +1,7 @@ -use std::borrow::Cow; - use async_recursion::async_recursion; use rspack_error::Result; use rspack_loader_runner::ResourceData; +use rspack_paths::Utf8Path; use crate::{DependencyCategory, ImportAttributes, ModuleRule, ModuleRuleEffect}; @@ -51,32 +50,30 @@ pub async fn module_rule_matcher<'a>( // Include all modules that pass test assertion. If you supply a Rule.test option, you cannot also supply a `Rule.resource`. // See: https://webpack.js.org/configuration/module/#ruletest - let resource_path = || { - resource_data - .resource_path - .as_deref() - .map(|p| p.to_string_lossy()) - .unwrap_or_else(|| Cow::Borrowed("")) - }; + let resource_path = resource_data + .resource_path + .as_deref() + .unwrap_or_else(|| Utf8Path::new("")) + .as_str(); if let Some(test_rule) = &module_rule.test - && !test_rule.try_match((&resource_path()).into()).await? + && !test_rule.try_match(resource_path.into()).await? { return Ok(false); } else if let Some(resource_rule) = &module_rule.resource - && !resource_rule.try_match((&resource_path()).into()).await? + && !resource_rule.try_match(resource_path.into()).await? { return Ok(false); } if let Some(include_rule) = &module_rule.include - && !include_rule.try_match((&resource_path()).into()).await? + && !include_rule.try_match(resource_path.into()).await? { return Ok(false); } if let Some(exclude_rule) = &module_rule.exclude - && exclude_rule.try_match((&resource_path()).into()).await? + && exclude_rule.try_match(resource_path.into()).await? { return Ok(false); } diff --git a/crates/rspack_error/Cargo.toml b/crates/rspack_error/Cargo.toml index 7a3f518ac134..362a2a691ae8 100644 --- a/crates/rspack_error/Cargo.toml +++ b/crates/rspack_error/Cargo.toml @@ -15,6 +15,7 @@ miette = { version = "5", features = ["fancy"] } once_cell = { workspace = true } owo-colors = "3.5.0" rspack_collections = { version = "0.1.0", path = "../rspack_collections" } +rspack_paths = { version = "0.1.0", path = "../rspack_paths" } swc_core = { workspace = true, features = ["common", "common_concurrent"] } termcolor = "1" textwrap = "0.15.2" diff --git a/crates/rspack_error/src/diagnostic.rs b/crates/rspack_error/src/diagnostic.rs index ec3e1b40c91a..ede6650ce960 100644 --- a/crates/rspack_error/src/diagnostic.rs +++ b/crates/rspack_error/src/diagnostic.rs @@ -1,12 +1,8 @@ -use std::{ - fmt, - ops::Deref, - path::{Path, PathBuf}, - sync::Arc, -}; +use std::{fmt, ops::Deref, sync::Arc}; use miette::{GraphicalTheme, IntoDiagnostic, MietteDiagnostic}; use rspack_collections::Identifier; +use rspack_paths::{Utf8Path, Utf8PathBuf}; use swc_core::common::{SourceMap, Span}; use crate::{graphical::GraphicalReportHandler, Error}; @@ -97,7 +93,7 @@ pub struct Diagnostic { inner: Arc, module_identifier: Option, loc: Option, - file: Option, + file: Option, hide_stack: Option, chunk: Option, stack: Option, @@ -208,11 +204,11 @@ impl Diagnostic { self } - pub fn file(&self) -> Option<&Path> { + pub fn file(&self) -> Option<&Utf8Path> { self.file.as_deref() } - pub fn with_file(mut self, file: Option) -> Self { + pub fn with_file(mut self, file: Option) -> Self { self.file = file; self } diff --git a/crates/rspack_fs/Cargo.toml b/crates/rspack_fs/Cargo.toml index 7850be5a0d6a..da0136ad6b91 100644 --- a/crates/rspack_fs/Cargo.toml +++ b/crates/rspack_fs/Cargo.toml @@ -17,10 +17,16 @@ optional = true path = "../rspack_error" version = "0.1.0" + +[dependencies.rspack_paths] +path = "../rspack_paths" +version = "0.1.0" + [dependencies.futures] optional = true workspace = true [dependencies.tokio] +features = ["fs"] optional = true workspace = true diff --git a/crates/rspack_fs/src/async.rs b/crates/rspack_fs/src/async.rs index 075ffea576c3..21fa2e3802ac 100644 --- a/crates/rspack_fs/src/async.rs +++ b/crates/rspack_fs/src/async.rs @@ -1,6 +1,5 @@ -use std::path::Path; - use futures::future::BoxFuture; +use rspack_paths::Utf8Path; use crate::Result; @@ -15,27 +14,27 @@ pub trait AsyncWritableFileSystem { /// - User lacks permissions to create directory at path. /// - A parent of the given path doesn’t exist. (To create a directory and all its missing parents at the same time, use the create_dir_all function.) /// - Path already exists. - fn create_dir(&self, dir: &Path) -> BoxFuture<'_, Result<()>>; + fn create_dir<'a>(&'a self, dir: &'a Utf8Path) -> BoxFuture<'a, Result<()>>; /// Recursively create a directory and all of its parent components if they are missing. - fn create_dir_all(&self, dir: &Path) -> BoxFuture<'_, Result<()>>; + fn create_dir_all<'a>(&'a self, dir: &'a Utf8Path) -> BoxFuture<'a, Result<()>>; /// Write a slice as the entire contents of a file. /// This function will create a file if it does not exist, and will entirely replace its contents if it does. - fn write(&self, file: &Path, data: &[u8]) -> BoxFuture<'_, Result<()>>; + fn write<'a>(&'a self, file: &'a Utf8Path, data: &'a [u8]) -> BoxFuture<'a, Result<()>>; /// Removes a file from the filesystem. - fn remove_file(&self, file: &Path) -> BoxFuture<'_, Result<()>>; + fn remove_file<'a>(&'a self, file: &'a Utf8Path) -> BoxFuture<'a, Result<()>>; /// Removes a directory at this path, after removing all its contents. Use carefully. - fn remove_dir_all(&self, dir: &Path) -> BoxFuture<'_, Result<()>>; + fn remove_dir_all<'a>(&'a self, dir: &'a Utf8Path) -> BoxFuture<'a, Result<()>>; } pub trait AsyncReadableFileSystem { /// Read the entire contents of a file into a bytes vector. /// /// Error: This function will return an error if path does not already exist. - fn read(&self, file: &Path) -> BoxFuture<'_, Result>>; + fn read<'a>(&'a self, file: &'a Utf8Path) -> BoxFuture<'a, Result>>; } /// Async readable and writable file system representation. diff --git a/crates/rspack_fs/src/native.rs b/crates/rspack_fs/src/native.rs index f37fa5220604..ff30b559df94 100644 --- a/crates/rspack_fs/src/native.rs +++ b/crates/rspack_fs/src/native.rs @@ -1,4 +1,6 @@ -use std::{fs, path::Path}; +use std::fs; + +use rspack_paths::Utf8Path; use super::{ cfg_async, @@ -9,21 +11,21 @@ use super::{ pub struct NativeFileSystem; impl WritableFileSystem for NativeFileSystem { - fn create_dir(&self, dir: &Path) -> Result<()> { + fn create_dir(&self, dir: &Utf8Path) -> Result<()> { fs::create_dir(dir).map_err(Error::from) } - fn create_dir_all(&self, dir: &Path) -> Result<()> { + fn create_dir_all(&self, dir: &Utf8Path) -> Result<()> { fs::create_dir_all(dir).map_err(Error::from) } - fn write(&self, file: &Path, data: &[u8]) -> Result<()> { + fn write(&self, file: &Utf8Path, data: &[u8]) -> Result<()> { fs::write(file, data).map_err(Error::from) } } impl ReadableFileSystem for NativeFileSystem { - fn read(&self, file: &Path) -> Result> { + fn read(&self, file: &Utf8Path) -> Result> { fs::read(file).map_err(Error::from) } } @@ -35,45 +37,40 @@ cfg_async! { pub struct AsyncNativeFileSystem; impl AsyncWritableFileSystem for AsyncNativeFileSystem { - fn create_dir(&self, dir: &Path) -> BoxFuture<'_, Result<()>> { - let dir = dir.to_string_lossy().to_string(); + fn create_dir<'a>(&'a self, dir: &'a Utf8Path) -> BoxFuture<'a, Result<()>> { + let dir = dir.to_path_buf(); let fut = async move { tokio::fs::create_dir(dir).await.map_err(Error::from) }; Box::pin(fut) } - fn create_dir_all(&self, dir: &Path) -> BoxFuture<'_, Result<()>> { - let dir = dir.to_string_lossy().to_string(); + fn create_dir_all<'a>(&'a self, dir: &'a Utf8Path) -> BoxFuture<'a, Result<()>> { let fut = async move { tokio::fs::create_dir_all(dir).await.map_err(Error::from) }; Box::pin(fut) } - fn write( - &self, - file: &Path, - data: &[u8], - ) -> BoxFuture<'_, Result<()>> { - let file = file.to_string_lossy().to_string(); - let data = data.to_vec(); + fn write<'a>( + &'a self, + file: &'a Utf8Path, + data: &'a [u8], + ) -> BoxFuture<'a, Result<()>> { let fut = async move { tokio::fs::write(file, data).await.map_err(Error::from) }; Box::pin(fut) } - fn remove_file(&self, file: &Path) -> BoxFuture<'_, Result<()>> { - let file = file.to_string_lossy().to_string(); + fn remove_file<'a>(&'a self, file: &'a Utf8Path) -> BoxFuture<'a, Result<()>> { let fut = async move { tokio::fs::remove_file(file).await.map_err(Error::from) }; Box::pin(fut) } - fn remove_dir_all(&self, dir: &Path) -> BoxFuture<'_, Result<()>> { - let dir = dir.to_string_lossy().to_string(); + fn remove_dir_all<'a>(&'a self, dir: &'a Utf8Path) -> BoxFuture<'a, Result<()>> { + let dir = dir.to_path_buf(); let fut = async move { tokio::fs::remove_dir_all(dir).await.map_err(Error::from) }; Box::pin(fut) } } impl AsyncReadableFileSystem for AsyncNativeFileSystem { - fn read(&self, file: &Path) -> BoxFuture<'_, Result>> { - let file = file.to_string_lossy().to_string(); + fn read<'a>(&'a self, file: &'a Utf8Path) -> BoxFuture<'a, Result>> { let fut = async move { tokio::fs::read(file).await.map_err(Error::from) }; Box::pin(fut) } diff --git a/crates/rspack_fs/src/sync.rs b/crates/rspack_fs/src/sync.rs index 420b736f1b3b..1b729aee4f4b 100644 --- a/crates/rspack_fs/src/sync.rs +++ b/crates/rspack_fs/src/sync.rs @@ -1,4 +1,4 @@ -use std::path::Path; +use rspack_paths::Utf8Path; use super::Result; @@ -13,21 +13,21 @@ pub trait WritableFileSystem { /// - User lacks permissions to create directory at path. /// - A parent of the given path doesn’t exist. (To create a directory and all its missing parents at the same time, use the create_dir_all function.) /// - Path already exists. - fn create_dir(&self, dir: &Path) -> Result<()>; + fn create_dir(&self, dir: &Utf8Path) -> Result<()>; /// Recursively create a directory and all of its parent components if they are missing. - fn create_dir_all(&self, dir: &Path) -> Result<()>; + fn create_dir_all(&self, dir: &Utf8Path) -> Result<()>; /// Write a slice as the entire contents of a file. /// This function will create a file if it does not exist, and will entirely replace its contents if it does. - fn write(&self, file: &Path, data: &[u8]) -> Result<()>; + fn write(&self, file: &Utf8Path, data: &[u8]) -> Result<()>; } pub trait ReadableFileSystem { /// Read the entire contents of a file into a bytes vector. /// /// Error: This function will return an error if path does not already exist. - fn read(&self, file: &Path) -> Result>; + fn read(&self, file: &Utf8Path) -> Result>; } /// Readable and writable file system representation. diff --git a/crates/rspack_fs_node/Cargo.toml b/crates/rspack_fs_node/Cargo.toml index 8751a33a4e52..3c4698665cd1 100644 --- a/crates/rspack_fs_node/Cargo.toml +++ b/crates/rspack_fs_node/Cargo.toml @@ -13,11 +13,12 @@ async = ["rspack_fs/async"] default = ["async"] [dependencies] -futures = { workspace = true } -napi = { workspace = true, features = ["napi4", "tokio_rt"] } -napi-derive = { workspace = true } -rspack_fs = { version = "0.1.0", path = "../rspack_fs", default-features = false, features = ["rspack-error"] } -rspack_napi = { version = "0.1.0", path = "../rspack_napi" } +futures = { workspace = true } +napi = { workspace = true, features = ["napi4", "tokio_rt"] } +napi-derive = { workspace = true } +rspack_fs = { version = "0.1.0", path = "../rspack_fs", default-features = false, features = ["rspack-error"] } +rspack_napi = { version = "0.1.0", path = "../rspack_napi" } +rspack_paths = { version = "0.1.0", path = "../rspack_paths" } [build-dependencies] napi-build = { workspace = true } diff --git a/crates/rspack_fs_node/src/async.rs b/crates/rspack_fs_node/src/async.rs index e1c39973b309..c49310e29be4 100644 --- a/crates/rspack_fs_node/src/async.rs +++ b/crates/rspack_fs_node/src/async.rs @@ -1,7 +1,6 @@ -use std::path::Path; - use futures::future::BoxFuture; use rspack_fs::r#async::AsyncWritableFileSystem; +use rspack_paths::Utf8Path; use crate::node::ThreadsafeNodeFS; @@ -14,9 +13,9 @@ impl AsyncNodeWritableFileSystem { } impl AsyncWritableFileSystem for AsyncNodeWritableFileSystem { - fn create_dir(&self, dir: &Path) -> BoxFuture<'_, rspack_fs::Result<()>> { - let dir = dir.to_string_lossy().to_string(); - let fut = async move { + fn create_dir<'a>(&'a self, dir: &'a Utf8Path) -> BoxFuture<'a, rspack_fs::Result<()>> { + let fut = async { + let dir = dir.as_str().to_string(); self.0.mkdir.call(dir).await.map_err(|e| { rspack_fs::Error::Io(std::io::Error::new( std::io::ErrorKind::Other, @@ -28,9 +27,9 @@ impl AsyncWritableFileSystem for AsyncNodeWritableFileSystem { Box::pin(fut) } - fn create_dir_all(&self, dir: &Path) -> BoxFuture<'_, rspack_fs::Result<()>> { - let dir = dir.to_string_lossy().to_string(); - let fut = async move { + fn create_dir_all<'a>(&'a self, dir: &'a Utf8Path) -> BoxFuture<'a, rspack_fs::Result<()>> { + let fut = async { + let dir = dir.as_str().to_string(); self .0 .mkdirp @@ -47,10 +46,14 @@ impl AsyncWritableFileSystem for AsyncNodeWritableFileSystem { Box::pin(fut) } - fn write(&self, file: &Path, data: &[u8]) -> BoxFuture<'_, rspack_fs::Result<()>> { - let file = file.to_string_lossy().to_string(); - let data = data.to_vec(); - let fut = async move { + fn write<'a>( + &'a self, + file: &'a Utf8Path, + data: &'a [u8], + ) -> BoxFuture<'a, rspack_fs::Result<()>> { + let fut = async { + let file = file.as_str().to_string(); + let data = data.to_vec(); self .0 .write_file @@ -66,9 +69,9 @@ impl AsyncWritableFileSystem for AsyncNodeWritableFileSystem { Box::pin(fut) } - fn remove_file(&self, file: &Path) -> BoxFuture<'_, rspack_fs::Result<()>> { - let file = file.to_string_lossy().to_string(); - let fut = async move { + fn remove_file<'a>(&'a self, file: &'a Utf8Path) -> BoxFuture<'a, rspack_fs::Result<()>> { + let fut = async { + let file = file.as_str().to_string(); self .0 .remove_file @@ -85,9 +88,9 @@ impl AsyncWritableFileSystem for AsyncNodeWritableFileSystem { Box::pin(fut) } - fn remove_dir_all(&self, dir: &Path) -> BoxFuture<'_, rspack_fs::Result<()>> { - let dir = dir.to_string_lossy().to_string(); - let fut = async move { + fn remove_dir_all<'a>(&'a self, dir: &'a Utf8Path) -> BoxFuture<'a, rspack_fs::Result<()>> { + let fut = async { + let dir = dir.as_str().to_string(); self .0 .remove_dir_all diff --git a/crates/rspack_fs_node/src/sync.rs b/crates/rspack_fs_node/src/sync.rs index 7203eb803a9d..4e07a05c175f 100644 --- a/crates/rspack_fs_node/src/sync.rs +++ b/crates/rspack_fs_node/src/sync.rs @@ -1,7 +1,8 @@ -use std::{marker::PhantomData, path::Path}; +use std::marker::PhantomData; use napi::Env; use rspack_fs::{sync::WritableFileSystem, Error, Result}; +use rspack_paths::Utf8Path; use crate::node::{NodeFS, NodeFSRef, TryIntoNodeFSRef}; @@ -22,15 +23,15 @@ impl NodeWritableFileSystem { } impl WritableFileSystem for NodeWritableFileSystem { - fn create_dir(&self, dir: &Path) -> Result<()> { - let dir = dir.to_string_lossy(); + fn create_dir(&self, dir: &Utf8Path) -> Result<()> { + let dir = dir.as_str(); let mkdir = self.fs_ref.mkdir.get().expect("Failed to get mkdir"); mkdir .call( None, &[self .env - .create_string(&dir) + .create_string(dir) .expect("Failed to create string")], ) .map_err(|err| { @@ -43,15 +44,15 @@ impl WritableFileSystem for NodeWritableFileSystem { Ok(()) } - fn create_dir_all(&self, dir: &Path) -> Result<()> { - let dir = dir.to_string_lossy(); + fn create_dir_all(&self, dir: &Utf8Path) -> Result<()> { + let dir = dir.as_str(); let mkdirp = self.fs_ref.mkdirp.get().expect("Failed to get mkdirp"); mkdirp .call( None, &[self .env - .create_string(&dir) + .create_string(dir) .expect("Failed to create string")], ) .map_err(|err| { @@ -64,8 +65,8 @@ impl WritableFileSystem for NodeWritableFileSystem { Ok(()) } - fn write(&self, file: &Path, data: &[u8]) -> Result<()> { - let file = file.to_string_lossy(); + fn write(&self, file: &Utf8Path, data: &[u8]) -> Result<()> { + let file = file.as_str(); let buf = data.to_vec(); let write_file = self .fs_ref @@ -79,7 +80,7 @@ impl WritableFileSystem for NodeWritableFileSystem { &[ self .env - .create_string(&file) + .create_string(file) .expect("Failed to create string") .into_unknown(), self diff --git a/crates/rspack_loader_lightningcss/src/lib.rs b/crates/rspack_loader_lightningcss/src/lib.rs index b0c409c75062..ce156d56d096 100644 --- a/crates/rspack_loader_lightningcss/src/lib.rs +++ b/crates/rspack_loader_lightningcss/src/lib.rs @@ -47,7 +47,7 @@ impl LightningCssLoader { return Ok(()); }; - let filename = resource_path.to_string_lossy().into_owned(); + let filename = resource_path.as_str().to_string(); let Some(content) = std::mem::take(&mut loader_context.content) else { return Ok(()); diff --git a/crates/rspack_loader_runner/Cargo.toml b/crates/rspack_loader_runner/Cargo.toml index c3ca0c7a9e09..e1808f0cf53e 100644 --- a/crates/rspack_loader_runner/Cargo.toml +++ b/crates/rspack_loader_runner/Cargo.toml @@ -16,6 +16,7 @@ once_cell = { workspace = true } regex = { workspace = true } rspack_collections = { version = "0.1.0", path = "../rspack_collections" } rspack_error = { version = "0.1.0", path = "../rspack_error" } +rspack_paths = { version = "0.1.0", path = "../rspack_paths" } rspack_sources = { workspace = true } rspack_util = { version = "0.1.0", path = "../rspack_util" } serde_json = { workspace = true } diff --git a/crates/rspack_loader_runner/src/content.rs b/crates/rspack_loader_runner/src/content.rs index ae6343abcfc2..b71d3239b700 100644 --- a/crates/rspack_loader_runner/src/content.rs +++ b/crates/rspack_loader_runner/src/content.rs @@ -7,6 +7,7 @@ use std::{ use anymap::CloneAny; use once_cell::sync::OnceCell; use rspack_error::{Error, Result}; +use rspack_paths::Utf8PathBuf; use crate::{get_scheme, Scheme}; @@ -108,7 +109,7 @@ pub struct ResourceData { /// Resource with absolute path, query and fragment pub resource: String, /// Absolute resource path only - pub resource_path: Option, + pub resource_path: Option, /// Resource query with `?` prefix pub resource_query: Option, /// Resource fragment with `#` prefix @@ -145,17 +146,17 @@ impl ResourceData { self.resource = v; } - pub fn path(mut self, v: PathBuf) -> Self { - self.resource_path = Some(v); + pub fn path>(mut self, v: P) -> Self { + self.resource_path = Some(v.into()); self } - pub fn set_path(&mut self, v: PathBuf) { - self.resource_path = Some(v); + pub fn set_path>(&mut self, v: P) { + self.resource_path = Some(v.into()); } - pub fn set_path_optional(&mut self, v: Option) { - self.resource_path = v; + pub fn set_path_optional>(&mut self, v: Option

) { + self.resource_path = v.map(Into::into); } pub fn query(mut self, v: String) -> Self { diff --git a/crates/rspack_loader_runner/src/context.rs b/crates/rspack_loader_runner/src/context.rs index 0c53766844ac..162c25392995 100644 --- a/crates/rspack_loader_runner/src/context.rs +++ b/crates/rspack_loader_runner/src/context.rs @@ -1,10 +1,8 @@ -use std::{ - path::{Path, PathBuf}, - sync::Arc, -}; +use std::{path::PathBuf, sync::Arc}; use derivative::Derivative; use rspack_error::Diagnostic; +use rspack_paths::Utf8Path; use rspack_sources::SourceMap; use rustc_hash::FxHashSet as HashSet; @@ -104,7 +102,7 @@ impl LoaderContext { /// The resource part of the request. /// E.g. /abc/resource.js - pub fn resource_path(&self) -> Option<&Path> { + pub fn resource_path(&self) -> Option<&Utf8Path> { self.resource_data.resource_path.as_deref() } diff --git a/crates/rspack_loader_runner/src/loader.rs b/crates/rspack_loader_runner/src/loader.rs index 28c8e95e08d3..749d7de29c20 100644 --- a/crates/rspack_loader_runner/src/loader.rs +++ b/crates/rspack_loader_runner/src/loader.rs @@ -2,7 +2,6 @@ use std::sync::LazyLock; use std::{ fmt::Display, ops::Deref, - path::PathBuf, sync::{ atomic::{AtomicBool, Ordering}, Arc, @@ -14,6 +13,7 @@ use derivative::Derivative; use regex::Regex; use rspack_collections::{Identifiable, Identifier}; use rspack_error::Result; +use rspack_paths::Utf8PathBuf; use rspack_util::identifier::strip_zero_width_space_for_fragment; use super::LoaderContext; @@ -29,7 +29,7 @@ pub struct LoaderItem { /// The absolute path is used to represent a loader stayed on the JS side. /// `$` split chain may be used to represent a composed loader chain from the JS side. /// Virtual path with a builtin protocol to represent a loader from the native side. e.g "builtin:". - path: PathBuf, + path: Utf8PathBuf, /// Query of a loader, starts with `?` query: Option, /// Fragment of a loader, starts with `#`. @@ -201,7 +201,7 @@ impl From>> for LoaderItem { #[derive(Debug)] pub struct ResourceParsedData { - pub path: PathBuf, + pub path: Utf8PathBuf, pub query: Option, pub fragment: Option, } diff --git a/crates/rspack_loader_runner/src/runner.rs b/crates/rspack_loader_runner/src/runner.rs index e3706959d0ef..d096b30538d4 100644 --- a/crates/rspack_loader_runner/src/runner.rs +++ b/crates/rspack_loader_runner/src/runner.rs @@ -37,12 +37,11 @@ async fn process_resource( let resource_data = &loader_context.resource_data; if loader_context.content.is_none() { if let Some(resource_path) = resource_data.resource_path.as_deref() - && !resource_path.to_string_lossy().is_empty() + && !resource_path.as_str().is_empty() { - let result = tokio::fs::read(resource_path).await.map_err(|e| { - let r = resource_path.to_string_lossy().to_string(); - error!("{e}, failed to read {r}") - })?; + let result = tokio::fs::read(resource_path) + .await + .map_err(|e| error!("{e}, failed to read {resource_path}"))?; loader_context.content = Some(Content::from(result)); } else if !resource_data.get_scheme().is_none() { let resource = &resource_data.resource; @@ -69,7 +68,7 @@ async fn create_loader_context( if let Some(resource_path) = &resource_data.resource_path && resource_path.is_absolute() { - file_dependencies.insert(resource_path.clone()); + file_dependencies.insert(resource_path.clone().into_std_path_buf()); } let mut loader_context = LoaderContext { @@ -106,8 +105,8 @@ pub async fn run_loaders( additional_data: AdditionalData, ) -> Result> { let loaders = loaders - .iter() - .map(|i| i.clone().into()) + .into_iter() + .map(|i| i.into()) .collect::>>(); let mut cx = diff --git a/crates/rspack_loader_swc/src/compiler.rs b/crates/rspack_loader_swc/src/compiler.rs index 00d02c846b81..e8d2b18538a1 100644 --- a/crates/rspack_loader_swc/src/compiler.rs +++ b/crates/rspack_loader_swc/src/compiler.rs @@ -577,18 +577,6 @@ Help: } } } - - pub fn comments(&self) -> &SingleThreadedComments { - &self.comments - } - - pub fn options(&self) -> &Options { - &self.options - } - - pub fn cm(&self) -> &Arc { - &self.cm - } } pub(crate) trait IntoJsAst { diff --git a/crates/rspack_loader_swc/src/lib.rs b/crates/rspack_loader_swc/src/lib.rs index 98b7f3a96b5c..bb644f0d5385 100644 --- a/crates/rspack_loader_swc/src/lib.rs +++ b/crates/rspack_loader_swc/src/lib.rs @@ -69,8 +69,8 @@ impl SwcLoader { swc_options.config.input_source_map = Some(InputSourceMap::Str(source_map)) } } - swc_options.filename = resource_path.to_string_lossy().to_string(); - swc_options.source_file_name = Some(resource_path.to_string_lossy().to_string()); + swc_options.filename = resource_path.as_str().to_string(); + swc_options.source_file_name = Some(resource_path.as_str().to_string()); if swc_options.config.jsc.target.is_some() && swc_options.config.env.is_some() { loader_context.emit_diagnostic(Diagnostic::warn( @@ -87,30 +87,16 @@ impl SwcLoader { }; let source = content.try_into_string()?; - let c = SwcCompiler::new(resource_path.clone(), source.clone(), swc_options) - .map_err(AnyhowError::from)?; - - let rspack_options = &*loader_context.context.options; - let swc_options = c.options(); - let top_level_mark = swc_options - .top_level_mark - .expect("`top_level_mark` should be initialized"); - let unresolved_mark = swc_options - .unresolved_mark - .expect("`unresolved_mark` should be initialized"); + let c = SwcCompiler::new( + resource_path.into_std_path_buf(), + source.clone(), + swc_options, + ) + .map_err(AnyhowError::from)?; let built = c .parse(None, |_| { - transformer::transform( - &resource_path, - rspack_options, - Some(c.comments()), - top_level_mark, - unresolved_mark, - c.cm().clone(), - &source, - &self.options_with_additional.rspack_experiments, - ) + transformer::transform(&self.options_with_additional.rspack_experiments) }) .map_err(AnyhowError::from)?; diff --git a/crates/rspack_loader_swc/src/transformer.rs b/crates/rspack_loader_swc/src/transformer.rs index 4be627599b38..ea494ef8ddfd 100644 --- a/crates/rspack_loader_swc/src/transformer.rs +++ b/crates/rspack_loader_swc/src/transformer.rs @@ -1,12 +1,7 @@ -use std::path::Path; -use std::sync::Arc; - use either::Either; -use rspack_core::CompilerOptions; use swc_core::atoms::Atom; use swc_core::common::collections::AHashMap; use swc_core::common::BytePos; -use swc_core::common::{comments::Comments, Mark, SourceMap}; use swc_core::ecma::ast::Ident; use swc_core::ecma::visit::{noop_visit_type, Visit}; use swc_core::ecma::{transforms::base::pass::noop, visit::Fold}; @@ -32,16 +27,7 @@ macro_rules! either { } #[allow(clippy::too_many_arguments)] -pub(crate) fn transform<'a>( - _resource_path: &'a Path, - _rspack_options: &'a CompilerOptions, - _comments: Option<&'a dyn Comments>, - _top_level_mark: Mark, - _unresolved_mark: Mark, - _cm: Arc, - _content: &'a str, - rspack_experiments: &'a RspackExperiments, -) -> impl Fold + 'a { +pub(crate) fn transform(rspack_experiments: &RspackExperiments) -> impl Fold + '_ { either!(rspack_experiments.import, |options| { swc_plugin_import::plugin_import(options) }) diff --git a/crates/rspack_paths/Cargo.toml b/crates/rspack_paths/Cargo.toml new file mode 100644 index 000000000000..01f42114fe7d --- /dev/null +++ b/crates/rspack_paths/Cargo.toml @@ -0,0 +1,12 @@ +[package] +description = "rspack paths" +edition = "2021" +license = "MIT" +name = "rspack_paths" +repository = "https://github.com/web-infra-dev/rspack" +version = "0.1.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +camino = { workspace = true } diff --git a/crates/rspack_paths/LICENSE b/crates/rspack_paths/LICENSE new file mode 100644 index 000000000000..46310101ad8a --- /dev/null +++ b/crates/rspack_paths/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2022-present Bytedance, Inc. and its affiliates. + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/rspack_paths/src/lib.rs b/crates/rspack_paths/src/lib.rs new file mode 100644 index 000000000000..f51dfe749978 --- /dev/null +++ b/crates/rspack_paths/src/lib.rs @@ -0,0 +1,38 @@ +use std::path::{Path, PathBuf}; + +pub use camino::{Utf8Component, Utf8Components, Utf8Path, Utf8PathBuf, Utf8Prefix}; + +pub trait AssertUtf8 { + type Output; + fn assert_utf8(self) -> Self::Output; +} + +impl AssertUtf8 for PathBuf { + type Output = Utf8PathBuf; + + /// Assert `self` is a valid UTF-8 [`PathBuf`] and convert to [`Utf8PathBuf`] + /// + /// # Panics + /// + /// Panics if `self` is not a valid UTF-8 path. + fn assert_utf8(self) -> Self::Output { + Utf8PathBuf::from_path_buf(self).unwrap_or_else(|p| { + panic!("expected UTF-8 path, got: {}", p.display()); + }) + } +} + +impl<'a> AssertUtf8 for &'a Path { + type Output = &'a Utf8Path; + + /// Assert `self` is a valid UTF-8 [`Path`] and convert to [`Utf8Path`] + /// + /// # Panics + /// + /// Panics if `self` is not a valid UTF-8 path. + fn assert_utf8(self) -> Self::Output { + Utf8Path::from_path(self).unwrap_or_else(|| { + panic!("expected UTF-8 path, got: {}", self.display()); + }) + } +} diff --git a/crates/rspack_plugin_asset/src/lib.rs b/crates/rspack_plugin_asset/src/lib.rs index e4a8736e57c1..afff1deb780c 100644 --- a/crates/rspack_plugin_asset/src/lib.rs +++ b/crates/rspack_plugin_asset/src/lib.rs @@ -122,9 +122,9 @@ impl AssetParserAndGenerator { filename: resource_data .resource_path .as_deref() - .map(|p| p.to_string_lossy().to_string()) + .map(|p| p.as_str().to_string()) .unwrap_or_default(), - content: source.source().into_owned().to_string(), + content: source.source().into_owned(), }; if let Some(AssetGeneratorDataUrl::Func(data_url)) = data_url { diff --git a/crates/rspack_plugin_copy/Cargo.toml b/crates/rspack_plugin_copy/Cargo.toml index 71024793ccc6..a5aeb04cd6d1 100644 --- a/crates/rspack_plugin_copy/Cargo.toml +++ b/crates/rspack_plugin_copy/Cargo.toml @@ -11,13 +11,14 @@ derivative = { workspace = true } futures = { workspace = true } glob = { workspace = true } lazy_static = "1.4.0" -pathdiff = { workspace = true } +pathdiff = { workspace = true, features = ["camino"] } regex = { workspace = true } rspack_core = { version = "0.1.0", path = "../rspack_core" } rspack_error = { version = "0.1.0", path = "../rspack_error" } rspack_futures = { version = "0.1.0", path = "../rspack_futures" } rspack_hash = { version = "0.1.0", path = "../rspack_hash" } rspack_hook = { version = "0.1.0", path = "../rspack_hook" } +rspack_paths = { version = "0.1.0", path = "../rspack_paths" } rspack_util = { version = "0.1.0", path = "../rspack_util" } rustc-hash = { workspace = true } sugar_path = { workspace = true } diff --git a/crates/rspack_plugin_copy/src/lib.rs b/crates/rspack_plugin_copy/src/lib.rs index 1d140e438c5c..b2bb0dda770e 100644 --- a/crates/rspack_plugin_copy/src/lib.rs +++ b/crates/rspack_plugin_copy/src/lib.rs @@ -1,5 +1,6 @@ #![feature(let_chains)] use std::{ + borrow::Cow, fmt::Display, fs, hash::Hash, @@ -20,6 +21,7 @@ use rspack_core::{ use rspack_error::{Diagnostic, DiagnosticError, Error, ErrorExt, Result}; use rspack_hash::{HashDigest, HashFunction, HashSalt, RspackHash, RspackHashDigest}; use rspack_hook::{plugin, plugin_hook}; +use rspack_paths::{AssertUtf8, Utf8Path, Utf8PathBuf}; use rspack_util::infallible::ResultInfallibleExt as _; use sugar_path::SugarPath; @@ -77,8 +79,8 @@ pub enum Transformer { } pub struct ToFnCtx<'a> { - pub context: &'a str, - pub absolute_filename: Option<&'a str>, + pub context: &'a Utf8Path, + pub absolute_filename: &'a Utf8Path, } pub type ToFn = Box Fn(ToFnCtx<'a>) -> BoxFuture<'a, Result> + Sync + Send>; @@ -94,7 +96,7 @@ pub struct CopyPattern { pub from: String, #[derivative(Debug = "ignore")] pub to: Option, - pub context: Option, + pub context: Option, pub to_type: Option, pub no_error_on_missing: bool, pub info: Option, @@ -114,8 +116,8 @@ pub struct CopyGlobOptions { #[derive(Debug, Clone)] pub struct RunPatternResult { - pub source_filename: PathBuf, - pub absolute_filename: PathBuf, + pub source_filename: Utf8PathBuf, + pub absolute_filename: Utf8PathBuf, pub filename: String, pub source: RawSource, pub info: Option, @@ -159,10 +161,10 @@ impl CopyRspackPlugin { #[allow(clippy::too_many_arguments)] async fn analyze_every_entry( - entry: PathBuf, + entry: Utf8PathBuf, pattern: &CopyPattern, - context: &Path, - output_path: &Path, + context: &Utf8Path, + output_path: &Utf8Path, from_type: FromType, file_dependencies: &DashSet, diagnostics: &Mutex>, @@ -174,16 +176,14 @@ impl CopyRspackPlugin { return None; } if let Some(ignore) = &pattern.glob_options.ignore - && ignore - .iter() - .any(|ignore| ignore.matches(&entry.to_string_lossy())) + && ignore.iter().any(|ignore| ignore.matches(entry.as_str())) { return None; } - let from = entry.as_path().to_path_buf(); + let from = entry; - logger.debug(format!("found '{}'", from.display())); + logger.debug(format!("found '{from}'")); let absolute_filename = if from.is_absolute() { from.clone() @@ -195,29 +195,25 @@ impl CopyRspackPlugin { let to = match to { ToOption::String(s) => s.to_owned(), ToOption::Fn(r) => { - if let Some(context) = context.to_str() { - let to_result = r(ToFnCtx { - context, - absolute_filename: absolute_filename.to_str(), - }) - .await; - let to = match to_result { - Ok(to) => to, - Err(e) => { - diagnostics - .lock() - .expect("failed to obtain lock of `diagnostics`") - .push(Diagnostic::error( - "Run copy to fn error".into(), - e.to_string(), - )); - "".to_string() - } - }; - to - } else { - "".to_string() - } + let to_result = r(ToFnCtx { + context, + absolute_filename: &absolute_filename, + }) + .await; + let to = match to_result { + Ok(to) => to, + Err(e) => { + diagnostics + .lock() + .expect("failed to obtain lock of `diagnostics`") + .push(Diagnostic::error( + "Run copy to fn error".into(), + e.to_string(), + )); + "".to_string() + } + }; + to } }; @@ -242,10 +238,10 @@ impl CopyRspackPlugin { logger.log(format!("'to' option '{to}' determined as '{to_type}'")); - let relative = pathdiff::diff_paths(&absolute_filename, context); + let relative = pathdiff::diff_utf8_paths(&absolute_filename, context); let filename = if matches!(to_type, ToType::Dir) { if let Some(relative) = &relative { - PathBuf::from(&to).join(relative) + Utf8PathBuf::from(&to).join(relative) } else { to.into() } @@ -254,37 +250,32 @@ impl CopyRspackPlugin { }; let filename = if filename.is_absolute() { - pathdiff::diff_paths(filename, output_path)? + pathdiff::diff_utf8_paths(filename, output_path)? } else { filename }; logger.log(format!( - "determined that '{}' should write to '{}'", - from.display(), - filename.display() + "determined that '{from}' should write to '{filename}'" )); let source_filename = relative?; // If this came from a glob or dir, add it to the file dependencies if matches!(from_type, FromType::Dir | FromType::Glob) { - logger.debug(format!( - "added '{}' as a file dependency", - absolute_filename.display() - )); + logger.debug(format!("added '{absolute_filename}' as a file dependency",)); - file_dependencies.insert(absolute_filename.clone()); + file_dependencies.insert(absolute_filename.clone().into_std_path_buf()); } // TODO cache - logger.debug(format!("reading '{}'...", absolute_filename.display())); + logger.debug(format!("reading '{}'...", absolute_filename)); // TODO inputFileSystem let source_vec = match tokio::fs::read(absolute_filename.clone()).await { Ok(data) => { - logger.debug(format!("read '{}'...", absolute_filename.display())); + logger.debug(format!("read '{}'...", absolute_filename)); data } @@ -302,25 +293,23 @@ impl CopyRspackPlugin { let mut source = RawSource::Buffer(source_vec.clone()); if let Some(transform) = &pattern.transform { - if let Some(absolute_filename) = absolute_filename.to_str() { - match transform { - Transformer::Fn(transformer) => { - let transformed = transformer(source_vec, absolute_filename).await; - match transformed { - Ok(code) => { - source = code; - } - Err(e) => { - diagnostics - .lock() - .expect("failed to obtain lock of `diagnostics`") - .push(Diagnostic::error( - "Run copy transform fn error".into(), - e.to_string(), - )); - } - }; - } + match transform { + Transformer::Fn(transformer) => { + let transformed = transformer(source_vec, absolute_filename.as_str()).await; + match transformed { + Ok(code) => { + source = code; + } + Err(e) => { + diagnostics + .lock() + .expect("failed to obtain lock of `diagnostics`") + .push(Diagnostic::error( + "Run copy transform fn error".into(), + e.to_string(), + )); + } + }; } } } @@ -328,8 +317,7 @@ impl CopyRspackPlugin { let filename = if matches!(&to_type, ToType::Template) { logger.log(format!( "interpolating template '{}' for '${}'...`", - filename.display(), - source_filename.display() + filename, source_filename )); let content_hash = Self::get_content_hash( @@ -341,9 +329,9 @@ impl CopyRspackPlugin { let content_hash = content_hash.rendered(compilation.options.output.hash_digest_length); let template_str = compilation .get_asset_path( - &FilenameTemplate::from(filename.to_string_lossy().to_string()), + &FilenameTemplate::from(filename.to_string()), PathData::default() - .filename(&source_filename.to_string_lossy()) + .filename(source_filename.as_str()) .content_hash(content_hash) .hash_optional(compilation.get_hash()), ) @@ -351,12 +339,12 @@ impl CopyRspackPlugin { logger.log(format!( "interpolated template '{template_str}' for '{}'", - filename.display() + filename )); template_str } else { - filename.normalize().to_string_lossy().to_string() + filename.as_str().normalize().to_string_lossy().to_string() }; Some(RunPatternResult { @@ -380,52 +368,47 @@ impl CopyRspackPlugin { logger: &CompilationLogger, ) -> Option>> { let orig_from = &pattern.from; - let normalized_orig_from = PathBuf::from(orig_from); + let normalized_orig_from = Utf8PathBuf::from(orig_from); let pattern_context = if pattern.context.is_none() { - Some(compilation.options.context.as_path().into()) - } else if let Some(ctx) = pattern.context.clone() + Some(Cow::Borrowed(compilation.options.context.as_path())) + } else if let Some(ref ctx) = pattern.context && !ctx.is_absolute() { - Some(compilation.options.context.as_path().join(ctx)) + Some(Cow::Owned(compilation.options.context.as_path().join(ctx))) } else { - pattern.context.clone() + pattern.context.as_deref().map(Into::into) }; - let mut context = pattern_context - .clone() - .unwrap_or(compilation.options.context.as_path().to_path_buf()); - logger.log(format!( "starting to process a pattern from '{}' using '{:?}' context", - normalized_orig_from.display(), - pattern_context.as_ref().map(|p| p.display()) + normalized_orig_from, pattern_context )); + let mut context = + pattern_context.unwrap_or_else(|| Cow::Borrowed(compilation.options.context.as_path())); + let abs_from = if normalized_orig_from.is_absolute() { normalized_orig_from } else { context.join(&normalized_orig_from) }; - logger.debug(format!("getting stats for '{}'...", abs_from.display())); + logger.debug(format!("getting stats for '{}'...", abs_from)); let from_type = if let Ok(meta) = fs::metadata(&abs_from) { if meta.is_dir() { - logger.debug(format!( - "determined '{}' is a directory", - abs_from.display() - )); + logger.debug(format!("determined '{}' is a directory", abs_from)); FromType::Dir } else if meta.is_file() { - logger.debug(format!("determined '{}' is a file", abs_from.display())); + logger.debug(format!("determined '{}' is a file", abs_from)); FromType::File } else { - logger.debug(format!("determined '{}' is a unknown", abs_from.display())); + logger.debug(format!("determined '{}' is a unknown", abs_from)); FromType::Glob } } else { - logger.debug(format!("determined '{}' is a glob", abs_from.display())); + logger.debug(format!("determined '{}' is a glob", abs_from)); FromType::Glob }; @@ -439,44 +422,35 @@ impl CopyRspackPlugin { let mut need_add_context_to_dependency = false; let glob_query = match from_type { FromType::Dir => { - logger.debug(format!( - "added '{}' as a context dependency", - abs_from.display() - )); - context_dependencies.insert(abs_from.clone()); - context = abs_from.clone(); + logger.debug(format!("added '{}' as a context dependency", abs_from)); + context_dependencies.insert(abs_from.clone().into_std_path_buf()); + context = abs_from.as_path().into(); if dot_enable.is_none() { dot_enable = Some(true); } - let mut escaped = PathBuf::from(escape_glob_chars(abs_from.to_str()?)); + let mut escaped = Utf8PathBuf::from(escape_glob_chars(abs_from.as_str())); escaped.push("**/*"); - escaped.to_string_lossy().to_string() + escaped.as_str().to_string() } FromType::File => { - logger.debug(format!( - "added '{}' as a file dependency", - abs_from.display() - )); - file_dependencies.insert(abs_from.clone()); - context = abs_from - .parent() - .map(|p| p.to_path_buf()) - .unwrap_or(PathBuf::new()); + logger.debug(format!("added '{}' as a file dependency", abs_from)); + file_dependencies.insert(abs_from.clone().into_std_path_buf()); + context = abs_from.parent().unwrap_or(Utf8Path::new("")).into(); if dot_enable.is_none() { dot_enable = Some(true); } - escape_glob_chars(abs_from.to_str()?) + escape_glob_chars(abs_from.as_str()) } FromType::Glob => { need_add_context_to_dependency = true; if Path::new(orig_from).is_absolute() { orig_from.into() } else { - context.join(orig_from).to_string_lossy().to_string() + context.join(orig_from).as_str().to_string() } } }; @@ -496,15 +470,13 @@ impl CopyRspackPlugin { Ok(entries) => { let entries: Vec<_> = entries .filter_map(|entry| { - let entry = entry.ok()?; + let entry = entry.ok()?.assert_utf8(); let filters = pattern.glob_options.ignore.as_ref(); if let Some(filters) = filters { // If filters length is 0, exist is true by default - let exist = filters - .iter() - .all(|filter| !filter.matches(&entry.to_string_lossy())); + let exist = filters.iter().all(|filter| !filter.matches(entry.as_str())); exist.then_some(entry) } else { Some(entry) @@ -517,7 +489,7 @@ impl CopyRspackPlugin { &entries.iter().map(|it| it.as_path()).collect::>(), ) { - context_dependencies.insert(common_dir); + context_dependencies.insert(common_dir.into_std_path_buf()); } if entries.is_empty() { @@ -588,8 +560,8 @@ impl CopyRspackPlugin { logger.log(format!( "finished to process a pattern from '{}' using '{}' context to '{:?}'", - PathBuf::from(orig_from).display(), - context.display(), + Utf8PathBuf::from(orig_from), + context, to, )); @@ -701,14 +673,14 @@ impl Plugin for CopyRspackPlugin { } } -fn get_closest_common_parent_dir(paths: &[&Path]) -> Option { +fn get_closest_common_parent_dir(paths: &[&Utf8Path]) -> Option { // If there are no matching files, return `None`. if paths.is_empty() { return None; } // Get the first file path and use it as the initial value for the common parent directory. - let mut parent_dir: PathBuf = paths[0].parent()?.into(); + let mut parent_dir: Utf8PathBuf = paths[0].parent()?.to_path_buf(); // Iterate over the remaining file paths, updating the common parent directory as necessary. for path in paths.iter().skip(1) { diff --git a/crates/rspack_plugin_css/src/parser_and_generator/mod.rs b/crates/rspack_plugin_css/src/parser_and_generator/mod.rs index cd2277f1663d..1a000dd5233d 100644 --- a/crates/rspack_plugin_css/src/parser_and_generator/mod.rs +++ b/crates/rspack_plugin_css/src/parser_and_generator/mod.rs @@ -125,7 +125,7 @@ impl ParserAndGenerator for CssParserAndGenerator { ModuleType::CssModule => css_module_lexer::Mode::Local, ModuleType::CssAuto if let Some(resource_path) = resource_path - && REGEX_IS_MODULES.is_match(resource_path.to_string_lossy().as_ref()) => + && REGEX_IS_MODULES.is_match(resource_path.as_str()) => { css_module_lexer::Mode::Local } diff --git a/crates/rspack_plugin_css/src/plugin/impl_plugin_for_css_plugin.rs b/crates/rspack_plugin_css/src/plugin/impl_plugin_for_css_plugin.rs index e8d35cae8b71..333baf91f61d 100644 --- a/crates/rspack_plugin_css/src/plugin/impl_plugin_for_css_plugin.rs +++ b/crates/rspack_plugin_css/src/plugin/impl_plugin_for_css_plugin.rs @@ -1,7 +1,6 @@ #![allow(clippy::comparison_chain)] use std::hash::Hash; -use std::path::PathBuf; use async_trait::async_trait; use rayon::prelude::*; @@ -382,7 +381,7 @@ async fn render_manifest( selected_module.readable_identifier(&compilation.options.context) ), ) - .with_file(Some(PathBuf::from(&output_path))) + .with_file(Some(output_path.to_owned().into())) .with_chunk(Some(chunk_ukey.as_u32())) })); } diff --git a/crates/rspack_plugin_extract_css/src/plugin.rs b/crates/rspack_plugin_extract_css/src/plugin.rs index 6ddcf995ff4a..85a7598a238d 100644 --- a/crates/rspack_plugin_extract_css/src/plugin.rs +++ b/crates/rspack_plugin_extract_css/src/plugin.rs @@ -1,5 +1,5 @@ use std::sync::LazyLock; -use std::{borrow::Cow, cmp::max, hash::Hash, path::PathBuf, sync::Arc}; +use std::{borrow::Cow, cmp::max, hash::Hash, sync::Arc}; use regex::Regex; use rspack_collections::{IdentifierMap, IdentifierSet, UkeySet}; @@ -378,16 +378,7 @@ impl PluginCssExtract { source.add(RawSource::from(format!("@layer {} {{\n", layer))); } - let undo_path = get_undo_path( - &filename, - compilation - .options - .output - .path - .to_str() - .expect("should have output.path"), - false, - ); + let undo_path = get_undo_path(&filename, compilation.options.output.path.as_str(), false); let content = ABSOLUTE_PUBLIC_PATH_RE.replace_all(&content, ""); let content = SINGLE_DOT_PATH_SEGMENT_RE.replace_all(&content, "."); @@ -655,7 +646,7 @@ despite it was not able to fulfill desired ordering with these modules:\n{}", .join("\n") ), ) - .with_file(Some(PathBuf::from(render_result.filename()))) + .with_file(Some(render_result.filename().to_owned().into())) .with_chunk(Some(chunk_ukey.as_u32())) })); } diff --git a/crates/rspack_plugin_html/Cargo.toml b/crates/rspack_plugin_html/Cargo.toml index df8347be8f2f..2be7d52ddff2 100644 --- a/crates/rspack_plugin_html/Cargo.toml +++ b/crates/rspack_plugin_html/Cargo.toml @@ -20,6 +20,7 @@ rspack_base64 = { version = "0.1.0", path = "../rspack_base64" } rspack_core = { version = "0.1.0", path = "../rspack_core" } rspack_error = { version = "0.1.0", path = "../rspack_error" } rspack_hook = { version = "0.1.0", path = "../rspack_hook" } +rspack_paths = { version = "0.1.0", path = "../rspack_paths" } rspack_util = { version = "0.1.0", path = "../rspack_util" } schemars = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } diff --git a/crates/rspack_plugin_html/src/plugin.rs b/crates/rspack_plugin_html/src/plugin.rs index f70512bdfe38..63038137d774 100644 --- a/crates/rspack_plugin_html/src/plugin.rs +++ b/crates/rspack_plugin_html/src/plugin.rs @@ -18,6 +18,7 @@ use rspack_core::{ }; use rspack_error::{miette, AnyhowError, Diagnostic, Result}; use rspack_hook::{plugin, plugin_hook}; +use rspack_paths::AssertUtf8; use rspack_util::infallible::ResultInfallibleExt as _; use swc_html::visit::VisitMutWith; @@ -63,8 +64,13 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { } else if let Some(template) = &config.template { // TODO: support loader query form let resolved_template = path_clean::clean( - AsRef::::as_ref(&compilation.options.context).join(template.as_str()), - ); + compilation + .options + .context + .as_path() + .join(template.as_str()), + ) + .assert_utf8(); let content = fs::read_to_string(&resolved_template) .context(format!( @@ -75,8 +81,10 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { match content { Ok(content) => { - let url = resolved_template.to_string_lossy().to_string(); - compilation.file_dependencies.insert(resolved_template); + let url = resolved_template.as_str().to_string(); + compilation + .file_dependencies + .insert(resolved_template.into_std_path_buf()); (content, url, template.clone()) } @@ -223,7 +231,7 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { let fake_html_file_name = compilation .get_path( &html_file_name, - PathData::default().filename(&output_path.to_string_lossy()), + PathData::default().filename(output_path.as_str()), ) .always_ok(); @@ -290,7 +298,7 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { .get_path_with_info( &html_file_name, PathData::default() - .filename(&output_path.to_string_lossy()) + .filename(output_path.as_str()) .content_hash(&hash), ) .always_ok(); diff --git a/crates/rspack_plugin_html/src/visitors/asset.rs b/crates/rspack_plugin_html/src/visitors/asset.rs index 15317046ac75..f80b3b927814 100644 --- a/crates/rspack_plugin_html/src/visitors/asset.rs +++ b/crates/rspack_plugin_html/src/visitors/asset.rs @@ -231,6 +231,7 @@ impl VisitMut for AssetWriter<'_, '_> { .options .output .path + .as_std_path() .join(favicon_relative_path) .relative(PathBuf::from(self.html_path).join("..")); } else { diff --git a/crates/rspack_plugin_javascript/Cargo.toml b/crates/rspack_plugin_javascript/Cargo.toml index b6ba27d4ca22..06de1a9da6c9 100644 --- a/crates/rspack_plugin_javascript/Cargo.toml +++ b/crates/rspack_plugin_javascript/Cargo.toml @@ -27,6 +27,7 @@ rspack_error = { version = "0.1.0", path = "../rspack_error" } rspack_hash = { version = "0.1.0", path = "../rspack_hash" } rspack_hook = { version = "0.1.0", path = "../rspack_hook" } rspack_ids = { version = "0.1.0", path = "../rspack_ids/" } +rspack_paths = { version = "0.1.0", path = "../rspack_paths" } rspack_regex = { version = "0.1.0", path = "../rspack_regex" } rspack_util = { version = "0.1.0", path = "../rspack_util" } rustc-hash = { workspace = true } diff --git a/crates/rspack_plugin_javascript/src/parser_and_generator/mod.rs b/crates/rspack_plugin_javascript/src/parser_and_generator/mod.rs index fd7c591a5a7f..cb59d43c6878 100644 --- a/crates/rspack_plugin_javascript/src/parser_and_generator/mod.rs +++ b/crates/rspack_plugin_javascript/src/parser_and_generator/mod.rs @@ -134,7 +134,7 @@ impl ParserAndGenerator for JavaScriptParserAndGenerator { resource_data .resource_path .as_ref() - .map(|p| p.to_string_lossy().to_string()) + .map(|p| p.as_str().to_string()) .unwrap_or_default(), )), source.source().to_string(), diff --git a/crates/rspack_plugin_javascript/src/parser_plugin/node_stuff_plugin.rs b/crates/rspack_plugin_javascript/src/parser_plugin/node_stuff_plugin.rs index f197a4e9f134..9ef8aa6731cb 100644 --- a/crates/rspack_plugin_javascript/src/parser_plugin/node_stuff_plugin.rs +++ b/crates/rspack_plugin_javascript/src/parser_plugin/node_stuff_plugin.rs @@ -70,6 +70,7 @@ impl JavascriptParserPlugin for NodeStuffPlugin { .resource_path .as_deref()? .parent()? + .as_std_path() .relative(&parser.compiler_options.context) .to_string_lossy() .to_string(), @@ -123,6 +124,7 @@ impl JavascriptParserPlugin for NodeStuffPlugin { .resource_data .resource_path .as_deref()? + .as_std_path() .relative(&parser.compiler_options.context) .to_string_lossy() .to_string(), diff --git a/crates/rspack_plugin_javascript/src/plugin/side_effects_flag_plugin.rs b/crates/rspack_plugin_javascript/src/plugin/side_effects_flag_plugin.rs index c0c35dedabd8..e5216df33273 100644 --- a/crates/rspack_plugin_javascript/src/plugin/side_effects_flag_plugin.rs +++ b/crates/rspack_plugin_javascript/src/plugin/side_effects_flag_plugin.rs @@ -1,6 +1,5 @@ use std::collections::VecDeque; use std::fmt::Debug; -use std::path::PathBuf; use std::sync::Arc; use std::sync::LazyLock; @@ -12,6 +11,8 @@ use rspack_core::{ }; use rspack_error::Result; use rspack_hook::{plugin, plugin_hook}; +use rspack_paths::AssertUtf8; +use rspack_paths::Utf8Path; use sugar_path::SugarPath; use swc_core::common::comments::Comments; // use rspack_core::Plugin; @@ -56,15 +57,13 @@ impl SideEffects { } } -fn get_side_effects_from_package_json(side_effects: SideEffects, relative_path: PathBuf) -> bool { +fn get_side_effects_from_package_json(side_effects: SideEffects, relative_path: &Utf8Path) -> bool { match side_effects { SideEffects::Bool(s) => s, - SideEffects::String(s) => { - glob_match_with_normalized_pattern(&s, &relative_path.to_string_lossy()) - } + SideEffects::String(s) => glob_match_with_normalized_pattern(&s, relative_path.as_str()), SideEffects::Array(patterns) => patterns .iter() - .any(|pattern| glob_match_with_normalized_pattern(pattern, &relative_path.to_string_lossy())), + .any(|pattern| glob_match_with_normalized_pattern(pattern, relative_path.as_str())), } } @@ -644,8 +643,11 @@ async fn nmf_module( let Some(side_effects) = SideEffects::from_description(description.json()) else { return Ok(()); }; - let relative_path = resource_path.relative(package_path); - let has_side_effects = get_side_effects_from_package_json(side_effects, relative_path); + let relative_path = resource_path + .as_std_path() + .relative(package_path) + .assert_utf8(); + let has_side_effects = get_side_effects_from_package_json(side_effects, relative_path.as_path()); module.set_factory_meta(FactoryMeta { side_effect_free: Some(!has_side_effects), }); @@ -833,7 +835,7 @@ mod test_side_effects { relative_path: &str, ) -> bool { assert!(!side_effects_config.is_empty()); - let relative_path = PathBuf::from(relative_path); + let relative_path = Utf8Path::new(relative_path); let side_effects = if side_effects_config.len() > 1 { SideEffects::Array( side_effects_config diff --git a/crates/rspack_plugin_javascript/src/visitors/dependency/context_dependency_helper.rs b/crates/rspack_plugin_javascript/src/visitors/dependency/context_dependency_helper.rs index 50a3b1fcc0f6..4c3966643c45 100644 --- a/crates/rspack_plugin_javascript/src/visitors/dependency/context_dependency_helper.rs +++ b/crates/rspack_plugin_javascript/src/visitors/dependency/context_dependency_helper.rs @@ -35,7 +35,7 @@ pub fn create_context_dependency( let (context, prefix) = split_context_from_prefix(prefix_raw.to_string()); let (postfix, query, fragment) = match parse_resource(&postfix_raw) { Some(data) => ( - data.path.to_string_lossy().to_string(), + data.path.as_str().to_string(), data.query.unwrap_or_default(), data.fragment.unwrap_or_default(), ), @@ -135,7 +135,7 @@ pub fn create_context_dependency( let (context, prefix) = split_context_from_prefix(prefix_raw.to_string()); let (postfix, query, fragment) = match parse_resource(&postfix_raw) { Some(data) => ( - data.path.to_string_lossy().to_string(), + data.path.as_str().to_string(), data.query.unwrap_or_default(), data.fragment.unwrap_or_default(), ), diff --git a/crates/rspack_plugin_lazy_compilation/src/module.rs b/crates/rspack_plugin_lazy_compilation/src/module.rs index 074ee51790e3..94f58eec537b 100644 --- a/crates/rspack_plugin_lazy_compilation/src/module.rs +++ b/crates/rspack_plugin_lazy_compilation/src/module.rs @@ -1,4 +1,4 @@ -use std::{path::PathBuf, sync::Arc}; +use std::sync::Arc; use rspack_collections::Identifiable; use rspack_core::{ @@ -156,7 +156,7 @@ impl Module for LazyCompilationProxyModule { let mut files = FxHashSet::default(); files.extend(self.create_data.file_dependencies.clone()); - files.insert(PathBuf::from(&self.resource)); + files.insert(self.resource.to_owned().into()); Ok(BuildResult { build_info: BuildInfo { diff --git a/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs b/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs index 5e9270e401bd..642a592a7d85 100644 --- a/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs +++ b/crates/rspack_plugin_mf/src/sharing/consume_shared_plugin.rs @@ -79,8 +79,10 @@ fn resolve_matched_configs( compilation.push_diagnostic(error!("Can't resolve shared module {request}").into()); continue; }; - resolved.insert(resource.path.to_string_lossy().into_owned(), config.clone()); - compilation.file_dependencies.insert(resource.path); + resolved.insert(resource.path.as_str().to_string(), config.clone()); + compilation + .file_dependencies + .insert(resource.path.into_std_path_buf()); } else if ABSOLUTE_REQUEST.is_match(request) { resolved.insert(request.to_owned(), config.clone()); } else if request.ends_with('/') { @@ -282,7 +284,7 @@ impl ConsumeSharedPlugin { .ok() }) .and_then(|i| match i { - ResolveResult::Resource(r) => Some(r.path.to_string_lossy().into_owned()), + ResolveResult::Resource(r) => Some(r.path.as_str().to_string()), ResolveResult::Ignored => None, }); let required_version = self diff --git a/crates/rspack_plugin_runtime/src/runtime_module/auto_public_path.rs b/crates/rspack_plugin_runtime/src/runtime_module/auto_public_path.rs index 9b39b3b93913..c18ca4be8666 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/auto_public_path.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/auto_public_path.rs @@ -62,7 +62,7 @@ impl RuntimeModule for AutoPublicPathRuntimeModule { } fn auto_public_path_template(filename: &str, output: &OutputOptions) -> String { - let output_path = output.path.display().to_string(); + let output_path = output.path.as_str().to_string(); let undo_path = get_undo_path(filename, output_path, false); let assign = if undo_path.is_empty() { format!("{} = scriptUrl", RuntimeGlobals::PUBLIC_PATH) diff --git a/crates/rspack_plugin_runtime/src/runtime_module/utils.rs b/crates/rspack_plugin_runtime/src/runtime_module/utils.rs index 727f4600db3a..b24c0e374b23 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/utils.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/utils.rs @@ -137,7 +137,7 @@ pub fn get_output_dir( )?; Ok(get_undo_path( output_dir.as_str(), - compilation.options.output.path.display().to_string(), + compilation.options.output.path.as_str().to_string(), enforce_relative, )) } diff --git a/crates/rspack_plugin_schemes/Cargo.toml b/crates/rspack_plugin_schemes/Cargo.toml index d345ad6171d1..a132695b0aed 100644 --- a/crates/rspack_plugin_schemes/Cargo.toml +++ b/crates/rspack_plugin_schemes/Cargo.toml @@ -15,6 +15,7 @@ rspack_base64 = { version = "0.1.0", path = "../rspack_base64" } rspack_core = { version = "0.1.0", path = "../rspack_core" } rspack_error = { version = "0.1.0", path = "../rspack_error" } rspack_hook = { version = "0.1.0", path = "../rspack_hook" } +rspack_paths = { version = "0.1.0", path = "../rspack_paths" } tracing = { workspace = true } url = { workspace = true } urlencoding = { workspace = true } diff --git a/crates/rspack_plugin_schemes/src/file_uri.rs b/crates/rspack_plugin_schemes/src/file_uri.rs index 3160459a549e..2083a790433c 100644 --- a/crates/rspack_plugin_schemes/src/file_uri.rs +++ b/crates/rspack_plugin_schemes/src/file_uri.rs @@ -4,6 +4,7 @@ use rspack_core::{ }; use rspack_error::{error, Result}; use rspack_hook::{plugin, plugin_hook}; +use rspack_paths::AssertUtf8; use url::Url; #[plugin] @@ -21,12 +22,13 @@ async fn normal_module_factory_resolve_for_scheme( let url = Url::parse(&resource_data.resource).map_err(|e| error!(e.to_string()))?; let path = url .to_file_path() - .map_err(|_| error!("Failed to get file path of {url}"))?; + .map_err(|_| error!("Failed to get file path of {url}"))? + .assert_utf8(); let query = url.query().map(|q| format!("?{q}")); let fragment = url.fragment().map(|f| format!("#{f}")); let new_resource_data = ResourceData::new(format!( "{}{}{}", - path.to_string_lossy(), + path, query.as_deref().unwrap_or(""), fragment.as_deref().unwrap_or("") ))