mirror of
https://github.com/oven-sh/bun
synced 2026-02-12 20:09:04 +00:00
406 lines
10 KiB
TypeScript
406 lines
10 KiB
TypeScript
import { FileSystemRouter } from "bun";
|
|
import { it, expect } from "bun:test";
|
|
import path, { dirname, resolve } from "path";
|
|
import fs, { mkdirSync, realpathSync, rmSync } from "fs";
|
|
import { tmpdir } from "os";
|
|
const tempdir = realpathSync(tmpdir()) + "/";
|
|
|
|
function createTree(basedir, paths) {
|
|
for (const end of paths) {
|
|
const abs = path.join(basedir, end);
|
|
try {
|
|
const dir = dirname(abs);
|
|
if (dir.length > 0 && dir !== "/") fs.mkdirSync(dir, { recursive: true });
|
|
} catch (e) {}
|
|
fs.writeFileSync(abs, "export default " + JSON.stringify(end) + ";\n");
|
|
}
|
|
}
|
|
var count = 0;
|
|
function make(files) {
|
|
const dir = tempdir + `fs-router-test-${count++}`;
|
|
rmSync(dir, {
|
|
recursive: true,
|
|
force: true,
|
|
});
|
|
|
|
createTree(dir, files);
|
|
if (files.length === 0) mkdirSync(dir, { recursive: true });
|
|
return {
|
|
dir,
|
|
};
|
|
}
|
|
|
|
it("should find files", () => {
|
|
const { dir } = make([
|
|
`index.tsx`,
|
|
`[id].tsx`,
|
|
`a.tsx`,
|
|
`abc/index.tsx`,
|
|
`abc/[id].tsx`,
|
|
`abc/def/[id].tsx`,
|
|
`abc/def/ghi/index.tsx`,
|
|
`abc/def/ghi/[id].tsx`,
|
|
`abc/def/ghi/jkl/index.tsx`,
|
|
`abc/def/ghi/jkl/[id].tsx`,
|
|
`abc/def/index.tsx`,
|
|
`b.tsx`,
|
|
`foo/[id].tsx`,
|
|
`catch-all/[[...id]].tsx`,
|
|
]);
|
|
|
|
const router = new FileSystemRouter({
|
|
dir,
|
|
fileExtensions: [".tsx"],
|
|
style: "nextjs",
|
|
});
|
|
|
|
const routes = router.routes;
|
|
const fixture = {
|
|
"/": `${dir}/index.tsx`,
|
|
"/[id]": `${dir}/[id].tsx`,
|
|
"/a": `${dir}/a.tsx`,
|
|
"/abc": `${dir}/abc/index.tsx`,
|
|
"/abc/[id]": `${dir}/abc/[id].tsx`,
|
|
"/abc/def/[id]": `${dir}/abc/def/[id].tsx`,
|
|
"/abc/def/ghi": `${dir}/abc/def/ghi/index.tsx`,
|
|
"/abc/def/ghi/[id]": `${dir}/abc/def/ghi/[id].tsx`,
|
|
"/abc/def/ghi/jkl": `${dir}/abc/def/ghi/jkl/index.tsx`,
|
|
"/abc/def/ghi/jkl/[id]": `${dir}/abc/def/ghi/jkl/[id].tsx`,
|
|
"/abc/def": `${dir}/abc/def/index.tsx`,
|
|
"/b": `${dir}/b.tsx`,
|
|
"/foo/[id]": `${dir}/foo/[id].tsx`,
|
|
"/catch-all/[[...id]]": `${dir}/catch-all/[[...id]].tsx`,
|
|
};
|
|
|
|
for (const route in fixture) {
|
|
if (!(route in routes)) {
|
|
throw new Error(`Route ${route} not found`);
|
|
}
|
|
|
|
expect(routes[route]).toBe(fixture[route]);
|
|
}
|
|
|
|
expect(Object.keys(routes).length).toBe(Object.keys(fixture).length);
|
|
expect(Object.values(routes).length).toBe(Object.values(fixture).length);
|
|
});
|
|
|
|
it("should handle empty dirs", () => {
|
|
const { dir } = make([]);
|
|
|
|
const router = new FileSystemRouter({
|
|
dir,
|
|
fileExtensions: [".tsx"],
|
|
style: "nextjs",
|
|
});
|
|
|
|
// assert this doesn't crash
|
|
expect(router.bar).toBeUndefined();
|
|
|
|
const routes = router.routes;
|
|
expect(Object.keys(routes).length).toBe(0);
|
|
expect(Object.values(routes).length).toBe(0);
|
|
});
|
|
|
|
it("should match dynamic routes", () => {
|
|
// set up the test
|
|
const { dir } = make(["index.tsx", "posts/[id].tsx", "posts.tsx"]);
|
|
|
|
const router = new Bun.FileSystemRouter({
|
|
dir,
|
|
style: "nextjs",
|
|
});
|
|
|
|
const { name, filePath } = router.match("/posts/hello-world");
|
|
|
|
expect(name).toBe("/posts/[id]");
|
|
expect(filePath).toBe(`${dir}/posts/[id].tsx`);
|
|
});
|
|
|
|
it(".params works on dynamic routes", () => {
|
|
// set up the test
|
|
const { dir } = make(["index.tsx", "posts/[id].tsx", "posts.tsx"]);
|
|
|
|
const router = new Bun.FileSystemRouter({
|
|
dir,
|
|
style: "nextjs",
|
|
});
|
|
|
|
const {
|
|
params: { id },
|
|
} = router.match("/posts/hello-world");
|
|
|
|
expect(id).toBe("hello-world");
|
|
});
|
|
|
|
it("should support static routes", () => {
|
|
// set up the test
|
|
const { dir } = make([
|
|
"index.tsx",
|
|
"posts/[id].tsx",
|
|
"posts.tsx",
|
|
"posts/hey.tsx",
|
|
]);
|
|
|
|
const router = new Bun.FileSystemRouter({
|
|
dir,
|
|
style: "nextjs",
|
|
});
|
|
|
|
const { name, params, filePath } = router.match("/posts/hey");
|
|
|
|
expect(name).toBe("/posts/hey");
|
|
expect(filePath).toBe(`${dir}/posts/hey.tsx`);
|
|
});
|
|
|
|
it("should support optional catch-all routes", () => {
|
|
// set up the test
|
|
const { dir } = make([
|
|
"index.tsx",
|
|
"posts/[id].tsx",
|
|
"posts.tsx",
|
|
"posts/hey.tsx",
|
|
"posts/[[...id]].tsx",
|
|
]);
|
|
|
|
const router = new Bun.FileSystemRouter({
|
|
dir,
|
|
style: "nextjs",
|
|
});
|
|
|
|
for (let fixture of [
|
|
"/posts/123",
|
|
"/posts/hey",
|
|
"/posts/zorp",
|
|
"/posts",
|
|
"/index",
|
|
"/posts/",
|
|
]) {
|
|
expect(router.match(fixture)?.name).not.toBe("/posts/[[...id]]");
|
|
}
|
|
|
|
for (let fixture of [
|
|
"/posts/hey/there",
|
|
"/posts/hey/there/you",
|
|
"/posts/zorp/123",
|
|
]) {
|
|
const { name, params, filePath } = router.match(fixture);
|
|
|
|
expect(name).toBe("/posts/[[...id]]");
|
|
expect(filePath).toBe(`${dir}/posts/[[...id]].tsx`);
|
|
expect(params.id).toBe(fixture.split("/").slice(2).join("/"));
|
|
}
|
|
});
|
|
|
|
it("should support catch-all routes", () => {
|
|
// set up the test
|
|
const { dir } = make([
|
|
"index.tsx",
|
|
"posts/[id].tsx",
|
|
"posts.tsx",
|
|
"posts/hey.tsx",
|
|
"posts/[...id].tsx",
|
|
"posts/wow/[[...id]].tsx",
|
|
]);
|
|
|
|
const router = new Bun.FileSystemRouter({
|
|
dir,
|
|
style: "nextjs",
|
|
});
|
|
|
|
for (let fixture of [
|
|
"/posts/123",
|
|
"/posts/hey",
|
|
"/posts/zorp",
|
|
"/posts",
|
|
"/index",
|
|
"/posts/",
|
|
]) {
|
|
expect(router.match(fixture)?.name).not.toBe("/posts/[...id]");
|
|
}
|
|
|
|
for (let fixture of [
|
|
"/posts/hey/there",
|
|
"/posts/hey/there/you",
|
|
"/posts/zorp/123",
|
|
"/posts/wow/hey/there",
|
|
]) {
|
|
const { name, params, filePath } = router.match(fixture);
|
|
|
|
expect(name).toBe("/posts/[...id]");
|
|
expect(filePath).toBe(`${dir}/posts/[...id].tsx`);
|
|
expect(params.id).toBe(fixture.split("/").slice(2).join("/"));
|
|
}
|
|
});
|
|
|
|
it("should support index routes", () => {
|
|
// set up the test
|
|
const { dir } = make([
|
|
"index.tsx",
|
|
"posts/[id].tsx",
|
|
"posts.tsx",
|
|
"posts/hey.tsx",
|
|
]);
|
|
|
|
const router = new Bun.FileSystemRouter({
|
|
dir,
|
|
style: "nextjs",
|
|
});
|
|
|
|
for (let route of ["/", "/index"]) {
|
|
const { name, params, filePath } = router.match(route);
|
|
|
|
expect(name).toBe("/");
|
|
expect(filePath).toBe(`${dir}/index.tsx`);
|
|
expect(Object.keys(params).length).toBe(0);
|
|
}
|
|
|
|
for (let route of ["/posts", "/posts/index", "/posts/"]) {
|
|
const { name, params, filePath } = router.match(route);
|
|
|
|
expect(name).toBe("/posts");
|
|
expect(filePath).toBe(`${dir}/posts.tsx`);
|
|
expect(Object.keys(params).length).toBe(0);
|
|
}
|
|
});
|
|
|
|
it("should support Request", async () => {
|
|
// set up the test
|
|
const { dir } = make(["index.tsx", "posts/[id].tsx", "posts.tsx"]);
|
|
|
|
const router = new Bun.FileSystemRouter({
|
|
dir,
|
|
style: "nextjs",
|
|
});
|
|
|
|
for (let current of [
|
|
new Request({ url: "/posts/hello-world" }),
|
|
new Request({ url: "http://example.com/posts/hello-world" }),
|
|
]) {
|
|
const {
|
|
name,
|
|
params: { id },
|
|
filePath,
|
|
} = router.match(current);
|
|
expect(name).toBe("/posts/[id]");
|
|
expect(filePath).toBe(`${dir}/posts/[id].tsx`);
|
|
expect(id).toBe("hello-world");
|
|
}
|
|
});
|
|
|
|
it("assetPrefix, src, and origin", async () => {
|
|
// set up the test
|
|
const { dir } = make(["index.tsx", "posts/[id].tsx", "posts.tsx"]);
|
|
|
|
const router = new Bun.FileSystemRouter({
|
|
dir,
|
|
style: "nextjs",
|
|
assetPrefix: "/_next/static/",
|
|
origin: "https://nextjs.org",
|
|
});
|
|
|
|
for (let current of [
|
|
// Reuqest
|
|
new Request({ url: "/posts/hello-world" }),
|
|
new Request({ url: "https://nextjs.org/posts/hello-world" }),
|
|
]) {
|
|
const { name, src, filePath, checkThisDoesntCrash } = router.match(current);
|
|
expect(name).toBe("/posts/[id]");
|
|
|
|
// check nothing is weird on the MatchedRoute object
|
|
expect(checkThisDoesntCrash).toBeUndefined();
|
|
|
|
expect(src).toBe("https://nextjs.org/_next/static/posts/[id].tsx");
|
|
expect(filePath).toBe(`${dir}/posts/[id].tsx`);
|
|
}
|
|
});
|
|
|
|
it(".query works", () => {
|
|
// set up the test
|
|
const { dir } = make(["posts.tsx"]);
|
|
|
|
const router = new Bun.FileSystemRouter({
|
|
dir,
|
|
style: "nextjs",
|
|
assetPrefix: "/_next/static/",
|
|
origin: "https://nextjs.org",
|
|
});
|
|
|
|
for (let [current, object] of [
|
|
[new URL("https://example.com/posts?hello=world").href, { hello: "world" }],
|
|
[
|
|
new URL("https://example.com/posts?hello=world&second=2").href,
|
|
{ hello: "world", second: "2" },
|
|
],
|
|
[
|
|
new URL("https://example.com/posts?hello=world&second=2&third=3").href,
|
|
{ hello: "world", second: "2", third: "3" },
|
|
],
|
|
[new URL("https://example.com/posts").href, {}],
|
|
]) {
|
|
const { name, src, filePath, checkThisDoesntCrash, query } =
|
|
router.match(current);
|
|
expect(name).toBe("/posts");
|
|
|
|
// check nothing is weird on the MatchedRoute object
|
|
expect(checkThisDoesntCrash).toBeUndefined();
|
|
|
|
expect(JSON.stringify(query)).toBe(JSON.stringify(object));
|
|
expect(filePath).toBe(`${dir}/posts.tsx`);
|
|
}
|
|
});
|
|
|
|
it("reload() works", () => {
|
|
// set up the test
|
|
const { dir } = make(["posts.tsx"]);
|
|
|
|
const router = new Bun.FileSystemRouter({
|
|
dir,
|
|
style: "nextjs",
|
|
assetPrefix: "/_next/static/",
|
|
origin: "https://nextjs.org",
|
|
});
|
|
|
|
expect(router.match("/posts").name).toBe("/posts");
|
|
router.reload();
|
|
expect(router.match("/posts").name).toBe("/posts");
|
|
});
|
|
|
|
it(".query works with dynamic routes, including params", () => {
|
|
// set up the test
|
|
const { dir } = make(["posts/[id].tsx"]);
|
|
|
|
const router = new Bun.FileSystemRouter({
|
|
dir,
|
|
style: "nextjs",
|
|
assetPrefix: "/_next/static/",
|
|
origin: "https://nextjs.org",
|
|
});
|
|
|
|
for (let [current, object] of [
|
|
[
|
|
new URL("https://example.com/posts/123?hello=world").href,
|
|
{ id: "123", hello: "world" },
|
|
],
|
|
[
|
|
new URL("https://example.com/posts/123?hello=world&second=2").href,
|
|
{ id: "123", hello: "world", second: "2" },
|
|
],
|
|
[
|
|
new URL("https://example.com/posts/123?hello=world&second=2&third=3")
|
|
.href,
|
|
{ id: "123", hello: "world", second: "2", third: "3" },
|
|
],
|
|
[new URL("https://example.com/posts/123").href, { id: "123" }],
|
|
]) {
|
|
const { name, src, filePath, checkThisDoesntCrash, query } =
|
|
router.match(current);
|
|
expect(name).toBe("/posts/[id]");
|
|
|
|
// check nothing is weird on the MatchedRoute object
|
|
expect(checkThisDoesntCrash).toBeUndefined();
|
|
|
|
expect(JSON.stringify(query)).toBe(JSON.stringify(object));
|
|
expect(filePath).toBe(`${dir}/posts/[id].tsx`);
|
|
}
|
|
});
|