From 6856bd99f8767bc21d7cf0d8474e8658fb8be888 Mon Sep 17 00:00:00 2001 From: Jonathan Romano Date: Tue, 16 Jan 2024 22:17:41 -0500 Subject: [PATCH] Fix node 20 reexports (#30) Resolves #29 --------- Co-authored-by: Trent Mick Co-authored-by: rochdev --- .eslintrc.yaml | 5 +++++ lib/get-exports.js | 30 +++++++++++++++++++++++++++--- test/fixtures/circular-a.js | 18 ++++++++++++++++++ test/fixtures/circular-b.js | 15 +++++++++++++++ test/fixtures/reexport.js | 15 +++++++++++++++ test/hook/circular-imports.mjs | 11 +++++++++++ test/hook/import-reexport-cjs.mjs | 11 +++++++++++ 7 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 test/fixtures/circular-a.js create mode 100644 test/fixtures/circular-b.js create mode 100644 test/fixtures/reexport.js create mode 100644 test/hook/circular-imports.mjs create mode 100644 test/hook/import-reexport-cjs.mjs diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 8d9221c..bd522c0 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -16,3 +16,8 @@ rules: extends: - "standard" + +ignorePatterns: + - test/fixtures/circular-a.js + - test/fixtures/circular-b.js + - test/fixtures/reexport.js diff --git a/lib/get-exports.js b/lib/get-exports.js index aa4e3c3..59ea5cc 100644 --- a/lib/get-exports.js +++ b/lib/get-exports.js @@ -3,12 +3,36 @@ const getEsmExports = require('./get-esm-exports.js') const { parse: getCjsExports } = require('cjs-module-lexer') const fs = require('fs') -const { fileURLToPath } = require('url') +const { fileURLToPath, pathToFileURL } = require('url') function addDefault (arr) { return Array.from(new Set(['default', ...arr])) } +const urlsBeingProcessed = new Set() // Guard against circular imports. + +async function getFullCjsExports (url, context, parentLoad, source) { + if (urlsBeingProcessed.has(url)) { + return [] + } + urlsBeingProcessed.add(url) + + const ex = getCjsExports(source) + const full = Array.from(new Set([ + ...addDefault(ex.exports), + ...(await Promise.all(ex.reexports.map(re => getExports(({ + url: (/^(..?($|\/|\\))/).test(re) + ? pathToFileURL(require.resolve(fileURLToPath(new URL(re, url)))).toString() + : pathToFileURL(require.resolve(re)).toString(), + context, + parentLoad + }))))).flat() + ])) + + urlsBeingProcessed.delete(url) + return full +} + /** * Inspects a module for its type (commonjs or module), attempts to get the * source code for said module from the loader API, and parses the result @@ -56,7 +80,7 @@ async function getExports ({ url, context, parentLoad, defaultAs = 'default' }) return getEsmExports({ moduleSource: source, defaultAs }) } if (format === 'commonjs') { - return addDefault(getCjsExports(source).exports) + return getFullCjsExports(url, context, parentLoad, source) } // At this point our `format` is either undefined or not known by us. Fall @@ -67,7 +91,7 @@ async function getExports ({ url, context, parentLoad, defaultAs = 'default' }) // isn't set at first and yet we have an ESM module with no exports. // I couldn't construct an example that would do this, so maybe it's // impossible? - return addDefault(getCjsExports(source).exports) + return getFullCjsExports(url, context, parentLoad, source) } } diff --git a/test/fixtures/circular-a.js b/test/fixtures/circular-a.js new file mode 100644 index 0000000..f267ad5 --- /dev/null +++ b/test/fixtures/circular-a.js @@ -0,0 +1,18 @@ +// The following is generated by tslib. __exportStar is a format which cjs-module-lexer exposes as a reexport +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); + + +__exportStar(require("./circular-b"), exports); + +exports.foo = 42; + diff --git a/test/fixtures/circular-b.js b/test/fixtures/circular-b.js new file mode 100644 index 0000000..597816d --- /dev/null +++ b/test/fixtures/circular-b.js @@ -0,0 +1,15 @@ +// The following is generated by tslib. __exportStar is a format which cjs-module-lexer exposes as a reexport +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); + +__exportStar(require("./circular-a"), exports); + diff --git a/test/fixtures/reexport.js b/test/fixtures/reexport.js new file mode 100644 index 0000000..01732ab --- /dev/null +++ b/test/fixtures/reexport.js @@ -0,0 +1,15 @@ +// The following is generated by tslib. __exportStar is a format which cjs-module-lexer exposes as a reexport +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __exportStar = (this && this.__exportStar) || function(m, exports) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); +}; +Object.defineProperty(exports, "__esModule", { value: true }); + +__exportStar(require("./something"), exports); + diff --git a/test/hook/circular-imports.mjs b/test/hook/circular-imports.mjs new file mode 100644 index 0000000..f407fb5 --- /dev/null +++ b/test/hook/circular-imports.mjs @@ -0,0 +1,11 @@ +import { foo } from '../fixtures/circular-b.js' +import Hook from '../../index.js' +import { strictEqual } from 'assert' + +Hook((exports, name) => { + if (name.match(/circular-[ab].js/)) { + exports.foo += 15 + } +}) + +strictEqual(foo, 57) diff --git a/test/hook/import-reexport-cjs.mjs b/test/hook/import-reexport-cjs.mjs new file mode 100644 index 0000000..8921073 --- /dev/null +++ b/test/hook/import-reexport-cjs.mjs @@ -0,0 +1,11 @@ +import { foo } from '../fixtures/reexport.js' +import Hook from '../../index.js' +import { strictEqual } from 'assert' + +Hook((exports, name) => { + if (name.match(/reexport.js/)) { + exports.foo += 15 + } +}) + +strictEqual(foo, 57)