@riebeckite/honox 0.0.15 → 0.0.17

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.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  export { loadRiebeckiteConfig, resolveHonoxConfig, } from "./src/config_loader.js";
2
2
  export { replaceHonoxIslandDependencyPlugin } from "./src/honox_islands.js";
3
+ export { assertGitHubRepository, deploymentTemplateFiles, type ScaffoldDeploymentOptions, } from "./src/scaffold/deployment.js";
3
4
  export type { ScaffoldSiteOptions, ScaffoldSiteResult, } from "./src/scaffold/index.js";
4
5
  export { ScaffoldSiteError, scaffoldRiebeckiteSite, } from "./src/scaffold/index.js";
5
6
  export { formatScaffoldNextSteps, type ScaffoldNextStepsOptions, } from "./src/scaffold/next-steps.js";
6
7
  export type { ScaffoldAppFileKey, ScaffoldPageKey, ScaffoldPluginSpec, ScaffoldPreset, ScaffoldPresetName, ScaffoldReadmeLevel, ScaffoldThemeSpec, } from "./src/scaffold/presets.js";
7
8
  export { empty, isScaffoldPresetName, minimal, resolveScaffoldPreset, SCAFFOLD_DEFAULT_PRESET, SCAFFOLD_PRESET_NAMES, scaffoldPresets, showcase, starter, } from "./src/scaffold/presets.js";
9
+ export { buildDefaultWranglerConfig, DEFAULT_WORKER_NAME, GITHUB_ACTIONS_SECRETS, PACKAGE_MANAGER, WORKER_NAME_MAX_LENGTH, WRANGLER_DEFAULTS, WRANGLER_VERSION, type WranglerDefaults, workerNameFromDirectory, } from "./src/scaffold/wrangler-defaults.js";
8
10
  export { createRiebeckiteSsg, defaultSsgEntry, riebeckiteSsgExtensionMap, } from "./src/ssg.js";
9
11
  export type { RiebeckiteSsgOptions } from "./src/ssg_plugin.js";
10
12
  export { riebeckiteSsg } from "./src/ssg_plugin.js";
package/dist/index.js CHANGED
@@ -254,10 +254,6 @@ function isJavaScriptModule(filePath) {
254
254
  return /\.[cm]?[jt]sx?$/.test(filePath);
255
255
  }
256
256
 
257
- // src/scaffold/index.ts
258
- import fs8 from "node:fs/promises";
259
- import path9 from "node:path";
260
-
261
257
  // src/scaffold/wrangler-defaults.ts
262
258
  var WRANGLER_DEFAULTS = {
263
259
  /** JSON schema for IDE support and validation */
@@ -271,6 +267,18 @@ var WRANGLER_DEFAULTS = {
271
267
  /** Static assets directory - Riebeckite builds to ./dist */
272
268
  assets: { directory: "./dist" }
273
269
  };
270
+ var WRANGLER_VERSION = "^4.83.0";
271
+ var DEFAULT_WORKER_NAME = "riebeckite-site";
272
+ var WORKER_NAME_MAX_LENGTH = 63;
273
+ function workerNameFromDirectory(directoryName) {
274
+ const normalized = directoryName.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
275
+ const name = normalized.slice(0, WORKER_NAME_MAX_LENGTH).replace(/-+$/g, "");
276
+ return name.length > 0 ? name : DEFAULT_WORKER_NAME;
277
+ }
278
+ function buildDefaultWranglerConfig(workerName) {
279
+ return `${JSON.stringify({ ...WRANGLER_DEFAULTS, name: workerName }, null, 2)}
280
+ `;
281
+ }
274
282
  var GITHUB_ACTIONS_SECRETS = {
275
283
  /** Cloudflare API token with Workers Scripts: Edit permission */
276
284
  CLOUDFLARE_API_TOKEN: "CLOUDFLARE_API_TOKEN",
@@ -281,6 +289,7 @@ var GITHUB_ACTIONS_SECRETS = {
281
289
  /** Optional: Token for site repo to read content repo */
282
290
  RIEBECKITE_CONTENT_READ_TOKEN: "RIEBECKITE_CONTENT_READ_TOKEN"
283
291
  };
292
+ var PACKAGE_MANAGER = "npm";
284
293
 
285
294
  // src/scaffold/deployment.ts
286
295
  var GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
@@ -324,9 +333,14 @@ function deploymentTemplateFiles(options) {
324
333
  ] : []
325
334
  ];
326
335
  }
336
+ function wranglerJsoncFile(workerName) {
337
+ return {
338
+ path: "wrangler.jsonc",
339
+ content: buildDefaultWranglerConfig(workerName)
340
+ };
341
+ }
327
342
  function wranglerConfig() {
328
- return `${JSON.stringify(WRANGLER_DEFAULTS, null, 2)}
329
- `;
343
+ return buildDefaultWranglerConfig(WRANGLER_DEFAULTS.name);
330
344
  }
331
345
  function deployWorkflow(contentRepository) {
332
346
  const contentCheckout = contentRepository === void 0 ? "" : `
@@ -428,6 +442,10 @@ jobs:
428
442
  `;
429
443
  }
430
444
 
445
+ // src/scaffold/index.ts
446
+ import fs8 from "node:fs/promises";
447
+ import path9 from "node:path";
448
+
431
449
  // src/scaffold/presets.ts
