From 3764b0c3a86b8f6bc7919821266de708406c05be Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Fri, 5 Jul 2024 17:32:21 -0700 Subject: [PATCH 1/4] support Node.js compatible ESM wrapper for CJS with cjs marker for distinguishing CJS transpiled to ESM --- .../requireesm-brand-nowrap/expected.js | 3 +- .../requireesm-brand-nowrap/options.js | 1 + test/fixtures/requireesm-brand/expected.js | 16 +++---- transform-cjs-dew.js | 42 ++++++++++++++----- 4 files changed, 43 insertions(+), 19 deletions(-) diff --git a/test/fixtures/requireesm-brand-nowrap/expected.js b/test/fixtures/requireesm-brand-nowrap/expected.js index 4a45ac6..54eec27 100644 --- a/test/fixtures/requireesm-brand-nowrap/expected.js +++ b/test/fixtures/requireesm-brand-nowrap/expected.js @@ -17,4 +17,5 @@ const { thing } = _thing; readFileSync("asdf"); -export default {}; \ No newline at end of file +export default {}; +export const __cjs = true; \ No newline at end of file diff --git a/test/fixtures/requireesm-brand-nowrap/options.js b/test/fixtures/requireesm-brand-nowrap/options.js index 955b6c5..dc5a153 100644 --- a/test/fixtures/requireesm-brand-nowrap/options.js +++ b/test/fixtures/requireesm-brand-nowrap/options.js @@ -1,5 +1,6 @@ module.exports = { nowrap: true, + cjsMarker: true, esmDependencies (x) { if (x === 'thing') return 'namespace'; diff --git a/test/fixtures/requireesm-brand/expected.js b/test/fixtures/requireesm-brand/expected.js index 079dd90..71b62a6 100644 --- a/test/fixtures/requireesm-brand/expected.js +++ b/test/fixtures/requireesm-brand/expected.js @@ -1,19 +1,19 @@ import * as _nobinding2 from "nobinding"; -var _nobinding = _nobinding2; -try { - if ("default" in _nobinding2) _nobinding = _nobinding2.default; -} catch (e) {} import * as _fs2 from "fs"; -var _fs = _fs2; -try { - if ("default" in _fs2) _fs = _fs2.default; -} catch (e) {} import * as _thing from "thing"; var exports = {}, _dewExec = false; export function dew() { if (_dewExec) return exports; _dewExec = true; + var _nobinding = _nobinding2.__cjs ? _nobinding2.default : "default" in _nobinding2 && !_nobinding2.__esModule ? new Proxy(_nobinding2, { + get: (t, k, r) => k === "__esModule" || Reflect.get(t, k, r), + has: (t, k) => k === "__esModule" || Reflect.has(t, k) + }) : _nobinding2; + var _fs = _fs2.__cjs ? _fs2.default : "default" in _fs2 && !_fs2.__esModule ? new Proxy(_fs2, { + get: (t, k, r) => k === "__esModule" || Reflect.get(t, k, r), + has: (t, k) => k === "__esModule" || Reflect.has(t, k) + }) : _fs2; _nobinding; const { readFileSync diff --git a/transform-cjs-dew.js b/transform-cjs-dew.js index 9c9198f..8f6c1d6 100644 --- a/transform-cjs-dew.js +++ b/transform-cjs-dew.js @@ -54,6 +54,7 @@ module.exports = function ({ types: t }) { const dewInterop = t.identifier('__dew'); const defaultIdentifier = t.identifier('default'); + const esModuleLiteral = t.stringLiteral('__esModule'); const globalThis = t.identifier('globalThis'); const globalThisPredicate = t.binaryExpression('!==', t.unaryExpression('typeof', globalThis), t.stringLiteral('undefined')); const selfIdentifier = t.identifier('self'); @@ -817,11 +818,11 @@ module.exports = function ({ types: t }) { t.exportDefaultDeclaration(exportsReturn) ); - if (state.opts.namedExports && state.opts.namedExports.length) { + if (state.opts.namedExports && state.opts.namedExports.length || state.opts.cjsMarker) { const exportDeclarations = []; const namedExports = []; const varDeclarations = []; - for (const name of state.opts.namedExports) { + for (const name of state.opts.namedExports || []) { const id = t.identifier(name); if (name === 'default') { if (exportsReturn) @@ -836,6 +837,11 @@ module.exports = function ({ types: t }) { namedExports.push(t.exportSpecifier(uid, id)); } } + + if (state.opts.cjsMarker) { + exportDeclarations.push(t.variableDeclarator(t.identifier('__cjs'), t.booleanLiteral(true))); + } + if (exportDeclarations.length) pushBody(path, t.exportNamedDeclaration(t.variableDeclaration('const', exportDeclarations), [])); if (varDeclarations.length) @@ -848,6 +854,8 @@ module.exports = function ({ types: t }) { let dewBodyWrapper = []; + const innerWrapper = []; + state.deps.forEach(dep => { dewBodyWrapper.push( t.importDeclaration([ @@ -855,15 +863,28 @@ module.exports = function ({ types: t }) { ], dep.literal) ); if (dep.ns && dep.mid.name !== dep.id.name) { - dewBodyWrapper.push( - t.variableDeclaration('var', [t.variableDeclarator(dep.id, dep.mid)]) + innerWrapper.push( + t.variableDeclaration('var', [ + t.variableDeclarator(dep.id, t.conditionalExpression( + t.memberExpression(dep.mid, t.identifier('__cjs')), + t.memberExpression(dep.mid, t.identifier('default')), + t.conditionalExpression( + t.logicalExpression('&&', t.binaryExpression('in', t.stringLiteral('default'), dep.mid), t.unaryExpression('!', t.memberExpression(dep.mid, t.identifier('__esModule')))), + t.newExpression(t.identifier('Proxy'), [dep.mid, t.objectExpression([ + t.objectProperty(t.identifier('get'), t.arrowFunctionExpression([t.identifier('t'), t.identifier('k'), t.identifier('r')], t.logicalExpression('||', + t.binaryExpression('===', t.identifier('k'), esModuleLiteral), + t.callExpression(t.memberExpression(t.identifier('Reflect'), t.identifier('get')), [t.identifier('t'), t.identifier('k'), t.identifier('r')]) + ))), + t.objectProperty(t.identifier('has'), t.arrowFunctionExpression([t.identifier('t'), t.identifier('k')], t.logicalExpression('||', + t.binaryExpression('===', t.identifier('k'), esModuleLiteral), + t.callExpression(t.memberExpression(t.identifier('Reflect'), t.identifier('has')), [t.identifier('t'), t.identifier('k')]) + ))) + ])]), + dep.mid + ) + )) + ]) ); - dewBodyWrapper.push(t.tryStatement(t.blockStatement([ - t.ifStatement( - t.binaryExpression('in', t.stringLiteral('default'), dep.mid), - t.expressionStatement(t.assignmentExpression('=', dep.id, t.memberExpression(dep.mid, defaultIdentifier))) - ) - ]), t.catchClause(t.identifier('e'), t.blockStatement([])))); } }); @@ -888,6 +909,7 @@ module.exports = function ({ types: t }) { t.functionDeclaration(dewIdentifier, [], t.blockStatement([ t.ifStatement(execIdentifier, t.returnStatement(exportsReturn)), t.expressionStatement(t.assignmentExpression('=', execIdentifier, t.booleanLiteral(true))), + ...innerWrapper, ...path.node.body, t.returnStatement(exportsReturn) ])), From 92a224197ab6ffa9c17119914e58b962efc6be31 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Fri, 5 Jul 2024 17:34:46 -0700 Subject: [PATCH 2/4] fix nowrap interop as well --- .../requireesm-brand-nowrap/expected.js | 16 +++++------ transform-cjs-dew.js | 27 ++++++++++++++----- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/test/fixtures/requireesm-brand-nowrap/expected.js b/test/fixtures/requireesm-brand-nowrap/expected.js index 54eec27..1fb11ae 100644 --- a/test/fixtures/requireesm-brand-nowrap/expected.js +++ b/test/fixtures/requireesm-brand-nowrap/expected.js @@ -1,13 +1,13 @@ import * as _nobinding2 from "nobinding"; -var _nobinding = _nobinding2; -try { - if ("default" in _nobinding2) _nobinding = _nobinding2.default; -} catch (e) {} +var _nobinding = _nobinding2.__cjs ? _nobinding2.default : "default" in _nobinding2 && !_nobinding2.__esModule ? new Proxy(_nobinding2, { + get: (t, k, r) => k === "__esModule" || Reflect.get(t, k, r), + has: (t, k) => k === "__esModule" || Reflect.has(t, k) +}) : _nobinding2; import * as _fs2 from "fs"; -var _fs = _fs2; -try { - if ("default" in _fs2) _fs = _fs2.default; -} catch (e) {} +var _fs = _fs2.__cjs ? _fs2.default : "default" in _fs2 && !_fs2.__esModule ? new Proxy(_fs2, { + get: (t, k, r) => k === "__esModule" || Reflect.get(t, k, r), + has: (t, k) => k === "__esModule" || Reflect.has(t, k) +}) : _fs2; import * as _thing from "thing"; _nobinding; const { diff --git a/transform-cjs-dew.js b/transform-cjs-dew.js index 8f6c1d6..7880624 100644 --- a/transform-cjs-dew.js +++ b/transform-cjs-dew.js @@ -790,13 +790,26 @@ module.exports = function ({ types: t }) { for (let i = state.deps.length - 1; i >= 0; i--) { const dep = state.deps[i]; if (dep.ns && dep.mid.name !== dep.id.name) { - unshiftBody(path, t.tryStatement(t.blockStatement([ - t.ifStatement( - t.binaryExpression('in', t.stringLiteral('default'), dep.mid), - t.expressionStatement(t.assignmentExpression('=', dep.id, t.memberExpression(dep.mid, defaultIdentifier))) - ) - ]), t.catchClause(t.identifier('e'), t.blockStatement([])))); - unshiftBody(path, t.variableDeclaration('var', [t.variableDeclarator(dep.id, dep.mid)])); + unshiftBody(path, t.variableDeclaration('var', [ + t.variableDeclarator(dep.id, t.conditionalExpression( + t.memberExpression(dep.mid, t.identifier('__cjs')), + t.memberExpression(dep.mid, t.identifier('default')), + t.conditionalExpression( + t.logicalExpression('&&', t.binaryExpression('in', t.stringLiteral('default'), dep.mid), t.unaryExpression('!', t.memberExpression(dep.mid, t.identifier('__esModule')))), + t.newExpression(t.identifier('Proxy'), [dep.mid, t.objectExpression([ + t.objectProperty(t.identifier('get'), t.arrowFunctionExpression([t.identifier('t'), t.identifier('k'), t.identifier('r')], t.logicalExpression('||', + t.binaryExpression('===', t.identifier('k'), esModuleLiteral), + t.callExpression(t.memberExpression(t.identifier('Reflect'), t.identifier('get')), [t.identifier('t'), t.identifier('k'), t.identifier('r')]) + ))), + t.objectProperty(t.identifier('has'), t.arrowFunctionExpression([t.identifier('t'), t.identifier('k')], t.logicalExpression('||', + t.binaryExpression('===', t.identifier('k'), esModuleLiteral), + t.callExpression(t.memberExpression(t.identifier('Reflect'), t.identifier('has')), [t.identifier('t'), t.identifier('k')]) + ))) + ])]), + dep.mid + ) + )) + ])); } unshiftBody(path, t.importDeclaration([ From e7bd13a087fb09de55833f658464acc7dddb4d9a Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sat, 6 Jul 2024 12:40:07 -0700 Subject: [PATCH 3/4] use __cjsModule --- test/fixtures/requireesm-brand-nowrap/expected.js | 6 +++--- test/fixtures/requireesm-brand/expected.js | 4 ++-- transform-cjs-dew.js | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/fixtures/requireesm-brand-nowrap/expected.js b/test/fixtures/requireesm-brand-nowrap/expected.js index 1fb11ae..548c5d8 100644 --- a/test/fixtures/requireesm-brand-nowrap/expected.js +++ b/test/fixtures/requireesm-brand-nowrap/expected.js @@ -1,10 +1,10 @@ import * as _nobinding2 from "nobinding"; -var _nobinding = _nobinding2.__cjs ? _nobinding2.default : "default" in _nobinding2 && !_nobinding2.__esModule ? new Proxy(_nobinding2, { +var _nobinding = _nobinding2.__cjsModule ? _nobinding2.default : "default" in _nobinding2 && !_nobinding2.__esModule ? new Proxy(_nobinding2, { get: (t, k, r) => k === "__esModule" || Reflect.get(t, k, r), has: (t, k) => k === "__esModule" || Reflect.has(t, k) }) : _nobinding2; import * as _fs2 from "fs"; -var _fs = _fs2.__cjs ? _fs2.default : "default" in _fs2 && !_fs2.__esModule ? new Proxy(_fs2, { +var _fs = _fs2.__cjsModule ? _fs2.default : "default" in _fs2 && !_fs2.__esModule ? new Proxy(_fs2, { get: (t, k, r) => k === "__esModule" || Reflect.get(t, k, r), has: (t, k) => k === "__esModule" || Reflect.has(t, k) }) : _fs2; @@ -18,4 +18,4 @@ const { } = _thing; readFileSync("asdf"); export default {}; -export const __cjs = true; \ No newline at end of file +export const __cjsModule = true; \ No newline at end of file diff --git a/test/fixtures/requireesm-brand/expected.js b/test/fixtures/requireesm-brand/expected.js index 71b62a6..28599ce 100644 --- a/test/fixtures/requireesm-brand/expected.js +++ b/test/fixtures/requireesm-brand/expected.js @@ -6,11 +6,11 @@ var exports = {}, export function dew() { if (_dewExec) return exports; _dewExec = true; - var _nobinding = _nobinding2.__cjs ? _nobinding2.default : "default" in _nobinding2 && !_nobinding2.__esModule ? new Proxy(_nobinding2, { + var _nobinding = _nobinding2.__cjsModule ? _nobinding2.default : "default" in _nobinding2 && !_nobinding2.__esModule ? new Proxy(_nobinding2, { get: (t, k, r) => k === "__esModule" || Reflect.get(t, k, r), has: (t, k) => k === "__esModule" || Reflect.has(t, k) }) : _nobinding2; - var _fs = _fs2.__cjs ? _fs2.default : "default" in _fs2 && !_fs2.__esModule ? new Proxy(_fs2, { + var _fs = _fs2.__cjsModule ? _fs2.default : "default" in _fs2 && !_fs2.__esModule ? new Proxy(_fs2, { get: (t, k, r) => k === "__esModule" || Reflect.get(t, k, r), has: (t, k) => k === "__esModule" || Reflect.has(t, k) }) : _fs2; diff --git a/transform-cjs-dew.js b/transform-cjs-dew.js index 7880624..59884d1 100644 --- a/transform-cjs-dew.js +++ b/transform-cjs-dew.js @@ -792,7 +792,7 @@ module.exports = function ({ types: t }) { if (dep.ns && dep.mid.name !== dep.id.name) { unshiftBody(path, t.variableDeclaration('var', [ t.variableDeclarator(dep.id, t.conditionalExpression( - t.memberExpression(dep.mid, t.identifier('__cjs')), + t.memberExpression(dep.mid, t.identifier('__cjsModule')), t.memberExpression(dep.mid, t.identifier('default')), t.conditionalExpression( t.logicalExpression('&&', t.binaryExpression('in', t.stringLiteral('default'), dep.mid), t.unaryExpression('!', t.memberExpression(dep.mid, t.identifier('__esModule')))), @@ -852,7 +852,7 @@ module.exports = function ({ types: t }) { } if (state.opts.cjsMarker) { - exportDeclarations.push(t.variableDeclarator(t.identifier('__cjs'), t.booleanLiteral(true))); + exportDeclarations.push(t.variableDeclarator(t.identifier('__cjsModule'), t.booleanLiteral(true))); } if (exportDeclarations.length) @@ -879,7 +879,7 @@ module.exports = function ({ types: t }) { innerWrapper.push( t.variableDeclaration('var', [ t.variableDeclarator(dep.id, t.conditionalExpression( - t.memberExpression(dep.mid, t.identifier('__cjs')), + t.memberExpression(dep.mid, t.identifier('__cjsModule')), t.memberExpression(dep.mid, t.identifier('default')), t.conditionalExpression( t.logicalExpression('&&', t.binaryExpression('in', t.stringLiteral('default'), dep.mid), t.unaryExpression('!', t.memberExpression(dep.mid, t.identifier('__esModule')))), From afffe547830470e536a3f4aabb005070beb544c9 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Thu, 11 Jul 2024 00:00:12 -0700 Subject: [PATCH 4/4] use in check for __esModule --- test/fixtures/requireesm-brand-nowrap/expected.js | 4 ++-- test/fixtures/requireesm-brand/expected.js | 4 ++-- transform-cjs-dew.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/fixtures/requireesm-brand-nowrap/expected.js b/test/fixtures/requireesm-brand-nowrap/expected.js index 548c5d8..0475ca1 100644 --- a/test/fixtures/requireesm-brand-nowrap/expected.js +++ b/test/fixtures/requireesm-brand-nowrap/expected.js @@ -1,10 +1,10 @@ import * as _nobinding2 from "nobinding"; -var _nobinding = _nobinding2.__cjsModule ? _nobinding2.default : "default" in _nobinding2 && !_nobinding2.__esModule ? new Proxy(_nobinding2, { +var _nobinding = _nobinding2.__cjsModule ? _nobinding2.default : "default" in _nobinding2 && !("__esModule" in _nobinding2) ? new Proxy(_nobinding2, { get: (t, k, r) => k === "__esModule" || Reflect.get(t, k, r), has: (t, k) => k === "__esModule" || Reflect.has(t, k) }) : _nobinding2; import * as _fs2 from "fs"; -var _fs = _fs2.__cjsModule ? _fs2.default : "default" in _fs2 && !_fs2.__esModule ? new Proxy(_fs2, { +var _fs = _fs2.__cjsModule ? _fs2.default : "default" in _fs2 && !("__esModule" in _fs2) ? new Proxy(_fs2, { get: (t, k, r) => k === "__esModule" || Reflect.get(t, k, r), has: (t, k) => k === "__esModule" || Reflect.has(t, k) }) : _fs2; diff --git a/test/fixtures/requireesm-brand/expected.js b/test/fixtures/requireesm-brand/expected.js index 28599ce..864502e 100644 --- a/test/fixtures/requireesm-brand/expected.js +++ b/test/fixtures/requireesm-brand/expected.js @@ -6,11 +6,11 @@ var exports = {}, export function dew() { if (_dewExec) return exports; _dewExec = true; - var _nobinding = _nobinding2.__cjsModule ? _nobinding2.default : "default" in _nobinding2 && !_nobinding2.__esModule ? new Proxy(_nobinding2, { + var _nobinding = _nobinding2.__cjsModule ? _nobinding2.default : "default" in _nobinding2 && !("__esModule" in _nobinding2) ? new Proxy(_nobinding2, { get: (t, k, r) => k === "__esModule" || Reflect.get(t, k, r), has: (t, k) => k === "__esModule" || Reflect.has(t, k) }) : _nobinding2; - var _fs = _fs2.__cjsModule ? _fs2.default : "default" in _fs2 && !_fs2.__esModule ? new Proxy(_fs2, { + var _fs = _fs2.__cjsModule ? _fs2.default : "default" in _fs2 && !("__esModule" in _fs2) ? new Proxy(_fs2, { get: (t, k, r) => k === "__esModule" || Reflect.get(t, k, r), has: (t, k) => k === "__esModule" || Reflect.has(t, k) }) : _fs2; diff --git a/transform-cjs-dew.js b/transform-cjs-dew.js index 59884d1..3b72140 100644 --- a/transform-cjs-dew.js +++ b/transform-cjs-dew.js @@ -795,7 +795,7 @@ module.exports = function ({ types: t }) { t.memberExpression(dep.mid, t.identifier('__cjsModule')), t.memberExpression(dep.mid, t.identifier('default')), t.conditionalExpression( - t.logicalExpression('&&', t.binaryExpression('in', t.stringLiteral('default'), dep.mid), t.unaryExpression('!', t.memberExpression(dep.mid, t.identifier('__esModule')))), + t.logicalExpression('&&', t.binaryExpression('in', t.stringLiteral('default'), dep.mid), t.unaryExpression('!', t.binaryExpression('in', t.stringLiteral('__esModule'), dep.mid))), t.newExpression(t.identifier('Proxy'), [dep.mid, t.objectExpression([ t.objectProperty(t.identifier('get'), t.arrowFunctionExpression([t.identifier('t'), t.identifier('k'), t.identifier('r')], t.logicalExpression('||', t.binaryExpression('===', t.identifier('k'), esModuleLiteral), @@ -882,7 +882,7 @@ module.exports = function ({ types: t }) { t.memberExpression(dep.mid, t.identifier('__cjsModule')), t.memberExpression(dep.mid, t.identifier('default')), t.conditionalExpression( - t.logicalExpression('&&', t.binaryExpression('in', t.stringLiteral('default'), dep.mid), t.unaryExpression('!', t.memberExpression(dep.mid, t.identifier('__esModule')))), + t.logicalExpression('&&', t.binaryExpression('in', t.stringLiteral('default'), dep.mid), t.unaryExpression('!', t.binaryExpression('in', t.stringLiteral('__esModule'), dep.mid))), t.newExpression(t.identifier('Proxy'), [dep.mid, t.objectExpression([ t.objectProperty(t.identifier('get'), t.arrowFunctionExpression([t.identifier('t'), t.identifier('k'), t.identifier('r')], t.logicalExpression('||', t.binaryExpression('===', t.identifier('k'), esModuleLiteral),