From eb7e1bc110d705c3990bd48ce351885c74e8aeaa Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 22 Apr 2025 13:51:39 -0400 Subject: [PATCH 1/3] test(NODE-6920): esm bundles do not have top-level await --- package.json | 2 +- rollup.config.mjs | 2 +- test/node/exports.test.ts | 28 ++++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 727c4b12a..01023111c 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "exports": { "browser": { "types": "./bson.d.ts", - "default": "./lib/bson.browser.mjs" + "default": "./lib/bson.mjs" }, "react-native": "./lib/bson.rn.cjs", "default": { diff --git a/rollup.config.mjs b/rollup.config.mjs index 9e25c0ada..f6e64fdf0 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -61,7 +61,7 @@ const config = [ nodeResolve({ resolveOnly: [] }) ], output: { - file: 'lib/bson.browser.mjs', + file: 'lib/bson.mjs', format: 'esm', sourcemap: true } diff --git a/test/node/exports.test.ts b/test/node/exports.test.ts index 55e1bd249..ca02c6417 100644 --- a/test/node/exports.test.ts +++ b/test/node/exports.test.ts @@ -3,6 +3,7 @@ import * as BSON from '../register-bson'; import { sorted, byStrings } from './tools/utils'; import { readFile } from 'fs/promises'; import { resolve } from 'path'; +import * as child_process from 'node:child_process'; const EXPECTED_EXPORTS = [ // This is our added web indicator not a real export but a small exception for this test. @@ -41,6 +42,7 @@ const EXPECTED_EXPORTS = [ ]; const EXPECTED_EJSON_EXPORTS = ['parse', 'stringify', 'serialize', 'deserialize']; +const NODE_MAJOR = Number(process.versions.node.split('.')[0]); describe('bson entrypoint', () => { it('should export all and only the expected keys in expected_exports', () => { @@ -96,4 +98,30 @@ describe('bson entrypoint', () => { expect(pkg).nested.property('exports.default.types', './bson.d.ts'); }); }); + + function testSyncESMImport(name, module) { + return () => { + const child = child_process.spawnSync( + 'node', + ['--experimental-print-required-tla', '--print', `require('${module}')`], + { encoding: 'utf-8' } + ); + + expect( + child.status, + `expected to be able to 'require' to import the ${name} ESM because there should be no top-level await:\n` + + child.stderr + ).to.equal(0); + }; + } + + for (const test of [ + it( + 'browser bundle does not use top-level await', + testSyncESMImport('browser', './lib/bson.mjs') + ), + it('node bundle does not use top-level await', testSyncESMImport('node', './lib/bson.node.mjs')) + ]) { + if (NODE_MAJOR < 22) test.skip(); + } }); From ade2e7a8ac7f23ce72ea4fe7ea4738578da4e7b6 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 22 Apr 2025 14:07:01 -0400 Subject: [PATCH 2/3] chore: fix files list --- test/node/exports.test.ts | 20 +++++++++++--------- test/node/release.test.ts | 10 ++++++---- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/test/node/exports.test.ts b/test/node/exports.test.ts index ca02c6417..0b5c90beb 100644 --- a/test/node/exports.test.ts +++ b/test/node/exports.test.ts @@ -115,13 +115,15 @@ describe('bson entrypoint', () => { }; } - for (const test of [ - it( - 'browser bundle does not use top-level await', - testSyncESMImport('browser', './lib/bson.mjs') - ), - it('node bundle does not use top-level await', testSyncESMImport('node', './lib/bson.node.mjs')) - ]) { - if (NODE_MAJOR < 22) test.skip(); - } + const itFn = NODE_MAJOR < 22 ? it.skip : it; + + itFn( + 'browser bundle does not use top-level await', + testSyncESMImport('browser', './lib/bson.mjs') + ); + + itFn( + 'node bundle does not use top-level await', + testSyncESMImport('node', './lib/bson.node.mjs') + ); }); diff --git a/test/node/release.test.ts b/test/node/release.test.ts index 1f5c161eb..cb91ac404 100644 --- a/test/node/release.test.ts +++ b/test/node/release.test.ts @@ -11,14 +11,16 @@ const REQUIRED_FILES = [ 'README.md', 'bson.d.ts', 'etc/prepare.js', - 'lib/bson.bundle.js', 'lib/bson.bundle.js.map', - 'lib/bson.cjs', + 'lib/bson.bundle.js', 'lib/bson.cjs.map', - 'lib/bson.mjs', + 'lib/bson.cjs', 'lib/bson.mjs.map', - 'lib/bson.rn.cjs', + 'lib/bson.mjs', + 'lib/bson.node.mjs.map', + 'lib/bson.node.mjs', 'lib/bson.rn.cjs.map', + 'lib/bson.rn.cjs', 'package.json', 'src/binary.ts', 'src/bson_value.ts', From 57dda903b8b86169c0d3981c46f05a88251aa9af Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 24 Apr 2025 13:22:42 -0400 Subject: [PATCH 3/3] chore: require target --- .../rollup-plugin-require-rewriter/require_rewriter.mjs | 8 ++++++-- rollup.config.mjs | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/etc/rollup/rollup-plugin-require-rewriter/require_rewriter.mjs b/etc/rollup/rollup-plugin-require-rewriter/require_rewriter.mjs index 681619636..7381a25f8 100644 --- a/etc/rollup/rollup-plugin-require-rewriter/require_rewriter.mjs +++ b/etc/rollup/rollup-plugin-require-rewriter/require_rewriter.mjs @@ -1,3 +1,4 @@ +import assert from 'node:assert/strict'; import MagicString from 'magic-string'; const CRYPTO_IMPORT_ESM_SRC = `import { randomBytes as nodejsRandomBytes } from 'crypto';`; @@ -11,7 +12,10 @@ const CODE_TO_REPLACE = `const nodejsRandomBytes = (() => { } })();`; -export function requireRewriter({ isBrowser = false } = {}) { +/** @param {{ target: 'browser' | 'node'}} configuration - destination information that changes the replacement syntax used. */ +export function requireRewriter({ target }) { + assert.match(target, /^(node|browser)$/, 'target must be either "node" or "browser"'); + return { /** * Take the compiled source code input; types are expected to already have been removed @@ -35,7 +39,7 @@ export function requireRewriter({ isBrowser = false } = {}) { // MagicString lets us edit the source code and still generate an accurate source map const magicString = new MagicString(code); - magicString.overwrite(start, end, isBrowser ? BROWSER_ESM_SRC : CRYPTO_IMPORT_ESM_SRC); + magicString.overwrite(start, end, target === 'browser' ? BROWSER_ESM_SRC : CRYPTO_IMPORT_ESM_SRC); return { code: magicString.toString(), diff --git a/rollup.config.mjs b/rollup.config.mjs index f6e64fdf0..a750bc149 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -57,7 +57,7 @@ const config = [ input, plugins: [ typescript(tsConfig), - requireRewriter({ isBrowser: true }), + requireRewriter({ target: 'browser' }), nodeResolve({ resolveOnly: [] }) ], output: { @@ -68,7 +68,7 @@ const config = [ }, { input, - plugins: [typescript(tsConfig), requireRewriter(), nodeResolve({ resolveOnly: [] })], + plugins: [typescript(tsConfig), requireRewriter({ target: 'node' }), nodeResolve({ resolveOnly: [] })], output: { file: 'lib/bson.node.mjs', format: 'esm',