@jasonshimmy/vite-plugin-cer-app 0.20.0 → 0.20.2

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/CHANGELOG.md CHANGED
@@ -1,6 +1,15 @@
1
1
  # Changelog
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
+ ## [v0.20.2] - 2026-04-11
5
+
6
+ - fix: correct loader error handling and improve data fetching in SSR/SSG modes (d225a99)
7
+ - docs: add Content Layer section to README for Markdown/JSON support (376a990)
8
+
9
+ ## [v0.20.1] - 2026-04-11
10
+
11
+ - fix(content): add fallback title and description extraction from Markdown body (9bab321)
12
+
4
13
  ## [v0.20.0] - 2026-04-11
5
14
 
6
15
  - feat(content): implement file-based content layer with Markdown/JSON support (#1) (c6ef0f7)
package/README.md CHANGED
@@ -220,6 +220,7 @@ component('page-about', () => {
220
220
  | [Layouts](docs/layouts.md) | Layout system and `<slot>` composition |
221
221
  | [Components](docs/components.md) | Auto-registered custom elements |
222
222
  | [Composables](docs/composables.md) | Auto-imported composables |
223
+ | [Content Layer](docs/content.md) | File-based Markdown/JSON content with `queryContent()` and `useContentSearch()` |
223
224
  | [Plugins](docs/plugins.md) | App plugin system and DI |
224
225
  | [Middleware](docs/middleware.md) | Route guards and server middleware |
225
226
  | [Server API Routes](docs/server-api.md) | HTTP handlers in `server/api/` |
package/commits.txt CHANGED
@@ -1,2 +1,2 @@
1
- - feat(content): implement file-based content layer with Markdown/JSON support (#1) (c6ef0f7)
2
- - chore: update dependencies for @jasonshimmy/custom-elements-runtime and typescript-eslint (15095bc)
1
+ - fix: correct loader error handling and improve data fetching in SSR/SSG modes (d225a99)
2
+ - docs: add Content Layer section to README for Markdown/JSON support (376a990)
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/plugin/content/parser.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAkB,WAAW,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACtF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AA0J/C;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,WAAW,EACjB,UAAU,EAAE,MAAM,GACjB,WAAW,CAGb;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,WAAW,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,WAAW,CAAC,CAGtB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,CAI5D"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/plugin/content/parser.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAkB,WAAW,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACtF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AA6N/C;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,WAAW,EACjB,UAAU,EAAE,MAAM,GACjB,WAAW,CAGb;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,WAAW,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,WAAW,CAAC,CAGtB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,CAI5D"}
@@ -40,6 +40,60 @@ function extractHeadings(tokens) {
40
40
  walk(tokens);
41
41
  return headings;
42
42
  }
43
+ // ─── Fallback title / description extraction ─────────────────────────────────
44
+ /**
45
+ * Converts a list of inline marked tokens to plain text by recursing into
46
+ * formatted tokens (strong, em, link, etc.) and collecting their leaf text.
47
+ * Used to derive readable fallback values from body content.
48
+ */
49
+ function inlineToPlainText(tokens) {
50
+ let result = '';
51
+ for (const t of tokens) {
52
+ const children = t.tokens;
53
+ if (Array.isArray(children) && children.length > 0) {
54
+ result += inlineToPlainText(children);
55
+ }
56
+ else if (t.type === 'br') {
57
+ result += ' ';
58
+ }
59
+ else if ('text' in t && typeof t.text === 'string') {
60
+ result += t.text;
61
+ }
62
+ }
63
+ return result;
64
+ }
65
+ const DESCRIPTION_MAX_LEN = 160;
66
+ /**
67
+ * Scans the top-level token list for a fallback `title` (first depth-1 heading)
68
+ * and `description` (first paragraph). Both are `undefined` when no matching
69
+ * token is found.
70
+ *
71
+ * These are applied only when the corresponding frontmatter field is absent, so
72
+ * frontmatter always wins.
73
+ */
74
+ function extractFallbacks(tokens) {
75
+ let title;
76
+ let description;
77
+ for (const token of tokens) {
78
+ if (title === undefined && token.type === 'heading' && token.depth === 1) {
79
+ const text = inlineToPlainText(token.tokens ?? []).trim();
80
+ if (text)
81
+ title = text;
82
+ }
83
+ if (description === undefined && token.type === 'paragraph') {
84
+ const text = inlineToPlainText(token.tokens ?? []).trim();
85
+ if (text) {
86
+ description =
87
+ text.length > DESCRIPTION_MAX_LEN
88
+ ? text.slice(0, DESCRIPTION_MAX_LEN).trimEnd() + '…'
89
+ : text;
90
+ }
91
+ }
92
+ if (title !== undefined && description !== undefined)
93
+ break;
94
+ }
95
+ return { title, description };
96
+ }
43
97
  // ─── Custom renderer: add id to heading tags ─────────────────────────────────
44
98
  const renderer = new marked.Renderer();
45
99
  renderer.heading = function ({ tokens, depth }) {
@@ -105,6 +159,9 @@ function parseContentFileFromRaw(file, contentDir, raw) {
105
159
  const excerpt = excerptSource !== null
106
160
  ? marked.parse(excerptSource, { renderer })
107
161
  : undefined;
162
+ // Derive fallback title / description from body tokens when frontmatter
163
+ // does not provide them. Frontmatter always wins — these only fill gaps.
164
+ const fallbacks = extractFallbacks(tokens);
108
165
  const item = {
109
166
  ...frontmatter,
110
167
  _path,
@@ -113,6 +170,12 @@ function parseContentFileFromRaw(file, contentDir, raw) {
113
170
  body,
114
171
  toc,
115
172
  };
173
+ if (item.title === undefined && fallbacks.title !== undefined) {
174
+ item.title = fallbacks.title;
175
+ }
176
+ if (item.description === undefined && fallbacks.description !== undefined) {
177
+ item.description = fallbacks.description;
178
+ }
116
179
  if (excerpt !== undefined) {
117
180
  item.excerpt = excerpt;
118
181
  }
@@ -1 +1 @@
1
- {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../../src/plugin/content/parser.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../../src/plugin/content/parser.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../../src/plugin/dev-server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAA;AAKzC,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAA;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAA;IAC1E,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;KAAE,CAAA;IAC9D,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAA;IACtD,WAAW,EAAE;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAA;IACjG,aAAa,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,OAAO,EAAE,OAAO,oBAAoB,EAAE,oBAAoB,CAAA;KAAE,CAAA;IAC9G,IAAI,EAAE,OAAO,oBAAoB,EAAE,UAAU,GAAG,IAAI,CAAA;IACpD,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,QAAQ,GAAG,uBAAuB,GAAG,WAAW,CAAA;KAAE,GAAG,IAAI,CAAA;CACtH;AA0GD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,iBAAiB,GACxB,IAAI,CA2KN"}
1
+ {"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../../src/plugin/dev-server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAA;AAKzC,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAA;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAA;IAC1E,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;KAAE,CAAA;IAC9D,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAA;IACtD,WAAW,EAAE;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAA;IACjG,aAAa,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,OAAO,EAAE,OAAO,oBAAoB,EAAE,oBAAoB,CAAA;KAAE,CAAA;IAC9G,IAAI,EAAE,OAAO,oBAAoB,EAAE,UAAU,GAAG,IAAI,CAAA;IACpD,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,QAAQ,GAAG,uBAAuB,GAAG,WAAW,CAAA;KAAE,GAAG,IAAI,CAAA;CACtH;AA0GD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,iBAAiB,GACxB,IAAI,CA8KN"}
@@ -186,8 +186,11 @@ export function configureCerDevServer(server, config) {
186
186
  catch {
187
187
  // virtual:cer-server-api not yet ready or empty — continue
188
188
  }
189
- // 3. SSR mode: intercept HTML requests
190
- if (config.mode === 'ssr') {
189
+ // 3. SSR/SSG mode: intercept HTML requests and server-render them.
190
+ // Both 'ssr' and 'ssg' modes run loaders on the server so usePageData()
191
+ // returns real data during dev — matching production behaviour.
192
+ // 'spa' mode never runs server loaders, so it falls through to the client bundle.
193
+ if (config.mode === 'ssr' || config.mode === 'ssg') {
191
194
  const acceptsHtml = (req.headers['accept'] ?? '').includes('text/html') ||
192
195
  url === '/' ||
193
196
  (!url.includes('.') && !url.startsWith('/api/'));
@@ -1 +1 @@
1
- {"version":3,"file":"dev-server.js","sourceRoot":"","sources":["../../src/plugin/dev-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dev-server.js","sourceRoot":"","sources":["../../src/plugin/dev-server.ts"],"names":[],"mappings":"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"}
@@ -83,7 +83,8 @@ export interface PageMeta {
83
83
  export interface PageLoaderContext<P extends Record<string, string> = Record<string, string>> {
84
84
  params: P;
85
85
  query: Record<string, string>;
86
- req: IncomingMessage;
86
+ /** Present during SSR/SSG server render. Absent (`undefined`) during client-side navigation. */
87
+ req?: IncomingMessage;
87
88
  }
88
89
  /**
89
90
  * Server-side data loader for a page. Export as `export const loader` (or `export async function loader`)
@@ -1 +1 @@
1
- {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../src/types/page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAEhD;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAA;AAElE,qHAAqH;AACrH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAED,6GAA6G;AAC7G,MAAM,WAAW,aAAa;IAC5B;;;;;;;;;;;;;;;;OAgBG;IACH,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,GAAG,eAAe,EAAE,CAAA;IAC5D;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,wGAAwG;IACxG,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,OAAO,CAAC,EAAE,eAAe,CAAA;IACzB,GAAG,CAAC,EAAE,aAAa,CAAA;IACnB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAC7B;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAA;CACrC;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAC1F,MAAM,EAAE,CAAC,CAAA;IACT,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,GAAG,EAAE,eAAe,CAAA;CACrB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,UAAU,CACpB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACzD,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACzB,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA"}
1
+ {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../src/types/page.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAEhD;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAA;AAElE,qHAAqH;AACrH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAED,6GAA6G;AAC7G,MAAM,WAAW,aAAa;IAC5B;;;;;;;;;;;;;;;;OAgBG;IACH,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,GAAG,eAAe,EAAE,CAAA;IAC5D;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,wGAAwG;IACxG,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,OAAO,CAAC,EAAE,eAAe,CAAA;IACzB,GAAG,CAAC,EAAE,aAAa,CAAA;IACnB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAC7B;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAA;CACrC;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAC1F,MAAM,EAAE,CAAC,CAAA;IACT,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,gGAAgG;IAChG,GAAG,CAAC,EAAE,eAAe,CAAA;CACtB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,UAAU,CACpB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACzD,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACzB,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA"}
package/docs/content.md CHANGED
@@ -131,6 +131,23 @@ Recognized frontmatter keys:
131
131
 
132
132
  Any additional frontmatter keys are stored verbatim in the `ContentMeta` / `ContentItem` object.
133
133
 
134
+ ### Automatic title and description
135
+
136
+ When `title` or `description` are absent from frontmatter, the parser derives them from the body:
137
+
138
+ - **`title`** — plain text of the first depth-1 heading (`# …`). Only `h1` is considered; `h2`–`h6` are ignored.
139
+ - **`description`** — plain text of the first paragraph, truncated to 160 characters (with `…` appended). Inline formatting is stripped.
140
+
141
+ Frontmatter values always win — these fallbacks only fill the gaps. JSON files do not receive fallbacks (they have no Markdown body to parse from).
142
+
143
+ ```md
144
+ # Hello World
145
+
146
+ This becomes the description because no description key is in frontmatter.
147
+ ```
148
+
149
+ Results in `title: 'Hello World'` and `description: 'This becomes the description because no description key is in frontmatter.'`.
150
+
134
151
  ### Date-prefixed filenames
135
152
 
136
153
  Filenames starting with `YYYY-MM-DD-` have the date prefix stripped when computing the content path:
@@ -11,12 +11,12 @@ Each page can export a `loader` function that runs on the server before the page
11
11
  import type { PageLoader } from '@jasonshimmy/vite-plugin-cer-app/types'
12
12
 
13
13
  component('page-blog-slug', () => {
14
- const props = useProps({ slug: '', title: '', body: '' })
14
+ const data = usePageData<{ title: string; body: string }>()
15
15
 
16
16
  return html`
17
17
  <article>
18
- <h1>${props.title}</h1>
19
- <div>${props.body}</div>
18
+ <h1>${data?.title ?? ''}</h1>
19
+ <div>${data?.body ?? ''}</div>
20
20
  </article>
21
21
  `
22
22
  })
@@ -27,7 +27,7 @@ export const loader: PageLoader = async ({ params }) => {
27
27
  }
28
28
  ```
29
29
 
30
- The object returned by `loader` is serialized and passed as props to the page component.
30
+ The object returned by `loader` is made available via `usePageData()` in the page component. Primitive values (strings, numbers, booleans) are also forwarded as HTML element attributes so `useProps()` can read them.
31
31
 
32
32
  ---
33
33
 
@@ -36,13 +36,13 @@ The object returned by `loader` is serialized and passed as props to the page co
36
36
  ```ts
37
37
  type PageLoader<
38
38
  Params extends Record<string, string> = Record<string, string>,
39
- Data extends Record<string, unknown> = Record<string, unknown>,
40
- > = (ctx: PageLoaderContext<Params>) => Promise<Data>
39
+ Data = Record<string, unknown>,
40
+ > = (ctx: PageLoaderContext<Params>) => Promise<Data> | Data
41
41
 
42
42
  interface PageLoaderContext<P extends Record<string, string>> {
43
43
  params: P // URL path parameters
44
44
  query: Record<string, string> // Parsed query string
45
- req: IncomingMessage // Raw Node.js request object (SSR only)
45
+ req?: IncomingMessage // Raw Node.js request (present during SSR/SSG server render; absent during client-side navigation)
46
46
  }
47
47
  ```
48
48
 
@@ -66,7 +66,7 @@ interface PageLoaderContext<P extends Record<string, string>> {
66
66
 
67
67
  1. Browser receives the full HTML — content is immediately visible via Declarative Shadow DOM before any JS runs
68
68
  2. Client JS boots; `usePageData()` reads `window.__CER_DATA__` and returns the hydrated values
69
- 3. After the initial `router.replace()` in `.cer/app.ts` completes, `.cer/app.ts` deletes `window.__CER_DATA__` so subsequent client-side navigations trigger a fresh fetch instead of reusing stale server data
69
+ 3. After the initial `router.replace()` in `.cer/app.ts` completes, `.cer/app.ts` clears the loader data from `globalThis` so subsequent client-side navigations trigger a fresh fetch instead of reusing stale server data
70
70
  4. Components that received SSR data skip their `useOnConnected` fetch — no duplicate request
71
71
 
72
72
  ---
@@ -106,18 +106,22 @@ export const loader = async ({ params }: { params: { id: string } }) => {
106
106
 
107
107
  ## Accessing loader data in components
108
108
 
109
- The loader's return value is passed as props. Use `useProps` to receive them:
109
+ Use `usePageData()` to read loader data it receives everything the loader returned (primitives and complex objects alike) and works in all modes: SSR, SSG, and client-side navigation.
110
110
 
111
111
  ```ts
112
112
  component('page-user', () => {
113
- // Loader returns { user: { id, name, email } }
114
- const props = useProps<{ user: { id: string; name: string; email: string } }>({
115
- user: { id: '', name: '', email: '' },
113
+ const ssrData = usePageData<{ user: { id: string; name: string; email: string } }>()
114
+ const user = ref(ssrData?.user ?? { id: '', name: '', email: '' })
115
+
116
+ useOnConnected(async () => {
117
+ if (ssrData) return // already hydrated
118
+ const { data } = await useFetch(`/api/users/${user.value.id}`)
119
+ if (data) user.value = data
116
120
  })
117
121
 
118
122
  return html`
119
- <h1>Hello, ${props.user.name}</h1>
120
- <p>${props.user.email}</p>
123
+ <h1>Hello, ${user.value.name}</h1>
124
+ <p>${user.value.email}</p>
121
125
  `
122
126
  })
123
127
 
@@ -127,6 +131,27 @@ export const loader: PageLoader<{ id: string }> = async ({ params }) => {
127
131
  }
128
132
  ```
129
133
 
134
+ `useProps()` is an alternative for **primitive values only** (strings, numbers, booleans). It reads element HTML attributes rather than the loader payload directly, which means it also picks up attributes passed by parent components in the component tree — but it cannot receive arrays or objects.
135
+
136
+ ```ts
137
+ component('page-profile', () => {
138
+ // Works for primitives only — use usePageData() if you need objects or arrays
139
+ const props = useProps<{ username: string; bio: string }>({ username: '', bio: '' })
140
+
141
+ return html`
142
+ <h1>${props.username}</h1>
143
+ <p>${props.bio}</p>
144
+ `
145
+ })
146
+
147
+ export const loader = async ({ params }: { params: { id: string } }) => {
148
+ const { data: user } = await useFetch(`/api/users/${params.id}`)
149
+ return { username: user?.name, bio: user?.bio } // primitives only
150
+ }
151
+ ```
152
+
153
+ > **Rule of thumb:** reach for `usePageData()` first. Only prefer `useProps()` when you specifically need the element attribute binding model — for example, when the same component is also used as a child that receives attributes from a parent.
154
+
130
155
  ---
131
156
 
132
157
  ## SSG and loaders
@@ -153,6 +178,14 @@ export const loader: PageLoader<{ slug: string }> = async ({ params }) => {
153
178
 
154
179
  ---
155
180
 
181
+ ## Dev server behavior
182
+
183
+ In `mode: 'ssr'` and `mode: 'ssg'`, the dev server runs the server entry module and calls your `loader` on every HTML request — matching production behaviour. Both `usePageData()` and `useProps()` receive real loader data during development.
184
+
185
+ In `mode: 'spa'`, no loaders run in the dev server. `usePageData()` always returns `null` and `useProps()` returns its defaults. Use `useOnConnected` to fetch data client-side in SPA mode.
186
+
187
+ ---
188
+
156
189
  ## Error handling in loaders
157
190
 
158
191
  When a `loader` throws, the SSR error boundary intercepts it:
@@ -173,12 +206,6 @@ export const loader: PageLoader<{ id: string }> = async ({ params }) => {
173
206
  }
174
207
  ```
175
208
 
176
- You can also throw a standard `Response` — the framework reads its `.status` property:
177
-
178
- ```ts
179
- throw new Response('Not Found', { status: 404 })
180
- ```
181
-
182
209
  Unhandled errors without a `status` property default to HTTP 500.
183
210
 
184
211
  ---
@@ -2,10 +2,11 @@
2
2
  * Content layer e2e tests — exercises queryContent() and useContentSearch().
3
3
  *
4
4
  * Pages under test:
5
- * /content-index — queryContent().find() (all content)
6
- * /content-blog — queryContent('/blog').find() (blog prefix, draft exclusion)
7
- * /content-doc — queryContent('/docs/getting-started').first() (body + TOC)
8
- * /content-search — useContentSearch() (MiniSearch, client-side)
5
+ * /content-index — queryContent().find() (all content)
6
+ * /content-blog — queryContent('/blog').find() (blog prefix, draft exclusion)
7
+ * /content-doc — queryContent('/docs/getting-started').first() (body + TOC)
8
+ * /content-search — useContentSearch() (MiniSearch, client-side)
9
+ * /content-fallback — title/description derived from body when frontmatter omits them
9
10
  */
10
11
 
11
12
  const mode = Cypress.env('mode') as 'spa' | 'ssr' | 'ssg'
@@ -226,3 +227,65 @@ describe('Content search — useContentSearch()', () => {
226
227
  cy.get('[data-cy=content-search-result]').should('not.exist')
227
228
  })
228
229
  })
230
+
231
+ // ─── /content-fallback ────────────────────────────────────────────────────────
232
+
233
+ describe('Content fallback — title and description derived from body', () => {
234
+ // blog/no-frontmatter.md has no frontmatter at all.
235
+ // Expected derived values:
236
+ // title → "Derived From Body" (first h1)
237
+ // description → "This description is…" (first paragraph, ≤160 chars)
238
+
239
+ if (mode !== 'spa') {
240
+ it('pre-renders the derived title in initial HTML (SSR/SSG)', () => {
241
+ cy.request('/content-fallback').then((response) => {
242
+ expect(response.body).to.include('Derived From Body')
243
+ })
244
+ })
245
+
246
+ it('pre-renders the derived description in initial HTML (SSR/SSG)', () => {
247
+ cy.request('/content-fallback').then((response) => {
248
+ expect(response.body).to.include('This description is derived from the first paragraph')
249
+ })
250
+ })
251
+ }
252
+
253
+ it('renders the derived title after hydration', () => {
254
+ cy.visit('/content-fallback')
255
+ cy.get('[data-cy=content-fallback-title]', { timeout: 8000 })
256
+ .should('contain', 'Derived From Body')
257
+ })
258
+
259
+ it('renders the derived description after hydration', () => {
260
+ cy.visit('/content-fallback')
261
+ cy.get('[data-cy=content-fallback-desc]', { timeout: 8000 })
262
+ .should('contain', 'This description is derived from the first paragraph')
263
+ })
264
+
265
+ it('document is found — fallback-missing element is absent', () => {
266
+ cy.visit('/content-fallback')
267
+ cy.get('[data-cy=content-fallback-heading]', { timeout: 8000 }).should('exist')
268
+ cy.get('[data-cy=content-fallback-missing]').should('not.exist')
269
+ })
270
+
271
+ it('derived item appears in queryContent find() results (content-index)', () => {
272
+ cy.visit('/content-index')
273
+ cy.get('[data-cy=content-item][data-path="/blog/no-frontmatter"]', { timeout: 8000 })
274
+ .should('exist')
275
+ })
276
+
277
+ it('derived title is shown in the content-index listing', () => {
278
+ cy.visit('/content-index')
279
+ cy.get('[data-cy=content-item][data-path="/blog/no-frontmatter"] [data-cy=content-item-title]', { timeout: 8000 })
280
+ .should('contain', 'Derived From Body')
281
+ })
282
+
283
+ it('derived item appears in search results when searching by derived title', () => {
284
+ cy.intercept('GET', '/_content/search-index.json').as('searchIndex')
285
+ cy.visit('/content-search')
286
+ cy.wait('@searchIndex')
287
+ setSearchQuery('Derived')
288
+ cy.get('[data-cy=content-search-result]', { timeout: 8000 })
289
+ .should('contain', 'Derived From Body')
290
+ })
291
+ })
@@ -65,4 +65,24 @@ if (mode === 'ssr') {
65
65
  cy.get('[data-cy=error-retry]').should('exist')
66
66
  })
67
67
  })
68
+
69
+ describe('SSR error boundary — loader throws a Response object', () => {
70
+ it('reads the numeric .status from the thrown Response (405)', () => {
71
+ cy.request({ url: '/loader-response-error-test', failOnStatusCode: false }).then((response) => {
72
+ expect(response.status).to.eq(405)
73
+ })
74
+ })
75
+
76
+ it('renders page-error element in the server response for a thrown Response', () => {
77
+ cy.request({ url: '/loader-response-error-test', failOnStatusCode: false }).then((response) => {
78
+ expect(response.body).to.include('page-error')
79
+ })
80
+ })
81
+
82
+ it('does not render the normal page heading when a Response is thrown', () => {
83
+ cy.request({ url: '/loader-response-error-test', failOnStatusCode: false }).then((response) => {
84
+ expect(response.body).not.to.include('loader-response-error-heading')
85
+ })
86
+ })
87
+ })
68
88
  }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Content fallback page — exercises title/description derivation from body
3
+ * when frontmatter does not supply them.
4
+ *
5
+ * Fetches /blog/no-frontmatter (no frontmatter at all) and renders its
6
+ * derived title and description so Cypress can assert both in all modes.
7
+ *
8
+ * Route: /content-fallback
9
+ */
10
+
11
+ component('page-content-fallback', () => {
12
+ useHead({ title: 'Content Fallback — Kitchen Sink' })
13
+
14
+ const ssrData = usePageData<{ doc: ContentItem | null }>()
15
+ const doc = ref<ContentItem | null>(ssrData?.doc ?? null)
16
+
17
+ useOnConnected(async () => {
18
+ if (ssrData) return // already hydrated
19
+ doc.value = await queryContent('/blog/no-frontmatter').first()
20
+ })
21
+
22
+ return html`
23
+ <div>
24
+ <h1 data-cy="content-fallback-heading">Content Fallback</h1>
25
+ ${doc.value ? html`
26
+ <p data-cy="content-fallback-title">${doc.value.title}</p>
27
+ <p data-cy="content-fallback-desc">${doc.value.description}</p>
28
+ ` : html`<p data-cy="content-fallback-missing">Document not found.</p>`}
29
+ </div>
30
+ `
31
+ })
32
+
33
+ export const loader = async () => {
34
+ const doc = await queryContent('/blog/no-frontmatter').first()
35
+ return { doc }
36
+ }
@@ -0,0 +1,13 @@
1
+ component('page-loader-response-error-test', () => {
2
+ return html`
3
+ <div>
4
+ <h1 data-cy="loader-response-error-heading">Response Error Test</h1>
5
+ </div>
6
+ `
7
+ })
8
+
9
+ export async function loader() {
10
+ // Verify that throwing a Response-like object (which has a numeric .status property)
11
+ // causes the framework to use that status code for the HTTP response.
12
+ throw new Response('Method Not Allowed', { status: 405 })
13
+ }
@@ -0,0 +1,7 @@
1
+ # Derived From Body
2
+
3
+ This description is derived from the first paragraph because no frontmatter is present.
4
+
5
+ ## More Content
6
+
7
+ Additional body content below the first paragraph.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jasonshimmy/vite-plugin-cer-app",
3
- "version": "0.20.0",
3
+ "version": "0.20.2",
4
4
  "description": "Nuxt-style meta-framework for @jasonshimmy/custom-elements-runtime",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -102,7 +102,7 @@
102
102
  "eslint": "^10.2.0",
103
103
  "happy-dom": "^20.8.9",
104
104
  "jiti": "^2.6.1",
105
- "start-server-and-test": "^3.0.0",
105
+ "start-server-and-test": "^3.0.2",
106
106
  "typescript": "^5.9.3",
107
107
  "typescript-eslint": "^8.58.1",
108
108
  "vite": "^8.0.8",
@@ -1,4 +1,4 @@
1
- import { describe, it, expect, beforeAll } from 'vitest'
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest'
2
2
  import { mkdtempSync, writeFileSync, mkdirSync, rmSync } from 'node:fs'
3
3
  import { tmpdir } from 'node:os'
4
4
  import { join } from 'pathe'
@@ -62,6 +62,62 @@ Just content, no more marker.
62
62
  `)
63
63
 
64
64
  writeFileSync(join(contentDir, 'data', 'products.json'), JSON.stringify([{ id: 1, name: 'Widget' }]))
65
+
66
+ // ── Fallback fixtures ────────────────────────────────────────────────────
67
+ // No frontmatter at all — title and description derived from body
68
+ writeFileSync(join(contentDir, 'no-frontmatter.md'), `# Derived Title
69
+
70
+ First paragraph for description.
71
+
72
+ Second paragraph.
73
+ `)
74
+
75
+ // Frontmatter title only — description derived from body
76
+ writeFileSync(join(contentDir, 'title-only.md'), `---
77
+ title: Explicit Title
78
+ ---
79
+
80
+ Description will come from this paragraph.
81
+ `)
82
+
83
+ // Frontmatter description only — title derived from h1
84
+ writeFileSync(join(contentDir, 'desc-only.md'), `---
85
+ description: Explicit description
86
+ ---
87
+
88
+ # Derived H1 Title
89
+
90
+ Some paragraph.
91
+ `)
92
+
93
+ // Both set in frontmatter — body values must NOT overwrite them
94
+ writeFileSync(join(contentDir, 'both-frontmatter.md'), `---
95
+ title: FM Title
96
+ description: FM Description
97
+ ---
98
+
99
+ # Different H1
100
+
101
+ Different paragraph.
102
+ `)
103
+
104
+ // h1 with inline formatting — plain text only
105
+ writeFileSync(join(contentDir, 'formatted-h1.md'), `# Hello **World**
106
+
107
+ Some intro.
108
+ `)
109
+
110
+ // Long paragraph — description truncated to 160 chars + ellipsis
111
+ writeFileSync(join(contentDir, 'long-para.md'), `# Long
112
+
113
+ ${'A'.repeat(200)}
114
+ `)
115
+
116
+ // No h1, only h2 — title fallback must remain undefined
117
+ writeFileSync(join(contentDir, 'no-h1.md'), `## Section Only
118
+
119
+ A paragraph here.
120
+ `)
65
121
  })
66
122
 
67
123
  function makeFile(filePath: string, ext: 'md' | 'json'): ContentFile {
@@ -237,3 +293,89 @@ describe('parseContentFile — date normalisation', () => {
237
293
  expect(date < '2027-01-01').toBe(true)
238
294
  })
239
295
  })
296
+
297
+ // ─── Fallback title / description ────────────────────────────────────────────
298
+
299
+ describe('parseContentFile — fallback title from h1', () => {
300
+ it('derives title from h1 when frontmatter title is absent', () => {
301
+ const item = parseContentFile(makeFile(join(contentDir, 'no-frontmatter.md'), 'md'), contentDir)
302
+ expect(item.title).toBe('Derived Title')
303
+ })
304
+
305
+ it('frontmatter title is not overwritten when present', () => {
306
+ const item = parseContentFile(makeFile(join(contentDir, 'title-only.md'), 'md'), contentDir)
307
+ expect(item.title).toBe('Explicit Title')
308
+ })
309
+
310
+ it('derives title from h1 when only description is in frontmatter', () => {
311
+ const item = parseContentFile(makeFile(join(contentDir, 'desc-only.md'), 'md'), contentDir)
312
+ expect(item.title).toBe('Derived H1 Title')
313
+ })
314
+
315
+ it('frontmatter title wins over h1 when both set', () => {
316
+ const item = parseContentFile(makeFile(join(contentDir, 'both-frontmatter.md'), 'md'), contentDir)
317
+ expect(item.title).toBe('FM Title')
318
+ })
319
+
320
+ it('strips inline formatting — title is plain text', () => {
321
+ const item = parseContentFile(makeFile(join(contentDir, 'formatted-h1.md'), 'md'), contentDir)
322
+ expect(item.title).toBe('Hello World')
323
+ })
324
+
325
+ it('does not derive title from h2 — must be h1 only', () => {
326
+ const item = parseContentFile(makeFile(join(contentDir, 'no-h1.md'), 'md'), contentDir)
327
+ expect(item.title).toBeUndefined()
328
+ })
329
+ })
330
+
331
+ describe('parseContentFile — fallback description from first paragraph', () => {
332
+ it('derives description from first paragraph when frontmatter description is absent', () => {
333
+ const item = parseContentFile(makeFile(join(contentDir, 'no-frontmatter.md'), 'md'), contentDir)
334
+ expect(item.description).toBe('First paragraph for description.')
335
+ })
336
+
337
+ it('frontmatter description is not overwritten when present', () => {
338
+ const item = parseContentFile(makeFile(join(contentDir, 'desc-only.md'), 'md'), contentDir)
339
+ expect(item.description).toBe('Explicit description')
340
+ })
341
+
342
+ it('derives description from first paragraph when only title is in frontmatter', () => {
343
+ const item = parseContentFile(makeFile(join(contentDir, 'title-only.md'), 'md'), contentDir)
344
+ expect(item.description).toBe('Description will come from this paragraph.')
345
+ })
346
+
347
+ it('frontmatter description wins when both set', () => {
348
+ const item = parseContentFile(makeFile(join(contentDir, 'both-frontmatter.md'), 'md'), contentDir)
349
+ expect(item.description).toBe('FM Description')
350
+ })
351
+
352
+ it('truncates long paragraphs to 160 chars with ellipsis', () => {
353
+ const item = parseContentFile(makeFile(join(contentDir, 'long-para.md'), 'md'), contentDir)
354
+ expect(typeof item.description).toBe('string')
355
+ expect((item.description as string).length).toBeLessThanOrEqual(164) // 160 + '…' (3 bytes)
356
+ expect(item.description as string).toMatch(/…$/)
357
+ })
358
+
359
+ it('derived description is on ContentMeta (present in manifest)', async () => {
360
+ const { toContentMeta } = await import('../../../plugin/content/parser.js')
361
+ const item = parseContentFile(makeFile(join(contentDir, 'no-frontmatter.md'), 'md'), contentDir)
362
+ const meta = toContentMeta(item)
363
+ expect(meta.description).toBe('First paragraph for description.')
364
+ })
365
+ })
366
+
367
+ describe('parseContentFile — JSON fallback', () => {
368
+ it('does not apply title/description fallbacks to JSON files', () => {
369
+ // JSON files have no markdown body to extract from
370
+ const item = parseContentFile(
371
+ makeFile(join(contentDir, 'data', 'products.json'), 'json'),
372
+ contentDir,
373
+ )
374
+ expect(item.title).toBeUndefined()
375
+ expect(item.description).toBeUndefined()
376
+ })
377
+ })
378
+
379
+ afterAll(() => {
380
+ rmSync(tmpDir, { recursive: true, force: true })
381
+ })
@@ -450,6 +450,43 @@ describe('configureCerDevServer — SSR mode', () => {
450
450
  expect(ssrHandler).toHaveBeenCalled()
451
451
  })
452
452
 
453
+ it('invokes SSR handler for HTML requests in ssg mode', async () => {
454
+ const ssrHandler = vi.fn(async (req: any, res: any) => res.end('<html>SSG-DEV</html>'))
455
+ const { server, getMiddleware } = makeServer()
456
+ server.ssrLoadModule.mockImplementation(async (path: string) => {
457
+ if (path.includes('server-middleware')) return { serverMiddleware: [] }
458
+ if (path.includes('server-api')) return { apiRoutes: [] }
459
+ return { handler: ssrHandler }
460
+ })
461
+ configureCerDevServer(server as any, makeConfig({ mode: 'ssg' }))
462
+ const res = createRes()
463
+ await getMiddleware()(
464
+ createReq({ url: '/', headers: { accept: 'text/html' } }),
465
+ res,
466
+ vi.fn(),
467
+ )
468
+ expect(ssrHandler).toHaveBeenCalled()
469
+ })
470
+
471
+ it('does not invoke SSR handler in spa mode', async () => {
472
+ const ssrHandler = vi.fn()
473
+ const { server, getMiddleware } = makeServer()
474
+ server.ssrLoadModule.mockImplementation(async (path: string) => {
475
+ if (path.includes('server-middleware')) return { serverMiddleware: [] }
476
+ if (path.includes('server-api')) return { apiRoutes: [] }
477
+ return { handler: ssrHandler }
478
+ })
479
+ configureCerDevServer(server as any, makeConfig({ mode: 'spa' }))
480
+ const next = vi.fn()
481
+ await getMiddleware()(
482
+ createReq({ url: '/', headers: { accept: 'text/html' } }),
483
+ createRes(),
484
+ next,
485
+ )
486
+ expect(ssrHandler).not.toHaveBeenCalled()
487
+ expect(next).toHaveBeenCalled()
488
+ })
489
+
453
490
  it('does not invoke SSR handler for non-HTML requests (e.g. assets)', async () => {
454
491
  const ssrHandler = vi.fn()
455
492
  const { server, getMiddleware } = makeServer()
@@ -469,6 +506,25 @@ describe('configureCerDevServer — SSR mode', () => {
469
506
  expect(next).toHaveBeenCalled()
470
507
  })
471
508
 
509
+ it('does not invoke SSR handler for non-HTML requests in ssg mode', async () => {
510
+ const ssrHandler = vi.fn()
511
+ const { server, getMiddleware } = makeServer()
512
+ server.ssrLoadModule.mockImplementation(async (path: string) => {
513
+ if (path.includes('server-middleware')) return { serverMiddleware: [] }
514
+ if (path.includes('server-api')) return { apiRoutes: [] }
515
+ return { handler: ssrHandler }
516
+ })
517
+ configureCerDevServer(server as any, makeConfig({ mode: 'ssg' }))
518
+ const next = vi.fn()
519
+ await getMiddleware()(
520
+ createReq({ url: '/assets/main.js', headers: { accept: 'application/javascript' } }),
521
+ createRes(),
522
+ next,
523
+ )
524
+ expect(ssrHandler).not.toHaveBeenCalled()
525
+ expect(next).toHaveBeenCalled()
526
+ })
527
+
472
528
  it('returns 500 and error text when SSR handler throws', async () => {
473
529
  const { server, getMiddleware } = makeServer()
474
530
  server.ssrLoadModule.mockImplementation(async (path: string) => {
@@ -531,13 +587,14 @@ describe('configureCerDevServer — SSR mode', () => {
531
587
  })
532
588
  })
533
589
 
534
- // ─── Per-route render mode (SSR mode) ────────────────────────────────────────
590
+ // ─── Per-route render mode (SSR/SSG mode) ────────────────────────────────────
535
591
 
536
592
  describe("configureCerDevServer — per-route render mode in SSR", () => {
537
593
  /** Build a server where virtual:cer-routes returns the given routes array. */
538
594
  function makeServerWithRoutes(
539
595
  pageRoutes: Array<{ path: string; meta?: Record<string, unknown> }>,
540
596
  ssrHandler = vi.fn(async (_req: any, res: any) => res.end('<html>SSR</html>')),
597
+ mode: 'ssr' | 'ssg' = 'ssr',
541
598
  ) {
542
599
  const { server, getMiddleware } = makeServer()
543
600
  server.ssrLoadModule.mockImplementation(async (path: string) => {
@@ -546,7 +603,7 @@ describe("configureCerDevServer — per-route render mode in SSR", () => {
546
603
  if (path.includes('cer-routes')) return { default: pageRoutes }
547
604
  return { handler: ssrHandler }
548
605
  })
549
- configureCerDevServer(server as any, makeConfig({ mode: 'ssr' }))
606
+ configureCerDevServer(server as any, makeConfig({ mode }))
550
607
  return { middleware: getMiddleware(), ssrHandler }
551
608
  }
552
609
 
@@ -564,6 +621,22 @@ describe("configureCerDevServer — per-route render mode in SSR", () => {
564
621
  expect(ssrHandler).not.toHaveBeenCalled()
565
622
  })
566
623
 
624
+ it("calls next() instead of SSR for a route with render: 'spa' in ssg mode", async () => {
625
+ const { middleware, ssrHandler } = makeServerWithRoutes(
626
+ [{ path: '/spa-page', meta: { render: 'spa' } }],
627
+ vi.fn(async (_req: any, res: any) => res.end('<html>SSG</html>')),
628
+ 'ssg',
629
+ )
630
+ const next = vi.fn()
631
+ await middleware(
632
+ createReq({ url: '/spa-page', headers: { accept: 'text/html' } }),
633
+ createRes(),
634
+ next,
635
+ )
636
+ expect(next).toHaveBeenCalled()
637
+ expect(ssrHandler).not.toHaveBeenCalled()
638
+ })
639
+
567
640
  it("proceeds to SSR for a route with render: 'server'", async () => {
568
641
  const { middleware, ssrHandler } = makeServerWithRoutes([
569
642
  { path: '/server-page', meta: { render: 'server' } },
@@ -620,6 +693,27 @@ describe("configureCerDevServer — per-route render mode in SSR", () => {
620
693
  )
621
694
  expect(routesCalls).toHaveLength(0)
622
695
  })
696
+
697
+ it("loads routes module in ssg mode to check per-route render mode", async () => {
698
+ const ssrHandler = vi.fn(async (_req: any, res: any) => res.end('<html>SSG</html>'))
699
+ const { server, getMiddleware } = makeServer()
700
+ server.ssrLoadModule.mockImplementation(async (path: string) => {
701
+ if (path.includes('server-middleware')) return { serverMiddleware: [] }
702
+ if (path.includes('server-api') || path.includes('cer-server-api')) return { apiRoutes: [] }
703
+ if (path.includes('cer-routes')) return { default: [] }
704
+ return { handler: ssrHandler }
705
+ })
706
+ configureCerDevServer(server as any, makeConfig({ mode: 'ssg' }))
707
+ await getMiddleware()(
708
+ createReq({ url: '/about', headers: { accept: 'text/html' } }),
709
+ createRes(),
710
+ vi.fn(),
711
+ )
712
+ const routesCalls = (server.ssrLoadModule as any).mock.calls.filter(
713
+ ([p]: [string]) => p.includes('cer-routes'),
714
+ )
715
+ expect(routesCalls.length).toBeGreaterThan(0)
716
+ })
623
717
  })
624
718
 
625
719
  // ─── parseBody edge cases ─────────────────────────────────────────────────────
@@ -173,6 +173,13 @@ describe('entry-server-template (ENTRY_SERVER_TEMPLATE content)', () => {
173
173
  expect(src).toContain('err.status')
174
174
  })
175
175
 
176
+ it('guards err.status with typeof === "number" so Response-like thrown values work', () => {
177
+ // `new Response("...", { status: 404 })` has a numeric `.status` property.
178
+ // The typeof guard ensures that non-numeric status values (e.g. a string "404")
179
+ // do not pass through — only genuine numbers are accepted.
180
+ expect(src).toContain("typeof err.status === 'number'")
181
+ })
182
+
176
183
  it('defaults to status 500 when thrown error has no .status', () => {
177
184
  expect(src).toContain(': 500')
178
185
  })
@@ -48,6 +48,62 @@ function extractHeadings(tokens: Token[]): ContentHeading[] {
48
48
  return headings
49
49
  }
50
50
 
51
+ // ─── Fallback title / description extraction ─────────────────────────────────
52
+
53
+ /**
54
+ * Converts a list of inline marked tokens to plain text by recursing into
55
+ * formatted tokens (strong, em, link, etc.) and collecting their leaf text.
56
+ * Used to derive readable fallback values from body content.
57
+ */
58
+ function inlineToPlainText(tokens: Token[]): string {
59
+ let result = ''
60
+ for (const t of tokens) {
61
+ const children = (t as { tokens?: Token[] }).tokens
62
+ if (Array.isArray(children) && children.length > 0) {
63
+ result += inlineToPlainText(children)
64
+ } else if (t.type === 'br') {
65
+ result += ' '
66
+ } else if ('text' in t && typeof (t as { text: string }).text === 'string') {
67
+ result += (t as { text: string }).text
68
+ }
69
+ }
70
+ return result
71
+ }
72
+
73
+ const DESCRIPTION_MAX_LEN = 160
74
+
75
+ /**
76
+ * Scans the top-level token list for a fallback `title` (first depth-1 heading)
77
+ * and `description` (first paragraph). Both are `undefined` when no matching
78
+ * token is found.
79
+ *
80
+ * These are applied only when the corresponding frontmatter field is absent, so
81
+ * frontmatter always wins.
82
+ */
83
+ function extractFallbacks(tokens: Token[]): { title?: string; description?: string } {
84
+ let title: string | undefined
85
+ let description: string | undefined
86
+
87
+ for (const token of tokens) {
88
+ if (title === undefined && token.type === 'heading' && token.depth === 1) {
89
+ const text = inlineToPlainText((token as { tokens: Token[] }).tokens ?? []).trim()
90
+ if (text) title = text
91
+ }
92
+ if (description === undefined && token.type === 'paragraph') {
93
+ const text = inlineToPlainText((token as { tokens: Token[] }).tokens ?? []).trim()
94
+ if (text) {
95
+ description =
96
+ text.length > DESCRIPTION_MAX_LEN
97
+ ? text.slice(0, DESCRIPTION_MAX_LEN).trimEnd() + '…'
98
+ : text
99
+ }
100
+ }
101
+ if (title !== undefined && description !== undefined) break
102
+ }
103
+
104
+ return { title, description }
105
+ }
106
+
51
107
  // ─── Custom renderer: add id to heading tags ─────────────────────────────────
52
108
 
53
109
  const renderer = new marked.Renderer()
@@ -129,6 +185,10 @@ function parseContentFileFromRaw(
129
185
  ? (marked.parse(excerptSource, { renderer }) as string)
130
186
  : undefined
131
187
 
188
+ // Derive fallback title / description from body tokens when frontmatter
189
+ // does not provide them. Frontmatter always wins — these only fill gaps.
190
+ const fallbacks = extractFallbacks(tokens)
191
+
132
192
  const item: ContentItem = {
133
193
  ...frontmatter,
134
194
  _path,
@@ -138,6 +198,13 @@ function parseContentFileFromRaw(
138
198
  toc,
139
199
  }
140
200
 
201
+ if (item.title === undefined && fallbacks.title !== undefined) {
202
+ item.title = fallbacks.title
203
+ }
204
+ if (item.description === undefined && fallbacks.description !== undefined) {
205
+ item.description = fallbacks.description
206
+ }
207
+
141
208
  if (excerpt !== undefined) {
142
209
  item.excerpt = excerpt
143
210
  }
@@ -238,8 +238,11 @@ export function configureCerDevServer(
238
238
  // virtual:cer-server-api not yet ready or empty — continue
239
239
  }
240
240
 
241
- // 3. SSR mode: intercept HTML requests
242
- if (config.mode === 'ssr') {
241
+ // 3. SSR/SSG mode: intercept HTML requests and server-render them.
242
+ // Both 'ssr' and 'ssg' modes run loaders on the server so usePageData()
243
+ // returns real data during dev — matching production behaviour.
244
+ // 'spa' mode never runs server loaders, so it falls through to the client bundle.
245
+ if (config.mode === 'ssr' || config.mode === 'ssg') {
243
246
  const acceptsHtml =
244
247
  (req.headers['accept'] ?? '').includes('text/html') ||
245
248
  url === '/' ||
package/src/types/page.ts CHANGED
@@ -88,7 +88,8 @@ export interface PageMeta {
88
88
  export interface PageLoaderContext<P extends Record<string, string> = Record<string, string>> {
89
89
  params: P
90
90
  query: Record<string, string>
91
- req: IncomingMessage
91
+ /** Present during SSR/SSG server render. Absent (`undefined`) during client-side navigation. */
92
+ req?: IncomingMessage
92
93
  }
93
94
 
94
95
  /**