Files
bun.sh/test/bundler/esbuild/metafile.test.ts

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");
},
});
});