diff --git a/test/bundler/css/wpt/background-computed.test.ts b/test/bundler/css/wpt/background-computed.test.ts index 21fc84cc39..28e20d7a8f 100644 --- a/test/bundler/css/wpt/background-computed.test.ts +++ b/test/bundler/css/wpt/background-computed.test.ts @@ -12,6 +12,7 @@ h1 { `, }, outfile: "out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` diff --git a/test/bundler/css/wpt/color-computed-rgb.test.ts b/test/bundler/css/wpt/color-computed-rgb.test.ts index 1611be6580..fb71246f13 100644 --- a/test/bundler/css/wpt/color-computed-rgb.test.ts +++ b/test/bundler/css/wpt/color-computed-rgb.test.ts @@ -12,6 +12,7 @@ h1 { `, }, outfile: "out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` diff --git a/test/bundler/css/wpt/color-computed.test.ts b/test/bundler/css/wpt/color-computed.test.ts index 4377deb91e..da3f812926 100644 --- a/test/bundler/css/wpt/color-computed.test.ts +++ b/test/bundler/css/wpt/color-computed.test.ts @@ -11,6 +11,7 @@ h1 { `, }, outfile: "out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` diff --git a/test/bundler/css/wpt/relative_color_out_of_gamut.test.ts b/test/bundler/css/wpt/relative_color_out_of_gamut.test.ts index fc734d383c..a8d317d2fe 100644 --- a/test/bundler/css/wpt/relative_color_out_of_gamut.test.ts +++ b/test/bundler/css/wpt/relative_color_out_of_gamut.test.ts @@ -13,6 +13,7 @@ h1 { `, }, outfile: "out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -33,6 +34,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -53,6 +55,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -73,6 +76,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -93,6 +97,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -113,6 +118,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -133,6 +139,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -153,6 +160,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -173,6 +181,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -193,6 +202,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -213,6 +223,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -233,6 +244,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -253,6 +265,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -273,6 +286,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -293,6 +307,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -313,6 +328,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -333,6 +349,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -353,6 +370,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -373,6 +391,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -393,6 +412,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -413,6 +433,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -433,6 +454,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -453,6 +475,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -473,6 +496,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -493,6 +517,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -513,6 +538,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` @@ -533,6 +559,7 @@ h1 { `, }, outfile: "/out.css", + memory: true, onAfterBundle(api) { api.expectFile("/out.css").toEqualIgnoringWhitespace(` diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts index 768b3ca4ab..64d7acda18 100644 --- a/test/bundler/expectBundled.ts +++ b/test/bundler/expectBundled.ts @@ -299,6 +299,9 @@ export interface BundlerTestInput { /** Run after the bun.build function is called with its output */ onAfterApiBundle?(build: BuildOutput): Promise | void; + + /** If true, build outputs are kept in memory instead of written to disk. Defaults to false. */ + memory?: boolean; } export interface SourceMapTests { @@ -494,6 +497,7 @@ function expectBundled( generateOutput = true, onAfterApiBundle, throw: _throw = false, + memory = false, ...unknownProps } = opts; @@ -584,7 +588,10 @@ function expectBundled( } return (async () => { - if (!backend) { + // memory mode requires API backend since we need in-memory outputs + if (memory) { + backend = "api"; + } else if (!backend) { backend = dotenv || typeof production !== "undefined" || @@ -701,6 +708,8 @@ function expectBundled( // Run bun build cli. In the future we can move to using `Bun.Transpiler.` let warningReference: Record = {}; + // Map to store build outputs in memory when memory=true + const memoryOutputs: Map = new Map(); const expectedErrors = bundleErrors ? Object.entries(bundleErrors).flatMap(([file, v]) => v.map(error => ({ file, error }))) : null; @@ -1111,7 +1120,7 @@ function expectBundled( }, plugins: pluginArray, treeShaking, - outdir: generateOutput ? buildOutDir : undefined, + outdir: generateOutput && !memory ? buildOutDir : undefined, sourcemap: sourceMap, splitting, target, @@ -1284,6 +1293,20 @@ for (const [key, blob] of build.outputs) { } else if (expectedErrors && expectedErrors.length > 0) { throw new Error("Errors were expected while bundling:\n" + expectedErrors.map(formatError).join("\n")); } + + // Populate memoryOutputs if memory mode is enabled + if (memory && build.success) { + for (const artifact of build.outputs) { + let normalizedPath = artifact.path; + // Normalize path to have leading slash + if (normalizedPath.startsWith("./")) { + normalizedPath = normalizedPath.slice(1); + } else if (!normalizedPath.startsWith("/")) { + normalizedPath = "/" + normalizedPath; + } + memoryOutputs.set(normalizedPath, await artifact.text()); + } + } } else { await esbuild.build({ bundle: true, @@ -1295,11 +1318,23 @@ for (const [key, blob] of build.outputs) { } const readCache: Record = {}; - const readFile = (file: string) => - readCache[file] || (readCache[file] = readFileSync(path.join(root, file)).toUnixString()); + const readFile = (file: string) => { + if (readCache[file]) return readCache[file]; + // Check memory outputs first + if (memoryOutputs.size > 0) { + const memContent = memoryOutputs.get(file); + if (memContent !== undefined) { + readCache[file] = memContent; + return memContent; + } + } + return (readCache[file] = readFileSync(path.join(root, file)).toUnixString()); + }; const writeFile = (file: string, contents: string) => { readCache[file] = contents; - writeFileSync(path.join(root, file), contents); + if (!memory) { + writeFileSync(path.join(root, file), contents); + } }; const api = { root, @@ -1312,7 +1347,11 @@ for (const [key, blob] of build.outputs) { prependFile: (file, contents) => writeFile(file, dedent(contents) + "\n" + readFile(file)), appendFile: (file, contents) => writeFile(file, readFile(file) + "\n" + dedent(contents)), assertFileExists: file => { - if (!existsSync(path.join(root, file))) { + if (memoryOutputs.size > 0) { + if (!memoryOutputs.has(file)) { + throw new Error("Expected file to be in memory: " + file); + } + } else if (!existsSync(path.join(root, file))) { throw new Error("Expected file to be written: " + file); } }, @@ -1369,7 +1408,7 @@ for (const [key, blob] of build.outputs) { // Check that the bundle failed with status code 0 by verifying all files exist. // TODO: clean up this entire bit into one main loop\ - if (!compile) { + if (!compile && !memory) { if (outfile) { if (!existsSync(outfile)) { throw new Error("Bundle was not written to disk: " + outfile);