aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/bundler/bundler_compile.test.ts84
-rw-r--r--test/bundler/bundler_npm.test.ts46
-rw-r--r--test/bundler/esbuild/loader.test.ts40
-rw-r--r--test/bundler/expectBundled.ts242
4 files changed, 300 insertions, 112 deletions
diff --git a/test/bundler/bundler_compile.test.ts b/test/bundler/bundler_compile.test.ts
new file mode 100644
index 000000000..16982ce5a
--- /dev/null
+++ b/test/bundler/bundler_compile.test.ts
@@ -0,0 +1,84 @@
+import assert from "assert";
+import dedent from "dedent";
+import { ESBUILD, itBundled, testForFile } from "./expectBundled";
+var { describe, test, expect } = testForFile(import.meta.path);
+
+describe("bundler", () => {
+ itBundled("compile/HelloWorld", {
+ compile: true,
+ files: {
+ "/entry.ts": /* js */ `
+ console.log("Hello, world!");
+ `,
+ },
+ run: { stdout: "Hello, world!" },
+ });
+ itBundled("compile/VariousBunAPIs", {
+ compile: true,
+ files: {
+ "/entry.ts": `
+ // testing random features of bun
+ import { Database } from "bun:sqlite";
+ import { serve } from 'bun';
+ import { getRandomSeed } from 'bun:jsc';
+ const db = new Database("test.db");
+ const query = db.query(\`select "Hello world" as message\`);
+ if (query.get().message !== "Hello world") throw "fail from sqlite";
+ const icon = await fetch("https://bun.sh/favicon.ico").then(x=>x.arrayBuffer())
+ if(icon.byteLength < 100) throw "fail from icon";
+ if (typeof getRandomSeed() !== 'number') throw "fail from bun:jsc";
+ const server = serve({
+ fetch() {
+ return new Response("Hello world");
+ },
+ port: 0,
+ });
+ const res = await fetch(\`http://\${server.hostname}:\${server.port}\`);
+ if (res.status !== 200) throw "fail from server";
+ if (await res.text() !== "Hello world") throw "fail from server";
+ server.stop();
+ `,
+ },
+ run: true,
+ });
+ itBundled("compile/ReactSSR", {
+ install: ["react@next", "react-dom@next"],
+ files: {
+ "/entry.tsx": /* tsx */ `
+ import React from "react";
+ import { renderToReadableStream } from "react-dom/server";
+
+ const headers = {
+ headers: {
+ "Content-Type": "text/html",
+ },
+ };
+
+ const App = () => (
+ <html>
+ <body>
+ <h1>Hello World</h1>
+ <p>This is an example.</p>
+ </body>
+ </html>
+ );
+
+ const port = 42001;
+ const server = Bun.serve({
+ port,
+ async fetch(req) {
+ return new Response(await renderToReadableStream(<App />), headers);
+ },
+ });
+ const res = await fetch("http://localhost:" + port);
+ if (res.status !== 200) throw "status error";
+ console.log(await res.text());
+ server.stop();
+ `,
+ },
+ run: {
+ stdout: "<!DOCTYPE html><html><head></head><body><h1>Hello World</h1><p>This is an example.</p></body></html>",
+ },
+ compile: true,
+ });
+});
diff --git a/test/bundler/bundler_npm.test.ts b/test/bundler/bundler_npm.test.ts
new file mode 100644
index 000000000..c421912ed
--- /dev/null
+++ b/test/bundler/bundler_npm.test.ts
@@ -0,0 +1,46 @@
+import assert from "assert";
+import dedent from "dedent";
+import { ESBUILD, itBundled, testForFile } from "./expectBundled";
+var { describe, test, expect } = testForFile(import.meta.path);
+
+describe("bundler", () => {
+ itBundled("npm/ReactSSR", {
+ install: ["react@next", "react-dom@next"],
+ files: {
+ "/entry.tsx": /* tsx */ `
+ import React from "react";
+ import { renderToReadableStream } from "react-dom/server";
+
+ const headers = {
+ headers: {
+ "Content-Type": "text/html",
+ },
+ };
+
+ const App = () => (
+ <html>
+ <body>
+ <h1>Hello World</h1>
+ <p>This is an example.</p>
+ </body>
+ </html>
+ );
+
+ const port = 42001;
+ const server = Bun.serve({
+ port,
+ async fetch(req) {
+ return new Response(await renderToReadableStream(<App />), headers);
+ },
+ });
+ const res = await fetch("http://localhost:" + port);
+ if (res.status !== 200) throw "status error";
+ console.log(await res.text());
+ server.stop();
+ `,
+ },
+ run: {
+ stdout: "<!DOCTYPE html><html><head></head><body><h1>Hello World</h1><p>This is an example.</p></body></html>",
+ },
+ });
+});
diff --git a/test/bundler/esbuild/loader.test.ts b/test/bundler/esbuild/loader.test.ts
index 97e26eb61..ce3e8caac 100644
--- a/test/bundler/esbuild/loader.test.ts
+++ b/test/bundler/esbuild/loader.test.ts
@@ -1,4 +1,5 @@
-import { RUN_UNCHECKED_TESTS, itBundled, testForFile } from "../expectBundled";
+import fs from "fs";
+import { itBundled, testForFile } from "../expectBundled";
var { describe, test, expect } = testForFile(import.meta.path);
// Tests ported from:
@@ -63,13 +64,20 @@ describe("bundler", () => {
});
itBundled("loader/File", {
files: {
- "/entry.js": `console.log(require('./test.svg'))`,
+ "/entry.js": `
+ import path from 'path';
+ const file = require('./test.svg');
+ console.log(file);
+ const contents = await Bun.file(path.join(import.meta.dir, file)).text();
+ if(contents !== '<svg></svg>') throw new Error('Contents did not match');
+ `,
"/test.svg": `<svg></svg>`,
},
outdir: "/out",
loader: {
- // ".svg": "file",
+ ".svg": "file",
},
+ target: "bun",
run: {
stdout: /\.\/test-.*\.svg/,
},
@@ -77,8 +85,15 @@ describe("bundler", () => {
itBundled("loader/FileMultipleNoCollision", {
files: {
"/entry.js": /* js */ `
- console.log(require('./a/test.svg'))
- console.log(require('./b/test.svg'))
+ import path from 'path';
+ const file1 = require('./a/test.svg');
+ console.log(file1);
+ const contents = await Bun.file(path.join(import.meta.dir, file1)).text();
+ if(contents !== '<svg></svg>') throw new Error('Contents did not match');
+ const file2 = require('./b/test.svg');
+ console.log(file2);
+ const contents2 = await Bun.file(path.join(import.meta.dir, file2)).text();
+ if(contents2 !== '<svg></svg>') throw new Error('Contents did not match');
`,
"/a/test.svg": `<svg></svg>`,
"/b/test.svg": `<svg></svg>`,
@@ -86,6 +101,7 @@ describe("bundler", () => {
loader: {
".svg": "file",
},
+ target: "bun",
outdir: "/out",
run: {
stdout: /\.\/test-.*\.svg\n\.\/test-.*\.svg/,
@@ -94,8 +110,15 @@ describe("bundler", () => {
itBundled("loader/FileMultipleNoCollisionAssetNames", {
files: {
"/entry.js": /* js */ `
- console.log(require('./a/test.svg'))
- console.log(require('./b/test.svg'))
+ import path from 'path';
+ const file1 = require('./a/test.svg');
+ console.log(file1);
+ const contents = await Bun.file(path.join(import.meta.dir, file1)).text();
+ if(contents !== '<svg></svg>') throw new Error('Contents did not match');
+ const file2 = require('./b/test.svg');
+ console.log(file2);
+ const contents2 = await Bun.file(path.join(import.meta.dir, file2)).text();
+ if(contents2 !== '<svg></svg>') throw new Error('Contents did not match');
`,
"/a/test.svg": `<svg></svg>`,
"/b/test.svg": `<svg></svg>`,
@@ -105,8 +128,9 @@ describe("bundler", () => {
loader: {
".svg": "file",
},
+ target: "bun",
run: {
- stdout: /\.\/test-.*\.svg \.\/test-.*\.svg/,
+ stdout: /\.\/assets\/test-.*\.svg\n\.\/assets\/test-.*\.svg/,
},
});
itBundled("loader/JSXSyntaxInJSWithJSXLoader", {
diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts
index 97e863655..b47dc7300 100644
--- a/test/bundler/expectBundled.ts
+++ b/test/bundler/expectBundled.ts
@@ -106,6 +106,8 @@ export interface BundlerTestInput {
stdin?: { contents: string; cwd: string };
/** Use when doing something weird with entryPoints and you need to check other output paths. */
outputPaths?: string[];
+ /** Use --compile */
+ compile?: boolean;
/** force using cli or js api. defaults to api if possible, then cli otherwise */
backend?: "cli" | "api";
@@ -161,6 +163,7 @@ export interface BundlerTestInput {
useDefineForClassFields?: boolean;
sourceMap?: "inline" | "external" | "none";
plugins?: BunPlugin[] | ((builder: PluginBuilder) => void | Promise<void>);
+ install?: string[];
// pass subprocess.env
env?: Record<string, any>;
@@ -319,6 +322,7 @@ function expectBundled(
capture,
chunkNaming,
cjs2esm,
+ compile,
dce,
dceKeepMarkerCount,
define,
@@ -331,6 +335,7 @@ function expectBundled(
format,
globalName,
inject,
+ install,
jsx = {},
keepNames,
legalComments,
@@ -449,7 +454,7 @@ function expectBundled(
external = external.map(x => (typeof x !== "string" ? x : x.replace(/\{\{root\}\}/g, root)));
}
- outfile = useOutFile ? path.join(root, outfile ?? "/out.js") : undefined;
+ outfile = useOutFile ? path.join(root, outfile ?? (compile ? "/out" : "/out.js")) : undefined;
outdir = !useOutFile ? path.join(root, outdir ?? "/out") : undefined;
metafile = metafile ? path.join(root, metafile) : undefined;
outputPaths = (
@@ -480,6 +485,16 @@ function expectBundled(
if (existsSync(root)) {
rmSync(root, { recursive: true });
}
+ mkdirSync(root, { recursive: true });
+ if (install) {
+ const installProcess = Bun.spawnSync({
+ cmd: [bunExe(), "install", ...install],
+ cwd: root,
+ });
+ if (!installProcess.success) {
+ throw new Error("Failed to install dependencies");
+ }
+ }
for (const [file, contents] of Object.entries(files)) {
const filename = path.join(root, file);
mkdirSync(path.dirname(filename), { recursive: true });
@@ -525,6 +540,7 @@ function expectBundled(
...entryPaths,
...(entryPointsRaw ?? []),
bundling === false ? "--no-bundle" : [],
+ compile ? "--compile" : [],
outfile ? `--outfile=${outfile}` : `--outdir=${outdir}`,
define && Object.entries(define).map(([k, v]) => ["--define", `${k}=${v}`]),
`--target=${target}`,
@@ -618,6 +634,22 @@ function expectBundled(
{
"version": "0.2.0",
"configurations": [
+ ...(compile
+ ? [
+ {
+ "type": "lldb",
+ "request": "launch",
+ "name": "run compiled exe",
+ "program": outfile,
+ "args": [],
+ "cwd": root,
+ "env": {
+ "FORCE_COLOR": "1",
+ },
+ "console": "internalConsole",
+ },
+ ]
+ : []),
{
"type": "lldb",
"request": "launch",
@@ -1000,125 +1032,127 @@ 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 (outfile) {
- if (!existsSync(outfile)) {
- throw new Error("Bundle was not written to disk: " + outfile);
- } else {
- const content = readFileSync(outfile).toString();
- if (dce) {
- const dceFails = [...content.matchAll(/FAIL|FAILED|DROP|REMOVE/gi)];
- if (dceFails.length) {
- throw new Error("DCE test did not remove all expected code in " + outfile + ".");
- }
- if (dceKeepMarkerCount !== false) {
- const keepMarkersThisFile = [...content.matchAll(/KEEP/gi)].length;
- keepMarkersFound += keepMarkersThisFile;
- if (
- (typeof dceKeepMarkerCount === "number"
- ? dceKeepMarkerCount
- : Object.values(keepMarkers).reduce((a, b) => a + b, 0)) !== keepMarkersThisFile
- ) {
- throw new Error(
- "DCE keep markers were not preserved in " +
- outfile +
- ". Expected " +
- keepMarkers[outfile] +
- " but found " +
- keepMarkersThisFile +
- ".",
- );
+ // TODO: clean up this entire bit into one main loop\
+ if (!compile) {
+ if (outfile) {
+ if (!existsSync(outfile)) {
+ throw new Error("Bundle was not written to disk: " + outfile);
+ } else {
+ if (dce) {
+ const content = readFileSync(outfile).toString();
+ const dceFails = [...content.matchAll(/FAIL|FAILED|DROP|REMOVE/gi)];
+ if (dceFails.length) {
+ throw new Error("DCE test did not remove all expected code in " + outfile + ".");
+ }
+ if (dceKeepMarkerCount !== false) {
+ const keepMarkersThisFile = [...content.matchAll(/KEEP/gi)].length;
+ keepMarkersFound += keepMarkersThisFile;
+ if (
+ (typeof dceKeepMarkerCount === "number"
+ ? dceKeepMarkerCount
+ : Object.values(keepMarkers).reduce((a, b) => a + b, 0)) !== keepMarkersThisFile
+ ) {
+ throw new Error(
+ "DCE keep markers were not preserved in " +
+ outfile +
+ ". Expected " +
+ keepMarkers[outfile] +
+ " but found " +
+ keepMarkersThisFile +
+ ".",
+ );
+ }
}
}
+ if (!ESBUILD) {
+ // expect(readFileSync(outfile).toString()).toMatchSnapshot(outfile.slice(root.length));
+ }
}
- if (!ESBUILD) {
- // expect(readFileSync(outfile).toString()).toMatchSnapshot(outfile.slice(root.length));
- }
- }
- } else {
- // entryNames makes it so we cannot predict the output file
- if (!entryNaming || entryNaming === "[dir]/[name].[ext]") {
- for (const fullpath of outputPaths) {
- if (!existsSync(fullpath)) {
- throw new Error("Bundle was not written to disk: " + fullpath);
- } else {
- if (!ESBUILD) {
- // expect(readFileSync(fullpath).toString()).toMatchSnapshot(fullpath.slice(root.length));
- }
- if (dce) {
- const content = readFileSync(fullpath, "utf8");
- const dceFails = [...content.replace(/\/\*.*?\*\//g, "").matchAll(/FAIL|FAILED|DROP|REMOVE/gi)];
- const key = fullpath.slice(root.length);
- if (dceFails.length) {
- throw new Error("DCE test did not remove all expected code in " + key + ".");
+ } else {
+ // entryNames makes it so we cannot predict the output file
+ if (!entryNaming || entryNaming === "[dir]/[name].[ext]") {
+ for (const fullpath of outputPaths) {
+ if (!existsSync(fullpath)) {
+ throw new Error("Bundle was not written to disk: " + fullpath);
+ } else {
+ if (!ESBUILD) {
+ // expect(readFileSync(fullpath).toString()).toMatchSnapshot(fullpath.slice(root.length));
}
- if (dceKeepMarkerCount !== false) {
- const keepMarkersThisFile = [...content.matchAll(/KEEP/gi)].length;
- keepMarkersFound += keepMarkersThisFile;
- if (keepMarkers[key] !== keepMarkersThisFile) {
- throw new Error(
- "DCE keep markers were not preserved in " +
- key +
- ". Expected " +
- keepMarkers[key] +
- " but found " +
- keepMarkersThisFile +
- ".",
- );
+ if (dce) {
+ const content = readFileSync(fullpath, "utf8");
+ const dceFails = [...content.replace(/\/\*.*?\*\//g, "").matchAll(/FAIL|FAILED|DROP|REMOVE/gi)];
+ const key = fullpath.slice(root.length);
+ if (dceFails.length) {
+ throw new Error("DCE test did not remove all expected code in " + key + ".");
+ }
+ if (dceKeepMarkerCount !== false) {
+ const keepMarkersThisFile = [...content.matchAll(/KEEP/gi)].length;
+ keepMarkersFound += keepMarkersThisFile;
+ if (keepMarkers[key] !== keepMarkersThisFile) {
+ throw new Error(
+ "DCE keep markers were not preserved in " +
+ key +
+ ". Expected " +
+ keepMarkers[key] +
+ " but found " +
+ keepMarkersThisFile +
+ ".",
+ );
+ }
}
}
}
}
+ } else if (!ESBUILD) {
+ // TODO: snapshot these test cases
}
- } else if (!ESBUILD) {
- // TODO: snapshot these test cases
}
- }
- if (
- dce &&
- dceKeepMarkerCount !== false &&
- typeof dceKeepMarkerCount === "number" &&
- keepMarkersFound !== dceKeepMarkerCount
- ) {
- throw new Error(
- `DCE keep markers were not preserved. Expected ${dceKeepMarkerCount} KEEP markers, but found ${keepMarkersFound}.`,
- );
- }
+ if (
+ dce &&
+ dceKeepMarkerCount !== false &&
+ typeof dceKeepMarkerCount === "number" &&
+ keepMarkersFound !== dceKeepMarkerCount
+ ) {
+ throw new Error(
+ `DCE keep markers were not preserved. Expected ${dceKeepMarkerCount} KEEP markers, but found ${keepMarkersFound}.`,
+ );
+ }
- if (assertNotPresent) {
- for (const [key, value] of Object.entries(assertNotPresent)) {
- const filepath = path.join(root, key);
- if (existsSync(filepath)) {
- const strings = Array.isArray(value) ? value : [value];
- for (const str of strings) {
- if (api.readFile(key).includes(str)) throw new Error(`Expected ${key} to not contain "${str}"`);
+ if (assertNotPresent) {
+ for (const [key, value] of Object.entries(assertNotPresent)) {
+ const filepath = path.join(root, key);
+ if (existsSync(filepath)) {
+ const strings = Array.isArray(value) ? value : [value];
+ for (const str of strings) {
+ if (api.readFile(key).includes(str)) throw new Error(`Expected ${key} to not contain "${str}"`);
+ }
}
}
}
- }
- if (capture) {
- const captures = api.captureFile(path.relative(root, outfile ?? outputPaths[0]));
- expect(captures).toEqual(capture);
- }
+ if (capture) {
+ const captures = api.captureFile(path.relative(root, outfile ?? outputPaths[0]));
+ expect(captures).toEqual(capture);
+ }
- // cjs2esm checks
- if (cjs2esm) {
- const outfiletext = api.readFile(path.relative(root, outfile ?? outputPaths[0]));
- const regex = /\/\/\s+(.+?)\nvar\s+([a-zA-Z0-9_$]+)\s+=\s+__commonJS/g;
- const matches = [...outfiletext.matchAll(regex)].map(match => "/" + match[1]);
- const expectedMatches = cjs2esm === true ? [] : cjs2esm.unhandled ?? [];
- try {
- expect(matches.sort()).toEqual(expectedMatches.sort());
- } catch (error) {
- if (matches.length === expectedMatches.length) {
- console.error(`cjs2esm check failed.`);
- } else {
- console.error(
- `cjs2esm check failed. expected ${expectedMatches.length} __commonJS helpers but found ${matches.length}.`,
- );
+ // cjs2esm checks
+ if (cjs2esm) {
+ const outfiletext = api.readFile(path.relative(root, outfile ?? outputPaths[0]));
+ const regex = /\/\/\s+(.+?)\nvar\s+([a-zA-Z0-9_$]+)\s+=\s+__commonJS/g;
+ const matches = [...outfiletext.matchAll(regex)].map(match => "/" + match[1]);
+ const expectedMatches = cjs2esm === true ? [] : cjs2esm.unhandled ?? [];
+ try {
+ expect(matches.sort()).toEqual(expectedMatches.sort());
+ } catch (error) {
+ if (matches.length === expectedMatches.length) {
+ console.error(`cjs2esm check failed.`);
+ } else {
+ console.error(
+ `cjs2esm check failed. expected ${expectedMatches.length} __commonJS helpers but found ${matches.length}.`,
+ );
+ }
+ throw error;
}
- throw error;
}
}
@@ -1175,7 +1209,7 @@ for (const [key, blob] of build.outputs) {
const { success, stdout, stderr } = Bun.spawnSync({
cmd: [
- (run.runtime ?? "bun") === "bun" ? bunExe() : "node",
+ ...(compile ? [] : [(run.runtime ?? "bun") === "bun" ? bunExe() : "node"]),
...(run.bunArgs ?? []),
file,
...(run.args ?? []),