432
450
  var SCAFFOLD_LANGUAGES = [
433
451
  "en",
@@ -472,13 +490,8 @@ var l10n = np("@riebeckite/plugin-l10n", "l10n", (context) => {
472
490
  return `{ defaultLang: ${JSON.stringify(defaultLang)}, languages: ${JSON.stringify(context.languages)} }`;
473
491
  });
474
492
  var seo = np("@riebeckite/plugin-seo", "seo", {
475
- siteName: {
476
- depth: 1,
477
- value: (context) => JSON.stringify(context.variables.title)
478
- },
479
493
  sitemap: { depth: 1, value: "true" },
480
494
  robots: { depth: 2, value: "true" },
481
- defaultImage: { depth: 3, value: '"/ogp.png"' },
482
495
  feed: { depth: 3, value: "{ rss: true, atom: true, json: true }" }
483
496
  });
484
497
  var toc = np("@riebeckite/plugin-toc", "tocPlugin");
@@ -499,6 +512,17 @@ var codeEnhance = np("@riebeckite/plugin-code-enhance", "codeEnhance", {
499
512
  });
500
513
  var search = np("@riebeckite/plugin-search", "searchPlugin");
501
514
  var backlinks = np("@riebeckite/plugin-backlinks", "backlinksPlugin");
515
+ var breadcrumbs = np("@riebeckite/plugin-breadcrumbs", "breadcrumbsPlugin");
516
+ var navigationStarter = np(
517
+ "@riebeckite/plugin-navigation",
518
+ "navigation",
519
+ `{ items: [{ label: "Guide", href: "/guide" }, { label: "Examples", href: "/examples" }, { label: "Notes", href: "/notes/planning", children: [{ label: "Planning", href: "/notes/planning" }, { label: "Writing", href: "/notes/writing" }] }], secondary: [{ label: "Guide", href: "/guide" }, { label: "Examples", href: "/examples" }] }`
520
+ );
521
+ var navigationShowcase = np(
522
+ "@riebeckite/plugin-navigation",
523
+ "navigation",
524
+ `{ items: [{ label: "Guide", href: "/guide" }, { label: "Examples", href: "/examples" }, { label: "Framework", href: "/framework/plugins", children: [{ label: "Plugins", href: "/framework/plugins" }, { label: "Themes", href: "/framework/themes" }] }], secondary: [{ label: "Guide", href: "/guide" }, { label: "Examples", href: "/examples" }] }`
525
+ );
502
526
  var relatedPosts = np("@riebeckite/plugin-related-posts", "relatedPosts", {
503
527
  limit: { depth: 2, value: "5" },
504
528
  useTags: { depth: 2, value: "true" },
@@ -597,6 +621,7 @@ var taxonomy = np("@riebeckite/plugin-taxonomy", "taxonomy", {
597
621
  feeds: { depth: 3, value: "{ rss: true, atom: true, json: true }" },
598
622
  relatedLimit: { depth: 3, value: "8" }
599
623
  });
624
+ var folderPages = np("@riebeckite/plugin-folder-pages", "folderPagesPlugin");
600
625
  var autoCardLink = np(
601
626
  "@riebeckite/plugin-autocardlink",
602
627
  "autoCardLinkPlugin",
@@ -785,7 +810,7 @@ var quality = np(
785
810
  var deploy = np(
786
811
  "@riebeckite/plugin-deploy",
787
812
  "deployPlugin",
788
- (context) => `{ provider: "cloudflare-pages", baseUrl: ${JSON.stringify(context.variables.baseUrl)} }`
813
+ `{ provider: "cloudflare-pages" }`
789
814
  );
790
815
  var diagnostics = np(
791
816
  "@riebeckite/plugin-diagnostics",
@@ -851,6 +876,8 @@ var starter = {
851
876
  codeEnhance,
852
877
  search,
853
878
  backlinks,
879
+ breadcrumbs,
880
+ navigationStarter,
854
881
  relatedPosts,
855
882
  recentPosts,
856
883
  responsiveImage,
@@ -878,6 +905,8 @@ var showcase = {
878
905
  codeEnhance,
879
906
  search,
880
907
  backlinks,
908
+ breadcrumbs,
909
+ navigationShowcase,
881
910
  relatedPosts,
882
911
  share,
883
912
  changelog,
@@ -894,6 +923,7 @@ var showcase = {
894
923
  shortcodes,
895
924
  series,
896
925
  taxonomy,
926
+ folderPages,
897
927
  autoCardLink,
898
928
  richEmbed,
899
929
  gallery,
@@ -1951,22 +1981,38 @@ async function findWorkspaceRoot(configRoot) {
1951
1981
  }
1952
1982
  }
1953
1983
  async function startHonoxDevServer(options) {
1954
- const server2 = await createServer2({ root: options.root });
1955
- await server2.listen();
1956
- server2.printUrls();
1957
- await waitForShutdown(server2);
1984
+ const root = path7.resolve(options.root);
1985
+ await withWorkingDirectory(root, async () => {
1986
+ const server2 = await createServer2({ root });
1987
+ await server2.listen();
1988
+ server2.printUrls();
1989
+ await waitForShutdown(server2);
1990
+ });
1958
1991
  }
1959
1992
  async function buildHonoxApplication(options) {
1960
- await runTraced(
1961
- options.tracer,
1962
- "integration.honox.client_build",
1963
- () => build({ root: options.root, mode: "client" })
1964
- );
1965
- await runTraced(
1966
- options.tracer,
1967
- "integration.honox.server_build",
1968
- () => build({ root: options.root })
1969
- );
1993
+ const root = path7.resolve(options.root);
1994
+ await withWorkingDirectory(root, async () => {
1995
+ await runTraced(
1996
+ options.tracer,
1997
+ "integration.honox.client_build",
1998
+ () => build({ root, mode: "client" })
1999
+ );
2000
+ await runTraced(
2001
+ options.tracer,
2002
+ "integration.honox.server_build",
2003
+ () => build({ root })
2004
+ );
2005
+ });
2006
+ }
2007
+ async function withWorkingDirectory(root, task) {
2008
+ const previousDirectory = process.cwd();
2009
+ if (previousDirectory === root) return await task();
2010
+ process.chdir(root);
2011
+ try {
2012
+ return await task();
2013
+ } finally {
2014
+ process.chdir(previousDirectory);
2015
+ }
1970
2016
  }
1971
2017
  async function runTraced(tracer, name, buildTask) {
1972
2018
  return tracer ? await tracer.span(name, {}, buildTask) : await buildTask();
@@ -2107,14 +2153,6 @@ function riebeckiteSsrExternals(externals) {
2107
2153
  function hasScaffoldPlugin(preset, packageName) {
2108
2154
  return preset.plugins.some((plugin) => plugin.package === packageName);
2109
2155
  }
2110
- function articleTitleHelper() {
2111
- return `
2112
- function getArticleTitle(slug: string, title: unknown): string {
2113
- if (typeof title === "string" && title.trim().length > 0) return title;
2114
- return slug.split("/").at(-1) ?? slug;
2115
- }
2116
- `;
2117
- }
2118
2156
  function server() {
2119
2157
  return `import { mountRiebeckiteEndpoints } from "@riebeckite/honox/server";
2120
2158
  import { createApp } from "honox/server";
@@ -2224,6 +2262,12 @@ function style(preset) {
2224
2262
  imports.push('@import "./.riebeckite/theme-styles.css";');
2225
2263
  }
2226
2264
  const blocks = [
2265
+ "",
2266
+ "*,",
2267
+ "*::before,",
2268
+ "*::after {",
2269
+ " box-sizing: border-box;",
2270
+ "}",
2227
2271
  "",
2228
2272
  "body {",
2229
2273
  " margin: 0;",
@@ -2231,26 +2275,158 @@ function style(preset) {
2231
2275
  "}",
2232
2276
  ""
2233
2277
  ];
2278
+ if (preset.appFiles.includes("header") || preset.appFiles.includes("article")) {
2279
+ blocks.push(
2280
+ ".riebeckite-page {",
2281
+ " display: flex;",
2282
+ " min-height: 100vh;",
2283
+ " width: 100%;",
2284
+ " flex-direction: column;",
2285
+ " align-items: center;",
2286
+ " padding-block: var(--rb-space-8, 4rem);",
2287
+ "}",
2288
+ ""
2289
+ );
2290
+ }
2234
2291
  if (preset.appFiles.includes("header")) {
2235
2292
  blocks.push(
2236
2293
  ".site-header {",
2237
2294
  " display: flex;",
2238
2295
  " align-items: center;",
2239
- " justify-content: space-between;",
2240
- " gap: 1rem;",
2241
- " padding: 1rem;",
2242
- " border-bottom: 1px solid #d1d5db;",
2296
+ " gap: var(--rb-space-3, 1.5rem);",
2297
+ " padding-block: var(--rb-space-3, 1.5rem);",
2298
+ " border-bottom: 1px solid var(--rb-color-border);",
2299
+ "}",
2300
+ "",
2301
+ ".site-header,",
2302
+ ".rb-site-footer {",
2303
+ " width: min(100% - 2rem, var(--rb-layout-article-max, 48rem));",
2243
2304
  "}",
2244
2305
  "",
2245
2306
  ".site-header__home {",
2246
2307
  " display: inline-flex;",
2247
2308
  " align-items: center;",
2248
2309
  " gap: 0.5rem;",
2249
- " color: #111827;",
2310
+ " margin-right: auto;",
2311
+ " color: var(--rb-color-ink);",
2250
2312
  " font-weight: 700;",
2251
2313
  " text-decoration: none;",
2252
2314
  "}",
2253
2315
  "",
2316
+ ".rb-nav {",
2317
+ " color: var(--rb-color-ink);",
2318
+ "}",
2319
+ "",
2320
+ ".rb-nav__list {",
2321
+ " display: flex;",
2322
+ " align-items: center;",
2323
+ " gap: var(--rb-space-3, 1.5rem);",
2324
+ " margin: 0;",
2325
+ " padding: 0;",
2326
+ " list-style: none;",
2327
+ "}",
2328
+ "",
2329
+ ".rb-nav__item {",
2330
+ " position: relative;",
2331
+ "}",
2332
+ "",
2333
+ ".rb-nav__link {",
2334
+ " color: inherit;",
2335
+ " text-decoration: none;",
2336
+ "}",
2337
+ "",
2338
+ ".rb-nav__link:hover,",
2339
+ ".rb-nav__link:focus-visible,",
2340
+ ".rb-nav__link--active {",
2341
+ " color: var(--rb-color-accent);",
2342
+ "}",
2343
+ "",
2344
+ ".rb-nav__link:focus-visible,",
2345
+ ".rb-nav__toggle:focus-visible {",
2346
+ " outline: 2px solid var(--rb-color-accent);",
2347
+ " outline-offset: 2px;",
2348
+ "}",
2349
+ "",
2350
+ ".rb-nav__children {",
2351
+ " display: none;",
2352
+ " position: absolute;",
2353
+ " top: 100%;",
2354
+ " left: 0;",
2355
+ " min-width: max-content;",
2356
+ " padding: var(--rb-space-2, 1rem);",
2357
+ " background: var(--rb-color-surface);",
2358
+ " border: 1px solid var(--rb-color-border);",
2359
+ "}",
2360
+ "",
2361
+ ".rb-nav__item:hover > .rb-nav__children,",
2362
+ ".rb-nav__item:focus-within > .rb-nav__children {",
2363
+ " display: flex;",
2364
+ " flex-direction: column;",
2365
+ " align-items: stretch;",
2366
+ "}",
2367
+ "",
2368
+ ".rb-nav__mobile {",
2369
+ " display: none;",
2370
+ "}",
2371
+ "",
2372
+ ".rb-site-footer {",
2373
+ " margin-top: 1rem;",
2374
+ " padding-block: var(--rb-space-4, 2rem);",
2375
+ " border-top: 1px solid var(--rb-color-border);",
2376
+ "}",
2377
+ "",
2378
+ "@media (max-width: 48rem) {",
2379
+ " .riebeckite-page {",
2380
+ " padding-block: var(--rb-space-3, 1.5rem);",
2381
+ " }",
2382
+ "",
2383
+ " .rb-site-header {",
2384
+ " flex-wrap: wrap;",
2385
+ " }",
2386
+ "",
2387
+ " .rb-site-header > .rb-nav {",
2388
+ " display: none;",
2389
+ " }",
2390
+ "",
2391
+ " .rb-nav__mobile {",
2392
+ " position: relative;",
2393
+ " display: block;",
2394
+ " }",
2395
+ "",
2396
+ " .rb-nav__mobile > .rb-nav {",
2397
+ " position: absolute;",
2398
+ " z-index: 10;",
2399
+ " top: calc(100% + var(--rb-space-2, 1rem));",
2400
+ " right: 0;",
2401
+ " min-width: 12rem;",
2402
+ " padding: var(--rb-space-3, 1.5rem);",
2403
+ " background: var(--rb-color-surface);",
2404
+ " border: 1px solid var(--rb-color-border);",
2405
+ " box-shadow: 0 4px 12px",
2406
+ " color-mix(in oklab, var(--rb-color-ink) 14%, transparent);",
2407
+ " }",
2408
+ "",
2409
+ " .rb-nav__mobile .rb-nav__list,",
2410
+ " .rb-nav__mobile .rb-nav__children {",
2411
+ " display: flex;",
2412
+ " flex-direction: column;",
2413
+ " align-items: flex-start;",
2414
+ " gap: var(--rb-space-2, 1rem);",
2415
+ " }",
2416
+ "",
2417
+ " .rb-nav__mobile .rb-nav__list {",
2418
+ " padding: 0;",
2419
+ " }",
2420
+ "",
2421
+ " .rb-nav__mobile .rb-nav__children {",
2422
+ " position: static;",
2423
+ " margin-top: var(--rb-space-2, 1rem);",
2424
+ " padding: 0;",
2425
+ " background: transparent;",
2426
+ " border: 0;",
2427
+ " }",
2428
+ "}",
2429
+ "",
2254
2430
  ".site-header__logo {",
2255
2431
  " width: 1.75rem;",
2256
2432
  " height: 1.75rem;",
@@ -2263,18 +2439,23 @@ function style(preset) {
2263
2439
  blocks.push(
2264
2440
  ".site-article {",
2265
2441
  " display: block;",
2266
- " max-width: 48rem;",
2267
- " padding: 2rem 1rem;",
2442
+ " width: min(100% - 2rem, var(--rb-layout-article-max, 48rem));",
2443
+ " padding-block: 2rem;",
2268
2444
  " margin: 0 auto;",
2269
2445
  "}",
2270
2446
  "",
2271
- ".article-shell__layout {",
2447
+ ".rb-article-layout {",
2272
2448
  " display: grid;",
2273
- " grid-template-columns: minmax(0, 1fr);",
2449
+ " grid-template-columns: minmax(0, var(--rb-layout-article-max, 48rem));",
2274
2450
  " justify-content: center;",
2275
2451
  " gap: var(--rb-layout-gap, 2rem);",
2276
2452
  "}",
2277
2453
  "",
2454
+ ".rb-article-body {",
2455
+ " min-width: 0;",
2456
+ " padding-inline: var(--rb-space-2, 1rem);",
2457
+ "}",
2458
+ "",
2278
2459
  ".site-article__aside {",
2279
2460
  " display: none;",
2280
2461
  "}",
@@ -2284,12 +2465,12 @@ function style(preset) {
2284
2465
  "}",
2285
2466
  "",
2286
2467
  "@media (min-width: 88rem) {",
2287
- ' :is(:root, .rb-theme-root)[data-article-layout="article"] .article-shell__layout {',
2468
+ ' :is(:root, .rb-theme-root)[data-article-layout="article"] .rb-article-layout {',
2288
2469
  " grid-template-columns: minmax(0, var(--rb-layout-article-max, 48rem));",
2289
2470
  " position: relative;",
2290
2471
  " }",
2291
2472
  "",
2292
- ' :is(:root, .rb-theme-root)[data-article-layout="article"] .article-shell__body {',
2473
+ ' :is(:root, .rb-theme-root)[data-article-layout="article"] .rb-article-body {',
2293
2474
  " grid-column: 1;",
2294
2475
  " grid-row: 1;",
2295
2476
  " }",
@@ -2333,13 +2514,22 @@ function style(preset) {
2333
2514
  return [...imports, ...blocks].join("\n");
2334
2515
  }
2335
2516
  function siteHeader() {
2336
- return `import { ColorModeToggle } from "@riebeckite/plugin-color-mode";
2517
+ return `import type { NavigationItem } from "@riebeckite/plugin-navigation";
2518
+ import { ColorModeToggle } from "@riebeckite/plugin-color-mode";
2337
2519
  import { config } from "../config";
2338
2520
 
2339
- export function SiteHeader() {
2521
+ export function SiteHeader({
2522
+ path,
2523
+ items,
2524
+ }: {
2525
+ path: string;
2526
+ items: readonly NavigationItem[];
2527
+ }) {
2528
+ const navigation = items.filter((item) => item.href !== "/");
2529
+
2340
2530
  return (
2341
- <header class="site-header">
2342
- <a href="/" class="site-header__home">
2531
+ <header class="site-header rb-site-header">
2532
+ <a href="/" class="site-header__home rb-site-header__home">
2343
2533
  <img
2344
2534
  src="/riebeckite-logo.png"
2345
2535
  alt=""
@@ -2349,10 +2539,94 @@ export function SiteHeader() {
2349
2539
  />
2350
2540
  {config.site.title}
2351
2541
  </a>
2542
+ {navigation.length > 0 ? (
2543
+ <>
2544
+ <SiteNavigation items={navigation} path={path} />
2545
+ <details class="site-navigation__mobile rb-nav__mobile">
2546
+ <summary class="rb-nav__toggle">Menu</summary>
2547
+ <SiteNavigation items={navigation} path={path} />
2548
+ </details>
2549
+ </>
2550
+ ) : null}
2352
2551
  <ColorModeToggle />
2353
2552
  </header>
2354
2553
  );
2355
2554
  }
2555
+
2556
+ export function SiteFooter({
2557
+ path,
2558
+ items,
2559
+ }: {
2560
+ path: string;
2561
+ items: readonly NavigationItem[];
2562
+ }) {
2563
+ if (items.length === 0) return null;
2564
+
2565
+ return (
2566
+ <footer class="site-footer rb-site-footer">
2567
+ <SiteNavigation items={items} path={path} />
2568
+ </footer>
2569
+ );
2570
+ }
2571
+
2572
+ function SiteNavigation({
2573
+ items,
2574
+ path,
2575
+ }: {
2576
+ items: readonly NavigationItem[];
2577
+ path: string;
2578
+ }) {
2579
+ return (
2580
+ <nav class="site-navigation rb-nav" aria-label="Site navigation">
2581
+ <NavigationItems items={items} path={path} />
2582
+ </nav>
2583
+ );
2584
+ }
2585
+
2586
+ function NavigationItems({
2587
+ items,
2588
+ path,
2589
+ isChildList = false,
2590
+ }: {
2591
+ items: readonly NavigationItem[];
2592
+ path: string;
2593
+ isChildList?: boolean;
2594
+ }) {
2595
+ return (
2596
+ <ul class={isChildList ? "site-navigation__list rb-nav__list rb-nav__children" : "site-navigation__list rb-nav__list"}>
2597
+ {items.map((item) => {
2598
+ const active = item.href ? isActive(item.href, path) : false;
2599
+ return (
2600
+ <li class="site-navigation__item rb-nav__item">
2601
+ {item.href ? (
2602
+ <a
2603
+ href={item.href}
2604
+ class={active ? "site-navigation__link rb-nav__link rb-nav__link--active is-active" : "site-navigation__link rb-nav__link"}
2605
+ aria-current={active ? "page" : undefined}
2606
+ target={item.external ? "_blank" : undefined}
2607
+ rel={item.external ? "noreferrer" : undefined}
2608
+ >
2609
+ {item.label}
2610
+ </a>
2611
+ ) : (
2612
+ <span class="site-navigation__label rb-nav__label">{item.label}</span>
2613
+ )}
2614
+ {item.children && item.children.length > 0 ? (
2615
+ <NavigationItems items={item.children} path={path} isChildList />
2616
+ ) : null}
2617
+ </li>
2618
+ );
2619
+ })}
2620
+ </ul>
2621
+ );
2622
+ }
2623
+
2624
+ function isActive(href: string, path: string): boolean {
2625
+ if (!href.startsWith("/")) return false;
2626
+ const target = href.replace(/\\/+$/, "") || "/";
2627
+ const current = path.replace(/^\\/[a-z]{2}(?:-[A-Z]{2})?(?=\\/|$)/, "").replace(/\\/+$/, "") || "/";
2628
+ return target === "/" ? current === target : current === target || current.startsWith(\`\${target}/\`);
2629
+ }
2356
2630
  `;
2357
2631
  }
2358
2632
  function article() {
@@ -2361,25 +2635,13 @@ import { Article, ArticleContent, ArticleLayout } from "@riebeckite/honox/ui";
2361
2635
 
2362
2636
  export function SiteArticle({
2363
2637
  post,
2364
- propertiesHtml,
2365
- afterHeaderHtml,
2366
- afterMetaHtml,
2367
- beforeContentHtml,
2368
- afterContentHtml,
2369
- asideHtml,
2370
- footerHtml,
2638
+ bodySlots,
2371
2639
  asideContent,
2372
2640
  afterContent,
2373
2641
  footerContent,
2374
2642
  }: {
2375
2643
  post: PostContent;
2376
- propertiesHtml?: string;
2377
- afterHeaderHtml?: string;
2378
- afterMetaHtml?: string;
2379
- beforeContentHtml?: string;
2380
- afterContentHtml?: string;
2381
- asideHtml?: string;
2382
- footerHtml?: string;
2644
+ bodySlots?: Readonly<Record<string, string>>;
2383
2645
  asideContent?: unknown;
2384
2646
  afterContent?: unknown;
2385
2647
  footerContent?: unknown;
@@ -2388,50 +2650,44 @@ export function SiteArticle({
2388
2650
  <Article class="site-article">
2389
2651
  <ArticleLayout>
2390
2652
  {asideContent}
2391
- {asideHtml ? (
2653
+ {bodySlots?.["article.aside"] ? (
2392
2654
  <div
2393
2655
  class="site-article__aside"
2394
- dangerouslySetInnerHTML={{ __html: asideHtml }}
2656
+ dangerouslySetInnerHTML={{ __html: bodySlots["article.aside"] }}
2395
2657
  />
2396
2658
  ) : null}
2397
2659
  <ArticleContent>
2398
- {afterHeaderHtml ? (
2399
- <div
2400
- class="site-article__after-header"
2401
- dangerouslySetInnerHTML={{ __html: afterHeaderHtml }}
2402
- />
2403
- ) : null}
2404
- {propertiesHtml ? (
2660
+ {bodySlots?.["article.header"] ? (
2405
2661
  <div
2406
- class="article-properties"
2407
- dangerouslySetInnerHTML={{ __html: propertiesHtml }}
2662
+ class="site-article__header"
2663
+ dangerouslySetInnerHTML={{ __html: bodySlots["article.header"] }}
2408
2664
  />
2409
2665
  ) : null}
2410
- {afterMetaHtml ? (
2666
+ {bodySlots?.["article.metadata"] ? (
2411
2667
  <div
2412
- class="site-article__after-meta"
2413
- dangerouslySetInnerHTML={{ __html: afterMetaHtml }}
2668
+ class="site-article__metadata"
2669
+ dangerouslySetInnerHTML={{ __html: bodySlots["article.metadata"] }}
2414
2670
  />
2415
2671
  ) : null}
2416
- {beforeContentHtml ? (
2672
+ {bodySlots?.["article.before-content"] ? (
2417
2673
  <div
2418
2674
  class="site-article__before-content"
2419
- dangerouslySetInnerHTML={{ __html: beforeContentHtml }}
2675
+ dangerouslySetInnerHTML={{ __html: bodySlots["article.before-content"] }}
2420
2676
  />
2421
2677
  ) : null}
2422
2678
  <div dangerouslySetInnerHTML={{ __html: post.html ?? "" }} />
2423
2679
  {afterContent}
2424
- {afterContentHtml ? (
2680
+ {bodySlots?.["article.after-content"] ? (
2425
2681
  <div
2426
2682
  class="site-article__after-content"
2427
- dangerouslySetInnerHTML={{ __html: afterContentHtml }}
2683
+ dangerouslySetInnerHTML={{ __html: bodySlots["article.after-content"] }}
2428
2684
  />
2429
2685
  ) : null}
2430
2686
  </ArticleContent>
2431
- {footerHtml || footerContent ? (
2687
+ {bodySlots?.["article.footer"] || footerContent ? (
2432
2688
  <div class="site-article__footer">
2433
- {footerHtml ? (
2434
- <div dangerouslySetInnerHTML={{ __html: footerHtml }} />
2689
+ {bodySlots?.["article.footer"] ? (
2690
+ <div dangerouslySetInnerHTML={{ __html: bodySlots["article.footer"] }} />
2435
2691
  ) : null}
2436
2692
  {footerContent}
2437
2693
  </div>
@@ -2454,7 +2710,9 @@ import { jsxRenderer } from "hono/jsx-renderer";
2454
2710
  import { Link, Script } from "honox/server";
2455
2711
  ${hasColorMode ? `import { ColorModeScript } from "@riebeckite/plugin-color-mode";
2456
2712
  ` : ""}${hasSearch ? `import { SearchBar } from "@riebeckite/plugin-search";
2457
- ` : ""}${hasHeader ? `import { SiteHeader } from "../components/site-header";
2713
+ ` : ""}${hasHeader ? `import { resolveSiteNavigation } from "@riebeckite/plugin-navigation";
2714
+ ` : ""}${hasHeader ? `import { SiteFooter, SiteHeader } from "../components/site-header";
2715
+ ` : ""}${hasHeader ? `import { content } from "../content";
2458
2716
  ` : ""}import { config } from "../config";
2459
2717
 
2460
2718
  function themeAttributes() {
@@ -2473,9 +2731,10 @@ function themeAttributes() {
2473
2731
  // an island component.
2474
2732
  export const __importing_islands = true;
2475
2733
 
2476
- export default jsxRenderer(({ children }, c) => {
2734
+ export default jsxRenderer(async ({ children }, c) => {
2477
2735
  const headTags: readonly PluginHeadTag[] = c.get("headTags") ?? [];
2478
-
2736
+ ${hasHeader ? ` const navigation = resolveSiteNavigation(config, await content.getManifest(), c.get("htmlLanguage")) ?? { primary: [], secondary: [] };
2737
+ ` : ""}
2479
2738
  return (
2480
2739
  <html
2481
2740
  lang={c.get("htmlLanguage") ?? config.site.locale}
@@ -2491,11 +2750,12 @@ ${hasColorMode ? ` <ColorModeScript />
2491
2750
  {headTags.map(renderHeadTag)}
2492
2751
  <Script src="/app/client.ts" async />
2493
2752
  </head>
2494
- <body class="riebeckite-page">
2495
- ${hasHeader ? ` <SiteHeader />
2753
+ <body class="riebeckite-page rb-site">
2754
+ ${hasHeader ? ` <SiteHeader path={c.req.path} items={navigation.primary} />
2496
2755
  ` : ""}${hasSearch ? ` <SearchBar />
2497
2756
  ` : ""} {children}
2498
- </body>
2757
+ ${hasHeader ? ` <SiteFooter path={c.req.path} items={navigation.secondary} />
2758
+ ` : ""} </body>
2499
2759
  </html>
2500
2760
  );
2501
2761
  });
@@ -2535,7 +2795,7 @@ export default jsxRenderer(({ children }, c) => (
2535
2795
  <Link href="/app/style.css" rel="stylesheet" />
2536
2796
  <Script src="/app/client.ts" async />
2537
2797
  </head>
2538
- <body class="riebeckite-page">{children}</body>
2798
+ <body class="riebeckite-page rb-site">{children}</body>
2539
2799
  </html>
2540
2800
  ));
2541
2801
  `;
@@ -2543,10 +2803,6 @@ export default jsxRenderer(({ children }, c) => (
2543
2803
  function indexRoute(preset) {
2544
2804
  if (!preset.appFiles.includes("article")) return staticIndexRoute();
2545
2805
  const hasToc = hasScaffoldPlugin(preset, "@riebeckite/plugin-toc");
2546
- const hasBacklinks = hasScaffoldPlugin(
2547
- preset,
2548
- "@riebeckite/plugin-backlinks"
2549
- );
2550
2806
  const hasRecentPosts = hasScaffoldPlugin(
2551
2807
  preset,
2552
2808
  "@riebeckite/plugin-recent-posts"
@@ -2555,16 +2811,11 @@ function indexRoute(preset) {
2555
2811
  preset,
2556
2812
  "@riebeckite/plugin-daily-notes"
2557
2813
  );
2558
- const needsTitle = hasBacklinks || hasRecentPosts;
2559
- const needsConfig = hasBacklinks || hasRecentPosts || hasDailyNotes;
2814
+ const needsConfig = hasRecentPosts || hasDailyNotes;
2560
2815
  const importLines = [
2816
+ 'import { getEntryLanguage } from "@riebeckite/core";',
2561
2817
  'import { createRoute } from "honox/factory";'
2562
2818
  ];
2563
- if (hasBacklinks) {
2564
- importLines.push(
2565
- 'import { Backlinks, getPublishedBacklinks } from "@riebeckite/plugin-backlinks";'
2566
- );
2567
- }
2568
2819
  if (hasDailyNotes) {
2569
2820
  importLines.push(
2570
2821
  'import { DailyNotes, getDailyNotes } from "@riebeckite/plugin-daily-notes";'
@@ -2584,14 +2835,9 @@ function indexRoute(preset) {
2584
2835
  if (needsConfig) importLines.push('import { config } from "../config";');
2585
2836
  importLines.push('import { content } from "../content";');
2586
2837
  const dataLines = [];
2587
- if (hasBacklinks) {
2588
- dataLines.push(
2589
- ' const backlinks = getPublishedBacklinks({ manifest, config, slug: "index", resolveTitle: getArticleTitle });'
2590
- );
2591
- }
2592
2838
  if (hasRecentPosts) {
2593
2839
  dataLines.push(
2594
- " const recentPosts = await getRecentPosts({ posts: manifest.discoverableEntries, config, getProcessedContent: (slug) => content.getProcessedContent(slug), resolveTitle: getArticleTitle });"
2840
+ ' const recentPosts = await getRecentPosts({ posts: manifest.discoverableEntries, config, getProcessedContent: (slug) => content.getProcessedContent(slug), resolveTitle: (slug, title) => typeof title === "string" && title.trim() ? title : slug.split("/").at(-1) ?? slug });'
2595
2841
  );
2596
2842
  }
2597
2843
  if (hasDailyNotes) {
@@ -2602,15 +2848,7 @@ function indexRoute(preset) {
2602
2848
  ' const tableOfContents = extractTableOfContents(post.html ?? "");'
2603
2849
  );
2604
2850
  }
2605
- const propLines = [
2606
- " propertiesHtml={indexEntry?.bodySlots?.properties}",
2607
- ' afterHeaderHtml={indexEntry?.bodySlots?.["article.after-header"]}',
2608
- ' afterMetaHtml={indexEntry?.bodySlots?.["article.after-meta"]}',
2609
- ' beforeContentHtml={indexEntry?.bodySlots?.["article.before-content"]}',
2610
- ' afterContentHtml={indexEntry?.bodySlots?.["article.after-content"]}',
2611
- ' asideHtml={indexEntry?.bodySlots?.["article.aside"]}',
2612
- ' footerHtml={indexEntry?.bodySlots?.["article.footer"]}'
2613
- ];
2851
+ const propLines = [" bodySlots={indexEntry?.bodySlots}"];
2614
2852
  if (hasToc) {
2615
2853
  propLines.push(
2616
2854
  ' asideContent={<TableOfContents className="table-of-contents--desktop" items={tableOfContents} />}'
@@ -2622,12 +2860,6 @@ function indexRoute(preset) {
2622
2860
  if (afterChildren.length > 0) {
2623
2861
  propLines.push(` afterContent={<>${afterChildren.join("")}</>}`);
2624
2862
  }
2625
- if (hasBacklinks) {
2626
- propLines.push(
2627
- " footerContent={<Backlinks backlinks={backlinks} />}"
2628
- );
2629
- }
2630
- const titleHelper = needsTitle ? articleTitleHelper() : "";
2631
2863
  const dataBlock = dataLines.length > 0 ? `
2632
2864
  ${dataLines.join("\n")}
2633
2865
  ` : "";
@@ -2646,7 +2878,7 @@ export default createRoute(async (c) => {
2646
2878
  }
2647
2879
  ${dataBlock}
2648
2880
  if (indexEntry) {
2649
- c.set("htmlLanguage", indexEntry.publicLocation.metadata?.["l10n.lang"]);
2881
+ c.set("htmlLanguage", getEntryLanguage(indexEntry));
2650
2882
  c.set("headTags", indexEntry.headTags ?? []);
2651
2883
  }
2652
2884
 
@@ -2656,7 +2888,7 @@ ${dataBlock}
2656
2888
  ${propLines.join("\n")}
2657
2889
  />,
2658
2890
  );
2659
- });${titleHelper}`;
2891
+ });`;
2660
2892
  }
2661
2893
  function staticIndexRoute() {
2662
2894
  return `import { createRoute } from "honox/factory";
@@ -2674,34 +2906,14 @@ export default createRoute((c) =>
2674
2906
  }
2675
2907
  function slugRoute(preset) {
2676
2908
  const hasToc = hasScaffoldPlugin(preset, "@riebeckite/plugin-toc");
2677
- const hasBacklinks = hasScaffoldPlugin(
2678
- preset,
2679
- "@riebeckite/plugin-backlinks"
2680
- );
2681
- const hasLocalGraph = hasScaffoldPlugin(
2682
- preset,
2683
- "@riebeckite/plugin-local-graph"
2684
- );
2685
- const needsTitle = hasBacklinks || hasLocalGraph;
2686
- const needsConfig = hasBacklinks || hasLocalGraph;
2687
- const needsManifest = hasBacklinks || hasLocalGraph;
2688
2909
  const importLines = [
2910
+ 'import { getEntryLanguage } from "@riebeckite/core";',
2689
2911
  "import {",
2690
2912
  " contentRouteSsgParams,",
2691
2913
  " pluginPageSsgParams,",
2692
2914
  " resolveRiebeckiteRoute,",
2693
2915
  '} from "@riebeckite/honox/server";'
2694
2916
  ];
2695
- if (hasBacklinks) {
2696
- importLines.push(
2697
- 'import { Backlinks, getPublishedBacklinks } from "@riebeckite/plugin-backlinks";'
2698
- );
2699
- }
2700
- if (hasLocalGraph) {
2701
- importLines.push(
2702
- 'import { LocalGraph, getLocalGraph } from "@riebeckite/plugin-local-graph";'
2703
- );
2704
- }
2705
2917
  if (hasToc) {
2706
2918
  importLines.push(
2707
2919
  'import { extractTableOfContents, TableOfContents } from "@riebeckite/plugin-toc";'
@@ -2709,52 +2921,19 @@ function slugRoute(preset) {
2709
2921
  }
2710
2922
  importLines.push('import { createRoute } from "honox/factory";');
2711
2923
  importLines.push('import { SiteArticle } from "../components/article";');
2712
- if (needsConfig) importLines.push('import { config } from "../config";');
2713
2924
  importLines.push('import { content } from "../content";');
2714
2925
  const dataLines = [];
2715
- if (needsManifest) {
2716
- dataLines.push(" const manifest = await content.getManifest();");
2717
- }
2718
- if (hasBacklinks) {
2719
- dataLines.push(
2720
- " const backlinks = getPublishedBacklinks({ manifest, config, slug: route.entry.slug, resolveTitle: getArticleTitle });"
2721
- );
2722
- }
2723
- if (hasLocalGraph) {
2724
- dataLines.push(
2725
- " const localGraph = getLocalGraph({ manifest, config, slug: route.entry.slug, resolveTitle: getArticleTitle });"
2726
- );
2727
- }
2728
2926
  if (hasToc) {
2729
2927
  dataLines.push(
2730
2928
  ' const tableOfContents = extractTableOfContents(post.html ?? "");'
2731
2929
  );
2732
2930
  }
2733
- const propLines = [
2734
- " propertiesHtml={route.entry.bodySlots?.properties}",
2735
- ' afterHeaderHtml={route.entry.bodySlots?.["article.after-header"]}',
2736
- ' afterMetaHtml={route.entry.bodySlots?.["article.after-meta"]}',
2737
- ' beforeContentHtml={route.entry.bodySlots?.["article.before-content"]}',
2738
- ' afterContentHtml={route.entry.bodySlots?.["article.after-content"]}',
2739
- ' asideHtml={route.entry.bodySlots?.["article.aside"]}',
2740
- ' footerHtml={route.entry.bodySlots?.["article.footer"]}'
2741
- ];
2931
+ const propLines = [" bodySlots={route.entry.bodySlots}"];
2742
2932
  if (hasToc) {
2743
2933
  propLines.push(
2744
2934
  ' asideContent={<TableOfContents className="table-of-contents--desktop" items={tableOfContents} />}'
2745
2935
  );
2746
2936
  }
2747
- const footerChildren = [];
2748
- if (hasLocalGraph) {
2749
- footerChildren.push("{localGraph && <LocalGraph graph={localGraph} />}");
2750
- }
2751
- if (hasBacklinks) {
2752
- footerChildren.push("<Backlinks backlinks={backlinks} />");
2753
- }
2754
- if (footerChildren.length > 0) {
2755
- propLines.push(` footerContent={<>${footerChildren.join("")}</>}`);
2756
- }
2757
- const titleHelper = needsTitle ? articleTitleHelper() : "";
2758
2937
  const dataBlock = dataLines.length > 0 ? `
2759
2938
  ${dataLines.join("\n")}
2760
2939
  ` : "";
@@ -2779,6 +2958,7 @@ export default createRoute(
2779
2958
  return c.redirect(route.location, route.status);
2780
2959
  if (route.kind === "page") {
2781
2960
  c.set("headTags", route.page.headTags ?? []);
2961
+ c.set("htmlLanguage", route.page.language);
2782
2962
  return c.render(
2783
2963
  <div dangerouslySetInnerHTML={{ __html: route.page.body }} />,
2784
2964
  );
@@ -2786,7 +2966,7 @@ export default createRoute(
2786
2966
 
2787
2967
  const post = await content.getProcessedContent(route.entry.slug);
2788
2968
  ${dataBlock}
2789
- c.set("htmlLanguage", route.entry.publicLocation.metadata?.["l10n.lang"]);
2969
+ c.set("htmlLanguage", getEntryLanguage(route.entry));
2790
2970
  c.set("headTags", route.entry.headTags ?? []);
2791
2971
 
2792
2972
  return c.render(
@@ -2796,7 +2976,7 @@ ${propLines.join("\n")}
2796
2976
  />,
2797
2977
  );
2798
2978
  },
2799
- );${titleHelper}`;
2979
+ );`;
2800
2980
  }
2801
2981
 
2802
2982
  // src/scaffold/localized-content.ts
@@ -2850,7 +3030,7 @@ var PLUGIN_MARKDOWN_GUIDES = {
2850
3030
  ].join("\n")
2851
3031
  },
2852
3032
  "@riebeckite/plugin-properties": {
2853
- summary: 'Renders selected frontmatter properties near the article when configured with `render: "slot"`.',
3033
+ summary: "Renders selected frontmatter properties in the article metadata slot.",
2854
3034
  markdown: [
2855
3035
  "---",
2856
3036
  "created: 2026-09-30",
@@ -4538,12 +4718,12 @@ function referenceThemesContent(preset) {
4538
4718
  }
4539
4719
 
4540
4720
  // src/scaffold/version.ts
4541
- var RIEBECKITE_VERSION = "^0.0.15";
4721
+ var RIEBECKITE_VERSION = "^0.0.17";
4542
4722
 
4543
4723
  // src/scaffold/templates.ts
4544
- function siteTemplateFiles(preset, variables) {
4724
+ function siteTemplateFiles(preset, variables, options = {}) {
4545
4725
  return [
4546
- { path: "package.json", content: packageJson(preset, variables) },
4726
+ { path: "package.json", content: packageJson(preset, variables, options) },
4547
4727
  {
4548
4728
  path: "riebeckite.config.ts",
4549
4729
  content: riebeckiteConfig(preset, variables)
@@ -4624,7 +4804,7 @@ function appFiles(preset) {
4624
4804
  ...has("article") ? [{ path: "app/components/article.tsx", content: get("article") }] : []
4625
4805
  ];
4626
4806
  }
4627
- function packageJson(preset, variables) {
4807
+ function packageJson(preset, variables, options) {
4628
4808
  const dependencies = {
4629
4809
  "@riebeckite/core": RIEBECKITE_VERSION,
4630
4810
  "@riebeckite/honox": RIEBECKITE_VERSION,
@@ -4656,7 +4836,8 @@ function packageJson(preset, variables) {
4656
4836
  "@riebeckite/cli": RIEBECKITE_VERSION,
4657
4837
  "@types/node": "^24.5.2",
4658
4838
  typescript: "^5.0.0",
4659
- vite: "^8.0.9"
4839
+ vite: "^8.0.9",
4840
+ ...options.cloudflareWorkers ? { wrangler: WRANGLER_VERSION } : {}
4660
4841
  }
4661
4842
  },
4662
4843
  null,
@@ -4682,6 +4863,7 @@ function riebeckiteConfig(preset, variables) {
4682
4863
  lines.push(` description: ${JSON.stringify(description)},`);
4683
4864
  lines.push(` baseUrl: ${JSON.stringify(baseUrl)},`);
4684
4865
  lines.push(` locale: ${JSON.stringify(locale)},`);
4866
+ lines.push(' defaultOgImage: "/ogp.png",');
4685
4867
  lines.push(" },");
4686
4868
  lines.push(" content: {");
4687
4869
  lines.push(' directory: "content",');
@@ -4917,6 +5099,19 @@ function readmeEn(preset, variables) {
4917
5099
  "```",
4918
5100
  ""
4919
5101
  );
5102
+ if (preset.name !== "empty") {
5103
+ lines.push(
5104
+ "## First edits",
5105
+ "",
5106
+ "- `content/index.md`: the first published page.",
5107
+ "- `riebeckite.config.ts`: set `site.title`, `site.baseUrl`, and `content.directory`.",
5108
+ "- `public/favicon.ico`: replace the site icon when you are ready.",
5109
+ ""
5110
+ );
5111
+ }
5112
+ if (variables.externalContent === true) {
5113
+ lines.push(...deploymentSecretLines("en"));
5114
+ }
4920
5115
  if (preset.readme === "rich") {
4921
5116
  lines.push(...demoPagesLines(preset, variables, words, "en"));
4922
5117
  lines.push(...copyPasteDemoLines(preset, words, "en"));
@@ -4965,6 +5160,19 @@ function readmeJa(preset, variables) {
4965
5160
  "```",
4966
5161
  ""
4967
5162
  );
5163
+ if (preset.name !== "empty") {
5164
+ lines.push(
5165
+ "## 最初に編集する場所",
5166
+ "",
5167
+ "- `content/index.md`: 最初に公開されるページです。",
5168
+ "- `riebeckite.config.ts`: `site.title`、`site.baseUrl`、`content.directory` を設定します。",
5169
+ "- `public/favicon.ico`: 必要になったらサイトアイコンを置き換えます。",
5170
+ ""
5171
+ );
5172
+ }
5173
+ if (variables.externalContent === true) {
5174
+ lines.push(...deploymentSecretLines("ja"));
5175
+ }
4968
5176
  if (preset.readme === "rich") {
4969
5177
  lines.push(...demoPagesLines(preset, variables, words, "ja"));
4970
5178
  lines.push(...copyPasteDemoLines(preset, words, "ja"));
@@ -4980,6 +5188,34 @@ function readmeJa(preset, variables) {
4980
5188
  }
4981
5189
  return lines.join("\n");
4982
5190
  }
5191
+ function deploymentSecretLines(language) {
5192
+ if (language === "ja") {
5193
+ return [
5194
+ "## デプロイ用 Secret",
5195
+ "",
5196
+ "GitHub Actions のデプロイは content repository を読み、Cloudflare へ配信します。初回実行の前に、次の repository secret を登録してください。",
5197
+ "",
5198
+ "- `RIEBECKITE_CONTENT_READ_TOKEN` — **この Site Repository** に登録します。content repository が private / internal のときだけ必要です。content repository に限定した fine-grained token を作成し、**Contents: read** を付与します。",
5199
+ "- `SITE_DISPATCH_TOKEN` — **content repository** に登録します。content repository の `notify-site.yml` からこの Site へ更新を通知するために使います。この Site Repository に限定した fine-grained token を作成し、**Contents: read and write** を付与します。",
5200
+ "- `CLOUDFLARE_API_TOKEN` と `CLOUDFLARE_ACCOUNT_ID` — **この Site Repository** に登録し、Cloudflare へのデプロイに使います。",
5201
+ "",
5202
+ `workflow はこれらの secret 名を参照するだけで、値は含みません。値は絶対に commit しないでください。全体の手順は [別 Content Repository のガイド](${REPO}/blob/main/docs/ja/docs/guides/deployment/separate-content-repository.md) を参照してください。`,
5203
+ ""
5204
+ ];
5205
+ }
5206
+ return [
5207
+ "## Deployment secrets",
5208
+ "",
5209
+ "The GitHub Actions deployment reads the content repository and deploys to Cloudflare. Add these repository secrets before the first run:",
5210
+ "",
5211
+ "- `RIEBECKITE_CONTENT_READ_TOKEN` — in **this site repository**. Required only when the content repository is private or internal. Use a fine-grained token scoped to the content repository with **Contents: read**.",
5212
+ "- `SITE_DISPATCH_TOKEN` — in the **content repository**. Lets its `notify-site.yml` dispatch updates to this site. Use a fine-grained token scoped to this site repository with **Contents: read and write**.",
5213
+ "- `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` — in **this site repository** for the Cloudflare deploy.",
5214
+ "",
5215
+ `The workflows reference these secret names and never contain their values. Never commit the values. See the [separate content repository guide](${REPO}/blob/main/docs/en/docs/guides/deployment/separate-content-repository.md) for the full setup.`,
5216
+ ""
5217
+ ];
5218
+ }
4983
5219
  function languagesLabel(preset, words) {
4984
5220
  if (preset.languages.length === 0) return words.none;
4985
5221
  if (preset.languages.length === 1)
@@ -5432,8 +5668,14 @@ async function scaffoldRiebeckiteSite(options) {
5432
5668
  const preset = resolveScaffoldPreset(options.preset);
5433
5669
  validateDeploymentOptions(options);
5434
5670
  const files = [
5435
- ...siteTemplateFiles(preset, templateVariables(options, targetDirectory)),
5436
- ...options.githubActions ? deploymentTemplateFiles(deploymentOptions(options)) : []
5671
+ ...siteTemplateFiles(preset, templateVariables(options, targetDirectory), {
5672
+ cloudflareWorkers: options.cloudflareWorkers
5673
+ }),
5674
+ ...options.githubActions ? deploymentTemplateFiles(deploymentOptions(options)) : options.cloudflareWorkers ? [
5675
+ wranglerJsoncFile(
5676
+ workerNameFromDirectory(path9.basename(targetDirectory))
5677
+ )
5678
+ ] : []
5437
5679
  ];
5438
5680
  await assertTargetWritable(
5439
5681
  targetDirectory,
@@ -5486,7 +5728,8 @@ function templateVariables(options, targetDirectory) {
5486
5728
  title: options.siteTitle ?? name,
5487
5729
  description: options.description ?? "",
5488
5730
  baseUrl: options.baseUrl ?? "",
5489
- locale: options.locale ?? "en"
5731
+ locale: options.locale ?? "en",
5732
+ externalContent: options.contentRepository !== void 0
5490
5733
  };
5491
5734
  }
5492
5735
  async function assertTargetWritable(targetDirectory, files, overwrite) {
@@ -5530,23 +5773,51 @@ function formatScaffoldNextSteps(relativeDirectory, options = {}) {
5530
5773
  lines.push("");
5531
5774
  lines.push("Then edit:");
5532
5775
  lines.push(` ${options.editFile}`);
5776
+ lines.push("");
5777
+ lines.push("Common first settings:");
5778
+ lines.push(
5779
+ " riebeckite.config.ts: site.title, site.baseUrl, content.directory"
5780
+ );
5533
5781
  }
5534
5782
  if (options.externalContent === true) {
5535
5783
  lines.push("");
5784
+ lines.push(
5785
+ "Operational content lives in the content repository. CI checks it out to content/."
5786
+ );
5536
5787
  lines.push(
5537
5788
  "Copy github/notify-site.yml to the content repository as .github/workflows/notify-site.yml."
5538
5789
  );
5790
+ lines.push("");
5791
+ lines.push("Required GitHub Actions repository secrets:");
5792
+ lines.push(
5793
+ " RIEBECKITE_CONTENT_READ_TOKEN: site repository; only when the content repository is private or internal (Contents: read)"
5794
+ );
5795
+ lines.push(
5796
+ " SITE_DISPATCH_TOKEN: content repository (fine-grained token for the site repository, Contents: read and write)"
5797
+ );
5798
+ lines.push(
5799
+ " CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID: site repository"
5800
+ );
5539
5801
  }
5540
5802
  return lines.join("\n");
5541
5803
  }
5542
5804
  export {
5805
+ DEFAULT_WORKER_NAME,
5806
+ GITHUB_ACTIONS_SECRETS,
5807
+ PACKAGE_MANAGER,
5543
5808
  SCAFFOLD_DEFAULT_PRESET,
5544
5809
  SCAFFOLD_PRESET_NAMES,
5545
5810
  ScaffoldSiteError,
5811
+ WORKER_NAME_MAX_LENGTH,
5812
+ WRANGLER_DEFAULTS,
5813
+ WRANGLER_VERSION,
5814
+ assertGitHubRepository,
5815
+ buildDefaultWranglerConfig,
5546
5816
  buildHonoxApplication,
5547
5817
  createRiebeckiteSsg,
5548
5818
  defaultSsgEntry,
5549
5819
  defaultSsrExternals,
5820
+ deploymentTemplateFiles,
5550
5821
  empty,
5551
5822
  formatScaffoldNextSteps,
5552
5823
  isScaffoldPresetName,
@@ -5565,5 +5836,6 @@ export {
5565
5836
  scaffoldRiebeckiteSite,
5566
5837
  showcase,
5567
5838
  startHonoxDevServer,
5568
- starter
5839
+ starter,
5840
+ workerNameFromDirectory
5569
5841
  };
package/dist/server.js CHANGED
@@ -17,6 +17,7 @@ async function riebeckiteSsgParams(content, parameter = "slug") {
17
17
  if (changeSet.fullRegenerationRequired || process.env.RIEBECKITE_SSG_FULL_REGENERATION === "1") {
18
18
  return [
19
19
  ...manifest.publicEntries.filter((entry) => entry.permalink !== "/").map((entry) => ({ [parameter]: entry.permalink.replace(/^\/+/, "") })),
20
+ ...[...manifest.publicRedirects.keys()].filter((path) => path !== "/").map((path) => ({ [parameter]: path.replace(/^\/+/, "") })),
20
21
  ...await pluginPageSsgParams(content, parameter)
21
22
  ];
22
23
  }
@@ -24,16 +25,19 @@ async function riebeckiteSsgParams(content, parameter = "slug") {
24
25
  changeSet.affected.filter((output) => output.kind !== "generated").map((output) => output.path)
25
26
  );
26
27
  const contentParams = manifest.publicEntries.filter((entry) => affected.has(routeOutputPath(entry.permalink))).map((entry) => ({ [parameter]: entry.permalink.replace(/^\/+/, "") }));
28
+ const redirectParams = [...manifest.publicRedirects.keys()].filter((path) => affected.has(routeOutputPath(path))).filter((path) => path !== "/").map((path) => ({ [parameter]: path.replace(/^\/+/, "") }));
27
29
  const pluginPageParams = (await content.getPagePaths()).filter((path) => affected.has(routeOutputPath(path))).filter((path) => path !== "/").map((path) => ({ [parameter]: path.replace(/^\/+/, "") }));
28
- return [...contentParams, ...pluginPageParams];
30
+ return [...contentParams, ...redirectParams, ...pluginPageParams];
29
31
  }
30
32
  function resolveContentRoute(manifest, pathname) {
31
33
  const path = normalizeRequestPath(pathname);
32
34
  const entry = manifest.byRoutablePermalink.get(path);
33
35
  if (entry) return { kind: "content", entry };
34
- const redirect = manifest.redirects.get(path);
36
+ const redirect = manifest.publicRedirects.get(path);
35
37
  if (!redirect) return null;
36
- const target = manifest.bySlug.get(redirect.slug);
38
+ const target = manifest.byRoutablePermalink.get(
39
+ manifest.bySlug.get(redirect.slug)?.permalink ?? ""
40
+ );
37
41
  if (!target) return null;
38
42
  return {
39
43
  kind: "redirect",
@@ -5,3 +5,4 @@ export type ScaffoldDeploymentOptions = {
5
5
  };
6
6
  export declare function assertGitHubRepository(value: string, option: string): void;
7
7
  export declare function deploymentTemplateFiles(options: ScaffoldDeploymentOptions): readonly SiteTemplateFile[];
8
+ export declare function wranglerJsoncFile(workerName: string): SiteTemplateFile;
@@ -10,6 +10,7 @@ export type ScaffoldSiteOptions = {
10
10
  readonly overwrite?: boolean;
11
11
  /** Generate the Cloudflare Workers GitHub Actions workflow. */
12
12
  readonly githubActions?: boolean;
13
+ readonly cloudflareWorkers?: boolean;
13
14
  /** Read content from this separate GitHub repository during deployment. */
14
15
  readonly contentRepository?: string;
15
16
  /** Site repository to notify from the generated content workflow. */
@@ -42,13 +42,8 @@ var l10n = np("@riebeckite/plugin-l10n", "l10n", (context) => {
42
42
  return `{ defaultLang: ${JSON.stringify(defaultLang)}, languages: ${JSON.stringify(context.languages)} }`;
43
43
  });
44
44
  var seo = np("@riebeckite/plugin-seo", "seo", {
45
- siteName: {
46
- depth: 1,
47
- value: (context) => JSON.stringify(context.variables.title)
48
- },
49
45
  sitemap: { depth: 1, value: "true" },
50
46
  robots: { depth: 2, value: "true" },
51
- defaultImage: { depth: 3, value: '"/ogp.png"' },
52
47
  feed: { depth: 3, value: "{ rss: true, atom: true, json: true }" }
53
48
  });
54
49
  var toc = np("@riebeckite/plugin-toc", "tocPlugin");
@@ -69,6 +64,17 @@ var codeEnhance = np("@riebeckite/plugin-code-enhance", "codeEnhance", {
69
64
  });
70
65
  var search = np("@riebeckite/plugin-search", "searchPlugin");
71
66
  var backlinks = np("@riebeckite/plugin-backlinks", "backlinksPlugin");
67
+ var breadcrumbs = np("@riebeckite/plugin-breadcrumbs", "breadcrumbsPlugin");
68
+ var navigationStarter = np(
69
+ "@riebeckite/plugin-navigation",
70
+ "navigation",
71
+ `{ items: [{ label: "Guide", href: "/guide" }, { label: "Examples", href: "/examples" }, { label: "Notes", href: "/notes/planning", children: [{ label: "Planning", href: "/notes/planning" }, { label: "Writing", href: "/notes/writing" }] }], secondary: [{ label: "Guide", href: "/guide" }, { label: "Examples", href: "/examples" }] }`
72
+ );
73
+ var navigationShowcase = np(
74
+ "@riebeckite/plugin-navigation",
75
+ "navigation",
76
+ `{ items: [{ label: "Guide", href: "/guide" }, { label: "Examples", href: "/examples" }, { label: "Framework", href: "/framework/plugins", children: [{ label: "Plugins", href: "/framework/plugins" }, { label: "Themes", href: "/framework/themes" }] }], secondary: [{ label: "Guide", href: "/guide" }, { label: "Examples", href: "/examples" }] }`
77
+ );
72
78
  var relatedPosts = np("@riebeckite/plugin-related-posts", "relatedPosts", {
73
79
  limit: { depth: 2, value: "5" },
74
80
  useTags: { depth: 2, value: "true" },
@@ -167,6 +173,7 @@ var taxonomy = np("@riebeckite/plugin-taxonomy", "taxonomy", {
167
173
  feeds: { depth: 3, value: "{ rss: true, atom: true, json: true }" },
168
174
  relatedLimit: { depth: 3, value: "8" }
169
175
  });
176
+ var folderPages = np("@riebeckite/plugin-folder-pages", "folderPagesPlugin");
170
177
  var autoCardLink = np(
171
178
  "@riebeckite/plugin-autocardlink",
172
179
  "autoCardLinkPlugin",
@@ -355,7 +362,7 @@ var quality = np(
355
362
  var deploy = np(
356
363
  "@riebeckite/plugin-deploy",
357
364
  "deployPlugin",
358
- (context) => `{ provider: "cloudflare-pages", baseUrl: ${JSON.stringify(context.variables.baseUrl)} }`
365
+ `{ provider: "cloudflare-pages" }`
359
366
  );
360
367
  var diagnostics = np(
361
368
  "@riebeckite/plugin-diagnostics",
@@ -421,6 +428,8 @@ var starter = {
421
428
  codeEnhance,
422
429
  search,
423
430
  backlinks,
431
+ breadcrumbs,
432
+ navigationStarter,
424
433
  relatedPosts,
425
434
  recentPosts,
426
435
  responsiveImage,
@@ -448,6 +457,8 @@ var showcase = {
448
457
  codeEnhance,
449
458
  search,
450
459
  backlinks,
460
+ breadcrumbs,
461
+ navigationShowcase,
451
462
  relatedPosts,
452
463
  share,
453
464
  changelog,
@@ -464,6 +475,7 @@ var showcase = {
464
475
  shortcodes,
465
476
  series,
466
477
  taxonomy,
478
+ folderPages,
467
479
  autoCardLink,
468
480
  richEmbed,
469
481
  gallery,
@@ -5,12 +5,16 @@ export type SiteTemplateVariables = {
5
5
  readonly description: string;
6
6
  readonly baseUrl: string;
7
7
  readonly locale: string;
8
+ readonly externalContent?: boolean;
9
+ };
10
+ export type SiteTemplateOptions = {
11
+ readonly cloudflareWorkers?: boolean;
8
12
  };
9
13
  export type SiteTemplateFile = {
10
14
  readonly path: string;
11
15
  readonly content: string | Uint8Array;
12
16
  };
13
- export declare function siteTemplateFiles(preset: ScaffoldPreset, variables: SiteTemplateVariables): readonly SiteTemplateFile[];
17
+ export declare function siteTemplateFiles(preset: ScaffoldPreset, variables: SiteTemplateVariables, options?: SiteTemplateOptions): readonly SiteTemplateFile[];
14
18
  /**
15
19
  * Resolve a plugin's options literal for the preset's depth, or `null` when
16
20
  * the plugin takes no options at that depth (and should be called bare).
@@ -1 +1 @@
1
- export declare const RIEBECKITE_VERSION = "^0.0.15";
1
+ export declare const RIEBECKITE_VERSION = "^0.0.17";
@@ -27,6 +27,11 @@ export declare const WRANGLER_DEFAULTS: {
27
27
  };
28
28
  /** Type for the wrangler config object */
29
29
  export type WranglerDefaults = typeof WRANGLER_DEFAULTS;
30
+ export declare const WRANGLER_VERSION = "^4.83.0";
31
+ export declare const DEFAULT_WORKER_NAME = "riebeckite-site";
32
+ export declare const WORKER_NAME_MAX_LENGTH = 63;
33
+ export declare function workerNameFromDirectory(directoryName: string): string;
34
+ export declare function buildDefaultWranglerConfig(workerName: string): string;
30
35
  /** Expected secret names in GitHub Actions workflow */
31
36
  export declare const GITHUB_ACTIONS_SECRETS: {
32
37
  /** Cloudflare API token with Workers Scripts: Edit permission */
@@ -4,11 +4,7 @@ function Article(props) {
4
4
  return /* @__PURE__ */ jsx(
5
5
  "article",
6
6
  {
7
- class: joinClassNames(
8
- "article-shell rb-article",
9
- props.class,
10
- props.className
11
- ),
7
+ class: joinClassNames("rb-article", props.class, props.className),
12
8
  "data-slot": props["data-slot"] ?? "article",
13
9
  children: props.children
14
10
  }
@@ -18,11 +14,7 @@ function ArticleLayout(props) {
18
14
  return /* @__PURE__ */ jsxs(
19
15
  "div",
20
16
  {
21
- class: joinClassNames(
22
- "article-shell__layout rb-article-layout",
23
- props.class,
24
- props.className
25
- ),
17
+ class: joinClassNames("rb-article-layout", props.class, props.className),
26
18
  children: [
27
19
  props.children,
28
20
  props.aside
@@ -32,7 +24,7 @@ function ArticleLayout(props) {
32
24
  }
33
25
  function ArticleHeader(props) {
34
26
  const className = joinClassNames(
35
- "article-shell__lead rb-article-header",
27
+ "rb-article-header",
36
28
  props.class,
37
29
  props.className
38
30
  );
@@ -51,11 +43,7 @@ function ArticleMeta(props) {
51
43
  return /* @__PURE__ */ jsx(
52
44
  "aside",
53
45
  {
54
- class: joinClassNames(
55
- "article-frontmatter rb-article-meta",
56
- props.class,
57
- props.className
58
- ),
46
+ class: joinClassNames("rb-article-meta", props.class, props.className),
59
47
  "aria-label": props.label ?? "Article metadata",
60
48
  children: props.children
61
49
  }
@@ -63,7 +51,7 @@ function ArticleMeta(props) {
63
51
  }
64
52
  function ArticleContent(props) {
65
53
  const className = joinClassNames(
66
- "article-shell__body rb-article-body",
54
+ "rb-article-body",
67
55
  props.class,
68
56
  props.className
69
57
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riebeckite/honox",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -36,7 +36,11 @@
36
36
  "esbuild": "^0.28.2",
37
37
  "hono": "^4.12.25",
38
38
  "vite": "^8.0.9",
39
- "@riebeckite/core": "0.0.15"
39
+ "@riebeckite/core": "0.0.17"
40
+ },
41
+ "devDependencies": {
42
+ "tsx": "^4.22.4",
43
+ "@riebeckite/test": "0.0.17"
40
44
  },
41
45
  "publishConfig": {
42
46
  "access": "public"