import assert from "assert"; import { itBundled, testForFile } from "./expectBundled"; var { describe, test, expect } = testForFile(import.meta.path); describe("bundler", () => { const nodePolyfillList = { "assert": "polyfill", "buffer": "polyfill", "child_process": "no-op", "cluster": "no-op", "console": "polyfill", "constants": "polyfill", "crypto": "polyfill", "dgram": "no-op", "dns": "no-op", "domain": "polyfill", "events": "polyfill", "fs": "no-op", "http": "polyfill", "https": "polyfill", "module": "no-op", "net": "polyfill", "os": "polyfill", "path": "polyfill", "perf_hooks": "no-op", "process": "polyfill", "punycode": "polyfill", "querystring": "polyfill", "readline": "no-op", "repl": "no-op", "stream": "polyfill", "string_decoder": "polyfill", "sys": "polyfill", "timers": "polyfill", "tls": "no-op", "tty": "polyfill", "url": "polyfill", "util": "polyfill", "v8": "no-op", "vm": "no-op", "zlib": "polyfill", }; itBundled("browser/NodeFS", { files: { "/entry.js": /* js */ ` import * as fs from "node:fs"; import * as fs2 from "fs"; import { readFileSync } from "fs"; console.log(typeof fs); console.log(typeof fs2); console.log(typeof readFileSync); `, }, target: "browser", run: { stdout: "function\nfunction\nundefined", }, }); // TODO: use nodePolyfillList to generate the code in here. const NodePolyfills = itBundled("browser/NodePolyfills", { files: { "/entry.js": /* js */ ` import * as assert from "node:assert"; import * as buffer from "node:buffer"; import * as child_process from "node:child_process"; import * as cluster from "node:cluster"; import * as console2 from "node:console"; import * as constants from "node:constants"; import * as crypto from "node:crypto"; import * as dgram from "node:dgram"; import * as dns from "node:dns"; import * as domain from "node:domain"; import * as events from "node:events"; import * as fs from "node:fs"; import * as http from "node:http"; import * as https from "node:https"; import * as module2 from "node:module"; import * as net from "node:net"; import * as os from "node:os"; import * as path from "node:path"; import * as perf_hooks from "node:perf_hooks"; import * as process from "node:process"; import * as punycode from "node:punycode"; import * as querystring from "node:querystring"; import * as readline from "node:readline"; import * as repl from "node:repl"; import * as stream from "node:stream"; import * as string_decoder from "node:string_decoder"; import * as sys from "node:sys"; import * as timers from "node:timers"; import * as tls from "node:tls"; import * as tty from "node:tty"; import * as url from "node:url"; import * as util from "node:util"; import * as v8 from "node:v8"; import * as vm from "node:vm"; import * as zlib from "node:zlib"; function scan(obj) { if (typeof obj === 'function') obj = obj() return Object.keys(obj).length === 0 ? 'no-op' : 'polyfill' } console.log('assert :', scan(assert)) console.log('buffer :', scan(buffer)) console.log('child_process :', scan(child_process)) console.log('cluster :', scan(cluster)) console.log('console :', console2 === console ? 'equal' : 'polyfill') console.log('constants :', scan(constants)) console.log('crypto :', scan(crypto)) console.log('dgram :', scan(dgram)) console.log('dns :', scan(dns)) console.log('domain :', scan(domain)) console.log('events :', scan(events)) console.log('fs :', scan(fs)) console.log('http :', scan(http)) console.log('https :', scan(https)) console.log('module :', scan(module2)) console.log('net :', scan(net)) console.log('os :', scan(os)) console.log('path :', scan(path)) console.log('perf_hooks :', scan(perf_hooks)) console.log('process :', scan(process)) console.log('punycode :', scan(punycode)) console.log('querystring :', scan(querystring)) console.log('readline :', scan(readline)) console.log('repl :', scan(repl)) console.log('stream :', scan(stream)) console.log('string_decoder :', scan(string_decoder)) console.log('sys :', scan(sys)) console.log('timers :', scan(timers)) console.log('tls :', scan(tls)) console.log('tty :', scan(tty)) console.log('url :', scan(url)) console.log('util :', scan(util)) console.log('v8 :', scan(v8)) console.log('vm :', scan(vm)) console.log('zlib :', scan(zlib)) `, }, target: "browser", onAfterBundle(api) { assert(!api.readFile("/out.js").includes("\0"), "bundle should not contain null bytes"); const file = api.readFile("/out.js"); const imports = new Bun.Transpiler().scanImports(file); expect(imports).toStrictEqual([]); }, run: { stdout: ` assert : polyfill buffer : polyfill child_process : no-op cluster : no-op console : polyfill constants : polyfill crypto : polyfill dgram : no-op dns : no-op domain : polyfill events : polyfill fs : no-op http : polyfill https : polyfill module : no-op net : polyfill os : polyfill path : polyfill perf_hooks : no-op process : polyfill punycode : polyfill querystring : polyfill readline : no-op repl : no-op stream : polyfill string_decoder : polyfill sys : polyfill timers : polyfill tls : no-op tty : polyfill url : polyfill util : polyfill v8 : no-op vm : no-op zlib : polyfill `, }, }); itBundled("browser/NodePolyfillExternal", { todo: true, skipOnEsbuild: true, files: { "/entry.js": NodePolyfills.options.files["/entry.js"], }, target: "browser", external: Object.keys(nodePolyfillList), onAfterBundle(api) { const file = api.readFile("/out.js"); const imports = new Bun.Transpiler().scanImports(file); expect(imports).toStrictEqual( Object.keys(nodePolyfillList).map(x => ({ kind: "import-statement", path: "node:" + x, })), ); }, }); // unsure: do we want polyfills or no-op stuff like node:* has // right now all error except bun:wrap which errors at resolve time, but is included if external const bunModules: Record = { "bun": "error", "bun:ffi": "error", "bun:dns": "error", "bun:test": "error", "bun:sqlite": "error", // "bun:wrap": "error", "bun:internal": "error", "bun:jsc": "error", }; const nonErroringBunModules = Object.entries(bunModules) .filter(x => x[1] !== "error") .map(x => x[0]); // all of them are set to error so this test doesnt make sense to run itBundled.skip("browser/BunPolyfill", { skipOnEsbuild: true, files: { "/entry.js": ` ${nonErroringBunModules.map((x, i) => `import * as bun_${i} from "${x}";`).join("\n")} function scan(obj) { if (typeof obj === 'function') obj = obj() return Object.keys(obj).length === 0 ? 'no-op' : 'polyfill' } ${nonErroringBunModules.map((x, i) => `console.log("${x.padEnd(12, " ")}:", scan(bun_${i}));`).join("\n")} `, }, target: "browser", onAfterBundle(api) { assert(!api.readFile("/out.js").includes("\0"), "bundle should not contain null bytes"); const file = api.readFile("/out.js"); const imports = new Bun.Transpiler().scanImports(file); expect(imports).toStrictEqual([]); }, run: { stdout: nonErroringBunModules.map(x => `${x.padEnd(12, " ")}: ${bunModules[x]}`).join("\n"), }, }); const ImportBunError = itBundled("browser/ImportBunError", { skipOnEsbuild: true, files: { "/entry.js": ` ${Object.keys(bunModules) .map((x, i) => `import * as bun_${i} from "${x}";`) .join("\n")} ${Object.keys(bunModules) .map((x, i) => `console.log("${x.padEnd(12, " ")}:", !!bun_${i});`) .join("\n")} `, }, target: "browser", bundleErrors: { "/entry.js": Object.keys(bunModules) .filter(x => bunModules[x] === "error") .map(x => `Could not resolve: "${x}". Maybe you need to "bun install"?`), }, }); // not implemented right now itBundled("browser/BunPolyfillExternal", { skipOnEsbuild: true, files: ImportBunError.options.files, target: "browser", external: Object.keys(bunModules), onAfterBundle(api) { const file = api.readFile("/out.js"); const imports = new Bun.Transpiler().scanImports(file); expect(imports).toStrictEqual( Object.keys(bunModules).map(x => ({ kind: "import-statement", path: x, })), ); }, }); itBundled("browser/ImportNonExistentNodeBuiltinShouldError", { skipOnEsbuild: true, files: { "/entry.js": ` import net1 from "node:net1"; `, }, bundleErrors: { "/entry.js": [`Could not resolve: "node:net1". Maybe you need to "bun install"?`], }, }); itBundled("browser/ImportNonExistentWithoutNodePrefix", { skipOnEsbuild: true, files: { "/entry.js": ` import net1 from "net1"; `, }, bundleErrors: { "/entry.js": [`Could not resolve: "net1". Maybe you need to "bun install"?`], }, }); itBundled("browser/TargetNodeNonExistentBuiltinShouldBeExternal", { skipOnEsbuild: true, files: { "/entry.js": ` import net1 from "node:net1"; `, }, target: "node", onAfterBundle(api) { const contents = api.readFile("out.js"); expect(contents).toContain('from "node:net1"'); }, }); });