///
import { plugin } from "bun";
import { describe, expect, it } from "bun:test";
import { resolve } from "path";
plugin({
name: "boop beep beep",
setup(builder) {
builder.onResolve({ filter: /boop/, namespace: "beep" }, () => ({
path: "boop",
namespace: "beep",
}));
builder.onLoad({ filter: /boop/, namespace: "beep" }, () => ({
contents: `export default 42;`,
loader: "js",
}));
},
});
plugin({
name: "an object module",
setup(builder) {
globalThis.objectModuleResult ||= {
hello: "world",
};
builder.onResolve({ filter: /.*/, namespace: "obj" }, ({ path }) => ({
path,
namespace: "obj",
}));
builder.onLoad({ filter: /.*/, namespace: "obj" }, () => ({
exports: globalThis.objectModuleResult,
loader: "object",
}));
},
});
plugin({
name: "failing loader",
setup(builder) {
globalThis.failingObject ||= {};
builder.onResolve({ filter: /.*/, namespace: "fail" }, ({ path }) => ({
path,
namespace: "fail",
}));
builder.onLoad({ filter: /.*/, namespace: "fail" }, () => globalThis.failingObject);
},
});
plugin({
name: "delayed loader",
setup(builder) {
globalThis.laterCode = "";
builder.onResolve({ filter: /.*/, namespace: "delay" }, ({ path }) => ({
namespace: "delay",
path,
}));
builder.onLoad({ filter: /.*/, namespace: "delay" }, ({ path }) => ({
contents: (globalThis.laterCode ||= ""),
loader: "js",
}));
},
});
plugin({
name: "async onLoad",
setup(builder) {
globalThis.asyncOnLoad = "";
builder.onResolve({ filter: /.*/, namespace: "async" }, ({ path }) => ({
namespace: "async",
path,
}));
builder.onLoad({ filter: /.*/, namespace: "async" }, async ({ path }) => {
await Promise.resolve(1);
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
contents: (globalThis.asyncOnLoad ||= ""),
loader: "js",
});
}, 1);
});
});
builder.onResolve({ filter: /.*/, namespace: "async-obj" }, ({ path }) => ({
namespace: "async-obj",
path,
}));
globalThis.asyncObject = {};
builder.onLoad({ filter: /.*/, namespace: "async-obj" }, async ({ path }) => {
await Promise.resolve(1);
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
exports: (globalThis.asyncObject ||= {}),
loader: "object",
});
}, 1);
});
});
builder.onResolve({ filter: /.*/, namespace: "asyncfail" }, ({ path }) => ({
namespace: "asyncfail",
path,
}));
globalThis.asyncfail = false;
builder.onLoad({ filter: /.*/, namespace: "asyncfail" }, async ({ path }) => {
await Promise.resolve(1);
await 1;
throw globalThis.asyncfail;
});
builder.onResolve({ filter: /.*/, namespace: "asyncret" }, ({ path }) => ({
namespace: "asyncret",
path,
}));
globalThis.asyncret = 123;
builder.onLoad({ filter: /.*/, namespace: "asyncret" }, async ({ path }) => {
await 100;
await Promise.resolve(10);
return await globalThis.asyncret;
});
},
});
// This is to test that it works when imported from a separate file
import "bun-loader-svelte";
describe("require", () => {
it("SSRs `
Hello world!
` with Svelte", () => {
const { default: App } = require("./hello.svelte");
const { html } = App.render();
expect(html).toBe("Hello world!
");
});
it("beep:boop returns 42", () => {
const result = require("beep:boop");
expect(result.default).toBe(42);
});
it("object module works", () => {
const result = require("obj:boop");
expect(result.hello).toBe(objectModuleResult.hello);
objectModuleResult.there = true;
const result2 = require("obj:boop2");
expect(result.there).toBe(undefined);
expect(result2.there).toBe(objectModuleResult.there);
expect(result2.there).toBe(true);
});
});
describe("dynamic import", () => {
it("SSRs `Hello world!
` with Svelte", async () => {
const { default: App }: any = await import("./hello.svelte");
const { html } = App.render();
expect(html).toBe("Hello world!
");
});
it("beep:boop returns 42", async () => {
const result = await import("beep:boop");
expect(result.default).toBe(42);
});
it("async:onLoad returns 42", async () => {
globalThis.asyncOnLoad = "export default 42;";
const result = await import("async:hello42");
expect(result.default).toBe(42);
});
it("async object loader returns 42", async () => {
globalThis.asyncObject = { foo: 42, default: 43 };
const result = await import("async-obj:hello42");
expect(result.foo).toBe(42);
expect(result.default).toBe(43);
});
});
describe("import statement", () => {
it("SSRs `Hello world!
` with Svelte", async () => {
laterCode = `
import Hello from "${resolve(import.meta.dir, "hello2.svelte")}";
export default Hello;
`;
const { default: SvelteApp } = await import("delay:hello2.svelte");
const { html } = SvelteApp.render();
expect(html).toBe("Hello world!
");
});
});
describe("errors", () => {
it("valid loaders work", () => {
const validLoaders = ["js", "jsx", "ts", "tsx"];
const inputs = ["export default 'hi';", "export default 'hi';", "export default 'hi';", "export default 'hi';"];
for (let i = 0; i < validLoaders.length; i++) {
const loader = validLoaders[i];
const input = inputs[i];
globalThis.failingObject = { contents: input, loader };
expect(require(`fail:my-file-${loader}`).default).toBe("hi");
}
});
it("invalid loaders throw", () => {
const invalidLoaders = ["blah", "blah2", "blah3", "blah4"];
const inputs = ["body { background: red; }", "hi
", '{"hi": "there"}', "hi"];
for (let i = 0; i < invalidLoaders.length; i++) {
const loader = invalidLoaders[i];
const input = inputs[i];
globalThis.failingObject = { contents: input, loader };
try {
require(`fail:my-file-${loader}`);
throw -1;
} catch (e: any) {
if (e === -1) {
throw new Error("Expected error");
}
expect(e.message.length > 0).toBe(true);
}
}
});
it("transpiler errors work", () => {
const invalidLoaders = ["ts"];
const inputs = ["const x: string = -NaNAn../!!;"];
for (let i = 0; i < invalidLoaders.length; i++) {
const loader = invalidLoaders[i];
const input = inputs[i];
globalThis.failingObject = { contents: input, loader };
try {
require(`fail:my-file-${loader}-3`);
throw -1;
} catch (e: any) {
if (e === -1) {
throw new Error("Expected error");
}
expect(e.message.length > 0).toBe(true);
}
}
});
it("invalid async return value", async () => {
try {
globalThis.asyncret = { wat: true };
await import("asyncret:my-file");
throw -1;
} catch (e: any) {
if (e === -1) {
throw new Error("Expected error");
}
expect(e.message.length > 0).toBe(true);
}
});
it("async errors work", async () => {
try {
globalThis.asyncfail = new Error("async error");
await import("asyncfail:my-file");
throw -1;
} catch (e: any) {
if (e === -1) {
throw new Error("Expected error");
}
expect(e.message.length > 0).toBe(true);
}
});
it("invalid onLoad objects throw", () => {
const invalidOnLoadObjects = [
{},
{ contents: -1 },
{ contents: "", loader: -1 },
{ contents: "", loader: "klz", resolveDir: -1 },
];
for (let i = 0; i < invalidOnLoadObjects.length; i++) {
globalThis.failingObject = invalidOnLoadObjects[i];
try {
require(`fail:my-file-${i}-2`);
throw -1;
} catch (e: any) {
if (e === -1) {
throw new Error("Expected error");
}
expect(e.message.length > 0).toBe(true);
}
}
});
it("async transpiler errors work", async () => {
try {
globalThis.asyncOnLoad = `const x: string = -NaNAn../!!;`;
await import("async:fail");
throw -1;
} catch (e: any) {
if (e === -1) {
throw new Error("Expected error");
}
expect(e.message.length > 0).toBe(true);
}
});
});