Skip to content

Commit 953025e

Browse files
authored
feat: support import.defer() for statical path (#12900)
1 parent fbaba99 commit 953025e

File tree

24 files changed

+505
-41
lines changed

24 files changed

+505
-41
lines changed

crates/rspack_core/src/runtime_globals.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,8 +288,9 @@ define_runtime_globals! {
288288
// defer import support
289289
const ASYNC_MODULE_EXPORT_SYMBOL;
290290
const MAKE_DEFERRED_NAMESPACE_OBJECT;
291-
const MAKE_DEFERRED_NAMESPACE_OBJECT_SYMBOL;
292291
const MAKE_OPTIMIZED_DEFERRED_NAMESPACE_OBJECT;
292+
const DEFERRED_MODULES_ASYNC_TRANSITIVE_DEPENDENCIES;
293+
const DEFERRED_MODULES_ASYNC_TRANSITIVE_DEPENDENCIES_SYMBOL;
293294

294295
// rspack only
295296
const ASYNC_STARTUP;
@@ -358,8 +359,11 @@ pub fn runtime_globals_to_string(
358359
RuntimeGlobals::STARTUP => format!("{scope_name}.x"),
359360
RuntimeGlobals::MAKE_NAMESPACE_OBJECT => format!("{scope_name}.r"),
360361
RuntimeGlobals::MAKE_DEFERRED_NAMESPACE_OBJECT => format!("{scope_name}.z"),
361-
RuntimeGlobals::MAKE_DEFERRED_NAMESPACE_OBJECT_SYMBOL => format!("{scope_name}.zS"),
362362
RuntimeGlobals::MAKE_OPTIMIZED_DEFERRED_NAMESPACE_OBJECT => format!("{scope_name}.zO"),
363+
RuntimeGlobals::DEFERRED_MODULES_ASYNC_TRANSITIVE_DEPENDENCIES => format!("{scope_name}.zT"),
364+
RuntimeGlobals::DEFERRED_MODULES_ASYNC_TRANSITIVE_DEPENDENCIES_SYMBOL => {
365+
format!("{scope_name}.zS")
366+
}
363367
RuntimeGlobals::EXPORTS => {
364368
runtime_variable_to_string(&RuntimeVariable::Exports, compiler_options)
365369
}

crates/rspack_core/src/runtime_template.rs

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,18 +1228,16 @@ impl ModuleCodegenRuntimeTemplate {
12281228
request: &str,
12291229
message: &str,
12301230
weak: bool,
1231+
phase: ImportPhase,
12311232
) -> String {
1232-
if compilation
1233-
.get_module_graph()
1234-
.module_identifier_by_dependency_id(dep_id)
1235-
.is_none()
1236-
{
1233+
let mg = compilation.get_module_graph();
1234+
let Some(target_module) = mg.get_module_by_dependency_id(dep_id) else {
12371235
return self.missing_module_promise(request);
12381236
};
12391237

12401238
let promise = self.block_promise(block, compilation, message);
12411239
let exports_type = get_exports_type(
1242-
compilation.get_module_graph(),
1240+
mg,
12431241
&compilation.module_graph_cache_artifact,
12441242
dep_id,
12451243
&module_id,
@@ -1257,8 +1255,73 @@ impl ModuleCodegenRuntimeTemplate {
12571255
} else {
12581256
None
12591257
};
1260-
let mut fake_type = FakeNamespaceObjectMode::PROMISE_LIKE;
1258+
12611259
let mut appending;
1260+
1261+
if phase.is_defer() && !target_module.build_meta().has_top_level_await {
1262+
let mode = render_make_deferred_namespace_mode_from_exports_type(exports_type);
1263+
let async_deps = get_outgoing_async_modules(compilation, target_module.as_ref());
1264+
if !async_deps.is_empty() {
1265+
if let Some(header) = header {
1266+
let rendered_async_deps_fn = self.render_runtime_globals(
1267+
&RuntimeGlobals::DEFERRED_MODULES_ASYNC_TRANSITIVE_DEPENDENCIES,
1268+
);
1269+
appending = format!(
1270+
".then({})",
1271+
self.basic_function(
1272+
"",
1273+
&format!(
1274+
"{header}\nreturn {}({})",
1275+
&rendered_async_deps_fn,
1276+
json_stringify(&async_deps)
1277+
)
1278+
)
1279+
);
1280+
} else {
1281+
let rendered_async_deps_fn = self.render_runtime_globals(
1282+
&RuntimeGlobals::DEFERRED_MODULES_ASYNC_TRANSITIVE_DEPENDENCIES,
1283+
);
1284+
appending = format!(
1285+
".then({})",
1286+
self.returning_function(
1287+
&format!(
1288+
"{}({})",
1289+
&rendered_async_deps_fn,
1290+
json_stringify(&async_deps)
1291+
),
1292+
""
1293+
)
1294+
);
1295+
}
1296+
appending.push_str(&format!(
1297+
".then({}.bind({}, {module_id_expr}, {mode}))",
1298+
self.render_runtime_globals(&RuntimeGlobals::MAKE_DEFERRED_NAMESPACE_OBJECT),
1299+
self.render_runtime_globals(&RuntimeGlobals::REQUIRE)
1300+
));
1301+
} else if let Some(header) = header {
1302+
let rendered_async_deps_fn =
1303+
self.render_runtime_globals(&RuntimeGlobals::MAKE_DEFERRED_NAMESPACE_OBJECT);
1304+
appending = format!(
1305+
".then({})",
1306+
self.basic_function(
1307+
"",
1308+
&format!(
1309+
"{header}\nreturn {}({module_id_expr}, {mode});",
1310+
&rendered_async_deps_fn
1311+
)
1312+
)
1313+
);
1314+
} else {
1315+
appending = format!(
1316+
".then({}.bind({}, {module_id_expr}, {mode}))",
1317+
self.render_runtime_globals(&RuntimeGlobals::MAKE_DEFERRED_NAMESPACE_OBJECT),
1318+
self.render_runtime_globals(&RuntimeGlobals::REQUIRE)
1319+
);
1320+
}
1321+
return format!("{promise}{appending}");
1322+
}
1323+
1324+
let mut fake_type = FakeNamespaceObjectMode::PROMISE_LIKE;
12621325
match exports_type {
12631326
ExportsType::Namespace => {
12641327
if let Some(header) = header {

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use rspack_cacheable::{
55
use rspack_core::{
66
AsContextDependency, Dependency, DependencyCategory, DependencyCodeGeneration, DependencyId,
77
DependencyRange, DependencyTemplate, DependencyTemplateType, DependencyType, ExportsType,
8-
ExtendedReferencedExport, FactorizeInfo, ImportAttributes, ModuleDependency, ModuleGraph,
9-
ModuleGraphCacheArtifact, ReferencedExport, ResourceIdentifier, TemplateContext,
8+
ExtendedReferencedExport, FactorizeInfo, ImportAttributes, ImportPhase, ModuleDependency,
9+
ModuleGraph, ModuleGraphCacheArtifact, ReferencedExport, ResourceIdentifier, TemplateContext,
1010
TemplateReplaceSource, create_exports_object_referenced,
1111
};
1212
use swc_core::ecma::atoms::Atom;
@@ -68,6 +68,7 @@ pub struct ImportDependency {
6868
#[cacheable(with=AsOption<AsVec<AsVec<AsPreset>>>)]
6969
referenced_exports: Option<Vec<Vec<Atom>>>,
7070
attributes: Option<ImportAttributes>,
71+
phase: ImportPhase,
7172
pub comments: Vec<(bool, String)>,
7273
resource_identifier: ResourceIdentifier,
7374
factorize_info: FactorizeInfo,
@@ -80,6 +81,7 @@ impl ImportDependency {
8081
range: DependencyRange,
8182
referenced_exports: Option<Vec<Vec<Atom>>>,
8283
attributes: Option<ImportAttributes>,
84+
phase: ImportPhase,
8385
optional: bool,
8486
comments: Vec<(bool, String)>,
8587
) -> Self {
@@ -91,6 +93,7 @@ impl ImportDependency {
9193
id: DependencyId::new(),
9294
referenced_exports,
9395
attributes,
96+
phase,
9497
resource_identifier,
9598
factorize_info: Default::default(),
9699
optional,
@@ -125,6 +128,10 @@ impl Dependency for ImportDependency {
125128
self.attributes.as_ref()
126129
}
127130

131+
fn get_phase(&self) -> ImportPhase {
132+
self.phase
133+
}
134+
128135
fn range(&self) -> Option<DependencyRange> {
129136
Some(self.range)
130137
}
@@ -217,6 +224,7 @@ impl DependencyTemplate for ImportDependencyTemplate {
217224
dep.request(),
218225
dep.dependency_type().as_str(),
219226
false,
227+
dep.get_phase(),
220228
)
221229
.as_str(),
222230
None,

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rspack_cacheable::{
55
use rspack_core::{
66
AsContextDependency, Dependency, DependencyCategory, DependencyCodeGeneration, DependencyId,
77
DependencyRange, DependencyTemplate, DependencyTemplateType, DependencyType, FactorizeInfo,
8-
ImportAttributes, ModuleDependency, ModuleGraphCacheArtifact, ResourceIdentifier,
8+
ImportAttributes, ImportPhase, ModuleDependency, ModuleGraphCacheArtifact, ResourceIdentifier,
99
TemplateContext, TemplateReplaceSource,
1010
};
1111
use swc_core::ecma::atoms::Atom;
@@ -25,6 +25,7 @@ pub struct ImportEagerDependency {
2525
#[cacheable(with=AsOption<AsVec<AsVec<AsPreset>>>)]
2626
referenced_exports: Option<Vec<Vec<Atom>>>,
2727
attributes: Option<ImportAttributes>,
28+
phase: ImportPhase,
2829
resource_identifier: ResourceIdentifier,
2930
factorize_info: FactorizeInfo,
3031
}
@@ -35,6 +36,7 @@ impl ImportEagerDependency {
3536
range: DependencyRange,
3637
referenced_exports: Option<Vec<Vec<Atom>>>,
3738
attributes: Option<ImportAttributes>,
39+
phase: ImportPhase,
3840
) -> Self {
3941
let resource_identifier =
4042
create_resource_identifier_for_esm_dependency(request.as_str(), attributes.as_ref());
@@ -44,6 +46,7 @@ impl ImportEagerDependency {
4446
id: DependencyId::new(),
4547
referenced_exports,
4648
attributes,
49+
phase,
4750
resource_identifier,
4851
factorize_info: Default::default(),
4952
}
@@ -76,6 +79,10 @@ impl Dependency for ImportEagerDependency {
7679
self.attributes.as_ref()
7780
}
7881

82+
fn get_phase(&self) -> ImportPhase {
83+
self.phase
84+
}
85+
7986
fn range(&self) -> Option<DependencyRange> {
8087
Some(self.range)
8188
}
@@ -164,6 +171,7 @@ impl DependencyTemplate for ImportEagerDependencyTemplate {
164171
&dep.request,
165172
dep.dependency_type().as_str(),
166173
false,
174+
dep.get_phase(),
167175
)
168176
.as_str(),
169177
None,

crates/rspack_plugin_javascript/src/parser_plugin/import_parser_plugin.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::borrow::Cow;
33
use rspack_core::{
44
AsyncDependenciesBlock, ChunkGroupOptions, ContextDependency, ContextNameSpaceObject,
55
ContextOptions, DependencyCategory, DependencyRange, DependencyType, DynamicImportFetchPriority,
6-
DynamicImportMode, GroupOptions, ImportAttributes,
6+
DynamicImportMode, GroupOptions, ImportAttributes, ImportPhase,
77
};
88
use rspack_error::{Error, Severity};
99
use rspack_util::{SpanExt, swc::get_swc_comments};
@@ -321,6 +321,16 @@ impl JavascriptParserPlugin for ImportParserPlugin {
321321
parser.add_warning(error.into());
322322
}
323323

324+
let phase: ImportPhase = node
325+
.callee
326+
.as_import()
327+
.expect("should be import")
328+
.phase
329+
.into();
330+
if phase.is_defer() && !parser.compiler_options.experiments.defer_import {
331+
parser.add_error(rspack_error::error!("deferImport is still an experimental feature. To continue using it, please enable 'experiments.deferImport'.").into());
332+
}
333+
324334
let attributes = get_attributes_from_call_expr(node);
325335
let param = parser.evaluate_expression(dyn_imported.expr.as_ref());
326336

@@ -331,6 +341,7 @@ impl JavascriptParserPlugin for ImportParserPlugin {
331341
import_call_span.into(),
332342
exports,
333343
attributes,
344+
phase,
334345
);
335346
let dep_idx = parser.next_dependency_idx();
336347
parser.add_dependency(Box::new(dep));
@@ -345,6 +356,7 @@ impl JavascriptParserPlugin for ImportParserPlugin {
345356
import_call_span.into(),
346357
exports,
347358
attributes,
359+
phase,
348360
parser.in_try,
349361
get_swc_comments(
350362
parser.comments,
@@ -378,6 +390,10 @@ impl JavascriptParserPlugin for ImportParserPlugin {
378390
return None;
379391
}
380392

393+
if phase.is_defer() {
394+
parser.add_error(rspack_error::error!("import.defer() is not yet supported for ContextModule (the import path is a dynamic expression).").into());
395+
}
396+
381397
let ContextModuleScanResult {
382398
context,
383399
reg,

crates/rspack_plugin_lazy_compilation/src/module.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use rspack_collections::Identifiable;
55
use rspack_core::{
66
AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, BoxDependency, BuildContext, BuildInfo,
77
BuildMeta, BuildResult, ChunkGraph, CodeGenerationResult, Compilation, Context,
8-
DependenciesBlock, DependencyId, DependencyRange, FactoryMeta, LibIdentOptions, Module,
9-
ModuleArgument, ModuleCodeGenerationContext, ModuleFactoryCreateData, ModuleGraph,
8+
DependenciesBlock, DependencyId, DependencyRange, FactoryMeta, ImportPhase, LibIdentOptions,
9+
Module, ModuleArgument, ModuleCodeGenerationContext, ModuleFactoryCreateData, ModuleGraph,
1010
ModuleIdentifier, ModuleLayer, ModuleType, RuntimeGlobals, RuntimeSpec, SourceType,
1111
ValueCacheVersions, impl_module_meta_info, module_update_hash,
1212
rspack_sources::{BoxSource, RawStringSource},
@@ -262,7 +262,8 @@ impl Module for LazyCompilationProxyModule {
262262
Some(block_id),
263263
&self.resource,
264264
"import()",
265-
false
265+
false,
266+
ImportPhase::Evaluation,
266267
),
267268
json_stringify(
268269
ChunkGraph::get_module_id(&compilation.module_ids_artifact, *module)

crates/rspack_plugin_rstest/src/parser_plugin.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use camino::Utf8PathBuf;
22
use rspack_core::{
3-
AsyncDependenciesBlock, ConstDependency, DependencyRange, ImportAttributes, RuntimeGlobals,
3+
AsyncDependenciesBlock, ConstDependency, DependencyRange, ImportAttributes, ImportPhase,
4+
RuntimeGlobals,
45
};
56
use rspack_plugin_javascript::{
67
JavascriptParserPlugin,
@@ -143,6 +144,7 @@ impl RstestParserPlugin {
143144
call_expr.span.into(),
144145
None,
145146
Some(attrs),
147+
ImportPhase::Evaluation,
146148
parser.in_try,
147149
get_swc_comments(
148150
parser.comments,
@@ -467,6 +469,7 @@ impl RstestParserPlugin {
467469
call_expr.span.into(),
468470
None,
469471
Some(attrs),
472+
ImportPhase::Evaluation,
470473
parser.in_try,
471474
get_swc_comments(
472475
parser.comments,

crates/rspack_plugin_runtime/src/runtime_module/runtime/async_module.ejs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,33 @@ var rspackQueues = hasSymbol ? Symbol("rspack queues") : "__rspack_queues";
33
var rspackExports = <%- ASYNC_MODULE_EXPORT_SYMBOL %> = hasSymbol ? Symbol("rspack exports") : "<%- EXPORTS %>";
44
var rspackError = hasSymbol ? Symbol("rspack error") : "__rspack_error";
55
var rspackDone = hasSymbol ? Symbol("rspack done") : "__rspack_done";
6-
var rspackDefer = <%- MAKE_DEFERRED_NAMESPACE_OBJECT_SYMBOL %> = hasSymbol ? Symbol("rspack defer") : "__rspack_defer";
6+
var rspackDefer = <%- DEFERRED_MODULES_ASYNC_TRANSITIVE_DEPENDENCIES_SYMBOL %> = hasSymbol ? Symbol("rspack defer") : "__rspack_defer";
7+
<%- DEFERRED_MODULES_ASYNC_TRANSITIVE_DEPENDENCIES %> = <%- basicFunction("asyncDeps") %> {
8+
var hasUnresolvedAsyncSubgraph = asyncDeps.some((id) => {
9+
var cache = <%- _module_cache %>[id];
10+
return !cache || cache[rspackDone] === false;
11+
});
12+
if (hasUnresolvedAsyncSubgraph) {
13+
return ({ then(onFulfilled, onRejected) { return Promise.all(asyncDeps.map(<%- REQUIRE %>)).then(onFulfilled, onRejected) } });
14+
}
15+
}
716
var resolveQueue = <%- basicFunction("queue") %> {
8-
if (queue && queue.d < 1) {
9-
queue.d = 1;
10-
queue.forEach(<%- expressionFunction("fn.r--", "fn") %>);
17+
if (queue && queue.d < 1) {
18+
queue.d = 1;
19+
queue.forEach(<%- expressionFunction("fn.r--", "fn") %>);
1120
queue.forEach(<%- expressionFunction("fn.r-- ? fn.r++ : fn()", "fn") %>);
1221
}
1322
}
1423
var wrapDeps = <%- basicFunction("deps") %> {
1524
return deps.map(<%- basicFunction("dep") %> {
1625
if (dep !== null && typeof dep === "object") {
1726
if(!dep[rspackQueues] && dep[rspackDefer]) {
18-
var asyncDeps = dep[rspackDefer];
19-
var hasUnresolvedAsyncSubgraph = asyncDeps.some(<%- basicFunction("id") %> {
20-
var cache = <%- _module_cache %>[id];
21-
return !cache || cache[rspackDone] === false;
22-
});
23-
if (hasUnresolvedAsyncSubgraph) {
27+
var asyncDeps = <%- DEFERRED_MODULES_ASYNC_TRANSITIVE_DEPENDENCIES %>(dep[rspackDefer]);
28+
if (asyncDeps) {
2429
var d = dep;
2530
dep = {
26-
then(callback) {
27-
Promise.all(asyncDeps.map(<%- REQUIRE %>)).then(<%- returningFunction("callback(d)", "") %>)
31+
then(onFulfilled, onRejected) {
32+
asyncDeps.then(<%- returningFunction("onFulfilled(d)", "") %>, onRejected);
2833
}
2934
};
3035
} else return dep;

crates/rspack_plugin_runtime/src/runtime_module/runtime/make_optimized_deferred_namespace_object.ejs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
}
2222
};
2323
<% if (_has_async) { %>
24-
if(isAsync) obj[<%- MAKE_DEFERRED_NAMESPACE_OBJECT_SYMBOL %>] = asyncDeps;
24+
if(isAsync) obj[<%- DEFERRED_MODULES_ASYNC_TRANSITIVE_DEPENDENCIES_SYMBOL %>] = asyncDeps;
2525
<% } %>
2626
return obj;
2727
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export {};
2+
3+
__configCases__defer_import_async_in_graph_dynamic_import.push("START async-mod-dep.js");
4+
__configCases__defer_import_async_in_graph_dynamic_import.push("END async-mod-dep.js");

0 commit comments

Comments
 (0)