diff --git a/docs/bundler/index.mdx b/docs/bundler/index.mdx index b9a1837b9a10..55431c940d95 100644 --- a/docs/bundler/index.mdx +++ b/docs/bundler/index.mdx @@ -562,6 +562,8 @@ Each `import()` of a bundled JavaScript module also becomes its own chunk. Tree With `target: "bun"`, a `require()` of a bundled ES module becomes a chunk of its own as well. The call is emitted as `import.meta.require("./chunk-.js")` and stays synchronous — Bun evaluates the chunk when the call runs — so a `require()` inside a function that never runs keeps its module out of the startup working set entirely. Tree shaking treats these chunks like `import()` chunks. `require()` of a CommonJS module is unaffected and keeps returning `module.exports`. A `require()` cycle between such chunks follows Bun's runtime `require()` of an ES module: a module required again while its chunk is still being loaded sees the partially initialized CommonJS placeholder (`{}`) rather than the live bindings the in-chunk wrapper provides. For other targets — and with `splitRequire: false` (`--no-split-require`) — the required module is inlined into the calling chunk behind a lazy wrapper instead, because the call must return synchronously. +Either way, `require()` of an ES module returns what it returns in Node.js and in Bun's runtime: a copy of the module namespace object with `__esModule: true` added, or, when the module exports the name `module.exports` (`export { EventEmitter as "module.exports" }`), the value of that export. The same holds for the bundle itself: with `format: "cjs"`, an entry point that exports that name assigns the value to the bundle's `module.exports`, so code that loads the output with `require()` gets it too. A polyfill that `target: "browser"` substitutes for a Node.js builtin exports that name when the builtin's `module.exports` is more than its set of named exports, so `require("events")` is the `EventEmitter` class, `require("assert")` is callable, and `require("path")` is the same object as `import path from "path"`. + #### Tree-shaking `import()` and `require()` results When the result of a string-literal `import()` or `require()` of an ES module is only ever used to read specific exports, the exports nobody reads are dropped from that module — and whatever only they depended on tree-shakes with them. This applies with and without `splitting`; the module is still loaded lazily, exactly where the call is written. diff --git a/src/ast/import_record.rs b/src/ast/import_record.rs index 915e3631d614..9c59fda7dc4a 100644 --- a/src/ast/import_record.rs +++ b/src/ast/import_record.rs @@ -103,6 +103,9 @@ bitflags::bitflags! { /// `IS_UNUSED` for its own reasons (an unused TypeScript import, a macro /// import), and those records must never be resolved. const IS_BARREL_DEFERRED = 1 << 18; + + /// `require()` of an ES module that exports `module.exports`: `__toCommonJS(exports_foo, 1)`. + const REQUIRE_MODULE_EXPORTS_EXPORT = 1 << 30; } } diff --git a/src/bundler/LinkerContext.rs b/src/bundler/LinkerContext.rs index 1c9c05f6ad3b..bfb6291fa486 100644 --- a/src/bundler/LinkerContext.rs +++ b/src/bundler/LinkerContext.rs @@ -4182,10 +4182,15 @@ impl<'a> LinkerContext<'a> { // Its own chunk: the name is a binding of the loaded module. record.source_index.is_valid() && !self.is_external_dynamic_import(record, source_index) - // `require()` returns this export, not the namespace. - && !(record.kind == ImportKind::Require - && self.graph.meta.items_resolved_exports()[record.source_index.get() as usize] - .contains(b"module.exports")) + && !self.require_returns_module_exports_export(record) + } + + /// `require()` of an ES module that exports `module.exports` returns that export, not the namespace. + pub(crate) fn require_returns_module_exports_export(&self, record: &ImportRecord) -> bool { + record.kind == ImportKind::Require + && record.source_index.is_valid() + && self.graph.meta.items_resolved_exports()[record.source_index.get() as usize] + .contains(b"module.exports") } /// Whether the export an item of such a record matched can stand in for it. diff --git a/src/bundler/linker_context/postProcessJSChunk.rs b/src/bundler/linker_context/postProcessJSChunk.rs index 1455395e4bc6..a8da34634d9a 100644 --- a/src/bundler/linker_context/postProcessJSChunk.rs +++ b/src/bundler/linker_context/postProcessJSChunk.rs @@ -1233,6 +1233,39 @@ pub(crate) fn generate_entry_point_tail_js( _ => {} } + // "module.exports = __toCommonJS(exports, 1);" once the body ran, so an external `require()` gets that export too. + if flags.wrap != crate::WrapKind::Cjs + && flags.force_include_exports_for_entry_point + && c.graph.meta.items_resolved_exports()[source_index as usize] + .contains(b"module.exports") + { + stmts.push(Stmt::assign( + Expr::init( + E::Dot { + target: Expr::init_identifier( + c.unbound_module_ref, + bun_ast::Loc::EMPTY, + ), + name: b"exports".into(), + name_loc: bun_ast::Loc::EMPTY, + ..Default::default() + }, + bun_ast::Loc::EMPTY, + ), + Expr::init( + E::Call { + target: Expr::init_identifier(to_common_js_ref, bun_ast::Loc::EMPTY), + args: bun_ast::ExprNodeList::from_slice(&[ + Expr::init_identifier(ast.exports_ref, bun_ast::Loc::EMPTY), + Expr::init(E::Number::new(1.0), bun_ast::Loc::EMPTY), + ]), + ..Default::default() + }, + bun_ast::Loc::EMPTY, + ), + )); + } + // TODO: // If we are generating CommonJS for node, encode the known export names in // a form that node can understand them. This relies on the specific behavior diff --git a/src/bundler/linker_context/scanImportsAndExports.rs b/src/bundler/linker_context/scanImportsAndExports.rs index c2de4b8dc18e..308f965d634c 100644 --- a/src/bundler/linker_context/scanImportsAndExports.rs +++ b/src/bundler/linker_context/scanImportsAndExports.rs @@ -532,6 +532,7 @@ pub(crate) fn scan_imports_and_exports( exports_kind, named_exports, ast_flags: ast_flags_list, + sources: input_files, }); } export_star_ctx.as_mut().unwrap().add_exports( @@ -700,6 +701,8 @@ pub(crate) fn scan_imports_and_exports( || !record.source_index.is_valid() || col_ref!(flags)[record.source_index.get() as usize].wrap != WrapKind::Esm || col_ref!(exports_kind)[record.source_index.get() as usize] != ExportsKind::Esm + // The value is that export, and `typeof` or `if (x)` observes it. + || this.require_returns_module_exports_export(record) { continue; } @@ -1269,10 +1272,18 @@ pub(crate) fn scan_imports_and_exports( // and subtle set of transpiler interop issues. See for example // https://github.com/evanw/esbuild/issues/1591. if kind == ImportKind::Require { + let mut record_flags = ImportRecordFlags::WRAP_WITH_TO_COMMONJS; + if this.require_returns_module_exports_export( + &col_ref!(import_records_list)[id].as_slice() + [import_record_index as usize], + ) { + record_flags |= + ImportRecordFlags::REQUIRE_MODULE_EXPORTS_EXPORT; + } col!(import_records_list)[id].as_mut_slice() [import_record_index as usize] .flags - .insert(ImportRecordFlags::WRAP_WITH_TO_COMMONJS); + .insert(record_flags); to_common_js_uses += 1; } } @@ -1593,6 +1604,7 @@ struct ExportStarContext<'a> { imports_to_bind: *mut [RefImportData], export_star_records: *mut [bun_alloc::AstVec], ast_flags: *mut [AstFlags], + sources: *mut [Source<'a>], } impl<'a> ExportStarContext<'a> { @@ -1647,6 +1659,14 @@ impl<'a> ExportStarContext<'a> { continue; } + let is_node_fallback = { + let path = &col_ref!(self.sources)[other_id].path; + path.namespace == b"node" + && path + .text + .starts_with(crate::bun_node_fallbacks::IMPORT_PATH) + }; + // Collect (alias, name) pairs so the // loop body can mutably borrow `resolved_exports` / `imports_to_bind`. let exports_len = col_ref!(self.named_exports)[other_id].keys().len(); @@ -1664,6 +1684,10 @@ impl<'a> ExportStarContext<'a> { if alias_slice == b"default" && !reexports_default { continue; } + // A builtin has no such export: its polyfill names its own `require()` value with it. + if alias_slice == b"module.exports" && !reexports_default && is_node_fallback { + continue; + } // This export star is shadowed if any file in the stack has a matching real named export for prev in self.stack.iter() { diff --git a/src/js_printer/lib.rs b/src/js_printer/lib.rs index 067c3be8510c..723f7038855b 100644 --- a/src/js_printer/lib.rs +++ b/src/js_printer/lib.rs @@ -2846,6 +2846,14 @@ pub(crate) mod __gated_printer { } self.print_symbol(meta.exports_ref); if wrap_with_to_cjs { + if record + .flags + .contains(ImportRecordFlags::REQUIRE_MODULE_EXPORTS_EXPORT) + { + self.print(b","); + self.print_space(); + self.print(b"1"); + } self.print(b")"); } } diff --git a/src/node-fallbacks/README.md b/src/node-fallbacks/README.md index d2c492a04aa5..ec5c51b8941d 100644 --- a/src/node-fallbacks/README.md +++ b/src/node-fallbacks/README.md @@ -4,6 +4,17 @@ When using `bun build --target=browser`, if you attempt to import a Node.js modu For example, if you import `zlib`, the `node-fallbacks/zlib.js` file will be loaded. +## `require()` shape + +The polyfills are ES modules. `require("events")` in bundled CommonJS code must still be what it is in Node.js (the `EventEmitter` class, not a namespace object), so each polyfill whose Node.js `module.exports` is not just its set of named exports names that value with the `require(esm)` interop export: + +```js +export default EventEmitter; +export { EventEmitter as "module.exports" }; +``` + +The bundler returns that export from `require()`, as Node.js and Bun's runtime do. Keep it the same value as the default export. The export stays inside the polyfill: `export * from "events"` in user code does not re-export it, because the Node.js builtin has no export with that name. + ## Not used by Bun's runtime These files are _not_ used by Bun's runtime. They are only used for the `bun build --target=browser` command. diff --git a/src/node-fallbacks/assert.js b/src/node-fallbacks/assert.js index 0181e54cea6c..c88fed9b1464 100644 --- a/src/node-fallbacks/assert.js +++ b/src/node-fallbacks/assert.js @@ -1,4 +1,4 @@ -import * as assert from "./node_modules/assert"; +import assert from "./node_modules/assert"; export const { AssertionError, @@ -23,3 +23,4 @@ export const { throws, } = assert; export default assert; +export { assert as "module.exports" }; diff --git a/src/node-fallbacks/console.js b/src/node-fallbacks/console.js index 34cc54b56528..2089bc2e4350 100644 --- a/src/node-fallbacks/console.js +++ b/src/node-fallbacks/console.js @@ -1 +1,3 @@ +var console = globalThis.console; export default console; +export { console as "module.exports" }; diff --git a/src/node-fallbacks/events.js b/src/node-fallbacks/events.js index 60a19d471083..fe265dcb67ba 100644 --- a/src/node-fallbacks/events.js +++ b/src/node-fallbacks/events.js @@ -539,6 +539,7 @@ export { getMaxListeners, EventEmitter as init, listenerCount, + EventEmitter as "module.exports", once, setMaxListeners, }; diff --git a/src/node-fallbacks/http.js b/src/node-fallbacks/http.js index 39a71744a1d1..aae0a0b87a97 100644 --- a/src/node-fallbacks/http.js +++ b/src/node-fallbacks/http.js @@ -1,3 +1,4 @@ import http from "stream-http"; export var { request, get, ClientRequest, IncomingMessage, Agent, globalAgent, STATUS_CODES, METHODS } = http; export default http; +export { http as "module.exports" }; diff --git a/src/node-fallbacks/https.js b/src/node-fallbacks/https.js index f7c4617ba02f..986d2c4183c7 100644 --- a/src/node-fallbacks/https.js +++ b/src/node-fallbacks/https.js @@ -1,4 +1,4 @@ -import * as https from "https-browserify"; +import https from "https-browserify"; export var { Agent, ClientRequest, @@ -19,3 +19,4 @@ export var { } = https; export default https; +export { https as "module.exports" }; diff --git a/src/node-fallbacks/net.js b/src/node-fallbacks/net.js index 4e5c9867eb69..dcc0ed10e750 100644 --- a/src/node-fallbacks/net.js +++ b/src/node-fallbacks/net.js @@ -53,8 +53,10 @@ export function isIP(s) { return 0; } -export default { +const net = { isIPv4, isIPv6, isIP, }; +export default net; +export { net as "module.exports" }; diff --git a/src/node-fallbacks/path.js b/src/node-fallbacks/path.js index bab0d6181698..3876ae0dc976 100644 --- a/src/node-fallbacks/path.js +++ b/src/node-fallbacks/path.js @@ -529,5 +529,5 @@ export const posix = /* @__PURE__ */ (p => ((p.posix = p), p))({ posix: null, }); -export { delimiter, sep }; +export { delimiter, posix as "module.exports", sep }; export default posix; diff --git a/src/node-fallbacks/punycode.js b/src/node-fallbacks/punycode.js index afbdd6821239..73a7ce143871 100644 --- a/src/node-fallbacks/punycode.js +++ b/src/node-fallbacks/punycode.js @@ -1,2 +1,2 @@ export * from "./node_modules/punycode"; -export { default } from "./node_modules/punycode"; +export { default, default as "module.exports" } from "./node_modules/punycode"; diff --git a/src/node-fallbacks/querystring.js b/src/node-fallbacks/querystring.js index a5b7d3e42aab..b2c4e39ec7fa 100644 --- a/src/node-fallbacks/querystring.js +++ b/src/node-fallbacks/querystring.js @@ -1,3 +1,4 @@ import querystring_module from "querystring-es3"; export var { unescapeBuffer, unescape, escape, stringify, encode, parse, decode } = querystring_module; -export * as default from "querystring-es3"; +export default querystring_module; +export { querystring_module as "module.exports" }; diff --git a/src/node-fallbacks/string_decoder.js b/src/node-fallbacks/string_decoder.js index 6e0a2c847032..f7bb5487bd63 100644 --- a/src/node-fallbacks/string_decoder.js +++ b/src/node-fallbacks/string_decoder.js @@ -1,2 +1,4 @@ // TODO: This depends on a separate buffer polyfill -export { StringDecoder, StringDecoder as default } from "./node_modules/string_decoder"; +import string_decoder from "./node_modules/string_decoder"; +export var { StringDecoder } = string_decoder; +export { StringDecoder as default, string_decoder as "module.exports" }; diff --git a/src/node-fallbacks/sys.js b/src/node-fallbacks/sys.js index 228f88ec5a35..1e9e113bf14f 100644 --- a/src/node-fallbacks/sys.js +++ b/src/node-fallbacks/sys.js @@ -3,5 +3,7 @@ * * Imported on usage in `bun build --target=browser` */ +import util from "util"; export * from "util"; -export * as default from "util"; +export { util as "module.exports" }; +export default util; diff --git a/src/node-fallbacks/tty.js b/src/node-fallbacks/tty.js index 05ded0b9add4..ff08eb1549e0 100644 --- a/src/node-fallbacks/tty.js +++ b/src/node-fallbacks/tty.js @@ -5,5 +5,6 @@ function WriteStream() { function ReadStream() { throw new Error("tty.ReadStream is not implemented for browsers"); } -export { ReadStream, WriteStream, isatty }; -export default { ReadStream, WriteStream, isatty }; +const tty = { ReadStream, WriteStream, isatty }; +export { ReadStream, WriteStream, isatty, tty as "module.exports" }; +export default tty; diff --git a/src/node-fallbacks/url.js b/src/node-fallbacks/url.js index 555ab07c2407..409ddde830b2 100644 --- a/src/node-fallbacks/url.js +++ b/src/node-fallbacks/url.js @@ -738,12 +738,13 @@ export { URLSearchParams, Url as Url, urlFormat as format, + urlModule as "module.exports", urlParse as parse, urlResolve as resolve, urlResolveObject as resolveObject, }; -export default { +const urlModule = { parse: urlParse, resolve: urlResolve, resolveObject: urlResolveObject, @@ -756,3 +757,4 @@ export default { URL, URLSearchParams, }; +export default urlModule; diff --git a/src/node-fallbacks/util.js b/src/node-fallbacks/util.js index 3be2f8754cc8..b4ae6a90d0cf 100644 --- a/src/node-fallbacks/util.js +++ b/src/node-fallbacks/util.js @@ -675,13 +675,35 @@ export function callbackify(original) { export const TextEncoder = /* @__PURE__ */ globalThis.TextEncoder; export const TextDecoder = /* @__PURE__ */ globalThis.TextDecoder; -export default { - TextEncoder, - TextDecoder, - promisify, +const util = { + format, + deprecate, + debuglog, + inspect, + types, + isArray, + isBoolean, + isNull, + isNullOrUndefined, + isNumber, + isString, + isSymbol, + isUndefined, + isRegExp, + isObject, + isDate, + isError, + isFunction, + isPrimitive, + isBuffer, log, inherits, _extend, + promisify, callbackifyOnRejected, callbackify, + TextEncoder, + TextDecoder, }; +export default util; +export { util as "module.exports" }; diff --git a/src/node-fallbacks/zlib.js b/src/node-fallbacks/zlib.js index 6e264b79be6c..6b65e94b9e8d 100644 --- a/src/node-fallbacks/zlib.js +++ b/src/node-fallbacks/zlib.js @@ -1,2 +1,2 @@ export * from "browserify-zlib"; -export { default } from "browserify-zlib"; +export { default, default as "module.exports" } from "browserify-zlib"; diff --git a/src/runtime.js b/src/runtime.js index 76a9d288c5bd..9e142d6ddb24 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -85,7 +85,14 @@ export var __toESM = (mod, isNodeMode, target) => { // Converts the module from ESM to CommonJS. This clones the input module // object with the addition of a non-enumerable "__esModule" property set // to "true", which overwrites any existing export named "__esModule". -export var __toCommonJS = from => { +// With `useModuleExports`, a set "module.exports" export (Node's `require(esm)` interop) is the result instead. +export var __toCommonJS = (from, useModuleExports) => { + if (useModuleExports) { + try { + var moduleExports = from["module.exports"]; + } catch {} + if (moduleExports != null) return moduleExports; + } var entry = (__moduleCache ??= new WeakMap()).get(from), desc; if (entry) return entry; diff --git a/src/runtime/bake/hmr-module.ts b/src/runtime/bake/hmr-module.ts index 713db57e8255..f845917fd0d2 100644 --- a/src/runtime/bake/hmr-module.ts +++ b/src/runtime/bake/hmr-module.ts @@ -79,6 +79,22 @@ export function staticExportNames(id: Id): string[] | null { return [...names]; } +/** Whether `id` has an export named `module.exports`, its own or through `export *`. A builtin polyfill ("node:" id) does not forward its own. */ +function exportsModuleExports(id: Id): boolean { + const queue: Id[] = [id]; + const visited = new Set(); + while (queue.length > 0) { + const current = queue.shift()!; + if (visited.has(current)) continue; + visited.add(current); + const unloaded = unloadedModuleRegistry[current]; + if (!Array.isArray(unloaded)) continue; + if (unloaded[ESMProps.exports].includes("module.exports")) return true; + for (const star of unloaded[ESMProps.stars]) if (!star.startsWith("node:")) queue.push(star); + } + return false; +} + interface HotAccept { modules: string[]; cb: HotAcceptFunction; @@ -146,7 +162,7 @@ export class HMRModule { require(id: Id) { try { const mod = loadModuleSync(id, true, this); - return mod.esm ? (mod.cjs ??= toCommonJS(mod.exports)) : mod.cjs.exports; + return mod.esm ? (mod.cjs ??= toCommonJS(mod.exports, exportsModuleExports(id))) : mod.cjs.exports; } catch (e: any) { if (e instanceof AsyncImportError) { e.message = `Cannot require "${id}" because "${e.asyncId}" uses top-level await, but 'require' is a synchronous operation.`; @@ -877,11 +893,18 @@ class AsyncImportError extends Error { } } -/** See `runtime.js`'s `__toCommonJS`. This omits the cache. */ -function toCommonJS(from: any) { +/** See `runtime.js`'s `__toCommonJS` (with `useModuleExports`). This omits the cache. */ +function toCommonJS(from: any, useModuleExports: boolean) { var desc, + moduleExports, entry = Object.defineProperty({}, "__esModule", { value: true }); - if ((from && typeof from === "object") || typeof from === "function") + if (useModuleExports) { + try { + moduleExports = from["module.exports"]; + } catch {} + if (moduleExports != null) return moduleExports; + } + if ((from && typeof from === "object") || typeof from === "function") { Object.getOwnPropertyNames(from).map( key => !Object.prototype.hasOwnProperty.call(entry, key) && @@ -890,6 +913,7 @@ function toCommonJS(from: any) { enumerable: !(desc = Object.getOwnPropertyDescriptor(from, key)) || desc.enumerable, }), ); + } return entry; } diff --git a/test/bake/dev/esm.test.ts b/test/bake/dev/esm.test.ts index 82f69cf6f377..e4cf88245843 100644 --- a/test/bake/dev/esm.test.ts +++ b/test/bake/dev/esm.test.ts @@ -265,6 +265,93 @@ devTest("ESM <-> CJS sync", { await c.expectMessage("PASS"); }, }); +devTest("require() of an ES module returns its `module.exports` export", { + files: { + "index.html": emptyHtmlFile({ + scripts: ["index.ts"], + }), + "index.ts": ` + const Point = require('./point'); + if (typeof Point !== 'function') throw new Error('require() should return the class, got ' + typeof Point); + if ('__esModule' in Point) throw new Error('Point.__esModule should be unset'); + class Point3D extends Point {} + if (!(new Point3D(1, 2) instanceof Point)) throw new Error('instanceof'); + if (require('./point') !== Point) throw new Error('identity'); + if (require('./star') !== Point) throw new Error('export * from a module with that export should carry it'); + if (require('./unset').x !== 1) throw new Error('an unset module.exports export falls back to the namespace'); + console.log('PASS ' + Point.distance(new Point(0, 0), new Point(3, 4))); + `, + "point.ts": ` + export default class Point { + constructor(public x: number, public y: number) {} + static distance(a: Point, b: Point) { return Math.hypot(b.x - a.x, b.y - a.y); } + } + export { Point as "module.exports" }; + `, + "star.ts": ` + export * from './point'; + `, + "unset.ts": ` + export const x = 1; + let unset; + export { unset as "module.exports" }; + `, + }, + async test(dev) { + await using c = await dev.client(); + await c.expectMessage("PASS 5"); + }, +}); +devTest("require() cycle through an ES module with a `module.exports` export", { + files: { + "index.html": emptyHtmlFile({ + scripts: ["index.ts"], + }), + "index.ts": ` + const api = require('./api'); + const { early } = require('./plugin'); + if (api.name !== 'api') throw new Error('the later require() should return the module.exports export, got ' + JSON.stringify(api)); + if (early.type !== 'object' || early.esModule !== true) throw new Error('the require() inside the cycle should return the namespace copy, got ' + JSON.stringify(early)); + console.log('PASS ' + api.early); + `, + "api.ts": ` + import { early } from './plugin'; + const api = { name: 'api', early: typeof early }; + export { api as "module.exports" }; + `, + "plugin.ts": ` + const api = require('./api'); + exports.early = { type: typeof api, esModule: api.__esModule }; + `, + }, + async test(dev) { + await using c = await dev.client(); + await c.expectMessage("PASS object"); + }, +}); +devTest("export * from a builtin polyfill does not carry its `module.exports` export", { + files: { + "index.html": emptyHtmlFile({ + scripts: ["index.ts"], + }), + "index.ts": ` + const EventEmitter = require('events'); + if (typeof EventEmitter !== 'function') throw new Error('require("events") should be the class, got ' + typeof EventEmitter); + const lib = require('./lib'); + if (typeof lib !== 'object') throw new Error('require() of the re-exporting module should be its namespace, got ' + typeof lib); + if (lib.foo !== 1 || lib.EventEmitter !== EventEmitter) throw new Error('the namespace should keep foo and EventEmitter'); + console.log('PASS'); + `, + "lib.ts": ` + export * from 'events'; + export const foo = 1; + `, + }, + async test(dev) { + await using c = await dev.client(); + await c.expectMessage("PASS"); + }, +}); devTest("ESM <-> CJS (async)", { files: { "index.html": emptyHtmlFile({ diff --git a/test/bundler/bundler_browser.test.ts b/test/bundler/bundler_browser.test.ts index 92c277d551c6..838f1b2e644c 100644 --- a/test/bundler/bundler_browser.test.ts +++ b/test/bundler/bundler_browser.test.ts @@ -182,6 +182,185 @@ describe("bundler", () => { stdout: "PASS", }, }); + // `require()` of a polyfilled builtin is that module's `module.exports` as in + // Node: the same value as the default import, with no `__esModule` marker, + // not a copy of the polyfill's ES module namespace. Each polyfill names that + // value with `export { x as "module.exports" }`. + itBundled("browser/NodePolyfillRequireShape", { + files: { + "/entry.js": /* js */ ` + import assert from "node:assert"; + import console2 from "node:console"; + import events from "node:events"; + import http from "node:http"; + import https from "node:https"; + import net from "node:net"; + import path from "node:path"; + import punycode from "node:punycode"; + import querystring from "node:querystring"; + import sys from "node:sys"; + import tty from "node:tty"; + import url from "node:url"; + import util from "node:util"; + import zlib from "node:zlib"; + import { required, identities } from "./required.cjs"; + const defaults = { assert, console: console2, events, http, https, net, path, punycode, querystring, sys, tty, url, util, zlib }; + for (const [name, { value, member }] of Object.entries(required)) { + console.log( + name, + typeof value, + typeof value[member], + Object.prototype.hasOwnProperty.call(value, "__esModule"), + name in defaults ? value === defaults[name] : "-", + ); + } + console.log(JSON.stringify(identities)); + `, + "/required.cjs": /* js */ ` + exports.required = { + assert: { value: require("assert"), member: "strictEqual" }, + console: { value: require("console"), member: "log" }, + events: { value: require("events"), member: "once" }, + http: { value: require("http"), member: "request" }, + https: { value: require("https"), member: "request" }, + net: { value: require("net"), member: "isIP" }, + path: { value: require("path"), member: "join" }, + punycode: { value: require("punycode"), member: "toASCII" }, + querystring: { value: require("querystring"), member: "parse" }, + string_decoder: { value: require("string_decoder"), member: "StringDecoder" }, + sys: { value: require("sys"), member: "format" }, + tty: { value: require("tty"), member: "isatty" }, + url: { value: require("url"), member: "parse" }, + util: { value: require("util"), member: "inspect" }, + zlib: { value: require("zlib"), member: "gzipSync" }, + }; + exports.identities = { + pathPosix: require("path").posix === require("path"), + sysIsUtil: require("sys") === require("util"), + consoleIsGlobal: require("console") === globalThis.console, + nodePrefix: require("node:events") === require("events"), + }; + `, + }, + target: "browser", + run: { + stdout: [ + "assert function function false true", + "console object function false true", + "events function function false true", + "http object function false true", + "https object function false true", + "net object function false true", + "path object function false true", + "punycode object function false true", + "querystring object function false true", + "string_decoder object function false -", + "sys object function false true", + "tty object function false true", + "url object function false true", + "util object function false true", + "zlib object function false true", + '{"pathPosix":true,"sysIsUtil":true,"consoleIsGlobal":true,"nodePrefix":true}', + ].join("\n"), + }, + }); + // The shapes above in use (#19808): `require("assert")` and `require("events")` + // are callable, the named exports are members of that same value, and + // browserify-zlib's own `require("assert")` calls work. + itBundled("browser/NodePolyfillRequireCallable", { + files: { + "/entry.js": /* js */ ` + import assert, { strictEqual } from "node:assert"; + import EventEmitter, { once } from "node:events"; + import zlib from "node:zlib"; + import { Assert, Events } from "./required.cjs"; + assert(true); + assert.strictEqual(typeof assert, "function"); + console.log(Assert === assert, strictEqual === assert.strictEqual, Events === EventEmitter, once === EventEmitter.once); + console.log(zlib.gunzipSync(zlib.gzipSync("hello")).toString()); + `, + "/required.cjs": /* js */ ` + const Assert = require("node:assert"); + Assert.equal(1, 1); + Assert(true, "assert is callable"); + const EventEmitter = require("events"); + class Bus extends EventEmitter {} + new Bus().on("x", v => console.log("got", v)).emit("x", 1); + console.log(new (require("node:events"))() instanceof EventEmitter, EventEmitter.EventEmitter === EventEmitter); + exports.Assert = Assert; + exports.Events = EventEmitter; + `, + }, + target: "browser", + run: { + stdout: "got 1\ntrue true\ntrue true true true\nhello", + }, + }); + // A Node.js builtin has no export named `module.exports`, so `export * from + // "events"` does not carry the one its polyfill declares. `require()` of the + // module that re-exports is its namespace, as in Node.js and `bun run`. + // `module.exports = require("events")` still is the class. + const exportStarFiles = { + "/lib.mjs": /* js */ ` + export * from "events"; + export const foo = 1; + `, + }; + itBundled("browser/NodePolyfillExportStar", { + files: { + "/entry.cjs": /* js */ ` + const EventEmitter = require("events"); + const lib = require("./lib.mjs"); + const both = require("./both.mjs"); + console.log(typeof lib, lib.foo, lib.EventEmitter === EventEmitter, Object.prototype.hasOwnProperty.call(lib, "module.exports")); + console.log(typeof both, both.default === EventEmitter, both.once === EventEmitter.once); + console.log(typeof require("./wrapper.cjs"), require("./wrapper.cjs") === EventEmitter); + `, + ...exportStarFiles, + "/both.mjs": /* js */ ` + export { default } from "node:events"; + export * from "node:events"; + `, + "/wrapper.cjs": /* js */ ` + module.exports = require("events"); + `, + }, + entryPoints: ["/entry.cjs"], + target: "browser", + run: { + stdout: "object 1 true false\nobject true true\nfunction true", + }, + }); + itBundled("browser/NodePolyfillExportStarFormatCjsEntry", { + files: exportStarFiles, + runtimeFiles: { + "/user.cjs": /* js */ ` + const lib = require("./out.js"); + console.log(typeof lib, lib.foo, typeof lib.EventEmitter); + `, + }, + target: "browser", + format: "cjs", + run: { + file: "/user.cjs", + stdout: "object 1 function", + }, + }); + itBundled("browser/NodePolyfillExportStarFormatEsmEntry", { + files: exportStarFiles, + runtimeFiles: { + "/user.mjs": /* js */ ` + import * as lib from "./out.js"; + console.log("module.exports" in lib, lib.foo, typeof lib.EventEmitter); + `, + }, + target: "browser", + format: "esm", + run: { + file: "/user.mjs", + stdout: "false 1 function", + }, + }); // TODO: use nodePolyfillList to generate the code in here. const NodePolyfills = itBundled("browser/NodePolyfills", { files: { @@ -264,7 +443,7 @@ describe("bundler", () => { target: "browser", run: { stdout: - "{\n assert: {\n AssertionError: [Getter/Setter],\n CallTracker: [Getter/Setter],\n deepEqual: [Getter/Setter],\n deepStrictEqual: [Getter/Setter],\n default: [Getter/Setter],\n doesNotMatch: [Getter/Setter],\n doesNotReject: [Getter/Setter],\n doesNotThrow: [Getter/Setter],\n equal: [Getter/Setter],\n fail: [Getter/Setter],\n ifError: [Getter/Setter],\n match: [Getter/Setter],\n notDeepEqual: [Getter/Setter],\n notDeepStrictEqual: [Getter/Setter],\n notEqual: [Getter/Setter],\n notStrictEqual: [Getter/Setter],\n ok: [Getter/Setter],\n rejects: [Getter/Setter],\n strict: [Getter/Setter],\n strictEqual: [Getter/Setter],\n throws: [Getter/Setter],\n },\n buffer: {\n Blob: [Getter/Setter],\n Buffer: [Getter/Setter],\n File: [Getter/Setter],\n INSPECT_MAX_BYTES: [Getter/Setter],\n atob: [Getter/Setter],\n btoa: [Getter/Setter],\n constants: [Getter/Setter],\n default: [Getter/Setter],\n isAscii: [Getter/Setter],\n isUtf8: [Getter/Setter],\n kMaxLength: [Getter/Setter],\n kStringMaxLength: [Getter/Setter],\n resolveObjectURL: [Getter/Setter],\n transcode: [Getter/Setter],\n },\n child_process: [Function: child_process],\n cluster: [Function: cluster],\n console2: {\n default: [Getter/Setter],\n },\n constants: {\n DH_CHECK_P_NOT_PRIME: [Getter/Setter],\n DH_CHECK_P_NOT_SAFE_PRIME: [Getter/Setter],\n DH_NOT_SUITABLE_GENERATOR: [Getter/Setter],\n DH_UNABLE_TO_CHECK_GENERATOR: [Getter/Setter],\n E2BIG: [Getter/Setter],\n EACCES: [Getter/Setter],\n EADDRINUSE: [Getter/Setter],\n EADDRNOTAVAIL: [Getter/Setter],\n EAFNOSUPPORT: [Getter/Setter],\n EAGAIN: [Getter/Setter],\n EALREADY: [Getter/Setter],\n EBADF: [Getter/Setter],\n EBADMSG: [Getter/Setter],\n EBUSY: [Getter/Setter],\n ECANCELED: [Getter/Setter],\n ECHILD: [Getter/Setter],\n ECONNABORTED: [Getter/Setter],\n ECONNREFUSED: [Getter/Setter],\n ECONNRESET: [Getter/Setter],\n EDEADLK: [Getter/Setter],\n EDESTADDRREQ: [Getter/Setter],\n EDOM: [Getter/Setter],\n EDQUOT: [Getter/Setter],\n EEXIST: [Getter/Setter],\n EFAULT: [Getter/Setter],\n EFBIG: [Getter/Setter],\n EHOSTUNREACH: [Getter/Setter],\n EIDRM: [Getter/Setter],\n EILSEQ: [Getter/Setter],\n EINPROGRESS: [Getter/Setter],\n EINTR: [Getter/Setter],\n EINVAL: [Getter/Setter],\n EIO: [Getter/Setter],\n EISCONN: [Getter/Setter],\n EISDIR: [Getter/Setter],\n ELOOP: [Getter/Setter],\n EMFILE: [Getter/Setter],\n EMLINK: [Getter/Setter],\n EMSGSIZE: [Getter/Setter],\n EMULTIHOP: [Getter/Setter],\n ENAMETOOLONG: [Getter/Setter],\n ENETDOWN: [Getter/Setter],\n ENETRESET: [Getter/Setter],\n ENETUNREACH: [Getter/Setter],\n ENFILE: [Getter/Setter],\n ENGINE_METHOD_ALL: [Getter/Setter],\n ENGINE_METHOD_CIPHERS: [Getter/Setter],\n ENGINE_METHOD_DH: [Getter/Setter],\n ENGINE_METHOD_DIGESTS: [Getter/Setter],\n ENGINE_METHOD_DSA: [Getter/Setter],\n ENGINE_METHOD_ECDH: [Getter/Setter],\n ENGINE_METHOD_ECDSA: [Getter/Setter],\n ENGINE_METHOD_NONE: [Getter/Setter],\n ENGINE_METHOD_PKEY_ASN1_METHS: [Getter/Setter],\n ENGINE_METHOD_PKEY_METHS: [Getter/Setter],\n ENGINE_METHOD_RAND: [Getter/Setter],\n ENGINE_METHOD_STORE: [Getter/Setter],\n ENOBUFS: [Getter/Setter],\n ENODATA: [Getter/Setter],\n ENODEV: [Getter/Setter],\n ENOENT: [Getter/Setter],\n ENOEXEC: [Getter/Setter],\n ENOLCK: [Getter/Setter],\n ENOLINK: [Getter/Setter],\n ENOMEM: [Getter/Setter],\n ENOMSG: [Getter/Setter],\n ENOPROTOOPT: [Getter/Setter],\n ENOSPC: [Getter/Setter],\n ENOSR: [Getter/Setter],\n ENOSTR: [Getter/Setter],\n ENOSYS: [Getter/Setter],\n ENOTCONN: [Getter/Setter],\n ENOTDIR: [Getter/Setter],\n ENOTEMPTY: [Getter/Setter],\n ENOTSOCK: [Getter/Setter],\n ENOTSUP: [Getter/Setter],\n ENOTTY: [Getter/Setter],\n ENXIO: [Getter/Setter],\n EOPNOTSUPP: [Getter/Setter],\n EOVERFLOW: [Getter/Setter],\n EPERM: [Getter/Setter],\n EPIPE: [Getter/Setter],\n EPROTO: [Getter/Setter],\n EPROTONOSUPPORT: [Getter/Setter],\n EPROTOTYPE: [Getter/Setter],\n ERANGE: [Getter/Setter],\n EROFS: [Getter/Setter],\n ESPIPE: [Getter/Setter],\n ESRCH: [Getter/Setter],\n ESTALE: [Getter/Setter],\n ETIME: [Getter/Setter],\n ETIMEDOUT: [Getter/Setter],\n ETXTBSY: [Getter/Setter],\n EWOULDBLOCK: [Getter/Setter],\n EXDEV: [Getter/Setter],\n F_OK: [Getter/Setter],\n NPN_ENABLED: [Getter/Setter],\n O_APPEND: [Getter/Setter],\n O_CREAT: [Getter/Setter],\n O_DIRECTORY: [Getter/Setter],\n O_EXCL: [Getter/Setter],\n O_NOCTTY: [Getter/Setter],\n O_NOFOLLOW: [Getter/Setter],\n O_NONBLOCK: [Getter/Setter],\n O_RDONLY: [Getter/Setter],\n O_RDWR: [Getter/Setter],\n O_SYMLINK: [Getter/Setter],\n O_SYNC: [Getter/Setter],\n O_TRUNC: [Getter/Setter],\n O_WRONLY: [Getter/Setter],\n POINT_CONVERSION_COMPRESSED: [Getter/Setter],\n POINT_CONVERSION_HYBRID: [Getter/Setter],\n POINT_CONVERSION_UNCOMPRESSED: [Getter/Setter],\n RSA_NO_PADDING: [Getter/Setter],\n RSA_PKCS1_OAEP_PADDING: [Getter/Setter],\n RSA_PKCS1_PADDING: [Getter/Setter],\n RSA_PKCS1_PSS_PADDING: [Getter/Setter],\n RSA_SSLV23_PADDING: [Getter/Setter],\n RSA_X931_PADDING: [Getter/Setter],\n R_OK: [Getter/Setter],\n SIGABRT: [Getter/Setter],\n SIGALRM: [Getter/Setter],\n SIGBUS: [Getter/Setter],\n SIGCHLD: [Getter/Setter],\n SIGCONT: [Getter/Setter],\n SIGFPE: [Getter/Setter],\n SIGHUP: [Getter/Setter],\n SIGILL: [Getter/Setter],\n SIGINT: [Getter/Setter],\n SIGIO: [Getter/Setter],\n SIGIOT: [Getter/Setter],\n SIGKILL: [Getter/Setter],\n SIGPIPE: [Getter/Setter],\n SIGPROF: [Getter/Setter],\n SIGQUIT: [Getter/Setter],\n SIGSEGV: [Getter/Setter],\n SIGSTOP: [Getter/Setter],\n SIGSYS: [Getter/Setter],\n SIGTERM: [Getter/Setter],\n SIGTRAP: [Getter/Setter],\n SIGTSTP: [Getter/Setter],\n SIGTTIN: [Getter/Setter],\n SIGTTOU: [Getter/Setter],\n SIGURG: [Getter/Setter],\n SIGUSR1: [Getter/Setter],\n SIGUSR2: [Getter/Setter],\n SIGVTALRM: [Getter/Setter],\n SIGWINCH: [Getter/Setter],\n SIGXCPU: [Getter/Setter],\n SIGXFSZ: [Getter/Setter],\n SSL_OP_ALL: [Getter/Setter],\n SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION: [Getter/Setter],\n SSL_OP_CIPHER_SERVER_PREFERENCE: [Getter/Setter],\n SSL_OP_CISCO_ANYCONNECT: [Getter/Setter],\n SSL_OP_COOKIE_EXCHANGE: [Getter/Setter],\n SSL_OP_CRYPTOPRO_TLSEXT_BUG: [Getter/Setter],\n SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: [Getter/Setter],\n SSL_OP_EPHEMERAL_RSA: [Getter/Setter],\n SSL_OP_LEGACY_SERVER_CONNECT: [Getter/Setter],\n SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER: [Getter/Setter],\n SSL_OP_MICROSOFT_SESS_ID_BUG: [Getter/Setter],\n SSL_OP_MSIE_SSLV2_RSA_PADDING: [Getter/Setter],\n SSL_OP_NETSCAPE_CA_DN_BUG: [Getter/Setter],\n SSL_OP_NETSCAPE_CHALLENGE_BUG: [Getter/Setter],\n SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG: [Getter/Setter],\n SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG: [Getter/Setter],\n SSL_OP_NO_COMPRESSION: [Getter/Setter],\n SSL_OP_NO_QUERY_MTU: [Getter/Setter],\n SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION: [Getter/Setter],\n SSL_OP_NO_SSLv2: [Getter/Setter],\n SSL_OP_NO_SSLv3: [Getter/Setter],\n SSL_OP_NO_TICKET: [Getter/Setter],\n SSL_OP_NO_TLSv1: [Getter/Setter],\n SSL_OP_NO_TLSv1_1: [Getter/Setter],\n SSL_OP_NO_TLSv1_2: [Getter/Setter],\n SSL_OP_PKCS1_CHECK_1: [Getter/Setter],\n SSL_OP_PKCS1_CHECK_2: [Getter/Setter],\n SSL_OP_SINGLE_DH_USE: [Getter/Setter],\n SSL_OP_SINGLE_ECDH_USE: [Getter/Setter],\n SSL_OP_SSLEAY_080_CLIENT_DH_BUG: [Getter/Setter],\n SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG: [Getter/Setter],\n SSL_OP_TLS_BLOCK_PADDING_BUG: [Getter/Setter],\n SSL_OP_TLS_D5_BUG: [Getter/Setter],\n SSL_OP_TLS_ROLLBACK_BUG: [Getter/Setter],\n S_IFBLK: [Getter/Setter],\n S_IFCHR: [Getter/Setter],\n S_IFDIR: [Getter/Setter],\n S_IFIFO: [Getter/Setter],\n S_IFLNK: [Getter/Setter],\n S_IFMT: [Getter/Setter],\n S_IFREG: [Getter/Setter],\n S_IFSOCK: [Getter/Setter],\n S_IRGRP: [Getter/Setter],\n S_IROTH: [Getter/Setter],\n S_IRUSR: [Getter/Setter],\n S_IRWXG: [Getter/Setter],\n S_IRWXO: [Getter/Setter],\n S_IRWXU: [Getter/Setter],\n S_IWGRP: [Getter/Setter],\n S_IWOTH: [Getter/Setter],\n S_IWUSR: [Getter/Setter],\n S_IXGRP: [Getter/Setter],\n S_IXOTH: [Getter/Setter],\n S_IXUSR: [Getter/Setter],\n UV_UDP_REUSEADDR: [Getter/Setter],\n W_OK: [Getter/Setter],\n X_OK: [Getter/Setter],\n },\n crypto: {\n Cipher: [Getter/Setter],\n Cipheriv: [Getter/Setter],\n DEFAULT_ENCODING: [Getter/Setter],\n Decipher: [Getter/Setter],\n Decipheriv: [Getter/Setter],\n DiffieHellman: [Getter/Setter],\n DiffieHellmanGroup: [Getter/Setter],\n Hash: [Getter/Setter],\n Hmac: [Getter/Setter],\n Sign: [Getter/Setter],\n Verify: [Getter/Setter],\n constants: [Getter/Setter],\n createCipher: [Getter/Setter],\n createCipheriv: [Getter/Setter],\n createCredentials: [Getter/Setter],\n createDecipher: [Getter/Setter],\n createDecipheriv: [Getter/Setter],\n createDiffieHellman: [Getter/Setter],\n createDiffieHellmanGroup: [Getter/Setter],\n createECDH: [Getter/Setter],\n createHash: [Getter/Setter],\n createHmac: [Getter/Setter],\n createSign: [Getter/Setter],\n createVerify: [Getter/Setter],\n default: [Getter/Setter],\n getCiphers: [Getter/Setter],\n getCurves: [Getter/Setter],\n getDiffieHellman: [Getter/Setter],\n getHashes: [Getter/Setter],\n getRandomValues: [Getter/Setter],\n listCiphers: [Getter/Setter],\n pbkdf2: [Getter/Setter],\n pbkdf2Sync: [Getter/Setter],\n privateDecrypt: [Getter/Setter],\n privateEncrypt: [Getter/Setter],\n prng: [Getter/Setter],\n pseudoRandomBytes: [Getter/Setter],\n publicDecrypt: [Getter/Setter],\n publicEncrypt: [Getter/Setter],\n randomBytes: [Getter/Setter],\n randomFill: [Getter/Setter],\n randomFillSync: [Getter/Setter],\n randomUUID: [Getter/Setter],\n rng: [Getter/Setter],\n webcrypto: [Getter/Setter],\n },\n dgram: [Function: dgram],\n dns: [Function: dns],\n domain: {\n create: [Getter/Setter],\n createDomain: [Getter/Setter],\n },\n events: {\n EventEmitter: [Getter/Setter],\n addAbortListener: [Getter/Setter],\n captureRejectionSymbol: [Getter/Setter],\n default: [Getter/Setter],\n getEventListeners: [Getter/Setter],\n getMaxListeners: [Getter/Setter],\n init: [Getter/Setter],\n listenerCount: [Getter/Setter],\n once: [Getter/Setter],\n setMaxListeners: [Getter/Setter],\n },\n fs: [Function: fs],\n http: {\n Agent: [Getter/Setter],\n ClientRequest: [Getter/Setter],\n IncomingMessage: [Getter/Setter],\n METHODS: [Getter/Setter],\n STATUS_CODES: [Getter/Setter],\n default: [Getter/Setter],\n get: [Getter/Setter],\n globalAgent: [Getter/Setter],\n request: [Getter/Setter],\n },\n https: {\n Agent: [Getter/Setter],\n ClientRequest: [Getter/Setter],\n IncomingMessage: [Getter/Setter],\n METHODS: [Getter/Setter],\n OutgoingMessage: [Getter/Setter],\n STATUS_CODES: [Getter/Setter],\n Server: [Getter/Setter],\n ServerResponse: [Getter/Setter],\n createServer: [Getter/Setter],\n default: [Getter/Setter],\n get: [Getter/Setter],\n globalAgent: [Getter/Setter],\n maxHeaderSize: [Getter/Setter],\n request: [Getter/Setter],\n setMaxIdleHTTPParsers: [Getter/Setter],\n validateHeaderName: [Getter/Setter],\n validateHeaderValue: [Getter/Setter],\n },\n module: [Function: module2],\n net: {\n default: [Getter/Setter],\n isIP: [Getter/Setter],\n isIPv4: [Getter/Setter],\n isIPv6: [Getter/Setter],\n },\n os: {\n EOL: [Getter/Setter],\n arch: [Getter/Setter],\n cpus: [Getter/Setter],\n endianness: [Getter/Setter],\n freemem: [Getter/Setter],\n getNetworkInterfaces: [Getter/Setter],\n homedir: [Getter/Setter],\n hostname: [Getter/Setter],\n loadavg: [Getter/Setter],\n networkInterfaces: [Getter/Setter],\n platform: [Getter/Setter],\n release: [Getter/Setter],\n tmpDir: [Getter/Setter],\n tmpdir: [Getter/Setter],\n totalmem: [Getter/Setter],\n type: [Getter/Setter],\n uptime: [Getter/Setter],\n },\n path: {\n _makeLong: [Getter/Setter],\n basename: [Getter/Setter],\n default: [Getter/Setter],\n delimiter: [Getter/Setter],\n dirname: [Getter/Setter],\n extname: [Getter/Setter],\n format: [Getter/Setter],\n isAbsolute: [Getter/Setter],\n join: [Getter/Setter],\n normalize: [Getter/Setter],\n parse: [Getter/Setter],\n posix: [Getter/Setter],\n relative: [Getter/Setter],\n resolve: [Getter/Setter],\n sep: [Getter/Setter],\n },\n perf_hooks: [Function: perf_hooks],\n process: {\n addListener: [Getter/Setter],\n argv: [Getter/Setter],\n binding: [Getter/Setter],\n browser: [Getter/Setter],\n chdir: [Getter/Setter],\n cwd: [Getter/Setter],\n emit: [Getter/Setter],\n env: [Getter/Setter],\n listeners: [Getter/Setter],\n nextTick: [Getter/Setter],\n off: [Getter/Setter],\n on: [Getter/Setter],\n once: [Getter/Setter],\n prependListener: [Getter/Setter],\n prependOnceListener: [Getter/Setter],\n removeAllListeners: [Getter/Setter],\n removeListener: [Getter/Setter],\n title: [Getter/Setter],\n umask: [Getter/Setter],\n version: [Getter/Setter],\n versions: [Getter/Setter],\n },\n punycode: {\n default: [Getter/Setter],\n },\n querystring: {\n decode: [Getter/Setter],\n default: [Getter/Setter],\n encode: [Getter/Setter],\n escape: [Getter/Setter],\n parse: [Getter/Setter],\n stringify: [Getter/Setter],\n unescape: [Getter/Setter],\n unescapeBuffer: [Getter/Setter],\n },\n readline: [Function: readline],\n repl: [Function: repl],\n stream: Function {\n default: [Stream: Readable],\n length: [Getter],\n name: [Getter],\n prototype: [Getter],\n ReadableState: [Getter],\n _fromList: [Getter],\n from: [Getter],\n fromWeb: [Getter],\n toWeb: [Getter],\n wrap: [Getter],\n _uint8ArrayToBuffer: [Getter],\n _isUint8Array: [Getter],\n isDisturbed: [Getter],\n isErrored: [Getter],\n isReadable: [Getter],\n Readable: [Getter],\n Writable: [Getter],\n Duplex: [Getter],\n Transform: [Getter],\n PassThrough: [Getter],\n addAbortSignal: [Getter],\n finished: [Getter],\n destroy: [Getter],\n pipeline: [Getter],\n compose: [Getter],\n Stream: [Getter],\n isDestroyed: [Function: isDestroyed],\n isWritable: [Function: isWritable],\n setDefaultHighWaterMark: [Function: setDefaultHighWaterMark],\n getDefaultHighWaterMark: [Function: getDefaultHighWaterMark],\n promises: [Getter],\n },\n string_decoder: {\n StringDecoder: [Getter/Setter],\n default: [Getter/Setter],\n },\n sys: {\n TextDecoder: [Getter/Setter],\n TextEncoder: [Getter/Setter],\n _extend: [Getter/Setter],\n callbackify: [Getter/Setter],\n callbackifyOnRejected: [Getter/Setter],\n debuglog: [Getter/Setter],\n default: [Getter/Setter],\n deprecate: [Getter/Setter],\n format: [Getter/Setter],\n inherits: [Getter/Setter],\n inspect: [Getter/Setter],\n isArray: [Getter/Setter],\n isBoolean: [Getter/Setter],\n isBuffer: [Getter/Setter],\n isDate: [Getter/Setter],\n isError: [Getter/Setter],\n isFunction: [Getter/Setter],\n isNull: [Getter/Setter],\n isNullOrUndefined: [Getter/Setter],\n isNumber: [Getter/Setter],\n isObject: [Getter/Setter],\n isPrimitive: [Getter/Setter],\n isRegExp: [Getter/Setter],\n isString: [Getter/Setter],\n isSymbol: [Getter/Setter],\n isUndefined: [Getter/Setter],\n log: [Getter/Setter],\n promisify: [Getter/Setter],\n types: [Getter/Setter],\n },\n timers: {\n _unrefActive: [Getter/Setter],\n clearImmediate: [Getter/Setter],\n clearInterval: [Getter/Setter],\n clearTimeout: [Getter/Setter],\n promises: [Getter/Setter],\n setImmediate: [Getter/Setter],\n setInterval: [Getter/Setter],\n setTimeout: [Getter/Setter],\n },\n tls: [Function: tls],\n tty: {\n ReadStream: [Getter/Setter],\n WriteStream: [Getter/Setter],\n default: [Getter/Setter],\n isatty: [Getter/Setter],\n },\n url: {\n URL: [Getter/Setter],\n URLSearchParams: [Getter/Setter],\n Url: [Getter/Setter],\n default: [Getter/Setter],\n format: [Getter/Setter],\n parse: [Getter/Setter],\n resolve: [Getter/Setter],\n resolveObject: [Getter/Setter],\n },\n util: {\n TextDecoder: [Getter/Setter],\n TextEncoder: [Getter/Setter],\n _extend: [Getter/Setter],\n callbackify: [Getter/Setter],\n callbackifyOnRejected: [Getter/Setter],\n debuglog: [Getter/Setter],\n default: [Getter/Setter],\n deprecate: [Getter/Setter],\n format: [Getter/Setter],\n inherits: [Getter/Setter],\n inspect: [Getter/Setter],\n isArray: [Getter/Setter],\n isBoolean: [Getter/Setter],\n isBuffer: [Getter/Setter],\n isDate: [Getter/Setter],\n isError: [Getter/Setter],\n isFunction: [Getter/Setter],\n isNull: [Getter/Setter],\n isNullOrUndefined: [Getter/Setter],\n isNumber: [Getter/Setter],\n isObject: [Getter/Setter],\n isPrimitive: [Getter/Setter],\n isRegExp: [Getter/Setter],\n isString: [Getter/Setter],\n isSymbol: [Getter/Setter],\n isUndefined: [Getter/Setter],\n log: [Getter/Setter],\n promisify: [Getter/Setter],\n types: [Getter/Setter],\n },\n v8: [Function: v8],\n vm: [Function: vm],\n zlib: {\n default: [Getter/Setter],\n },\n}", + '{\n assert: {\n AssertionError: [Getter/Setter],\n CallTracker: [Getter/Setter],\n deepEqual: [Getter/Setter],\n deepStrictEqual: [Getter/Setter],\n default: [Getter/Setter],\n doesNotMatch: [Getter/Setter],\n doesNotReject: [Getter/Setter],\n doesNotThrow: [Getter/Setter],\n equal: [Getter/Setter],\n fail: [Getter/Setter],\n ifError: [Getter/Setter],\n match: [Getter/Setter],\n "module.exports": [Getter/Setter],\n notDeepEqual: [Getter/Setter],\n notDeepStrictEqual: [Getter/Setter],\n notEqual: [Getter/Setter],\n notStrictEqual: [Getter/Setter],\n ok: [Getter/Setter],\n rejects: [Getter/Setter],\n strict: [Getter/Setter],\n strictEqual: [Getter/Setter],\n throws: [Getter/Setter],\n },\n buffer: {\n Blob: [Getter/Setter],\n Buffer: [Getter/Setter],\n File: [Getter/Setter],\n INSPECT_MAX_BYTES: [Getter/Setter],\n atob: [Getter/Setter],\n btoa: [Getter/Setter],\n constants: [Getter/Setter],\n default: [Getter/Setter],\n isAscii: [Getter/Setter],\n isUtf8: [Getter/Setter],\n kMaxLength: [Getter/Setter],\n kStringMaxLength: [Getter/Setter],\n resolveObjectURL: [Getter/Setter],\n transcode: [Getter/Setter],\n },\n child_process: [Function: child_process],\n cluster: [Function: cluster],\n console2: {\n default: [Getter/Setter],\n "module.exports": [Getter/Setter],\n },\n constants: {\n DH_CHECK_P_NOT_PRIME: [Getter/Setter],\n DH_CHECK_P_NOT_SAFE_PRIME: [Getter/Setter],\n DH_NOT_SUITABLE_GENERATOR: [Getter/Setter],\n DH_UNABLE_TO_CHECK_GENERATOR: [Getter/Setter],\n E2BIG: [Getter/Setter],\n EACCES: [Getter/Setter],\n EADDRINUSE: [Getter/Setter],\n EADDRNOTAVAIL: [Getter/Setter],\n EAFNOSUPPORT: [Getter/Setter],\n EAGAIN: [Getter/Setter],\n EALREADY: [Getter/Setter],\n EBADF: [Getter/Setter],\n EBADMSG: [Getter/Setter],\n EBUSY: [Getter/Setter],\n ECANCELED: [Getter/Setter],\n ECHILD: [Getter/Setter],\n ECONNABORTED: [Getter/Setter],\n ECONNREFUSED: [Getter/Setter],\n ECONNRESET: [Getter/Setter],\n EDEADLK: [Getter/Setter],\n EDESTADDRREQ: [Getter/Setter],\n EDOM: [Getter/Setter],\n EDQUOT: [Getter/Setter],\n EEXIST: [Getter/Setter],\n EFAULT: [Getter/Setter],\n EFBIG: [Getter/Setter],\n EHOSTUNREACH: [Getter/Setter],\n EIDRM: [Getter/Setter],\n EILSEQ: [Getter/Setter],\n EINPROGRESS: [Getter/Setter],\n EINTR: [Getter/Setter],\n EINVAL: [Getter/Setter],\n EIO: [Getter/Setter],\n EISCONN: [Getter/Setter],\n EISDIR: [Getter/Setter],\n ELOOP: [Getter/Setter],\n EMFILE: [Getter/Setter],\n EMLINK: [Getter/Setter],\n EMSGSIZE: [Getter/Setter],\n EMULTIHOP: [Getter/Setter],\n ENAMETOOLONG: [Getter/Setter],\n ENETDOWN: [Getter/Setter],\n ENETRESET: [Getter/Setter],\n ENETUNREACH: [Getter/Setter],\n ENFILE: [Getter/Setter],\n ENGINE_METHOD_ALL: [Getter/Setter],\n ENGINE_METHOD_CIPHERS: [Getter/Setter],\n ENGINE_METHOD_DH: [Getter/Setter],\n ENGINE_METHOD_DIGESTS: [Getter/Setter],\n ENGINE_METHOD_DSA: [Getter/Setter],\n ENGINE_METHOD_ECDH: [Getter/Setter],\n ENGINE_METHOD_ECDSA: [Getter/Setter],\n ENGINE_METHOD_NONE: [Getter/Setter],\n ENGINE_METHOD_PKEY_ASN1_METHS: [Getter/Setter],\n ENGINE_METHOD_PKEY_METHS: [Getter/Setter],\n ENGINE_METHOD_RAND: [Getter/Setter],\n ENGINE_METHOD_STORE: [Getter/Setter],\n ENOBUFS: [Getter/Setter],\n ENODATA: [Getter/Setter],\n ENODEV: [Getter/Setter],\n ENOENT: [Getter/Setter],\n ENOEXEC: [Getter/Setter],\n ENOLCK: [Getter/Setter],\n ENOLINK: [Getter/Setter],\n ENOMEM: [Getter/Setter],\n ENOMSG: [Getter/Setter],\n ENOPROTOOPT: [Getter/Setter],\n ENOSPC: [Getter/Setter],\n ENOSR: [Getter/Setter],\n ENOSTR: [Getter/Setter],\n ENOSYS: [Getter/Setter],\n ENOTCONN: [Getter/Setter],\n ENOTDIR: [Getter/Setter],\n ENOTEMPTY: [Getter/Setter],\n ENOTSOCK: [Getter/Setter],\n ENOTSUP: [Getter/Setter],\n ENOTTY: [Getter/Setter],\n ENXIO: [Getter/Setter],\n EOPNOTSUPP: [Getter/Setter],\n EOVERFLOW: [Getter/Setter],\n EPERM: [Getter/Setter],\n EPIPE: [Getter/Setter],\n EPROTO: [Getter/Setter],\n EPROTONOSUPPORT: [Getter/Setter],\n EPROTOTYPE: [Getter/Setter],\n ERANGE: [Getter/Setter],\n EROFS: [Getter/Setter],\n ESPIPE: [Getter/Setter],\n ESRCH: [Getter/Setter],\n ESTALE: [Getter/Setter],\n ETIME: [Getter/Setter],\n ETIMEDOUT: [Getter/Setter],\n ETXTBSY: [Getter/Setter],\n EWOULDBLOCK: [Getter/Setter],\n EXDEV: [Getter/Setter],\n F_OK: [Getter/Setter],\n NPN_ENABLED: [Getter/Setter],\n O_APPEND: [Getter/Setter],\n O_CREAT: [Getter/Setter],\n O_DIRECTORY: [Getter/Setter],\n O_EXCL: [Getter/Setter],\n O_NOCTTY: [Getter/Setter],\n O_NOFOLLOW: [Getter/Setter],\n O_NONBLOCK: [Getter/Setter],\n O_RDONLY: [Getter/Setter],\n O_RDWR: [Getter/Setter],\n O_SYMLINK: [Getter/Setter],\n O_SYNC: [Getter/Setter],\n O_TRUNC: [Getter/Setter],\n O_WRONLY: [Getter/Setter],\n POINT_CONVERSION_COMPRESSED: [Getter/Setter],\n POINT_CONVERSION_HYBRID: [Getter/Setter],\n POINT_CONVERSION_UNCOMPRESSED: [Getter/Setter],\n RSA_NO_PADDING: [Getter/Setter],\n RSA_PKCS1_OAEP_PADDING: [Getter/Setter],\n RSA_PKCS1_PADDING: [Getter/Setter],\n RSA_PKCS1_PSS_PADDING: [Getter/Setter],\n RSA_SSLV23_PADDING: [Getter/Setter],\n RSA_X931_PADDING: [Getter/Setter],\n R_OK: [Getter/Setter],\n SIGABRT: [Getter/Setter],\n SIGALRM: [Getter/Setter],\n SIGBUS: [Getter/Setter],\n SIGCHLD: [Getter/Setter],\n SIGCONT: [Getter/Setter],\n SIGFPE: [Getter/Setter],\n SIGHUP: [Getter/Setter],\n SIGILL: [Getter/Setter],\n SIGINT: [Getter/Setter],\n SIGIO: [Getter/Setter],\n SIGIOT: [Getter/Setter],\n SIGKILL: [Getter/Setter],\n SIGPIPE: [Getter/Setter],\n SIGPROF: [Getter/Setter],\n SIGQUIT: [Getter/Setter],\n SIGSEGV: [Getter/Setter],\n SIGSTOP: [Getter/Setter],\n SIGSYS: [Getter/Setter],\n SIGTERM: [Getter/Setter],\n SIGTRAP: [Getter/Setter],\n SIGTSTP: [Getter/Setter],\n SIGTTIN: [Getter/Setter],\n SIGTTOU: [Getter/Setter],\n SIGURG: [Getter/Setter],\n SIGUSR1: [Getter/Setter],\n SIGUSR2: [Getter/Setter],\n SIGVTALRM: [Getter/Setter],\n SIGWINCH: [Getter/Setter],\n SIGXCPU: [Getter/Setter],\n SIGXFSZ: [Getter/Setter],\n SSL_OP_ALL: [Getter/Setter],\n SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION: [Getter/Setter],\n SSL_OP_CIPHER_SERVER_PREFERENCE: [Getter/Setter],\n SSL_OP_CISCO_ANYCONNECT: [Getter/Setter],\n SSL_OP_COOKIE_EXCHANGE: [Getter/Setter],\n SSL_OP_CRYPTOPRO_TLSEXT_BUG: [Getter/Setter],\n SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: [Getter/Setter],\n SSL_OP_EPHEMERAL_RSA: [Getter/Setter],\n SSL_OP_LEGACY_SERVER_CONNECT: [Getter/Setter],\n SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER: [Getter/Setter],\n SSL_OP_MICROSOFT_SESS_ID_BUG: [Getter/Setter],\n SSL_OP_MSIE_SSLV2_RSA_PADDING: [Getter/Setter],\n SSL_OP_NETSCAPE_CA_DN_BUG: [Getter/Setter],\n SSL_OP_NETSCAPE_CHALLENGE_BUG: [Getter/Setter],\n SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG: [Getter/Setter],\n SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG: [Getter/Setter],\n SSL_OP_NO_COMPRESSION: [Getter/Setter],\n SSL_OP_NO_QUERY_MTU: [Getter/Setter],\n SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION: [Getter/Setter],\n SSL_OP_NO_SSLv2: [Getter/Setter],\n SSL_OP_NO_SSLv3: [Getter/Setter],\n SSL_OP_NO_TICKET: [Getter/Setter],\n SSL_OP_NO_TLSv1: [Getter/Setter],\n SSL_OP_NO_TLSv1_1: [Getter/Setter],\n SSL_OP_NO_TLSv1_2: [Getter/Setter],\n SSL_OP_PKCS1_CHECK_1: [Getter/Setter],\n SSL_OP_PKCS1_CHECK_2: [Getter/Setter],\n SSL_OP_SINGLE_DH_USE: [Getter/Setter],\n SSL_OP_SINGLE_ECDH_USE: [Getter/Setter],\n SSL_OP_SSLEAY_080_CLIENT_DH_BUG: [Getter/Setter],\n SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG: [Getter/Setter],\n SSL_OP_TLS_BLOCK_PADDING_BUG: [Getter/Setter],\n SSL_OP_TLS_D5_BUG: [Getter/Setter],\n SSL_OP_TLS_ROLLBACK_BUG: [Getter/Setter],\n S_IFBLK: [Getter/Setter],\n S_IFCHR: [Getter/Setter],\n S_IFDIR: [Getter/Setter],\n S_IFIFO: [Getter/Setter],\n S_IFLNK: [Getter/Setter],\n S_IFMT: [Getter/Setter],\n S_IFREG: [Getter/Setter],\n S_IFSOCK: [Getter/Setter],\n S_IRGRP: [Getter/Setter],\n S_IROTH: [Getter/Setter],\n S_IRUSR: [Getter/Setter],\n S_IRWXG: [Getter/Setter],\n S_IRWXO: [Getter/Setter],\n S_IRWXU: [Getter/Setter],\n S_IWGRP: [Getter/Setter],\n S_IWOTH: [Getter/Setter],\n S_IWUSR: [Getter/Setter],\n S_IXGRP: [Getter/Setter],\n S_IXOTH: [Getter/Setter],\n S_IXUSR: [Getter/Setter],\n UV_UDP_REUSEADDR: [Getter/Setter],\n W_OK: [Getter/Setter],\n X_OK: [Getter/Setter],\n },\n crypto: {\n Cipher: [Getter/Setter],\n Cipheriv: [Getter/Setter],\n DEFAULT_ENCODING: [Getter/Setter],\n Decipher: [Getter/Setter],\n Decipheriv: [Getter/Setter],\n DiffieHellman: [Getter/Setter],\n DiffieHellmanGroup: [Getter/Setter],\n Hash: [Getter/Setter],\n Hmac: [Getter/Setter],\n Sign: [Getter/Setter],\n Verify: [Getter/Setter],\n constants: [Getter/Setter],\n createCipher: [Getter/Setter],\n createCipheriv: [Getter/Setter],\n createCredentials: [Getter/Setter],\n createDecipher: [Getter/Setter],\n createDecipheriv: [Getter/Setter],\n createDiffieHellman: [Getter/Setter],\n createDiffieHellmanGroup: [Getter/Setter],\n createECDH: [Getter/Setter],\n createHash: [Getter/Setter],\n createHmac: [Getter/Setter],\n createSign: [Getter/Setter],\n createVerify: [Getter/Setter],\n default: [Getter/Setter],\n getCiphers: [Getter/Setter],\n getCurves: [Getter/Setter],\n getDiffieHellman: [Getter/Setter],\n getHashes: [Getter/Setter],\n getRandomValues: [Getter/Setter],\n listCiphers: [Getter/Setter],\n pbkdf2: [Getter/Setter],\n pbkdf2Sync: [Getter/Setter],\n privateDecrypt: [Getter/Setter],\n privateEncrypt: [Getter/Setter],\n prng: [Getter/Setter],\n pseudoRandomBytes: [Getter/Setter],\n publicDecrypt: [Getter/Setter],\n publicEncrypt: [Getter/Setter],\n randomBytes: [Getter/Setter],\n randomFill: [Getter/Setter],\n randomFillSync: [Getter/Setter],\n randomUUID: [Getter/Setter],\n rng: [Getter/Setter],\n webcrypto: [Getter/Setter],\n },\n dgram: [Function: dgram],\n dns: [Function: dns],\n domain: {\n create: [Getter/Setter],\n createDomain: [Getter/Setter],\n },\n events: {\n EventEmitter: [Getter/Setter],\n addAbortListener: [Getter/Setter],\n captureRejectionSymbol: [Getter/Setter],\n default: [Getter/Setter],\n getEventListeners: [Getter/Setter],\n getMaxListeners: [Getter/Setter],\n init: [Getter/Setter],\n listenerCount: [Getter/Setter],\n "module.exports": [Getter/Setter],\n once: [Getter/Setter],\n setMaxListeners: [Getter/Setter],\n },\n fs: [Function: fs],\n http: {\n Agent: [Getter/Setter],\n ClientRequest: [Getter/Setter],\n IncomingMessage: [Getter/Setter],\n METHODS: [Getter/Setter],\n STATUS_CODES: [Getter/Setter],\n default: [Getter/Setter],\n get: [Getter/Setter],\n globalAgent: [Getter/Setter],\n "module.exports": [Getter/Setter],\n request: [Getter/Setter],\n },\n https: {\n Agent: [Getter/Setter],\n ClientRequest: [Getter/Setter],\n IncomingMessage: [Getter/Setter],\n METHODS: [Getter/Setter],\n OutgoingMessage: [Getter/Setter],\n STATUS_CODES: [Getter/Setter],\n Server: [Getter/Setter],\n ServerResponse: [Getter/Setter],\n createServer: [Getter/Setter],\n default: [Getter/Setter],\n get: [Getter/Setter],\n globalAgent: [Getter/Setter],\n maxHeaderSize: [Getter/Setter],\n "module.exports": [Getter/Setter],\n request: [Getter/Setter],\n setMaxIdleHTTPParsers: [Getter/Setter],\n validateHeaderName: [Getter/Setter],\n validateHeaderValue: [Getter/Setter],\n },\n module: [Function: module2],\n net: {\n default: [Getter/Setter],\n isIP: [Getter/Setter],\n isIPv4: [Getter/Setter],\n isIPv6: [Getter/Setter],\n "module.exports": [Getter/Setter],\n },\n os: {\n EOL: [Getter/Setter],\n arch: [Getter/Setter],\n cpus: [Getter/Setter],\n endianness: [Getter/Setter],\n freemem: [Getter/Setter],\n getNetworkInterfaces: [Getter/Setter],\n homedir: [Getter/Setter],\n hostname: [Getter/Setter],\n loadavg: [Getter/Setter],\n networkInterfaces: [Getter/Setter],\n platform: [Getter/Setter],\n release: [Getter/Setter],\n tmpDir: [Getter/Setter],\n tmpdir: [Getter/Setter],\n totalmem: [Getter/Setter],\n type: [Getter/Setter],\n uptime: [Getter/Setter],\n },\n path: {\n _makeLong: [Getter/Setter],\n basename: [Getter/Setter],\n default: [Getter/Setter],\n delimiter: [Getter/Setter],\n dirname: [Getter/Setter],\n extname: [Getter/Setter],\n format: [Getter/Setter],\n isAbsolute: [Getter/Setter],\n join: [Getter/Setter],\n "module.exports": [Getter/Setter],\n normalize: [Getter/Setter],\n parse: [Getter/Setter],\n posix: [Getter/Setter],\n relative: [Getter/Setter],\n resolve: [Getter/Setter],\n sep: [Getter/Setter],\n },\n perf_hooks: [Function: perf_hooks],\n process: {\n addListener: [Getter/Setter],\n argv: [Getter/Setter],\n binding: [Getter/Setter],\n browser: [Getter/Setter],\n chdir: [Getter/Setter],\n cwd: [Getter/Setter],\n emit: [Getter/Setter],\n env: [Getter/Setter],\n listeners: [Getter/Setter],\n nextTick: [Getter/Setter],\n off: [Getter/Setter],\n on: [Getter/Setter],\n once: [Getter/Setter],\n prependListener: [Getter/Setter],\n prependOnceListener: [Getter/Setter],\n removeAllListeners: [Getter/Setter],\n removeListener: [Getter/Setter],\n title: [Getter/Setter],\n umask: [Getter/Setter],\n version: [Getter/Setter],\n versions: [Getter/Setter],\n },\n punycode: {\n default: [Getter/Setter],\n "module.exports": [Getter/Setter],\n },\n querystring: {\n decode: [Getter/Setter],\n default: [Getter/Setter],\n encode: [Getter/Setter],\n escape: [Getter/Setter],\n "module.exports": [Getter/Setter],\n parse: [Getter/Setter],\n stringify: [Getter/Setter],\n unescape: [Getter/Setter],\n unescapeBuffer: [Getter/Setter],\n },\n readline: [Function: readline],\n repl: [Function: repl],\n stream: Function {\n default: [Stream: Readable],\n length: [Getter],\n name: [Getter],\n prototype: [Getter],\n ReadableState: [Getter],\n _fromList: [Getter],\n from: [Getter],\n fromWeb: [Getter],\n toWeb: [Getter],\n wrap: [Getter],\n _uint8ArrayToBuffer: [Getter],\n _isUint8Array: [Getter],\n isDisturbed: [Getter],\n isErrored: [Getter],\n isReadable: [Getter],\n Readable: [Getter],\n Writable: [Getter],\n Duplex: [Getter],\n Transform: [Getter],\n PassThrough: [Getter],\n addAbortSignal: [Getter],\n finished: [Getter],\n destroy: [Getter],\n pipeline: [Getter],\n compose: [Getter],\n Stream: [Getter],\n isDestroyed: [Function: isDestroyed],\n isWritable: [Function: isWritable],\n setDefaultHighWaterMark: [Function: setDefaultHighWaterMark],\n getDefaultHighWaterMark: [Function: getDefaultHighWaterMark],\n promises: [Getter],\n },\n string_decoder: {\n StringDecoder: [Getter/Setter],\n default: [Getter/Setter],\n "module.exports": [Getter/Setter],\n },\n sys: {\n TextDecoder: [Getter/Setter],\n TextEncoder: [Getter/Setter],\n _extend: [Getter/Setter],\n callbackify: [Getter/Setter],\n callbackifyOnRejected: [Getter/Setter],\n debuglog: [Getter/Setter],\n default: [Getter/Setter],\n deprecate: [Getter/Setter],\n format: [Getter/Setter],\n inherits: [Getter/Setter],\n inspect: [Getter/Setter],\n isArray: [Getter/Setter],\n isBoolean: [Getter/Setter],\n isBuffer: [Getter/Setter],\n isDate: [Getter/Setter],\n isError: [Getter/Setter],\n isFunction: [Getter/Setter],\n isNull: [Getter/Setter],\n isNullOrUndefined: [Getter/Setter],\n isNumber: [Getter/Setter],\n isObject: [Getter/Setter],\n isPrimitive: [Getter/Setter],\n isRegExp: [Getter/Setter],\n isString: [Getter/Setter],\n isSymbol: [Getter/Setter],\n isUndefined: [Getter/Setter],\n log: [Getter/Setter],\n "module.exports": [Getter/Setter],\n promisify: [Getter/Setter],\n types: [Getter/Setter],\n },\n timers: {\n _unrefActive: [Getter/Setter],\n clearImmediate: [Getter/Setter],\n clearInterval: [Getter/Setter],\n clearTimeout: [Getter/Setter],\n promises: [Getter/Setter],\n setImmediate: [Getter/Setter],\n setInterval: [Getter/Setter],\n setTimeout: [Getter/Setter],\n },\n tls: [Function: tls],\n tty: {\n ReadStream: [Getter/Setter],\n WriteStream: [Getter/Setter],\n default: [Getter/Setter],\n isatty: [Getter/Setter],\n "module.exports": [Getter/Setter],\n },\n url: {\n URL: [Getter/Setter],\n URLSearchParams: [Getter/Setter],\n Url: [Getter/Setter],\n default: [Getter/Setter],\n format: [Getter/Setter],\n "module.exports": [Getter/Setter],\n parse: [Getter/Setter],\n resolve: [Getter/Setter],\n resolveObject: [Getter/Setter],\n },\n util: {\n TextDecoder: [Getter/Setter],\n TextEncoder: [Getter/Setter],\n _extend: [Getter/Setter],\n callbackify: [Getter/Setter],\n callbackifyOnRejected: [Getter/Setter],\n debuglog: [Getter/Setter],\n default: [Getter/Setter],\n deprecate: [Getter/Setter],\n format: [Getter/Setter],\n inherits: [Getter/Setter],\n inspect: [Getter/Setter],\n isArray: [Getter/Setter],\n isBoolean: [Getter/Setter],\n isBuffer: [Getter/Setter],\n isDate: [Getter/Setter],\n isError: [Getter/Setter],\n isFunction: [Getter/Setter],\n isNull: [Getter/Setter],\n isNullOrUndefined: [Getter/Setter],\n isNumber: [Getter/Setter],\n isObject: [Getter/Setter],\n isPrimitive: [Getter/Setter],\n isRegExp: [Getter/Setter],\n isString: [Getter/Setter],\n isSymbol: [Getter/Setter],\n isUndefined: [Getter/Setter],\n log: [Getter/Setter],\n "module.exports": [Getter/Setter],\n promisify: [Getter/Setter],\n types: [Getter/Setter],\n },\n v8: [Function: v8],\n vm: [Function: vm],\n zlib: {\n default: [Getter/Setter],\n "module.exports": [Getter/Setter],\n },\n}', validate(ctx) {}, }, diff --git a/test/bundler/bundler_bytecode_portable.test.ts b/test/bundler/bundler_bytecode_portable.test.ts index 48fa1e114082..b10cfb0f8563 100644 --- a/test/bundler/bundler_bytecode_portable.test.ts +++ b/test/bundler/bundler_bytecode_portable.test.ts @@ -479,10 +479,10 @@ describe("bytecode cache portability", () => { }, }, "bun build --bytecode acorn/dist/acorn.mjs": { - "js": "aa22cb20382fa5d66ff2ddd90817c0899f82b346bdd1da87dc9e193d203a0ce9", + "js": "f438329328d12d8e9946cdddce7358c1f7aa76a3d6ea40883cfcb24b825d2732", "jsc": { - "bytes": 236672, - "sha256": "50aa233b4359896224903fa94bf7a222d81ff59aefaf2a4cc89824b1338f2418", + "bytes": 236776, + "sha256": "7cc3814a4d9c4c960006c28097c6029be10792f032675605cbee3071c5613a14", }, }, "bun build --bytecode all.js": { @@ -507,24 +507,24 @@ describe("bytecode cache portability", () => { }, }, "bun build --bytecode happy-dom/lib/index.js": { - "js": "1415a67d0da987f3bb7fc68ca431e222b3898fa14be8af88e2845813c4120f43", + "js": "0cc7363169ff0fe205e3d55441ab829cc48d556a475626ddf9940e84ac873dd3", "jsc": { - "bytes": 2218688, - "sha256": "8e013575bfc25c74a98f8e5acffba93e5dea1f4040ae5a82ce45bb04833f9671", + "bytes": 2218792, + "sha256": "c96a9fbdd34b5e16f6916def31213774ecaa29cad3e7c41a140c0737b0c3668c", }, }, "bun build --bytecode immutable/dist/immutable.es.js": { - "js": "d011b6c5105dad96f17aaf541c848b8d2be1b2e65a1de050112380352979bb6b", + "js": "e0f95127a9b7a615ae831744e65c76278c9804cfa5feaea7c0dac7e7811159c7", "jsc": { - "bytes": 239072, - "sha256": "1087e48ac0bba0c939cdb2bf14dc4b57214cf85aea0bdb5449140a32e4cef99e", + "bytes": 239160, + "sha256": "d24f6333b7e6475ae2dd21fe32064dcf400dfdf33d1767b29289f45c025678dd", }, }, "bun build --bytecode libraries.js": { - "js": "efb37dc3389e11bf3b88e5b53164a56d20f9640d37c019182f92e391cb0bf3b2", + "js": "27735ba5c6d580ae69eb60a5375fbe296a383d4c2acff8e99ee3988f9246c2cc", "jsc": { - "bytes": 21178784, - "sha256": "a73c6e2255f9f8cbe422d46368e63e9cc7a02e01f6d4af4c79ba55319b72c05f", + "bytes": 21178904, + "sha256": "c426e323bcf6f09325824e2114d1bbb2788481c30cf82fc72b0187f09670ae5d", }, }, "bun build --bytecode lodash/lodash.js": { diff --git a/test/bundler/bundler_cjs.test.ts b/test/bundler/bundler_cjs.test.ts index 81a4caed037a..4c68bc271313 100644 --- a/test/bundler/bundler_cjs.test.ts +++ b/test/bundler/bundler_cjs.test.ts @@ -1235,4 +1235,278 @@ describe("bundler", () => { stdout: "function/undefined", }, }); + + // ============================================================================ + // `require()` of an ES module that exports the name `module.exports` + // + // Node.js (nodejs/node#54563) and `bun run` return that export from + // `require()` instead of the namespace object. The bundle does the same: + // the call is printed as `__toCommonJS(exports_foo, 1)`, which returns + // `exports_foo["module.exports"]` once it is set. `import()` and `import` + // statements still see the namespace. + // ============================================================================ + + const moduleExportsExportFiles = { + "/point.mjs": /* js */ ` + export default class Point { + constructor(x, y) { this.x = x; this.y = y; } + static distance(a, b) { return Math.hypot(b.x - a.x, b.y - a.y); } + } + export function midpoint(a, b) { return new Point((a.x + b.x) / 2, (a.y + b.y) / 2); } + export { Point as "module.exports" }; + `, + "/star.mjs": /* js */ ` + export * from "./point.mjs"; + `, + "/falsy.mjs": /* js */ ` + const value = false; + export const other = 1; + export { value as "module.exports" }; + `, + "/entry.cjs": /* js */ ` + const Point = require("./point.mjs"); + const { distance, midpoint } = require("./point.mjs"); + class Point3D extends Point { + constructor(x, y, z) { super(x, y); this.z = z; } + } + console.log(typeof Point, new Point3D(1, 2, 3) instanceof Point, distance(new Point(0, 0), new Point(3, 4)), typeof midpoint); + console.log(require("./point.mjs") === Point, Object.prototype.hasOwnProperty.call(Point, "__esModule")); + const star = require("./star.mjs"); + console.log("star:", typeof star, star === Point); + console.log("falsy:", JSON.stringify(require("./falsy.mjs"))); + import("./point.mjs").then(ns => console.log("import():", ns.default === Point, typeof ns.midpoint, Object.keys(ns).join(","))); + `, + }; + // The same lines that `node entry.cjs` and `bun entry.cjs` print. + const moduleExportsExportStdout = [ + "function true 5 undefined", + "true false", + "star: function true", + "falsy: false", + "import(): true function default,midpoint,module.exports", + ].join("\n"); + + itBundled("cjs/require_esm_module_exports_export", { + files: moduleExportsExportFiles, + entryPoints: ["/entry.cjs"], + run: { + stdout: moduleExportsExportStdout, + }, + }); + + itBundled("cjs/require_esm_module_exports_export_minified", { + files: moduleExportsExportFiles, + entryPoints: ["/entry.cjs"], + minifyIdentifiers: true, + minifySyntax: true, + minifyWhitespace: true, + run: { + stdout: moduleExportsExportStdout, + }, + }); + + itBundled("cjs/require_esm_module_exports_export_target_browser", { + files: moduleExportsExportFiles, + entryPoints: ["/entry.cjs"], + target: "browser", + run: { + stdout: moduleExportsExportStdout, + }, + }); + + // The program from #29985: the export is a primitive. + itBundled("cjs/require_esm_module_exports_export_primitive", { + files: { + "/entry.cjs": /* js */ ` + const m = require("./m.mjs"); + console.log(m); + `, + "/m.mjs": /* js */ ` + const a = 1; + const b = 2; + export const c = 3; + export default a; + export { b as "module.exports" }; + `, + }, + entryPoints: ["/entry.cjs"], + run: { + stdout: "2", + }, + }); + + // An export whose value is `null` or `undefined` gives the namespace copy, as + // in `bun run` (Node returns the value itself). + itBundled("cjs/require_esm_module_exports_export_null", { + files: { + "/entry.cjs": /* js */ ` + const m = require("./m.mjs"); + console.log(m.a, m["module.exports"], m.__esModule, require("./m.mjs") === m); + `, + "/m.mjs": /* js */ ` + const n = null; + export const a = 1; + export { n as "module.exports" }; + `, + }, + entryPoints: ["/entry.cjs"], + run: { + stdout: "1 null true true", + }, + }); + + // A module without that export keeps the `__toCommonJS` wrapper. + itBundled("cjs/require_esm_without_module_exports_export", { + files: { + "/entry.cjs": /* js */ ` + const lib = require("./lib.mjs"); + console.log(typeof lib, lib.__esModule, typeof lib.default, lib.named); + `, + "/lib.mjs": /* js */ ` + export default function lib() {} + export const named = "named"; + `, + }, + entryPoints: ["/entry.cjs"], + run: { + stdout: "object true function named", + }, + }); + + // A `require()` result that is only feature-tested (`typeof`, truthiness) or + // only has properties read off it is still that export: tree shaking must not + // replace the call with `{}` or bind the reads to the module's other exports. + itBundled("cjs/require_esm_module_exports_export_tree_shaken_uses", { + files: { + "/entry.cjs": /* js */ ` + const EventEmitter = require("./events.mjs"); + console.log(typeof EventEmitter, typeof require("./events.mjs")); + const enabled = require("./disabled.mjs"); + if (!enabled) console.log("disabled"); + const lib = require("./lib.mjs"); + console.log(lib.version, lib.named); + `, + "/events.mjs": /* js */ ` + export default class EventEmitter {} + export function once() {} + export { EventEmitter as "module.exports" }; + `, + "/disabled.mjs": /* js */ ` + const enabled = false; + export const other = 1; + export { enabled as "module.exports" }; + `, + "/lib.mjs": /* js */ ` + function lib() {} + lib.version = 3; + export const named = "named"; + export { lib as "module.exports" }; + `, + }, + entryPoints: ["/entry.cjs"], + run: { + stdout: "function function\ndisabled\n3 undefined", + }, + }); + + // Inside a `require()` cycle the export is not assigned yet. The early + // `require()` gets the namespace copy, as it did before, and a later one gets + // the export. (Node throws ERR_REQUIRE_CYCLE_MODULE here; `bun run` hands out + // its partial `module.exports` object.) + itBundled("cjs/require_esm_module_exports_export_cycle", { + files: { + "/entry.cjs": /* js */ ` + const api = require("./api.mjs"); + console.log(JSON.stringify(api), JSON.stringify(require("./plugin.cjs").early)); + `, + "/api.mjs": /* js */ ` + import { early } from "./plugin.cjs"; + const api = { name: "api", early: typeof early }; + export const version = 2; + export { api as "module.exports" }; + `, + "/plugin.cjs": /* js */ ` + const api = require("./api.mjs"); + exports.early = { type: typeof api, version: api.version, esModule: api.__esModule }; + `, + }, + entryPoints: ["/entry.cjs"], + run: { + stdout: '{"name":"api","early":"object"} {"type":"object","version":2,"esModule":true}', + }, + }); + + // A `--format=cjs` bundle of such an entry point is what CommonJS code + // outside the bundle loads: its `require()` gets the export too, the same + // value `require("./lib.mjs")` of the source gives in Node.js and `bun run`. + const formatCjsEntryUser = /* js */ ` + const Foo = require("./out.js"); + class Bar extends Foo {} + console.log(typeof Foo, new Bar() instanceof Foo, Foo.inBundle(), require("./out.js") === Foo); + `; + itBundled("cjs/require_esm_module_exports_export_format_cjs_entry", { + files: { + "/lib.mjs": /* js */ ` + export default class Foo { + static inBundle() { return "static"; } + } + export const named = 1; + export { Foo as "module.exports" }; + `, + }, + runtimeFiles: { "/user.cjs": formatCjsEntryUser }, + format: "cjs", + target: "node", + run: { + file: "/user.cjs", + stdout: "function true static true", + }, + }); + + // The same entry point behind its `__esm` wrapper: a file in the bundle + // requires it too, and both `require()` calls agree. + itBundled("cjs/require_esm_module_exports_export_format_cjs_entry_wrapped", { + files: { + "/lib.mjs": /* js */ ` + import { inBundle } from "./inside.cjs"; + export default class Foo {} + Foo.inBundle = inBundle; + export { Foo as "module.exports" }; + `, + "/inside.cjs": /* js */ ` + exports.inBundle = () => typeof require("./lib.mjs"); + `, + }, + runtimeFiles: { "/user.cjs": formatCjsEntryUser }, + entryPoints: ["/lib.mjs"], + format: "cjs", + target: "node", + run: { + file: "/user.cjs", + stdout: "function true function true", + }, + }); + + // An entry point whose export is `null` still gives the namespace copy. + itBundled("cjs/require_esm_module_exports_export_format_cjs_entry_null", { + files: { + "/lib.mjs": /* js */ ` + const n = null; + export const a = 1; + export { n as "module.exports" }; + `, + }, + runtimeFiles: { + "/user.cjs": /* js */ ` + const m = require("./out.js"); + console.log(typeof m, m.a, m["module.exports"], m.__esModule); + `, + }, + format: "cjs", + target: "node", + run: { + file: "/user.cjs", + stdout: "object 1 null true", + }, + }); });