@pagesmith/core 0.3.0 → 0.4.0

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.
Files changed (72) hide show
  1. package/README.md +9 -4
  2. package/REFERENCE.md +5 -2
  3. package/dist/ai/index.d.mts +5 -3
  4. package/dist/ai/index.d.mts.map +1 -1
  5. package/dist/ai/index.mjs +300 -206
  6. package/dist/ai/index.mjs.map +1 -1
  7. package/dist/assets/index.d.mts +10 -1
  8. package/dist/assets/index.d.mts.map +1 -1
  9. package/dist/assets/index.mjs +2 -2
  10. package/dist/{assets-DXiWF_KI.mjs → assets-CAPOqQ_P.mjs} +42 -5
  11. package/dist/assets-CAPOqQ_P.mjs.map +1 -0
  12. package/dist/{content-config-Bfe4W9us.d.mts → content-config-DJXUOcNG.d.mts} +49 -17
  13. package/dist/{content-config-Bfe4W9us.d.mts.map → content-config-DJXUOcNG.d.mts.map} +1 -1
  14. package/dist/{content-layer-DPK1EmfY.mjs → content-layer-B5enqWeJ.mjs} +123 -28
  15. package/dist/content-layer-B5enqWeJ.mjs.map +1 -0
  16. package/dist/content-layer-CpHYUYNN.d.mts +121 -0
  17. package/dist/content-layer-CpHYUYNN.d.mts.map +1 -0
  18. package/dist/create/index.d.mts.map +1 -1
  19. package/dist/create/index.mjs +26 -28
  20. package/dist/create/index.mjs.map +1 -1
  21. package/dist/css/index.d.mts +1 -1
  22. package/dist/{heading-BpDXnl-7.d.mts → heading-Dhvzlay-.d.mts} +1 -1
  23. package/dist/{heading-BpDXnl-7.d.mts.map → heading-Dhvzlay-.d.mts.map} +1 -1
  24. package/dist/{index-Bg9srb5U.d.mts → index-B7NRZAxd.d.mts} +1 -1
  25. package/dist/{index-Bg9srb5U.d.mts.map → index-B7NRZAxd.d.mts.map} +1 -1
  26. package/dist/{index-BBYkDxwI.d.mts → index-C0QFHYwb.d.mts} +1 -1
  27. package/dist/{index-BBYkDxwI.d.mts.map → index-C0QFHYwb.d.mts.map} +1 -1
  28. package/dist/{index-CbOKbkjJ.d.mts → index-CJkBs8YQ.d.mts} +2 -2
  29. package/dist/index-CJkBs8YQ.d.mts.map +1 -0
  30. package/dist/{index-YXQxMV6J.d.mts → index-DCznbvaV.d.mts} +2 -2
  31. package/dist/{index-YXQxMV6J.d.mts.map → index-DCznbvaV.d.mts.map} +1 -1
  32. package/dist/index.d.mts +15 -99
  33. package/dist/index.d.mts.map +1 -1
  34. package/dist/index.mjs +13 -9
  35. package/dist/index.mjs.map +1 -1
  36. package/dist/loaders/index.d.mts +2 -2
  37. package/dist/markdown/index.d.mts +2 -2
  38. package/dist/markdown/index.mjs +1 -1
  39. package/dist/{markdown-CyrHoDhP.mjs → markdown-BmDJgYeB.mjs} +23 -1
  40. package/dist/{markdown-CyrHoDhP.mjs.map → markdown-BmDJgYeB.mjs.map} +1 -1
  41. package/dist/mcp/index.d.mts +23 -0
  42. package/dist/mcp/index.d.mts.map +1 -0
  43. package/dist/mcp/index.mjs +2 -0
  44. package/dist/mcp/server.d.mts +13 -0
  45. package/dist/mcp/server.d.mts.map +1 -0
  46. package/dist/mcp/server.mjs +2 -0
  47. package/dist/runtime/index.mjs +1 -1
  48. package/dist/schemas/index.d.mts +3 -3
  49. package/dist/server-D3DHoh5f.mjs +202 -0
  50. package/dist/server-D3DHoh5f.mjs.map +1 -0
  51. package/dist/ssg-utils/index.d.mts +61 -0
  52. package/dist/ssg-utils/index.d.mts.map +1 -0
  53. package/dist/ssg-utils/index.mjs +118 -0
  54. package/dist/ssg-utils/index.mjs.map +1 -0
  55. package/dist/{types-Cn52sdoq.d.mts → types-B-V5qemH.d.mts} +1 -1
  56. package/dist/{types-Cn52sdoq.d.mts.map → types-B-V5qemH.d.mts.map} +1 -1
  57. package/dist/vite/index.d.mts +69 -34
  58. package/dist/vite/index.d.mts.map +1 -1
  59. package/dist/vite/index.mjs +294 -226
  60. package/dist/vite/index.mjs.map +1 -1
  61. package/docs/agents/AGENTS.md.template +9 -0
  62. package/docs/agents/changelog-notes.md +15 -0
  63. package/docs/agents/errors.md +96 -0
  64. package/docs/agents/migration.md +25 -0
  65. package/docs/agents/recipes.md +26 -0
  66. package/docs/agents/usage.md +58 -0
  67. package/docs/llms-full.txt +53 -0
  68. package/docs/llms.txt +29 -0
  69. package/package.json +56 -4
  70. package/dist/assets-DXiWF_KI.mjs.map +0 -1
  71. package/dist/content-layer-DPK1EmfY.mjs.map +0 -1
  72. package/dist/index-CbOKbkjJ.d.mts.map +0 -1
