mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
346 lines
12 KiB
TypeScript
346 lines
12 KiB
TypeScript
import { describe, expect } from "bun:test";
|
|
import { existsSync, readFileSync } from "fs";
|
|
import { itBundled } from "../expectBundled";
|
|
|
|
// Tests ported from:
|
|
// https://github.com/evanw/esbuild/blob/main/internal/bundler_tests/bundler_default_test.go
|
|
|
|
describe("bundler", () => {
|
|
itBundled("metafile/ImportWithTypeJSON", {
|
|
files: {
|
|
"/project/entry.js": /* js */ `
|
|
import a from './data.json'
|
|
import b from './data.json' assert { type: 'json' }
|
|
import c from './data.json' with { type: 'json' }
|
|
x = [a, b, c]
|
|
`,
|
|
"/project/data.json": `{"some": "data"}`,
|
|
},
|
|
outdir: "/out",
|
|
metafile: "/metafile.json",
|
|
onAfterBundle(api) {
|
|
const metafilePath = api.join("metafile.json");
|
|
expect(existsSync(metafilePath)).toBe(true);
|
|
const metafile = JSON.parse(readFileSync(metafilePath, "utf-8"));
|
|
expect(metafile.inputs).toBeDefined();
|
|
expect(metafile.outputs).toBeDefined();
|
|
// Should have imports with 'with' clause for JSON
|
|
const entryInputKey = Object.keys(metafile.inputs).find(k => k.includes("entry.js"));
|
|
expect(entryInputKey).toBeDefined();
|
|
const entryInput = metafile.inputs[entryInputKey!];
|
|
expect(entryInput.imports.length).toBeGreaterThan(0);
|
|
// At least one import should have a 'with' clause
|
|
const hasWithClause = entryInput.imports.some((imp: any) => imp.with?.type === "json");
|
|
expect(hasWithClause).toBe(true);
|
|
},
|
|
});
|
|
|
|
itBundled("metafile/BasicStructure", {
|
|
files: {
|
|
"/entry.js": /* js */ `
|
|
import { foo } from './foo.js';
|
|
console.log(foo);
|
|
`,
|
|
"/foo.js": /* js */ `
|
|
export const foo = 42;
|
|
`,
|
|
},
|
|
outdir: "/out",
|
|
metafile: "/metafile.json",
|
|
onAfterBundle(api) {
|
|
const metafilePath = api.join("metafile.json");
|
|
expect(existsSync(metafilePath)).toBe(true);
|
|
const metafile = JSON.parse(readFileSync(metafilePath, "utf-8"));
|
|
// Check basic structure
|
|
expect(metafile.inputs).toBeDefined();
|
|
expect(metafile.outputs).toBeDefined();
|
|
expect(Object.keys(metafile.inputs).length).toBeGreaterThanOrEqual(2);
|
|
expect(Object.keys(metafile.outputs).length).toBeGreaterThanOrEqual(1);
|
|
// Check input has bytes and imports
|
|
for (const input of Object.values(metafile.inputs) as any[]) {
|
|
expect(typeof input.bytes).toBe("number");
|
|
expect(Array.isArray(input.imports)).toBe(true);
|
|
}
|
|
// Check output has bytes, inputs, imports, exports
|
|
for (const output of Object.values(metafile.outputs) as any[]) {
|
|
expect(typeof output.bytes).toBe("number");
|
|
expect(typeof output.inputs).toBe("object");
|
|
expect(Array.isArray(output.imports)).toBe(true);
|
|
expect(Array.isArray(output.exports)).toBe(true);
|
|
}
|
|
},
|
|
});
|
|
|
|
itBundled("metafile/MultipleEntryPoints", {
|
|
files: {
|
|
"/a.js": /* js */ `
|
|
import { shared } from './shared.js';
|
|
console.log('a', shared);
|
|
`,
|
|
"/b.js": /* js */ `
|
|
import { shared } from './shared.js';
|
|
console.log('b', shared);
|
|
`,
|
|
"/shared.js": /* js */ `
|
|
export const shared = 'shared value';
|
|
`,
|
|
},
|
|
entryPoints: ["/a.js", "/b.js"],
|
|
outdir: "/out",
|
|
metafile: "/metafile.json",
|
|
splitting: true,
|
|
onAfterBundle(api) {
|
|
const metafilePath = api.join("metafile.json");
|
|
expect(existsSync(metafilePath)).toBe(true);
|
|
const metafile = JSON.parse(readFileSync(metafilePath, "utf-8"));
|
|
expect(metafile.inputs).toBeDefined();
|
|
expect(metafile.outputs).toBeDefined();
|
|
// With splitting, we should have multiple outputs
|
|
expect(Object.keys(metafile.outputs).length).toBeGreaterThanOrEqual(2);
|
|
},
|
|
});
|
|
|
|
itBundled("metafile/ExternalImports", {
|
|
files: {
|
|
"/entry.js": /* js */ `
|
|
import ext1 from 'external-pkg-1';
|
|
import ext2 from 'external-pkg-2';
|
|
console.log(ext1, ext2);
|
|
`,
|
|
},
|
|
outdir: "/out",
|
|
metafile: "/metafile.json",
|
|
external: ["external-pkg-1", "external-pkg-2"],
|
|
onAfterBundle(api) {
|
|
const metafilePath = api.join("metafile.json");
|
|
expect(existsSync(metafilePath)).toBe(true);
|
|
const metafile = JSON.parse(readFileSync(metafilePath, "utf-8"));
|
|
// Find the entry file
|
|
const entryKey = Object.keys(metafile.inputs).find(k => k.includes("entry.js"));
|
|
expect(entryKey).toBeDefined();
|
|
const entry = metafile.inputs[entryKey!];
|
|
// Check that external imports are marked
|
|
const externalImports = entry.imports.filter((imp: any) => imp.external === true);
|
|
expect(externalImports.length).toBe(2);
|
|
},
|
|
});
|
|
|
|
itBundled("metafile/DynamicImport", {
|
|
files: {
|
|
"/entry.js": /* js */ `
|
|
import('./dynamic.js').then(m => console.log(m));
|
|
`,
|
|
"/dynamic.js": /* js */ `
|
|
export const value = 123;
|
|
`,
|
|
},
|
|
outdir: "/out",
|
|
metafile: "/metafile.json",
|
|
splitting: true,
|
|
onAfterBundle(api) {
|
|
const metafilePath = api.join("metafile.json");
|
|
expect(existsSync(metafilePath)).toBe(true);
|
|
const metafile = JSON.parse(readFileSync(metafilePath, "utf-8"));
|
|
expect(metafile.inputs).toBeDefined();
|
|
expect(metafile.outputs).toBeDefined();
|
|
// Find the entry file
|
|
const entryKey = Object.keys(metafile.inputs).find(k => k.includes("entry.js"));
|
|
expect(entryKey).toBeDefined();
|
|
const entry = metafile.inputs[entryKey!];
|
|
// Should have a dynamic import
|
|
const dynamicImports = entry.imports.filter((imp: any) => imp.kind === "dynamic-import");
|
|
expect(dynamicImports.length).toBe(1);
|
|
},
|
|
});
|
|
|
|
itBundled("metafile/RequireCall", {
|
|
files: {
|
|
"/entry.js": /* js */ `
|
|
const foo = require('./foo.js');
|
|
console.log(foo);
|
|
`,
|
|
"/foo.js": /* js */ `
|
|
module.exports = { value: 42 };
|
|
`,
|
|
},
|
|
outdir: "/out",
|
|
metafile: "/metafile.json",
|
|
onAfterBundle(api) {
|
|
const metafilePath = api.join("metafile.json");
|
|
expect(existsSync(metafilePath)).toBe(true);
|
|
const metafile = JSON.parse(readFileSync(metafilePath, "utf-8"));
|
|
expect(metafile.inputs).toBeDefined();
|
|
// Find the entry file
|
|
const entryKey = Object.keys(metafile.inputs).find(k => k.includes("entry.js"));
|
|
expect(entryKey).toBeDefined();
|
|
const entry = metafile.inputs[entryKey!];
|
|
// Should have a require call
|
|
const requireImports = entry.imports.filter((imp: any) => imp.kind === "require-call");
|
|
expect(requireImports.length).toBe(1);
|
|
},
|
|
});
|
|
|
|
itBundled("metafile/ReExports", {
|
|
files: {
|
|
"/entry.js": /* js */ `
|
|
export { foo } from './foo.js';
|
|
export * from './bar.js';
|
|
`,
|
|
"/foo.js": /* js */ `
|
|
export const foo = 1;
|
|
`,
|
|
"/bar.js": /* js */ `
|
|
export const bar = 2;
|
|
export const baz = 3;
|
|
`,
|
|
},
|
|
outdir: "/out",
|
|
metafile: "/metafile.json",
|
|
onAfterBundle(api) {
|
|
const metafilePath = api.join("metafile.json");
|
|
expect(existsSync(metafilePath)).toBe(true);
|
|
const metafile = JSON.parse(readFileSync(metafilePath, "utf-8"));
|
|
expect(metafile.outputs).toBeDefined();
|
|
// Find the output
|
|
const outputKey = Object.keys(metafile.outputs)[0];
|
|
const output = metafile.outputs[outputKey];
|
|
// Should have exports
|
|
expect(output.exports.length).toBeGreaterThanOrEqual(3); // foo, bar, baz
|
|
},
|
|
});
|
|
|
|
itBundled("metafile/NestedImports", {
|
|
files: {
|
|
"/entry.js": /* js */ `
|
|
import { a } from './a.js';
|
|
console.log(a);
|
|
`,
|
|
"/a.js": /* js */ `
|
|
import { b } from './b.js';
|
|
export const a = b + 1;
|
|
`,
|
|
"/b.js": /* js */ `
|
|
import { c } from './c.js';
|
|
export const b = c + 1;
|
|
`,
|
|
"/c.js": /* js */ `
|
|
export const c = 1;
|
|
`,
|
|
},
|
|
outdir: "/out",
|
|
metafile: "/metafile.json",
|
|
onAfterBundle(api) {
|
|
const metafilePath = api.join("metafile.json");
|
|
expect(existsSync(metafilePath)).toBe(true);
|
|
const metafile = JSON.parse(readFileSync(metafilePath, "utf-8"));
|
|
expect(metafile.inputs).toBeDefined();
|
|
// Should have 4 input files
|
|
expect(Object.keys(metafile.inputs).length).toBe(4);
|
|
// Each file should have proper imports
|
|
for (const [path, input] of Object.entries(metafile.inputs) as any) {
|
|
expect(typeof input.bytes).toBe("number");
|
|
expect(Array.isArray(input.imports)).toBe(true);
|
|
}
|
|
},
|
|
});
|
|
|
|
itBundled("metafile/JSONImport", {
|
|
files: {
|
|
"/entry.js": /* js */ `
|
|
import data from './data.json';
|
|
console.log(data);
|
|
`,
|
|
"/data.json": `{"key": "value", "number": 42}`,
|
|
},
|
|
outdir: "/out",
|
|
metafile: "/metafile.json",
|
|
onAfterBundle(api) {
|
|
const metafilePath = api.join("metafile.json");
|
|
expect(existsSync(metafilePath)).toBe(true);
|
|
const metafile = JSON.parse(readFileSync(metafilePath, "utf-8"));
|
|
// Find the entry file
|
|
const entryKey = Object.keys(metafile.inputs).find(k => k.includes("entry.js"));
|
|
expect(entryKey).toBeDefined();
|
|
const entry = metafile.inputs[entryKey!];
|
|
// Should have an import to the JSON file with 'with' clause
|
|
const jsonImport = entry.imports.find((imp: any) => imp.path.includes("data.json"));
|
|
expect(jsonImport).toBeDefined();
|
|
expect(jsonImport.with?.type).toBe("json");
|
|
},
|
|
});
|
|
|
|
itBundled("metafile/TextImport", {
|
|
files: {
|
|
"/entry.js": /* js */ `
|
|
import text from './file.txt';
|
|
console.log(text);
|
|
`,
|
|
"/file.txt": `Hello, World!`,
|
|
},
|
|
outdir: "/out",
|
|
metafile: "/metafile.json",
|
|
loader: {
|
|
".txt": "text",
|
|
},
|
|
onAfterBundle(api) {
|
|
const metafilePath = api.join("metafile.json");
|
|
expect(existsSync(metafilePath)).toBe(true);
|
|
const metafile = JSON.parse(readFileSync(metafilePath, "utf-8"));
|
|
// Find the entry file
|
|
const entryKey = Object.keys(metafile.inputs).find(k => k.includes("entry.js"));
|
|
expect(entryKey).toBeDefined();
|
|
const entry = metafile.inputs[entryKey!];
|
|
// Should have an import to the text file with 'with' clause
|
|
const textImport = entry.imports.find((imp: any) => imp.path.includes("file.txt"));
|
|
expect(textImport).toBeDefined();
|
|
expect(textImport.with?.type).toBe("text");
|
|
},
|
|
});
|
|
|
|
itBundled("metafile/EntryPoint", {
|
|
files: {
|
|
"/entry.js": /* js */ `
|
|
console.log('entry');
|
|
`,
|
|
},
|
|
outdir: "/out",
|
|
metafile: "/metafile.json",
|
|
onAfterBundle(api) {
|
|
const metafilePath = api.join("metafile.json");
|
|
expect(existsSync(metafilePath)).toBe(true);
|
|
const metafile = JSON.parse(readFileSync(metafilePath, "utf-8"));
|
|
expect(metafile.outputs).toBeDefined();
|
|
// Find an output with entryPoint
|
|
const outputWithEntryPoint = Object.values(metafile.outputs).find((o: any) => o.entryPoint);
|
|
expect(outputWithEntryPoint).toBeDefined();
|
|
expect(typeof (outputWithEntryPoint as any).entryPoint).toBe("string");
|
|
},
|
|
});
|
|
|
|
itBundled("metafile/OriginalPath", {
|
|
files: {
|
|
"/entry.js": /* js */ `
|
|
import { helper } from './lib/helper.js';
|
|
console.log(helper);
|
|
`,
|
|
"/lib/helper.js": /* js */ `
|
|
export const helper = 'helper';
|
|
`,
|
|
},
|
|
outdir: "/out",
|
|
metafile: "/metafile.json",
|
|
onAfterBundle(api) {
|
|
const metafilePath = api.join("metafile.json");
|
|
expect(existsSync(metafilePath)).toBe(true);
|
|
const metafile = JSON.parse(readFileSync(metafilePath, "utf-8"));
|
|
// Find the entry file
|
|
const entryKey = Object.keys(metafile.inputs).find(k => k.includes("entry.js"));
|
|
expect(entryKey).toBeDefined();
|
|
const entry = metafile.inputs[entryKey!];
|
|
// Should have an import with original path
|
|
expect(entry.imports.length).toBe(1);
|
|
expect(entry.imports[0].original).toBe("./lib/helper.js");
|
|
},
|
|
});
|
|
});
|