Skip to content

Commit a8f1748

Browse files
committed
lexer: Support (function(){...}).call(this) expr
1 parent 9e234f0 commit a8f1748

File tree

2 files changed

+118
-49
lines changed

2 files changed

+118
-49
lines changed

lexer/src/lexer.rs

Lines changed: 73 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@ impl ModuleLexer {
3636
self.reexports.clear();
3737
}
3838

39-
fn reset(&mut self, expr: &Expr) {
39+
fn replace_exports_from_expr(&mut self, expr: &Expr) {
4040
if let Expr::Paren(ParenExpr { expr, .. }) = expr {
41-
self.reset(expr);
41+
self.replace_exports_from_expr(expr);
4242
return;
4343
}
44+
4445
if let Some(reexport) = self.as_reexport(expr) {
4546
self.clear();
4647
self.reexports.insert(reexport);
@@ -72,6 +73,8 @@ impl ModuleLexer {
7273
}
7374
}
7475
}
76+
} else if let Expr::Assign(assign) = expr {
77+
self.replace_exports_from_expr(&assign.right);
7578
}
7679
}
7780

@@ -101,7 +104,7 @@ impl ModuleLexer {
101104
}
102105
}
103106
Expr::Call(call) => {
104-
if let Some(file) = is_require_call(&call) {
107+
if let Some(file) = with_require_call(&call) {
105108
self.idents.insert(name.into(), IdentKind::Reexport(file));
106109
}
107110
}
@@ -244,7 +247,7 @@ impl ModuleLexer {
244247
fn as_reexport(&self, expr: &Expr) -> Option<String> {
245248
match expr {
246249
Expr::Paren(ParenExpr { expr, .. }) => return self.as_reexport(expr),
247-
Expr::Call(call) => is_require_call(&call),
250+
Expr::Call(call) => with_require_call(&call),
248251
Expr::Ident(id) => {
249252
if let Some(value) = self.idents.get(id.sym.as_ref()) {
250253
match value {
@@ -328,7 +331,7 @@ impl ModuleLexer {
328331
}
329332
}
330333
Expr::Call(call) => {
331-
if let Some(reexport) = is_require_call(call) {
334+
if let Some(reexport) = with_require_call(call) {
332335
self.reexports.insert(reexport);
333336
}
334337
}
@@ -487,46 +490,50 @@ impl ModuleLexer {
487490

488491
fn get_exports_from_assign(&mut self, assign: &AssignExpr) {
489492
if assign.op == AssignOp::Assign {
490-
let member = if let AssignTarget::Simple(simple) = &assign.left {
491-
if let SimpleAssignTarget::Member(member) = &simple {
492-
Some(member)
493-
} else {
494-
None
495-
}
496-
} else {
497-
None
498-
};
499-
if let Some(MemberExpr { obj, prop, .. }) = member {
500-
let prop = get_prop_name(&prop);
501-
if let Some(prop) = prop {
502-
match obj.as_ref() {
503-
Expr::Ident(obj) => {
504-
let obj_name = obj.sym.as_ref();
505-
if self.is_exports_ident(obj_name) {
506-
// exports.foo = 'bar'
507-
self.named_exports.insert(prop);
508-
if let Expr::Assign(dep_assign) = assign.right.as_ref() {
509-
self.get_exports_from_assign(dep_assign);
493+
if let AssignTarget::Simple(simple) = &assign.left {
494+
if let SimpleAssignTarget::Member(MemberExpr { obj, prop, .. }) = &simple {
495+
let prop = get_prop_name(&prop);
496+
if let Some(prop) = prop {
497+
match obj.as_ref() {
498+
Expr::Ident(obj) => {
499+
let obj_name = obj.sym.as_ref();
500+
if self.is_exports_ident(obj_name) {
501+
// exports.foo = 'bar'
502+
self.named_exports.insert(prop);
503+
if let Expr::Assign(right_as_assign) = assign.right.as_ref() {
504+
self.get_exports_from_assign(right_as_assign);
505+
}
506+
return;
507+
} else if obj_name.eq("module") && self.is_exports_ident(&prop) {
508+
// module.exports = ??
509+
self.replace_exports_from_expr(assign.right.as_ref());
510+
return;
510511
}
511-
} else if obj_name.eq("module") && self.is_exports_ident(&prop) {
512-
// module.exports = ??
513-
let right_expr = assign.right.as_ref();
514-
self.reset(right_expr)
515512
}
516-
}
517-
Expr::Member(_) => {
518-
if is_member(obj, "module", "exports") {
519-
self.named_exports.insert(prop);
513+
// module.exports.foo = 'bar'
514+
Expr::Member(_) => {
515+
if is_member(obj, "module", "exports") {
516+
self.named_exports.insert(prop);
517+
if let Expr::Assign(right_as_assign) = assign.right.as_ref() {
518+
self.get_exports_from_assign(right_as_assign);
519+
}
520+
return;
521+
}
520522
}
523+
_ => {}
521524
}
522-
_ => {}
523525
}
524-
}
525-
} else {
526-
if let Some(name) = self.get_export_name_of_call_arg(assign.right.as_ref()) {
527-
self.named_exports.insert(name);
526+
} else if let SimpleAssignTarget::Ident(id) = &simple {
527+
if self.is_exports_ident(id.sym.as_ref()) {
528+
// exports = ??
529+
self.replace_exports_from_expr(assign.right.as_ref());
530+
return
531+
}
528532
}
529533
}
534+
if let Some(name) = self.get_export_name_of_call_arg(assign.right.as_ref()) {
535+
self.named_exports.insert(name);
536+
}
530537
}
531538
}
532539

@@ -712,7 +719,7 @@ impl ModuleLexer {
712719
return 0;
713720
}
714721
Expr::Call(call) => {
715-
if let Some(body) = is_iife_call(call) {
722+
if let Some(body) = get_iife_body(call) {
716723
return self.get_webpack_require_props_from_stmts(&body, webpack_require_sym);
717724
}
718725
return 0;
@@ -969,6 +976,7 @@ impl ModuleLexer {
969976
// module.exports = { foo: 'bar' }
970977
// module.exports = { ...require('a'), ...require('b') }
971978
// module.exports = require('lib')
979+
// exports = module.exports = { foo: 'bar' }
972980
// foo = exports.foo || (exports.foo = {})
973981
Expr::Assign(assign) => {
974982
self.get_exports_from_assign(&assign);
@@ -982,6 +990,7 @@ impl ModuleLexer {
982990
// Object.assign(module, { exports: { foo: 'bar' } })
983991
// Object.assign(module, { exports: require('lib') })
984992
// (function() { ... })()
993+
// (function() { ... }).call(this)
985994
// require("tslib").__exportStar(..., exports)
986995
// tslib.__exportStar(..., exports)
987996
// __exportStar(..., exports)
@@ -1028,7 +1037,7 @@ impl ModuleLexer {
10281037
}
10291038
if is_module {
10301039
if let Some(expr) = with_value {
1031-
self.reset(&expr);
1040+
self.replace_exports_from_expr(&expr);
10321041
}
10331042
}
10341043
} else if is_object_static_mothod_call(&call, "assign") && call.args.len() >= 2 {
@@ -1052,7 +1061,7 @@ impl ModuleLexer {
10521061
}
10531062
}
10541063
if let Some(exports_expr) = with_exports {
1055-
self.reset(&exports_expr);
1064+
self.replace_exports_from_expr(&exports_expr);
10561065
}
10571066
} else if is_exports {
10581067
self.use_object_as_exports(props);
@@ -1080,7 +1089,7 @@ impl ModuleLexer {
10801089
}
10811090
} else if let Some(body) = self.is_umd_iife_call(&call) {
10821091
self.walk_body(body, false);
1083-
} else if let Some(body) = is_iife_call(&call) {
1092+
} else if let Some(body) = get_iife_body(&call) {
10841093
for arg in &call.args {
10851094
if arg.spread.is_none() {
10861095
// (function() { ... })(exports.foo || (exports.foo = {}))
@@ -1099,7 +1108,7 @@ impl ModuleLexer {
10991108
if let Expr::Call(call) = arg.as_ref() {
11001109
if let Some(body) = self.is_umd_iife_call(&call) {
11011110
self.walk_body(body, false);
1102-
} else if let Some(body) = is_iife_call(&call) {
1111+
} else if let Some(body) = get_iife_body(&call) {
11031112
// (function() { ... })(exports.foo || (exports.foo = {}))
11041113
for arg in &call.args {
11051114
if arg.spread.is_none() {
@@ -1128,7 +1137,7 @@ impl ModuleLexer {
11281137
Expr::Bin(BinExpr { left, op, right, .. }) => {
11291138
if matches!(op, BinaryOp::LogicalAnd) {
11301139
if let Expr::Call(call) = right.as_ref() {
1131-
if let Some(body) = is_iife_call(&call) {
1140+
if let Some(body) = get_iife_body(&call) {
11321141
if self.is_true(left) {
11331142
for arg in &call.args {
11341143
if arg.spread.is_none() {
@@ -1177,7 +1186,7 @@ impl ModuleLexer {
11771186
Stmt::Return(ReturnStmt { arg, .. }) => {
11781187
self.fn_returned = true;
11791188
if let Some(arg) = arg {
1180-
self.reset(arg);
1189+
self.replace_exports_from_expr(arg);
11811190
}
11821191
}
11831192
_ => {}
@@ -1404,7 +1413,7 @@ impl ModuleLexer {
14041413
if let Expr::Seq(SeqExpr { exprs, .. }) = &**arg {
14051414
if let Some(expr) = exprs.get(0) {
14061415
if let Expr::Call(call) = &**expr {
1407-
if let Some(stmts) = is_iife_call(call) {
1416+
if let Some(stmts) = get_iife_body(call) {
14081417
self.get_webpack_exports(&stmts, &webpack_require_sym, &0);
14091418
}
14101419
}
@@ -1421,7 +1430,7 @@ impl ModuleLexer {
14211430
if let Some(module_exports_expr) = exprs.get(exprs.len() - 1) {
14221431
if let Some(module_iife_expr) = exprs.get(0) {
14231432
if let Expr::Call(module_iife_call_expr) = &**module_iife_expr {
1424-
if let Some(stmts) = is_iife_call(module_iife_call_expr) {
1433+
if let Some(stmts) = get_iife_body(module_iife_call_expr) {
14251434
if let Expr::Ident(Ident {
14261435
sym: module_exports_sym,
14271436
..
@@ -1564,7 +1573,7 @@ fn with_expr_callee(call: &CallExpr) -> Option<&Expr> {
15641573
}
15651574

15661575
// require('lib')
1567-
fn is_require_call(call: &CallExpr) -> Option<String> {
1576+
fn with_require_call(call: &CallExpr) -> Option<String> {
15681577
if let Some(Expr::Ident(id)) = with_expr_callee(call) {
15691578
if id.sym.as_ref().eq("require") && call.args.len() > 0 {
15701579
return match call.args[0].expr.as_ref() {
@@ -1758,10 +1767,25 @@ fn is_umd_checks(stmts: &Vec<Stmt>) -> bool {
17581767
}
17591768
}
17601769

1761-
fn is_iife_call(call: &CallExpr) -> Option<Vec<Stmt>> {
1770+
fn get_iife_body(call: &CallExpr) -> Option<Vec<Stmt>> {
17621771
let expr = if let Some(callee) = with_expr_callee(call) {
17631772
match callee {
17641773
Expr::Paren(ParenExpr { expr, .. }) => expr.as_ref(),
1774+
// (function() { ... }).call(this)
1775+
Expr::Member(MemberExpr { obj, prop, .. }) => {
1776+
if let Some(prop_name) = get_prop_name(prop) {
1777+
if !prop_name.eq("call") {
1778+
return None;
1779+
}
1780+
} else {
1781+
return None;
1782+
}
1783+
if let Expr::Paren(ParenExpr { expr, .. }) = obj.as_ref() {
1784+
expr.as_ref()
1785+
} else {
1786+
return None;
1787+
}
1788+
}
17651789
_ => callee,
17661790
}
17671791
} else {

lexer/src/test.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,50 @@ mod tests {
232232

233233
#[test]
234234
fn parse_cjs_exports_case_12_4() {
235+
let source = r#"
236+
!function() {
237+
module.exports = { foo: 'bar' }
238+
}()
239+
"#;
240+
let lexer = CommonJSModuleLexer::init("index.cjs", source).expect("could not parse the module");
241+
let (exports, _) = lexer.analyze("development", false);
242+
assert_eq!(exports.join(","), "foo");
243+
}
244+
245+
#[test]
246+
fn parse_cjs_exports_case_12_5() {
247+
let source = r#"
248+
(function() {
249+
if (typeof exports !== 'undefined') {
250+
exports = module.exports = {
251+
foo: 'bar'
252+
};
253+
}
254+
}).call(this)
255+
"#;
256+
let lexer = CommonJSModuleLexer::init("index.cjs", source).expect("could not parse the module");
257+
let (exports, _) = lexer.analyze("development", false);
258+
assert_eq!(exports.join(","), "foo");
259+
}
260+
261+
#[test]
262+
fn parse_cjs_exports_case_12_6() {
263+
let source = r#"
264+
(() => {
265+
if (typeof exports !== 'undefined') {
266+
module.exports = exports = {
267+
foo: 'bar'
268+
};
269+
}
270+
}).call(this)
271+
"#;
272+
let lexer = CommonJSModuleLexer::init("index.cjs", source).expect("could not parse the module");
273+
let (exports, _) = lexer.analyze("development", false);
274+
assert_eq!(exports.join(","), "foo");
275+
}
276+
277+
#[test]
278+
fn parse_cjs_exports_case_12_7() {
235279
let source = r#"
236280
let es = { foo: 'bar' };
237281
(function() {
@@ -270,6 +314,7 @@ mod tests {
270314
}
271315

272316
#[test]
317+
// NODE_ENV
273318
fn parse_cjs_exports_case_14() {
274319
let source = r#"
275320
if (process.env.NODE_ENV === 'development') {

0 commit comments

Comments
 (0)