improved middlewares + added _layout support
This commit is contained in:
parent
b8107c8f04
commit
4cc9669468
|
|
@ -15,10 +15,6 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lightningcss": "^1.29.1",
|
"lightningcss": "^1.29.1",
|
||||||
"pokedex-promise-v2": "^4.2.1",
|
"serve-static-bun": "^0.5.3"
|
||||||
"serve-static-bun": "^0.5.3",
|
|
||||||
"tree-sitter-cli": "^0.25.2",
|
|
||||||
"tree-sitter-typescript": "^0.23.2",
|
|
||||||
"web-tree-sitter": "^0.25.2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,23 @@
|
||||||
import { myPlugin } from "./myPlugin";
|
import { myPlugin } from "./myPlugin";
|
||||||
import { fillTemplate, render } from "./lib/fast-web";
|
import {
|
||||||
|
fillTemplate,
|
||||||
|
render,
|
||||||
|
resolveLayoutPaths,
|
||||||
|
wrapLayouts,
|
||||||
|
} from "./lib/fast-web";
|
||||||
import { requestLog } from "./middlewares/request-log";
|
import { requestLog } from "./middlewares/request-log";
|
||||||
import template from "./template.html" with { type: "text" };
|
import template from "./template.html" with { type: "text" };
|
||||||
import theme from "./theme.css" with { type: "text" };
|
import theme from "./theme.css" with { type: "text" };
|
||||||
import staticDir from "serve-static-bun";
|
import staticDir from "serve-static-bun";
|
||||||
|
import path from "path";
|
||||||
|
import { withMiddlewares } from "./middlewares/with-middlewares";
|
||||||
|
import { gzip } from "./middlewares/gzip";
|
||||||
|
|
||||||
|
const pagesDir = __dirname + "/pages";
|
||||||
|
|
||||||
const router = new Bun.FileSystemRouter({
|
const router = new Bun.FileSystemRouter({
|
||||||
style: "nextjs",
|
style: "nextjs",
|
||||||
dir: __dirname + "/pages",
|
dir: pagesDir,
|
||||||
});
|
});
|
||||||
|
|
||||||
const staticPublic = staticDir("./public");
|
const staticPublic = staticDir("./public");
|
||||||
|
|
@ -16,16 +26,24 @@ const scriptsGlob = new Bun.Glob(__dirname + "/scripts/*");
|
||||||
|
|
||||||
const server = Bun.serve({
|
const server = Bun.serve({
|
||||||
port: 8080,
|
port: 8080,
|
||||||
fetch: requestLog(async (req): Promise<Response> => {
|
fetch: withMiddlewares(
|
||||||
|
async (req): Promise<Response> => {
|
||||||
if (req.url.endsWith("ws")) {
|
if (req.url.endsWith("ws")) {
|
||||||
if (server.upgrade(req)) return new Response("ok");
|
if (server.upgrade(req)) return new Response("ok");
|
||||||
}
|
}
|
||||||
|
|
||||||
const match = router.match(req);
|
const match = router.match(req);
|
||||||
if (!match) return staticPublic(req);
|
if (!match) return staticPublic(req);
|
||||||
const Comp = (await import(__dirname + "/pages/" + match?.pathname))
|
const layouts = await resolveLayoutPaths(
|
||||||
.default;
|
path.join(pagesDir, match.src),
|
||||||
const renderResult = await render(<Comp />, [theme], new URL(req.url));
|
pagesDir,
|
||||||
|
);
|
||||||
|
const Comp = (await import(path.join(pagesDir, match.src))).default;
|
||||||
|
const renderResult = await render(
|
||||||
|
await wrapLayouts(<Comp />, layouts),
|
||||||
|
[theme],
|
||||||
|
new URL(req.url),
|
||||||
|
);
|
||||||
|
|
||||||
const systemScripts = await Bun.build({
|
const systemScripts = await Bun.build({
|
||||||
entrypoints: [
|
entrypoints: [
|
||||||
|
|
@ -47,15 +65,16 @@ const server = Bun.serve({
|
||||||
body: renderResult.html,
|
body: renderResult.html,
|
||||||
});
|
});
|
||||||
|
|
||||||
const gzipResponseData = Bun.gzipSync(responseData);
|
return new Response(responseData, {
|
||||||
|
|
||||||
return new Response(gzipResponseData, {
|
|
||||||
headers: {
|
headers: {
|
||||||
"content-type": "text/html",
|
"content-type": "text/html",
|
||||||
"Content-Encoding": "gzip",
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
},
|
||||||
|
requestLog(),
|
||||||
|
gzip,
|
||||||
|
requestLog("gzip"),
|
||||||
|
),
|
||||||
websocket: {
|
websocket: {
|
||||||
message: () => {},
|
message: () => {},
|
||||||
open: async (ws) => {
|
open: async (ws) => {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { camelToKebab, renderStyle } from "~/system/style-rules";
|
||||||
import { transform } from "lightningcss";
|
import { transform } from "lightningcss";
|
||||||
import reset from "./reset.css" with { type: "text" };
|
import reset from "./reset.css" with { type: "text" };
|
||||||
import { AsyncLocalStorage } from "async_hooks";
|
import { AsyncLocalStorage } from "async_hooks";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
interface RenderContext {
|
interface RenderContext {
|
||||||
styles: string[];
|
styles: string[];
|
||||||
|
|
@ -122,3 +123,27 @@ export const fillTemplate = (
|
||||||
return acc.replace(`<!-- ${key} -->`, value);
|
return acc.replace(`<!-- ${key} -->`, value);
|
||||||
}, template);
|
}, template);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const resolveLayoutPaths = async (file: string, rootDir: string) => {
|
||||||
|
const layouts = [];
|
||||||
|
let dir = file;
|
||||||
|
console.log(path.normalize(dir));
|
||||||
|
console.log(path.normalize(rootDir));
|
||||||
|
do {
|
||||||
|
dir = path.join(dir, "..");
|
||||||
|
const layoutFile = path.join(dir, "_layout.tsx");
|
||||||
|
if (await Bun.file(layoutFile).exists()) {
|
||||||
|
layouts.push(layoutFile);
|
||||||
|
}
|
||||||
|
} while (path.normalize(dir) != path.normalize(rootDir));
|
||||||
|
return layouts;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const wrapLayouts = async (element: JSX.Element, layouts: string[]) => {
|
||||||
|
let wrapped = element;
|
||||||
|
for (const part of layouts) {
|
||||||
|
const Layout = (await import(part)).default;
|
||||||
|
wrapped = <Layout>{wrapped}</Layout>;
|
||||||
|
}
|
||||||
|
return wrapped;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
export const gzip = (handler: (req: Request) => Promise<Response>) => {
|
||||||
|
return async (req: Request) => {
|
||||||
|
const response = await handler(req);
|
||||||
|
const bytes = await response.clone().bytes();
|
||||||
|
const gzipped = Bun.gzipSync(bytes);
|
||||||
|
return new Response(gzipped, {
|
||||||
|
headers: { ...response.headers.toJSON(), "Content-Encoding": "gzip" },
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -11,15 +11,16 @@ const formatBytes = (bytes: number) => {
|
||||||
return bytes + "B";
|
return bytes + "B";
|
||||||
};
|
};
|
||||||
|
|
||||||
export const requestLog = (handler: (req: Request) => Promise<Response>) => {
|
export const requestLog =
|
||||||
|
(name?: string) => (handler: (req: Request) => Promise<Response>) => {
|
||||||
return async (req: Request) => {
|
return async (req: Request) => {
|
||||||
const start = performance.now();
|
const start = performance.now();
|
||||||
const response = await handler(req);
|
const response = await handler(req);
|
||||||
const bytes = await response.clone().bytes();
|
const bytes = await response.clone().bytes();
|
||||||
const delta = performance.now() - start;
|
const delta = performance.now() - start;
|
||||||
console.log(
|
console.log(
|
||||||
`[${req.method}] ${req.url} -> ${response.headers.get("Content-Type")} ${formatBytes(bytes.length)} in ${delta}ms`,
|
`[${req.method}] ${req.url}${name ? " " + name : ""} -> ${response.headers.get("Content-Type")} ${formatBytes(bytes.length)} in ${delta}ms`,
|
||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
type Handler = (req: Request) => Promise<Response>;
|
||||||
|
|
||||||
|
export const withMiddlewares = (
|
||||||
|
fetchFn: Handler,
|
||||||
|
...handlers: ((handler: Handler) => Handler)[]
|
||||||
|
) => {
|
||||||
|
return async (req: Request) => {
|
||||||
|
return handlers.reduce((acc, c) => {
|
||||||
|
return c(acc);
|
||||||
|
}, fetchFn)(req);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
import Layout from "../components/Layout";
|
||||||
|
|
||||||
|
export default Layout;
|
||||||
|
|
@ -12,7 +12,6 @@ export default async function Index() {
|
||||||
offset: page * 20,
|
offset: page * 20,
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<Layout>
|
|
||||||
<>
|
<>
|
||||||
<div>Hello World</div>
|
<div>Hello World</div>
|
||||||
<Counter />
|
<Counter />
|
||||||
|
|
@ -25,6 +24,5 @@ export default async function Index() {
|
||||||
<Link href={`/?page=${page - 1}`}>Prev</Link>
|
<Link href={`/?page=${page - 1}`}>Prev</Link>
|
||||||
<Link href={`/?page=${page + 1}`}>Next</Link>
|
<Link href={`/?page=${page + 1}`}>Next</Link>
|
||||||
</>
|
</>
|
||||||
</Layout>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue