diff --git a/crates/rspack_binding_values/src/raw_options/raw_module/mod.rs b/crates/rspack_binding_values/src/raw_options/raw_module/mod.rs index da924f94b08..bd9d0e46a95 100644 --- a/crates/rspack_binding_values/src/raw_options/raw_module/mod.rs +++ b/crates/rspack_binding_values/src/raw_options/raw_module/mod.rs @@ -138,6 +138,14 @@ impl TryFrom for rspack_core::RuleSetCondition { } } +impl TryFrom for rspack_core::RuleSetConditionWithEmpty { + type Error = rspack_error::Error; + + fn try_from(x: RawRuleSetCondition) -> rspack_error::Result { + Ok(Self::new(x.try_into()?)) + } +} + type ThreadsafeUse = ThreadsafeFunction>; #[derive(Debug, Default)] diff --git a/crates/rspack_core/src/options/module.rs b/crates/rspack_core/src/options/module.rs index 9579dc6c5b1..9ff79097201 100644 --- a/crates/rspack_core/src/options/module.rs +++ b/crates/rspack_core/src/options/module.rs @@ -12,6 +12,7 @@ use rspack_macros::MergeFrom; use rspack_regex::RspackRegex; use rspack_util::{try_all, try_any, MergeFrom}; use rustc_hash::FxHashMap as HashMap; +use tokio::sync::OnceCell; use crate::{Compilation, Filename, Module, ModuleType, PublicPath, Resolve}; @@ -560,8 +561,8 @@ impl Default for CssExportsConvention { } } -pub type DescriptionData = HashMap; -pub type With = HashMap; +pub type DescriptionData = HashMap; +pub type With = HashMap; pub type RuleSetConditionFnMatcher = Box BoxFuture<'static, Result> + Sync + Send>; @@ -636,6 +637,47 @@ impl RuleSetCondition { Self::Func(f) => f(data).await, } } + + #[async_recursion] + async fn match_when_empty(&self) -> Result { + let res = match self { + RuleSetCondition::String(s) => s.is_empty(), + RuleSetCondition::Regexp(rspack_regex) => rspack_regex.test(""), + RuleSetCondition::Logical(logical) => logical.match_when_empty().await?, + RuleSetCondition::Array(arr) => { + arr.is_empty() && try_any(arr, |c| async move { c.match_when_empty().await }).await? + } + RuleSetCondition::Func(func) => func("".into()).await?, + }; + Ok(res) + } +} + +#[derive(Debug)] +pub struct RuleSetConditionWithEmpty { + condition: RuleSetCondition, + match_when_empty: OnceCell, +} + +impl RuleSetConditionWithEmpty { + pub fn new(condition: RuleSetCondition) -> Self { + Self { + condition, + match_when_empty: OnceCell::new(), + } + } + + pub async fn try_match(&self, data: DataRef<'_>) -> Result { + self.condition.try_match(data).await + } + + pub async fn match_when_empty(&self) -> Result { + self + .match_when_empty + .get_or_try_init(|| async { self.condition.match_when_empty().await }) + .await + .copied() + } } #[derive(Debug, Default)] @@ -665,6 +707,24 @@ impl RuleSetLogicalConditions { } Ok(true) } + + pub async fn match_when_empty(&self) -> Result { + let mut has_condition = false; + let mut match_when_empty = true; + if let Some(and) = &self.and { + has_condition = true; + match_when_empty &= try_all(and, |i| async { i.match_when_empty().await }).await?; + } + if let Some(or) = &self.or { + has_condition = true; + match_when_empty &= try_any(or, |i| async { i.match_when_empty().await }).await?; + } + if let Some(not) = &self.not { + has_condition = true; + match_when_empty &= !not.match_when_empty().await?; + } + Ok(has_condition && match_when_empty) + } } pub struct FuncUseCtx { @@ -701,13 +761,13 @@ pub struct ModuleRule { /// A condition matcher matching an absolute path. pub resource: Option, /// A condition matcher against the resource query. - pub resource_query: Option, - pub resource_fragment: Option, + pub resource_query: Option, + pub resource_fragment: Option, pub dependency: Option, - pub issuer: Option, - pub issuer_layer: Option, - pub scheme: Option, - pub mimetype: Option, + pub issuer: Option, + pub issuer_layer: Option, + pub scheme: Option, + pub mimetype: Option, pub description_data: Option, pub with: Option, pub one_of: Option>, diff --git a/crates/rspack_core/src/utils/module_rules.rs b/crates/rspack_core/src/utils/module_rules.rs index 5f3e15938fa..02b3beac3d1 100644 --- a/crates/rspack_core/src/utils/module_rules.rs +++ b/crates/rspack_core/src/utils/module_rules.rs @@ -86,7 +86,7 @@ pub async fn module_rule_matcher<'a>( { return Ok(false); } - } else { + } else if !resource_query_rule.match_when_empty().await? { return Ok(false); } } @@ -99,7 +99,7 @@ pub async fn module_rule_matcher<'a>( { return Ok(false); } - } else { + } else if !resource_fragment_condition.match_when_empty().await? { return Ok(false); } } @@ -112,14 +112,14 @@ pub async fn module_rule_matcher<'a>( { return Ok(false); } - } else { + } else if !mimetype_condition.match_when_empty().await? { return Ok(false); } } if let Some(scheme_condition) = &module_rule.scheme { let scheme = resource_data.get_scheme(); - if scheme.is_none() { + if scheme.is_none() && !scheme_condition.match_when_empty().await? { return Ok(false); } if !scheme_condition.try_match(scheme.as_str().into()).await? { @@ -134,7 +134,11 @@ pub async fn module_rule_matcher<'a>( return Ok(false); } } - None => return Ok(false), + None => { + if !issuer_rule.match_when_empty().await? { + return Ok(false); + } + } } } @@ -145,7 +149,11 @@ pub async fn module_rule_matcher<'a>( return Ok(false); } } - None => return Ok(false), + None => { + if !issuer_layer_rule.match_when_empty().await? { + return Ok(false); + } + } }; } @@ -167,12 +175,16 @@ pub async fn module_rule_matcher<'a>( if !matcher.try_match(v.into()).await? { return Ok(false); } - } else { + } else if !matcher.match_when_empty().await? { return Ok(false); } } } else { - return Ok(false); + for matcher in description_data.values() { + if !matcher.match_when_empty().await? { + return Ok(false); + } + } } } @@ -183,12 +195,16 @@ pub async fn module_rule_matcher<'a>( if !matcher.try_match(v.into()).await? { return Ok(false); } - } else { + } else if !matcher.match_when_empty().await? { return Ok(false); } } } else { - return Ok(false); + for matcher in with.values() { + if !matcher.match_when_empty().await? { + return Ok(false); + } + } } } diff --git a/packages/rspack-test-tools/tests/configCases/loader/match-when-empty/a.js b/packages/rspack-test-tools/tests/configCases/loader/match-when-empty/a.js new file mode 100644 index 00000000000..f31c910d79c --- /dev/null +++ b/packages/rspack-test-tools/tests/configCases/loader/match-when-empty/a.js @@ -0,0 +1 @@ +export default "a.js" \ No newline at end of file diff --git a/packages/rspack-test-tools/tests/configCases/loader/match-when-empty/index.js b/packages/rspack-test-tools/tests/configCases/loader/match-when-empty/index.js new file mode 100644 index 00000000000..a7ced44acc5 --- /dev/null +++ b/packages/rspack-test-tools/tests/configCases/loader/match-when-empty/index.js @@ -0,0 +1,7 @@ +import a1 from "./a.js" with { type: "raw" }; +import a2 from "./a.js"; + +it("should hit loader", () => { + expect(a1).toEqual("export default \"a.js\""); + expect(a2).toEqual("loader.js"); +}); \ No newline at end of file diff --git a/packages/rspack-test-tools/tests/configCases/loader/match-when-empty/loader.js b/packages/rspack-test-tools/tests/configCases/loader/match-when-empty/loader.js new file mode 100644 index 00000000000..290ed756509 --- /dev/null +++ b/packages/rspack-test-tools/tests/configCases/loader/match-when-empty/loader.js @@ -0,0 +1,3 @@ +module.exports = function (content) { + return "export default 'loader.js'"; +}; diff --git a/packages/rspack-test-tools/tests/configCases/loader/match-when-empty/rspack.config.js b/packages/rspack-test-tools/tests/configCases/loader/match-when-empty/rspack.config.js new file mode 100644 index 00000000000..c76cdcb7d9e --- /dev/null +++ b/packages/rspack-test-tools/tests/configCases/loader/match-when-empty/rspack.config.js @@ -0,0 +1,26 @@ +module.exports = { + mode: "development", + entry: "./index.js", + devtool: false, + module: { + rules: [ + { + test: /a\.js/, + with: { + type: { + not: "raw" + } + }, + use: [ + { + loader: "./loader.js", + } + ] + }, + { + with: { type: "raw" }, + type: "asset/source" + } + ] + } +};