Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ rayon = { version = "1.12.0", default-features = false }
regex = { version = "1.12.3", default-features = false, features = ["perf"] }
regex-syntax = { version = "0.8.10", default-features = false, features = ["std"] }
regress = { version = "0.10.5", default-features = false, features = ["pattern"] }
rspack_resolver = { features = ["package_json_raw_json_api", "yarn_pnp"], version = "=0.8.0", default-features = false }
rspack_resolver = { git = "https://github.com/rstackjs/rspack-resolver", branch = "codex/prehash-path-dependencies", features = ["package_json_raw_json_api", "yarn_pnp"], default-features = false }
rspack_sources = { version = "=0.4.22", default-features = false, features = ["rspack_cacheable"] }
rustc-hash = { version = "2.1.2", default-features = false }
ryu-js = { version = "1.0.2", default-features = false }
Expand Down
16 changes: 11 additions & 5 deletions crates/rspack_core/src/resolver/resolver_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{
use rspack_error::{Error, Severity, cyan, yellow};
use rspack_fs::ReadableFileSystem;
use rspack_loader_runner::DescriptionData;
use rspack_paths::{ArcPathSet, AssertUtf8};
use rspack_paths::{ArcPath, ArcPathSet, AssertUtf8};
use rspack_util::location::byte_line_column_to_offset;

use super::{ResolveResult, Resource, boxfs::BoxFS};
Expand Down Expand Up @@ -167,20 +167,26 @@ impl Resolver {
ResolveDependencies,
) {
let resolver = &self.resolver;
let mut context = Default::default();
let mut context = rspack_resolver::ResolvePreHashedContext::default();
let result = resolver
.resolve_with_context(path, request, &mut context)
.resolve_with_prehashed_context(path, request, &mut context)
.await;
let dependencies = ResolveDependencies {
file_dependencies: context
.file_dependencies
.into_iter()
.map(Into::into)
.map(|dependency| {
let hash = dependency.precomputed_hash();
ArcPath::from_path_buf_with_hash(dependency.into_path_buf(), hash)
})
.collect(),
missing_dependencies: context
.missing_dependencies
.into_iter()
.map(Into::into)
.map(|dependency| {
let hash = dependency.precomputed_hash();
ArcPath::from_path_buf_with_hash(dependency.into_path_buf(), hash)
})
.collect(),
};
let result = match result {
Expand Down
32 changes: 20 additions & 12 deletions crates/rspack_core/src/utils/module_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ pub async fn module_rules_matcher<'a>(
matched_rules: &mut Vec<&'a ModuleRuleEffect>,
) -> Result<()> {
let matched_rules_len = matched_rules.len();
let resource_path = resource_data
.path()
.unwrap_or_else(|| Utf8Path::new(""))
.as_str();
if let Some(result) = module_rules_matcher_sync(
rules,
resource_data,
resource_path,
issuer,
issuer_layer,
dependency,
Expand All @@ -33,6 +38,7 @@ pub async fn module_rules_matcher<'a>(
module_rules_matcher_async(
rules,
resource_data,
resource_path,
issuer,
issuer_layer,
dependency,
Expand All @@ -45,6 +51,7 @@ pub async fn module_rules_matcher<'a>(
fn module_rules_matcher_sync<'a>(
rules: &'a [ModuleRule],
resource_data: &ResourceData,
resource_path: &str,
issuer: Option<&'a str>,
issuer_layer: Option<&'a str>,
dependency: &DependencyCategory,
Expand All @@ -55,6 +62,7 @@ fn module_rules_matcher_sync<'a>(
match module_rule_matcher_sync(
rule,
resource_data,
resource_path,
issuer,
issuer_layer,
dependency,
Expand All @@ -72,6 +80,7 @@ fn module_rules_matcher_sync<'a>(
async fn module_rules_matcher_async<'a>(
rules: &'a [ModuleRule],
resource_data: &ResourceData,
resource_path: &str,
issuer: Option<&'a str>,
issuer_layer: Option<&'a str>,
dependency: &DependencyCategory,
Expand All @@ -82,6 +91,7 @@ async fn module_rules_matcher_async<'a>(
module_rule_matcher_async(
rule,
resource_data,
resource_path,
issuer,
issuer_layer,
dependency,
Expand Down Expand Up @@ -146,9 +156,14 @@ pub async fn module_rule_matcher<'a>(
matched_rules: &mut Vec<&'a ModuleRuleEffect>,
) -> Result<bool> {
let matched_rules_len = matched_rules.len();
let resource_path = resource_data
.path()
.unwrap_or_else(|| Utf8Path::new(""))
.as_str();
if let Some(result) = module_rule_matcher_sync(
module_rule,
resource_data,
resource_path,
issuer,
issuer_layer,
dependency,
Expand All @@ -161,6 +176,7 @@ pub async fn module_rule_matcher<'a>(
module_rule_matcher_async(
module_rule,
resource_data,
resource_path,
issuer,
issuer_layer,
dependency,
Expand All @@ -173,6 +189,7 @@ pub async fn module_rule_matcher<'a>(
fn module_rule_matcher_sync<'a>(
module_rule: &'a ModuleRule,
resource_data: &ResourceData,
resource_path: &str,
issuer: Option<&'a str>,
issuer_layer: Option<&'a str>,
dependency: &DependencyCategory,
Expand All @@ -183,11 +200,6 @@ fn module_rule_matcher_sync<'a>(
ensure_sync_matched!(test_rule.try_match_sync(resource_data.resource().into()));
}

let resource_path = resource_data
.path()
.unwrap_or_else(|| Utf8Path::new(""))
.as_str();

if let Some(test_rule) = &module_rule.test {
ensure_sync_matched!(test_rule.try_match_sync(resource_path.into()));
} else if let Some(resource_rule) = &module_rule.resource {
Expand Down Expand Up @@ -284,6 +296,7 @@ fn module_rule_matcher_sync<'a>(
match module_rules_matcher_sync(
rules,
resource_data,
resource_path,
issuer,
issuer_layer,
dependency,
Expand All @@ -302,6 +315,7 @@ fn module_rule_matcher_sync<'a>(
match module_rule_matcher_sync(
rule,
resource_data,
resource_path,
issuer,
issuer_layer,
dependency,
Expand Down Expand Up @@ -329,6 +343,7 @@ fn module_rule_matcher_sync<'a>(
async fn module_rule_matcher_async<'a>(
module_rule: &'a ModuleRule,
resource_data: &ResourceData,
resource_path: &str,
issuer: Option<&'a str>,
issuer_layer: Option<&'a str>,
dependency: &DependencyCategory,
Expand All @@ -341,13 +356,6 @@ async fn module_rule_matcher_async<'a>(
return Ok(false);
}

// 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
.path()
.unwrap_or_else(|| Utf8Path::new(""))
.as_str();

if let Some(test_rule) = &module_rule.test
&& !test_rule.try_match(resource_path.into()).await?
{
Expand Down
32 changes: 29 additions & 3 deletions crates/rspack_paths/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ impl<'a> AssertUtf8 for &'a Path {
}

#[cacheable(with=Custom)]
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone)]
pub struct ArcPath {
path: Arc<Path>,
// Pre-calculating and caching the hash value upon creation, making hashing operations
Expand All @@ -69,14 +69,40 @@ impl Debug for ArcPath {
}

impl ArcPath {
#[inline]
pub fn new(path: Arc<Path>) -> Self {
let hash = Self::hash_path(path.as_ref());
Self { path, hash }
}

#[inline]
pub fn from_path_buf_with_hash(path: PathBuf, hash: u64) -> Self {
Self::new_with_hash(path.into(), hash)
}

#[inline]
fn new_with_hash(path: Arc<Path>, hash: u64) -> Self {
debug_assert_eq!(hash, Self::hash_path(path.as_ref()));
Self { path, hash }
}

#[inline]
fn hash_path(path: &Path) -> u64 {
let mut hasher = FxHasher::default();
path.hash(&mut hasher);
let hash = hasher.finish();
Self { path, hash }
hasher.finish()
}
}

impl PartialEq for ArcPath {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.hash == other.hash && (Arc::ptr_eq(&self.path, &other.path) || self.path == other.path)
}
}

impl Eq for ArcPath {}

impl Deref for ArcPath {
type Target = Arc<Path>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1320,10 +1320,15 @@ impl<'parser> JavascriptParser<'parser> {
match ast {
Program::Module(m) => {
self.set_strict(true);
self.prev_statement = None;
self.module_pre_walk_module_items(&m.body);
self.prev_statement = None;
self.pre_walk_module_items(&m.body);
self.is_esm = true;
if Self::module_items_need_module_pre_walk(&m.body) {
self.prev_statement = None;
self.module_pre_walk_module_items(&m.body);
}
if Self::module_items_need_pre_walk(&m.body) {
self.prev_statement = None;
self.pre_walk_module_items(&m.body);
}
self.prev_statement = None;
self.block_pre_walk_module_items(&m.body);
self.prev_statement = None;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ use crate::{
};

impl JavascriptParser<'_> {
pub(super) fn module_items_need_module_pre_walk(statements: &[ModuleItem]) -> bool {
statements.iter().any(|statement| match statement {
ModuleItem::ModuleDecl(ModuleDecl::Import(_) | ModuleDecl::ExportAll(_)) => true,
ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(decl)) => {
decl.src.is_some()
|| decl.specifiers.len() == 1
&& matches!(decl.specifiers.first(), Some(ExportSpecifier::Namespace(_)))
}
ModuleItem::ModuleDecl(_) | ModuleItem::Stmt(_) => false,
})
}

pub fn module_pre_walk_module_items(&mut self, statements: &Vec<ModuleItem>) {
for statement in statements {
self.statement_path.push(statement.span().into());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::borrow::Cow;
use swc_core::{
common::Spanned,
ecma::ast::{
ArrayPat, AssignExpr, BlockStmt, CatchClause, DoWhileStmt, ForHead, ForInStmt, ForOfStmt,
ArrayPat, AssignExpr, BlockStmt, CatchClause, Decl, DoWhileStmt, ForHead, ForInStmt, ForOfStmt,
ForStmt, IfStmt, LabeledStmt, ModuleDecl, ModuleItem, ObjectPat, ObjectPatProp, Pat, Stmt,
SwitchCase, SwitchStmt, TryStmt, VarDeclarator, WhileStmt, WithStmt,
},
Expand All @@ -20,6 +20,64 @@ use crate::{
};

impl JavascriptParser<'_> {
pub(super) fn module_items_need_pre_walk(statements: &[ModuleItem]) -> bool {
statements.iter().any(|statement| match statement {
ModuleItem::ModuleDecl(_) => false,
ModuleItem::Stmt(stmt) => Self::stmt_needs_pre_walk(stmt),
})
}

fn stmts_need_pre_walk(statements: &[Stmt]) -> bool {
statements.iter().any(Self::stmt_needs_pre_walk)
}

fn stmt_needs_pre_walk(statement: &Stmt) -> bool {
match statement {
Stmt::Block(stmt) => Self::stmts_need_pre_walk(&stmt.stmts),
Stmt::With(stmt) => Self::stmt_needs_pre_walk(&stmt.body),
Stmt::Labeled(stmt) => Self::stmt_needs_pre_walk(&stmt.body),
Stmt::If(stmt) => {
Self::stmt_needs_pre_walk(&stmt.cons)
|| stmt.alt.as_deref().is_some_and(Self::stmt_needs_pre_walk)
}
Stmt::Switch(stmt) => stmt
.cases
.iter()
.any(|case| Self::stmts_need_pre_walk(&case.cons)),
Stmt::Try(stmt) => {
Self::stmts_need_pre_walk(&stmt.block.stmts)
|| stmt
.handler
.as_ref()
.is_some_and(|handler| Self::stmts_need_pre_walk(&handler.body.stmts))
|| stmt
.finalizer
.as_ref()
.is_some_and(|finalizer| Self::stmts_need_pre_walk(&finalizer.stmts))
}
Stmt::While(stmt) => Self::stmt_needs_pre_walk(&stmt.body),
Stmt::DoWhile(stmt) => Self::stmt_needs_pre_walk(&stmt.body),
Stmt::For(stmt) => {
stmt
.init
.as_ref()
.is_some_and(|init| init.as_var_decl().is_some())
|| Self::stmt_needs_pre_walk(&stmt.body)
}
Stmt::ForIn(stmt) => {
matches!(stmt.left, ForHead::VarDecl(_) | ForHead::UsingDecl(_))
|| Self::stmt_needs_pre_walk(&stmt.body)
}
Stmt::ForOf(stmt) => {
matches!(stmt.left, ForHead::VarDecl(_) | ForHead::UsingDecl(_))
|| Self::stmt_needs_pre_walk(&stmt.body)
}
Stmt::Decl(Decl::Fn(_) | Decl::Var(_)) => true,
Stmt::Decl(_) | Stmt::Expr(_) | Stmt::Empty(_) | Stmt::Debugger(_) => false,
Stmt::Return(_) | Stmt::Break(_) | Stmt::Continue(_) | Stmt::Throw(_) => false,
}
}

pub fn pre_walk_module_items(&mut self, statements: &Vec<ModuleItem>) {
for statement in statements {
self.pre_walk_module_item(statement);
Expand Down