@@ -1,9 +1,9 @@
1
- import "../markdown-CyrHoDhP.mjs";
2
- import { c as toSlug, t as createContentLayer } from "../content-layer-DPK1EmfY.mjs";
1
+ import "../markdown-BmDJgYeB.mjs";
2
+ import { l as toSlug, t as createContentLayer } from "../content-layer-B5enqWeJ.mjs";
3
3
  import { r as resolveLoader } from "../loaders-Cf-BXf2L.mjs";
4
- import { n as copyPublicFiles } from "../assets-DXiWF_KI.mjs";
5
- import { basename, dirname, extname, join, relative, resolve } from "path";
4
+ import { i as copyPublicFiles, n as collectContentAssets, t as CONTENT_ASSET_EXTS } from "../assets-CAPOqQ_P.mjs";
6
5
  import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "fs";
6
+ import { basename, dirname, extname, join, relative, resolve } from "path";
7
7
  import { fileURLToPath, pathToFileURL } from "url";
8
8
  import { uneval } from "devalue";
9
9
  //#region src/vite/ssg.ts
@@ -105,27 +105,16 @@ function sharedAssetsPlugin() {
105
105
  };
106
106
  }
107
107
  //#endregion
108
- //#region src/vite/ssg-plugin.ts
108
+ //#region src/vite/ssg-render.ts
109
109
  /**
110
- * Vite plugin for static site generation with @pagesmith/core.
111
- *
112
- * Handles both development (on-the-fly SSR via middleware) and
113
- * production (post-build SSG + pagefind indexing).
114
- *
115
- * The SSR entry module must export:
116
- * - `getRoutes(config)` — returns route paths to pre-render
117
- * - `render(url, config)` — renders a route to an HTML string
110
+ * Route collection, pre-rendering, and content asset handling for the SSG plugin.
118
111
  *
119
- * @example
120
- * ```ts
121
- * // vite.config.ts
122
- * import { pagesmithSsg } from '@pagesmith/core/vite'
123
- *
124
- * export default defineConfig({
125
- * base: '/my-site/',
126
- * plugins: [pagesmithSsg({ entry: './src/entry-server.tsx' })],
127
- * })
128
- * ```
112
+ * Provides:
113
+ * - Content companion asset discovery and copying
114
+ * - HTML asset reference rewriting
115
+ * - SSR bundle building via child process
116
+ * - Route pre-rendering to static HTML files
117
+ * - Font and public file copying
129
118
  */
130
119
  const MIME = {
131
120
  ".html": "text/html; charset=utf-8",
@@ -146,16 +135,6 @@ const MIME = {
146
135
  ".txt": "text/plain; charset=utf-8",
147
136
  ".xml": "application/xml; charset=utf-8"
148
137
  };
149
- const CONTENT_ASSET_EXTS = new Set([
150
- ".svg",
151
- ".png",
152
- ".jpg",
153
- ".jpeg",
154
- ".gif",
155
- ".webp",
156
- ".avif",
157
- ".ico"
158
- ]);
159
138
  function resolveContentDirs(projectRoot, contentDirs = []) {
160
139
  return contentDirs.map((dir) => resolve(projectRoot, dir));
161
140
  }
@@ -175,32 +154,250 @@ function rewriteContentAssetRefs(html, base) {
175
154
  return `${attr}=${quote}${basePrefix}/assets/${pathname.split("/").pop() ?? pathname}${suffix}${quote}`;
176
155
  });
