@riebeckite/honox 0.0.1

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/index.js ADDED
@@ -0,0 +1,1091 @@
1
+ // src/config_loader.ts
2
+ import fs2 from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path2 from "node:path";
5
+ import { build as buildWithEsbuild } from "esbuild";
6
+
7
+ // src/workspace_packages.ts
8
+ import fs from "node:fs";
9
+ import path from "node:path";
10
+ function createWorkspacePackageAliases(workspaceRoot) {
11
+ const packagesRoot = path.join(workspaceRoot, "packages");
12
+ if (!fs.existsSync(packagesRoot)) return [];
13
+ return findWorkspacePackageDirectories(packagesRoot).flatMap(
14
+ (packageDirectory) => {
15
+ const packageJsonPath = path.join(packageDirectory, "package.json");
16
+ const packageJson2 = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
17
+ if (typeof packageJson2.name !== "string") return [];
18
+ return createPackageAliases(
19
+ packageJson2.name,
20
+ packageDirectory,
21
+ packageJson2
22
+ );
23
+ }
24
+ );
25
+ }
26
+ function workspacePackageResolver(workspaceRoot) {
27
+ const aliases = createWorkspacePackageAliases(workspaceRoot);
28
+ return {
29
+ name: "workspace-package-resolver",
30
+ setup(buildApi) {
31
+ buildApi.onResolve({ filter: /.*/ }, (args) => {
32
+ const alias = aliases.find(
33
+ (currentAlias) => currentAlias.find.test(args.path)
34
+ );
35
+ return alias ? { path: alias.replacement } : null;
36
+ });
37
+ }
38
+ };
39
+ }
40
+ function findWorkspacePackageDirectories(rootDirectory) {
41
+ const packageJsonPath = path.join(rootDirectory, "package.json");
42
+ if (fs.existsSync(packageJsonPath)) return [rootDirectory];
43
+ return fs.readdirSync(rootDirectory, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap(
44
+ (entry) => findWorkspacePackageDirectories(path.join(rootDirectory, entry.name))
45
+ );
46
+ }
47
+ function createPackageAliases(packageName, packageDirectory, packageJson2) {
48
+ const aliases = [];
49
+ const mainEntry = resolveExportsEntry(packageJson2.exports) ?? packageJson2.main;
50
+ if (mainEntry) {
51
+ aliases.push({
52
+ find: new RegExp(`^${escapeRegExp(packageName)}$`),
53
+ replacement: path.join(packageDirectory, mainEntry)
54
+ });
55
+ }
56
+ if (!packageJson2.exports || typeof packageJson2.exports !== "object") {
57
+ return aliases;
58
+ }
59
+ for (const [subpath, target] of Object.entries(packageJson2.exports)) {
60
+ if (subpath === "." || !subpath.startsWith(".")) continue;
61
+ const resolvedTarget = resolveExportTarget(target);
62
+ if (!resolvedTarget) continue;
63
+ aliases.push({
64
+ find: new RegExp(`^${escapeRegExp(packageName + subpath.slice(1))}$`),
65
+ replacement: path.join(packageDirectory, resolvedTarget)
66
+ });
67
+ }
68
+ return aliases;
69
+ }
70
+ function resolveExportsEntry(exportsField) {
71
+ if (typeof exportsField === "string") return exportsField;
72
+ if (!exportsField || typeof exportsField !== "object") return void 0;
73
+ const rootTarget = exportsField["."];
74
+ if (rootTarget !== void 0) return resolveExportTarget(rootTarget);
75
+ const hasSubpaths = Object.keys(exportsField).some(
76
+ (key) => key.startsWith(".")
77
+ );
78
+ if (hasSubpaths) return void 0;
79
+ return resolveExportTarget(exportsField);
80
+ }
81
+ var exportConditionOrder = [
82
+ "source",
83
+ "import",
84
+ "module",
85
+ "browser",
86
+ "default",
87
+ "node",
88
+ "require",
89
+ "types"
90
+ ];
91
+ function resolveExportTarget(target) {
92
+ if (typeof target === "string") return target;
93
+ if (!target || typeof target !== "object") return void 0;
94
+ const record = target;
95
+ for (const condition of exportConditionOrder) {
96
+ if (condition in record) {
97
+ const resolved = resolveExportTarget(record[condition]);
98
+ if (resolved) return resolved;
99
+ }
100
+ }
101
+ for (const value of Object.values(record)) {
102
+ const resolved = resolveExportTarget(value);
103
+ if (resolved) return resolved;
104
+ }
105
+ return void 0;
106
+ }
107
+ function escapeRegExp(value) {
108
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
109
+ }
110
+
111
+ // src/config_loader.ts
112
+ async function loadRiebeckiteConfig(options) {
113
+ const configFile = options.configFile ?? "riebeckite.config.ts";
114
+ const temporaryDirectory = await fs2.mkdtemp(
115
+ path2.join(os.tmpdir(), "riebeckite-config-")
116
+ );
117
+ const outputFile = path2.join(temporaryDirectory, "config.mjs");
118
+ try {
119
+ await buildWithEsbuild({
120
+ stdin: {
121
+ contents: `
122
+ import rawConfig from ${JSON.stringify(path2.resolve(options.configRoot, configFile))};
123
+ import { resolveConfigModule } from "@riebeckite/core";
124
+ export default resolveConfigModule(rawConfig);
125
+ `,
126
+ resolveDir: options.configRoot,
127
+ loader: "ts"
128
+ },
129
+ outfile: outputFile,
130
+ bundle: true,
131
+ platform: "node",
132
+ format: "esm",
133
+ banner: {
134
+ js: 'import { createRequire as __riebeckiteCreateRequire } from "node:module"; const require = __riebeckiteCreateRequire(import.meta.url);'
135
+ },
136
+ plugins: options.workspaceRoot ? [workspacePackageResolver(options.workspaceRoot)] : []
137
+ });
138
+ const module = await import(`${pathToFileUrl(outputFile)}?t=${Date.now()}`);
139
+ return module.default;
140
+ } finally {
141
+ await fs2.rm(temporaryDirectory, { recursive: true, force: true });
142
+ }
143
+ }
144
+ function resolveHonoxConfig(config, appRoot) {
145
+ return {
146
+ ...config,
147
+ content: {
148
+ ...config.content,
149
+ directory: path2.resolve(appRoot, config.content.directory)
150
+ }
151
+ };
152
+ }
153
+ function pathToFileUrl(filePath) {
154
+ return `file:///${filePath.replace(/\\/g, "/").replace(/^([A-Za-z]):/, "$1:")}`;
155
+ }
156
+
157
+ // src/ssg_plugin.ts
158
+ import { defaultExtensionMap, toSSG } from "hono/ssg";
159
+ import { relative } from "node:path";
160
+ import { createServer } from "vite";
161
+ var defaultEntry = "./src/index.tsx";
162
+ function riebeckiteSsg(options = {}) {
163
+ const virtualId = "virtual:riebeckite-ssg-void-entry";
164
+ const resolvedVirtualId = `\0${virtualId}`;
165
+ const entry = options.entry ?? defaultEntry;
166
+ let resolvedConfig;
167
+ return {
168
+ name: "riebeckite-ssg",
169
+ apply: "build",
170
+ enforce: "post",
171
+ config() {
172
+ return {
173
+ build: {
174
+ rollupOptions: {
175
+ input: [virtualId]
176
+ }
177
+ }
178
+ };
179
+ },
180
+ configResolved(config) {
181
+ resolvedConfig = config;
182
+ },
183
+ resolveId(id) {
184
+ return id === virtualId ? resolvedVirtualId : void 0;
185
+ },
186
+ load(id) {
187
+ return id === resolvedVirtualId ? 'console.log("suppress empty chunk message")' : void 0;
188
+ },
189
+ async generateBundle(_outputOptions, bundle) {
190
+ const config = resolvedConfig;
191
+ if (!config) {
192
+ throw new Error("Riebeckite SSG could not resolve the Vite configuration.");
193
+ }
194
+ removeVirtualEntryChunk(bundle, resolvedVirtualId);
195
+ const server2 = await createServer({
196
+ root: config.root,
197
+ define: config.define,
198
+ resolve: {
199
+ ...config.resolve,
200
+ builtins: [...config.resolve.builtins, /^node:/]
201
+ },
202
+ plugins: [],
203
+ build: { ssr: true },
204
+ mode: config.mode
205
+ });
206
+ try {
207
+ const module = await server2.ssrLoadModule(entry);
208
+ const app = module.default;
209
+ if (!app) {
210
+ throw new Error(`Failed to find a default export from ${entry}.`);
211
+ }
212
+ const generatedHtml = [];
213
+ const result = await toSSG(
214
+ app,
215
+ {
216
+ writeFile: async (filePath, data) => {
217
+ const fileName = relative(config.build.outDir, filePath).replaceAll(
218
+ "\\",
219
+ "/"
220
+ );
221
+ if (fileName.endsWith(".html") && typeof data === "string") {
222
+ generatedHtml.push({ path: fileName, html: data });
223
+ }
224
+ this.emitFile({
225
+ type: "asset",
226
+ fileName,
227
+ source: data
228
+ });
229
+ },
230
+ async mkdir() {
231
+ }
232
+ },
233
+ {
234
+ dir: config.build.outDir,
235
+ plugins: options.plugins ?? [],
236
+ extensionMap: options.extensionMap ?? defaultExtensionMap
237
+ }
238
+ );
239
+ if (!result.success) throw result.error;
240
+ await emitGeneratedOutputs(
241
+ module,
242
+ (asset) => this.emitFile(asset)
243
+ );
244
+ inspectGeneratedHtmlPages(module, generatedHtml, {
245
+ warn: (message) => this.warn(message),
246
+ info: (message) => this.info(message)
247
+ });
248
+ } finally {
249
+ await server2.close();
250
+ }
251
+ }
252
+ };
253
+ }
254
+ async function emitGeneratedOutputs(module, emit) {
255
+ const content = module.content;
256
+ if (!content) return;
257
+ const manifest = await content.getManifest();
258
+ for (const output of manifest.generatedOutputs ?? []) {
259
+ emit({
260
+ type: "asset",
261
+ fileName: output.path,
262
+ source: output.content
263
+ });
264
+ }
265
+ }
266
+ function inspectGeneratedHtmlPages(module, pages, log) {
267
+ const plugins = module.config?.plugins ?? [];
268
+ for (const plugin of plugins) {
269
+ const inspector = plugin.inspectGeneratedHtml;
270
+ if (typeof inspector !== "function") continue;
271
+ for (const page of pages) {
272
+ for (const diagnostic of inspector(page) ?? []) {
273
+ const label = `[${plugin.name ?? "plugin"}] ${page.path}: ${diagnostic.message}`;
274
+ if (diagnostic.severity === "info") log.info(label);
275
+ else log.warn(label);
276
+ }
277
+ }
278
+ }
279
+ }
280
+ function removeVirtualEntryChunk(bundle, virtualEntryId) {
281
+ for (const chunk of Object.values(bundle)) {
282
+ if (chunk.type === "chunk" && chunk.moduleIds?.includes(virtualEntryId)) {
283
+ delete bundle[chunk.fileName];
284
+ }
285
+ }
286
+ }
287
+
288
+ // src/ssg.ts
289
+ var defaultSsgEntry = "./app/server.ts";
290
+ function riebeckiteSsgExtensionMap() {
291
+ return {
292
+ "application/atom+xml": "xml",
293
+ "application/json": "json",
294
+ "application/rss+xml": "xml",
295
+ "application/xml": "xml",
296
+ "text/html": "html",
297
+ "text/plain": "txt",
298
+ "text/xml": "xml"
299
+ };
300
+ }
301
+ function createRiebeckiteSsg(options = {}) {
302
+ return riebeckiteSsg({
303
+ ...options,
304
+ entry: options.entry ?? defaultSsgEntry,
305
+ extensionMap: {
306
+ ...riebeckiteSsgExtensionMap(),
307
+ ...options.extensionMap
308
+ }
309
+ });
310
+ }
311
+
312
+ // src/vite_plugin.ts
313
+ import path5 from "node:path";
314
+
315
+ // src/asset_entries.ts
316
+ import fs3 from "node:fs";
317
+ import path3 from "node:path";
318
+ import { uniqueStrings } from "@riebeckite/core";
319
+ function writeRiebeckiteAssetEntries(config, paths2) {
320
+ writeGeneratedFile(paths2.pluginStyles, createPluginStylesEntry(config));
321
+ writeGeneratedFile(paths2.themeStyles, createThemeStylesEntry(config));
322
+ }
323
+ function createPluginStylesEntry(config) {
324
+ return createStylesheetEntry(
325
+ collectPluginStyleSpecifiers(config),
326
+ "Plugin default styles, in resolved plugin order. Generated by @riebeckite/honox; do not edit. Import this before the theme stylesheet so a theme can override plugin defaults."
327
+ );
328
+ }
329
+ function createThemeStylesEntry(config) {
330
+ return createStylesheetEntry(
331
+ config.theme.styles.map((style2) => style2.moduleSpecifier),
332
+ "Theme styles. Generated by @riebeckite/honox; do not edit. Import this after the plugin stylesheet so the theme wins the plugin/theme cascade."
333
+ );
334
+ }
335
+ function collectPluginStyleSpecifiers(config) {
336
+ return config.plugins.flatMap(
337
+ (plugin) => (plugin.assets ?? []).filter((asset) => asset.kind === "style").map((asset) => asset.moduleSpecifier)
338
+ );
339
+ }
340
+ function createStylesheetEntry(moduleSpecifiers, banner) {
341
+ const imports = uniqueStrings(moduleSpecifiers).map((specifier) => `@import ${JSON.stringify(specifier)};`).join("\n");
342
+ if (!banner) return imports;
343
+ const header = `/* ${banner.replace(/\*\//g, "* /")} */`;
344
+ return imports ? `${header}
345
+ ${imports}
346
+ ` : `${header}
347
+ `;
348
+ }
349
+ function writeGeneratedFile(filePath, contents) {
350
+ fs3.mkdirSync(path3.dirname(filePath), { recursive: true });
351
+ fs3.writeFileSync(filePath, contents);
352
+ }
353
+
354
+ // src/client_module.ts
355
+ var clientModuleId = "virtual:riebeckite/client";
356
+ var resolvedClientModuleId = `\0${clientModuleId}`;
357
+ function riebeckiteClientModule(getConfig) {
358
+ return {
359
+ name: "riebeckite-client-module",
360
+ resolveId(id) {
361
+ if (id === clientModuleId) return resolvedClientModuleId;
362
+ return null;
363
+ },
364
+ load(id) {
365
+ if (id !== resolvedClientModuleId) return null;
366
+ return createClientModule(getConfig());
367
+ }
368
+ };
369
+ }
370
+ function createClientModule(config) {
371
+ const imports = [];
372
+ const initializers = [];
373
+ for (const [index, entry] of config.plugins.flatMap((plugin) => plugin.clientEntries ?? []).entries()) {
374
+ const localName = `pluginClientInitializer${index}`;
375
+ const importTarget = entry.exportName ? `{ ${entry.exportName} as ${localName} }` : localName;
376
+ imports.push(
377
+ `import ${importTarget} from ${JSON.stringify(entry.moduleSpecifier)};`
378
+ );
379
+ initializers.push(localName);
380
+ }
381
+ return `${imports.join("\n")}
382
+
383
+ export function initRiebeckiteClient() {
384
+ ${initializers.map((name) => ` ${name}();`).join("\n")}
385
+ }
386
+ `;
387
+ }
388
+
389
+ // src/vite_runner.ts
390
+ import fs4 from "node:fs/promises";
391
+ import path4 from "node:path";
392
+ import { build, createServer as createServer2 } from "vite";
393
+ var viteConfigFileNames = [
394
+ "vite.config.ts",
395
+ "vite.config.js",
396
+ "vite.config.mjs"
397
+ ];
398
+ async function resolveHonoxApplicationRoot(configRoot) {
399
+ const roots = await findViteApplicationRoots(configRoot);
400
+ if (roots.length === 1) return roots[0];
401
+ if (roots.length === 0) {
402
+ throw new HonoxApplicationRootError(
403
+ `Could not find a Vite application under ${configRoot}.`
404
+ );
405
+ }
406
+ throw new HonoxApplicationRootError(
407
+ `Found multiple Vite applications under ${configRoot}: ${roots.join(", ")}.`
408
+ );
409
+ }
410
+ var HonoxApplicationRootError = class extends Error {
411
+ constructor(message) {
412
+ super(message);
413
+ this.name = "HonoxApplicationRootError";
414
+ }
415
+ };
416
+ async function resolveHonoxApplication(options) {
417
+ const configRoot = path4.resolve(options.configRoot);
418
+ const appRoot = options.appRoot ? path4.resolve(options.appRoot) : await resolveHonoxApplicationRoot(configRoot);
419
+ const workspaceRoot = options.workspaceRoot ?? await findWorkspaceRoot(configRoot);
420
+ const rawConfig = await loadRiebeckiteConfig({
421
+ configRoot,
422
+ configFile: options.configFile,
423
+ workspaceRoot
424
+ });
425
+ const config = resolveHonoxConfig(rawConfig, appRoot);
426
+ return {
427
+ config,
428
+ configRoot,
429
+ appRoot,
430
+ contentRoot: config.content.directory
431
+ };
432
+ }
433
+ async function findWorkspaceRoot(configRoot) {
434
+ try {
435
+ const workspaceConfig = path4.join(configRoot, "pnpm-workspace.yaml");
436
+ return (await fs4.stat(workspaceConfig)).isFile() ? configRoot : void 0;
437
+ } catch {
438
+ return void 0;
439
+ }
440
+ }
441
+ async function startHonoxDevServer(options) {
442
+ const server2 = await createServer2({ root: options.root });
443
+ await server2.listen();
444
+ server2.printUrls();
445
+ await waitForShutdown(server2);
446
+ }
447
+ async function buildHonoxApplication(options) {
448
+ await runTraced(
449
+ options.tracer,
450
+ "integration.honox.client_build",
451
+ () => build({ root: options.root, mode: "client" })
452
+ );
453
+ await runTraced(
454
+ options.tracer,
455
+ "integration.honox.server_build",
456
+ () => build({ root: options.root })
457
+ );
458
+ }
459
+ async function runTraced(tracer, name, buildTask) {
460
+ return tracer ? await tracer.span(name, {}, buildTask) : await buildTask();
461
+ }
462
+ async function waitForShutdown(server2) {
463
+ await new Promise((resolve) => {
464
+ const close = () => {
465
+ void server2.close().finally(resolve);
466
+ };
467
+ process.once("SIGINT", close);
468
+ process.once("SIGTERM", close);
469
+ });
470
+ }
471
+ async function findViteApplicationRoots(root) {
472
+ const directories = [root];
473
+ const applicationRoots = [];
474
+ while (directories.length > 0) {
475
+ const directory = directories.pop();
476
+ if (!directory) continue;
477
+ if (await hasViteConfig(directory)) applicationRoots.push(directory);
478
+ const entries = await fs4.readdir(directory, { withFileTypes: true });
479
+ for (const entry of entries) {
480
+ if (!entry.isDirectory() || entry.name === "node_modules" || entry.name === ".git" || entry.name === "tests") {
481
+ continue;
482
+ }
483
+ directories.push(path4.join(directory, entry.name));
484
+ }
485
+ }
486
+ return applicationRoots;
487
+ }
488
+ async function hasViteConfig(directory) {
489
+ for (const fileName of viteConfigFileNames) {
490
+ try {
491
+ if ((await fs4.stat(path4.join(directory, fileName))).isFile()) return true;
492
+ } catch {
493
+ }
494
+ }
495
+ return false;
496
+ }
497
+
498
+ // src/vite_plugin.ts
499
+ var defaultSsrExternals = [
500
+ "extend",
501
+ "debug",
502
+ "node:fs/promises",
503
+ "node:path",
504
+ "parse-numeric-range",
505
+ "slugify",
506
+ "vfile-matter"
507
+ ];
508
+ function riebeckite(options = {}) {
509
+ let resolvedConfig;
510
+ const getConfig = () => {
511
+ if (!resolvedConfig) {
512
+ throw new Error("Riebeckite config has not been loaded yet.");
513
+ }
514
+ return resolvedConfig;
515
+ };
516
+ return [
517
+ {
518
+ name: "riebeckite-host-integration",
519
+ async config(userConfig) {
520
+ const root = userConfig.root ? path5.resolve(userConfig.root) : process.cwd();
521
+ const appRoot = options.appRoot ?? root;
522
+ const application = await resolveHonoxApplication({
523
+ configRoot: options.configRoot ?? appRoot,
524
+ configFile: options.configFile,
525
+ appRoot,
526
+ workspaceRoot: options.workspaceRoot
527
+ });
528
+ resolvedConfig = application.config;
529
+ writeRiebeckiteAssetEntries(resolvedConfig, {
530
+ pluginStyles: path5.join(appRoot, "app/.riebeckite/plugin-styles.css"),
531
+ themeStyles: path5.join(appRoot, "app/.riebeckite/theme-styles.css")
532
+ });
533
+ return {
534
+ define: {
535
+ "process.env.RIEBECKITE_APP_ROOT": JSON.stringify(appRoot)
536
+ },
537
+ resolve: {
538
+ alias: options.workspaceRoot ? createWorkspacePackageAliases(options.workspaceRoot) : []
539
+ }
540
+ };
541
+ }
542
+ },
543
+ riebeckiteClientModule(getConfig)
544
+ ];
545
+ }
546
+ function riebeckiteVite(options = {}) {
547
+ const { ssg, ssrExternals, ...integration } = options;
548
+ return [
549
+ ...riebeckite(integration),
550
+ riebeckiteSsrExternals([...defaultSsrExternals, ...ssrExternals ?? []]),
551
+ createRiebeckiteSsg(ssg)
552
+ ];
553
+ }
554
+ function riebeckiteSsrExternals(externals) {
555
+ return {
556
+ name: "riebeckite-ssr-externals",
557
+ config() {
558
+ return {
559
+ environments: {
560
+ ssr: {
561
+ resolve: {
562
+ external: [...externals]
563
+ }
564
+ }
565
+ }
566
+ };
567
+ }
568
+ };
569
+ }
570
+
571
+ // src/scaffold/index.ts
572
+ import fs5 from "node:fs/promises";
573
+ import path6 from "node:path";
574
+
575
+ // src/scaffold/templates.ts
576
+ function siteTemplateFiles(variables) {
577
+ return [
578
+ { path: "package.json", content: packageJson(variables) },
579
+ { path: "riebeckite.config.ts", content: riebeckiteConfig(variables) },
580
+ { path: "vite.config.ts", content: viteConfig() },
581
+ { path: "tsconfig.json", content: tsconfig() },
582
+ { path: ".gitignore", content: gitignore() },
583
+ { path: "README.md", content: readme(variables) },
584
+ { path: "content/index.md", content: indexContent(variables) },
585
+ { path: "app/server.ts", content: server() },
586
+ { path: "app/client.ts", content: client() },
587
+ { path: "app/config.ts", content: appConfig() },
588
+ { path: "app/content.ts", content: appContent() },
589
+ { path: "app/constants/paths.ts", content: paths() },
590
+ { path: "app/global.d.ts", content: globalDeclarations() },
591
+ { path: "app/style.css", content: style() },
592
+ { path: "app/components/site-header.tsx", content: siteHeader() },
593
+ { path: "app/components/article.tsx", content: article() },
594
+ { path: "app/routes/_renderer.tsx", content: renderer() },
595
+ { path: "app/routes/index.tsx", content: indexRoute() },
596
+ { path: "app/routes/[slug{.+}].tsx", content: slugRoute() }
597
+ ];
598
+ }
599
+ function packageJson(variables) {
600
+ return `${JSON.stringify(
601
+ {
602
+ name: variables.name,
603
+ private: true,
604
+ version: "0.0.0",
605
+ type: "module",
606
+ scripts: {
607
+ dev: "riebeckite dev",
608
+ build: "riebeckite build",
609
+ check: "riebeckite check",
610
+ doctor: "riebeckite doctor",
611
+ inspect: "riebeckite inspect"
612
+ },
613
+ dependencies: {
614
+ "@riebeckite/core": "^0.0.1",
615
+ "@riebeckite/honox": "^0.0.1",
616
+ "@riebeckite/plugin-obsidian-markdown": "^0.0.1",
617
+ "@riebeckite/theme-default": "^0.0.1",
618
+ hono: "^4.12.25",
619
+ honox: "0.1.56"
620
+ },
621
+ devDependencies: {
622
+ "@hono/vite-build": "^1.11.1",
623
+ "@riebeckite/cli": "^0.0.1",
624
+ "@types/node": "^24.5.2",
625
+ typescript: "^5.0.0",
626
+ vite: "^8.0.9"
627
+ }
628
+ },
629
+ null,
630
+ 2
631
+ )}
632
+ `;
633
+ }
634
+ function riebeckiteConfig(variables) {
635
+ return `import { defineConfig } from "@riebeckite/core";
636
+ import { obsidianMarkdown } from "@riebeckite/plugin-obsidian-markdown";
637
+ import { defaultTheme } from "@riebeckite/theme-default";
638
+
639
+ export default defineConfig({
640
+ site: {
641
+ title: ${JSON.stringify(variables.title)},
642
+ description: ${JSON.stringify(variables.description)},
643
+ baseUrl: ${JSON.stringify(variables.baseUrl)},
644
+ locale: ${JSON.stringify(variables.locale)},
645
+ },
646
+ content: {
647
+ directory: "content",
648
+ },
649
+ theme: defaultTheme(),
650
+ plugins: [obsidianMarkdown()],
651
+ });
652
+ `;
653
+ }
654
+ function viteConfig() {
655
+ return `import path from "node:path";
656
+ import { fileURLToPath } from "node:url";
657
+ import build from "@hono/vite-build/node";
658
+ import {
659
+ riebeckite,
660
+ riebeckiteSsg,
661
+ riebeckiteSsgExtensionMap,
662
+ } from "@riebeckite/honox";
663
+ import honox from "honox/vite";
664
+ import { defineConfig } from "vite";
665
+
666
+ const appRoot = fileURLToPath(new URL(".", import.meta.url));
667
+
668
+ export default defineConfig({
669
+ plugins: [
670
+ honox({
671
+ client: { input: ["/app/client.ts", "/app/style.css"] },
672
+ }),
673
+ riebeckite({ appRoot }),
674
+ build(),
675
+ riebeckiteSsg({
676
+ entry: path.join(appRoot, "app/server.ts"),
677
+ extensionMap: riebeckiteSsgExtensionMap(),
678
+ }),
679
+ ],
680
+ environments: {
681
+ ssr: {
682
+ resolve: {
683
+ external: [
684
+ "extend",
685
+ "debug",
686
+ "node:fs/promises",
687
+ "node:path",
688
+ "parse-numeric-range",
689
+ "slugify",
690
+ "vfile-matter",
691
+ ],
692
+ },
693
+ },
694
+ },
695
+ });
696
+ `;
697
+ }
698
+ function tsconfig() {
699
+ return `${JSON.stringify(
700
+ {
701
+ compilerOptions: {
702
+ target: "ES2022",
703
+ module: "ESNext",
704
+ moduleResolution: "Bundler",
705
+ lib: ["ES2022", "DOM", "DOM.Iterable"],
706
+ jsx: "react-jsx",
707
+ jsxImportSource: "hono/jsx",
708
+ types: ["node", "vite/client"],
709
+ strict: true,
710
+ noEmit: true,
711
+ esModuleInterop: true,
712
+ allowSyntheticDefaultImports: true,
713
+ resolveJsonModule: true,
714
+ skipLibCheck: true
715
+ },
716
+ include: [
717
+ "app/**/*.ts",
718
+ "app/**/*.tsx",
719
+ "riebeckite.config.ts",
720
+ "vite.config.ts"
721
+ ]
722
+ },
723
+ null,
724
+ 2
725
+ )}
726
+ `;
727
+ }
728
+ function gitignore() {
729
+ return ["node_modules/", "dist/", ".riebeckite/", ""].join("\n");
730
+ }
731
+ function readme(variables) {
732
+ return [
733
+ `# ${variables.title}`,
734
+ "",
735
+ "A minimal Riebeckite site generated by `riebeckite init`.",
736
+ "",
737
+ "## Commands",
738
+ "",
739
+ "```sh",
740
+ "pnpm install",
741
+ "pnpm exec riebeckite check",
742
+ "pnpm exec riebeckite dev",
743
+ "pnpm exec riebeckite build",
744
+ "```",
745
+ "",
746
+ "Content lives in `content/`. The site owns `app/routes`, `app/components`,",
747
+ "`app/islands`, and `app/style.css`; see the Riebeckite HonoX integration",
748
+ "documentation for the site application contract.",
749
+ ""
750
+ ].join("\n");
751
+ }
752
+ function indexContent(variables) {
753
+ return [
754
+ "---",
755
+ "publish: true",
756
+ "---",
757
+ "",
758
+ `# ${variables.title}`,
759
+ "",
760
+ "Welcome to your Riebeckite site.",
761
+ "",
762
+ "Edit `content/index.md` to change this page, and add more Markdown files",
763
+ "next to it. Files need `publish: true` frontmatter before they appear.",
764
+ ""
765
+ ].join("\n");
766
+ }
767
+ function server() {
768
+ return `import { mountRiebeckiteEndpoints } from "@riebeckite/honox/server";
769
+ import { createApp } from "honox/server";
770
+ import { config } from "./config";
771
+ import { content } from "./content";
772
+
773
+ const app = createApp({
774
+ init: (app) => {
775
+ mountRiebeckiteEndpoints(app, { config, content });
776
+ },
777
+ });
778
+
779
+ export default app;
780
+ `;
781
+ }
782
+ function client() {
783
+ return `import { initRiebeckiteClient } from "virtual:riebeckite/client";
784
+ import { createClient } from "honox/client";
785
+
786
+ createClient();
787
+
788
+ if (document.readyState === "loading") {
789
+ document.addEventListener("DOMContentLoaded", initPage, { once: true });
790
+ } else {
791
+ initPage();
792
+ }
793
+
794
+ function initPage() {
795
+ initRiebeckiteClient();
796
+ }
797
+ `;
798
+ }
799
+ function appConfig() {
800
+ return `import path from "node:path";
801
+ import { fileURLToPath } from "node:url";
802
+ import { resolveConfigModule } from "@riebeckite/core";
803
+ import * as rawConfigModule from "../riebeckite.config";
804
+
805
+ const appRoot =
806
+ process.env.RIEBECKITE_APP_ROOT ??
807
+ fileURLToPath(new URL("../", import.meta.url));
808
+ const resolvedConfig = resolveConfigModule(rawConfigModule);
809
+
810
+ export const config = {
811
+ ...resolvedConfig,
812
+ content: {
813
+ ...resolvedConfig.content,
814
+ directory: path.resolve(appRoot, resolvedConfig.content.directory),
815
+ },
816
+ };
817
+ `;
818
+ }
819
+ function appContent() {
820
+ return `import { ContentManager } from "@riebeckite/core";
821
+ import { config } from "./config";
822
+ import { CONTENT_DIR } from "./constants/paths";
823
+
824
+ export const content = new ContentManager(
825
+ CONTENT_DIR,
826
+ config.content.exclude,
827
+ {
828
+ config,
829
+ plugins: config.plugins,
830
+ },
831
+ );
832
+ `;
833
+ }
834
+ function paths() {
835
+ return `import path from "node:path";
836
+ import { fileURLToPath } from "node:url";
837
+ import { config } from "../config";
838
+
839
+ const appRoot =
840
+ process.env.RIEBECKITE_APP_ROOT ??
841
+ fileURLToPath(new URL("../../", import.meta.url));
842
+
843
+ export const CONTENT_DIR = path.resolve(appRoot, config.content.directory);
844
+ export const ASSETS_ROOT = "public/";
845
+ `;
846
+ }
847
+ function globalDeclarations() {
848
+ return `declare module "virtual:riebeckite/client" {
849
+ export function initRiebeckiteClient(): void;
850
+ }
851
+ `;
852
+ }
853
+ function style() {
854
+ return `@import "./.riebeckite/plugin-styles.css";
855
+ @import "./.riebeckite/theme-styles.css";
856
+
857
+ body {
858
+ margin: 0;
859
+ font-family: system-ui, sans-serif;
860
+ }
861
+
862
+ .site-header {
863
+ display: flex;
864
+ gap: 1rem;
865
+ padding: 1rem;
866
+ border-bottom: 1px solid #d1d5db;
867
+ }
868
+
869
+ .site-header__home {
870
+ color: #111827;
871
+ font-weight: 700;
872
+ text-decoration: none;
873
+ }
874
+
875
+ .site-article {
876
+ display: block;
877
+ max-width: 48rem;
878
+ padding: 2rem 1rem;
879
+ margin: 0 auto;
880
+ }
881
+ `;
882
+ }
883
+ function siteHeader() {
884
+ return `import { config } from "../config";
885
+
886
+ export function SiteHeader() {
887
+ return (
888
+ <header class="site-header">
889
+ <a href="/" class="site-header__home">
890
+ {config.site.title}
891
+ </a>
892
+ </header>
893
+ );
894
+ }
895
+ `;
896
+ }
897
+ function article() {
898
+ return `import type { PostContent } from "@riebeckite/core";
899
+ import { Article, ArticleContent, ArticleLayout } from "@riebeckite/honox/ui";
900
+
901
+ export function SiteArticle({ post }: { post: PostContent }) {
902
+ return (
903
+ <Article class="site-article">
904
+ <ArticleLayout>
905
+ <ArticleContent>
906
+ <div dangerouslySetInnerHTML={{ __html: post.html ?? "" }} />
907
+ </ArticleContent>
908
+ </ArticleLayout>
909
+ </Article>
910
+ );
911
+ }
912
+ `;
913
+ }
914
+ function renderer() {
915
+ return `import { jsxRenderer } from "hono/jsx-renderer";
916
+ import { Link, Script } from "honox/server";
917
+ import { SiteHeader } from "../components/site-header";
918
+ import { config } from "../config";
919
+
920
+ function themeAttributes() {
921
+ const { theme } = config;
922
+
923
+ return {
924
+ ...theme.attributes,
925
+ "data-theme": theme.colorMode === "system" ? undefined : theme.colorMode,
926
+ "data-theme-name": theme.name,
927
+ "data-typography": theme.typography,
928
+ "data-article-layout": theme.articleLayout,
929
+ };
930
+ }
931
+
932
+ export default jsxRenderer(({ children }) => {
933
+ return (
934
+ <html lang={config.site.locale} {...themeAttributes()}>
935
+ <head>
936
+ <meta charset="utf-8" />
937
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
938
+ <title>{config.site.title}</title>
939
+ <Link href="/app/style.css" rel="stylesheet" />
940
+ <Script src="/app/client.ts" async />
941
+ </head>
942
+ <body class="riebeckite-page">
943
+ <SiteHeader />
944
+ {children}
945
+ </body>
946
+ </html>
947
+ );
948
+ });
949
+ `;
950
+ }
951
+ function indexRoute() {
952
+ return `import { isPublished } from "@riebeckite/core";
953
+ import { createRoute } from "honox/factory";
954
+ import { SiteArticle } from "../components/article";
955
+ import { config } from "../config";
956
+ import { content } from "../content";
957
+
958
+ export default createRoute(async (c) => {
959
+ const manifest = await content.getManifest();
960
+ const indexEntry = manifest.bySlug.get("index");
961
+ if (indexEntry && indexEntry.permalink !== "/") {
962
+ return c.redirect(indexEntry.permalink, 308);
963
+ }
964
+
965
+ const post = await content.getProcessedContent("index");
966
+ if (!isPublished(config, post.frontmatter)) {
967
+ return c.notFound();
968
+ }
969
+
970
+ return c.render(<SiteArticle post={post} />);
971
+ });
972
+ `;
973
+ }
974
+ function slugRoute() {
975
+ return `import { isPublished } from "@riebeckite/core";
976
+ import {
977
+ contentRouteSsgParams,
978
+ resolveContentRoute,
979
+ } from "@riebeckite/honox/server";
980
+ import { createRoute } from "honox/factory";
981
+ import { SiteArticle } from "../components/article";
982
+ import { config } from "../config";
983
+ import { content } from "../content";
984
+
985
+ export default createRoute(
986
+ contentRouteSsgParams("/:slug{.+}", async () => {
987
+ const manifest = await content.getManifest();
988
+ return manifest.entries
989
+ .filter((entry) => isPublished(config, entry.frontmatter))
990
+ .filter((entry) => entry.permalink !== "/")
991
+ .map((entry) => ({ slug: entry.permalink.replace(/^\\/+/, "") }));
992
+ }),
993
+ async (c) => {
994
+ const requestedSlug = c.req.param("slug");
995
+ if (!requestedSlug) return c.notFound();
996
+ if (/\\.[a-zA-Z0-9]+$/.test(requestedSlug)) return c.notFound();
997
+
998
+ const manifest = await content.getManifest();
999
+ const route = resolveContentRoute(manifest, c.req.path);
1000
+ if (!route) return c.notFound();
1001
+ if (route.kind === "redirect")
1002
+ return c.redirect(route.location, route.status);
1003
+
1004
+ const post = await content.getProcessedContent(route.entry.slug);
1005
+ if (!isPublished(config, post.frontmatter)) {
1006
+ return c.notFound();
1007
+ }
1008
+
1009
+ return c.render(<SiteArticle post={post} />);
1010
+ },
1011
+ );
1012
+ `;
1013
+ }
1014
+
1015
+ // src/scaffold/index.ts
1016
+ var ScaffoldSiteError = class extends Error {
1017
+ constructor(message) {
1018
+ super(message);
1019
+ this.name = "ScaffoldSiteError";
1020
+ }
1021
+ };
1022
+ async function scaffoldRiebeckiteSite(options) {
1023
+ const targetDirectory = path6.resolve(options.targetDirectory);
1024
+ const files = siteTemplateFiles(templateVariables(options, targetDirectory));
1025
+ await assertTargetWritable(
1026
+ targetDirectory,
1027
+ files,
1028
+ options.overwrite ?? false
1029
+ );
1030
+ for (const file of files) {
1031
+ const filePath = path6.join(targetDirectory, file.path);
1032
+ await fs5.mkdir(path6.dirname(filePath), { recursive: true });
1033
+ await fs5.writeFile(filePath, file.content, "utf8");
1034
+ }
1035
+ return { targetDirectory, files: files.map((file) => file.path) };
1036
+ }
1037
+ function templateVariables(options, targetDirectory) {
1038
+ const name = normalizePackageName(
1039
+ options.name ?? path6.basename(targetDirectory)
1040
+ );
1041
+ return {
1042
+ name,
1043
+ title: options.siteTitle ?? name,
1044
+ description: options.description ?? "",
1045
+ baseUrl: options.baseUrl ?? "",
1046
+ locale: options.locale ?? "en"
1047
+ };
1048
+ }
1049
+ async function assertTargetWritable(targetDirectory, files, overwrite) {
1050
+ let entries;
1051
+ try {
1052
+ entries = await fs5.readdir(targetDirectory);
1053
+ } catch (error) {
1054
+ if (isNotFoundError(error)) return;
1055
+ throw error;
1056
+ }
1057
+ const conflicts = entries.filter(
1058
+ (entry) => files.some((file) => file.path.split("/")[0] === entry)
1059
+ );
1060
+ if (conflicts.length === 0) return;
1061
+ if (overwrite) return;
1062
+ throw new ScaffoldSiteError(
1063
+ `The target directory is not empty: ${targetDirectory}
1064
+ Existing entries: ${conflicts.slice(0, 5).join(", ")}
1065
+ Pass --force to overwrite generated files.`
1066
+ );
1067
+ }
1068
+ function normalizePackageName(value) {
1069
+ const normalized = value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[-_.]+/, "").replace(/[-_.]+$/, "");
1070
+ return normalized || "riebeckite-site";
1071
+ }
1072
+ function isNotFoundError(error) {
1073
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
1074
+ }
1075
+ export {
1076
+ ScaffoldSiteError,
1077
+ buildHonoxApplication,
1078
+ createRiebeckiteSsg,
1079
+ defaultSsgEntry,
1080
+ defaultSsrExternals,
1081
+ loadRiebeckiteConfig,
1082
+ resolveHonoxApplication,
1083
+ resolveHonoxApplicationRoot,
1084
+ resolveHonoxConfig,
1085
+ riebeckite,
1086
+ riebeckiteSsg,
1087
+ riebeckiteSsgExtensionMap,
1088
+ riebeckiteVite,
1089
+ scaffoldRiebeckiteSite,
1090
+ startHonoxDevServer
1091
+ };