Skip to content

Commit 9822cef

Browse files
authored
fix: template literal dynamic import (#2732)
* fix: template_literal_dynamic_import * chore: lint fix * chore: lint rs * chore: changeset * chore: fix unexpected change * chore: indent revert * fix: clippy warning * fix: scan context module * fix: test error * chore: decrease dist file count
1 parent 27afffc commit 9822cef

File tree

10 files changed

+268
-27
lines changed

10 files changed

+268
-27
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@rspack/binding": patch
3+
---
4+
5+
fix templete literal in dynamic import
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const a = 'a';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const b = "b";
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
exports.ids = ['main'];
2+
exports.modules = {
3+
"./child Lazy recursive ^.*.js$": function (module, exports, __webpack_require__) {
4+
var map = {"./a.js": "./child/a.js","./b.js": "./child/b.js",};
5+
function webpackAsyncContext(req) {
6+
if(!__webpack_require__.o(map, req)) {
7+
return Promise.resolve().then(function() {
8+
var e = new Error("Cannot find module '" + req + "'");
9+
e.code = 'MODULE_NOT_FOUND';
10+
throw e;
11+
});
12+
}
13+
// extract logic from generate
14+
var id = map[req];
15+
16+
return __webpack_require__.el(id).then(function() {
17+
return __webpack_require__(id);
18+
});
19+
}
20+
webpackAsyncContext.keys = function() {
21+
return Object.keys(map);
22+
};
23+
webpackAsyncContext.id = "./child Lazy recursive ^.*.js$";
24+
module.exports = webpackAsyncContext;},
25+
"./index.js": function (module, exports, __webpack_require__) {
26+
const request = 'a';
27+
__webpack_require__.el("./child/a.js").then(__webpack_require__.bind(__webpack_require__, "./child/a.js")).then(__webpack_require__.ir).then(({ a })=>console.log("Literal", a));
28+
__webpack_require__.el("./child/b.js").then(__webpack_require__.bind(__webpack_require__, "./child/b.js")).then(__webpack_require__.ir).then(({ b })=>console.log("Template Literal", b));
29+
__webpack_require__('./child Lazy recursive ^.*.js$')(`./child/${request}.js`.replace("./child/", "./")).then(({ a })=>console.log("context_module_tpl", a));
30+
__webpack_require__('./child Lazy recursive ^.*.js$')(('./child/' + request + '.js').replace("./child/", "./")).then(({ a })=>console.log("context_module_bin", a));
31+
__webpack_require__('./child Lazy recursive ^.*.js$')("./child/".concat(request, ".js").replace("./child/", "./")).then(({ a })=>console.log("context_module_concat", a));
32+
},
33+
34+
};
35+
36+
var __webpack_require__ = require('./runtime.js')
37+
__webpack_require__.C(exports)
38+
var __webpack_exports__ = __webpack_require__('./index.js');
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
(function() {
2+
var __webpack_modules__ = {
3+
4+
}
5+
// The module cache
6+
var __webpack_module_cache__ = {};
7+
function __webpack_require__(moduleId) {
8+
// Check if module is in cache
9+
var cachedModule = __webpack_module_cache__[moduleId];
10+
if (cachedModule !== undefined) {
11+
return cachedModule.exports;
12+
}
13+
// Create a new module (and put it into the cache)
14+
var module = (__webpack_module_cache__[moduleId] = {
15+
exports: {}
16+
});
17+
// Execute the module function
18+
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
19+
// Return the exports of the module
20+
return module.exports;
21+
22+
}
23+
// expose the modules object (__webpack_modules__)
24+
__webpack_require__.m = __webpack_modules__;
25+
// webpack/runtime/ensure_chunk
26+
(function() {
27+
__webpack_require__.f = {};
28+
// This file contains only the entry chunk.
29+
// The chunk loading function for additional chunks
30+
__webpack_require__.e = function (chunkId) {
31+
return Promise.all(
32+
Object.keys(__webpack_require__.f).reduce(function (promises, key) {
33+
__webpack_require__.f[key](chunkId, promises);
34+
return promises;
35+
}, [])
36+
);
37+
};
38+
39+
})();
40+
// ir
41+
(function() {
42+
(function () {
43+
function _getRequireCache(nodeInterop) {
44+
if (typeof WeakMap !== "function") return null;
45+
46+
var cacheBabelInterop = new WeakMap();
47+
var cacheNodeInterop = new WeakMap();
48+
return (_getRequireCache = function (nodeInterop) {
49+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
50+
})(nodeInterop);
51+
}
52+
53+
__webpack_require__.ir = function (obj, nodeInterop) {
54+
if (!nodeInterop && obj && obj.__esModule) {
55+
return obj;
56+
}
57+
58+
if (
59+
obj === null ||
60+
(typeof obj !== "object" && typeof obj !== "function")
61+
) {
62+
return { default: obj };
63+
}
64+
65+
var cache = _getRequireCache(nodeInterop);
66+
if (cache && cache.has(obj)) {
67+
return cache.get(obj);
68+
}
69+
70+
var newObj = {};
71+
var hasPropertyDescriptor =
72+
Object.defineProperty && Object.getOwnPropertyDescriptor;
73+
for (var key in obj) {
74+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
75+
var desc = hasPropertyDescriptor
76+
? Object.getOwnPropertyDescriptor(obj, key)
77+
: null;
78+
if (desc && (desc.get || desc.set)) {
79+
Object.defineProperty(newObj, key, desc);
80+
} else {
81+
newObj[key] = obj[key];
82+
}
83+
}
84+
}
85+
newObj.default = obj;
86+
if (cache) {
87+
cache.set(obj, newObj);
88+
}
89+
return newObj;
90+
};
91+
})();
92+
93+
})();
94+
// webpack/runtime/load_chunk_with_module
95+
(function() {
96+
var map = {"./child/a.js": ["child_a_js",],"./child/b.js": ["child_b_js",],};
97+
98+
__webpack_require__.el = function(module) {
99+
var chunkId = map[module];
100+
if (chunkId === undefined) {
101+
return Promise.resolve();
102+
}
103+
if (chunkId.length > 1) {
104+
return Promise.all(chunkId.map(__webpack_require__.e));
105+
} else {
106+
return __webpack_require__.e(chunkId[0]);
107+
};
108+
}
109+
110+
})();
111+
// webpack/runtime/has_own_property
112+
(function() {
113+
__webpack_require__.o = function (obj, prop) {
114+
return Object.prototype.hasOwnProperty.call(obj, prop);
115+
};
116+
117+
})();
118+
// webpack/runtime/get_chunk_filename/__webpack_require__.u
119+
(function() {
120+
// This function allow to reference chunks
121+
__webpack_require__.u = function (chunkId) {
122+
// return url for filenames based on template
123+
return {"child_a_js": "child_a_js.js","child_b_js": "child_b_js.js",}[chunkId];
124+
};
125+
126+
})();
127+
// webpack/runtime/require_chunk_loading
128+
(function() {
129+
var installedChunks = {"runtime": 0,};
130+
// object to store loaded chunks
131+
// "1" means "loaded", otherwise not loaded yet
132+
133+
var installChunk = function (chunk) {
134+
var moreModules = chunk.modules,
135+
chunkIds = chunk.ids,
136+
runtime = chunk.runtime;
137+
for (var moduleId in moreModules) {
138+
if (__webpack_require__.o(moreModules, moduleId)) {
139+
__webpack_require__.m[moduleId] = moreModules[moduleId];
140+
}
141+
}
142+
if (runtime) runtime(__webpack_require__);
143+
for (var i = 0; i < chunkIds.length; i++) installedChunks[chunkIds[i]] = 1;
144+
};
145+
// require() chunk loading for javascript
146+
__webpack_require__.f.require = function (chunkId, promises) {
147+
// "1" is the signal for "already loaded"
148+
if (!installedChunks[chunkId]) {
149+
if (chunkId) {
150+
installChunk(require("./" + __webpack_require__.u(chunkId)));
151+
} else installedChunks[chunkId] = 1;
152+
}
153+
};
154+
module.exports = __webpack_require__;
155+
__webpack_require__.C = installChunk;
156+
157+
})();
158+
159+
})();
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const request = 'a'
2+
import('./child/a.js').then(({ a }) => console.log("Literal", a))
3+
import(`./child/b.js`).then(({b}) => console.log("Template Literal", b))
4+
import(`./child/${request}.js`).then(({a}) => console.log("context_module_tpl", a))
5+
import('./child/' + request + '.js').then(({a}) => console.log("context_module_bin", a))
6+
import("./child/".concat(request, ".js")).then(({a}) => console.log("context_module_concat", a))
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"target": ["node"],
3+
"entry": {
4+
"main": {
5+
"import": [
6+
"./index.js"
7+
]
8+
}
9+
}
10+
}

