diff --git a/etc/rollup/rollup-plugin-require-rewriter/require_rewriter.mjs b/etc/rollup/rollup-plugin-require-rewriter/require_rewriter.mjs index 68161963..7381a25f 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/package.json b/package.json index 727c4b12..01023111 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 9e25c0ad..a750bc14 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -57,18 +57,18 @@ const config = [ input, plugins: [ typescript(tsConfig), - requireRewriter({ isBrowser: true }), + requireRewriter({ target: 'browser' }), nodeResolve({ resolveOnly: [] }) ], output: { - file: 'lib/bson.browser.mjs', + file: 'lib/bson.mjs', format: 'esm', sourcemap: true } }, { input, - plugins: [typescript(tsConfig), requireRewriter(), nodeResolve({ resolveOnly: [] })], + plugins: [typescript(tsConfig), requireRewriter({ target: 'node' }), nodeResolve({ resolveOnly: [] })], output: { file: 'lib/bson.node.mjs', format: 'esm', diff --git a/test/node/exports.test.ts b/test/node/exports.test.ts index 55e1bd24..0b5c90be 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,32 @@ 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); + }; + } + + 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 1f5c161e..cb91ac40 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',