Skip to content

Commit 2462e17

Browse files
authored
feat: provide plugin use value dependencies (#11380)
* feat: provide plugin use value dependencies * add test * fix: commit
1 parent bb43f17 commit 2462e17

19 files changed

Lines changed: 274 additions & 175 deletions

File tree

crates/rspack_plugin_javascript/src/parser_plugin/define_plugin/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use serde_json::Value;
2020
use self::walk_data::WalkData;
2121
use crate::parser_and_generator::JavaScriptParserAndGenerator;
2222

23-
const VALUE_DEP_PREFIX: &str = "webpack/DefinePlugin ";
23+
const VALUE_DEP_PREFIX: &str = "rspack/DefinePlugin ";
2424

2525
#[derive(Debug, Error, MietteDiagnostic)]
2626
#[error("DefinePlugin:\nConflicting values for '{0}' ({1} !== {2})")]

crates/rspack_plugin_javascript/src/parser_plugin/provide_plugin.rs

Lines changed: 0 additions & 173 deletions
This file was deleted.
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
mod parser;
2+
3+
use std::sync::Arc;
4+
5+
use rspack_core::{
6+
Compilation, CompilationParams, CompilerCompilation, ModuleType, NormalModuleFactoryParser,
7+
ParserAndGenerator, ParserOptions, Plugin,
8+
};
9+
use rspack_error::{
10+
DiagnosticExt, Result,
11+
miette::{self, Diagnostic as MietteDiagnostic},
12+
thiserror::{self, Error},
13+
};
14+
use rspack_hook::{plugin, plugin_hook};
15+
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
16+
17+
use self::parser::ProvideParserPlugin;
18+
use crate::parser_and_generator::JavaScriptParserAndGenerator;
19+
20+
const VALUE_DEP_PREFIX: &str = "rspack/ProvidePlugin ";
21+
type ProvideValue = HashMap<String, Vec<String>>;
22+
23+
#[derive(Debug, Error, MietteDiagnostic)]
24+
#[error("ProvidePlugin:\nConflicting values for '{0}' ({1} !== {2})")]
25+
#[diagnostic(severity(Warning))]
26+
struct ConflictingValuesError(String, String, String);
27+
28+
#[plugin]
29+
#[derive(Default, Debug, Clone)]
30+
pub struct ProvidePlugin {
31+
provide: Arc<ProvideValue>,
32+
names: Arc<HashSet<String>>,
33+
}
34+
35+
impl ProvidePlugin {
36+
pub fn new(provide: ProvideValue) -> Self {
37+
let names = provide
38+
.keys()
39+
.flat_map(|name| {
40+
let splitted: Vec<&str> = name.split('.').collect();
41+
// splitted.len() is always greater than 0
42+
(0..splitted.len() - 1)
43+
.map(|i| splitted[0..i + 1].join("."))
44+
.collect::<Vec<_>>()
45+
})
46+
.collect::<HashSet<_>>();
47+
Self::new_inner(provide.into(), names.into())
48+
}
49+
}
50+
51+
#[plugin_hook(CompilerCompilation for ProvidePlugin, tracing=false)]
52+
async fn compilation(
53+
&self,
54+
compilation: &mut Compilation,
55+
_params: &mut CompilationParams,
56+
) -> Result<()> {
57+
for (key, value) in self.provide.iter() {
58+
let cache_key = format!("{VALUE_DEP_PREFIX}{key}");
59+
let value = value.join(".");
60+
if let Some(prev) = compilation.value_cache_versions.get(&cache_key)
61+
&& prev != &value
62+
{
63+
compilation.push_diagnostic(
64+
ConflictingValuesError(key.clone(), prev.clone(), value)
65+
.boxed()
66+
.into(),
67+
);
68+
} else {
69+
compilation.value_cache_versions.insert(cache_key, value);
70+
}
71+
}
72+
73+
Ok(())
74+
}
75+
76+
#[plugin_hook(NormalModuleFactoryParser for ProvidePlugin)]
77+
async fn nmf_parser(
78+
&self,
79+
module_type: &ModuleType,
80+
parser: &mut dyn ParserAndGenerator,
81+
_parser_options: Option<&ParserOptions>,
82+
) -> Result<()> {
83+
if module_type.is_js_like()
84+
&& let Some(parser) = parser.downcast_mut::<JavaScriptParserAndGenerator>()
85+
{
86+
parser.add_parser_plugin(Box::new(ProvideParserPlugin::new(
87+
self.provide.clone(),
88+
self.names.clone(),
89+
)));
90+
}
91+
Ok(())
92+
}
93+
94+
impl Plugin for ProvidePlugin {
95+
fn name(&self) -> &'static str {
96+
"rspack.ProvidePlugin"
97+
}
98+
99+
fn apply(&self, ctx: &mut rspack_core::ApplyContext<'_>) -> Result<()> {
100+
ctx.compiler_hooks.compilation.tap(compilation::new(self));
101+
ctx
102+
.normal_module_factory_hooks
103+
.parser
104+
.tap(nmf_parser::new(self));
105+
Ok(())
106+
}
107+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
use std::sync::Arc;
2+
3+
use cow_utils::CowUtils;
4+
use itertools::Itertools;
5+
use rspack_core::DependencyRange;
6+
use rustc_hash::FxHashSet as HashSet;
7+
use swc_core::{atoms::Atom, common::Spanned};
8+
9+
use super::{super::JavascriptParserPlugin, ProvideValue, VALUE_DEP_PREFIX};
10+
use crate::{dependency::ProvideDependency, visitors::JavascriptParser};
11+
12+
const SOURCE_DOT: &str = r#"."#;
13+
const MODULE_DOT: &str = r#"_dot_"#;
14+
15+
pub struct ProvideParserPlugin {
16+
provide: Arc<ProvideValue>,
17+
names: Arc<HashSet<String>>,
18+
}
19+
20+
impl ProvideParserPlugin {
21+
pub fn new(provide: Arc<ProvideValue>, names: Arc<HashSet<String>>) -> Self {
22+
Self { provide, names }
23+
}
24+
25+
fn add_provide_dep(
26+
&self,
27+
name: &str,
28+
range: DependencyRange,
29+
parser: &mut JavascriptParser,
30+
) -> bool {
31+
if let Some(requests) = self.provide.get(name) {
32+
let name_identifier = if name.contains(SOURCE_DOT) {
33+
format!(
34+
"__webpack_provide_{}",
35+
name.cow_replace(SOURCE_DOT, MODULE_DOT)
36+
)
37+
} else {
38+
name.to_string()
39+
};
40+
let dep = ProvideDependency::new(
41+
range,
42+
Atom::from(requests[0].as_str()),
43+
name_identifier,
44+
requests[1..]
45+
.iter()
46+
.map(|s| Atom::from(s.as_str()))
47+
.collect_vec(),
48+
Some(parser.source_map.clone()),
49+
);
50+
parser.dependencies.push(Box::new(dep));
51+
52+
// add value dependency
53+
let cache_key = format!("{VALUE_DEP_PREFIX}{name}");
54+
parser
55+
.build_info
56+
.value_dependencies
57+
.insert(cache_key, requests.join("."));
58+
return true;
59+
}
60+
false
61+
}
62+
}
63+
64+
impl JavascriptParserPlugin for ProvideParserPlugin {
65+
fn can_rename(&self, _parser: &mut JavascriptParser, str: &str) -> Option<bool> {
66+
self.names.contains(str).then_some(true)
67+
}
68+
69+
fn call(
70+
&self,
71+
parser: &mut JavascriptParser,
72+
expr: &swc_core::ecma::ast::CallExpr,
73+
for_name: &str,
74+
) -> Option<bool> {
75+
if self.add_provide_dep(for_name, expr.callee.span().into(), parser) {
76+
// FIXME: webpack use `walk_expression` here
77+
parser.walk_expr_or_spread(&expr.args);
78+
return Some(true);
79+
}
80+
None
81+
}
82+
83+
fn member(
84+
&self,
85+
parser: &mut JavascriptParser,
86+
expr: &swc_core::ecma::ast::MemberExpr,
87+
for_name: &str,
88+
) -> Option<bool> {
89+
self
90+
.add_provide_dep(for_name, expr.span().into(), parser)
91+
.then_some(true)
92+
}
93+
94+
fn identifier(
95+
&self,
96+
parser: &mut JavascriptParser,
97+
ident: &swc_core::ecma::ast::Ident,
98+
for_name: &str,
99+
) -> Option<bool> {
100+
self
101+
.add_provide_dep(for_name, ident.span.into(), parser)
102+
.then_some(true)
103+
}
104+
}

packages/rspack-test-tools/tests/cacheCases/make/issue-11221/a.js renamed to packages/rspack-test-tools/tests/cacheCases/make/define-plugin/a.js

File renamed without changes.

packages/rspack-test-tools/tests/cacheCases/make/issue-11221/b.js renamed to packages/rspack-test-tools/tests/cacheCases/make/define-plugin/b.js

File renamed without changes.

0 commit comments

Comments
 (0)