Skip to content

Commit bf75981

Browse files
authored
fix(es/modules): Improve compatibility with cjs-module-lexer (#5835)
1 parent cae4173 commit bf75981

File tree

32 files changed

+335
-75
lines changed

32 files changed

+335
-75
lines changed

crates/swc/tests/fixture/interop/node/output/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
Object.defineProperty(exports, "__esModule", {
33
value: true
44
});
5-
exports.default = exports.y = exports.x = void 0;
5+
0 && (module.exports = {
6+
x: _,
7+
y: _,
8+
default: _
9+
});
610
function _export(target, all) {
711
for(var name in all)Object.defineProperty(target, name, {
812
enumerable: true,

crates/swc_ecma_transforms_module/src/common_js.rs

Lines changed: 116 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use swc_common::{
66
use swc_ecma_ast::*;
77
use swc_ecma_transforms_base::{feature::FeatureFlag, helper_expr};
88
use swc_ecma_utils::{
9-
member_expr, private_ident, quote_ident, undefined, ExprFactory, FunctionFactory, IsDirective,
9+
member_expr, private_ident, quote_ident, ExprFactory, FunctionFactory, IsDirective,
1010
};
1111
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
1212

@@ -254,11 +254,18 @@ where
254254
is_export_assign: bool,
255255
) -> impl Iterator<Item = Stmt> {
256256
let import_interop = self.config.import_interop();
257+
let is_node = import_interop.is_node();
257258

258259
let mut stmts = Vec::with_capacity(link.len());
259260

260261
let mut export_obj_prop_list = export.into_iter().map(Into::into).collect();
261262

263+
let lexer_reexport = if is_node {
264+
self.emit_lexer_ts_reexport(&link)
265+
} else {
266+
None
267+
};
268+
262269
link.into_iter().for_each(
263270
|(src, LinkItem(src_span, link_specifier_set, mut link_flag))| {
264271
// Optimize for `@swc/helpers`:
@@ -277,7 +284,7 @@ where
277284
let is_swc_default_helper =
278285
!link_flag.has_named() && src.starts_with("@swc/helpers/");
279286

280-
let is_node_default = !link_flag.has_named() && import_interop.is_node();
287+
let is_node_default = !link_flag.has_named() && is_node;
281288

282289
if import_interop.is_none() || is_swc_default_helper {
283290
link_flag -= LinkFlag::NAMESPACE;
@@ -371,16 +378,24 @@ where
371378
if !export_obj_prop_list.is_empty() && !is_export_assign {
372379
export_obj_prop_list.sort_by_key(|prop| prop.span());
373380

374-
if import_interop.is_node() {
375-
export_stmts = self.emit_lexer_exports_init(&export_obj_prop_list);
376-
}
377-
378-
let features = self.available_features;
381+
let mut features = self.available_features;
379382
let exports = self.exports();
380383

384+
if is_node {
385+
if export_obj_prop_list.len() > 1 {
386+
export_stmts.extend(self.emit_lexer_exports_init(&export_obj_prop_list));
387+
} else {
388+
// `cjs-module-lexer` does not support `get: ()=> foo`
389+
// see https://github.com/nodejs/cjs-module-lexer/pull/74
390+
features -= FeatureFlag::ArrowFunctions;
391+
}
392+
}
393+
381394
export_stmts.extend(emit_export_stmts(features, exports, export_obj_prop_list));
382395
}
383396

397+
export_stmts.extend(lexer_reexport);
398+
384399
export_stmts.into_iter().chain(stmts)
385400
}
386401

@@ -445,29 +460,104 @@ where
445460

446461
/// emit [cjs-module-lexer](https://github.com/nodejs/cjs-module-lexer) friendly exports list
447462
/// ```javascript
448-
/// exports.foo = exports.bar = void 0;
463+
/// 0 && (exports.foo = 0);
464+
/// 0 && (module.exports = { foo: _, bar: _ });
449465
/// ```
450-
fn emit_lexer_exports_init(&mut self, export_id_list: &[ObjPropKeyIdent]) -> Vec<Stmt> {
451-
export_id_list
452-
.chunks(100)
453-
.map(|group| {
454-
let mut expr = *undefined(DUMMY_SP);
455-
456-
for key_value in group {
457-
let prop = prop_name(key_value.key(), DUMMY_SP).into();
458-
459-
let export_binding = MemberExpr {
460-
obj: Box::new(self.exports().into()),
461-
span: key_value.span(),
462-
prop,
463-
};
466+
fn emit_lexer_exports_init(&mut self, export_id_list: &[ObjPropKeyIdent]) -> Option<Stmt> {
467+
match export_id_list.len() {
468+
0 => None,
469+
1 => {
470+
let expr: Expr = 0.into();
471+
472+
let key_value = &export_id_list[0];
473+
let prop = prop_name(key_value.key(), DUMMY_SP).into();
474+
let export_binding = MemberExpr {
475+
obj: Box::new(self.exports().into()),
476+
span: key_value.span(),
477+
prop,
478+
};
479+
let expr = expr.make_assign_to(op!("="), export_binding.as_pat_or_expr());
480+
let expr = BinExpr {
481+
span: DUMMY_SP,
482+
op: op!("&&"),
483+
left: 0.into(),
484+
right: Box::new(expr),
485+
};
464486

465-
expr = expr.make_assign_to(op!("="), export_binding.as_pat_or_expr());
487+
Some(expr.into_stmt())
488+
}
489+
_ => {
490+
let props = export_id_list
491+
.iter()
492+
.map(|key_value| prop_name(key_value.key(), DUMMY_SP))
493+
.map(|key| KeyValueProp {
494+
key: key.into(),
495+
// `cjs-module-lexer` only support identifier as value
496+
value: quote_ident!("_").into(),
497+
})
498+
.map(Prop::KeyValue)
499+
.map(Box::new)
500+
.map(PropOrSpread::Prop)
501+
.collect();
502+
503+
let module_exports_assign = ObjectLit {
504+
span: DUMMY_SP,
505+
props,
466506
}
507+
.make_assign_to(
508+
op!("="),
509+
member_expr!(DUMMY_SP.apply_mark(self.unresolved_mark), module.exports).into(),
510+
);
467511

468-
expr.into_stmt()
469-
})
470-
.collect()
512+
let expr = BinExpr {
513+
span: DUMMY_SP,
514+
op: op!("&&"),
515+
left: 0.into(),
516+
right: Box::new(module_exports_assign),
517+
};
518+
519+
Some(expr.into_stmt())
520+
}
521+
}
522+
}
523+
524+
/// emit [cjs-module-lexer](https://github.com/nodejs/cjs-module-lexer) friendly exports list
525+
/// ```javascript
526+
/// 0 && (__export(require("foo")));
527+
/// ```
528+
fn emit_lexer_ts_reexport(&self, link: &Link) -> Option<Stmt> {
529+
let mut seq_list = vec![];
530+
link.iter().for_each(|(src, LinkItem(_, _, link_flag))| {
531+
if link_flag.export_star() {
532+
let import_expr =
533+
self.resolver
534+
.make_require_call(self.unresolved_mark, src.clone(), DUMMY_SP);
535+
536+
let export = Expr::Ident(quote_ident!("__export"))
537+
.as_call(DUMMY_SP, vec![import_expr.as_arg()]);
538+
539+
seq_list.push(Box::new(export));
540+
}
541+
});
542+
543+
if seq_list.is_empty() {
544+
None
545+
} else {
546+
let seq_expr = SeqExpr {
547+
span: DUMMY_SP,
548+
exprs: seq_list,
549+
}
550+
.into();
551+
552+
let expr = BinExpr {
553+
span: DUMMY_SP,
554+
op: op!("&&"),
555+
left: 0.into(),
556+
right: seq_expr,
557+
};
558+
559+
Some(expr.into_stmt())
560+
}
471561
}
472562

473563
fn pure_span(&self) -> Span {

crates/swc_ecma_transforms_module/tests/fixture/common/interop-node/export-all/output.cjs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
Object.defineProperty(exports, "__esModule", {
66
value: true
77
});
8-
exports.default = void 0;
98
Object.defineProperty(exports, "default", {
109
enumerable: true,
11-
get: ()=>_default
10+
get () {
11+
return _default;
12+
}
1213
});
14+
0 && __export(require("react"));
1315
const _react = _exportStar(require("react"), exports);
1416
const _default = _react;

crates/swc_ecma_transforms_module/tests/fixture/common/interop-node/export-default-10/output.cjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
Object.defineProperty(exports, "__esModule", {
33
value: true
44
});
5-
exports.default = void 0;
65
Object.defineProperty(exports, "default", {
76
enumerable: true,
8-
get: ()=>_default
7+
get () {
8+
return _default;
9+
}
910
});
1011
const _default = function() {
1112
return "foo";

crates/swc_ecma_transforms_module/tests/fixture/common/interop-node/export-default-11/output.cjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
Object.defineProperty(exports, "__esModule", {
33
value: true
44
});
5-
exports.Cachier = exports.default = void 0;
5+
0 && (module.exports = {
6+
default: _,
7+
Cachier: _
8+
});
69
function _export(target, all) {
710
for(var name in all)Object.defineProperty(target, name, {
811
enumerable: true,

crates/swc_ecma_transforms_module/tests/fixture/common/interop-node/export-default-2/output.cjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
Object.defineProperty(exports, "__esModule", {
33
value: true
44
});
5-
exports.default = void 0;
65
Object.defineProperty(exports, "default", {
76
enumerable: true,
8-
get: ()=>_default
7+
get () {
8+
return _default;
9+
}
910
});
1011
const _default = {};

crates/swc_ecma_transforms_module/tests/fixture/common/interop-node/export-default-3/output.cjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
Object.defineProperty(exports, "__esModule", {
33
value: true
44
});
5-
exports.default = void 0;
65
Object.defineProperty(exports, "default", {
76
enumerable: true,
8-
get: ()=>_default
7+
get () {
8+
return _default;
9+
}
910
});
1011
const _default = [];

crates/swc_ecma_transforms_module/tests/fixture/common/interop-node/export-default-4/output.cjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
Object.defineProperty(exports, "__esModule", {
33
value: true
44
});
5-
exports.default = void 0;
65
Object.defineProperty(exports, "default", {
76
enumerable: true,
8-
get: ()=>_default
7+
get () {
8+
return _default;
9+
}
910
});
1011
const _default = foo;

crates/swc_ecma_transforms_module/tests/fixture/common/interop-node/export-default-5/output.cjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
Object.defineProperty(exports, "__esModule", {
33
value: true
44
});
5-
exports.default = void 0;
65
Object.defineProperty(exports, "default", {
76
enumerable: true,
8-
get: ()=>_default
7+
get () {
8+
return _default;
9+
}
910
});
1011
function _default() {}

crates/swc_ecma_transforms_module/tests/fixture/common/interop-node/export-default-6/output.cjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
Object.defineProperty(exports, "__esModule", {
33
value: true
44
});
5-
exports.default = void 0;
65
Object.defineProperty(exports, "default", {
76
enumerable: true,
8-
get: ()=>_default
7+
get () {
8+
return _default;
9+
}
910
});
1011
class _default {
1112
}

crates/swc_ecma_transforms_module/tests/fixture/common/interop-node/export-default-7/output.cjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
Object.defineProperty(exports, "__esModule", {
33
value: true
44
});
5-
exports.default = void 0;
65
Object.defineProperty(exports, "default", {
76
enumerable: true,
8-
get: ()=>foo
7+
get () {
8+
return foo;
9+
}
910
});
1011
function foo() {}

crates/swc_ecma_transforms_module/tests/fixture/common/interop-node/export-default-8/output.cjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
Object.defineProperty(exports, "__esModule", {
33
value: true
44
});
5-
exports.default = void 0;
65
Object.defineProperty(exports, "default", {
76
enumerable: true,
8-
get: ()=>Foo
7+
get () {
8+
return Foo;
9+
}
910
});
1011
class Foo {
1112
}

crates/swc_ecma_transforms_module/tests/fixture/common/interop-node/export-default-9/output.cjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
Object.defineProperty(exports, "__esModule", {
33
value: true
44
});
5-
exports.default = void 0;
65
Object.defineProperty(exports, "default", {
76
enumerable: true,
8-
get: ()=>foo
7+
get () {
8+
return foo;
9+
}
910
});
1011
var foo;

crates/swc_ecma_transforms_module/tests/fixture/common/interop-node/export-default/output.cjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
Object.defineProperty(exports, "__esModule", {
33
value: true
44
});
5-
exports.default = void 0;
65
Object.defineProperty(exports, "default", {
76
enumerable: true,
8-
get: ()=>_default
7+
get () {
8+
return _default;
9+
}
910
});
1011
const _default = 42;

crates/swc_ecma_transforms_module/tests/fixture/common/interop-node/export-destructured/output.cjs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@
22
Object.defineProperty(exports, "__esModule", {
33
value: true
44
});
5-
exports.f4 = exports.f3 = exports.f2 = exports.f1 = exports.y = exports.x = void 0;
5+
0 && (module.exports = {
6+
x: _,
7+
y: _,
8+
f1: _,
9+
f2: _,
10+
f3: _,
11+
f4: _
12+
});
613
function _export(target, all) {
714
for(var name in all)Object.defineProperty(target, name, {
815
enumerable: true,

crates/swc_ecma_transforms_module/tests/fixture/common/interop-node/export-from-1/output.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
Object.defineProperty(exports, "__esModule", {
33
value: true
44
});
5+
0 && __export(require("foo"));
56
_exportStar(require("foo"), exports);

crates/swc_ecma_transforms_module/tests/fixture/common/interop-node/export-from-2/output.cjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
Object.defineProperty(exports, "__esModule", {
33
value: true
44
});
5-
exports.foo = void 0;
65
Object.defineProperty(exports, "foo", {
76
enumerable: true,
8-
get: ()=>_foo.foo
7+
get () {
8+
return _foo.foo;
9+
}
910
});
1011
const _foo = require("foo");

0 commit comments

Comments
 (0)