mirror of
https://github.com/swc-project/swc.git
synced 2024-12-25 06:36:08 +03:00
fix(webpack/ast): Fix handling of define
(#2935)
swc_webpack_ast: - Support `define` calls with `require` .
This commit is contained in:
parent
8a55870ce5
commit
9f5fc11a8f
@ -8,7 +8,7 @@ use swc_common::{
|
|||||||
};
|
};
|
||||||
use swc_ecma_ast::*;
|
use swc_ecma_ast::*;
|
||||||
use swc_ecma_utils::{ident::IdentLike, Id, IsEmpty, StmtLike, StmtOrModuleItem};
|
use swc_ecma_utils::{ident::IdentLike, Id, IsEmpty, StmtLike, StmtOrModuleItem};
|
||||||
use swc_ecma_visit::{VisitMut, VisitMutWith};
|
use swc_ecma_visit::{Node, Visit, VisitMut, VisitMutWith, VisitWith};
|
||||||
use swc_timer::timer;
|
use swc_timer::timer;
|
||||||
|
|
||||||
/// # Usage
|
/// # Usage
|
||||||
@ -65,9 +65,69 @@ pub fn ast_reducer(top_level_mark: Mark) -> impl VisitMut {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Analyzer {
|
||||||
|
amd_requires: AHashSet<Id>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Visit for Analyzer {
|
||||||
|
fn visit_call_expr(&mut self, e: &CallExpr, _: &dyn Node) {
|
||||||
|
e.visit_children_with(self);
|
||||||
|
|
||||||
|
match &e.callee {
|
||||||
|
ExprOrSuper::Expr(callee) => match &**callee {
|
||||||
|
Expr::Ident(callee) => {
|
||||||
|
if &*callee.sym == "define" {
|
||||||
|
// find `require`
|
||||||
|
|
||||||
|
let require_arg_idx = e
|
||||||
|
.args
|
||||||
|
.first()
|
||||||
|
.and_then(|v| match &*v.expr {
|
||||||
|
Expr::Array(arr) => Some(&arr.elems),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.iter()
|
||||||
|
.flat_map(|v| v.iter())
|
||||||
|
.flatten()
|
||||||
|
.position(|arg| match &*arg.expr {
|
||||||
|
Expr::Lit(Lit::Str(s)) => &*s.value == "require",
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let fn_arg = e.args.iter().find_map(|arg| match &*arg.expr {
|
||||||
|
Expr::Fn(f) => Some(f),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(require_arg_idx) = require_arg_idx {
|
||||||
|
if let Some(fn_arg) = fn_arg {
|
||||||
|
if let Some(require_arg) =
|
||||||
|
fn_arg.function.params.iter().nth(require_arg_idx)
|
||||||
|
{
|
||||||
|
match &require_arg.pat {
|
||||||
|
Pat::Ident(r) => {
|
||||||
|
self.amd_requires.insert(r.to_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ScopeData {
|
struct ScopeData {
|
||||||
imported_ids: AHashSet<Id>,
|
imported_ids: AHashSet<Id>,
|
||||||
|
/// amd `require` modules.
|
||||||
|
amd_requires: AHashSet<Id>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScopeData {
|
impl ScopeData {
|
||||||
@ -96,11 +156,21 @@ impl ScopeData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopeData { imported_ids }
|
let mut analyzer = Analyzer {
|
||||||
|
amd_requires: AHashSet::default(),
|
||||||
|
};
|
||||||
|
for i in items {
|
||||||
|
i.visit_with(&Invalid { span: DUMMY_SP }, &mut analyzer);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopeData {
|
||||||
|
imported_ids,
|
||||||
|
amd_requires: analyzer.amd_requires,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_preserve(&self, i: &Ident) -> bool {
|
fn should_preserve(&self, i: &Ident) -> bool {
|
||||||
if self.imported_ids.contains(&i.to_id()) {
|
if self.imported_ids.contains(&i.to_id()) || self.amd_requires.contains(&i.to_id()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,6 +200,7 @@ struct ReduceAst {
|
|||||||
|
|
||||||
can_remove_pat: bool,
|
can_remove_pat: bool,
|
||||||
preserve_fn: bool,
|
preserve_fn: bool,
|
||||||
|
preserve_lit: bool,
|
||||||
|
|
||||||
changed: bool,
|
changed: bool,
|
||||||
}
|
}
|
||||||
@ -282,7 +353,7 @@ impl ReduceAst {
|
|||||||
// Remove empty statements
|
// Remove empty statements
|
||||||
new.retain(|stmt| match StmtOrModuleItem::as_stmt(stmt) {
|
new.retain(|stmt| match StmtOrModuleItem::as_stmt(stmt) {
|
||||||
Ok(Stmt::Empty(..)) => return false,
|
Ok(Stmt::Empty(..)) => return false,
|
||||||
Ok(Stmt::Expr(es)) => return !can_remove(&es.expr),
|
Ok(Stmt::Expr(es)) => return !self.can_remove(&es.expr),
|
||||||
_ => true,
|
_ => true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -326,7 +397,7 @@ impl ReduceAst {
|
|||||||
self.ignore_expr(prop);
|
self.ignore_expr(prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
match (can_remove(&obj), can_remove(&prop)) {
|
match (self.can_remove(&obj), self.can_remove(&prop)) {
|
||||||
(true, true) => {
|
(true, true) => {
|
||||||
e.take();
|
e.take();
|
||||||
return;
|
return;
|
||||||
@ -381,6 +452,15 @@ impl ReduceAst {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn can_remove(&self, e: &Expr) -> bool {
|
||||||
|
match e {
|
||||||
|
Expr::Invalid(..) => true,
|
||||||
|
Expr::Lit(..) => !self.preserve_lit,
|
||||||
|
Expr::Seq(seq) => seq.exprs.iter().all(|e| self.can_remove(e)),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitMut for ReduceAst {
|
impl VisitMut for ReduceAst {
|
||||||
@ -412,7 +492,7 @@ impl VisitMut for ReduceAst {
|
|||||||
c.visit_mut_children_with(self);
|
c.visit_mut_children_with(self);
|
||||||
|
|
||||||
if let Some(s) = &c.super_class {
|
if let Some(s) = &c.super_class {
|
||||||
if can_remove(&s) {
|
if self.can_remove(&s) {
|
||||||
c.super_class = None;
|
c.super_class = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -429,8 +509,11 @@ impl VisitMut for ReduceAst {
|
|||||||
ClassMember::ClassProp(p) => {
|
ClassMember::ClassProp(p) => {
|
||||||
if !p.computed
|
if !p.computed
|
||||||
&& p.decorators.is_empty()
|
&& p.decorators.is_empty()
|
||||||
&& can_remove(&p.key)
|
&& self.can_remove(&p.key)
|
||||||
&& p.value.as_deref().map(can_remove).unwrap_or(true)
|
&& p.value
|
||||||
|
.as_deref()
|
||||||
|
.map(|e| self.can_remove(e))
|
||||||
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -454,7 +537,7 @@ impl VisitMut for ReduceAst {
|
|||||||
ClassMember::PrivateProp(PrivateProp {
|
ClassMember::PrivateProp(PrivateProp {
|
||||||
value: Some(value), ..
|
value: Some(value), ..
|
||||||
}) => {
|
}) => {
|
||||||
if can_remove(&value) {
|
if self.can_remove(&value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -482,6 +565,17 @@ impl VisitMut for ReduceAst {
|
|||||||
}) => {
|
}) => {
|
||||||
self.ignore_expr(callee);
|
self.ignore_expr(callee);
|
||||||
|
|
||||||
|
let is_define = match &**callee {
|
||||||
|
Expr::Ident(callee) => {
|
||||||
|
if &*callee.sym == "define" {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
if callee.is_invalid() {
|
if callee.is_invalid() {
|
||||||
let mut seq = Expr::Seq(SeqExpr {
|
let mut seq = Expr::Seq(SeqExpr {
|
||||||
span: DUMMY_SP,
|
span: DUMMY_SP,
|
||||||
@ -493,10 +587,15 @@ impl VisitMut for ReduceAst {
|
|||||||
*e = seq;
|
*e = seq;
|
||||||
} else {
|
} else {
|
||||||
// We should preserve the arguments if the callee is not invalid.
|
// We should preserve the arguments if the callee is not invalid.
|
||||||
let old = self.preserve_fn;
|
let old_preserver_fn = self.preserve_fn;
|
||||||
self.preserve_fn = !callee.is_fn_expr() && !callee.is_arrow();
|
self.preserve_fn = !callee.is_fn_expr() && !callee.is_arrow();
|
||||||
|
let old_preserve_lit = self.preserve_lit;
|
||||||
|
self.preserve_lit |= is_define;
|
||||||
e.visit_mut_children_with(self);
|
e.visit_mut_children_with(self);
|
||||||
self.preserve_fn = old;
|
|
||||||
|
self.preserve_lit = old_preserve_lit;
|
||||||
|
self.preserve_fn = old_preserver_fn;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -504,6 +603,10 @@ impl VisitMut for ReduceAst {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.preserve_lit && e.is_lit() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
e.visit_mut_children_with(self);
|
e.visit_mut_children_with(self);
|
||||||
|
|
||||||
match e {
|
match e {
|
||||||
@ -855,7 +958,7 @@ impl VisitMut for ReduceAst {
|
|||||||
|
|
||||||
if !self.preserve_fn {
|
if !self.preserve_fn {
|
||||||
elems.retain(|e| {
|
elems.retain(|e| {
|
||||||
if can_remove(&e.expr) {
|
if self.can_remove(&e.expr) {
|
||||||
self.changed = true;
|
self.changed = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -901,7 +1004,7 @@ impl VisitMut for ReduceAst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Some(VarDeclOrExpr::Expr(init)) => {
|
Some(VarDeclOrExpr::Expr(init)) => {
|
||||||
if can_remove(&init) {
|
if self.can_remove(&init) {
|
||||||
s.init = None;
|
s.init = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -911,7 +1014,7 @@ impl VisitMut for ReduceAst {
|
|||||||
if let Some(test) = &mut s.test {
|
if let Some(test) = &mut s.test {
|
||||||
self.ignore_expr(&mut **test);
|
self.ignore_expr(&mut **test);
|
||||||
|
|
||||||
if can_remove(&test) {
|
if self.can_remove(&test) {
|
||||||
s.test = None;
|
s.test = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -919,7 +1022,7 @@ impl VisitMut for ReduceAst {
|
|||||||
if let Some(update) = &mut s.update {
|
if let Some(update) = &mut s.update {
|
||||||
self.ignore_expr(&mut **update);
|
self.ignore_expr(&mut **update);
|
||||||
|
|
||||||
if can_remove(&update) {
|
if self.can_remove(&update) {
|
||||||
s.update = None;
|
s.update = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -971,7 +1074,7 @@ impl VisitMut for ReduceAst {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
JSXExpr::Expr(e) => {
|
JSXExpr::Expr(e) => {
|
||||||
if can_remove(&e) {
|
if self.can_remove(&e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -985,7 +1088,7 @@ impl VisitMut for ReduceAst {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
JSXAttrOrSpread::SpreadElement(s) => {
|
JSXAttrOrSpread::SpreadElement(s) => {
|
||||||
if can_remove(&s.expr) {
|
if self.can_remove(&s.expr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1033,7 +1136,7 @@ impl VisitMut for ReduceAst {
|
|||||||
JSXElementChild::JSXExprContainer(JSXExprContainer {
|
JSXElementChild::JSXExprContainer(JSXExprContainer {
|
||||||
expr: JSXExpr::Expr(expr),
|
expr: JSXExpr::Expr(expr),
|
||||||
..
|
..
|
||||||
}) => return !can_remove(&expr),
|
}) => return !self.can_remove(&expr),
|
||||||
|
|
||||||
JSXElementChild::JSXElement(el) => {
|
JSXElementChild::JSXElement(el) => {
|
||||||
// Remove empty, non-component elements.
|
// Remove empty, non-component elements.
|
||||||
@ -1126,7 +1229,7 @@ impl VisitMut for ReduceAst {
|
|||||||
|
|
||||||
if !self.preserve_fn {
|
if !self.preserve_fn {
|
||||||
if let Some(elem) = e {
|
if let Some(elem) = e {
|
||||||
if can_remove(&elem.expr) {
|
if self.can_remove(&elem.expr) {
|
||||||
*e = None;
|
*e = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1158,7 +1261,7 @@ impl VisitMut for ReduceAst {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
TsParamPropParam::Assign(p) => {
|
TsParamPropParam::Assign(p) => {
|
||||||
if p.left.is_invalid() && can_remove(&p.right) {
|
if p.left.is_invalid() && self.can_remove(&p.right) {
|
||||||
self.changed = true;
|
self.changed = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1198,6 +1301,9 @@ impl VisitMut for ReduceAst {
|
|||||||
|
|
||||||
match pat {
|
match pat {
|
||||||
Pat::Ident(p) => {
|
Pat::Ident(p) => {
|
||||||
|
if self.data.should_preserve(&p.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if p.id.span.ctxt != self.top_level_ctxt {
|
if p.id.span.ctxt != self.top_level_ctxt {
|
||||||
pat.take();
|
pat.take();
|
||||||
return;
|
return;
|
||||||
@ -1219,7 +1325,7 @@ impl VisitMut for ReduceAst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Pat::Assign(a) => {
|
Pat::Assign(a) => {
|
||||||
if can_remove(&a.right) {
|
if self.can_remove(&a.right) {
|
||||||
a.left.visit_mut_with(self);
|
a.left.visit_mut_with(self);
|
||||||
|
|
||||||
*pat = *a.left.take();
|
*pat = *a.left.take();
|
||||||
@ -1283,7 +1389,7 @@ impl VisitMut for ReduceAst {
|
|||||||
self.ignore_expr(&mut **elem);
|
self.ignore_expr(&mut **elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
e.exprs.retain(|e| !can_remove(&e));
|
e.exprs.retain(|e| !self.can_remove(&e));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Normalize statements.
|
/// Normalize statements.
|
||||||
@ -1450,7 +1556,7 @@ impl VisitMut for ReduceAst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
if can_remove(&is.test) {
|
if self.can_remove(&is.test) {
|
||||||
if is.cons.is_empty() && is.alt.is_empty() {
|
if is.cons.is_empty() && is.alt.is_empty() {
|
||||||
*stmt = Stmt::Empty(EmptyStmt { span: DUMMY_SP });
|
*stmt = Stmt::Empty(EmptyStmt { span: DUMMY_SP });
|
||||||
return;
|
return;
|
||||||
@ -1478,7 +1584,7 @@ impl VisitMut for ReduceAst {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if can_remove(&s.test) {
|
if self.can_remove(&s.test) {
|
||||||
*stmt = *s.body.take();
|
*stmt = *s.body.take();
|
||||||
self.changed = true;
|
self.changed = true;
|
||||||
return;
|
return;
|
||||||
@ -1495,7 +1601,7 @@ impl VisitMut for ReduceAst {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if can_remove(&s.test) {
|
if self.can_remove(&s.test) {
|
||||||
*stmt = *s.body.take();
|
*stmt = *s.body.take();
|
||||||
self.changed = true;
|
self.changed = true;
|
||||||
return;
|
return;
|
||||||
@ -1647,7 +1753,13 @@ impl VisitMut for ReduceAst {
|
|||||||
cases.visit_mut_children_with(self);
|
cases.visit_mut_children_with(self);
|
||||||
|
|
||||||
cases.retain(|case| {
|
cases.retain(|case| {
|
||||||
if case.test.as_deref().map(can_remove).unwrap_or(true) && case.cons.is_empty() {
|
if case
|
||||||
|
.test
|
||||||
|
.as_deref()
|
||||||
|
.map(|e| self.can_remove(e))
|
||||||
|
.unwrap_or(true)
|
||||||
|
&& case.cons.is_empty()
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1772,12 +1884,3 @@ fn left_most(e: &Expr) -> Option<Ident> {
|
|||||||
fn null_expr(span: Span) -> Expr {
|
fn null_expr(span: Span) -> Expr {
|
||||||
Expr::Lit(Lit::Null(Null { span }))
|
Expr::Lit(Lit::Null(Null { span }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_remove(e: &Expr) -> bool {
|
|
||||||
match e {
|
|
||||||
Expr::Invalid(..) => true,
|
|
||||||
Expr::Lit(..) => true,
|
|
||||||
Expr::Seq(seq) => seq.exprs.iter().all(|e| can_remove(e)),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function foo() {
|
||||||
|
define(["require"], function (r) {
|
||||||
|
console.log('foo');
|
||||||
|
r("lodash")
|
||||||
|
|
||||||
|
'should be removed';
|
||||||
|
console.log('should be removed');
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
export function foo() {
|
||||||
|
define("require", function(r) {
|
||||||
|
r("lodash");
|
||||||
|
});
|
||||||
|
}
|
@ -1 +1 @@
|
|||||||
module, require(), define, define.amd, define(null, null);
|
module, require(), define, define.amd, define("react", null);
|
||||||
|
@ -1 +1 @@
|
|||||||
module, require("react"), define, define.amd, define(null, null);
|
module, require("react"), define, define.amd, define("react", null);
|
||||||
|
Loading…
Reference in New Issue
Block a user