177
156
  }
178
- function collectContentAssets(contentDirs) {
179
- const assets = /* @__PURE__ */ new Map();
180
- function walk(dir) {
181
- if (!existsSync(dir)) return;
182
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
183
- if (entry.name.startsWith(".")) continue;
184
- const fullPath = join(dir, entry.name);
185
- if (entry.isDirectory()) {
186
- walk(fullPath);
187
- continue;
188
- }
189
- const ext = extname(entry.name).toLowerCase();
190
- if (!CONTENT_ASSET_EXTS.has(ext)) continue;
191
- if (assets.has(entry.name) && assets.get(entry.name) !== fullPath) console.warn(`pagesmith:ssg duplicate companion asset basename "${entry.name}" detected; using ${fullPath}`);
192
- assets.set(entry.name, fullPath);
193
- }
194
- }
195
- for (const dir of contentDirs) walk(dir);
196
- return assets;
197
- }
198
157
  function copyContentAssetsToOutDir(outDir, assets) {
199
158
  if (assets.size === 0) return;
200
159
  const assetsDir = join(outDir, "assets");
201
160
  mkdirSync(assetsDir, { recursive: true });
202
161
  for (const [fileName, sourcePath] of assets) copyFileSync(sourcePath, join(assetsDir, fileName));
203
162
  }