crates/rspack_core/src/dependency/dynamic_import.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ impl CodeGeneratable for EsmDynamicImportDependency {
140140
code_gen.visitors.push(
141141
create_javascript_visitor!(exact &self.ast_path, visit_mut_call_expr(n: &mut CallExpr) {
142142
if let Some(import) = n.args.get_mut(0) {
143-
if import.spread.is_none() && let Expr::Lit(Lit::Str(_)) = import.expr.as_mut() {
143+
if import.spread.is_none() && let Expr::Lit(_) | Expr::Tpl(_) = import.expr.as_mut() {
144144
let call_expr = CallExpr {
145145
span: DUMMY_SP,
146146
callee: Ident::new(RuntimeGlobals::LOAD_CHUNK_WITH_MODULE.into(), DUMMY_SP).as_callee(),

crates/rspack_plugin_javascript/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,5 @@ swc_emotion = { workspace = true }
4848
swc_plugin_import = { workspace = true }
4949
tokio = { workspace = true, features = ["rt"] }
5050
tracing = { workspace = true }
51+
url = "2.3.1"
5152
xxhash-rust = { workspace = true, features = ["xxh3"] }
52-
url = "2.3.1"

crates/rspack_plugin_javascript/src/visitors/dependency/scanner.rs

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use swc_core::ecma::ast::{
1111
AssignExpr, AssignOp, BinExpr, BinaryOp, CallExpr, Callee, Expr, ExprOrSpread, Ident, Lit,
1212
MemberExpr, MemberProp, MetaPropExpr, MetaPropKind, ModuleDecl, NewExpr, Pat, PatOrExpr, Tpl,
1313
};
14-
use swc_core::ecma::atoms::js_word;
14+
use swc_core::ecma::atoms::{js_word, JsWord};
1515
use swc_core::ecma::utils::{member_expr, quote_ident, quote_str};
1616
use swc_core::ecma::visit::{AstParentNodeRef, VisitAstPath, VisitWithPath};
1717
use swc_core::quote;
@@ -123,30 +123,51 @@ impl DependencyScanner<'_> {
123123
if let Callee::Import(_) = node.callee {
124124
if let Some(dyn_imported) = node.args.get(0) {
125125
if dyn_imported.spread.is_none() {
126-
if let Expr::Lit(Lit::Str(imported)) = dyn_imported.expr.as_ref() {
127-
let chunk_name = self.try_extract_webpack_chunk_name(&imported.span);
128-
self.add_dependency(box EsmDynamicImportDependency::new(
129-
imported.value.clone(),
130-
Some(node.span.into()),
131-
as_parent_path(ast_path),
132-
chunk_name,
133-
));
134-
}
135-
if let Some((context, reg)) = scanner_context_module(dyn_imported.expr.as_ref()) {
136-
self.add_dependency(box ImportContextDependency::new(
137-
ContextOptions {
138-
mode: ContextMode::Lazy,
139-
recursive: true,
140-
reg_exp: RspackRegex::new(&reg).expect("reg failed"),
141-
reg_str: reg,
142-
include: None,
143-
exclude: None,
144-
category: DependencyCategory::Esm,
145-
request: context,
146-
},
147-
Some(node.span.into()),
148-
as_parent_path(ast_path),
149-
));
126+
match dyn_imported.expr.as_ref() {
127+
Expr::Lit(Lit::Str(imported)) => {
128+
let chunk_name = self.try_extract_webpack_chunk_name(&imported.span);
129+
self.add_dependency(box EsmDynamicImportDependency::new(
130+
imported.value.clone(),
131+
Some(node.span.into()),
132+
as_parent_path(ast_path),
133+
chunk_name,
134+
));
135+
}
136+
Expr::Tpl(tpl) if tpl.quasis.len() == 1 => {
137+
let chunk_name = self.try_extract_webpack_chunk_name(&tpl.span);
138+
let request = JsWord::from(
139+
tpl
140+
.quasis
141+
.first()
142+
.expect("should have one quasis")
143+
.raw
144+
.to_string(),
145+
);
146+
self.add_dependency(box EsmDynamicImportDependency::new(
147+
request,
148+
Some(node.span.into()),
149+
as_parent_path(ast_path),
150+
chunk_name,
151+
));
152+
}
153+
_ => {
154+
if let Some((context, reg)) = scanner_context_module(dyn_imported.expr.as_ref()) {
155+
self.add_dependency(box ImportContextDependency::new(
156+
ContextOptions {
157+
mode: ContextMode::Lazy,
158+
recursive: true,
159+
reg_exp: RspackRegex::new(&reg).expect("reg failed"),
160+
reg_str: reg,
161+
include: None,
162+
exclude: None,
163+
category: DependencyCategory::Esm,
164+
request: context,
165+
},
166+
Some(node.span.into()),
167+
as_parent_path(ast_path),
168+
));
169+
}
170+
}
150171
}
151172
}
152173
}

0 commit comments

Comments
 (0)