From 121b256fc95bfe6dbda71c80fc30598b87faf651 Mon Sep 17 00:00:00 2001 From: Lee Cheneler Date: Mon, 18 Nov 2024 22:43:44 +0000 Subject: [PATCH] feat: preact pivot (#8) --- .npm/react-dom.ts | 2 - .npm/react-dom/server.ts | 2 - .npm/react.ts | 4 -- README.md | 81 ++++++++++++++++++++++++++++++---------- deno.json | 8 ++-- example-app.tsx | 2 +- src/context.ts | 9 +++-- 7 files changed, 70 insertions(+), 38 deletions(-) delete mode 100644 .npm/react-dom.ts delete mode 100644 .npm/react-dom/server.ts delete mode 100644 .npm/react.ts diff --git a/.npm/react-dom.ts b/.npm/react-dom.ts deleted file mode 100644 index bd89b16..0000000 --- a/.npm/react-dom.ts +++ /dev/null @@ -1,2 +0,0 @@ -// @deno-types="@types/react-dom" -export * from "react-dom"; diff --git a/.npm/react-dom/server.ts b/.npm/react-dom/server.ts deleted file mode 100644 index d499000..0000000 --- a/.npm/react-dom/server.ts +++ /dev/null @@ -1,2 +0,0 @@ -// @deno-types="@types/react-dom/server" -export * from "react-dom/server"; diff --git a/.npm/react.ts b/.npm/react.ts deleted file mode 100644 index 7c4d5f2..0000000 --- a/.npm/react.ts +++ /dev/null @@ -1,4 +0,0 @@ -// @deno-types="@types/react" -import React from "react"; - -export default React; diff --git a/README.md b/README.md index 8f8dcd7..619338b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Mage -Simple, composable APIs and web apps for Deno. +Build web applications with [Deno](https://deno.com) and +[Preact](https://preactjs.com). ## Installation @@ -41,7 +42,7 @@ app.get("/render", async (context) => { app.run({ port: 8000, onListen({ hostname, port }) { - console.log(`Listening on ${hostname}:${port}`); + console.log(`Listening on http://${hostname}:${port}`); }, }); ``` @@ -81,28 +82,63 @@ A collection of prebuilt middleware is available to use. ## Context -The context object is passed to each middleware and contains details about the -request and response. +A context object is passed to each middleware. + +### Url + +The URL is parsed and placed on the context. ```tsx -app.post("/", async (context) => { - console.log(context.url); - console.log(context.request.method - console.log(context.request.headers.get("Content-Type")); - console.log(await context.request.text()); -}); +context.url.pathname +context.url.searchParams +... +``` + +### Request + +The request object is available on the context. + +```tsx +context.request.method +context.request.headers.get("Content-Type") +await context.request.text() +... ``` -Additionally it contains utility functions to respond to the request. +### Response + +The response object is available on the context. + +```tsx +context.response.headers.set("Content-Type", "text/plain"); +context.response.headers.delete("Content-Type", "text/plain"); +``` + +### Response utilities + +A number of utility methods are available to configure the response content. + +#### `context.text(...)` + +Respond with text. ```tsx -// Text response context.text(StatusCode.OK, "Hello, World!"); +``` -// JSON response +#### `context.json(...)` + +Respond with JSON. + +```tsx context.json(StatusCode.OK, { message: "Hello, World!" }); +``` + +#### `context.render(...)` -// Render JSX to HTML response +Render JSX to HTML using [Preact](https://preactjs.com). + +```tsx await context.render( StatusCode.OK, @@ -111,19 +147,24 @@ await context.render( , ); +``` + +#### `context.empty(...)` + +Respond with an empty response, useful for response like `204 No Content`. +```tsx // Empty response context.empty(StatusCode.NoContent); - -// Redirect -context.redirect(RedirectType.Permanent, "/new-location"); ``` -You can also configure headers for the response: +#### `context.redirect(...)` + +Redirect the request to another location. ```tsx -context.response.headers.set("Content-Type", "text/plain"); -context.response.headers.delete("Content-Type", "text/plain"); +// Redirect +context.redirect(RedirectType.Permanent, "/new-location"); ``` ## Routing diff --git a/deno.json b/deno.json index ab2ecd0..d617d4f 100644 --- a/deno.json +++ b/deno.json @@ -14,13 +14,11 @@ "imports": { "@std/expect": "jsr:@std/expect@^1.0.8", "@std/testing": "jsr:@std/testing@^1.0.5", - "@types/react": "npm:@types/react@18.3.12", - "@types/react-dom": "npm:@types/react-dom@18.3.1", - "react": "npm:react@^18.3.1", - "react-dom": "npm:react-dom@^18.3.1" + "preact": "npm:preact@10.24.3", + "preact-render-to-string": "npm:preact-render-to-string@6.5.11" }, "compilerOptions": { "jsx": "react-jsx", - "jsxImportSource": "react" + "jsxImportSource": "preact" } } diff --git a/example-app.tsx b/example-app.tsx index 0f7fa18..323f3f0 100644 --- a/example-app.tsx +++ b/example-app.tsx @@ -26,6 +26,6 @@ app.get("/render", async (context) => { app.run({ port: 8000, onListen({ hostname, port }) { - console.log(`Listening on ${hostname}:${port}`); + console.log(`Listening on http://${hostname}:${port}`); }, }); diff --git a/src/context.ts b/src/context.ts index 1204b14..cb4987a 100644 --- a/src/context.ts +++ b/src/context.ts @@ -1,5 +1,5 @@ -import type React from "../.npm/react.ts"; -import { renderToReadableStream } from "../.npm/react-dom/server.ts"; +import type { VNode } from "preact"; +import { renderToStringAsync } from "preact-render-to-string"; import { RedirectType, StatusCode, statusTextMap } from "./http.ts"; import type { MageRouter } from "./router.ts"; @@ -68,8 +68,9 @@ export class MageContext { * @param status * @param body */ - public async render(status: StatusCode, body: React.ReactNode) { - this.response = new Response(await renderToReadableStream(body), { + public async render(status: StatusCode, body: VNode) { + const html = await renderToStringAsync(body); + this.response = new Response(`${html}`, { status: status, statusText: statusTextMap[status], headers: this.response.headers,