163
+ /**
164
+ * Run the full SSG build: SSR bundle, route pre-rendering, asset copying.
165
+ *
166
+ * Returns the number of pages rendered.
167
+ */
168
+ async function renderStaticSite(context) {
169
+ const { config, projectRoot, base, outDir, contentDirs, entry } = context;
170
+ console.log("\nSSG: Starting static site generation...");
171
+ const contentAssets = collectContentAssets(contentDirs);
172
+ copyFontAssets(outDir);
173
+ copyPublicFiles(join(projectRoot, "public"), outDir);
174
+ const { cssPath, jsPath } = discoverBuiltAssets(outDir, base);
175
+ console.log("SSG: Building SSR bundle...");
176
+ await buildSsrBundle(config, projectRoot, outDir, entry);
177
+ const entryBaseName = basename(entry).replace(/\.(c|m)?[jt]sx?$/u, ".js");
178
+ const serverDir = join(outDir, ".server");
179
+ const ssrMod = await import(pathToFileURL(join(serverDir, entryBaseName)).href);
180
+ const renderConfig = {
181
+ base,
182
+ root: projectRoot,
183
+ cssPath,
184
+ jsPath,
185
+ searchEnabled: true,
186
+ isDev: false
187
+ };
188
+ const routes = await ssrMod.getRoutes(renderConfig);
189
+ console.log(`SSG: Rendering ${routes.length} pages...`);
190
+ const concurrency = Math.min(routes.length, 8);
191
+ let routeIndex = 0;
192
+ async function renderWorker() {
193
+ while (routeIndex < routes.length) {
194
+ const route = routes[routeIndex++];
195
+ const html = rewriteContentAssetRefs(await ssrMod.render(route, renderConfig), base);
196
+ const outputPath = join(outDir, route === "/" ? "" : route.replace(/^\//, ""), "index.html");
197
+ mkdirSync(dirname(outputPath), { recursive: true });
198
+ writeFileSync(outputPath, `<!DOCTYPE html>\n${html}`);
199
+ if (route === "/404") writeFileSync(join(outDir, "404.html"), `<!DOCTYPE html>\n${html}`);
200
+ }
201
+ }
202
+ const workers = Array.from({ length: concurrency }, () => renderWorker());
203
+ await Promise.all(workers);
204
+ copyContentAssetsToOutDir(outDir, contentAssets);
205
+ rmSync(serverDir, {
206
+ recursive: true,
207
+ force: true
208
+ });
209
+ return routes.length;
210
+ }
211
+ function copyFontAssets(outDir) {
212
+ const corePkgDir = dirname(fileURLToPath(import.meta.resolve("@pagesmith/core/package.json")));
213
+ const coreFontsDir = join(corePkgDir, "assets", "fonts");
214
+ const outFontsDir = join(outDir, "assets", "fonts");
215
+ mkdirSync(outFontsDir, { recursive: true });
216
+ for (const file of readdirSync(coreFontsDir)) if (file.endsWith(".woff2")) copyFileSync(join(coreFontsDir, file), join(outFontsDir, file));
217
+ copyFileSync(join(corePkgDir, "assets", "fonts.css"), join(outDir, "assets", "fonts.css"));
218
+ }
219
+ function discoverBuiltAssets(outDir, base) {
220
+ const builtIndex = join(outDir, "index.html");
221
+ let cssPath = `${base}/assets/style.css`;
222
+ let jsPath;
223
+ if (existsSync(builtIndex)) {
224
+ const html = readFileSync(builtIndex, "utf-8");
225
+ const cssMatch = html.match(/href="([^"]*\.css)"/);
226
+ const jsMatch = html.match(/src="([^"]*\.js)"/);
227
+ if (cssMatch) cssPath = cssMatch[1];
228
+ if (jsMatch) jsPath = jsMatch[1];
229
+ }
230
+ return {
231
+ cssPath,
232
+ jsPath
233
+ };
234
+ }
235
+ async function buildSsrBundle(config, projectRoot, outDir, entry) {
236
+ const { execFileSync } = await import("child_process");
237
+ const serverDir = join(outDir, ".server");
238
+ const ssrEntry = resolve(projectRoot, entry);
239
+ const buildScript = `
240
+ import { build } from 'vite';
241
+ await build({
242
+ root: ${JSON.stringify(projectRoot)},
243
+ logLevel: 'warn',
244
+ mode: ${JSON.stringify(config.mode)},
245
+ build: {
246
+ ssr: ${JSON.stringify(ssrEntry)},
247
+ outDir: ${JSON.stringify(serverDir)},
248
+ emptyOutDir: true,
249
+ },
250
+ });
251
+ `;
252
+ execFileSync(process.execPath, [
253
+ "--input-type=module",
254
+ "-e",
255
+ buildScript
256
+ ], {
257
+ stdio: "inherit",
258
+ cwd: projectRoot
259
+ });
260
+ }
261
+ //#endregion
262
+ //#region src/vite/ssg-hmr.ts
263
+ /**
264
+ * Dev-server middleware for the SSG plugin.
265
+ *
266
+ * Handles on-the-fly SSR rendering during development, including:
267
+ * - Content companion asset serving from content directories
268
+ * - HTML navigation request handling via server-side rendering
269
+ * - Vite HMR client injection for live reload
270
+ */
271
+ /**
272
+ * Configure the Vite dev server with SSR middleware for on-the-fly rendering.
273
+ *
274
+ * Sets up:
275
+ * - File watcher for content companion assets
276
+ * - Middleware for serving companion assets from `/assets/` paths
277
+ * - Middleware for SSR-rendering HTML navigation requests
278
+ */
279
+ function configureSsgDevServer(server, context) {
280
+ const { projectRoot, base, contentDirs, entry } = context;
281
+ let contentAssets = /* @__PURE__ */ new Map();
282
+ async function refreshContentArtifacts() {
283
+ contentAssets = collectContentAssets(contentDirs);
284
+ }
285
+ refreshContentArtifacts().catch((error) => {
286
+ console.warn(`pagesmith:ssg failed to prepare companion assets: ${error instanceof Error ? error.message : String(error)}`);
287
+ });
288
+ if (contentDirs.length > 0) {
289
+ server.watcher.add(contentDirs);
290
+ const refresh = () => {
291
+ refreshContentArtifacts().catch((error) => {
292
+ console.warn(`pagesmith:ssg failed to refresh companion assets: ${error instanceof Error ? error.message : String(error)}`);
293
+ });
294
+ };
295
+ server.watcher.on("add", refresh);
296
+ server.watcher.on("change", refresh);
297
+ server.watcher.on("unlink", refresh);
298
+ }
299
+ server.middlewares.use(async (req, res, next) => {
300
+ const url = req.url ?? "/";
301
+ const pathname = url.split(/[?#]/u, 1)[0] ?? url;
302
+ if (pathname.includes("/assets/")) {
303
+ const assetName = pathname.split("/assets/").pop();
304
+ const assetPath = assetName ? contentAssets.get(assetName) : void 0;
305
+ if (assetPath) {
306
+ const ext = extname(assetPath).toLowerCase();
307
+ res.writeHead(200, {
308
+ "Content-Type": MIME[ext] ?? "application/octet-stream",
309
+ "Cache-Control": "no-cache"
310
+ });
311
+ res.end(readFileSync(assetPath));
312
+ return;
313
+ }
314
+ }
315
+ const accept = req.headers.accept ?? "";
316
+ const pathExt = extname(pathname);
317
+ if (pathExt && pathExt !== ".html") return next();
318
+ if (!pathExt && !accept.includes("text/html")) return next();
319
+ if (base && (url === "/" || url === "")) {
320
+ res.writeHead(302, { Location: `${base}/` });
321
+ res.end();
322
+ return;
323
+ }
324
+ if (base && !url.startsWith(base)) return next();
325
+ try {
326
+ const renderFn = (await server.ssrLoadModule(resolve(projectRoot, entry))).render;
327
+ if (typeof renderFn !== "function") return next();
328
+ let html = await renderFn(url, {
329
+ base,
330
+ root: projectRoot,
331
+ cssPath: `${base}/src/theme.css`,
332
+ jsPath: void 0,
333
+ searchEnabled: false,
334
+ isDev: true
335
+ });
336
+ html = rewriteContentAssetRefs(html, base);
337
+ html = html.replace("</head>", `<script type="module" src="/@vite/client"><\/script>\n<link rel="stylesheet" href="${base}/src/theme.css">\n</head>`);
338
+ html = await server.transformIndexHtml(url, html);
339
+ const status = html.includes("doc-not-found") ? 404 : 200;
340
+ res.writeHead(status, { "Content-Type": "text/html; charset=utf-8" });
341
+ res.end(html);
342
+ } catch (err) {
343
+ server.ssrFixStacktrace(err);
344
+ console.error(`SSR error for ${url}:`, err instanceof Error ? err.message : String(err));
345
+ next(err);
346
+ }
347
+ });
348
+ }
349
+ //#endregion
350
+ //#region src/vite/ssg-pagefind.ts
351
+ /**
352
+ * Pagefind search indexing for the SSG plugin.
353
+ *
354
+ * Runs the Pagefind CLI to index the generated static site output,
355
+ * producing the search index used by the docs theme.
356
+ */
357
+ /**
358
+ * Run Pagefind indexing on the built site output directory.
359
+ *
360
+ * Resolves the Pagefind binary from the installed package and invokes it
361
+ * via a child process. Logs a warning and continues if Pagefind is not
362
+ * installed.
363
+ */
364
+ async function runPagefindIndexing(outDir) {
365
+ console.log("SSG: Indexing with Pagefind...");
366
+ try {
367
+ const pagefindBin = join(dirname(fileURLToPath(import.meta.resolve("pagefind"))), "..", "lib", "runner", "bin.cjs");
368
+ const { execFileSync } = await import("child_process");
369
+ execFileSync(process.execPath, [
370
+ pagefindBin,
371
+ "--site",
372
+ outDir
373
+ ], { stdio: "inherit" });
374
+ } catch {
375
+ console.warn("SSG: Pagefind not found, skipping search indexing");
376
+ }
377
+ }
378
+ //#endregion
379
+ //#region src/vite/ssg-plugin.ts
380
+ /**
381
+ * Vite plugin for static site generation with @pagesmith/core.
382
+ *
383
+ * Handles both development (on-the-fly SSR via middleware) and
384
+ * production (post-build SSG + pagefind indexing).
385
+ *
386
+ * The SSR entry module must export:
387
+ * - `getRoutes(config)` — returns route paths to pre-render
388
+ * - `render(url, config)` — renders a route to an HTML string
389
+ *
390
+ * @example
391
+ * ```ts
392
+ * // vite.config.ts
393
+ * import { pagesmithSsg } from '@pagesmith/core/vite'
394
+ *
395
+ * export default defineConfig({
396
+ * base: '/my-site/',
397
+ * plugins: [pagesmithSsg({ entry: './src/entry-server.tsx' })],
398
+ * })
399
+ * ```
400
+ */
204
401
  function pagesmithSsg(options) {
205
402
  const enablePagefind = options.pagefind !== false;
206
403
  let config;
@@ -208,7 +405,6 @@ function pagesmithSsg(options) {
208
405
  let base;
209
406
  let outDir;
210
407
  let contentDirs = [];
211
- let contentAssets = /* @__PURE__ */ new Map();
212
408
  return [{
213
409
  name: "pagesmith:ssg-dev",
214
410
  apply: "serve",
@@ -223,71 +419,11 @@ function pagesmithSsg(options) {
223
419
  contentDirs = resolveContentDirs(projectRoot, options.contentDirs);
224
420
  },
225
421
  configureServer(server) {
226
- async function refreshContentArtifacts() {
227
- contentAssets = collectContentAssets(contentDirs);
228
- }
229
- refreshContentArtifacts().catch((error) => {
230
- console.warn(`pagesmith:ssg failed to prepare companion assets: ${error instanceof Error ? error.message : String(error)}`);
231
- });
232
- if (contentDirs.length > 0) {
233
- server.watcher.add(contentDirs);
234
- const refresh = () => {
235
- refreshContentArtifacts().catch((error) => {
236
- console.warn(`pagesmith:ssg failed to refresh companion assets: ${error instanceof Error ? error.message : String(error)}`);
237
- });
238
- };
239
- server.watcher.on("add", refresh);
240
- server.watcher.on("change", refresh);
241
- server.watcher.on("unlink", refresh);
242
- }
243
- server.middlewares.use(async (req, res, next) => {
244
- const url = req.url ?? "/";
245
- const pathname = url.split(/[?#]/u, 1)[0] ?? url;
246
- if (pathname.includes("/assets/")) {
247
- const assetName = pathname.split("/assets/").pop();
248
- const assetPath = assetName ? contentAssets.get(assetName) : void 0;
249
- if (assetPath) {
250
- const ext = extname(assetPath).toLowerCase();
251
- res.writeHead(200, {
252
- "Content-Type": MIME[ext] ?? "application/octet-stream",
253
- "Cache-Control": "no-cache"
254
- });
255
- res.end(readFileSync(assetPath));
256
- return;
257
- }
258
- }
259
- const accept = req.headers.accept ?? "";
260
- const pathExt = extname(pathname);
261
- if (pathExt && pathExt !== ".html") return next();
262
- if (!pathExt && !accept.includes("text/html")) return next();
263
- if (base && (url === "/" || url === "")) {
264
- res.writeHead(302, { Location: `${base}/` });
265
- res.end();
266
- return;
267
- }
268
- if (base && !url.startsWith(base)) return next();
269
- try {
270
- const renderFn = (await server.ssrLoadModule(resolve(projectRoot, options.entry))).render;
271
- if (typeof renderFn !== "function") return next();
272
- let html = await renderFn(url, {
273
- base,
274
- root: projectRoot,
275
- cssPath: `${base}/src/theme.css`,
276
- jsPath: void 0,
277
- searchEnabled: false,
278
- isDev: true
279
- });
280
- html = rewriteContentAssetRefs(html, base);
281
- html = html.replace("</head>", `<script type="module" src="/@vite/client"><\/script>\n<link rel="stylesheet" href="${base}/src/theme.css">\n</head>`);
282
- html = await server.transformIndexHtml(url, html);
283
- const status = html.includes("doc-not-found") ? 404 : 200;
284
- res.writeHead(status, { "Content-Type": "text/html; charset=utf-8" });
285
- res.end(html);
286
- } catch (err) {
287
- server.ssrFixStacktrace(err);
288
- console.error(`SSR error for ${url}:`, err.message);
289
- next(err);
290
- }
422
+ configureSsgDevServer(server, {
423
+ projectRoot,
424
+ base,
425
+ contentDirs,
426
+ entry: options.entry
291
427
  });
292
428
  }
293
429
  }, {
@@ -302,123 +438,27 @@ function pagesmithSsg(options) {
302
438
  },
303
439
  async closeBundle() {
304
440
  if (config.build.ssr) return;
305
- console.log("\nSSG: Starting static site generation...");
306
- contentAssets = collectContentAssets(contentDirs);
307
- const corePkgDir = dirname(fileURLToPath(import.meta.resolve("@pagesmith/core/package.json")));
308
- const coreFontsDir = join(corePkgDir, "assets", "fonts");
309
- const outFontsDir = join(outDir, "assets", "fonts");
310
- mkdirSync(outFontsDir, { recursive: true });
311
- for (const file of readdirSync(coreFontsDir)) if (file.endsWith(".woff2")) copyFileSync(join(coreFontsDir, file), join(outFontsDir, file));
312
- copyFileSync(join(corePkgDir, "assets", "fonts.css"), join(outDir, "assets", "fonts.css"));
313
- copyPublicFiles(join(projectRoot, "public"), outDir);
314
- const builtIndex = join(outDir, "index.html");
315
- let cssPath = `${base}/assets/style.css`;
316
- let jsPath;
317
- if (existsSync(builtIndex)) {
318
- const html = readFileSync(builtIndex, "utf-8");
319
- const cssMatch = html.match(/href="([^"]*\.css)"/);
320
- const jsMatch = html.match(/src="([^"]*\.js)"/);
321
- if (cssMatch) cssPath = cssMatch[1];
322
- if (jsMatch) jsPath = jsMatch[1];
323
- }
324
- console.log("SSG: Building SSR bundle...");
325
- const { execFileSync } = await import("child_process");
326
- const serverDir = join(outDir, ".server");
327
- const ssrEntry = resolve(projectRoot, options.entry);
328
- const buildScript = `
329
- import { build } from 'vite-plus';
330
- await build({
331
- root: ${JSON.stringify(projectRoot)},
332
- logLevel: 'warn',
333
- mode: ${JSON.stringify(config.mode)},
334
- build: {
335
- ssr: ${JSON.stringify(ssrEntry)},
336
- outDir: ${JSON.stringify(serverDir)},
337
- emptyOutDir: true,
338
- },
339
- });
340
- `;
341
- execFileSync(process.execPath, [
342
- "--input-type=module",
343
- "-e",
344
- buildScript
345
- ], {
346
- stdio: "inherit",
347
- cwd: projectRoot
348
- });
349
- const ssrMod = await import(pathToFileURL(join(serverDir, basename(options.entry).replace(/\.(c|m)?[jt]sx?$/u, ".js"))).href);
350
- const renderConfig = {
441
+ const pageCount = await renderStaticSite({
442
+ config,
443
+ projectRoot,
351
444
  base,
352
- root: projectRoot,
353
- cssPath,
354
- jsPath,
355
- searchEnabled: true,
356
- isDev: false
357
- };
358
- const routes = await ssrMod.getRoutes(renderConfig);
359
- console.log(`SSG: Rendering ${routes.length} pages...`);
360
- for (const route of routes) {
361
- const html = rewriteContentAssetRefs(await ssrMod.render(route, renderConfig), base);
362
- const routePath = route === "/" ? "" : route.replace(/^\//, "");
363
- const outputPath = join(outDir, routePath, "index.html");
364
- mkdirSync(dirname(outputPath), { recursive: true });
365
- writeFileSync(outputPath, `<!DOCTYPE html>\n${html}`);
366
- if (route === "/404") writeFileSync(join(outDir, "404.html"), `<!DOCTYPE html>\n${html}`);
367
- }
368
- copyContentAssetsToOutDir(outDir, contentAssets);
369
- rmSync(serverDir, {
370
- recursive: true,
371
- force: true
445
+ outDir,
446
+ contentDirs,
447
+ entry: options.entry
372
448
  });
373
- if (enablePagefind) {
374
- console.log("SSG: Indexing with Pagefind...");
375
- try {
376
- const pagefindBin = join(dirname(fileURLToPath(import.meta.resolve("pagefind"))), "..", "lib", "runner", "bin.cjs");
377
- const { execFileSync } = await import("child_process");
378
- execFileSync(process.execPath, [
379
- pagefindBin,
380
- "--site",
381
- outDir
382
- ], { stdio: "inherit" });
383
- } catch {
384
- console.warn("SSG: Pagefind not found, skipping search indexing");
385
- }
386
- }
387
- console.log(`SSG: Done — ${routes.length} pages generated`);
449
+ if (enablePagefind) await runPagefindIndexing(outDir);
450
+ console.log(`SSG: Done ${pageCount} pages generated`);
388
451
  }
389
452
  }];
390
453
  }
391
454
  //#endregion
392
- //#region src/vite/index.ts
393
- const DEFAULT_MODULE_ID = "virtual:content";
455
+ //#region src/vite/dts.ts
394
456
  function stripExtension(filePath) {
395
457
  return filePath.replace(/\.(c|m)?[jt]sx?$/u, "");
396
458
  }
397
- function normalizePath(value) {
459
+ function normalizePath$1(value) {
398
460
  return value.replace(/\\/g, "/");
399
461
  }
400
- function isPathWithin(parent, candidate) {
401
- const rel = normalizePath(relative(parent, candidate));
402
- return rel === "" || !rel.startsWith("..") && !rel.startsWith("/");
403
- }
404
- function commonDirectory(paths) {
405
- const normalized = paths.map((path) => normalizePath(resolve(path)));
406
- if (normalized.length === 0) return process.cwd();
407
- if (normalized.length === 1) return normalized[0];
408
- const segments = normalized.map((path) => path.split("/").filter(Boolean));
409
- const shared = [];
410
- const first = segments[0];
411
- for (let index = 0; index < first.length; index += 1) {
412
- const segment = first[index];
413
- if (segments.every((parts) => parts[index] === segment)) {
414
- shared.push(segment);
415
- continue;
416
- }
417
- break;
418
- }
419
- if (shared.length === 0) return resolve("/");
420
- return resolve(`/${shared.join("/")}`);
421
- }
422
462
  function resolveDtsPath(projectRoot, dts) {
423
463
  if (dts === false) return "";
424
464
  if (typeof dts === "string") return resolve(projectRoot, dts);
@@ -429,7 +469,7 @@ function resolveDtsPath(projectRoot, dts) {
429
469
  }
430
470
  function createDtsSource(moduleId, collectionNames, dtsPath, configPath) {
431
471
  return `// Generated by @pagesmith/core/vite. Do not edit manually.
432
- type __PagesmithCollections = typeof import('${normalizePath(stripExtension(relative(dirname(dtsPath), configPath)).replace(/^[^.]/u, "./$&"))}').default
472
+ type __PagesmithCollections = typeof import('${normalizePath$1(stripExtension(relative(dirname(dtsPath), configPath)).replace(/^[^.]/u, "./$&"))}').default
433
473
 
434
474
  declare module '${moduleId}' {
435
475
  const content: import('@pagesmith/core/vite').ContentModuleMap<__PagesmithCollections>
@@ -444,6 +484,34 @@ ${collectionNames.map((name) => `declare module '${moduleId}/${name}' {
444
484
  }`).join("\n\n")}
445
485
  `;
446
486
  }
487
+ //#endregion
488
+ //#region src/vite/content-plugin.ts
489
+ const DEFAULT_MODULE_ID = "virtual:content";
490
+ function normalizePath(value) {
491
+ return value.replace(/\\/g, "/");
492
+ }
493
+ function isPathWithin(parent, candidate) {
494
+ const rel = normalizePath(relative(parent, candidate));
495
+ return rel === "" || !rel.startsWith("..") && !rel.startsWith("/");
496
+ }
497
+ function commonDirectory(paths) {
498
+ const normalized = paths.map((path) => normalizePath(resolve(path)));
499
+ if (normalized.length === 0) return process.cwd();
500
+ if (normalized.length === 1) return normalized[0];
501
+ const segments = normalized.map((path) => path.split("/").filter(Boolean));
502
+ const shared = [];
503
+ const first = segments[0];
504
+ for (let index = 0; index < first.length; index += 1) {
505
+ const segment = first[index];
506
+ if (segments.every((parts) => parts[index] === segment)) {
507
+ shared.push(segment);
508
+ continue;
509
+ }
510
+ break;
511
+ }
512
+ if (shared.length === 0) return resolve("/");
513
+ return resolve(`/${shared.join("/")}`);
514
+ }
447
515
  async function serializeCollection(layer, collectionName, collectionDef, contentRoot) {
448
516
  const entries = await layer.getCollection(collectionName);
449
517
  const loader = resolveLoader(collectionDef.loader);