Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 26282e0

Browse files
fix: remove __toCommonJS if it is plain require (#2547)
<!-- Thank you for contributing! --> ### Description 1. esbuild will not call (init_xxx(), __toCommonJS(xxx_exports))` if the require is just a plain `require` call expr https://hyrious.me/esbuild-repl/?version=0.23.0&b=e%00entry.js%00%2F%2F+MULTIPLE+ENTRY+MODULES%0Arequire%28%27.%2Fhyper-cube.js%27%29%0A&b=%00hyper-cube.js%00%0A%2F%2F+This+is+only+imported+by+one+entry+module+and%0A%2F%2F+shares+a+chunk+with+that+module%0Aexport+default+function+hyperCube%28x%29+%7B%0A%09return+cube%28x%29+*+x%3B%0A%7D%0A&o=%7B%0A++treeShaking%3A+true%2C%0A++bundle%3A+true%2C%0A%7D <!-- Please insert your description here and provide especially info about the "what" this PR is solving -->
1 parent 440daed commit 26282e0

40 files changed

Lines changed: 350 additions & 256 deletions

File tree

crates/rolldown/src/ast_scanner/impl_visit.rs

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use oxc::{
22
ast::{
33
ast::{self, Expression, IdentifierReference, MemberExpression},
44
visit::walk,
5-
Visit,
5+
AstKind, Visit,
66
},
77
span::{GetSpan, Span},
88
};
@@ -15,7 +15,15 @@ use crate::utils::call_expression_ext::CallExpressionExt;
1515

1616
use super::{side_effect_detector::SideEffectDetector, AstScanner};
1717

18-
impl<'me, 'ast> Visit<'ast> for AstScanner<'me> {
18+
impl<'me, 'ast: 'me> Visit<'ast> for AstScanner<'me, 'ast> {
19+
fn enter_node(&mut self, kind: oxc::ast::AstKind<'ast>) {
20+
self.visit_path.push(kind);
21+
}
22+
23+
fn leave_node(&mut self, _: oxc::ast::AstKind<'ast>) {
24+
self.visit_path.pop();
25+
}
26+
1927
fn visit_program(&mut self, program: &ast::Program<'ast>) {
2028
for (idx, stmt) in program.body.iter().enumerate() {
2129
self.current_stmt_info.stmt_idx = Some(idx);
@@ -124,6 +132,7 @@ impl<'me, 'ast> Visit<'ast> for AstScanner<'me> {
124132
}
125133
walk::walk_declaration(self, it);
126134
}
135+
127136
fn visit_assignment_expression(&mut self, node: &ast::AssignmentExpression<'ast>) {
128137
match &node.left {
129138
ast::AssignmentTarget::AssignmentTargetIdentifier(id_ref) => {
@@ -184,7 +193,39 @@ impl<'me, 'ast> Visit<'ast> for AstScanner<'me> {
184193
if request.span().is_empty() {
185194
ImportRecordMeta::IS_UNSPANNED_IMPORT
186195
} else {
187-
ImportRecordMeta::empty()
196+
let mut is_require_used = true;
197+
let mut meta = ImportRecordMeta::empty();
198+
// traverse nearest ExpressionStatement and check if there are potential used
199+
for ancestor in self.visit_path.iter().rev() {
200+
match ancestor {
201+
AstKind::ParenthesizedExpression(_) => {}
202+
AstKind::ExpressionStatement(_) => {
203+
meta.insert(ImportRecordMeta::IS_REQUIRE_UNUSED);
204+
break;
205+
}
206+
AstKind::SequenceExpression(seq_expr) => {
207+
// the child node has require and it is potential used
208+
// the state may changed according to the child node position
209+
// 1. `1, 2, (1, require('a'))` => since the last child contains `require`, and
210+
// in the last position, it is still used if it meant any other astKind
211+
// 2. `1, 2, (1, require('a')), 1` => since the last child contains `require`, but it is
212+
// not in the last position, the state should change to unused
213+
let last = seq_expr.expressions.last().expect("should have at least one child");
214+
215+
if !last.span().is_empty() && !expr.span.is_empty() {
216+
is_require_used = last.span().contains_inclusive(expr.span);
217+
} else {
218+
is_require_used = true;
219+
}
220+
}
221+
_ => {
222+
if is_require_used {
223+
break;
224+
}
225+
}
226+
}
227+
}
228+
meta
188229
},
189230
);
190231
self.result.imports.insert(expr.span, id);
@@ -195,9 +236,9 @@ impl<'me, 'ast> Visit<'ast> for AstScanner<'me> {
195236
}
196237
}
197238

198-
impl<'me> AstScanner<'me> {
239+
impl<'me, 'ast: 'me> AstScanner<'me, 'ast> {
199240
/// visit `Class` of declaration
200-
pub fn scan_class_declaration(&mut self, class: &ast::Class<'_>) {
241+
pub fn scan_class_declaration(&mut self, class: &ast::Class<'ast>) {
201242
let Some(id) = class.id.as_ref() else {
202243
return;
203244
};

crates/rolldown/src/ast_scanner/mod.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ pub mod impl_visit;
22
pub mod side_effect_detector;
33

44
use arcstr::ArcStr;
5-
use oxc::ast::ast;
5+
use oxc::ast::{ast, AstKind};
66
use oxc::index::IndexVec;
77
use oxc::semantic::{Reference, ReferenceId, SymbolTable};
88
use oxc::{
@@ -51,7 +51,7 @@ pub struct ScanResult {
5151
pub has_star_exports: bool,
5252
}
5353

54-
pub struct AstScanner<'me> {
54+
pub struct AstScanner<'me, 'ast> {
5555
idx: ModuleIdx,
5656
source: &'me ArcStr,
5757
module_type: ModuleDefFormat,
@@ -74,9 +74,10 @@ pub struct AstScanner<'me> {
7474
/// lhs of AssignmentExpression
7575
ast_usage: EcmaModuleAstUsage,
7676
cur_class_decl_and_symbol_referenced_ids: Option<(SymbolId, &'me Vec<ReferenceId>)>,
77+
visit_path: Vec<AstKind<'ast>>,
7778
}
7879

79-
impl<'me> AstScanner<'me> {
80+
impl<'me, 'ast: 'me> AstScanner<'me, 'ast> {
8081
#[allow(clippy::too_many_arguments)]
8182
pub fn new(
8283
idx: ModuleIdx,
@@ -135,10 +136,11 @@ impl<'me> AstScanner<'me> {
135136
comments,
136137
ast_usage: EcmaModuleAstUsage::empty(),
137138
cur_class_decl_and_symbol_referenced_ids: None,
139+
visit_path: vec![],
138140
}
139141
}
140142

141-
pub fn scan(mut self, program: &Program<'_>) -> BuildResult<ScanResult> {
143+
pub fn scan(mut self, program: &Program<'ast>) -> BuildResult<ScanResult> {
142144
self.visit_program(program);
143145
let mut exports_kind = ExportsKind::None;
144146

@@ -549,7 +551,7 @@ impl<'me> AstScanner<'me> {
549551
}
550552
});
551553
}
552-
fn scan_module_decl(&mut self, decl: &ModuleDeclaration) {
554+
fn scan_module_decl(&mut self, decl: &ModuleDeclaration<'ast>) {
553555
match decl {
554556
ast::ModuleDeclaration::ImportDeclaration(decl) => {
555557
self.esm_import_keyword.get_or_insert(Span::new(decl.span.start, decl.span.start + 6));

crates/rolldown/src/module_finalizers/scope_hoisting/impl_visit_mut.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -414,12 +414,16 @@ impl<'me, 'ast> VisitMut<'ast> for ScopeHoistingFinalizer<'me, 'ast> {
414414
if matches!(importee.exports_kind, ExportsKind::CommonJs) {
415415
*expr = self.snippet.call_expr_expr(wrap_ref_name);
416416
} else {
417-
let ns_name = self.canonical_name_for(importee.namespace_object_ref);
418-
let to_commonjs_ref_name = self.canonical_name_for_runtime("__toCommonJS");
419-
*expr = self.snippet.seq2_in_paren_expr(
420-
self.snippet.call_expr_expr(wrap_ref_name),
421-
self.snippet.call_expr_with_arg_expr(to_commonjs_ref_name, ns_name),
422-
);
417+
if rec.meta.contains(ImportRecordMeta::IS_REQUIRE_UNUSED) {
418+
*expr = self.snippet.call_expr_expr(wrap_ref_name);
419+
} else {
420+
let ns_name = self.canonical_name_for(importee.namespace_object_ref);
421+
let to_commonjs_ref_name = self.canonical_name_for_runtime("__toCommonJS");
422+
*expr = self.snippet.seq2_in_paren_expr(
423+
self.snippet.call_expr_expr(wrap_ref_name),
424+
self.snippet.call_expr_with_arg_expr(to_commonjs_ref_name, ns_name),
425+
);
426+
}
423427
}
424428
}
425429
}

crates/rolldown/src/stages/link_stage/mod.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -413,15 +413,18 @@ impl<'a> LinkStage<'a> {
413413
.push(importee_linking_info.wrapper_ref.unwrap().into());
414414
}
415415
WrapKind::Esm => {
416-
// something like `(init_foo(), toCommonJS(foo_exports))`
417-
// Reference to `init_foo`
416+
// convert require record into `(init_foo(), __toCommonJS(foo_exports))` if
417+
// `require('xxx)` is used, else convert it to `init_foo()`
418418
stmt_info
419419
.referenced_symbols
420420
.push(importee_linking_info.wrapper_ref.unwrap().into());
421-
stmt_info
422-
.referenced_symbols
423-
.push(self.runtime.resolve_symbol("__toCommonJS").into());
424421
stmt_info.referenced_symbols.push(importee.namespace_object_ref.into());
422+
423+
if !rec.meta.contains(ImportRecordMeta::IS_REQUIRE_UNUSED) {
424+
stmt_info
425+
.referenced_symbols
426+
.push(self.runtime.resolve_symbol("__toCommonJS").into());
427+
}
425428
}
426429
},
427430
ImportKind::DynamicImport => {

crates/rolldown/tests/esbuild/dce/package_json_side_effects_array_keep_main_implicit_main/artifacts.snap

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
source: crates/rolldown_testing/src/integration_test.rs
3+
snapshot_kind: text
34
---
45
# Assets
56

@@ -19,7 +20,7 @@ var init_index_module = __esm({ "node_modules/demo-pkg/index-module.js"() {
1920
2021
//#endregion
2122
//#region src/require-demo-pkg.js
22-
init_index_module(), __toCommonJS(index_module_exports);
23+
init_index_module();
2324
2425
//#endregion
2526
//#region src/entry.js

crates/rolldown/tests/esbuild/dce/package_json_side_effects_array_keep_main_implicit_main/diff.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ var init_index_module = __esm({ "node_modules/demo-pkg/index-module.js"() {
4141

4242
//#endregion
4343
//#region src/require-demo-pkg.js
44-
init_index_module(), __toCommonJS(index_module_exports);
44+
init_index_module();
4545

4646
//#endregion
4747
//#region src/entry.js
@@ -74,7 +74,7 @@ console.log("unused import");
7474
});
7575
-init_index_main();
7676
-init_index_main();
77-
+(init_index_module(), __toCommonJS(index_module_exports));
77+
+init_index_module();
7878
+init_index_module();
7979
console.log("unused import");
8080

crates/rolldown/tests/esbuild/dce/package_json_side_effects_array_keep_module_implicit_main/artifacts.snap

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
source: crates/rolldown_testing/src/integration_test.rs
3+
snapshot_kind: text
34
---
45
# Assets
56

@@ -19,7 +20,7 @@ var init_index_module = __esm({ "node_modules/demo-pkg/index-module.js"() {
1920
2021
//#endregion
2122
//#region src/require-demo-pkg.js
22-
init_index_module(), __toCommonJS(index_module_exports);
23+
init_index_module();
2324
2425
//#endregion
2526
//#region src/entry.js

crates/rolldown/tests/esbuild/dce/package_json_side_effects_array_keep_module_implicit_main/diff.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ var init_index_module = __esm({ "node_modules/demo-pkg/index-module.js"() {
3838

3939
//#endregion
4040
//#region src/require-demo-pkg.js
41-
init_index_module(), __toCommonJS(index_module_exports);
41+
init_index_module();
4242

4343
//#endregion
4444
//#region src/entry.js
@@ -70,7 +70,7 @@ console.log("unused import");
7070
}
7171
});
7272
-init_index_main();
73-
+(init_index_module(), __toCommonJS(index_module_exports));
73+
+init_index_module();
7474
+init_index_module();
7575
console.log("unused import");
7676

crates/rolldown/tests/esbuild/dce/package_json_side_effects_false_keep_bare_import_and_require_es6/artifacts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ var init_demo_pkg = __esm({ "node_modules/demo-pkg/index.js"() {
2020
2121
//#endregion
2222
//#region src/entry.js
23-
init_demo_pkg(), __toCommonJS(demo_pkg_exports);
23+
init_demo_pkg();
2424
console.log("unused import");
2525
2626
//#endregion

crates/rolldown/tests/esbuild/dce/package_json_side_effects_false_keep_bare_import_and_require_es6/diff.md renamed to crates/rolldown/tests/esbuild/dce/package_json_side_effects_false_keep_bare_import_and_require_es6/bypass.md

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Reason
2-
1. double module initialization
2+
1. different file system
33
# Diff
44
## /out.js
55
### esbuild
@@ -36,7 +36,7 @@ var init_demo_pkg = __esm({ "node_modules/demo-pkg/index.js"() {
3636

3737
//#endregion
3838
//#region src/entry.js
39-
init_demo_pkg(), __toCommonJS(demo_pkg_exports);
39+
init_demo_pkg();
4040
console.log("unused import");
4141

4242
//#endregion
@@ -46,7 +46,7 @@ console.log("unused import");
4646
===================================================================
4747
--- esbuild /out.js
4848
+++ rolldown src_entry.js
49-
@@ -3,11 +3,11 @@
49+
@@ -3,9 +3,9 @@
5050
foo: () => foo
5151
});
5252
var foo;
@@ -57,8 +57,5 @@ console.log("unused import");
5757
console.log("hello");
5858
}
5959
});
60-
-init_demo_pkg();
61-
+(init_demo_pkg(), __toCommonJS(demo_pkg_exports));
62-
console.log("unused import");
6360

6461
```

0 commit comments

Comments
 (0)