From aa4e71113392d4838650168370ee702b932e1206 Mon Sep 17 00:00:00 2001 From: rossrobino Date: Mon, 9 Sep 2024 14:28:02 -0400 Subject: [PATCH] createApp middleware modification --- .changeset/twenty-zebras-watch.md | 8 ++ apps/docs/src/+server.tsx | 3 + apps/docs/src/components/Layout.tsx | 2 +- apps/docs/src/content/deploy.md | 4 +- apps/docs/src/generated/globals.md | 119 ++++++++++++++---- apps/docs/src/lib/docs/+client.ts | 2 - packages/create-domco/src/template/index.ts | 3 +- packages/domco/src/adapter/vercel/index.ts | 46 ++++--- packages/domco/src/app/dev/index.ts | 25 ++-- packages/domco/src/app/index.ts | 23 ++-- packages/domco/src/app/util/index.ts | 14 ++- .../src/plugin/configure-server/index.ts | 7 +- packages/domco/src/types/public/index.ts | 31 ++++- 13 files changed, 201 insertions(+), 86 deletions(-) create mode 100644 .changeset/twenty-zebras-watch.md diff --git a/.changeset/twenty-zebras-watch.md b/.changeset/twenty-zebras-watch.md new file mode 100644 index 0000000..cece18e --- /dev/null +++ b/.changeset/twenty-zebras-watch.md @@ -0,0 +1,8 @@ +--- +"domco": minor +"create-domco": patch +--- + +Adds the ability paths to `CreateAppMiddleware`. + +- This is breaking if you are using a custom setup and passing middleware into `createApp`, you now need to specify the `path` in addition to the `handler` passed in. [See example here](https://domco.robino.dev/deploy#example) diff --git a/apps/docs/src/+server.tsx b/apps/docs/src/+server.tsx index 393086b..b682c31 100644 --- a/apps/docs/src/+server.tsx +++ b/apps/docs/src/+server.tsx @@ -6,12 +6,15 @@ import apiReference from "@/generated/globals.md?raw"; import { processMarkdown } from "@robino/md"; import type { Prerender } from "domco"; import { Hono } from "hono"; +import { etag } from "hono/etag"; import { raw } from "hono/html"; export const prerender: Prerender = ["/", "/api-reference"]; const app = new Hono(); +app.use(etag()); + app.use(async (c, next) => { c.setRenderer(({ title, client }, content) => { const tags = [c.var.client()]; diff --git a/apps/docs/src/components/Layout.tsx b/apps/docs/src/components/Layout.tsx index df43e67..56a4935 100644 --- a/apps/docs/src/components/Layout.tsx +++ b/apps/docs/src/components/Layout.tsx @@ -36,7 +36,7 @@ export const Layout: FC = ({ title, children, client }) => {
diff --git a/apps/docs/src/content/deploy.md b/apps/docs/src/content/deploy.md index 0621f14..80e87fb 100644 --- a/apps/docs/src/content/deploy.md +++ b/apps/docs/src/content/deploy.md @@ -30,7 +30,9 @@ import { createApp } from "./dist/server/app.js"; import { serve } from "@hono/node-server"; import { serveStatic } from "@hono/node-server/serve-static"; -const app = createApp({ middleware: [serveStatic({ root: "./dist/client" })] }); +const app = createApp({ + middleware: [{ path: "/*", handler: serveStatic({ root: "./dist/client" }) }], +}); serve(app); ``` diff --git a/apps/docs/src/generated/globals.md b/apps/docs/src/generated/globals.md index 4bc8f44..68b6c06 100644 --- a/apps/docs/src/generated/globals.md +++ b/apps/docs/src/generated/globals.md @@ -12,7 +12,7 @@ ##### devMiddleware? -> `optional` **devMiddleware**: `MiddlewareHandler`[] +> `optional` **devMiddleware**: [`CreateAppOptions`](globals.md#createappoptions)\[`"middleware"`\] Middleware to apply in `dev` mode. For production middleware, export it from the adapter module, @@ -46,7 +46,7 @@ The name of the adapter. ##### previewMiddleware? -> `optional` **previewMiddleware**: `MiddlewareHandler`[] +> `optional` **previewMiddleware**: [`CreateAppOptions`](globals.md#createappoptions)\[`"middleware"`\] Middleware to apply in `preview` mode. For production middleware, export it from the adapter module, @@ -74,9 +74,9 @@ Target for SSR build. #### Defined in -[types/public/index.ts:21](https://github.com/rossrobino/domco/blob/901651aa5239610bc8c1558f2e4c7e072bc52ee6/packages/domco/src/types/public/index.ts#L21) +[types/public/index.ts:44](https://github.com/rossrobino/domco/blob/fde430c2d32883df987ac54237edd49a27dfdd7e/packages/domco/src/types/public/index.ts#L44) -*** +--- @@ -98,9 +98,9 @@ Target for SSR build. #### Defined in -[types/public/index.ts:60](https://github.com/rossrobino/domco/blob/901651aa5239610bc8c1558f2e4c7e072bc52ee6/packages/domco/src/types/public/index.ts#L60) +[types/public/index.ts:83](https://github.com/rossrobino/domco/blob/fde430c2d32883df987ac54237edd49a27dfdd7e/packages/domco/src/types/public/index.ts#L83) -*** +--- @@ -135,14 +135,14 @@ The name of the entrypoint without extension. ###### Example ```ts -"main" +"main"; ``` #### Defined in -[types/public/index.ts:6](https://github.com/rossrobino/domco/blob/901651aa5239610bc8c1558f2e4c7e072bc52ee6/packages/domco/src/types/public/index.ts#L6) +[types/public/index.ts:29](https://github.com/rossrobino/domco/blob/fde430c2d32883df987ac54237edd49a27dfdd7e/packages/domco/src/types/public/index.ts#L29) -*** +--- @@ -160,9 +160,78 @@ The name of the entrypoint without extension. #### Defined in -[types/public/index.ts:123](https://github.com/rossrobino/domco/blob/901651aa5239610bc8c1558f2e4c7e072bc52ee6/packages/domco/src/types/public/index.ts#L123) +[types/public/index.ts:146](https://github.com/rossrobino/domco/blob/fde430c2d32883df987ac54237edd49a27dfdd7e/packages/domco/src/types/public/index.ts#L146) -*** +--- + + + +### CreateAppMiddleware + +> **CreateAppMiddleware**: `object` + +#### Type declaration + + + +##### handler + +> **handler**: `MiddlewareHandler` + +The middleware to apply. + + + +##### path + +> **path**: `string` + +Path to apply the middleware to. + +#### Defined in + +[types/public/index.ts:7](https://github.com/rossrobino/domco/blob/fde430c2d32883df987ac54237edd49a27dfdd7e/packages/domco/src/types/public/index.ts#L7) + +--- + + + +### CreateAppOptions + +> **CreateAppOptions**: `object` + +#### Type declaration + + + +##### devServer? + +> `optional` **devServer**: `ViteDevServer` + +Only used in `dev` to call the server. + + + +##### honoOptions? + +> `optional` **honoOptions**: `HonoOptions`\<`Env`\> + +Passthrough options to the Hono app. + + + +##### middleware? + +> `optional` **middleware**: [`CreateAppMiddleware`](globals.md#createappmiddleware)[] + +Middleware to be applied before any routes. Useful for adapters that need to +inject middleware. + +#### Defined in + +[types/public/index.ts:15](https://github.com/rossrobino/domco/blob/fde430c2d32883df987ac54237edd49a27dfdd7e/packages/domco/src/types/public/index.ts#L15) + +--- @@ -190,7 +259,7 @@ Defaults to `undefined` - creates a `app` build only. ###### Default ```ts -undefined +undefined; ``` ###### Example @@ -203,8 +272,8 @@ import { adapter } from `"domco/adapter/...";` ```ts // vite.config.ts -import { defineConfig } from "vite"; import { domco, type DomcoConfig } from "domco"; +import { defineConfig } from "vite"; const config: DomcoConfig = { // options... @@ -217,9 +286,9 @@ export default defineConfig({ #### Defined in -[types/public/index.ts:86](https://github.com/rossrobino/domco/blob/901651aa5239610bc8c1558f2e4c7e072bc52ee6/packages/domco/src/types/public/index.ts#L86) +[types/public/index.ts:109](https://github.com/rossrobino/domco/blob/fde430c2d32883df987ac54237edd49a27dfdd7e/packages/domco/src/types/public/index.ts#L109) -*** +--- @@ -320,7 +389,7 @@ app.get("/", (c) => { const page = c.var.page(); // gets `src/route/path/+page.html` - const differentPage = c.var.page("/route/path") + const differentPage = c.var.page("/route/path"); return c.html(page); }); @@ -378,9 +447,9 @@ declare module "hono" { #### Defined in -[types/public/index.ts:148](https://github.com/rossrobino/domco/blob/901651aa5239610bc8c1558f2e4c7e072bc52ee6/packages/domco/src/types/public/index.ts#L148) +[types/public/index.ts:171](https://github.com/rossrobino/domco/blob/fde430c2d32883df987ac54237edd49a27dfdd7e/packages/domco/src/types/public/index.ts#L171) -*** +--- @@ -398,9 +467,9 @@ declare module "hono" { #### Defined in -[types/public/index.ts:121](https://github.com/rossrobino/domco/blob/901651aa5239610bc8c1558f2e4c7e072bc52ee6/packages/domco/src/types/public/index.ts#L121) +[types/public/index.ts:144](https://github.com/rossrobino/domco/blob/fde430c2d32883df987ac54237edd49a27dfdd7e/packages/domco/src/types/public/index.ts#L144) -*** +--- @@ -425,9 +494,9 @@ export const prerender: Prerender = ["/", "/post-1", "/post-2"]; #### Defined in -[types/public/index.ts:119](https://github.com/rossrobino/domco/blob/901651aa5239610bc8c1558f2e4c7e072bc52ee6/packages/domco/src/types/public/index.ts#L119) +[types/public/index.ts:142](https://github.com/rossrobino/domco/blob/fde430c2d32883df987ac54237edd49a27dfdd7e/packages/domco/src/types/public/index.ts#L142) -*** +--- @@ -447,7 +516,7 @@ export const prerender: Prerender = ["/", "/post-1", "/post-2"]; #### Defined in -[types/public/index.ts:125](https://github.com/rossrobino/domco/blob/901651aa5239610bc8c1558f2e4c7e072bc52ee6/packages/domco/src/types/public/index.ts#L125) +[types/public/index.ts:148](https://github.com/rossrobino/domco/blob/fde430c2d32883df987ac54237edd49a27dfdd7e/packages/domco/src/types/public/index.ts#L148) ## Functions @@ -474,8 +543,8 @@ The domco Vite plugin. ```ts // vite.config.ts -import { defineConfig } from "vite"; import { domco } from "domco"; +import { defineConfig } from "vite"; export default defineConfig({ plugins: [domco()], @@ -484,4 +553,4 @@ export default defineConfig({ #### Defined in -[plugin/index.ts:31](https://github.com/rossrobino/domco/blob/901651aa5239610bc8c1558f2e4c7e072bc52ee6/packages/domco/src/plugin/index.ts#L31) +[plugin/index.ts:31](https://github.com/rossrobino/domco/blob/fde430c2d32883df987ac54237edd49a27dfdd7e/packages/domco/src/plugin/index.ts#L31) diff --git a/apps/docs/src/lib/docs/+client.ts b/apps/docs/src/lib/docs/+client.ts index a275a09..0f7a6e0 100644 --- a/apps/docs/src/lib/docs/+client.ts +++ b/apps/docs/src/lib/docs/+client.ts @@ -1,5 +1,3 @@ -import { html } from "hono/html"; - const headings = document.querySelectorAll("h2, h3"); const tableOfContents = document.createElement("ul"); tableOfContents.classList.add("overflow-hidden", "!my-0", "!pl-0"); diff --git a/packages/create-domco/src/template/index.ts b/packages/create-domco/src/template/index.ts index fc513be..e8972e2 100644 --- a/packages/create-domco/src/template/index.ts +++ b/packages/create-domco/src/template/index.ts @@ -1,5 +1,5 @@ const versions = { - domco: "0.10.0", + domco: "0.11.0", hono: "4.5.11", autoprefixer: "10.4.20", prettier: "3.3.3", @@ -118,7 +118,6 @@ lerna-debug.log* node_modules dist -dist-ssr *.local .vercel diff --git a/packages/domco/src/adapter/vercel/index.ts b/packages/domco/src/adapter/vercel/index.ts index f4962b1..c762c40 100644 --- a/packages/domco/src/adapter/vercel/index.ts +++ b/packages/domco/src/adapter/vercel/index.ts @@ -1,5 +1,9 @@ import { dirNames, headers } from "../../constants/index.js"; -import type { AdapterBuilder, AdapterEntry } from "../../types/public/index.js"; +import type { + AdapterBuilder, + AdapterEntry, + CreateAppMiddleware, +} from "../../types/public/index.js"; import { clearDir, copyClient, copyServer } from "../../util/fs/index.js"; import { version } from "../../version/index.js"; import type { @@ -121,26 +125,30 @@ export const adapter: AdapterBuilder = ( /** * This is applied in `dev` and `preview` so users can see the src images. */ - const imageMiddleware = createMiddleware(async (c, next) => { - if (resolvedOptions.images) { - if (c.req.path.startsWith("/_vercel/image")) { - const { url, w, q } = c.req.query(); - - if (!url) throw new Error(`Add a \`url\` query param to ${c.req.url}`); - if (!w) throw new Error(`Add a \`w\` query param to ${c.req.url}`); - if (!q) throw new Error(`Add a \`q\` query param to ${c.req.url}`); - - if (!resolvedOptions.images.sizes.includes(parseInt(w))) { - throw new Error( - `\`${w}\` is not an included image size. Add \`${w}\` to \`sizes\` in your adapter config to support this width.`, - ); - } + const imageMiddleware: CreateAppMiddleware = { + path: "/*", + handler: createMiddleware(async (c, next) => { + if (resolvedOptions.images) { + if (c.req.path.startsWith("/_vercel/image")) { + const { url, w, q } = c.req.query(); + + if (!url) + throw new Error(`Add a \`url\` query param to ${c.req.url}`); + if (!w) throw new Error(`Add a \`w\` query param to ${c.req.url}`); + if (!q) throw new Error(`Add a \`q\` query param to ${c.req.url}`); - return c.redirect(url); + if (!resolvedOptions.images.sizes.includes(parseInt(w))) { + throw new Error( + `\`${w}\` is not an included image size. Add \`${w}\` to \`sizes\` in your adapter config to support this width.`, + ); + } + + return c.redirect(url); + } } - } - await next(); - }); + await next(); + }), + }; return { name: "vercel", diff --git a/packages/domco/src/app/dev/index.ts b/packages/domco/src/app/dev/index.ts index 1387d69..a99fcba 100644 --- a/packages/domco/src/app/dev/index.ts +++ b/packages/domco/src/app/dev/index.ts @@ -1,8 +1,13 @@ import type { Routes } from "../../types/private/index.js"; +import type { CreateAppOptions } from "../../types/public/index.js"; import { createRoutes } from "../../util/create-routes/index.js"; -import { addRoutes, applySetup, setServer } from "../util/index.js"; -import { Hono, type MiddlewareHandler } from "hono"; -import type { HonoOptions } from "hono/hono-base"; +import { + addMiddleware, + addRoutes, + addSetup, + setServer, +} from "../util/index.js"; +import { Hono } from "hono"; import fs from "node:fs/promises"; import path from "node:path"; import process from "node:process"; @@ -15,21 +20,13 @@ import type { ViteDevServer } from "vite"; * @returns Hono app instance. */ export const createAppDev = ( - options: { - devServer?: ViteDevServer; - honoOptions?: HonoOptions; - middleware?: MiddlewareHandler[]; - } = {}, + options: CreateAppOptions = {}, ) => { const { devServer, honoOptions, middleware } = options; const rootApp = new Hono(honoOptions); - if (middleware) { - for (const mw of middleware) { - rootApp.use(mw); - } - } + addMiddleware(rootApp, middleware); rootApp.all("/*", async (c) => { // this has to be called each request for HMR @@ -58,7 +55,7 @@ export const createAppDev = ( app.use(setServer); - applySetup(app, routes); + addSetup(app, routes); addRoutes({ app, routes }); diff --git a/packages/domco/src/app/index.ts b/packages/domco/src/app/index.ts index 1706ffd..0eeff16 100644 --- a/packages/domco/src/app/index.ts +++ b/packages/domco/src/app/index.ts @@ -1,8 +1,8 @@ -import { addRoutes, applySetup, setServer } from "./util/index.js"; +import type { CreateAppOptions } from "../types/public/index.js"; +import { addMiddleware, addRoutes, addSetup, setServer } from "./util/index.js"; import { manifest } from "domco:manifest"; import { routes } from "domco:routes"; -import { Hono, type MiddlewareHandler } from "hono"; -import type { HonoOptions } from "hono/hono-base"; +import { Hono } from "hono"; /** * Creates your production Hono app instance. You can import `createApp` from @@ -25,28 +25,23 @@ import type { HonoOptions } from "hono/hono-base"; * import { serveStatic } from "@hono/node-server/serve-static"; * import { createApp } from "./dist/server/app.js"; * - * const app = createApp({ middleware: [serveStatic({ root: "./dist/client" })] }); + * const app = createApp({ + * middleware: [{ path: "/*", handler: serveStatic({ root: "./dist/client" }) }], + * }); * * serve(app); * ``` */ export const createApp = ( - options: { - honoOptions?: HonoOptions; - middleware?: MiddlewareHandler[]; - } = {}, + options: CreateAppOptions = {}, ) => { const app = new Hono(options.honoOptions); app.use(setServer); - applySetup(app, routes); + addSetup(app, routes); - if (options.middleware) { - for (const mw of options.middleware) { - app.use(mw); - } - } + addMiddleware(app, options.middleware); addRoutes({ app, routes, manifest }); diff --git a/packages/domco/src/app/util/index.ts b/packages/domco/src/app/util/index.ts index cbdbb61..e03331f 100644 --- a/packages/domco/src/app/util/index.ts +++ b/packages/domco/src/app/util/index.ts @@ -3,6 +3,7 @@ import { type TagDescriptor, serializeTags } from "../../injector/index.js"; import type { Routes } from "../../types/private/index.js"; import type { Client, + CreateAppOptions, DomcoContextVariableMap, Page, Server, @@ -12,6 +13,17 @@ import { createMiddleware } from "hono/factory"; import { raw } from "hono/html"; import type { Manifest } from "vite"; +export const addMiddleware = ( + app: Hono, + middleware: CreateAppOptions["middleware"], +) => { + if (middleware) { + for (const mw of middleware) { + app.use(mw.path, mw.handler); + } + } +}; + /** * Adds routes based on route modules to the base app. * @@ -145,7 +157,7 @@ export const setVariables = (variables: Partial) => await next(); }); -export const applySetup = (app: Hono, routes: Routes) => { +export const addSetup = (app: Hono, routes: Routes) => { if (routes[setup]?.server?.default) { app.route("/", routes[setup].server.default); } diff --git a/packages/domco/src/plugin/configure-server/index.ts b/packages/domco/src/plugin/configure-server/index.ts index 145f12b..4e40b49 100644 --- a/packages/domco/src/plugin/configure-server/index.ts +++ b/packages/domco/src/plugin/configure-server/index.ts @@ -3,8 +3,7 @@ import type { createApp as createAppType } from "../../app/index.js"; import { dirNames, fileNames } from "../../constants/index.js"; import { createRequestListener } from "../../node/request-listener/index.js"; import { serveStatic } from "../../node/serve-static/index.js"; -import type { Adapter } from "../../types/public/index.js"; -import type { MiddlewareHandler } from "hono"; +import type { Adapter, CreateAppOptions } from "../../types/public/index.js"; import path from "node:path"; import process from "node:process"; import url from "node:url"; @@ -95,7 +94,9 @@ export const configureServerPlugin = (adapter?: Adapter): Plugin => { ) ).createApp as typeof createAppType; - const middleware: MiddlewareHandler[] = [serveStatic]; + const middleware: CreateAppOptions["middleware"] = [ + { path: "/*", handler: serveStatic }, + ]; if (adapter?.previewMiddleware) { middleware.push(...adapter.previewMiddleware); diff --git a/packages/domco/src/types/public/index.ts b/packages/domco/src/types/public/index.ts index 9a53f76..9c2730f 100644 --- a/packages/domco/src/types/public/index.ts +++ b/packages/domco/src/types/public/index.ts @@ -1,7 +1,30 @@ import type { MaybePromise } from "../helper/index.js"; -import type { MiddlewareHandler } from "hono"; +import type { Env, MiddlewareHandler } from "hono"; +import type { HonoOptions } from "hono/hono-base"; import type { HtmlEscapedString } from "hono/utils/html"; -import type { SSRTarget } from "vite"; +import type { SSRTarget, ViteDevServer } from "vite"; + +export type CreateAppMiddleware = { + /** Path to apply the middleware to. */ + path: string; + + /** The middleware to apply. */ + handler: MiddlewareHandler; +}; + +export type CreateAppOptions = { + /** Only used in `dev` to call the server. */ + devServer?: ViteDevServer; + + /** Passthrough options to the Hono app. */ + honoOptions?: HonoOptions; + + /** + * Middleware to be applied before any routes. Useful for adapters that need to + * inject middleware. + */ + middleware?: CreateAppMiddleware[]; +}; export type AdapterEntry = (AdapterEntryOptions: { /** The app entrypoint to import `createApp` from. */ @@ -47,14 +70,14 @@ export type Adapter = { * For production middleware, export it from the adapter module, * and then import into the entry point. */ - devMiddleware?: MiddlewareHandler[]; + devMiddleware?: CreateAppOptions["middleware"]; /** * Middleware to apply in `preview` mode. * For production middleware, export it from the adapter module, * and then import into the entry point. */ - previewMiddleware?: MiddlewareHandler[]; + previewMiddleware?: CreateAppOptions["middleware"]; }; export type AdapterBuilder = (