Skip to content

Commit 34fbd33

Browse files
authored
feat: if no commonjs specific syntax detected, treat module as esm (#12008)
1 parent 7ab3987 commit 34fbd33

File tree

11 files changed

+94
-268
lines changed

11 files changed

+94
-268
lines changed

crates/rspack_core/src/init_fragment.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,9 @@ impl InitFragmentKey {
140140
| InitFragmentKey::ModuleExternal(_)
141141
| InitFragmentKey::ModuleDecorator(_)
142142
| InitFragmentKey::CommonJsExports(_)
143+
| InitFragmentKey::ESMCompatibility
143144
| InitFragmentKey::Const(_) => first(fragments),
144-
InitFragmentKey::ESMCompatibility | InitFragmentKey::Unique(_) => {
145+
InitFragmentKey::Unique(_) => {
145146
debug_assert!(fragments.len() == 1, "fragment = {self:?}");
146147
first(fragments)
147148
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use rspack_core::DependencyType;
2+
use rspack_plugin_javascript::{
3+
JavascriptParserPlugin, dependency::ESMCompatibilityDependency, visitors::JavascriptParser,
4+
};
5+
pub struct EsmLibParserPlugin;
6+
7+
impl JavascriptParserPlugin for EsmLibParserPlugin {
8+
fn finish(&self, parser: &mut JavascriptParser) -> Option<bool> {
9+
if parser.module_type.is_js_auto()
10+
&& matches!(
11+
parser.build_meta.exports_type,
12+
rspack_core::BuildMetaExportsType::Unset
13+
)
14+
&& !parser.get_dependencies().iter().any(|dep| {
15+
matches!(
16+
dep.dependency_type(),
17+
DependencyType::CjsExportRequire
18+
| DependencyType::CjsExports
19+
| DependencyType::CjsFullRequire
20+
| DependencyType::CjsRequire
21+
| DependencyType::CjsSelfReference
22+
| DependencyType::CommonJSRequireContext
23+
)
24+
})
25+
{
26+
parser.build_meta.exports_type = rspack_core::BuildMetaExportsType::Namespace;
27+
parser.add_presentational_dependency(Box::new(ESMCompatibilityDependency));
28+
}
29+
30+
None
31+
}
32+
}

crates/rspack_plugin_esm_library/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod chunk_link;
22
mod dependency;
3+
mod esm_lib_parser_plugin;
34
mod link;
45
mod plugin;
56
mod preserve_modules;

crates/rspack_plugin_esm_library/src/link.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,10 +1724,6 @@ impl EsmLibraryPlugin {
17241724
continue;
17251725
};
17261726

1727-
if !matches!(dep.dependency_type(), DependencyType::EsmImport) {
1728-
continue;
1729-
}
1730-
17311727
let Some(conn) = module_graph.connection_by_dependency_id(dep_id) else {
17321728
continue;
17331729
};
@@ -1744,6 +1740,10 @@ impl EsmLibraryPlugin {
17441740
//ensure chunk
17451741
chunk_imports.entry(ref_module).or_default();
17461742

1743+
if !matches!(dep.dependency_type(), DependencyType::EsmImport) {
1744+
continue;
1745+
}
1746+
17471747
if outgoing_module_info.is_external() {
17481748
if ChunkGraph::get_module_id(&compilation.module_ids_artifact, ref_module).is_none() {
17491749
// if module don't contains id, it no need to be required

crates/rspack_plugin_esm_library/src/plugin.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,25 @@ use rspack_core::{
1111
CompilationFinishModules, CompilationOptimizeChunks, CompilationParams, CompilationProcessAssets,
1212
CompilationRuntimeRequirementInTree, CompilerCompilation, ConcatenatedModuleInfo,
1313
ConcatenationScope, DependencyType, ExportsInfoGetter, ExternalModuleInfo, Logger, ModuleGraph,
14-
ModuleIdentifier, ModuleInfo, Plugin, PrefetchExportsInfoMode, RuntimeCondition, RuntimeGlobals,
15-
get_target, is_esm_dep_like,
14+
ModuleIdentifier, ModuleInfo, ModuleType, NormalModuleFactoryParser, ParserAndGenerator,
15+
ParserOptions, Plugin, PrefetchExportsInfoMode, RuntimeCondition, RuntimeGlobals, get_target,
16+
is_esm_dep_like,
1617
rspack_sources::{ReplaceSource, Source},
1718
};
1819
use rspack_error::Result;
1920
use rspack_hook::{plugin, plugin_hook};
2021
use rspack_plugin_javascript::{
21-
JavascriptModulesRenderChunkContent, JsPlugin, RenderSource, dependency::ImportDependencyTemplate,
22+
JavascriptModulesRenderChunkContent, JsPlugin, RenderSource,
23+
dependency::ImportDependencyTemplate, parser_and_generator::JavaScriptParserAndGenerator,
2224
};
2325
use rspack_util::fx_hash::FxHashMap;
2426
use sugar_path::SugarPath;
2527
use tokio::sync::{Mutex, RwLock};
2628

2729
use crate::{
2830
chunk_link::ChunkLinkContext, dependency::dyn_import::DynamicImportDependencyTemplate,
29-
preserve_modules::preserve_modules, runtime::RegisterModuleRuntime,
31+
esm_lib_parser_plugin::EsmLibParserPlugin, preserve_modules::preserve_modules,
32+
runtime::RegisterModuleRuntime,
3033
};
3134

3235
pub static CONCATENATED_MODULES_MAP_FOR_CODEGEN: LazyLock<
@@ -120,10 +123,13 @@ async fn finish_modules(&self, compilation: &mut Compilation) -> Result<()> {
120123
} else if ModuleGraph::is_async(compilation, module_identifier) {
121124
logger.debug(format!("module {module_identifier} is an async module"));
122125
should_scope_hoisting = false;
123-
} else if !module.build_info().strict {
124-
logger.debug(format!("module {module_identifier} is not strict module"));
125-
should_scope_hoisting = false;
126-
} else if module_graph
126+
}
127+
// TODO: support config to disable scope hoisting for non strict module
128+
// else if !module.build_info().strict {
129+
// logger.debug(format!("module {module_identifier} is not strict module"));
130+
// should_scope_hoisting = false;
131+
// }
132+
else if module_graph
127133
.get_incoming_connections(module_identifier)
128134
.filter_map(|conn| module_graph.dependency_by_id(&conn.dependency_id))
129135
.any(|dep| {
@@ -483,6 +489,21 @@ async fn optimize_chunks(&self, compilation: &mut Compilation) -> Result<Option<
483489
Ok(None)
484490
}
485491

492+
#[plugin_hook(NormalModuleFactoryParser for EsmLibraryPlugin)]
493+
async fn parse(
494+
&self,
495+
module_type: &ModuleType,
496+
parser: &mut dyn ParserAndGenerator,
497+
_parser_options: Option<&ParserOptions>,
498+
) -> Result<()> {
499+
if module_type.is_js_like()
500+
&& let Some(parser) = parser.downcast_mut::<JavaScriptParserAndGenerator>()
501+
{
502+
parser.add_parser_plugin(Box::new(EsmLibParserPlugin {}));
503+
}
504+
Ok(())
505+
}
506+
486507
impl Plugin for EsmLibraryPlugin {
487508
fn apply(&self, ctx: &mut ApplyContext) -> Result<()> {
488509
ctx.compiler_hooks.compilation.tap(compilation::new(self));
@@ -524,6 +545,8 @@ impl Plugin for EsmLibraryPlugin {
524545
.optimize_chunks
525546
.tap(optimize_chunks::new(self));
526547

548+
ctx.normal_module_factory_hooks.parser.tap(parse::new(self));
549+
527550
Ok(())
528551
}
529552
}

crates/rspack_plugin_javascript/src/visitors/dependency/parser/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ pub struct JavascriptParser<'parser> {
326326
pub resource_data: &'parser ResourceData,
327327
pub(crate) compiler_options: &'parser CompilerOptions,
328328
pub(crate) javascript_options: &'parser JavascriptParserOptions,
329-
pub(crate) module_type: &'parser ModuleType,
329+
pub module_type: &'parser ModuleType,
330330
pub(crate) module_layer: Option<&'parser ModuleLayer>,
331331
pub module_identifier: &'parser ModuleIdentifier,
332332
pub(crate) plugin_drive: Rc<JavaScriptParserPluginDrive>,
@@ -566,6 +566,10 @@ impl<'parser> JavascriptParser<'parser> {
566566
self.dependencies.len()
567567
}
568568

569+
pub fn get_dependencies(&self) -> &[BoxDependency] {
570+
&self.dependencies
571+
}
572+
569573
pub fn get_dependency_mut(&mut self, idx: usize) -> Option<&mut BoxDependency> {
570574
self.dependencies.get_mut(idx)
571575
}
Lines changed: 5 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,18 @@
11
```mjs title=dynamic_js.mjs
2-
import { __webpack_require__ } from "./runtime.mjs";
3-
__webpack_require__.add({
4-
"./dynamic.js":
5-
/*!********************!*\
6-
!*** ./dynamic.js ***!
7-
\********************/
8-
(function (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
9-
__webpack_require__.d(__webpack_exports__, {
10-
"default": () => (__WEBPACK_DEFAULT_EXPORT__)
11-
});
12-
/* ESM default export */ const __WEBPACK_DEFAULT_EXPORT__ = (42);
2+
// ./dynamic.js
3+
/* ESM default export */ const dynamic = (42);
134

14-
15-
}),
16-
});
5+
export { dynamic };
176

187
```
198

209
```mjs title=main.mjs
21-
import { __webpack_require__ } from "./runtime.mjs";
22-
__webpack_require__.add({
23-
"./index.js":
24-
/*!******************!*\
25-
!*** ./index.js ***!
26-
\******************/
27-
(function (__unused_webpack_module, __unused_webpack_exports, __webpack_require__) {
10+
// ./index.js
2811
it('should support dynamic import', async () => {
29-
const value = await import("./dynamic_js.mjs").then(__webpack_require__.bind(__webpack_require__, /*! ./dynamic */ "./dynamic.js"))
12+
const value = await import("./dynamic_js.mjs").then((mod) => ({ default: mod.dynamic }))
3013
expect(value.default).toBe(42)
3114
})
3215

3316

3417

35-
}),
36-
});
37-
const index = __webpack_require__("./index.js");
38-
39-
40-
```
41-
42-
```mjs title=runtime.mjs
43-
44-
var __webpack_modules__ = {};
45-
// The module cache
46-
var __webpack_module_cache__ = {};
47-
// The require function
48-
function __webpack_require__(moduleId) {
49-
// Check if module is in cache
50-
var cachedModule = __webpack_module_cache__[moduleId];
51-
if (cachedModule !== undefined) {
52-
return cachedModule.exports;
53-
}
54-
// Create a new module (and put it into the cache)
55-
var module = (__webpack_module_cache__[moduleId] = {
56-
exports: {}
57-
});
58-
// Execute the module function
59-
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
60-
61-
// Return the exports of the module
62-
return module.exports;
63-
}
64-
// expose the modules object (__webpack_modules__)
65-
__webpack_require__.m = __webpack_modules__;
66-
67-
// esm library register module runtime
68-
(() => {
69-
__webpack_require__.add = function registerModules(modules) { Object.assign(__webpack_require__.m, modules) }
70-
71-
})();
72-
// webpack/runtime/define_property_getters
73-
(() => {
74-
__webpack_require__.d = (exports, definition) => {
75-
for(var key in definition) {
76-
if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
77-
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
78-
}
79-
}
80-
};
81-
})();
82-
// webpack/runtime/has_own_property
83-
(() => {
84-
__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
85-
})();
86-
87-
export { __webpack_require__ };
88-
8918
```
Lines changed: 2 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,11 @@
11
```mjs title=main.mjs
2-
import { __webpack_require__ } from "./runtime.mjs";
3-
__webpack_require__.add({
4-
"./bar.js":
5-
/*!****************!*\
6-
!*** ./bar.js ***!
7-
\****************/
8-
(function () {
2+
// ./bar.js
93
globalThis.bar = 'bar';
104

11-
12-
}),
13-
"./foo.js":
14-
/*!****************!*\
15-
!*** ./foo.js ***!
16-
\****************/
17-
(function () {
5+
// ./foo.js
186
globalThis.foo = 'foo';
197

20-
21-
}),
22-
});
238
// ./index.js
24-
__webpack_require__("./bar.js");
25-
__webpack_require__("./foo.js");
269

2710

2811

@@ -32,39 +15,4 @@ it('should have correct import', () => {
3215
})
3316

3417

35-
```
36-
37-
```mjs title=runtime.mjs
38-
39-
var __webpack_modules__ = {};
40-
// The module cache
41-
var __webpack_module_cache__ = {};
42-
// The require function
43-
function __webpack_require__(moduleId) {
44-
// Check if module is in cache
45-
var cachedModule = __webpack_module_cache__[moduleId];
46-
if (cachedModule !== undefined) {
47-
return cachedModule.exports;
48-
}
49-
// Create a new module (and put it into the cache)
50-
var module = (__webpack_module_cache__[moduleId] = {
51-
exports: {}
52-
});
53-
// Execute the module function
54-
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
55-
56-
// Return the exports of the module
57-
return module.exports;
58-
}
59-
// expose the modules object (__webpack_modules__)
60-
__webpack_require__.m = __webpack_modules__;
61-
62-
// esm library register module runtime
63-
(() => {
64-
__webpack_require__.add = function registerModules(modules) { Object.assign(__webpack_require__.m, modules) }
65-
66-
})();
67-
68-
export { __webpack_require__ };
69-
7018
```

tests/rspack-test/esmOutputCases/new-url/keep-new-url/__snapshots__/esm.snap.txt

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,6 @@
55
```mjs title=main.mjs
66
import { __webpack_require__ } from "./runtime.mjs";
77
__webpack_require__.add({
8-
"./index.js":
9-
/*!******************!*\
10-
!*** ./index.js ***!
11-
\******************/
12-
(function (__unused_webpack_module, __unused_webpack_exports, __webpack_require__) {
13-
14-
function neverUse() {
15-
new URL("./ef46db3751d8e999.js", import.meta.url)(function foo() {})();
16-
}
17-
neverUse.bind(neverUse)
18-
19-
20-
}),
218
"./asset.js":
229
/*!******************!*\
2310
!*** ./asset.js ***!
@@ -27,7 +14,12 @@ module.exports = __webpack_require__.p + "ef46db3751d8e999.js";
2714

2815
}),
2916
});
30-
const index = __webpack_require__("./index.js");
17+
// ./index.js
18+
19+
function neverUse() {
20+
new URL("./ef46db3751d8e999.js", import.meta.url)(function foo() {})();
21+
}
22+
neverUse.bind(neverUse)
3123

3224

3325
```

0 commit comments

Comments
 (0)