Skip to content

Commit

Permalink
feat: support match_when_empty for rule condition (#8809)
Browse files Browse the repository at this point in the history
  • Loading branch information
CPunisher authored Dec 24, 2024
1 parent 89f567b commit 4da33a8
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,14 @@ impl TryFrom<RawRuleSetCondition> for rspack_core::RuleSetCondition {
}
}

impl TryFrom<RawRuleSetCondition> for rspack_core::RuleSetConditionWithEmpty {
type Error = rspack_error::Error;

fn try_from(x: RawRuleSetCondition) -> rspack_error::Result<Self> {
Ok(Self::new(x.try_into()?))
}
}

type ThreadsafeUse = ThreadsafeFunction<RawFuncUseCtx, Vec<RawModuleRuleUse>>;

#[derive(Debug, Default)]
Expand Down
76 changes: 68 additions & 8 deletions crates/rspack_core/src/options/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -568,8 +569,8 @@ impl Default for CssExportsConvention {
}
}

pub type DescriptionData = HashMap<String, RuleSetCondition>;
pub type With = HashMap<String, RuleSetCondition>;
pub type DescriptionData = HashMap<String, RuleSetConditionWithEmpty>;
pub type With = HashMap<String, RuleSetConditionWithEmpty>;

pub type RuleSetConditionFnMatcher =
Box<dyn Fn(DataRef) -> BoxFuture<'static, Result<bool>> + Sync + Send>;
Expand Down Expand Up @@ -644,6 +645,47 @@ impl RuleSetCondition {
Self::Func(f) => f(data).await,
}
}

#[async_recursion]
async fn match_when_empty(&self) -> Result<bool> {
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<bool>,
}

impl RuleSetConditionWithEmpty {
pub fn new(condition: RuleSetCondition) -> Self {
Self {
condition,
match_when_empty: OnceCell::new(),
}
}

pub async fn try_match(&self, data: DataRef<'_>) -> Result<bool> {
self.condition.try_match(data).await
}

pub async fn match_when_empty(&self) -> Result<bool> {
self
.match_when_empty
.get_or_try_init(|| async { self.condition.match_when_empty().await })
.await
.copied()
}
}

#[derive(Debug, Default)]
Expand Down Expand Up @@ -673,6 +715,24 @@ impl RuleSetLogicalConditions {
}
Ok(true)
}

pub async fn match_when_empty(&self) -> Result<bool> {
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 {
Expand Down Expand Up @@ -709,13 +769,13 @@ pub struct ModuleRule {
/// A condition matcher matching an absolute path.
pub resource: Option<RuleSetCondition>,
/// A condition matcher against the resource query.
pub resource_query: Option<RuleSetCondition>,
pub resource_fragment: Option<RuleSetCondition>,
pub resource_query: Option<RuleSetConditionWithEmpty>,
pub resource_fragment: Option<RuleSetConditionWithEmpty>,
pub dependency: Option<RuleSetCondition>,
pub issuer: Option<RuleSetCondition>,
pub issuer_layer: Option<RuleSetCondition>,
pub scheme: Option<RuleSetCondition>,
pub mimetype: Option<RuleSetCondition>,
pub issuer: Option<RuleSetConditionWithEmpty>,
pub issuer_layer: Option<RuleSetConditionWithEmpty>,
pub scheme: Option<RuleSetConditionWithEmpty>,
pub mimetype: Option<RuleSetConditionWithEmpty>,
pub description_data: Option<DescriptionData>,
pub with: Option<With>,
pub one_of: Option<Vec<ModuleRule>>,
Expand Down
36 changes: 26 additions & 10 deletions crates/rspack_core/src/utils/module_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand All @@ -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);
}
}
Expand All @@ -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? {
Expand All @@ -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);
}
}
}
}

Expand All @@ -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);
}
}
};
}

Expand All @@ -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);
}
}
}
}

Expand All @@ -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);
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default "a.js"
Original file line number Diff line number Diff line change
@@ -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");
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = function (content) {
return "export default 'loader.js'";
};
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
};

0 comments on commit 4da33a8

Please sign in to comment.