Skip to content

Commit cdb0cb7

Browse files
committed
feat(modern-module): make dynamic import runtime-less
1 parent 01a460d commit cdb0cb7

File tree

10 files changed

+256
-8
lines changed

10 files changed

+256
-8
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/rspack_core/src/dependencies_block.rs

+6
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,12 @@ impl DependenciesBlock for AsyncDependenciesBlock {
187187
}
188188
}
189189

190+
impl AsyncDependenciesBlock {
191+
pub fn remove_dependency_id(&mut self, dependency: DependencyId) {
192+
self.dependency_ids.retain(|dep| dep != &dependency);
193+
}
194+
}
195+
190196
#[derive(Debug, Error, Diagnostic)]
191197
#[diagnostic(code(AsyncDependencyToInitialChunkError))]
192198
#[error("It's not allowed to load an initial chunk on demand. The chunk name \"{0}\" is already used by an entrypoint.")]

crates/rspack_core/src/external_module.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub enum ExternalRequest {
3232

3333
#[derive(Debug, Clone)]
3434
pub struct ExternalRequestValue {
35-
primary: String,
35+
pub primary: String,
3636
rest: Option<Vec<String>>,
3737
}
3838

@@ -121,9 +121,9 @@ pub struct ExternalModule {
121121
blocks: Vec<AsyncDependenciesBlockIdentifier>,
122122
id: Identifier,
123123
pub request: ExternalRequest,
124-
external_type: ExternalType,
124+
pub external_type: ExternalType,
125125
/// Request intended by user (without loaders from config)
126-
user_request: String,
126+
pub user_request: String,
127127
factory_meta: Option<FactoryMeta>,
128128
build_info: Option<BuildInfo>,
129129
build_meta: Option<BuildMeta>,

crates/rspack_core/src/module_graph/mod.rs

+16
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,22 @@ impl<'a> ModuleGraph<'a> {
645645
.map(|b| &**b)
646646
}
647647

648+
pub fn block_by_id_mut(
649+
&mut self,
650+
block_id: &AsyncDependenciesBlockIdentifier,
651+
) -> Option<&mut Box<AsyncDependenciesBlock>> {
652+
self
653+
.loop_partials_mut(
654+
|p| p.blocks.contains_key(block_id),
655+
|p, search_result| {
656+
p.blocks.insert(*block_id, search_result);
657+
},
658+
|p| p.blocks.get(block_id).cloned(),
659+
|p| p.blocks.get_mut(block_id),
660+
)?
661+
.as_mut()
662+
}
663+
648664
pub fn block_by_id_expect(
649665
&self,
650666
block_id: &AsyncDependenciesBlockIdentifier,

crates/rspack_plugin_javascript/src/dependency/esm/import_dependency.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ pub fn create_import_dependency_referenced_exports(
5555
#[derive(Debug, Clone)]
5656
pub struct ImportDependency {
5757
id: DependencyId,
58-
request: Atom,
59-
range: RealDependencyLocation,
58+
pub request: Atom,
59+
pub range: RealDependencyLocation,
6060
referenced_exports: Option<Vec<Atom>>,
6161
attributes: Option<ImportAttributes>,
6262
resource_identifier: String,

crates/rspack_plugin_library/Cargo.toml

+12
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ rspack_util = { version = "0.1.0", path = "../rspack_util" }
2222
rustc-hash = { workspace = true }
2323
serde_json = { workspace = true }
2424
tracing = { workspace = true }
25+
swc_core = { workspace = true, features = [
26+
"__parser",
27+
"__utils",
28+
"common_sourcemap",
29+
"ecma_preset_env",
30+
"ecma_transforms_optimization",
31+
"ecma_transforms_module",
32+
"ecma_transforms_compat",
33+
"ecma_transforms_typescript",
34+
"base",
35+
"ecma_quote",
36+
] }
2537

2638
[package.metadata.cargo-shear]
2739
ignored = ["tracing"]

crates/rspack_plugin_library/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
mod amd_library_plugin;
44
mod assign_library_plugin;
55
mod export_property_library_plugin;
6+
mod modern_module;
67
mod modern_module_library_plugin;
78
mod module_library_plugin;
89
mod system_library_plugin;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
use rspack_core::{AsContextDependency, Dependency};
2+
use rspack_core::{
3+
Compilation, DependencyType, ErrorSpan, ExternalRequest, ExternalType, ImportAttributes,
4+
RealDependencyLocation, RuntimeSpec,
5+
};
6+
use rspack_core::{DependencyCategory, DependencyId, DependencyTemplate};
7+
use rspack_core::{ModuleDependency, TemplateContext, TemplateReplaceSource};
8+
use rspack_plugin_javascript::dependency::create_resource_identifier_for_esm_dependency;
9+
use swc_core::ecma::atoms::Atom;
10+
11+
#[derive(Debug, Clone)]
12+
pub struct ModernModuleImportDependency {
13+
id: DependencyId,
14+
request: Atom,
15+
target_request: ExternalRequest,
16+
external_type: ExternalType,
17+
range: RealDependencyLocation,
18+
attributes: Option<ImportAttributes>,
19+
resource_identifier: String,
20+
}
21+
22+
impl ModernModuleImportDependency {
23+
pub fn new(
24+
request: Atom,
25+
target_request: ExternalRequest,
26+
external_type: ExternalType,
27+
range: RealDependencyLocation,
28+
attributes: Option<ImportAttributes>,
29+
) -> Self {
30+
let resource_identifier =
31+
create_resource_identifier_for_esm_dependency(request.as_str(), attributes.as_ref());
32+
Self {
33+
request,
34+
target_request,
35+
external_type,
36+
range,
37+
id: DependencyId::new(),
38+
attributes,
39+
resource_identifier,
40+
}
41+
}
42+
}
43+
44+
impl Dependency for ModernModuleImportDependency {
45+
fn id(&self) -> &DependencyId {
46+
&self.id
47+
}
48+
49+
fn resource_identifier(&self) -> Option<&str> {
50+
Some(&self.resource_identifier)
51+
}
52+
53+
fn category(&self) -> &DependencyCategory {
54+
&DependencyCategory::Esm
55+
}
56+
57+
fn dependency_type(&self) -> &DependencyType {
58+
&DependencyType::DynamicImport
59+
}
60+
61+
fn get_attributes(&self) -> Option<&ImportAttributes> {
62+
self.attributes.as_ref()
63+
}
64+
65+
fn span(&self) -> Option<ErrorSpan> {
66+
Some(ErrorSpan::new(self.range.start, self.range.end))
67+
}
68+
69+
fn could_affect_referencing_module(&self) -> rspack_core::AffectType {
70+
rspack_core::AffectType::True
71+
}
72+
}
73+
74+
impl ModuleDependency for ModernModuleImportDependency {
75+
fn request(&self) -> &str {
76+
&self.request
77+
}
78+
79+
fn user_request(&self) -> &str {
80+
&self.request
81+
}
82+
83+
fn set_request(&mut self, request: String) {
84+
self.request = request.into();
85+
}
86+
}
87+
88+
impl DependencyTemplate for ModernModuleImportDependency {
89+
fn apply(
90+
&self,
91+
source: &mut TemplateReplaceSource,
92+
_code_generatable_context: &mut TemplateContext,
93+
) {
94+
let request_and_external_type = match &self.target_request {
95+
ExternalRequest::Single(request) => (Some(request), &self.external_type),
96+
ExternalRequest::Map(map) => (map.get(&self.external_type), &self.external_type),
97+
};
98+
99+
if let Some(request_and_external_type) = request_and_external_type.0 {
100+
source.replace(
101+
self.range.start,
102+
self.range.end,
103+
format!(
104+
"import({})",
105+
serde_json::to_string(request_and_external_type.primary())
106+
.expect("invalid json to_string")
107+
)
108+
.as_str(),
109+
None,
110+
);
111+
}
112+
}
113+
114+
fn dependency_id(&self) -> Option<DependencyId> {
115+
Some(self.id)
116+
}
117+
118+
fn update_hash(
119+
&self,
120+
_hasher: &mut dyn std::hash::Hasher,
121+
_compilation: &Compilation,
122+
_runtime: Option<&RuntimeSpec>,
123+
) {
124+
}
125+
}
126+
127+
impl AsContextDependency for ModernModuleImportDependency {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
mod dependency;
2+
3+
pub use dependency::*;

crates/rspack_plugin_library/src/modern_module_library_plugin.rs

+85-3
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,23 @@ use std::hash::Hash;
33
use rspack_core::rspack_sources::{ConcatSource, RawSource, SourceExt};
44
use rspack_core::{
55
merge_runtime, to_identifier, ApplyContext, ChunkUkey, CodeGenerationExportsFinalNames,
6-
Compilation, CompilationOptimizeChunkModules, CompilationParams, CompilerCompilation,
7-
CompilerOptions, ConcatenatedModule, ConcatenatedModuleExportsDefinitions, LibraryOptions,
8-
ModuleIdentifier, Plugin, PluginContext,
6+
Compilation, CompilationFinishModules, CompilationOptimizeChunkModules, CompilationParams,
7+
CompilerCompilation, CompilerOptions, ConcatenatedModule, ConcatenatedModuleExportsDefinitions,
8+
DependenciesBlock, Dependency, ExternalModule, LibraryOptions, ModuleIdentifier, Plugin,
9+
PluginContext,
910
};
1011
use rspack_error::{error_bail, Result};
1112
use rspack_hash::RspackHash;
1213
use rspack_hook::{plugin, plugin_hook};
14+
use rspack_plugin_javascript::dependency::ImportDependency;
1315
use rspack_plugin_javascript::ModuleConcatenationPlugin;
1416
use rspack_plugin_javascript::{
1517
ConcatConfiguration, JavascriptModulesChunkHash, JavascriptModulesRenderStartup, JsPlugin,
1618
RenderSource,
1719
};
1820
use rustc_hash::FxHashSet as HashSet;
1921

22+
use super::modern_module::ModernModuleImportDependency;
2023
use crate::utils::{get_options_for_chunk, COMMON_LIBRARY_NAME_MESSAGE};
2124

2225
const PLUGIN_NAME: &str = "rspack.ModernModuleLibraryPlugin";
@@ -189,6 +192,80 @@ fn render_startup(
189192
Ok(())
190193
}
191194

195+
#[plugin_hook(CompilationFinishModules for ModernModuleLibraryPlugin)]
196+
async fn finish_modules(&self, compilation: &mut Compilation) -> Result<()> {
197+
let mut mg = compilation.get_module_graph_mut();
198+
let modules = mg.modules();
199+
let module_ids = modules.keys().cloned().collect::<Vec<_>>();
200+
201+
for module_id in module_ids {
202+
let mut deps_to_replace = Vec::new();
203+
let module = mg
204+
.module_by_identifier(&module_id)
205+
.expect("should have mgm");
206+
let connections = mg.get_outgoing_connections(&module_id);
207+
let block_ids = module.get_blocks();
208+
209+
for block_id in block_ids {
210+
let block = mg.block_by_id(block_id).expect("should have block");
211+
for block_dep_id in block.get_dependencies() {
212+
let block_dep = mg.dependency_by_id(block_dep_id);
213+
if let Some(block_dep) = block_dep {
214+
if let Some(import_dependency) = block_dep.as_any().downcast_ref::<ImportDependency>() {
215+
// TODO: do not use find
216+
// Try find the connection that marks import dependency as an external module.
217+
let connection_to_external = connections.iter().find(|c| {
218+
let module_id = c.module_identifier();
219+
let module = mg.module_by_identifier(module_id).expect("should have mgm");
220+
if let Some(external_module) = module.as_any().downcast_ref::<ExternalModule>() {
221+
import_dependency.request == external_module.user_request
222+
} else {
223+
false
224+
}
225+
});
226+
227+
if let Some(connection_to_external) = connection_to_external {
228+
let external_module_id = connection_to_external.module_identifier();
229+
let external_module = mg
230+
.module_by_identifier(external_module_id)
231+
.expect("should have mgm");
232+
233+
if let Some(external_module) = external_module.as_external_module() {
234+
let new_dep = ModernModuleImportDependency::new(
235+
import_dependency.request.as_str().into(),
236+
external_module.request.clone(),
237+
external_module.external_type.clone(),
238+
import_dependency.range.clone(),
239+
None,
240+
);
241+
242+
deps_to_replace.push((
243+
*block_id,
244+
block_dep.clone(),
245+
new_dep.clone(),
246+
connection_to_external.id,
247+
));
248+
}
249+
}
250+
}
251+
}
252+
}
253+
}
254+
255+
for (block_id, dep, new_dep, connection_id) in deps_to_replace.iter() {
256+
let block = mg.block_by_id_mut(block_id).expect("should have block");
257+
let dep_id = dep.id();
258+
block.remove_dependency_id(*dep_id);
259+
let boxed_dep = Box::new(new_dep.clone()) as Box<dyn rspack_core::Dependency>;
260+
block.add_dependency_id(*new_dep.id());
261+
mg.add_dependency(boxed_dep);
262+
mg.revoke_connection(connection_id, true);
263+
}
264+
}
265+
266+
Ok(())
267+
}
268+
192269
#[plugin_hook(JavascriptModulesChunkHash for ModernModuleLibraryPlugin)]
193270
async fn js_chunk_hash(
194271
&self,
@@ -256,6 +333,11 @@ impl Plugin for ModernModuleLibraryPlugin {
256333
.compilation_hooks
257334
.optimize_chunk_modules
258335
.tap(optimize_chunk_modules::new(self));
336+
ctx
337+
.context
338+
.compilation_hooks
339+
.finish_modules
340+
.tap(finish_modules::new(self));
259341

260342
Ok(())
261343
}

0 commit comments

Comments
 (0)