vinojs 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { c as PAGE_SLOT_ID, l as PAYLOAD_SCRIPT_ID, o as NAV_HEADER, t as COMPONENT_TAG } from "./constants-BVT2bJEd.mjs";
2
- import { s as resolvePageRender } from "./definePage--9Pz7rHs.mjs";
2
+ import { u as resolvePageRender } from "./definePage-CcqUZAiy.mjs";
3
3
  //#region src/client/create-client.ts
4
4
  const islands = /* @__PURE__ */ new Map();
5
5
  let pageRoot = null;
@@ -1,4 +1,4 @@
1
- import { o as readPageDefinition } from "./definePage--9Pz7rHs.mjs";
1
+ import { l as readPageDefinition } from "./definePage-CcqUZAiy.mjs";
2
2
  //#region src/vino/config.ts
3
3
  function loadMergedConfig(page, pageMod) {
4
4
  const merged = { ...page.config };
@@ -108,4 +108,4 @@ function compareRoutes(a, b) {
108
108
  //#endregion
109
109
  export { pathDepth as a, loadMergedData as c, pathCovers as i, compareRoutes as n, urlToHtmlFile as o, compileFilePath as r, loadMergedConfig as s, applyParams as t };
110
110
 
111
- //# sourceMappingURL=compile-glNMkyyx.mjs.map
111
+ //# sourceMappingURL=compile-BGVCPc4H.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"compile-glNMkyyx.mjs","names":[],"sources":["../src/vino/config.ts","../src/vino/compile.ts"],"sourcesContent":["import { readPageDefinition } from \"./definePage.ts\";\nimport type { DataLoader, ManifestPage, PageConfig, PageModule } from \"./types.ts\";\n\nexport function loadMergedConfig(page: ManifestPage, pageMod?: PageModule): PageConfig {\n const merged: PageConfig = { ...page.config };\n const inline = readPageDefinition(pageMod?.default)?.config;\n if (inline) Object.assign(merged, inline);\n return merged;\n}\n\nexport async function loadMergedData(\n _page: ManifestPage,\n c: Parameters<DataLoader>[0],\n pageMod?: PageModule,\n): Promise<unknown> {\n const fn = readPageDefinition(pageMod?.default)?.data;\n if (!fn) return undefined;\n return await fn(c);\n}\n","export interface CompiledRoute {\n /** Filesystem-derived path using `:param` (no Hono regex). `/`, `/about`, `/blog/:slug`. */\n path: string;\n /** Hono route pattern (`:slug{.+}` for catch-all). */\n pattern: string;\n isCatchAll: boolean;\n isStatic: boolean;\n paramNames: string[];\n}\n\nconst GROUP_RE = /^\\([^)]+\\)$/;\nconst CATCH_ALL_RE = /^\\[\\.\\.\\.(.+)\\]$/;\nconst PARAM_RE = /^\\[([^\\]]+)\\]$/;\n\nfunction toPosix(relativePath: string): string {\n return relativePath.replaceAll(\"\\\\\", \"/\").replace(/^\\.\\//, \"\");\n}\n\nfunction stripExt(posixPath: string): string {\n return posixPath.replace(/\\.(tsx|ts|jsx|js|mjs)$/, \"\");\n}\n\n/**\n * Compile a pages-dir-relative file path to a Hono route.\n * `(group)` dirs are omitted, `[param]` becomes `:param`, `[...slug]` becomes `:slug{.+}`,\n * trailing `index` is dropped.\n */\nexport function compileFilePath(relativePath: string): CompiledRoute {\n const posix = toPosix(relativePath);\n const withoutExt = stripExt(posix);\n\n const rawSegs = withoutExt.split(\"/\").filter(Boolean);\n const segs = rawSegs.filter((seg) => !GROUP_RE.test(seg));\n if (segs.at(-1) === \"index\") segs.pop();\n\n const out: string[] = [];\n const paramNames: string[] = [];\n let isCatchAll = false;\n\n for (const seg of segs) {\n const catchAll = CATCH_ALL_RE.exec(seg);\n if (catchAll) {\n paramNames.push(catchAll[1]);\n out.push(`:${catchAll[1]}{.+}`);\n isCatchAll = true;\n continue;\n }\n const param = PARAM_RE.exec(seg);\n if (param && !param[1].startsWith(\"...\")) {\n paramNames.push(param[1]);\n out.push(`:${param[1]}`);\n continue;\n }\n out.push(seg);\n }\n\n const pattern = out.length === 0 ? \"/\" : `/${out.join(\"/\")}`;\n const path = pattern.replaceAll(\"{.+}\", \"\");\n const isStatic = paramNames.length === 0 && !isCatchAll;\n\n return { path, pattern, isCatchAll, isStatic, paramNames };\n}\n\nexport function pathSegments(path: string): string[] {\n return path.split(\"/\").filter(Boolean);\n}\n\nexport function pathDepth(path: string): number {\n return pathSegments(path).length;\n}\n\n/**\n * True if `parent` is `/` or a prefix of `child`.\n * A `:param` segment in `parent` matches any child segment; a static parent\n * segment does not match a param child segment.\n */\nexport function pathCovers(parent: string, child: string): boolean {\n if (parent === \"/\" || parent === \"\") return true;\n const a = pathSegments(parent);\n const b = pathSegments(child);\n if (a.length > b.length) return false;\n return a.every((seg, i) => {\n const other = b[i];\n if (seg === other) return true;\n if (seg.startsWith(\":\")) return true;\n return false;\n });\n}\n\nexport function applyParams(path: string, params: Record<string, string>): string {\n let url = path;\n for (const [key, value] of Object.entries(params)) {\n url = url.replace(`:${key}`, value);\n }\n if (!url.startsWith(\"/\")) url = `/${url}`;\n return url;\n}\n\nexport function urlToHtmlFile(url: string): string {\n const clean = url.split(\"?\")[0].split(\"#\")[0];\n if (clean === \"/\") return \"index.html\";\n const trimmed = clean.replace(/^\\//, \"\").replace(/\\/$/, \"\");\n if (clean.endsWith(\"/\")) return `${trimmed}/index.html`;\n return `${trimmed}.html`;\n}\n\nexport function routeRank(pattern: string): number {\n if (pattern.includes(\"{.+}\")) return 2;\n if (pattern.includes(\":\")) return 1;\n return 0;\n}\n\nexport function compareRoutes(a: { pattern: string }, b: { pattern: string }): number {\n const d = routeRank(a.pattern) - routeRank(b.pattern);\n if (d !== 0) return d;\n return b.pattern.length - a.pattern.length;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"compile-BGVCPc4H.mjs","names":[],"sources":["../src/vino/config.ts","../src/vino/compile.ts"],"sourcesContent":["import { readPageDefinition } from \"./definePage.ts\";\nimport type { DataLoader, ManifestPage, PageConfig, PageModule } from \"./types.ts\";\n\nexport function loadMergedConfig(page: ManifestPage, pageMod?: PageModule): PageConfig {\n const merged: PageConfig = { ...page.config };\n const inline = readPageDefinition(pageMod?.default)?.config;\n if (inline) Object.assign(merged, inline);\n return merged;\n}\n\nexport async function loadMergedData(\n _page: ManifestPage,\n c: Parameters<DataLoader>[0],\n pageMod?: PageModule,\n): Promise<unknown> {\n const fn = readPageDefinition(pageMod?.default)?.data;\n if (!fn) return undefined;\n return await fn(c);\n}\n","export interface CompiledRoute {\n /** Filesystem-derived path using `:param` (no Hono regex). `/`, `/about`, `/blog/:slug`. */\n path: string;\n /** Hono route pattern (`:slug{.+}` for catch-all). */\n pattern: string;\n isCatchAll: boolean;\n isStatic: boolean;\n paramNames: string[];\n}\n\nconst GROUP_RE = /^\\([^)]+\\)$/;\nconst CATCH_ALL_RE = /^\\[\\.\\.\\.(.+)\\]$/;\nconst PARAM_RE = /^\\[([^\\]]+)\\]$/;\n\nfunction toPosix(relativePath: string): string {\n return relativePath.replaceAll(\"\\\\\", \"/\").replace(/^\\.\\//, \"\");\n}\n\nfunction stripExt(posixPath: string): string {\n return posixPath.replace(/\\.(tsx|ts|jsx|js|mjs)$/, \"\");\n}\n\n/**\n * Compile a pages-dir-relative file path to a Hono route.\n * `(group)` dirs are omitted, `[param]` becomes `:param`, `[...slug]` becomes `:slug{.+}`,\n * trailing `index` is dropped.\n */\nexport function compileFilePath(relativePath: string): CompiledRoute {\n const posix = toPosix(relativePath);\n const withoutExt = stripExt(posix);\n\n const rawSegs = withoutExt.split(\"/\").filter(Boolean);\n const segs = rawSegs.filter((seg) => !GROUP_RE.test(seg));\n if (segs.at(-1) === \"index\") segs.pop();\n\n const out: string[] = [];\n const paramNames: string[] = [];\n let isCatchAll = false;\n\n for (const seg of segs) {\n const catchAll = CATCH_ALL_RE.exec(seg);\n if (catchAll) {\n paramNames.push(catchAll[1]);\n out.push(`:${catchAll[1]}{.+}`);\n isCatchAll = true;\n continue;\n }\n const param = PARAM_RE.exec(seg);\n if (param && !param[1].startsWith(\"...\")) {\n paramNames.push(param[1]);\n out.push(`:${param[1]}`);\n continue;\n }\n out.push(seg);\n }\n\n const pattern = out.length === 0 ? \"/\" : `/${out.join(\"/\")}`;\n const path = pattern.replaceAll(\"{.+}\", \"\");\n const isStatic = paramNames.length === 0 && !isCatchAll;\n\n return { path, pattern, isCatchAll, isStatic, paramNames };\n}\n\nexport function pathSegments(path: string): string[] {\n return path.split(\"/\").filter(Boolean);\n}\n\nexport function pathDepth(path: string): number {\n return pathSegments(path).length;\n}\n\n/**\n * True if `parent` is `/` or a prefix of `child`.\n * A `:param` segment in `parent` matches any child segment; a static parent\n * segment does not match a param child segment.\n */\nexport function pathCovers(parent: string, child: string): boolean {\n if (parent === \"/\" || parent === \"\") return true;\n const a = pathSegments(parent);\n const b = pathSegments(child);\n if (a.length > b.length) return false;\n return a.every((seg, i) => {\n const other = b[i];\n if (seg === other) return true;\n if (seg.startsWith(\":\")) return true;\n return false;\n });\n}\n\nexport function applyParams(path: string, params: Record<string, string>): string {\n let url = path;\n for (const [key, value] of Object.entries(params)) {\n url = url.replace(`:${key}`, value);\n }\n if (!url.startsWith(\"/\")) url = `/${url}`;\n return url;\n}\n\nexport function urlToHtmlFile(url: string): string {\n const clean = url.split(\"?\")[0].split(\"#\")[0];\n if (clean === \"/\") return \"index.html\";\n const trimmed = clean.replace(/^\\//, \"\").replace(/\\/$/, \"\");\n if (clean.endsWith(\"/\")) return `${trimmed}/index.html`;\n return `${trimmed}.html`;\n}\n\nexport function routeRank(pattern: string): number {\n if (pattern.includes(\"{.+}\")) return 2;\n if (pattern.includes(\":\")) return 1;\n return 0;\n}\n\nexport function compareRoutes(a: { pattern: string }, b: { pattern: string }): number {\n const d = routeRank(a.pattern) - routeRank(b.pattern);\n if (d !== 0) return d;\n return b.pattern.length - a.pattern.length;\n}\n"],"mappings":"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"}
@@ -0,0 +1,159 @@
1
+ //#region src/vino/definePage.ts
2
+ const PAGE_KIND = "vino:page";
3
+ const SERVER_PAGE_KIND = "vino:server-page";
4
+ const pageRuns = /* @__PURE__ */ new WeakMap();
5
+ /** Bind the filesystem-route renderer used by the terminal page handler. */
6
+ function bindPageRun(c, run) {
7
+ pageRuns.set(c, run);
8
+ }
9
+ /**
10
+ * Strip page metadata so the list spreads onto `app.get(path, ...handlers)`
11
+ * the same way `hono/factory` `createHandlers` does.
12
+ */
13
+ function asHandlers(page) {
14
+ return page;
15
+ }
16
+ function requestParams(c) {
17
+ try {
18
+ return c.req.param();
19
+ } catch {
20
+ return {};
21
+ }
22
+ }
23
+ async function renderStandalone(c, fields) {
24
+ const params = requestParams(c);
25
+ const data = fields.data ? await fields.data(c) : void 0;
26
+ const node = fields.render({
27
+ data,
28
+ params
29
+ });
30
+ try {
31
+ return await c.render(node);
32
+ } catch {
33
+ return c.html(node);
34
+ }
35
+ }
36
+ function createPageHandler(fields) {
37
+ return async (c) => {
38
+ const run = pageRuns.get(c);
39
+ if (run) return run();
40
+ return renderStandalone(c, fields);
41
+ };
42
+ }
43
+ /**
44
+ * Run a `createHandlers`-style list on the current context.
45
+ * Returning a `Response` from a handler sets `c.res`, matching Hono's compose.
46
+ */
47
+ async function dispatchHandlers(handlers, c, next) {
48
+ let index = -1;
49
+ const dispatch = async (i) => {
50
+ if (i <= index) throw new Error("next() called multiple times");
51
+ index = i;
52
+ const handler = handlers[i];
53
+ if (handler) {
54
+ const out = await handler(c, () => dispatch(i + 1));
55
+ if (out instanceof Response && !c.finalized) c.res = out;
56
+ } else if (next) await next();
57
+ };
58
+ await dispatch(0);
59
+ return c.res;
60
+ }
61
+ function isPageDefinition(value) {
62
+ if (value === null || typeof value !== "object" && typeof value !== "function") return false;
63
+ const candidate = value;
64
+ return (candidate.kind === "vino:page" || candidate.kind === "vino:server-page") && typeof candidate.render === "function";
65
+ }
66
+ function isClientPageDefinition(value) {
67
+ return isPageDefinition(value) && value.kind === "vino:page";
68
+ }
69
+ function isServerPageDefinition(value) {
70
+ return isPageDefinition(value) && value.kind === "vino:server-page";
71
+ }
72
+ function readPageDefinition(value) {
73
+ return isPageDefinition(value) ? value : void 0;
74
+ }
75
+ function resolvePageRender(value) {
76
+ if (isPageDefinition(value)) return value.render;
77
+ if (typeof value === "function") return value;
78
+ }
79
+ /** True when source calls `definePage(...)` (hydratable pages). */
80
+ function usesDefinePage(code) {
81
+ return /(?:^|[^\w$])definePage\s*(?:<[^>]*>\s*)?\(/.test(code);
82
+ }
83
+ function isPageSpec(value) {
84
+ return typeof value === "function" || value !== null && typeof value === "object" && !Array.isArray(value);
85
+ }
86
+ /** Same runtime as `hono/factory` `createHandlers`: drop holes, return the list. */
87
+ function createHandlers(...handlers) {
88
+ return handlers.filter((handler) => handler !== void 0);
89
+ }
90
+ function asPageDefinition(fields, handlers) {
91
+ const list = createHandlers(...handlers);
92
+ if (list.length === 0) throw new TypeError("[vino] definePage() produced no handlers");
93
+ return Object.assign(list, fields);
94
+ }
95
+ function pageDefinition(args, kind, fnName) {
96
+ const spec = args.at(-1);
97
+ if (args.length === 0 || !isPageSpec(spec)) throw new TypeError(`[vino] ${fnName}() requires a JSX function or { render }`);
98
+ const leading = args.slice(0, -1).filter((handler) => typeof handler === "function");
99
+ if (typeof spec === "function") {
100
+ const fields = {
101
+ kind,
102
+ client: kind === PAGE_KIND,
103
+ render: spec
104
+ };
105
+ return asPageDefinition(fields, [...leading, createPageHandler(fields)]);
106
+ }
107
+ const render = spec.render ?? spec.page;
108
+ if (typeof render !== "function") throw new TypeError(`[vino] ${fnName}() requires a JSX function or { render }`);
109
+ const fields = {
110
+ kind,
111
+ client: kind === PAGE_KIND,
112
+ render,
113
+ data: spec.data,
114
+ config: spec.config
115
+ };
116
+ return asPageDefinition(fields, [
117
+ ...leading,
118
+ ...spec.use ?? [],
119
+ createPageHandler(fields)
120
+ ]);
121
+ }
122
+ /**
123
+ * Declare a page that SSRs with `hono/jsx`, then hydrates `#vino-page` with `hono/jsx/dom`.
124
+ *
125
+ * Returns a Hono handler list (same shape as `createHandlers`) with `render` /
126
+ * `data` / `config` attached.
127
+ *
128
+ * @example
129
+ * export default definePage(() => {
130
+ * const [n, setN] = useState(0)
131
+ * return <button onClick={() => setN(n + 1)}>{n}</button>
132
+ * })
133
+ *
134
+ * @example
135
+ * export default definePage({
136
+ * config: { prerender: 'full', clientRouting: true },
137
+ * data: async (c) => ({ title: 'Hello' }),
138
+ * render: ({ data }) => <h1>{data.title}</h1>,
139
+ * })
140
+ *
141
+ * @example
142
+ * import data from './[slug].server'
143
+ * export default definePage({ data, render: ({ data }) => <h1>{data.title}</h1> })
144
+ *
145
+ * @example
146
+ * export default definePage(auth, { data, render: ({ data }) => <h1>{data.title}</h1> })
147
+ */
148
+ const definePage = ((...args) => pageDefinition(args, PAGE_KIND, "definePage"));
149
+ /**
150
+ * Declare a page that SSRs only. The output is never hydrated.
151
+ *
152
+ * @example
153
+ * export default defineServerPage(() => <h1>About</h1>)
154
+ */
155
+ const defineServerPage = ((...args) => pageDefinition(args, SERVER_PAGE_KIND, "defineServerPage"));
156
+ //#endregion
157
+ export { dispatchHandlers as a, isServerPageDefinition as c, usesDefinePage as d, defineServerPage as i, readPageDefinition as l, bindPageRun as n, isClientPageDefinition as o, definePage as r, isPageDefinition as s, asHandlers as t, resolvePageRender as u };
158
+
159
+ //# sourceMappingURL=definePage-CcqUZAiy.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definePage-CcqUZAiy.mjs","names":[],"sources":["../src/vino/definePage.ts"],"sourcesContent":["import type { Context, Env, MiddlewareHandler, Next } from \"hono\";\nimport type { ServerFn } from \"./defineServerFn.ts\";\nimport type { DataLoader, PageConfig, PageProps, VinoHtml } from \"./types.ts\";\n\nexport const PAGE_KIND = \"vino:page\" as const;\nexport const SERVER_PAGE_KIND = \"vino:server-page\" as const;\n\nexport type PageRender<Data = unknown> = (props: PageProps<Data>) => VinoHtml;\n\nexport type PageDataFn<E extends Env = Env> = DataLoader<E> | ServerFn<unknown, E>;\n\nexport interface DefinePageOptions<Data = unknown, E extends Env = Env> {\n /** JSX page component. Alias: `page`. */\n render?: PageRender<Data>;\n page?: PageRender<Data>;\n /** Loader or `defineServerFn(...)` */\n data?: DataLoader<E> | ServerFn<Data, E>;\n config?: PageConfig;\n /** Extra Hono middleware, composed before the page handler. */\n use?: MiddlewareHandler<E>[];\n}\n\n/** Options object that infers `data` return type into `render` / `page`. */\nexport type InferDefinePageOptions<\n D extends (...args: never[]) => unknown,\n E extends Env = Env,\n Data = Awaited<ReturnType<D>>,\n> = {\n data?: D;\n config?: PageConfig;\n use?: MiddlewareHandler<E>[];\n} & (\n | { render: PageRender<Data>; page?: PageRender<Data> }\n | { page: PageRender<Data>; render?: PageRender<Data> }\n);\n\n/**\n * `definePage` / `defineServerPage` call signature, with `Env` fixed so `data(c)`\n * is typed without blocking inference of the loader return type.\n *\n * Returns a Hono handler list (same shape as `hono/factory` `createHandlers`)\n * with page metadata attached, so it can be spread onto a route:\n * `app.get('/path', ...definePage(...))`.\n */\nexport interface DefinePageFn<E extends Env = Env> {\n <Data = unknown>(page: PageRender<Data>): PageDefinition<Data, E>;\n <D extends PageDataFn<E>>(options: InferDefinePageOptions<D, E>): PageDefinition<Awaited<ReturnType<D>>, E>;\n <Data = unknown>(options: DefinePageOptions<Data, E>): PageDefinition<Data, E>;\n <Data = unknown>(\n middleware: MiddlewareHandler<E>,\n page: PageRender<Data> | DefinePageOptions<Data, E>,\n ): PageDefinition<Data, E>;\n}\n\nexport interface PageDefinitionFields<Data = unknown, E extends Env = Env> {\n readonly kind: typeof PAGE_KIND | typeof SERVER_PAGE_KIND;\n readonly client: boolean;\n readonly render: PageRender<Data>;\n readonly data?: DataLoader<E> | ServerFn<Data, E>;\n readonly config?: PageConfig;\n}\n\n/**\n * Hono handler list plus page metadata (`kind`, `render`, `data`, `config`).\n * Typed as a non-empty tuple so it spreads onto `app.get(path, ...handlers)`\n * the same way `hono/factory` `createHandlers` does.\n */\nexport type PageDefinition<Data = unknown, E extends Env = Env> = [\n MiddlewareHandler<E>,\n ...MiddlewareHandler<E>[],\n] &\n PageDefinitionFields<Data, E>;\n\nconst pageRuns = new WeakMap<Context, () => Promise<Response>>();\n\n/** Bind the filesystem-route renderer used by the terminal page handler. */\nexport function bindPageRun(c: Context, run: () => Promise<Response>): void {\n pageRuns.set(c, run);\n}\n\n/**\n * Strip page metadata so the list spreads onto `app.get(path, ...handlers)`\n * the same way `hono/factory` `createHandlers` does.\n */\nexport function asHandlers<Data, E extends Env>(\n page: PageDefinition<Data, E>,\n): [MiddlewareHandler<E>, ...MiddlewareHandler<E>[]] {\n return page;\n}\n\nfunction requestParams(c: Context): Record<string, string> {\n try {\n return c.req.param();\n } catch {\n return {};\n }\n}\n\nasync function renderStandalone(c: Context, fields: PageDefinitionFields): Promise<Response> {\n const params = requestParams(c);\n const data = fields.data ? await fields.data(c as never) : undefined;\n const node = fields.render({ data, params });\n try {\n return await c.render(node as never);\n } catch {\n return c.html(node as never);\n }\n}\n\nfunction createPageHandler(fields: PageDefinitionFields): MiddlewareHandler {\n return async (c) => {\n const run = pageRuns.get(c);\n if (run) return run();\n return renderStandalone(c, fields);\n };\n}\n\ntype DispatchHandler = (c: Context, next: Next) => unknown;\n\n/**\n * Run a `createHandlers`-style list on the current context.\n * Returning a `Response` from a handler sets `c.res`, matching Hono's compose.\n */\nexport async function dispatchHandlers(\n handlers: readonly DispatchHandler[],\n c: Context,\n next?: Next,\n): Promise<Response> {\n let index = -1;\n const dispatch = async (i: number): Promise<void> => {\n if (i <= index) throw new Error(\"next() called multiple times\");\n index = i;\n const handler = handlers[i];\n if (handler) {\n const out = await handler(c, () => dispatch(i + 1));\n if (out instanceof Response && !c.finalized) c.res = out;\n } else if (next) {\n await next();\n }\n };\n await dispatch(0);\n return c.res;\n}\n\nexport function isPageDefinition(value: unknown): value is PageDefinition {\n if (value === null || (typeof value !== \"object\" && typeof value !== \"function\")) return false;\n const candidate = value as PageDefinition;\n return (\n (candidate.kind === PAGE_KIND || candidate.kind === SERVER_PAGE_KIND) &&\n typeof candidate.render === \"function\"\n );\n}\n\nexport function isClientPageDefinition(value: unknown): value is PageDefinition {\n return isPageDefinition(value) && value.kind === PAGE_KIND;\n}\n\nexport function isServerPageDefinition(value: unknown): value is PageDefinition {\n return isPageDefinition(value) && value.kind === SERVER_PAGE_KIND;\n}\n\nexport function readPageDefinition(value: unknown): PageDefinition | undefined {\n return isPageDefinition(value) ? value : undefined;\n}\n\nexport function resolvePageRender(value: unknown): PageRender | undefined {\n if (isPageDefinition(value)) return value.render;\n if (typeof value === \"function\") return value as PageRender;\n return undefined;\n}\n\n/** True when source calls `definePage(...)` (hydratable pages). */\nexport function usesDefinePage(code: string): boolean {\n return /(?:^|[^\\w$])definePage\\s*(?:<[^>]*>\\s*)?\\(/.test(code);\n}\n\nfunction isPageSpec(value: unknown): value is PageRender | DefinePageOptions {\n return typeof value === \"function\" || (value !== null && typeof value === \"object\" && !Array.isArray(value));\n}\n\n/** Same runtime as `hono/factory` `createHandlers`: drop holes, return the list. */\nfunction createHandlers<E extends Env>(...handlers: (MiddlewareHandler<E> | undefined)[]): MiddlewareHandler<E>[] {\n return handlers.filter((handler): handler is MiddlewareHandler<E> => handler !== undefined);\n}\n\nfunction asPageDefinition<Data, E extends Env>(\n fields: PageDefinitionFields<Data, E>,\n handlers: (MiddlewareHandler<E> | undefined)[],\n): PageDefinition<Data, E> {\n const list = createHandlers(...handlers);\n if (list.length === 0) {\n throw new TypeError(\"[vino] definePage() produced no handlers\");\n }\n return Object.assign(list, fields) as PageDefinition<Data, E>;\n}\n\nfunction pageDefinition(\n args: unknown[],\n kind: typeof PAGE_KIND | typeof SERVER_PAGE_KIND,\n fnName: string,\n): PageDefinition {\n const spec = args.at(-1);\n if (args.length === 0 || !isPageSpec(spec)) {\n throw new TypeError(`[vino] ${fnName}() requires a JSX function or { render }`);\n }\n const leading = args\n .slice(0, -1)\n .filter((handler): handler is MiddlewareHandler => typeof handler === \"function\");\n\n if (typeof spec === \"function\") {\n const fields = { kind, client: kind === PAGE_KIND, render: spec };\n return asPageDefinition(fields, [...leading, createPageHandler(fields)]);\n }\n\n const render = spec.render ?? spec.page;\n if (typeof render !== \"function\") {\n throw new TypeError(`[vino] ${fnName}() requires a JSX function or { render }`);\n }\n const fields = { kind, client: kind === PAGE_KIND, render, data: spec.data, config: spec.config };\n return asPageDefinition(fields, [...leading, ...(spec.use ?? []), createPageHandler(fields)]);\n}\n\n/**\n * Declare a page that SSRs with `hono/jsx`, then hydrates `#vino-page` with `hono/jsx/dom`.\n *\n * Returns a Hono handler list (same shape as `createHandlers`) with `render` /\n * `data` / `config` attached.\n *\n * @example\n * export default definePage(() => {\n * const [n, setN] = useState(0)\n * return <button onClick={() => setN(n + 1)}>{n}</button>\n * })\n *\n * @example\n * export default definePage({\n * config: { prerender: 'full', clientRouting: true },\n * data: async (c) => ({ title: 'Hello' }),\n * render: ({ data }) => <h1>{data.title}</h1>,\n * })\n *\n * @example\n * import data from './[slug].server'\n * export default definePage({ data, render: ({ data }) => <h1>{data.title}</h1> })\n *\n * @example\n * export default definePage(auth, { data, render: ({ data }) => <h1>{data.title}</h1> })\n */\nexport const definePage = ((...args: never[]) => pageDefinition(args, PAGE_KIND, \"definePage\")) as DefinePageFn;\n\n/**\n * Declare a page that SSRs only. The output is never hydrated.\n *\n * @example\n * export default defineServerPage(() => <h1>About</h1>)\n */\nexport const defineServerPage = ((...args: never[]) =>\n pageDefinition(args, SERVER_PAGE_KIND, \"defineServerPage\")) as DefinePageFn;\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { a as PageConfig, c as VinoHtml, n as DataLoader, o as PageProps } from "./types-CQITdkpF.mjs";
2
- import { Context, Env, Hono } from "hono";
2
+ import { Context, Env, Hono, MiddlewareHandler } from "hono";
3
3
  //#region src/vino/defineServerFn.d.ts
4
4
  declare const SERVER_FN_KIND: "vino:server-fn";
5
5
  type ServerFnHandler<T = unknown, E extends Env = Env> = (c: Context<E, string>) => T | Promise<T>;
@@ -48,11 +48,14 @@ interface DefinePageOptions<Data = unknown, E extends Env = Env> {
48
48
  /** Loader or `defineServerFn(...)` */
49
49
  data?: DataLoader<E> | ServerFn<Data, E>;
50
50
  config?: PageConfig;
51
+ /** Extra Hono middleware, composed before the page handler. */
52
+ use?: MiddlewareHandler<E>[];
51
53
  }
52
54
  /** Options object that infers `data` return type into `render` / `page`. */
53
- type InferDefinePageOptions<D extends (...args: never[]) => unknown, Data = Awaited<ReturnType<D>>> = {
55
+ type InferDefinePageOptions<D extends (...args: never[]) => unknown, E extends Env = Env, Data = Awaited<ReturnType<D>>> = {
54
56
  data?: D;
55
57
  config?: PageConfig;
58
+ use?: MiddlewareHandler<E>[];
56
59
  } & ({
57
60
  render: PageRender<Data>;
58
61
  page?: PageRender<Data>;
@@ -63,19 +66,35 @@ type InferDefinePageOptions<D extends (...args: never[]) => unknown, Data = Awai
63
66
  /**
64
67
  * `definePage` / `defineServerPage` call signature, with `Env` fixed so `data(c)`
65
68
  * is typed without blocking inference of the loader return type.
69
+ *
70
+ * Returns a Hono handler list (same shape as `hono/factory` `createHandlers`)
71
+ * with page metadata attached, so it can be spread onto a route:
72
+ * `app.get('/path', ...definePage(...))`.
66
73
  */
67
74
  interface DefinePageFn<E extends Env = Env> {
68
- <Data = unknown>(page: PageRender<Data>): PageDefinition<Data>;
69
- <D extends PageDataFn<E>>(options: InferDefinePageOptions<D>): PageDefinition<Awaited<ReturnType<D>>>;
70
- <Data = unknown>(options: DefinePageOptions<Data, E>): PageDefinition<Data>;
75
+ <Data = unknown>(page: PageRender<Data>): PageDefinition<Data, E>;
76
+ <D extends PageDataFn<E>>(options: InferDefinePageOptions<D, E>): PageDefinition<Awaited<ReturnType<D>>, E>;
77
+ <Data = unknown>(options: DefinePageOptions<Data, E>): PageDefinition<Data, E>;
78
+ <Data = unknown>(middleware: MiddlewareHandler<E>, page: PageRender<Data> | DefinePageOptions<Data, E>): PageDefinition<Data, E>;
71
79
  }
72
- interface PageDefinition<Data = unknown> {
80
+ interface PageDefinitionFields<Data = unknown, E extends Env = Env> {
73
81
  readonly kind: typeof PAGE_KIND | typeof SERVER_PAGE_KIND;
74
82
  readonly client: boolean;
75
83
  readonly render: PageRender<Data>;
76
- readonly data?: DataLoader | ServerFn<Data>;
84
+ readonly data?: DataLoader<E> | ServerFn<Data, E>;
77
85
  readonly config?: PageConfig;
78
86
  }
87
+ /**
88
+ * Hono handler list plus page metadata (`kind`, `render`, `data`, `config`).
89
+ * Typed as a non-empty tuple so it spreads onto `app.get(path, ...handlers)`
90
+ * the same way `hono/factory` `createHandlers` does.
91
+ */
92
+ type PageDefinition<Data = unknown, E extends Env = Env> = [MiddlewareHandler<E>, ...MiddlewareHandler<E>[]] & PageDefinitionFields<Data, E>;
93
+ /**
94
+ * Strip page metadata so the list spreads onto `app.get(path, ...handlers)`
95
+ * the same way `hono/factory` `createHandlers` does.
96
+ */
97
+ declare function asHandlers<Data, E extends Env>(page: PageDefinition<Data, E>): [MiddlewareHandler<E>, ...MiddlewareHandler<E>[]];
79
98
  declare function isPageDefinition(value: unknown): value is PageDefinition;
80
99
  declare function isClientPageDefinition(value: unknown): value is PageDefinition;
81
100
  declare function isServerPageDefinition(value: unknown): value is PageDefinition;
@@ -83,6 +102,9 @@ declare function resolvePageRender(value: unknown): PageRender | undefined;
83
102
  /**
84
103
  * Declare a page that SSRs with `hono/jsx`, then hydrates `#vino-page` with `hono/jsx/dom`.
85
104
  *
105
+ * Returns a Hono handler list (same shape as `createHandlers`) with `render` /
106
+ * `data` / `config` attached.
107
+ *
86
108
  * @example
87
109
  * export default definePage(() => {
88
110
  * const [n, setN] = useState(0)
@@ -99,6 +121,9 @@ declare function resolvePageRender(value: unknown): PageRender | undefined;
99
121
  * @example
100
122
  * import data from './[slug].server'
101
123
  * export default definePage({ data, render: ({ data }) => <h1>{data.title}</h1> })
124
+ *
125
+ * @example
126
+ * export default definePage(auth, { data, render: ({ data }) => <h1>{data.title}</h1> })
102
127
  */
103
128
  declare const definePage: DefinePageFn;
104
129
  /**
@@ -120,6 +145,11 @@ interface FactoryOptions<E extends Env = Env> {
120
145
  /** `initApp` stamped onto a `definePage` / `defineServerFn` result. */
121
146
  declare function readBoundInitApp(value: unknown): InitApp | undefined;
122
147
  declare function readPageModuleInitApp(pageDefault: unknown, data?: unknown): InitApp | undefined;
148
+ /**
149
+ * `initApp` as a single Hono middleware, for `createHandlers`-style lists.
150
+ * Skips if this `initApp` already ran on `c` (e.g. via `createApp()`).
151
+ */
152
+ declare function initAppMiddleware(initApp: InitApp): MiddlewareHandler;
123
153
  /**
124
154
  * Run a factory `initApp`'s `app.use()` middleware on the current context.
125
155
  * Skips if this `initApp` already ran on `c` (e.g. via `createApp()`).
@@ -136,52 +166,17 @@ declare class Factory<E extends Env = Env> {
136
166
  * before you call `mountVino(app)`.
137
167
  */
138
168
  createApp: (options?: HonoCtorOptions) => Hono<E>;
139
- /** Same as `definePage`, with `c` typed as `Context<E>` and this factory's `initApp`. */
169
+ /**
170
+ * Same as `definePage`, with `c` typed as `Context<E>`.
171
+ * Prepends this factory's `initApp` middleware onto the handler list.
172
+ */
140
173
  definePage: DefinePageFn<E>;
141
174
  /** Same as `defineServerPage`, with `c` typed as `Context<E>` and this factory's `initApp`. */
142
175
  defineServerPage: DefinePageFn<E>;
143
176
  /** Same as `defineServerFn`, with `c` typed as `Context<E>` and this factory's `initApp`. */
144
177
  defineServerFn: DefineServerFnFn<E>;
145
178
  }
146
- /**
147
- * Create a factory that binds `Env` onto page helpers and `createApp()`.
148
- *
149
- * Pages keep the `initApp` of the factory that defined them, so a blog factory
150
- * can set `c.var.db` only for `blogFactory.definePage` routes while the root
151
- * app still uses the global factory.
152
- *
153
- * @example
154
- * // src/factory.ts
155
- * export const factory = createFactory<Env>({
156
- * initApp: (app) => {
157
- * app.use(async (c, next) => {
158
- * c.set("db", drizzle(c.env.DB))
159
- * await next()
160
- * })
161
- * },
162
- * })
163
- * export const blogFactory = createFactory<BlogEnv>({
164
- * initApp: (app) => {
165
- * app.use(async (c, next) => {
166
- * c.set("posts", createPosts(c.env.BLOG_DB))
167
- * await next()
168
- * })
169
- * },
170
- * })
171
- *
172
- * @example
173
- * // src/server.ts
174
- * const app = factory.createApp()
175
- * mountVino(app)
176
- *
177
- * @example
178
- * // src/pages/blog/[slug].tsx
179
- * export default blogFactory.definePage({
180
- * data: (c) => c.var.posts.get(c.req.param("slug")),
181
- * render: ({ data }) => <h1>{data.title}</h1>,
182
- * })
183
- */
184
179
  declare function createFactory<E extends Env = Env>(options?: FactoryOptions<E>): Factory<E>;
185
180
  //#endregion
186
- export { defineServerFn as C, ServerFnHandler as S, readServerFn as T, isPageDefinition as _, createFactory as a, DefineServerFnFn as b, DefinePageFn as c, PageDataFn as d, PageDefinition as f, isClientPageDefinition as g, defineServerPage as h, applyInitApp as i, DefinePageOptions as l, definePage as m, FactoryOptions as n, readBoundInitApp as o, PageRender as p, InitApp as r, readPageModuleInitApp as s, Factory as t, InferDefinePageOptions as u, isServerPageDefinition as v, isServerFn as w, ServerFn as x, resolvePageRender as y };
187
- //# sourceMappingURL=factory-CVT0-OZw.d.mts.map
181
+ export { ServerFn as C, readServerFn as D, isServerFn as E, DefineServerFnFn as S, defineServerFn as T, defineServerPage as _, createFactory as a, isServerPageDefinition as b, readPageModuleInitApp as c, InferDefinePageOptions as d, PageDataFn as f, definePage as g, asHandlers as h, applyInitApp as i, DefinePageFn as l, PageRender as m, FactoryOptions as n, initAppMiddleware as o, PageDefinition as p, InitApp as r, readBoundInitApp as s, Factory as t, DefinePageOptions as u, isClientPageDefinition as v, ServerFnHandler as w, resolvePageRender as x, isPageDefinition as y };
182
+ //# sourceMappingURL=factory-Dxz3nh-p.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory-Dxz3nh-p.d.mts","names":[],"sources":["../src/vino/defineServerFn.ts","../src/vino/definePage.ts","../src/vino/factory.ts"],"mappings":";;;cAEa;KAED,gBAAgB,aAAa,UAAU,MAAM,QACvD,GAAG,QAAQ,eACR,IAAI,QAAQ;;;;KAKL,SAAS,aAAa,UAAU,MAAM,OAAO,gBAAgB,GAAG;WACjE,aAAa;;;;;;KAOZ,iBAAiB,UAAU,MAAM,QAAQ,GAAG,IAAI,gBAAgB,GAAG,OAAO,SAAS,GAAG;iBAElF,WAAW,iBAAiB,SAAS;iBAIrC,aAAa,iBAAiB;;;;;;;;;;;;;;;;;;;iBAgC9B,eAAe,GAAG,UAAU,MAAM,KAAK,IAAI,gBAAgB,GAAG,KAAK,SAAS,GAAG;;;cCrDlF;cACA;KAED,WAAW,mBAAmB,OAAO,UAAU,UAAU;KAEzD,WAAW,UAAU,MAAM,OAAO,WAAW,KAAK,kBAAkB;UAE/D,kBAAkB,gBAAgB,UAAU,MAAM;;EAEjE,SAAS,WAAW;EACpB,OAAO,WAAW;;EAElB,OAAO,WAAW,KAAK,SAAS,MAAM;EACtC,SAAS;;EAET,MAAM,kBAAkB;;;KAId,uBACV,cAAc,2BACd,UAAU,MAAM,KAChB,OAAO,QAAQ,WAAW;EAE1B,OAAO;EACP,SAAS;EACT,MAAM,kBAAkB;;EAEpB,QAAQ,WAAW;EAAO,OAAO,WAAW;;EAC5C,MAAM,WAAW;EAAO,SAAS,WAAW;;;;;;;;;;UAWjC,aAAa,UAAU,MAAM;GAC3C,gBAAgB,MAAM,WAAW,QAAQ,eAAe,MAAM;GAC9D,UAAU,WAAW,IAAI,SAAS,uBAAuB,GAAG,KAAK,eAAe,QAAQ,WAAW,KAAK;GACxG,gBAAgB,SAAS,kBAAkB,MAAM,KAAK,eAAe,MAAM;GAC3E,gBACC,YAAY,kBAAkB,IAC9B,MAAM,WAAW,QAAQ,kBAAkB,MAAM,KAChD,eAAe,MAAM;;UAGT,qBAAqB,gBAAgB,UAAU,MAAM;WAC3D,aAAa,mBAAmB;WAChC;WACA,QAAQ,WAAW;WACnB,OAAO,WAAW,KAAK,SAAS,MAAM;WACtC,SAAS;;;;;;;KAQR,eAAe,gBAAgB,UAAU,MAAM,QACzD,kBAAkB,OACf,kBAAkB,QAErB,qBAAqB,MAAM;;;;;iBAab,WAAW,MAAM,UAAU,KACzC,MAAM,eAAe,MAAM,MACzB,kBAAkB,OAAO,kBAAkB;iBA0D/B,iBAAiB,iBAAiB,SAAS;iBAS3C,uBAAuB,iBAAiB,SAAS;iBAIjD,uBAAuB,iBAAiB,SAAS;iBAQjD,kBAAkB,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;cAmFtC,YAAsF;;;;;;;cAQtF,kBACoD;;;;KCvOrD,QAAQ,UAAU,MAAM,QAAQ,KAAK,KAAK;KAEjD,kBAAkB,YAAY,6BAA6B;UAG/C,eAAe,UAAU,MAAM;EAC9C,UAAU,QAAQ;EAClB,oBAAoB;;;iBAiBN,iBAAiB,iBAAiB;iBAKlC,sBAAsB,sBAAsB,iBAAiB;;;;;iBAgC7D,kBAAkB,SAAS,UAAU;;;;;;iBAqB/B,aACpB,SAAS,qBACT,GAAG,SACH,YAAY,QAAQ,YACnB,QAAQ;cAgBE,QAAQ,UAAU,MAAM;UAC3B;UACA;EAEI,YAAA,OAAO,eAAe;;;;;;EAUlC,YAAa,UAAU,oBAAkB,KAAK;;;;;EAqB9C,YAAY,aAAa;;EAOzB,kBAAkB,aAAa;;EAO/B,gBAAgB,iBAAiB;;iBA4CnB,cAAc,UAAU,MAAM,KAAK,UAAU,eAAe,KAAK,QAAQ"}
@@ -1,2 +1,2 @@
1
- import { a as createFactory, i as applyInitApp, n as FactoryOptions, o as readBoundInitApp, r as InitApp, s as readPageModuleInitApp, t as Factory } from "./factory-CVT0-OZw.mjs";
2
- export { Factory, FactoryOptions, InitApp, applyInitApp, createFactory, readBoundInitApp, readPageModuleInitApp };
1
+ import { a as createFactory, c as readPageModuleInitApp, h as asHandlers, i as applyInitApp, n as FactoryOptions, o as initAppMiddleware, r as InitApp, s as readBoundInitApp, t as Factory } from "./factory-Dxz3nh-p.mjs";
2
+ export { Factory, FactoryOptions, InitApp, applyInitApp, asHandlers, createFactory, initAppMiddleware, readBoundInitApp, readPageModuleInitApp };
package/dist/factory.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as defineServerPage, o as readPageDefinition, t as definePage } from "./definePage--9Pz7rHs.mjs";
1
+ import { a as dispatchHandlers, i as defineServerPage, l as readPageDefinition, r as definePage, t as asHandlers } from "./definePage-CcqUZAiy.mjs";
2
2
  import { r as defineServerFn } from "./defineServerFn-H-bz7xBX.mjs";
3
3
  import { Hono } from "hono";
4
4
  //#region src/vino/factory.ts
@@ -50,27 +50,41 @@ function middlewareFromInitApp(initApp) {
50
50
  return list;
51
51
  }
52
52
  /**
53
+ * `initApp` as a single Hono middleware, for `createHandlers`-style lists.
54
+ * Skips if this `initApp` already ran on `c` (e.g. via `createApp()`).
55
+ */
56
+ function initAppMiddleware(initApp) {
57
+ return async (c, next) => {
58
+ if (isInitApplied(c, initApp)) {
59
+ await next();
60
+ return;
61
+ }
62
+ markInitApplied(c, initApp);
63
+ const handlers = middlewareFromInitApp(initApp);
64
+ if (handlers.length === 0) {
65
+ await next();
66
+ return;
67
+ }
68
+ await dispatchHandlers(handlers, c, next);
69
+ };
70
+ }
71
+ /**
53
72
  * Run a factory `initApp`'s `app.use()` middleware on the current context.
54
73
  * Skips if this `initApp` already ran on `c` (e.g. via `createApp()`).
55
74
  * Params and `c.set` stay on the same context so page loaders still see them.
56
75
  */
57
76
  async function applyInitApp(initApp, c, next) {
58
77
  if (!initApp || isInitApplied(c, initApp)) return next();
59
- markInitApplied(c, initApp);
60
- const handlers = middlewareFromInitApp(initApp);
61
- if (handlers.length === 0) return next();
62
- let response;
63
- const run = async (i) => {
64
- const handler = handlers[i];
65
- if (!handler) {
66
- response = await next();
67
- return;
68
- }
69
- const out = await handler(c, () => run(i + 1));
70
- if (out instanceof Response) response = out;
71
- };
72
- await run(0);
73
- return response ?? c.res;
78
+ await initAppMiddleware(initApp)(c, async () => {
79
+ const response = await next();
80
+ if (!c.finalized) c.res = response;
81
+ });
82
+ return c.res;
83
+ }
84
+ function withFactoryHandlers(def, initApp) {
85
+ bindInitApp(def, initApp);
86
+ if (initApp) def.unshift(initAppMiddleware(initApp));
87
+ return def;
74
88
  }
75
89
  var Factory = class {
76
90
  initApp;
@@ -99,55 +113,20 @@ var Factory = class {
99
113
  }
100
114
  return app;
101
115
  };
102
- /** Same as `definePage`, with `c` typed as `Context<E>` and this factory's `initApp`. */
103
- definePage = ((page) => bindInitApp(definePage(page), storedInitApp(this.initApp)));
116
+ /**
117
+ * Same as `definePage`, with `c` typed as `Context<E>`.
118
+ * Prepends this factory's `initApp` middleware onto the handler list.
119
+ */
120
+ definePage = ((...args) => withFactoryHandlers(definePage(...args), storedInitApp(this.initApp)));
104
121
  /** Same as `defineServerPage`, with `c` typed as `Context<E>` and this factory's `initApp`. */
105
- defineServerPage = ((page) => bindInitApp(defineServerPage(page), storedInitApp(this.initApp)));
122
+ defineServerPage = ((...args) => withFactoryHandlers(defineServerPage(...args), storedInitApp(this.initApp)));
106
123
  /** Same as `defineServerFn`, with `c` typed as `Context<E>` and this factory's `initApp`. */
107
124
  defineServerFn = ((fn) => bindInitApp(defineServerFn(fn), storedInitApp(this.initApp)));
108
125
  };
109
- /**
110
- * Create a factory that binds `Env` onto page helpers and `createApp()`.
111
- *
112
- * Pages keep the `initApp` of the factory that defined them, so a blog factory
113
- * can set `c.var.db` only for `blogFactory.definePage` routes while the root
114
- * app still uses the global factory.
115
- *
116
- * @example
117
- * // src/factory.ts
118
- * export const factory = createFactory<Env>({
119
- * initApp: (app) => {
120
- * app.use(async (c, next) => {
121
- * c.set("db", drizzle(c.env.DB))
122
- * await next()
123
- * })
124
- * },
125
- * })
126
- * export const blogFactory = createFactory<BlogEnv>({
127
- * initApp: (app) => {
128
- * app.use(async (c, next) => {
129
- * c.set("posts", createPosts(c.env.BLOG_DB))
130
- * await next()
131
- * })
132
- * },
133
- * })
134
- *
135
- * @example
136
- * // src/server.ts
137
- * const app = factory.createApp()
138
- * mountVino(app)
139
- *
140
- * @example
141
- * // src/pages/blog/[slug].tsx
142
- * export default blogFactory.definePage({
143
- * data: (c) => c.var.posts.get(c.req.param("slug")),
144
- * render: ({ data }) => <h1>{data.title}</h1>,
145
- * })
146
- */
147
126
  function createFactory(options) {
148
127
  return new Factory(options);
149
128
  }
150
129
  //#endregion
151
- export { Factory, applyInitApp, createFactory, readBoundInitApp, readPageModuleInitApp };
130
+ export { Factory, applyInitApp, asHandlers, createFactory, initAppMiddleware, readBoundInitApp, readPageModuleInitApp };
152
131
 
153
132
  //# sourceMappingURL=factory.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"factory.mjs","names":["definePageImpl","defineServerPageImpl","defineServerFnImpl"],"sources":["../src/vino/factory.ts"],"sourcesContent":["/**\n * Factory helper for Vino, analogous to `hono/factory`.\n *\n * Bind `Env` once so `definePage` / `defineServerPage` / `defineServerFn` type\n * `c` without blocking inference of loader return data. `createApp()` builds a\n * Hono instance with that `Env`. Each factory's `initApp` also runs for pages\n * defined with that factory, so multiple Envs can share one mounted app.\n */\n\nimport { Hono } from \"hono\";\nimport type { Context, Env } from \"hono\";\nimport {\n definePage as definePageImpl,\n defineServerPage as defineServerPageImpl,\n readPageDefinition,\n type DefinePageFn,\n} from \"./definePage.ts\";\nimport {\n defineServerFn as defineServerFnImpl,\n type DefineServerFnFn,\n type ServerFnHandler,\n} from \"./defineServerFn.ts\";\n\n/** Initialize every app created by `factory.createApp()`, and pages from `definePage`. */\nexport type InitApp<E extends Env = Env> = (app: Hono<E>) => void;\n\ntype HonoCtorOptions = NonNullable<ConstructorParameters<typeof Hono>[0]>;\ntype InitMiddleware = (c: Context, next: () => Promise<void>) => Promise<unknown>;\n\nexport interface FactoryOptions<E extends Env = Env> {\n initApp?: InitApp<E>;\n defaultAppOptions?: HonoCtorOptions;\n}\n\nconst boundInit = new WeakMap<object, InitApp>();\nconst appliedInit = new WeakMap<Context, Set<InitApp>>();\nconst middlewareCache = new WeakMap<InitApp, InitMiddleware[]>();\n\nfunction storedInitApp<E extends Env>(initApp: InitApp<E> | undefined): InitApp | undefined {\n return initApp as unknown as InitApp | undefined;\n}\n\nfunction bindInitApp<T extends object>(value: T, initApp?: InitApp): T {\n if (initApp) boundInit.set(value, initApp);\n return value;\n}\n\n/** `initApp` stamped onto a `definePage` / `defineServerFn` result. */\nexport function readBoundInitApp(value: unknown): InitApp | undefined {\n if (value === null || (typeof value !== \"object\" && typeof value !== \"function\")) return undefined;\n return boundInit.get(value);\n}\n\nexport function readPageModuleInitApp(pageDefault: unknown, data?: unknown): InitApp | undefined {\n return readBoundInitApp(pageDefault) ?? readBoundInitApp(data ?? readPageDefinition(pageDefault)?.data);\n}\n\nfunction markInitApplied(c: Context, initApp: InitApp): void {\n let set = appliedInit.get(c);\n if (!set) {\n set = new Set();\n appliedInit.set(c, set);\n }\n set.add(initApp);\n}\n\nfunction isInitApplied(c: Context, initApp: InitApp): boolean {\n return appliedInit.get(c)?.has(initApp) ?? false;\n}\n\nfunction middlewareFromInitApp(initApp: InitApp): InitMiddleware[] {\n let list = middlewareCache.get(initApp);\n if (!list) {\n const app = new Hono();\n initApp(app);\n list = app.routes.filter((route) => route.method === \"ALL\").map((route) => route.handler as InitMiddleware);\n middlewareCache.set(initApp, list);\n }\n return list;\n}\n\n/**\n * Run a factory `initApp`'s `app.use()` middleware on the current context.\n * Skips if this `initApp` already ran on `c` (e.g. via `createApp()`).\n * Params and `c.set` stay on the same context so page loaders still see them.\n */\nexport async function applyInitApp(\n initApp: InitApp | undefined,\n c: Context,\n next: () => Promise<Response>,\n): Promise<Response> {\n if (!initApp || isInitApplied(c, initApp)) return next();\n markInitApplied(c, initApp);\n const handlers = middlewareFromInitApp(initApp);\n if (handlers.length === 0) return next();\n\n let response: Response | undefined;\n const run = async (i: number): Promise<void> => {\n const handler = handlers[i];\n if (!handler) {\n response = await next();\n return;\n }\n const out = await handler(c, () => run(i + 1));\n if (out instanceof Response) response = out;\n };\n await run(0);\n return response ?? c.res;\n}\n\nexport class Factory<E extends Env = Env> {\n private initApp?: InitApp<E>;\n private defaultAppOptions?: HonoCtorOptions;\n\n constructor(init?: FactoryOptions<E>) {\n this.initApp = init?.initApp;\n this.defaultAppOptions = init?.defaultAppOptions;\n }\n\n /**\n * Create a Hono app typed with this factory's `Env`.\n * Runs `initApp` (if provided) before returning, so middleware is registered\n * before you call `mountVino(app)`.\n */\n createApp = (options?: HonoCtorOptions): Hono<E> => {\n const app = new Hono<E>(\n options && this.defaultAppOptions\n ? { ...this.defaultAppOptions, ...options }\n : (options ?? this.defaultAppOptions),\n );\n if (this.initApp) {\n const initApp = this.initApp;\n app.use(async (c, next) => {\n markInitApplied(c, initApp as unknown as InitApp);\n await next();\n });\n initApp(app);\n }\n return app;\n };\n\n /** Same as `definePage`, with `c` typed as `Context<E>` and this factory's `initApp`. */\n definePage: DefinePageFn<E> = ((page: never) =>\n bindInitApp(definePageImpl(page), storedInitApp(this.initApp))) as DefinePageFn<E>;\n\n /** Same as `defineServerPage`, with `c` typed as `Context<E>` and this factory's `initApp`. */\n defineServerPage: DefinePageFn<E> = ((page: never) =>\n bindInitApp(defineServerPageImpl(page), storedInitApp(this.initApp))) as DefinePageFn<E>;\n\n /** Same as `defineServerFn`, with `c` typed as `Context<E>` and this factory's `initApp`. */\n defineServerFn: DefineServerFnFn<E> = ((fn: ServerFnHandler<never, E>) =>\n bindInitApp(defineServerFnImpl(fn), storedInitApp(this.initApp))) as DefineServerFnFn<E>;\n}\n\n/**\n * Create a factory that binds `Env` onto page helpers and `createApp()`.\n *\n * Pages keep the `initApp` of the factory that defined them, so a blog factory\n * can set `c.var.db` only for `blogFactory.definePage` routes while the root\n * app still uses the global factory.\n *\n * @example\n * // src/factory.ts\n * export const factory = createFactory<Env>({\n * initApp: (app) => {\n * app.use(async (c, next) => {\n * c.set(\"db\", drizzle(c.env.DB))\n * await next()\n * })\n * },\n * })\n * export const blogFactory = createFactory<BlogEnv>({\n * initApp: (app) => {\n * app.use(async (c, next) => {\n * c.set(\"posts\", createPosts(c.env.BLOG_DB))\n * await next()\n * })\n * },\n * })\n *\n * @example\n * // src/server.ts\n * const app = factory.createApp()\n * mountVino(app)\n *\n * @example\n * // src/pages/blog/[slug].tsx\n * export default blogFactory.definePage({\n * data: (c) => c.var.posts.get(c.req.param(\"slug\")),\n * render: ({ data }) => <h1>{data.title}</h1>,\n * })\n */\nexport function createFactory<E extends Env = Env>(options?: FactoryOptions<E>): Factory<E> {\n return new Factory<E>(options);\n}\n"],"mappings":";;;;;;;;;;;;AAkCA,MAAM,4BAAY,IAAI,QAAyB;AAC/C,MAAM,8BAAc,IAAI,QAA+B;AACvD,MAAM,kCAAkB,IAAI,QAAmC;AAE/D,SAAS,cAA6B,SAAsD;CAC1F,OAAO;AACT;AAEA,SAAS,YAA8B,OAAU,SAAsB;CACrE,IAAI,SAAS,UAAU,IAAI,OAAO,OAAO;CACzC,OAAO;AACT;;AAGA,SAAgB,iBAAiB,OAAqC;CACpE,IAAI,UAAU,QAAS,OAAO,UAAU,YAAY,OAAO,UAAU,YAAa,OAAO,KAAA;CACzF,OAAO,UAAU,IAAI,KAAK;AAC5B;AAEA,SAAgB,sBAAsB,aAAsB,MAAqC;CAC/F,OAAO,iBAAiB,WAAW,KAAK,iBAAiB,QAAQ,mBAAmB,WAAW,CAAC,EAAE,IAAI;AACxG;AAEA,SAAS,gBAAgB,GAAY,SAAwB;CAC3D,IAAI,MAAM,YAAY,IAAI,CAAC;CAC3B,IAAI,CAAC,KAAK;EACR,sBAAM,IAAI,IAAI;EACd,YAAY,IAAI,GAAG,GAAG;CACxB;CACA,IAAI,IAAI,OAAO;AACjB;AAEA,SAAS,cAAc,GAAY,SAA2B;CAC5D,OAAO,YAAY,IAAI,CAAC,CAAC,EAAE,IAAI,OAAO,KAAK;AAC7C;AAEA,SAAS,sBAAsB,SAAoC;CACjE,IAAI,OAAO,gBAAgB,IAAI,OAAO;CACtC,IAAI,CAAC,MAAM;EACT,MAAM,MAAM,IAAI,KAAK;EACrB,QAAQ,GAAG;EACX,OAAO,IAAI,OAAO,QAAQ,UAAU,MAAM,WAAW,KAAK,CAAC,CAAC,KAAK,UAAU,MAAM,OAAyB;EAC1G,gBAAgB,IAAI,SAAS,IAAI;CACnC;CACA,OAAO;AACT;;;;;;AAOA,eAAsB,aACpB,SACA,GACA,MACmB;CACnB,IAAI,CAAC,WAAW,cAAc,GAAG,OAAO,GAAG,OAAO,KAAK;CACvD,gBAAgB,GAAG,OAAO;CAC1B,MAAM,WAAW,sBAAsB,OAAO;CAC9C,IAAI,SAAS,WAAW,GAAG,OAAO,KAAK;CAEvC,IAAI;CACJ,MAAM,MAAM,OAAO,MAA6B;EAC9C,MAAM,UAAU,SAAS;EACzB,IAAI,CAAC,SAAS;GACZ,WAAW,MAAM,KAAK;GACtB;EACF;EACA,MAAM,MAAM,MAAM,QAAQ,SAAS,IAAI,IAAI,CAAC,CAAC;EAC7C,IAAI,eAAe,UAAU,WAAW;CAC1C;CACA,MAAM,IAAI,CAAC;CACX,OAAO,YAAY,EAAE;AACvB;AAEA,IAAa,UAAb,MAA0C;CACxC;CACA;CAEA,YAAY,MAA0B;EACpC,KAAK,UAAU,MAAM;EACrB,KAAK,oBAAoB,MAAM;CACjC;;;;;;CAOA,aAAa,YAAuC;EAClD,MAAM,MAAM,IAAI,KACd,WAAW,KAAK,oBACZ;GAAE,GAAG,KAAK;GAAmB,GAAG;EAAQ,IACvC,WAAW,KAAK,iBACvB;EACA,IAAI,KAAK,SAAS;GAChB,MAAM,UAAU,KAAK;GACrB,IAAI,IAAI,OAAO,GAAG,SAAS;IACzB,gBAAgB,GAAG,OAA6B;IAChD,MAAM,KAAK;GACb,CAAC;GACD,QAAQ,GAAG;EACb;EACA,OAAO;CACT;;CAGA,eAAgC,SAC9B,YAAYA,WAAe,IAAI,GAAG,cAAc,KAAK,OAAO,CAAC;;CAG/D,qBAAsC,SACpC,YAAYC,iBAAqB,IAAI,GAAG,cAAc,KAAK,OAAO,CAAC;;CAGrE,mBAAwC,OACtC,YAAYC,eAAmB,EAAE,GAAG,cAAc,KAAK,OAAO,CAAC;AACnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAgB,cAAmC,SAAyC;CAC1F,OAAO,IAAI,QAAW,OAAO;AAC/B"}
1
+ {"version":3,"file":"factory.mjs","names":["definePageImpl","defineServerPageImpl","defineServerFnImpl"],"sources":["../src/vino/factory.ts"],"sourcesContent":["/**\n * Factory helper for Vino, analogous to `hono/factory`.\n *\n * Bind `Env` once so `definePage` / `defineServerPage` / `defineServerFn` type\n * `c` without blocking inference of loader return data. `createApp()` builds a\n * Hono instance with that `Env`. Each factory's `initApp` also runs for pages\n * defined with that factory, so multiple Envs can share one mounted app.\n */\n\nimport { Hono } from \"hono\";\nimport type { Context, Env, MiddlewareHandler } from \"hono\";\nimport {\n definePage as definePageImpl,\n defineServerPage as defineServerPageImpl,\n dispatchHandlers,\n readPageDefinition,\n type DefinePageFn,\n type PageDefinition,\n} from \"./definePage.ts\";\nimport {\n defineServerFn as defineServerFnImpl,\n type DefineServerFnFn,\n type ServerFnHandler,\n} from \"./defineServerFn.ts\";\n\n/** Initialize every app created by `factory.createApp()`, and pages from `definePage`. */\nexport type InitApp<E extends Env = Env> = (app: Hono<E>) => void;\n\ntype HonoCtorOptions = NonNullable<ConstructorParameters<typeof Hono>[0]>;\ntype InitMiddleware = (c: Context, next: () => Promise<void>) => Promise<unknown>;\n\nexport interface FactoryOptions<E extends Env = Env> {\n initApp?: InitApp<E>;\n defaultAppOptions?: HonoCtorOptions;\n}\n\nconst boundInit = new WeakMap<object, InitApp>();\nconst appliedInit = new WeakMap<Context, Set<InitApp>>();\nconst middlewareCache = new WeakMap<InitApp, InitMiddleware[]>();\n\nfunction storedInitApp<E extends Env>(initApp: InitApp<E> | undefined): InitApp | undefined {\n return initApp as unknown as InitApp | undefined;\n}\n\nfunction bindInitApp<T extends object>(value: T, initApp?: InitApp): T {\n if (initApp) boundInit.set(value, initApp);\n return value;\n}\n\n/** `initApp` stamped onto a `definePage` / `defineServerFn` result. */\nexport function readBoundInitApp(value: unknown): InitApp | undefined {\n if (value === null || (typeof value !== \"object\" && typeof value !== \"function\")) return undefined;\n return boundInit.get(value);\n}\n\nexport function readPageModuleInitApp(pageDefault: unknown, data?: unknown): InitApp | undefined {\n return readBoundInitApp(pageDefault) ?? readBoundInitApp(data ?? readPageDefinition(pageDefault)?.data);\n}\n\nfunction markInitApplied(c: Context, initApp: InitApp): void {\n let set = appliedInit.get(c);\n if (!set) {\n set = new Set();\n appliedInit.set(c, set);\n }\n set.add(initApp);\n}\n\nfunction isInitApplied(c: Context, initApp: InitApp): boolean {\n return appliedInit.get(c)?.has(initApp) ?? false;\n}\n\nfunction middlewareFromInitApp(initApp: InitApp): InitMiddleware[] {\n let list = middlewareCache.get(initApp);\n if (!list) {\n const app = new Hono();\n initApp(app);\n list = app.routes.filter((route) => route.method === \"ALL\").map((route) => route.handler as InitMiddleware);\n middlewareCache.set(initApp, list);\n }\n return list;\n}\n\n/**\n * `initApp` as a single Hono middleware, for `createHandlers`-style lists.\n * Skips if this `initApp` already ran on `c` (e.g. via `createApp()`).\n */\nexport function initAppMiddleware(initApp: InitApp): MiddlewareHandler {\n return async (c, next) => {\n if (isInitApplied(c, initApp)) {\n await next();\n return;\n }\n markInitApplied(c, initApp);\n const handlers = middlewareFromInitApp(initApp);\n if (handlers.length === 0) {\n await next();\n return;\n }\n await dispatchHandlers(handlers as MiddlewareHandler[], c, next);\n };\n}\n\n/**\n * Run a factory `initApp`'s `app.use()` middleware on the current context.\n * Skips if this `initApp` already ran on `c` (e.g. via `createApp()`).\n * Params and `c.set` stay on the same context so page loaders still see them.\n */\nexport async function applyInitApp(\n initApp: InitApp | undefined,\n c: Context,\n next: () => Promise<Response>,\n): Promise<Response> {\n if (!initApp || isInitApplied(c, initApp)) return next();\n const middleware: InitMiddleware = initAppMiddleware(initApp);\n await middleware(c, async () => {\n const response = await next();\n if (!c.finalized) c.res = response;\n });\n return c.res;\n}\n\nfunction withFactoryHandlers<T extends PageDefinition>(def: T, initApp?: InitApp): T {\n bindInitApp(def, initApp);\n if (initApp) def.unshift(initAppMiddleware(initApp));\n return def;\n}\n\nexport class Factory<E extends Env = Env> {\n private initApp?: InitApp<E>;\n private defaultAppOptions?: HonoCtorOptions;\n\n constructor(init?: FactoryOptions<E>) {\n this.initApp = init?.initApp;\n this.defaultAppOptions = init?.defaultAppOptions;\n }\n\n /**\n * Create a Hono app typed with this factory's `Env`.\n * Runs `initApp` (if provided) before returning, so middleware is registered\n * before you call `mountVino(app)`.\n */\n createApp = (options?: HonoCtorOptions): Hono<E> => {\n const app = new Hono<E>(\n options && this.defaultAppOptions\n ? { ...this.defaultAppOptions, ...options }\n : (options ?? this.defaultAppOptions),\n );\n if (this.initApp) {\n const initApp = this.initApp;\n app.use(async (c, next) => {\n markInitApplied(c, initApp as unknown as InitApp);\n await next();\n });\n initApp(app);\n }\n return app;\n };\n\n /**\n * Same as `definePage`, with `c` typed as `Context<E>`.\n * Prepends this factory's `initApp` middleware onto the handler list.\n */\n definePage: DefinePageFn<E> = ((...args: unknown[]) =>\n withFactoryHandlers(\n (definePageImpl as (...a: unknown[]) => PageDefinition)(...args),\n storedInitApp(this.initApp),\n )) as unknown as DefinePageFn<E>;\n\n /** Same as `defineServerPage`, with `c` typed as `Context<E>` and this factory's `initApp`. */\n defineServerPage: DefinePageFn<E> = ((...args: unknown[]) =>\n withFactoryHandlers(\n (defineServerPageImpl as (...a: unknown[]) => PageDefinition)(...args),\n storedInitApp(this.initApp),\n )) as unknown as DefinePageFn<E>;\n\n /** Same as `defineServerFn`, with `c` typed as `Context<E>` and this factory's `initApp`. */\n defineServerFn: DefineServerFnFn<E> = ((fn: ServerFnHandler<never, E>) =>\n bindInitApp(defineServerFnImpl(fn), storedInitApp(this.initApp))) as DefineServerFnFn<E>;\n}\n\n/**\n * Create a factory that binds `Env` onto page helpers and `createApp()`.\n *\n * Pages keep the `initApp` of the factory that defined them, so a blog factory\n * can set `c.var.db` only for `blogFactory.definePage` routes while the root\n * app still uses the global factory.\n *\n * @example\n * // src/factory.ts\n * export const factory = createFactory<Env>({\n * initApp: (app) => {\n * app.use(async (c, next) => {\n * c.set(\"db\", drizzle(c.env.DB))\n * await next()\n * })\n * },\n * })\n * export const blogFactory = createFactory<BlogEnv>({\n * initApp: (app) => {\n * app.use(async (c, next) => {\n * c.set(\"posts\", createPosts(c.env.BLOG_DB))\n * await next()\n * })\n * },\n * })\n *\n * @example\n * // src/server.ts\n * const app = factory.createApp()\n * mountVino(app)\n *\n * @example\n * // src/pages/blog/[slug].tsx\n * export default blogFactory.definePage({\n * data: (c) => c.var.posts.get(c.req.param(\"slug\")),\n * render: ({ data }) => <h1>{data.title}</h1>,\n * })\n */\nexport { asHandlers } from \"./definePage.ts\";\n\nexport function createFactory<E extends Env = Env>(options?: FactoryOptions<E>): Factory<E> {\n return new Factory<E>(options);\n}\n"],"mappings":"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"}
package/dist/vino.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { a as PageConfig, d as VinoViteOptions, i as ManifestPage, l as VinoManifest, o as PageProps, r as LayoutProps, s as RenderMode, t as CreateVinoOptions, u as VinoPayload } from "./types-CQITdkpF.mjs";
2
2
  import { wrapComponent, wrapModule } from "./component-wrap.mjs";
3
- import { C as defineServerFn, S as ServerFnHandler, T as readServerFn, _ as isPageDefinition, a as createFactory, b as DefineServerFnFn, c as DefinePageFn, d as PageDataFn, f as PageDefinition, g as isClientPageDefinition, h as defineServerPage, l as DefinePageOptions, m as definePage, n as FactoryOptions, p as PageRender, r as InitApp, t as Factory, u as InferDefinePageOptions, v as isServerPageDefinition, w as isServerFn, x as ServerFn, y as resolvePageRender } from "./factory-CVT0-OZw.mjs";
3
+ import { C as ServerFn, D as readServerFn, E as isServerFn, S as DefineServerFnFn, T as defineServerFn, _ as defineServerPage, a as createFactory, b as isServerPageDefinition, d as InferDefinePageOptions, f as PageDataFn, g as definePage, h as asHandlers, l as DefinePageFn, m as PageRender, n as FactoryOptions, p as PageDefinition, r as InitApp, t as Factory, u as DefinePageOptions, v as isClientPageDefinition, w as ServerFnHandler, x as resolvePageRender, y as isPageDefinition } from "./factory-Dxz3nh-p.mjs";
4
4
  import { Env, Hono, Schema } from "hono";
5
5
  import { BlankSchema } from "hono/types";
6
6
  //#region src/vino/createVino.d.ts
@@ -41,5 +41,5 @@ declare const COMPONENT_TAG = "vino-component";
41
41
  declare const NAV_HEADER = "X-Vino-Nav";
42
42
  declare const SHELL_HEADER = "X-Vino-Shell";
43
43
  //#endregion
44
- export { COMPONENT_TAG, type CreateVinoOptions, type DefinePageFn, type DefinePageOptions, type DefineServerFnFn, Factory, type FactoryOptions, type InferDefinePageOptions, type InitApp, type LayoutProps, type ManifestPage, NAV_HEADER, PAGE_SLOT_ID, PAYLOAD_SCRIPT_ID, type PageConfig, type PageDataFn, type PageDefinition, type PageProps, type PageRender, type RenderMode, SHELL_HEADER, type ServerFn, type ServerFnHandler, type VinoManifest, type VinoPayload, type VinoViteOptions, applyParams, compileFilePath, createFactory, createVino, definePage, defineServerFn, defineServerPage, isClientPageDefinition, isPageDefinition, isServerFn, isServerPageDefinition, mountVino, readServerFn, resolvePageRender, wrapComponent, wrapModule };
44
+ export { COMPONENT_TAG, type CreateVinoOptions, type DefinePageFn, type DefinePageOptions, type DefineServerFnFn, Factory, type FactoryOptions, type InferDefinePageOptions, type InitApp, type LayoutProps, type ManifestPage, NAV_HEADER, PAGE_SLOT_ID, PAYLOAD_SCRIPT_ID, type PageConfig, type PageDataFn, type PageDefinition, type PageProps, type PageRender, type RenderMode, SHELL_HEADER, type ServerFn, type ServerFnHandler, type VinoManifest, type VinoPayload, type VinoViteOptions, applyParams, asHandlers, compileFilePath, createFactory, createVino, definePage, defineServerFn, defineServerPage, isClientPageDefinition, isPageDefinition, isServerFn, isServerPageDefinition, mountVino, readServerFn, resolvePageRender, wrapComponent, wrapModule };
45
45
  //# sourceMappingURL=vino.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vino.d.mts","names":[],"sources":["../src/vino/createVino.ts","../src/vino/compile.ts","../src/vino/constants.ts"],"mappings":";;;;;;;;;;iBAqGgB,UAAU,UAAU,KAAK,UAAU,QAAQ,yBAAyB,KAAK,KAAK,GAAG,GAAG,WAAW,UAAS,oBAAyB,KAAK,GAAG,GAAG;;;;;;iBAc5I,WAAW,UAAU,MAAM,KAAK,UAAU,SAAS,aAAa,+BAA+B,UAAS,oBAAyB,KAAK,GAAG,GAAG;;;UCnH3I;;EAEf;;EAEA;EACA;EACA;EACA;;;;;;;iBAoBc,gBAAgB,uBAAuB;iBA8DvC,YAAY,cAAc,QAAQ;;;cCzFrC;cACA;cACA;cAKA;cACA"}
1
+ {"version":3,"file":"vino.d.mts","names":[],"sources":["../src/vino/createVino.ts","../src/vino/compile.ts","../src/vino/constants.ts"],"mappings":";;;;;;;;;;iBAiHgB,UAAU,UAAU,KAAK,UAAU,QAAQ,yBAAyB,KAAK,KAAK,GAAG,GAAG,WAAW,UAAS,oBAAyB,KAAK,GAAG,GAAG;;;;;;iBAc5I,WAAW,UAAU,MAAM,KAAK,UAAU,SAAS,aAAa,+BAA+B,UAAS,oBAAyB,KAAK,GAAG,GAAG;;;UC/H3I;;EAEf;;EAEA;EACA;EACA;EACA;;;;;;;iBAoBc,gBAAgB,uBAAuB;iBA8DvC,YAAY,cAAc,QAAQ;;;cCzFrC;cACA;cACA;cAKA;cACA"}
package/dist/vino.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { c as PAGE_SLOT_ID, l as PAYLOAD_SCRIPT_ID, o as NAV_HEADER, t as COMPONENT_TAG, u as SHELL_HEADER } from "./constants-BVT2bJEd.mjs";
2
- import { a as isServerPageDefinition, i as isPageDefinition, n as defineServerPage, r as isClientPageDefinition, s as resolvePageRender, t as definePage } from "./definePage--9Pz7rHs.mjs";
3
- import { c as loadMergedData, r as compileFilePath, s as loadMergedConfig, t as applyParams } from "./compile-glNMkyyx.mjs";
2
+ import { a as dispatchHandlers, c as isServerPageDefinition, i as defineServerPage, l as readPageDefinition, n as bindPageRun, o as isClientPageDefinition, r as definePage, s as isPageDefinition, t as asHandlers, u as resolvePageRender } from "./definePage-CcqUZAiy.mjs";
3
+ import { c as loadMergedData, r as compileFilePath, s as loadMergedConfig, t as applyParams } from "./compile-BGVCPc4H.mjs";
4
4
  import { a as readServerFn, i as isServerFn, r as defineServerFn } from "./defineServerFn-H-bz7xBX.mjs";
5
- import { Factory, applyInitApp, createFactory, readPageModuleInitApp } from "./factory.mjs";
5
+ import { Factory, applyInitApp, createFactory, initAppMiddleware, readBoundInitApp, readPageModuleInitApp } from "./factory.mjs";
6
6
  import { wrapComponent, wrapModule } from "./component-wrap.mjs";
7
7
  import { Hono } from "hono";
8
8
  import { jsxDEV } from "hono/jsx/jsx-dev-runtime";
@@ -130,6 +130,13 @@ function resolveRenderMode(c) {
130
130
  }
131
131
  async function handlePage(c, page) {
132
132
  const pageMod = await page.loadPage();
133
+ const def = readPageDefinition(pageMod.default);
134
+ if (def) {
135
+ bindPageRun(c, () => handlePageBody(c, page, pageMod));
136
+ const init = readBoundInitApp(def) ? void 0 : readPageModuleInitApp(pageMod.default);
137
+ const handlers = init ? [initAppMiddleware(init), ...def] : def;
138
+ return dispatchHandlers(handlers, c);
139
+ }
133
140
  return applyInitApp(readPageModuleInitApp(pageMod.default), c, () => handlePageBody(c, page, pageMod));
134
141
  }
135
142
  async function handlePageBody(c, page, pageMod) {
@@ -197,6 +204,6 @@ function createVino(options = {}) {
197
204
  return mountVino(new Hono(), options);
198
205
  }
199
206
  //#endregion
200
- export { COMPONENT_TAG, Factory, NAV_HEADER, PAGE_SLOT_ID, PAYLOAD_SCRIPT_ID, SHELL_HEADER, applyParams, compileFilePath, createFactory, createVino, definePage, defineServerFn, defineServerPage, isClientPageDefinition, isPageDefinition, isServerFn, isServerPageDefinition, mountVino, readServerFn, resolvePageRender, wrapComponent, wrapModule };
207
+ export { COMPONENT_TAG, Factory, NAV_HEADER, PAGE_SLOT_ID, PAYLOAD_SCRIPT_ID, SHELL_HEADER, applyParams, asHandlers, compileFilePath, createFactory, createVino, definePage, defineServerFn, defineServerPage, isClientPageDefinition, isPageDefinition, isServerFn, isServerPageDefinition, mountVino, readServerFn, resolvePageRender, wrapComponent, wrapModule };
201
208
 
202
209
  //# sourceMappingURL=vino.mjs.map
package/dist/vino.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"vino.mjs","names":["jsx","jsx","assetsIsDev","manifest","virtualManifest"],"sources":["../src/vino/render.ts","../src/vino/createVino.ts"],"sourcesContent":["import { Hono } from \"hono\";\nimport type { Context } from \"hono\";\nimport type { Child } from \"hono/jsx\";\nimport { jsxDEV as jsx } from \"hono/jsx/jsx-dev-runtime\";\nimport { jsxRenderer } from \"hono/jsx-renderer\";\nimport { PAGE_SLOT_ID, PAYLOAD_SCRIPT_ID } from \"./constants.ts\";\nimport type { ManifestPage, PageConfig, PageModule, RenderMode, VinoPayload } from \"./types.ts\";\n\nfunction DefaultLayout(props: { children?: Child }) {\n return jsx(\"html\", {\n children: [\n jsx(\"head\", {\n children: [\n jsx(\"meta\", { charset: \"utf-8\" }),\n jsx(\"meta\", { name: \"viewport\", content: \"width=device-width, initial-scale=1\" }),\n ],\n }),\n jsx(\"body\", { children: props.children }),\n ],\n });\n}\n\nexport function isHonoApp(value: unknown): value is Hono {\n return value instanceof Hono;\n}\n\nfunction payloadScript(payload: VinoPayload): string {\n const json = JSON.stringify(payload).replaceAll(\"<\", \"\\\\u003c\");\n return `<script id=\"${PAYLOAD_SCRIPT_ID}\" type=\"application/json\">${json}</script>`;\n}\n\nexport function injectDocument(\n html: string,\n payload: VinoPayload,\n isDev: boolean,\n scriptSrc: string,\n): string {\n const vite = isDev ? `<script type=\"module\" src=\"/@vite/client\"></script>` : \"\";\n const client = `<script type=\"module\" src=\"${scriptSrc}\"></script>`;\n const inject = `${vite}${payloadScript(payload)}${client}`;\n if (html.includes(\"</body>\")) return html.replace(\"</body>\", `${inject}</body>`);\n if (html.includes(\"</html>\")) return html.replace(\"</html>\", `${inject}</html>`);\n return `<!DOCTYPE html>${html}${inject}`;\n}\n\nasync function ensureJsxRenderer(c: Context): Promise<void> {\n await jsxRenderer(undefined, { docType: true })(c, async () => {});\n}\n\nfunction needsPageSlot(page: ManifestPage, config: PageConfig, mode: RenderMode): boolean {\n if (mode === \"shell\" || mode === \"content\") return true;\n if (page.isClientPage) return true;\n if (config.clientRouting === true) return true;\n if (config.prerender === \"partial\") return true;\n return false;\n}\n\nfunction emptySlot(): Child {\n return jsx(\"div\", { id: PAGE_SLOT_ID });\n}\n\nfunction wrapSlot(children: Child): Child {\n return jsx(\"div\", { id: PAGE_SLOT_ID, children });\n}\n\nfunction buildPayload(options: {\n page: ManifestPage;\n params: Record<string, string>;\n data: unknown;\n config: PageConfig;\n mode: RenderMode;\n requestPath?: string;\n title?: string;\n}): VinoPayload {\n const { page, params, data, config, mode, requestPath, title } = options;\n const path = requestPath ?? (page.path === \"\" ? \"/\" : page.path);\n return {\n path,\n params,\n data,\n clientRouting: config.clientRouting === true,\n clientPage: page.isClientPage ? page.path : null,\n partial: mode === \"shell\",\n title,\n };\n}\n\nexport async function renderPageHtml(options: {\n c: Context;\n page: ManifestPage;\n pageMod: PageModule;\n pageNode?: Child | null;\n data: unknown;\n params: Record<string, string>;\n config: PageConfig;\n isDev: boolean;\n clientScriptSrc: string;\n mode?: RenderMode;\n title?: string;\n}): Promise<{ html: string; payload: VinoPayload }> {\n const {\n c,\n page,\n pageNode = null,\n data,\n params,\n config,\n isDev,\n clientScriptSrc,\n mode = \"full\",\n title,\n } = options;\n\n const payload = buildPayload({\n page,\n params,\n data,\n config,\n mode,\n requestPath: c.req.path,\n title,\n });\n\n if (mode === \"content\") {\n if (pageNode == null) throw new Error(\"content render requires pageNode\");\n const node = wrapSlot(pageNode);\n await ensureJsxRenderer(c);\n const rendered = await c.render(node as never);\n let html = await rendered.text();\n html = html.replace(/^<!DOCTYPE html>/i, \"\");\n html = `${html}${payloadScript(payload)}`;\n return { html, payload };\n }\n\n let node: Child = mode === \"shell\" ? emptySlot() : (pageNode as Child);\n if (mode !== \"shell\" && needsPageSlot(page, config, mode)) {\n node = wrapSlot(node);\n }\n\n const layouts = await Promise.all(page.loadLayouts.map((load) => load()));\n for (const layout of [...layouts].reverse()) {\n const Layout = layout.default;\n node = jsx(Layout, { data, params, children: node });\n }\n if (layouts.length === 0) {\n node = jsx(DefaultLayout, { children: node });\n }\n\n await ensureJsxRenderer(c);\n const rendered = await c.render(node as never);\n let html = await rendered.text();\n\n html = injectDocument(html, payload, isDev, clientScriptSrc);\n if (!html.startsWith(\"<!\")) html = `<!DOCTYPE html>${html}`;\n return { html, payload };\n}\n","import { Hono } from \"hono\";\nimport type { Context, Env, Schema } from \"hono\";\nimport type { BlankSchema } from \"hono/types\";\nimport { jsxDEV as jsx } from \"hono/jsx/jsx-dev-runtime\";\nimport { manifest as virtualManifest } from \"virtual:vino/manifest\";\nimport { isDev as assetsIsDev, clientScriptSrc } from \"virtual:vino/assets\";\nimport { NAV_HEADER, SHELL_HEADER } from \"./constants.ts\";\nimport { loadMergedConfig, loadMergedData } from \"./config.ts\";\nimport { resolvePageRender } from \"./definePage.ts\";\nimport { applyInitApp, readPageModuleInitApp } from \"./factory.ts\";\nimport { isHonoApp, renderPageHtml } from \"./render.ts\";\nimport type { CreateVinoOptions, ManifestPage, PageModule, RenderMode } from \"./types.ts\";\n\ntype AppContext = Context;\n\nfunction requestParams(c: AppContext): Record<string, string> {\n try {\n return c.req.param();\n } catch {\n return {};\n }\n}\n\nfunction pageFromModule(pageMod: PageModule, data: unknown, params: Record<string, string>) {\n const Page = resolvePageRender(pageMod.default);\n if (Page) return jsx(Page, { data, params });\n return pageMod.default as never;\n}\n\nfunction resolveRenderMode(c: AppContext): RenderMode {\n if (c.req.header(SHELL_HEADER)) return \"shell\";\n if (c.req.header(NAV_HEADER)) return \"content\";\n return \"full\";\n}\n\nasync function handlePage(c: AppContext, page: ManifestPage): Promise<Response> {\n const pageMod = await page.loadPage();\n return applyInitApp(readPageModuleInitApp(pageMod.default), c, () => handlePageBody(c, page, pageMod));\n}\n\nasync function handlePageBody(c: AppContext, page: ManifestPage, pageMod: PageModule): Promise<Response> {\n if (isHonoApp(pageMod.default)) {\n return pageMod.default.fetch(c.req.raw, c.env, c.executionCtx);\n }\n\n const method = c.req.method;\n const named = pageMod[method];\n const hasPage = Boolean(resolvePageRender(pageMod.default));\n const mode = resolveRenderMode(c);\n\n if (typeof named === \"function\" && (method !== \"GET\" || !hasPage)) {\n const result: unknown = await (named as (ctx: AppContext) => unknown)(c);\n if (result instanceof Response) return result;\n if (method === \"GET\" || method === \"HEAD\") {\n const config = loadMergedConfig(page, pageMod);\n const params = requestParams(c);\n const data = mode === \"shell\" ? undefined : await loadMergedData(page, c, pageMod);\n const { html } = await renderPageHtml({\n c,\n page,\n pageMod,\n pageNode: mode === \"shell\" ? null : (result as never),\n data,\n params,\n config,\n isDev: assetsIsDev,\n clientScriptSrc,\n mode,\n });\n return c.html(html);\n }\n return c.json(result as never);\n }\n\n if (method !== \"GET\" && method !== \"HEAD\") {\n return c.text(\"Method Not Allowed\", 405);\n }\n\n const config = loadMergedConfig(page, pageMod);\n const params = requestParams(c);\n const data = mode === \"shell\" ? undefined : await loadMergedData(page, c, pageMod);\n const pageNode = mode === \"shell\" ? null : pageFromModule(pageMod, data, params);\n const { html } = await renderPageHtml({\n c,\n page,\n pageMod,\n pageNode: pageNode,\n data,\n params,\n config,\n isDev: assetsIsDev,\n clientScriptSrc,\n mode,\n });\n return c.html(html);\n}\n\n/**\n * Mount filesystem pages from the Vite-generated manifest onto a user-owned Hono app.\n * Extra API routes should be registered on `app` before calling this.\n */\nexport function mountVino<E extends Env, S extends Schema, BasePath extends string>(app: Hono<E, S, BasePath>, options: CreateVinoOptions = {}): Hono<E, S, BasePath> {\n const manifest = options.manifest ?? virtualManifest;\n for (const page of manifest.pages) {\n app.get(page.pattern, (c) => handlePage(c as AppContext, page));\n }\n\n return app;\n}\n\n/**\n * Create a new Vino app. Returns a Hono app with the Vino pages mounted.\n * @param options - The options for the Vino app.\n * @returns A Hono app with the Vino pages mounted.\n */\nexport function createVino<E extends Env = Env, S extends Schema = BlankSchema, BasePath extends string = \"/\">(options: CreateVinoOptions = {}): Hono<E, S, BasePath> {\n const app = new Hono<E, S, BasePath>();\n return mountVino(app, options);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"vino.mjs","names":["jsx","jsx","assetsIsDev","manifest","virtualManifest"],"sources":["../src/vino/render.ts","../src/vino/createVino.ts"],"sourcesContent":["import { Hono } from \"hono\";\nimport type { Context } from \"hono\";\nimport type { Child } from \"hono/jsx\";\nimport { jsxDEV as jsx } from \"hono/jsx/jsx-dev-runtime\";\nimport { jsxRenderer } from \"hono/jsx-renderer\";\nimport { PAGE_SLOT_ID, PAYLOAD_SCRIPT_ID } from \"./constants.ts\";\nimport type { ManifestPage, PageConfig, PageModule, RenderMode, VinoPayload } from \"./types.ts\";\n\nfunction DefaultLayout(props: { children?: Child }) {\n return jsx(\"html\", {\n children: [\n jsx(\"head\", {\n children: [\n jsx(\"meta\", { charset: \"utf-8\" }),\n jsx(\"meta\", { name: \"viewport\", content: \"width=device-width, initial-scale=1\" }),\n ],\n }),\n jsx(\"body\", { children: props.children }),\n ],\n });\n}\n\nexport function isHonoApp(value: unknown): value is Hono {\n return value instanceof Hono;\n}\n\nfunction payloadScript(payload: VinoPayload): string {\n const json = JSON.stringify(payload).replaceAll(\"<\", \"\\\\u003c\");\n return `<script id=\"${PAYLOAD_SCRIPT_ID}\" type=\"application/json\">${json}</script>`;\n}\n\nexport function injectDocument(\n html: string,\n payload: VinoPayload,\n isDev: boolean,\n scriptSrc: string,\n): string {\n const vite = isDev ? `<script type=\"module\" src=\"/@vite/client\"></script>` : \"\";\n const client = `<script type=\"module\" src=\"${scriptSrc}\"></script>`;\n const inject = `${vite}${payloadScript(payload)}${client}`;\n if (html.includes(\"</body>\")) return html.replace(\"</body>\", `${inject}</body>`);\n if (html.includes(\"</html>\")) return html.replace(\"</html>\", `${inject}</html>`);\n return `<!DOCTYPE html>${html}${inject}`;\n}\n\nasync function ensureJsxRenderer(c: Context): Promise<void> {\n await jsxRenderer(undefined, { docType: true })(c, async () => {});\n}\n\nfunction needsPageSlot(page: ManifestPage, config: PageConfig, mode: RenderMode): boolean {\n if (mode === \"shell\" || mode === \"content\") return true;\n if (page.isClientPage) return true;\n if (config.clientRouting === true) return true;\n if (config.prerender === \"partial\") return true;\n return false;\n}\n\nfunction emptySlot(): Child {\n return jsx(\"div\", { id: PAGE_SLOT_ID });\n}\n\nfunction wrapSlot(children: Child): Child {\n return jsx(\"div\", { id: PAGE_SLOT_ID, children });\n}\n\nfunction buildPayload(options: {\n page: ManifestPage;\n params: Record<string, string>;\n data: unknown;\n config: PageConfig;\n mode: RenderMode;\n requestPath?: string;\n title?: string;\n}): VinoPayload {\n const { page, params, data, config, mode, requestPath, title } = options;\n const path = requestPath ?? (page.path === \"\" ? \"/\" : page.path);\n return {\n path,\n params,\n data,\n clientRouting: config.clientRouting === true,\n clientPage: page.isClientPage ? page.path : null,\n partial: mode === \"shell\",\n title,\n };\n}\n\nexport async function renderPageHtml(options: {\n c: Context;\n page: ManifestPage;\n pageMod: PageModule;\n pageNode?: Child | null;\n data: unknown;\n params: Record<string, string>;\n config: PageConfig;\n isDev: boolean;\n clientScriptSrc: string;\n mode?: RenderMode;\n title?: string;\n}): Promise<{ html: string; payload: VinoPayload }> {\n const {\n c,\n page,\n pageNode = null,\n data,\n params,\n config,\n isDev,\n clientScriptSrc,\n mode = \"full\",\n title,\n } = options;\n\n const payload = buildPayload({\n page,\n params,\n data,\n config,\n mode,\n requestPath: c.req.path,\n title,\n });\n\n if (mode === \"content\") {\n if (pageNode == null) throw new Error(\"content render requires pageNode\");\n const node = wrapSlot(pageNode);\n await ensureJsxRenderer(c);\n const rendered = await c.render(node as never);\n let html = await rendered.text();\n html = html.replace(/^<!DOCTYPE html>/i, \"\");\n html = `${html}${payloadScript(payload)}`;\n return { html, payload };\n }\n\n let node: Child = mode === \"shell\" ? emptySlot() : (pageNode as Child);\n if (mode !== \"shell\" && needsPageSlot(page, config, mode)) {\n node = wrapSlot(node);\n }\n\n const layouts = await Promise.all(page.loadLayouts.map((load) => load()));\n for (const layout of [...layouts].reverse()) {\n const Layout = layout.default;\n node = jsx(Layout, { data, params, children: node });\n }\n if (layouts.length === 0) {\n node = jsx(DefaultLayout, { children: node });\n }\n\n await ensureJsxRenderer(c);\n const rendered = await c.render(node as never);\n let html = await rendered.text();\n\n html = injectDocument(html, payload, isDev, clientScriptSrc);\n if (!html.startsWith(\"<!\")) html = `<!DOCTYPE html>${html}`;\n return { html, payload };\n}\n","import { Hono } from \"hono\";\nimport type { Context, Env, Schema } from \"hono\";\nimport type { BlankSchema } from \"hono/types\";\nimport { jsxDEV as jsx } from \"hono/jsx/jsx-dev-runtime\";\nimport { manifest as virtualManifest } from \"virtual:vino/manifest\";\nimport { isDev as assetsIsDev, clientScriptSrc } from \"virtual:vino/assets\";\nimport { NAV_HEADER, SHELL_HEADER } from \"./constants.ts\";\nimport { loadMergedConfig, loadMergedData } from \"./config.ts\";\nimport {\n bindPageRun,\n dispatchHandlers,\n readPageDefinition,\n resolvePageRender,\n} from \"./definePage.ts\";\nimport { applyInitApp, initAppMiddleware, readBoundInitApp, readPageModuleInitApp } from \"./factory.ts\";\nimport { isHonoApp, renderPageHtml } from \"./render.ts\";\nimport type { CreateVinoOptions, ManifestPage, PageModule, RenderMode } from \"./types.ts\";\n\ntype AppContext = Context;\n\nfunction requestParams(c: AppContext): Record<string, string> {\n try {\n return c.req.param();\n } catch {\n return {};\n }\n}\n\nfunction pageFromModule(pageMod: PageModule, data: unknown, params: Record<string, string>) {\n const Page = resolvePageRender(pageMod.default);\n if (Page) return jsx(Page, { data, params });\n return pageMod.default as never;\n}\n\nfunction resolveRenderMode(c: AppContext): RenderMode {\n if (c.req.header(SHELL_HEADER)) return \"shell\";\n if (c.req.header(NAV_HEADER)) return \"content\";\n return \"full\";\n}\n\nasync function handlePage(c: AppContext, page: ManifestPage): Promise<Response> {\n const pageMod = await page.loadPage();\n const def = readPageDefinition(pageMod.default);\n if (def) {\n bindPageRun(c, () => handlePageBody(c, page, pageMod));\n const init = readBoundInitApp(def) ? undefined : readPageModuleInitApp(pageMod.default);\n const handlers = init ? [initAppMiddleware(init), ...def] : def;\n return dispatchHandlers(handlers, c);\n }\n return applyInitApp(readPageModuleInitApp(pageMod.default), c, () => handlePageBody(c, page, pageMod));\n}\n\nasync function handlePageBody(c: AppContext, page: ManifestPage, pageMod: PageModule): Promise<Response> {\n if (isHonoApp(pageMod.default)) {\n return pageMod.default.fetch(c.req.raw, c.env, c.executionCtx);\n }\n\n const method = c.req.method;\n const named = pageMod[method];\n const hasPage = Boolean(resolvePageRender(pageMod.default));\n const mode = resolveRenderMode(c);\n\n if (typeof named === \"function\" && (method !== \"GET\" || !hasPage)) {\n const result: unknown = await (named as (ctx: AppContext) => unknown)(c);\n if (result instanceof Response) return result;\n if (method === \"GET\" || method === \"HEAD\") {\n const config = loadMergedConfig(page, pageMod);\n const params = requestParams(c);\n const data = mode === \"shell\" ? undefined : await loadMergedData(page, c, pageMod);\n const { html } = await renderPageHtml({\n c,\n page,\n pageMod,\n pageNode: mode === \"shell\" ? null : (result as never),\n data,\n params,\n config,\n isDev: assetsIsDev,\n clientScriptSrc,\n mode,\n });\n return c.html(html);\n }\n return c.json(result as never);\n }\n\n if (method !== \"GET\" && method !== \"HEAD\") {\n return c.text(\"Method Not Allowed\", 405);\n }\n\n const config = loadMergedConfig(page, pageMod);\n const params = requestParams(c);\n const data = mode === \"shell\" ? undefined : await loadMergedData(page, c, pageMod);\n const pageNode = mode === \"shell\" ? null : pageFromModule(pageMod, data, params);\n const { html } = await renderPageHtml({\n c,\n page,\n pageMod,\n pageNode: pageNode,\n data,\n params,\n config,\n isDev: assetsIsDev,\n clientScriptSrc,\n mode,\n });\n return c.html(html);\n}\n\n/**\n * Mount filesystem pages from the Vite-generated manifest onto a user-owned Hono app.\n * Extra API routes should be registered on `app` before calling this.\n */\nexport function mountVino<E extends Env, S extends Schema, BasePath extends string>(app: Hono<E, S, BasePath>, options: CreateVinoOptions = {}): Hono<E, S, BasePath> {\n const manifest = options.manifest ?? virtualManifest;\n for (const page of manifest.pages) {\n app.get(page.pattern, (c) => handlePage(c as AppContext, page));\n }\n\n return app;\n}\n\n/**\n * Create a new Vino app. Returns a Hono app with the Vino pages mounted.\n * @param options - The options for the Vino app.\n * @returns A Hono app with the Vino pages mounted.\n */\nexport function createVino<E extends Env = Env, S extends Schema = BlankSchema, BasePath extends string = \"/\">(options: CreateVinoOptions = {}): Hono<E, S, BasePath> {\n const app = new Hono<E, S, BasePath>();\n return mountVino(app, options);\n}\n"],"mappings":"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"}
package/dist/vite.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { d as VIRTUAL_ASSETS, f as VIRTUAL_CLIENT, g as VIRTUAL_SSR_HOOKS, h as VIRTUAL_MANIFEST, m as VIRTUAL_COMPONENTS, p as VIRTUAL_CLIENT_PAGES, s as PAGE_EXTS, u as SHELL_HEADER } from "./constants-BVT2bJEd.mjs";
2
- import { c as usesDefinePage, o as readPageDefinition } from "./definePage--9Pz7rHs.mjs";
3
- import { a as pathDepth, i as pathCovers, n as compareRoutes, o as urlToHtmlFile, r as compileFilePath, s as loadMergedConfig, t as applyParams$1 } from "./compile-glNMkyyx.mjs";
2
+ import { d as usesDefinePage, l as readPageDefinition } from "./definePage-CcqUZAiy.mjs";
3
+ import { a as pathDepth, i as pathCovers, n as compareRoutes, o as urlToHtmlFile, r as compileFilePath, s as loadMergedConfig, t as applyParams$1 } from "./compile-BGVCPc4H.mjs";
4
4
  import { n as SERVER_FN_KIND, o as usesDefineServerFn, t as DEFINE_SERVER_FN_CALL } from "./defineServerFn-H-bz7xBX.mjs";
5
5
  import { existsSync, readFileSync } from "node:fs";
6
6
  import { dirname, isAbsolute, join, relative } from "node:path";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vinojs",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Hono + Vite meta-framework: file routing, SSR, SSG, and hono/jsx hydration designed for Cloudflare Workers.",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -67,6 +67,7 @@
67
67
  "lint": "eslint .",
68
68
  "test": "node --experimental-strip-types --test src/vino/*.test.ts src/vite/*.test.ts",
69
69
  "example": "pnpm --filter @vinojs/example-basic dev",
70
- "example:full": "pnpm --filter @vinojs/example-full dev"
70
+ "example:full": "pnpm --filter @vinojs/example-full dev",
71
+ "example:hono": "pnpm --filter @vinojs/example-hono dev"
71
72
  }
72
73
  }
@@ -1,73 +0,0 @@
1
- //#region src/vino/definePage.ts
2
- const PAGE_KIND = "vino:page";
3
- const SERVER_PAGE_KIND = "vino:server-page";
4
- function isPageDefinition(value) {
5
- if (value === null || typeof value !== "object" && typeof value !== "function") return false;
6
- const candidate = value;
7
- return (candidate.kind === "vino:page" || candidate.kind === "vino:server-page") && typeof candidate.render === "function";
8
- }
9
- function isClientPageDefinition(value) {
10
- return isPageDefinition(value) && value.kind === "vino:page";
11
- }
12
- function isServerPageDefinition(value) {
13
- return isPageDefinition(value) && value.kind === "vino:server-page";
14
- }
15
- function readPageDefinition(value) {
16
- return isPageDefinition(value) ? value : void 0;
17
- }
18
- function resolvePageRender(value) {
19
- if (isPageDefinition(value)) return value.render;
20
- if (typeof value === "function") return value;
21
- }
22
- /** True when source calls `definePage(...)` (hydratable pages). */
23
- function usesDefinePage(code) {
24
- return /(?:^|[^\w$])definePage\s*(?:<[^>]*>\s*)?\(/.test(code);
25
- }
26
- function pageDefinition(page, kind, fnName) {
27
- if (typeof page === "function") return {
28
- kind,
29
- client: kind === PAGE_KIND,
30
- render: page
31
- };
32
- const render = page.render ?? page.page;
33
- if (typeof render !== "function") throw new TypeError(`[vino] ${fnName}() requires a JSX function or { render }`);
34
- return {
35
- kind,
36
- client: kind === PAGE_KIND,
37
- render,
38
- data: page.data,
39
- config: page.config
40
- };
41
- }
42
- /**
43
- * Declare a page that SSRs with `hono/jsx`, then hydrates `#vino-page` with `hono/jsx/dom`.
44
- *
45
- * @example
46
- * export default definePage(() => {
47
- * const [n, setN] = useState(0)
48
- * return <button onClick={() => setN(n + 1)}>{n}</button>
49
- * })
50
- *
51
- * @example
52
- * export default definePage({
53
- * config: { prerender: 'full', clientRouting: true },
54
- * data: async (c) => ({ title: 'Hello' }),
55
- * render: ({ data }) => <h1>{data.title}</h1>,
56
- * })
57
- *
58
- * @example
59
- * import data from './[slug].server'
60
- * export default definePage({ data, render: ({ data }) => <h1>{data.title}</h1> })
61
- */
62
- const definePage = ((page) => pageDefinition(page, PAGE_KIND, "definePage"));
63
- /**
64
- * Declare a page that SSRs only. The output is never hydrated.
65
- *
66
- * @example
67
- * export default defineServerPage(() => <h1>About</h1>)
68
- */
69
- const defineServerPage = ((page) => pageDefinition(page, SERVER_PAGE_KIND, "defineServerPage"));
70
- //#endregion
71
- export { isServerPageDefinition as a, usesDefinePage as c, isPageDefinition as i, defineServerPage as n, readPageDefinition as o, isClientPageDefinition as r, resolvePageRender as s, definePage as t };
72
-
73
- //# sourceMappingURL=definePage--9Pz7rHs.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"definePage--9Pz7rHs.mjs","names":[],"sources":["../src/vino/definePage.ts"],"sourcesContent":["import type { Env } from \"hono\";\nimport type { ServerFn } from \"./defineServerFn.ts\";\nimport type { DataLoader, PageConfig, PageProps, VinoHtml } from \"./types.ts\";\n\nexport const PAGE_KIND = \"vino:page\" as const;\nexport const SERVER_PAGE_KIND = \"vino:server-page\" as const;\n\nexport type PageRender<Data = unknown> = (props: PageProps<Data>) => VinoHtml;\n\nexport type PageDataFn<E extends Env = Env> = DataLoader<E> | ServerFn<unknown, E>;\n\nexport interface DefinePageOptions<Data = unknown, E extends Env = Env> {\n /** JSX page component. Alias: `page`. */\n render?: PageRender<Data>;\n page?: PageRender<Data>;\n /** Loader or `defineServerFn(...)` */\n data?: DataLoader<E> | ServerFn<Data, E>;\n config?: PageConfig;\n}\n\n/** Options object that infers `data` return type into `render` / `page`. */\nexport type InferDefinePageOptions<\n D extends (...args: never[]) => unknown,\n Data = Awaited<ReturnType<D>>,\n> = {\n data?: D;\n config?: PageConfig;\n} & (\n | { render: PageRender<Data>; page?: PageRender<Data> }\n | { page: PageRender<Data>; render?: PageRender<Data> }\n);\n\n/**\n * `definePage` / `defineServerPage` call signature, with `Env` fixed so `data(c)`\n * is typed without blocking inference of the loader return type.\n */\nexport interface DefinePageFn<E extends Env = Env> {\n <Data = unknown>(page: PageRender<Data>): PageDefinition<Data>;\n <D extends PageDataFn<E>>(\n options: InferDefinePageOptions<D>,\n ): PageDefinition<Awaited<ReturnType<D>>>;\n <Data = unknown>(options: DefinePageOptions<Data, E>): PageDefinition<Data>;\n}\n\nexport interface PageDefinition<Data = unknown> {\n readonly kind: typeof PAGE_KIND | typeof SERVER_PAGE_KIND;\n readonly client: boolean;\n readonly render: PageRender<Data>;\n readonly data?: DataLoader | ServerFn<Data>;\n readonly config?: PageConfig;\n}\n\nexport function isPageDefinition(value: unknown): value is PageDefinition {\n if (value === null || (typeof value !== \"object\" && typeof value !== \"function\")) return false;\n const candidate = value as PageDefinition;\n return (\n (candidate.kind === PAGE_KIND || candidate.kind === SERVER_PAGE_KIND) &&\n typeof candidate.render === \"function\"\n );\n}\n\nexport function isClientPageDefinition(value: unknown): value is PageDefinition {\n return isPageDefinition(value) && value.kind === PAGE_KIND;\n}\n\nexport function isServerPageDefinition(value: unknown): value is PageDefinition {\n return isPageDefinition(value) && value.kind === SERVER_PAGE_KIND;\n}\n\nexport function readPageDefinition(value: unknown): PageDefinition | undefined {\n return isPageDefinition(value) ? value : undefined;\n}\n\nexport function resolvePageRender(value: unknown): PageRender | undefined {\n if (isPageDefinition(value)) return value.render;\n if (typeof value === \"function\") return value as PageRender;\n return undefined;\n}\n\n/** True when source calls `definePage(...)` (hydratable pages). */\nexport function usesDefinePage(code: string): boolean {\n return /(?:^|[^\\w$])definePage\\s*(?:<[^>]*>\\s*)?\\(/.test(code);\n}\n\nfunction pageDefinition<Data>(\n page: PageRender<Data> | DefinePageOptions<Data>,\n kind: typeof PAGE_KIND | typeof SERVER_PAGE_KIND,\n fnName: string,\n): PageDefinition<Data> {\n if (typeof page === \"function\") {\n return { kind, client: kind === PAGE_KIND, render: page };\n }\n const render = page.render ?? page.page;\n if (typeof render !== \"function\") {\n throw new TypeError(`[vino] ${fnName}() requires a JSX function or { render }`);\n }\n return {\n kind,\n client: kind === PAGE_KIND,\n render,\n data: page.data,\n config: page.config,\n };\n}\n\n/**\n * Declare a page that SSRs with `hono/jsx`, then hydrates `#vino-page` with `hono/jsx/dom`.\n *\n * @example\n * export default definePage(() => {\n * const [n, setN] = useState(0)\n * return <button onClick={() => setN(n + 1)}>{n}</button>\n * })\n *\n * @example\n * export default definePage({\n * config: { prerender: 'full', clientRouting: true },\n * data: async (c) => ({ title: 'Hello' }),\n * render: ({ data }) => <h1>{data.title}</h1>,\n * })\n *\n * @example\n * import data from './[slug].server'\n * export default definePage({ data, render: ({ data }) => <h1>{data.title}</h1> })\n */\nexport const definePage = ((page: PageRender | DefinePageOptions) => pageDefinition(page, PAGE_KIND, \"definePage\")) as DefinePageFn;\n\n/**\n * Declare a page that SSRs only. The output is never hydrated.\n *\n * @example\n * export default defineServerPage(() => <h1>About</h1>)\n */\nexport const defineServerPage = ((page: PageRender | DefinePageOptions) => pageDefinition(page, SERVER_PAGE_KIND, \"defineServerPage\")) as DefinePageFn;\n"],"mappings":";AAIA,MAAa,YAAY;AACzB,MAAa,mBAAmB;AA+ChC,SAAgB,iBAAiB,OAAyC;CACxE,IAAI,UAAU,QAAS,OAAO,UAAU,YAAY,OAAO,UAAU,YAAa,OAAO;CACzF,MAAM,YAAY;CAClB,QACG,UAAU,SAAA,eAAsB,UAAU,SAAA,uBAC3C,OAAO,UAAU,WAAW;AAEhC;AAEA,SAAgB,uBAAuB,OAAyC;CAC9E,OAAO,iBAAiB,KAAK,KAAK,MAAM,SAAA;AAC1C;AAEA,SAAgB,uBAAuB,OAAyC;CAC9E,OAAO,iBAAiB,KAAK,KAAK,MAAM,SAAA;AAC1C;AAEA,SAAgB,mBAAmB,OAA4C;CAC7E,OAAO,iBAAiB,KAAK,IAAI,QAAQ,KAAA;AAC3C;AAEA,SAAgB,kBAAkB,OAAwC;CACxE,IAAI,iBAAiB,KAAK,GAAG,OAAO,MAAM;CAC1C,IAAI,OAAO,UAAU,YAAY,OAAO;AAE1C;;AAGA,SAAgB,eAAe,MAAuB;CACpD,OAAO,6CAA6C,KAAK,IAAI;AAC/D;AAEA,SAAS,eACP,MACA,MACA,QACsB;CACtB,IAAI,OAAO,SAAS,YAClB,OAAO;EAAE;EAAM,QAAQ,SAAS;EAAW,QAAQ;CAAK;CAE1D,MAAM,SAAS,KAAK,UAAU,KAAK;CACnC,IAAI,OAAO,WAAW,YACpB,MAAM,IAAI,UAAU,UAAU,OAAO,yCAAyC;CAEhF,OAAO;EACL;EACA,QAAQ,SAAS;EACjB;EACA,MAAM,KAAK;EACX,QAAQ,KAAK;CACf;AACF;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,eAAe,SAAyC,eAAe,MAAM,WAAW,YAAY;;;;;;;AAQjH,MAAa,qBAAqB,SAAyC,eAAe,MAAM,kBAAkB,kBAAkB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"factory-CVT0-OZw.d.mts","names":[],"sources":["../src/vino/defineServerFn.ts","../src/vino/definePage.ts","../src/vino/factory.ts"],"mappings":";;;cAEa;KAED,gBAAgB,aAAa,UAAU,MAAM,QACvD,GAAG,QAAQ,eACR,IAAI,QAAQ;;;;KAKL,SAAS,aAAa,UAAU,MAAM,OAAO,gBAAgB,GAAG;WACjE,aAAa;;;;;;KAOZ,iBAAiB,UAAU,MAAM,QAAQ,GAAG,IAAI,gBAAgB,GAAG,OAAO,SAAS,GAAG;iBAElF,WAAW,iBAAiB,SAAS;iBAIrC,aAAa,iBAAiB;;;;;;;;;;;;;;;;;;;iBAgC9B,eAAe,GAAG,UAAU,MAAM,KAAK,IAAI,gBAAgB,GAAG,KAAK,SAAS,GAAG;;;cCrDlF;cACA;KAED,WAAW,mBAAmB,OAAO,UAAU,UAAU;KAEzD,WAAW,UAAU,MAAM,OAAO,WAAW,KAAK,kBAAkB;UAE/D,kBAAkB,gBAAgB,UAAU,MAAM;;EAEjE,SAAS,WAAW;EACpB,OAAO,WAAW;;EAElB,OAAO,WAAW,KAAK,SAAS,MAAM;EACtC,SAAS;;;KAIC,uBACV,cAAc,2BACd,OAAO,QAAQ,WAAW;EAE1B,OAAO;EACP,SAAS;;EAEL,QAAQ,WAAW;EAAO,OAAO,WAAW;;EAC5C,MAAM,WAAW;EAAO,SAAS,WAAW;;;;;;UAOjC,aAAa,UAAU,MAAM;GAC3C,gBAAgB,MAAM,WAAW,QAAQ,eAAe;GACxD,UAAU,WAAW,IACpB,SAAS,uBAAuB,KAC/B,eAAe,QAAQ,WAAW;GACpC,gBAAgB,SAAS,kBAAkB,MAAM,KAAK,eAAe;;UAGvD,eAAe;WACrB,aAAa,mBAAmB;WAChC;WACA,QAAQ,WAAW;WACnB,OAAO,aAAa,SAAS;WAC7B,SAAS;;iBAGJ,iBAAiB,iBAAiB,SAAS;iBAS3C,uBAAuB,iBAAiB,SAAS;iBAIjD,uBAAuB,iBAAiB,SAAS;iBAQjD,kBAAkB,iBAAiB;;;;;;;;;;;;;;;;;;;;;cAoDtC,YAA0G;;;;;;;cAQ1G,kBAA6H;;;;KC7G9H,QAAQ,UAAU,MAAM,QAAQ,KAAK,KAAK;KAEjD,kBAAkB,YAAY,6BAA6B;UAG/C,eAAe,UAAU,MAAM;EAC9C,UAAU,QAAQ;EAClB,oBAAoB;;;iBAiBN,iBAAiB,iBAAiB;iBAKlC,sBAAsB,sBAAsB,iBAAiB;;;;;;iBAiCvD,aACpB,SAAS,qBACT,GAAG,SACH,YAAY,QAAQ,YACnB,QAAQ;cAoBE,QAAQ,UAAU,MAAM;UAC3B;UACA;EAEI,YAAA,OAAO,eAAe;;;;;;EAUlC,YAAa,UAAU,oBAAkB,KAAK;;EAkB9C,YAAY,aAAa;;EAIzB,kBAAkB,aAAa;;EAI/B,gBAAgB,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0CnB,cAAc,UAAU,MAAM,KAAK,UAAU,eAAe,KAAK,QAAQ"}