Skip to content

Commit

Permalink
fix(next-swc): Detect exports.foo from cjs_finder (#61795)
Browse files Browse the repository at this point in the history
### What?

Patch cjs finder to detect `exports.foo` (instead of module.exports)

### Why?

This causes an issue for `react.production.min.js`.

x-ref: https://vercel.slack.com/archives/C02HY34AKME/p1707343215879979

### How?

Closes PACK-2443
  • Loading branch information
kdy1 authored and huozhi committed Feb 28, 2024
1 parent af47328 commit b747e08
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 132 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,9 @@ impl TransformOptions {
self.swc.swcrc = false;

let should_enable_commonjs = self.swc.config.module.is_none()
&& (fm.src.contains("module.exports") || fm.src.contains("__esModule"))
&& (fm.src.contains("module.exports")
|| fm.src.contains("exports.")
|| fm.src.contains("__esModule"))
&& {
let syntax = self.swc.config.jsc.syntax.unwrap_or_default();
let target = self.swc.config.jsc.target.unwrap_or_else(EsVersion::latest);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,25 @@ pub fn contains_cjs(m: &Module) -> bool {
struct CjsFinder {
found: bool,
is_esm: bool,
ignore_module: bool,
ignore_exports: 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
fn adjust_state<'a, I>(&mut self, iter: I)
where
I: Iterator<Item = &'a Pat>,
{
iter.any(|p| {
iter.for_each(|p| {
if let Pat::Ident(i) = p {
&*i.id.sym == "module"
} else {
false
if &*i.id.sym == "module" {
self.ignore_module = true;
}
if &*i.id.sym == "exports" {
self.ignore_exports = true;
}
}
})
}
Expand All @@ -36,31 +41,39 @@ impl CjsFinder {
/// does not support changing configuration based on content of the file.
impl Visit for CjsFinder {
fn visit_arrow_expr(&mut self, n: &ArrowExpr) {
if self.contains_module_param(n.params.iter()) {
return;
}
let old_ignore_module = self.ignore_module;
let old_ignore_exports = self.ignore_exports;

self.adjust_state(n.params.iter());

n.visit_children_with(self);

self.ignore_module = old_ignore_module;
self.ignore_exports = old_ignore_exports;
}

// Detect `Object.defineProperty(exports, "__esModule", ...)`
// Note that `Object.defineProperty(module.exports, ...)` will be handled by
// `visit_member_expr`.
fn visit_call_expr(&mut self, e: &CallExpr) {
if let Callee::Expr(expr) = &e.callee {
if let Expr::Member(member_expr) = &**expr {
if let (Expr::Ident(obj), MemberProp::Ident(prop)) =
(&*member_expr.obj, &member_expr.prop)
{
if &*obj.sym == "Object" && &*prop.sym == "defineProperty" {
if let Some(ExprOrSpread { expr: expr0, .. }) = e.args.first() {
if let Expr::Ident(arg0) = &**expr0 {
if &*arg0.sym == "exports" {
if let Some(ExprOrSpread { expr: expr1, .. }) = e.args.get(1) {
if let Expr::Lit(Lit::Str(arg1)) = &**expr1 {
if &*arg1.value == "__esModule" {
self.found = true;
return;
if !self.ignore_exports {
if let Callee::Expr(expr) = &e.callee {
if let Expr::Member(member_expr) = &**expr {
if let (Expr::Ident(obj), MemberProp::Ident(prop)) =
(&*member_expr.obj, &member_expr.prop)
{
if &*obj.sym == "Object" && &*prop.sym == "defineProperty" {
if let Some(ExprOrSpread { expr: expr0, .. }) = e.args.first() {
if let Expr::Ident(arg0) = &**expr0 {
if &*arg0.sym == "exports" {
if let Some(ExprOrSpread { expr: expr1, .. }) =
e.args.get(1)
{
if let Expr::Lit(Lit::Str(arg1)) = &**expr1 {
if &*arg1.value == "__esModule" {
self.found = true;
return;
}
}
}
}
Expand All @@ -76,27 +89,35 @@ impl Visit for CjsFinder {
}

fn visit_class_method(&mut self, n: &ClassMethod) {
if self.contains_module_param(n.function.params.iter().map(|v| &v.pat)) {
return;
}
let old_ignore_module = self.ignore_module;
let old_ignore_exports = self.ignore_exports;

self.adjust_state(n.function.params.iter().map(|v| &v.pat));

n.visit_children_with(self);

self.ignore_module = old_ignore_module;
self.ignore_exports = old_ignore_exports;
}

fn visit_function(&mut self, n: &Function) {
if self.contains_module_param(n.params.iter().map(|v| &v.pat)) {
return;
}
let old_ignore_module = self.ignore_module;
let old_ignore_exports = self.ignore_exports;

self.adjust_state(n.params.iter().map(|v| &v.pat));

n.visit_children_with(self);

self.ignore_module = old_ignore_module;
self.ignore_exports = old_ignore_exports;
}

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")
if (!self.ignore_module && &*obj.sym == "module" && &*prop.sym == "exports")
|| (!self.ignore_exports && &*obj.sym == "exports")
{
self.found = true;
return;
Expand All @@ -109,11 +130,15 @@ impl Visit for CjsFinder {
}

fn visit_method_prop(&mut self, n: &MethodProp) {
if self.contains_module_param(n.function.params.iter().map(|v| &v.pat)) {
return;
}
let old_ignore_module = self.ignore_module;
let old_ignore_exports = self.ignore_exports;

self.adjust_state(n.function.params.iter().map(|v| &v.pat));

n.visit_children_with(self);

self.ignore_module = old_ignore_module;
self.ignore_exports = old_ignore_exports;
}

fn visit_module_decl(&mut self, n: &ModuleDecl) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ fn test(input: &Path, minify: bool) {
tsx: true,
..Default::default()
})),
external_helpers: true.into(),
..Default::default()
},
..Default::default()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,8 @@
function r(r, t) {
(null == t || t > r.length) && (t = r.length);
for(var e = 0, n = Array(t); e < t; e++)n[e] = r[e];
return n;
}
import t from "other";
((function(r) {
if (Array.isArray(r)) return r;
})(t) || function(r, t) {
var e, n, o = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (null != o) {
var a = [], l = !0, u = !1;
try {
for(o = o.call(r); !(l = (e = o.next()).done) && (a.push(e.value), !t || a.length !== t); l = !0);
} catch (r) {
u = !0, n = r;
} finally{
try {
l || null == o.return || o.return();
} finally{
if (u) throw n;
}
}
return a;
}
}(t, 1) || function(t, e) {
if (t) {
if ("string" == typeof t) return r(t, e);
var n = Object.prototype.toString.call(t).slice(8, -1);
if ("Object" === n && t.constructor && (n = t.constructor.name), "Map" === n || "Set" === n) return Array.from(n);
if ("Arguments" === n || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return r(t, e);
}
}(t, 1) || function() {
throw TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}())[0];
import "@swc/helpers/_/_class_call_check";
import { _ as e } from "@swc/helpers/_/_sliced_to_array";
import r from "other";
e(r, 1)[0];
export var __N_SSG = !0;
export default function e() {
export default function t() {
return React.createElement("div", null);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@
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
};
}
var _interop_require_default = require("@swc/helpers/_/_interop_require_default");
var _esm = /*#__PURE__*/ _interop_require_default._(require("esm"));
console.log(_esm.default.foo);
module.exports = _esm.default;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// This file should not import helpers using ESM syntax
const E = class Foo {}
exports.Component = E
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// This file should not import helpers using ESM syntax
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _class_call_check = require("@swc/helpers/_/_class_call_check");
var E = function Foo() {
"use strict";
_class_call_check._(this, Foo);
};
exports.Component = E;
Original file line number Diff line number Diff line change
@@ -1,54 +1,5 @@
function _array_like_to_array(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
return arr2;
}
function _array_with_holes(arr) {
if (Array.isArray(arr)) return arr;
}
function _class_call_check(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _iterable_to_array_limit(arr, i) {
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
if (_i == null) return;
var _arr = [];
var _n = true;
var _d = false;
var _s, _e;
try {
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally{
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally{
if (_d) throw _e;
}
}
return _arr;
}
function _non_iterable_rest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _sliced_to_array(arr, i) {
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
}
function _unsupported_iterable_to_array(o, minLen) {
if (!o) return;
if (typeof o === "string") return _array_like_to_array(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(n);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
}
import { _ as _class_call_check } from "@swc/helpers/_/_class_call_check";
import { _ as _sliced_to_array } from "@swc/helpers/_/_sliced_to_array";
import other from "other";
var _other = _sliced_to_array(other, 1), foo = _other[0];
var Foo = function Foo() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
function _class_call_check(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
import { _ as _class_call_check } from "@swc/helpers/_/_class_call_check";
import { useEffect } from "react";
import { select, selectAll } from "d3-selection";
export default function Home() {
Expand Down

0 comments on commit b747e08

Please sign in to comment.