fix: Fix wrong cjs detection of auto-cjs pass (#60118)

### What?

Make `auto-cjs` pass consider shadowing of `module`, so it can ignore
`module.exports = foo` in nested scopes.

### Why?

It's problematic for many tasks

### How?

Closes PACK-2074
This commit is contained in:
Donny/강동윤 2024-01-03 00:59:09 +09:00 committed by GitHub
parent 5f6cd8f649
commit 145a0c0797
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 112 additions and 38 deletions

View file

@ -14,24 +14,32 @@ struct CjsFinder {
found: bool,
}
impl CjsFinder {
/// If the given pattern contains `module` as a parameter, we don't need to
/// recurse into it because `module` is shadowed.
fn contains_module_param<'a, I>(&self, mut iter: I) -> bool
where
I: Iterator<Item = &'a Pat>,
{
iter.any(|p| {
if let Pat::Ident(i) = p {
&*i.id.sym == "module"
} else {
false
}
})
}
}
/// This visitor implementation supports typescript, because the api of `swc`
/// does not support changing configuration based on content of the file.
impl Visit for CjsFinder {
fn visit_member_expr(&mut self, e: &MemberExpr) {
if let Expr::Ident(obj) = &*e.obj {
if let MemberProp::Ident(prop) = &e.prop {
// Detect `module.exports` and `exports.__esModule`
if (&*obj.sym == "module" && &*prop.sym == "exports")
|| (&*obj.sym == "exports" && &*prop.sym == "__esModule")
{
self.found = true;
return;
}
}
fn visit_arrow_expr(&mut self, n: &ArrowExpr) {
if self.contains_module_param(n.params.iter()) {
return;
}
e.obj.visit_with(self);
e.prop.visit_with(self);
n.visit_children_with(self);
}
// Detect `Object.defineProperty(exports, "__esModule", ...)`
@ -65,4 +73,45 @@ impl Visit for CjsFinder {
e.callee.visit_with(self);
}
fn visit_class_method(&mut self, n: &ClassMethod) {
if self.contains_module_param(n.function.params.iter().map(|v| &v.pat)) {
return;
}
n.visit_children_with(self);
}
fn visit_function(&mut self, n: &Function) {
if self.contains_module_param(n.params.iter().map(|v| &v.pat)) {
return;
}
n.visit_children_with(self);
}
fn visit_member_expr(&mut self, e: &MemberExpr) {
if let Expr::Ident(obj) = &*e.obj {
if let MemberProp::Ident(prop) = &e.prop {
// Detect `module.exports` and `exports.__esModule`
if (&*obj.sym == "module" && &*prop.sym == "exports")
|| (&*obj.sym == "exports" && &*prop.sym == "__esModule")
{
self.found = true;
return;
}
}
}
e.obj.visit_with(self);
e.prop.visit_with(self);
}
fn visit_method_prop(&mut self, n: &MethodProp) {
if self.contains_module_param(n.function.params.iter().map(|v| &v.pat)) {
return;
}
n.visit_children_with(self);
}
}

View file

@ -1,8 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: !0
});
var e, o = (e = require("esm")) && e.__esModule ? e : {
default: e
};
console.log(o.default.foo), module.exports = o.default;

View file

@ -1,13 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: !0
}), Object.defineProperty(exports, "default", {
enumerable: !0,
get: function() {
return e;
}
});
var e = 1;
Object.defineProperty(exports, "__esModule", {
value: !0
});

View file

@ -1,4 +0,0 @@
export default 1;
console.log("__esModule"), Object.defineProperty({}, "__esModule", {
value: !0
}), Object.defineProperty();

View file

@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _esm = /*#__PURE__*/ _interop_require_default(require("esm"));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
console.log(_esm.default.foo);
module.exports = _esm.default;

View file

@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "default", {
enumerable: true,
get: function() {
return _default;
}
});
var _default = 1;
Object.defineProperty(exports, "__esModule", {
value: true
});

View file

@ -0,0 +1,6 @@
export default 1;
console.log("__esModule");
Object.defineProperty({}, "__esModule", {
value: true
});
Object.defineProperty();

View file

@ -0,0 +1,5 @@
export default (module) => {
module.exports = {}
}
export const value = 'mixed-syntax-esm'

View file

@ -0,0 +1,4 @@
export default function(module) {
module.exports = {};
};
export var value = "mixed-syntax-esm";

View file

@ -0,0 +1,5 @@
function foo(module) {
module.exports = 'this is just normal assignment of scope variable'
}
export const value = 'mixed-syntax-esm'

View file

@ -0,0 +1,4 @@
function foo(module) {
module.exports = "this is just normal assignment of scope variable";
}
export var value = "mixed-syntax-esm";