diff --git a/example/preactSSR/handlers/github.handler.ts b/example/preactSSR/handlers/github.handler.ts new file mode 100644 index 00000000..2a20ca47 --- /dev/null +++ b/example/preactSSR/handlers/github.handler.ts @@ -0,0 +1,24 @@ +import { Handler, file } from "../../../mod.ts"; + +const base = `https://raw.githubusercontent.com/sejori/peko/main/example/preactSSR`; + +export const githubHandler = + (path: string, type?: string): Handler => + async (ctx) => + ( + await file( + ctx.state.env.ENVIRONMENT === "production" + ? new URL(`${base}${path}`) + : new URL("../" + path, import.meta.url), + { + headers: new Headers({ + ...(type && { "Content-Type": type }), + // instruct browser to cache file in prod env + "Cache-Control": + ctx.state.env.ENVIRONMENT === "production" + ? "max-age=86400, stale-while-revalidate=86400" + : "no-store", + }), + } + ) + )(ctx); diff --git a/example/preactSSR/handlers/parrot.handler.ts b/example/preactSSR/handlers/parrot.handler.ts new file mode 100644 index 00000000..54413799 --- /dev/null +++ b/example/preactSSR/handlers/parrot.handler.ts @@ -0,0 +1,24 @@ +import { Handler } from "../../../mod.ts"; + +export const parrotHandler: Handler = async (ctx) => { + const reader = ctx.request.body?.getReader(); + let result = ""; + const chunkSize = 1024; + + if (reader) { + let done = false; + while (!done) { + const { value, done: chunkDone } = await reader.read(); + if (value) { + result += new TextDecoder().decode(value); + if (result.length >= chunkSize) { + result = result.slice(0, chunkSize); + break; + } + } + done = chunkDone; + } + } + + return new Response(`Parrot sqwarks: ${result}`); +}; diff --git a/example/preactSSR/handlers/preact.handler.ts b/example/preactSSR/handlers/preact.handler.ts new file mode 100644 index 00000000..72f226b2 --- /dev/null +++ b/example/preactSSR/handlers/preact.handler.ts @@ -0,0 +1,32 @@ +import { renderToString } from "preact-render-to-string"; +import { Handler, ssr } from "../../../mod.ts"; +import htmlTemplate from "../src/document.ts"; + +export const preactHandler = + ( + component: (props: Record) => unknown, + title: string, + entrypoint: string + ): Handler => + (ctx) => { + return ssr( + () => { + const ssrHTML = renderToString(component(ctx.state), null, null); + return htmlTemplate({ + title, + ssrHTML, + entrypoint, + serverState: ctx.state, + }); + }, + { + headers: new Headers({ + // instruct browser to cache page in prod env + "Cache-Control": + ctx.state.env.ENVIRONMENT === "production" + ? "max-age=86400, stale-while-revalidate=86400" + : "no-store", + }), + } + )(ctx); + }; diff --git a/example/preactSSR/handlers/rdmEvent.handler.ts b/example/preactSSR/handlers/rdmEvent.handler.ts new file mode 100644 index 00000000..7aab8fc2 --- /dev/null +++ b/example/preactSSR/handlers/rdmEvent.handler.ts @@ -0,0 +1,12 @@ +import { Handler, sse } from "../../../mod.ts"; + +const demoEventTarget = new EventTarget(); +export const randomEventHandler: Handler = (ctx) => { + setInterval(() => { + demoEventTarget.dispatchEvent( + new CustomEvent("send", { detail: Math.random() }) + ); + }, 2500); + + return sse(demoEventTarget)(ctx); +}; diff --git a/example/preactSSR/middleware/reqTime.middleware.ts b/example/preactSSR/middleware/reqTime.middleware.ts new file mode 100644 index 00000000..cef4f89a --- /dev/null +++ b/example/preactSSR/middleware/reqTime.middleware.ts @@ -0,0 +1,9 @@ +import { Middleware } from "../../../mod.ts"; + +export const reqTime: Middleware = (ctx) => { + ctx.state = { + env: ctx.state.env, + request_time: `${Date.now()}`, + DENO_REGION: ctx.state.env.DENO_REGION, + }; +}; diff --git a/example/preactSSR/router.ts b/example/preactSSR/router.ts index 30c8a3b8..2661bc88 100644 --- a/example/preactSSR/router.ts +++ b/example/preactSSR/router.ts @@ -1,137 +1,42 @@ -import { - Router, - RequestContext, - logger, - sse, - cacher, - ssr, - file, -} from "../../mod.ts"; //"https://deno.land/x/peko/mod.ts" -import { renderToString } from "preact-render-to-string"; - -import Home from "./src/pages/Home.ts"; +import { Router, logger, cacher } from "../../mod.ts"; //"https://deno.land/x/peko/mod.ts" +import { preactHandler } from "./handlers/preact.handler.ts"; +import { githubHandler } from "./handlers/github.handler.ts"; +import { reqTime } from "./middleware/reqTime.middleware.ts"; +import { randomEventHandler } from "./handlers/rdmEvent.handler.ts"; +import { parrotHandler } from "./handlers/parrot.handler.ts"; import About from "./src/pages/About.ts"; -import htmlTemplate from "./document.ts"; +import Home from "./src/pages/Home.ts"; const router = new Router(); router.use(logger(console.log)); -// SSR example, with cache bc static page -router.get("/", { - middleware: cacher(), - handler: (ctx) => { - return ssr( - () => { - const ssrHTML = renderToString(Home(), null, null); - return htmlTemplate({ - title: "Peko", - ssrHTML, - entrypoint: "/src/pages/Home.ts", - }); - }, - { - headers: new Headers({ - // instruct browser to cache page in prod env - "Cache-Control": - ctx.state.env.ENVIRONMENT === "production" - ? "max-age=86400, stale-while-revalidate=86400" - : "no-store", - }), - } - )(ctx); - }, -}); +// SSR, with cache because static page +router.get("/", cacher(), preactHandler(Home, "Peko", "/src/pages/Home.ts")); -// SSR example, no cache because dynamic content -// (About page renders server state into HTML) +// SSR, no cache because dynamic content router.get( "/about", - (ctx) => { - ctx.state = { - request_time: `${Date.now()}`, - ...ctx.state.env, - }; - }, - ssr((ctx) => { - const ssrHTML = renderToString(About(ctx.state), null, null); - return htmlTemplate({ - title: "Peko | About", - ssrHTML, - entrypoint: "/src/pages/About.ts", - serverState: ctx.state, - }); - }) + reqTime, + preactHandler(About, "Peko | About", "/src/pages/About.ts") ); -// Static file example -// dynamic URL param for filename, always cache -router.get("/assets/:filename", cacher(), async (ctx) => - ( - await file( - new URL( - `https://raw.githubusercontent.com/sejori/peko/main/example/preactSSR/assets/${ctx.params.filename}` - ), - { - headers: new Headers({ - // instruct browser to cache file in prod env - "Cache-Control": - ctx.state.env.ENVIRONMENT === "production" - ? "max-age=86400, stale-while-revalidate=86400" - : "no-store", - }), - } - ) - )(ctx) +// Static, URL param for filename, always cache +router.get("/assets/:filename", cacher(), (ctx) => + githubHandler(`/assets/${ctx.params.filename}`)(ctx) ); -// Return transformed source code example -// dynamic URL param for filename, always cache, transformed with esbuild at build time -router.get("/src/:dirname/:filename", cacher(), async (ctx) => { - const transformedFilename = ctx.params.filename.replace(".ts", ".js"); - return ( - await file( - ctx.state.env.ENVIRONMENT === "production" - ? new URL( - `https://raw.githubusercontent.com/sejori/peko/main/example/preactSSR/dist/${ctx.params.dirname}/${transformedFilename}` - ) - : new URL( - `./dist/${ctx.params.dirname}/${transformedFilename}`, - import.meta.url - ), - { - headers: new Headers({ - "Content-Type": "application/javascript", - // instruct browser to cache file in prod env - "Cache-Control": - ctx.state.env.ENVIRONMENT === "production" - ? "max-age=86400, stale-while-revalidate=86400" - : "no-store", - }), - } - ) - )(ctx); -}); - -// Server-sent events example -const demoEventTarget = new EventTarget(); -router.get("/sse", (ctx: RequestContext) => { - setInterval(() => { - demoEventTarget.dispatchEvent( - new CustomEvent("send", { detail: Math.random() }) - ); - }, 2500); +// Transformed src at build-time with esbuild, always cache +router.get("/src/:dirname/:filename", cacher(), (ctx) => + githubHandler( + `/dist/${ctx.params.dirname}/${ctx.params.filename.replace(".ts", ".js")}`, + "application/javascript" + )(ctx) +); - return sse(demoEventTarget)(ctx); -}); +// Server-sent events demo +router.get("/sse", randomEventHandler); -// .addRoute example -router.addRoute({ - path: "/api/parrot", - method: "POST", - handler: async (ctx: RequestContext) => { - const body = await ctx.request.text(); - return new Response(`Parrot sqwarks: ${body}`); - }, -}); +// Basic +router.post("/parrot", parrotHandler); export default router; diff --git a/example/preactSSR/document.ts b/example/preactSSR/src/document.ts similarity index 100% rename from example/preactSSR/document.ts rename to example/preactSSR/src/document.ts