mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 02:48:50 +00:00
* Add bun-types to packages * Improve typing * Fix types in tests * Fix dts tests * Run formatter * Fix all type errors * Add strict mode, fix type errors * Add ffi changes * Move workflows to root * Add workflows * Remove labeler * Add child_process types * Fix synthetic defaults issue * Remove docs * Move scripts * Run prettier * Include examples in typechecking * captureStackTrace types * moved captureStackTrace types to globals * Address reviews Co-authored-by: Colin McDonnell <colinmcd@alum.mit.edu> Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
334 lines
8.5 KiB
TypeScript
334 lines
8.5 KiB
TypeScript
/// <reference types="./plugins" />
|
|
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 `<h1>Hello world!</h1>` with Svelte", () => {
|
|
const { default: App } = require("./hello.svelte");
|
|
const { html } = App.render();
|
|
|
|
expect(html).toBe("<h1>Hello world!</h1>");
|
|
});
|
|
|
|
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 `<h1>Hello world!</h1>` with Svelte", async () => {
|
|
const { default: App }: any = await import("./hello.svelte");
|
|
|
|
const { html } = App.render();
|
|
|
|
expect(html).toBe("<h1>Hello world!</h1>");
|
|
});
|
|
|
|
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 `<h1>Hello world!</h1>` 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("<h1>Hello world!</h1>");
|
|
});
|
|
});
|
|
|
|
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; }",
|
|
"<h1>hi</h1>",
|
|
'{"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);
|
|
}
|
|
});
|
|
});
|