@riebeckite/honox 0.0.19 → 0.0.21

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/README.md CHANGED
@@ -84,6 +84,18 @@ imported from. Installed npm consumers do not need `workspaceRoot`.
84
84
  - Types: `RiebeckiteSsgOptions`, `ResolveHonoxApplicationOptions`,
85
85
  `ResolvedHonoxApplication`
86
86
 
87
+ The plugin also provides the bootstrap modules a generated site imports:
88
+ `virtual:riebeckite/config` (the resolved config) and
89
+ `virtual:riebeckite/content` (a configured `ContentManager`). Sites therefore no
90
+ longer need `app/config.ts`, `app/content.ts`, or `app/constants/paths.ts`.
91
+
92
+ ### `@riebeckite/honox/runtime`
93
+
94
+ - `resolveHonoxConfig(config, appRoot)` — applies `appRoot` to a resolved
95
+ config (build/output directories and absolute `content.directory`). It is a
96
+ lightweight subpath, so scripts outside the Vite pipeline can resolve the same
97
+ values without loading the Vite plugin.
98
+
87
99
  ### `@riebeckite/honox/server`
88
100
 
89
101
  - `mountRiebeckiteEndpoints(app, { config, content })`
@@ -100,8 +112,8 @@ layout, islands, and CSS.
100
112
 
101
113
  ## See also
102
114
 
103
- - [HonoX Integration](../../../docs/docs/framework/honox-integration.en.md)
104
- - [CLI Reference](../../../docs/docs/reference/cli.en.md)
105
- - [Configuration](../../../docs/docs/reference/configuration.en.md)
106
- - [Plugin System](../../../docs/docs/reference/plugin-api.en.md) / [Theme System](../../../docs/docs/reference/theme-api.en.md)
115
+ - [HonoX Integration](../../../docs/docs/framework/honox-integration.md)
116
+ - [CLI Reference](../../../docs/docs/reference/cli.md)
117
+ - [Configuration](../../../docs/docs/reference/configuration.md)
118
+ - [Plugin System](../../../docs/docs/reference/plugin-api.md) / [Theme System](../../../docs/docs/reference/theme-api.md)
107
119
 
package/README_ja.md CHANGED
@@ -72,6 +72,12 @@ const app = createApp({
72
72
  - `resolveHonoxApplication`、`resolveHonoxApplicationRoot`、`buildHonoxApplication`、`startHonoxDevServer`
73
73
  - 型: `RiebeckiteSsgOptions`、`ResolveHonoxApplicationOptions`、`ResolvedHonoxApplication`
74
74
 
75
+ Plugin は generated Site が import する bootstrap module も提供します。`virtual:riebeckite/config`(解決済み config)と `virtual:riebeckite/content`(構成済み `ContentManager`)です。そのため Site が `app/config.ts`、`app/content.ts`、`app/constants/paths.ts` を持つ必要はありません。
76
+
77
+ ### `@riebeckite/honox/runtime`
78
+
79
+ - `resolveHonoxConfig(config, appRoot)` — 解決済み config に `appRoot` を適用します(build / output directory と絶対 `content.directory`)。Vite の外で動く script が Vite Plugin を読み込まずに同じ値を解決できる軽量な subpath です。
80
+
75
81
  ### `@riebeckite/honox/server`
76
82
 
77
83
  - `mountRiebeckiteEndpoints(app, { config, content })`
@@ -84,8 +90,8 @@ const app = createApp({
84
90
 
85
91
  ## 関連資料
86
92
 
87
- - [HonoX Integration](../../../docs/docs/framework/honox-integration.md)
88
- - [CLI Reference](../../../docs/docs/reference/cli.md)
89
- - [Configuration](../../../docs/docs/reference/configuration.md)
90
- - [Plugin System](../../../docs/docs/reference/plugin-api.md) / [Theme System](../../../docs/docs/reference/theme-api.md)
93
+ - [HonoX Integration](../../../docs/docs/framework/honox-integration.ja.md)
94
+ - [CLI Reference](../../../docs/docs/reference/cli.ja.md)
95
+ - [Configuration](../../../docs/docs/reference/configuration.ja.md)
96
+ - [Plugin System](../../../docs/docs/reference/plugin-api.ja.md) / [Theme System](../../../docs/docs/reference/theme-api.ja.md)
91
97
 
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/config_loader.ts
2
2
  import fs2 from "node:fs/promises";
3
- import path2 from "node:path";
3
+ import path3 from "node:path";
4
4
  import { build as buildWithEsbuild } from "esbuild";
5
5
 
6
6
  // src/workspace_packages.ts
@@ -107,18 +107,32 @@ function escapeRegExp(value) {
107
107
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
108
108
  }
109
109
 
110
+ // src/resolved_config.ts
111
+ import path2 from "node:path";
112
+ function resolveHonoxConfig(config, appRoot) {
113
+ return {
114
+ ...config,
115
+ buildDirectory: path2.join(appRoot, ".riebeckite"),
116
+ outputDirectory: path2.join(appRoot, "dist"),
117
+ content: {
118
+ ...config.content,
119
+ directory: path2.resolve(appRoot, config.content.directory)
120
+ }
121
+ };
122
+ }
123
+
110
124
  // src/config_loader.ts
111
125
  async function loadRiebeckiteConfig(options) {
112
126
  const configFile = options.configFile ?? "riebeckite.config.ts";
113
127
  const temporaryDirectory = await fs2.mkdtemp(
114
- path2.join(options.configRoot, ".riebeckite-config-")
128
+ path3.join(options.configRoot, ".riebeckite-config-")
115
129
  );
116
- const outputFile = path2.join(temporaryDirectory, "config.mjs");
130
+ const outputFile = path3.join(temporaryDirectory, "config.mjs");
117
131
  try {
118
132
  await buildWithEsbuild({
119
133
  stdin: {
120
134
  contents: `
121
- import rawConfig from ${JSON.stringify(path2.resolve(options.configRoot, configFile))};
135
+ import rawConfig from ${JSON.stringify(path3.resolve(options.configRoot, configFile))};
122
136
  import { resolveConfigModule } from "@riebeckite/core";
123
137
  export default resolveConfigModule(rawConfig);
124
138
  `,
@@ -140,24 +154,13 @@ async function loadRiebeckiteConfig(options) {
140
154
  await fs2.rm(temporaryDirectory, { recursive: true, force: true });
141
155
  }
142
156
  }
143
- function resolveHonoxConfig(config, appRoot) {
144
- return {
145
- ...config,
146
- buildDirectory: path2.join(appRoot, ".riebeckite"),
147
- outputDirectory: path2.join(appRoot, "dist"),
148
- content: {
149
- ...config.content,
150
- directory: path2.resolve(appRoot, config.content.directory)
151
- }
152
- };
153
- }
154
157
  function pathToFileUrl(filePath) {
155
158
  return `file:///${filePath.replace(/\\/g, "/").replace(/^([A-Za-z]):/, "$1:")}`;
156
159
  }
157
160
 
158
161
  // src/honox_islands.ts
159
162
  import fs3 from "node:fs/promises";
160
- import path3 from "node:path";
163
+ import path4 from "node:path";
161
164
  import { parse } from "@babel/parser";
162
165
  var honoxIslandDependencyPluginName = "inject-importing-islands";
163
166
  var importingIslandsExport = "__importing_islands";
@@ -184,8 +187,8 @@ function scopedIslandDependencyPlugin() {
184
187
  return {
185
188
  name: "riebeckite-inject-importing-islands",
186
189
  configResolved(config) {
187
- appDirectory = path3.resolve(config.root, "app");
188
- islandDirectory = path3.join(appDirectory, "islands");
190
+ appDirectory = path4.resolve(config.root, "app");
191
+ islandDirectory = path4.join(appDirectory, "islands");
189
192
  },
190
193
  async transform(sourceCode, id) {
191
194
  const modulePath = toFilePath(id);
@@ -248,8 +251,8 @@ function toFilePath(id) {
248
251
  return id.split("?", 1)[0] ?? id;
249
252
  }
250
253
  function isInDirectory(filePath, directory) {
251
- const relativePath = path3.relative(directory, filePath);
252
- return relativePath !== "" && !relativePath.startsWith(`..${path3.sep}`) && !path3.isAbsolute(relativePath);
254
+ const relativePath = path4.relative(directory, filePath);
255
+ return relativePath !== "" && !relativePath.startsWith(`..${path4.sep}`) && !path4.isAbsolute(relativePath);
253
256
  }
254
257
  function isJavaScriptModule(filePath) {
255
258
  return /\.[cm]?[jt]sx?$/.test(filePath);
@@ -275,7 +278,7 @@ import {
275
278
 
276
279
  // src/content_assets.ts
277
280
  import fs4 from "node:fs/promises";
278
- import path5 from "node:path";
281
+ import path6 from "node:path";
279
282
  import {
280
283
  isIgnoredContentPath as isIgnoredContentPath2,
281
284
  isImagePath as isImagePath2,
@@ -283,13 +286,72 @@ import {
283
286
  } from "@riebeckite/core";
284
287
 
285
288
  // src/content_watch.ts
286
- import path4 from "node:path";
289
+ import path5 from "node:path";
287
290
  import {
288
291
  isAttachmentPath,
289
292
  isIgnoredContentPath,
290
293
  isImagePath,
291
294
  normalizeContentPath
292
295
  } from "@riebeckite/core";
296
+
297
+ // src/runtime_modules.ts
298
+ var riebeckiteConfigModuleId = "virtual:riebeckite/config";
299
+ var riebeckiteContentModuleId = "virtual:riebeckite/content";
300
+ var runtimeModuleIds = [
301
+ riebeckiteConfigModuleId,
302
+ riebeckiteContentModuleId
303
+ ];
304
+ var resolvedConfigModuleId = `\0${riebeckiteConfigModuleId}`;
305
+ var resolvedContentModuleId = `\0${riebeckiteContentModuleId}`;
306
+ function riebeckiteRuntimeModules(getApplication) {
307
+ return {
308
+ name: "riebeckite-runtime-modules",
309
+ resolveId(id) {
310
+ if (id === riebeckiteConfigModuleId) return resolvedConfigModuleId;
311
+ if (id === riebeckiteContentModuleId) return resolvedContentModuleId;
312
+ return null;
313
+ },
314
+ load(id) {
315
+ if (id === resolvedConfigModuleId) {
316
+ const { appRoot, configFile } = getApplication();
317
+ return createConfigModule(appRoot, configFile);
318
+ }
319
+ if (id === resolvedContentModuleId) return createContentModule();
320
+ return null;
321
+ }
322
+ };
323
+ }
324
+ function isRiebeckiteRuntimeModuleId(id) {
325
+ if (!id) return false;
326
+ return runtimeModuleIds.some(
327
+ (moduleId) => id === moduleId || id === `\0${moduleId}`
328
+ );
329
+ }
330
+ function createConfigModule(appRoot, configFile) {
331
+ const configSpecifier = configFile.replace(/\\/g, "/");
332
+ return `import * as rawConfigModule from ${JSON.stringify(configSpecifier)};
333
+ import { resolveConfigModule } from "@riebeckite/core";
334
+ import { resolveHonoxConfig } from "@riebeckite/honox/runtime";
335
+
336
+ export const config = resolveHonoxConfig(
337
+ resolveConfigModule(rawConfigModule),
338
+ ${JSON.stringify(appRoot)},
339
+ );
340
+ `;
341
+ }
342
+ function createContentModule() {
343
+ return `import { ContentManager } from "@riebeckite/core";
344
+ import { config } from ${JSON.stringify(riebeckiteConfigModuleId)};
345
+
346
+ export const content = new ContentManager(
347
+ config.content.directory,
348
+ config.content.exclude,
349
+ { config, plugins: config.plugins },
350
+ );
351
+ `;
352
+ }
353
+
354
+ // src/content_watch.ts
293
355
  var CONTENT_RELOAD_DEBOUNCE_MS = 60;
294
356
  var configuredWatchers = /* @__PURE__ */ new WeakSet();
295
357
  function riebeckiteContentWatch(options) {
@@ -345,24 +407,27 @@ function isPathInsideDirectory(directory, file) {
345
407
  }
346
408
  function toLogicalPath(directory, file) {
347
409
  return normalizeContentPath(
348
- path4.relative(normalizeContentPath(directory), normalizeContentPath(file))
410
+ path5.relative(normalizeContentPath(directory), normalizeContentPath(file))
349
411
  );
350
412
  }
351
413
  function invalidateApplicationRuntime(server, appRoot) {
352
414
  const moduleGraph = server.environments?.ssr?.moduleGraph;
353
415
  if (!moduleGraph) return 0;
354
- const appDirectory = path4.join(appRoot, "app") + path4.sep;
416
+ const appDirectory = path5.join(appRoot, "app") + path5.sep;
355
417
  const seen = /* @__PURE__ */ new Set();
356
418
  const timestamp = Date.now();
357
419
  let invalidated = 0;
358
420
  for (const mod of moduleGraph.idToModuleMap.values()) {
359
- const file = mod.file;
360
- if (!file || !isPathInsideDirectory(appDirectory, file)) continue;
421
+ if (!isApplicationRuntimeModule(mod, appDirectory)) continue;
361
422
  moduleGraph.invalidateModule(mod, seen, timestamp);
362
423
  invalidated += 1;
363
424
  }
364
425
  return invalidated;
365
426
  }
427
+ function isApplicationRuntimeModule(mod, appDirectory) {
428
+ if (mod.file && isPathInsideDirectory(appDirectory, mod.file)) return true;
429
+ return isRiebeckiteRuntimeModuleId(mod.id);
430
+ }
366
431
  function suppressRestartOnContentEvents(server, isContentFile) {
367
432
  for (const event of ["add", "unlink"]) {
368
433
  const listeners = server.watcher.rawListeners(event).slice();
@@ -397,7 +462,7 @@ function riebeckiteContentAssets(options) {
397
462
  enforce: "pre",
398
463
  configureServer(server) {
399
464
  const contentRoot = options.contentRoot();
400
- const entry = path5.join(options.appRoot(), "app", "server.ts");
465
+ const entry = path6.join(options.appRoot(), "app", "server.ts");
401
466
  const base = server.config.base;
402
467
  const publicDir = server.config.publicDir;
403
468
  let cachedManifest;
@@ -437,7 +502,7 @@ function riebeckiteContentAssets(options) {
437
502
  next();
438
503
  return;
439
504
  }
440
- const file = path5.join(contentRoot, logicalPath);
505
+ const file = path6.join(contentRoot, logicalPath);
441
506
  if (!isPathInsideDirectory(contentRoot, file)) {
442
507
  next();
443
508
  return;
@@ -484,16 +549,16 @@ function collectPublicImagePaths(manifest) {
484
549
  async function collectSiteOwnedOutputPaths(publicDir) {
485
550
  const paths = /* @__PURE__ */ new Set();
486
551
  if (!publicDir) return paths;
487
- const root = path5.resolve(publicDir);
552
+ const root = path6.resolve(publicDir);
488
553
  const walk = async (directory) => {
489
554
  const entries = await fs4.readdir(directory, { withFileTypes: true }).catch(() => void 0);
490
555
  if (!entries) return;
491
556
  for (const entry of entries) {
492
- const full = path5.join(directory, entry.name);
557
+ const full = path6.join(directory, entry.name);
493
558
  if (entry.isDirectory()) {
494
559
  await walk(full);
495
560
  } else {
496
- paths.add(path5.relative(root, full).replaceAll("\\", "/"));
561
+ paths.add(path6.relative(root, full).replaceAll("\\", "/"));
497
562
  }
498
563
  }
499
564
  };
@@ -528,7 +593,7 @@ function imageContentType(logicalPath) {
528
593
  }
529
594
  async function hasSiteOwnedFile(publicDir, logicalPath) {
530
595
  if (!publicDir) return false;
531
- const file = path5.join(publicDir, logicalPath);
596
+ const file = path6.join(publicDir, logicalPath);
532
597
  try {
533
598
  return (await fs4.stat(file)).isFile();
534
599
  } catch {
@@ -737,7 +802,7 @@ function riebeckiteSsg(options = {}) {
737
802
  outputChangeSet.affected.filter((output) => output.kind === "generated").map((output) => output.path)
738
803
  ) : void 0,
739
804
  siteOwnedOutputPaths,
740
- (path9) => shadowedOutputs.add(path9)
805
+ (path10) => shadowedOutputs.add(path10)
741
806
  )
742
807
  );
743
808
  if (!outputChangeSet)
@@ -745,21 +810,21 @@ function riebeckiteSsg(options = {}) {
745
810
  if (canUseIncremental) {
746
811
  await measure(timingsMs, "unchangedOutputReuse", async () => {
747
812
  for (const output of outputChangeSet.unchanged) {
748
- const path9 = output.path;
749
- if (siteOwnedOutputPaths.has(path9)) {
750
- shadowedOutputs.add(path9);
813
+ const path10 = output.path;
814
+ if (siteOwnedOutputPaths.has(path10)) {
815
+ shadowedOutputs.add(path10);
751
816
  continue;
752
817
  }
753
- const cached = previousOutputCache.outputs[path9];
818
+ const cached = previousOutputCache.outputs[path10];
754
819
  if (!cached) continue;
755
820
  this.emitFile({
756
821
  type: "asset",
757
- fileName: path9,
822
+ fileName: path10,
758
823
  source: decodeOutput(cached)
759
824
  });
760
825
  metrics.emittedOutputCount += 1;
761
826
  metrics.reusedOutputCount += 1;
762
- nextOutputCache.outputs[path9] = cached;
827
+ nextOutputCache.outputs[path10] = cached;
763
828
  }
764
829
  });
765
830
  }
@@ -791,6 +856,9 @@ function riebeckiteSsg(options = {}) {
791
856
  );
792
857
  }
793
858
  await writeSsgOutputMetrics(metrics);
859
+ this.info(
860
+ `SSG outputs: ${metrics.renderedOutputCount} rendered · ${metrics.reusedOutputCount} reused · ${metrics.deletedOutputCount} removed`
861
+ );
794
862
  inspectGeneratedHtmlPages(module, generatedHtml, {
795
863
  warn: (message) => this.warn(message),
796
864
  info: (message) => this.info(message)
@@ -820,16 +888,16 @@ async function findOversizedAssets(directory, relativeDirectory = "") {
820
888
  );
821
889
  const nestedAssets = await Promise.all(
822
890
  entries.map(async (entry) => {
823
- const path9 = join(directory, entry.name);
891
+ const path10 = join(directory, entry.name);
824
892
  const relativePath = join(relativeDirectory, entry.name).replaceAll(
825
893
  "\\",
826
894
  "/"
827
895
  );
828
896
  if (entry.isDirectory()) {
829
- return findOversizedAssets(path9, relativePath);
897
+ return findOversizedAssets(path10, relativePath);
830
898
  }
831
899
  if (!entry.isFile()) return [];
832
- const size = (await stat(path9)).size;
900
+ const size = (await stat(path10)).size;
833
901
  return size > cloudflareWorkerAssetLimit ? [{ path: relativePath, size }] : [];
834
902
  })
835
903
  );
@@ -846,8 +914,8 @@ function shouldApplyRiebeckiteSsg(_config, env) {
846
914
  }
847
915
  function findBuildOutputSiteCollisions(buildOutputPaths, siteOwnedOutputPaths) {
848
916
  const collisions = /* @__PURE__ */ new Set();
849
- for (const path9 of buildOutputPaths) {
850
- if (siteOwnedOutputPaths.has(path9)) collisions.add(path9);
917
+ for (const path10 of buildOutputPaths) {
918
+ if (siteOwnedOutputPaths.has(path10)) collisions.add(path10);
851
919
  }
852
920
  return [...collisions].sort();
853
921
  }
@@ -922,14 +990,14 @@ async function collectAppFiles(directory, files) {
922
990
  }
923
991
  }
924
992
  }
925
- async function loadOutputCache(path9, timingsMs, log = () => {
993
+ async function loadOutputCache(path10, timingsMs, log = () => {
926
994
  }) {
927
995
  let raw;
928
996
  try {
929
997
  raw = await measure(
930
998
  timingsMs,
931
999
  "outputCacheRead",
932
- () => readFile(path9, "utf8")
1000
+ () => readFile(path10, "utf8")
933
1001
  );
934
1002
  } catch (error) {
935
1003
  if (isNotFoundError(error)) return void 0;
@@ -966,10 +1034,10 @@ function isOutputCacheEntries(value) {
966
1034
  (entry) => typeof entry === "object" && entry !== null && typeof entry.source === "string" && (entry.encoding === "utf8" || entry.encoding === "base64")
967
1035
  );
968
1036
  }
969
- async function saveOutputCache(path9, state, timingsMs) {
970
- const directory = dirname(path9);
1037
+ async function saveOutputCache(path10, state, timingsMs) {
1038
+ const directory = dirname(path10);
971
1039
  await mkdir(directory, { recursive: true });
972
- const temporaryPath = `${path9}.${randomUUID()}.tmp`;
1040
+ const temporaryPath = `${path10}.${randomUUID()}.tmp`;
973
1041
  const serialized = measureSync(
974
1042
  timingsMs,
975
1043
  "outputCacheSerialize",
@@ -985,7 +1053,7 @@ async function saveOutputCache(path9, state, timingsMs) {
985
1053
  await measure(
986
1054
  timingsMs,
987
1055
  "outputCacheRename",
988
- () => replaceFile(temporaryPath, path9)
1056
+ () => replaceFile(temporaryPath, path10)
989
1057
  );
990
1058
  } catch (error) {
991
1059
  await rm(temporaryPath, { force: true }).catch(() => void 0);
@@ -1088,16 +1156,23 @@ function createRiebeckiteSsg(options = {}) {
1088
1156
  }
1089
1157
 
1090
1158
  // src/vite_plugin.ts
1091
- import path8 from "node:path";
1159
+ import path9 from "node:path";
1092
1160
 
1093
1161
  // src/asset_entries.ts
1094
1162
  import fs5 from "node:fs";
1095
- import path6 from "node:path";
1163
+ import path7 from "node:path";
1096
1164
  import { uniqueStrings } from "@riebeckite/core";
1097
1165
  function writeRiebeckiteAssetEntries(config, paths) {
1166
+ writeGeneratedFile(paths.frameworkStyles, createFrameworkStylesEntry());
1098
1167
  writeGeneratedFile(paths.pluginStyles, createPluginStylesEntry(config));
1099
1168
  writeGeneratedFile(paths.themeStyles, createThemeStylesEntry(config));
1100
1169
  }
1170
+ function createFrameworkStylesEntry() {
1171
+ return createStylesheetEntry(
1172
+ ["@riebeckite/honox/style.css"],
1173
+ "Framework structural styles. Generated by @riebeckite/honox; do not edit. Import this before the plugin and theme stylesheets."
1174
+ );
1175
+ }
1101
1176
  function createPluginStylesEntry(config) {
1102
1177
  return createStylesheetEntry(
1103
1178
  collectPluginStyleSpecifiers(config),
@@ -1126,7 +1201,7 @@ ${imports}
1126
1201
  }
1127
1202
  function writeGeneratedFile(filePath, contents) {
1128
1203
  if (readGeneratedFile(filePath) === contents) return;
1129
- fs5.mkdirSync(path6.dirname(filePath), { recursive: true });
1204
+ fs5.mkdirSync(path7.dirname(filePath), { recursive: true });
1130
1205
  fs5.writeFileSync(filePath, contents);
1131
1206
  }
1132
1207
  function readGeneratedFile(filePath) {
@@ -1200,15 +1275,15 @@ ${initializers.map((initializer) => ` ${initializer};`).join("\n")}
1200
1275
 
1201
1276
  // src/vite_runner.ts
1202
1277
  import fs6 from "node:fs/promises";
1203
- import path7 from "node:path";
1278
+ import path8 from "node:path";
1204
1279
  import { build, createServer as createServer2 } from "vite";
1205
1280
  var viteConfigFileNames = [
1206
1281
  "vite.config.ts",
1207
1282
  "vite.config.js",
1208
1283
  "vite.config.mjs"
1209
1284
  ];
1210
- async function resolveHonoxApplicationRoot(configRoot) {
1211
- const roots = await findViteApplicationRoots(configRoot);
1285
+ async function resolveHonoxApplicationRoot(configRoot, startDirectory) {
1286
+ const roots = await findViteApplicationRoots(configRoot, startDirectory);
1212
1287
  if (roots.length === 1) return roots[0];
1213
1288
  if (roots.length === 0) {
1214
1289
  throw new HonoxApplicationRootError(
@@ -1226,9 +1301,13 @@ var HonoxApplicationRootError = class extends Error {
1226
1301
  }
1227
1302
  };
1228
1303
  async function resolveHonoxApplication(options) {
1229
- const configRoot = path7.resolve(options.configRoot);
1230
- const appRoot = options.appRoot ? path7.resolve(options.appRoot) : await resolveHonoxApplicationRoot(configRoot);
1304
+ const configRoot = path8.resolve(options.configRoot);
1305
+ const appRoot = options.appRoot ? path8.resolve(options.appRoot) : await resolveHonoxApplicationRoot(configRoot, options.startDirectory);
1231
1306
  const workspaceRoot = options.workspaceRoot ?? await findWorkspaceRoot(configRoot);
1307
+ const configFile = path8.resolve(
1308
+ configRoot,
1309
+ options.configFile ?? "riebeckite.config.ts"
1310
+ );
1232
1311
  const rawConfig = await loadRiebeckiteConfig({
1233
1312
  configRoot,
1234
1313
  configFile: options.configFile,
@@ -1238,20 +1317,21 @@ async function resolveHonoxApplication(options) {
1238
1317
  return {
1239
1318
  config,
1240
1319
  configRoot,
1320
+ configFile,
1241
1321
  appRoot,
1242
1322
  contentRoot: config.content.directory
1243
1323
  };
1244
1324
  }
1245
1325
  async function findWorkspaceRoot(configRoot) {
1246
1326
  try {
1247
- const workspaceConfig = path7.join(configRoot, "pnpm-workspace.yaml");
1327
+ const workspaceConfig = path8.join(configRoot, "pnpm-workspace.yaml");
1248
1328
  return (await fs6.stat(workspaceConfig)).isFile() ? configRoot : void 0;
1249
1329
  } catch {
1250
1330
  return void 0;
1251
1331
  }
1252
1332
  }
1253
1333
  async function startHonoxDevServer(options) {
1254
- const root = path7.resolve(options.root);
1334
+ const root = path8.resolve(options.root);
1255
1335
  await withWorkingDirectory(root, async () => {
1256
1336
  const server = await createServer2({ root });
1257
1337
  await server.listen();
@@ -1260,7 +1340,7 @@ async function startHonoxDevServer(options) {
1260
1340
  });
1261
1341
  }
1262
1342
  async function buildHonoxApplication(options) {
1263
- const root = path7.resolve(options.root);
1343
+ const root = path8.resolve(options.root);
1264
1344
  await withWorkingDirectory(root, async () => {
1265
1345
  await runTraced(
1266
1346
  options.tracer,
@@ -1296,7 +1376,9 @@ async function waitForShutdown(server) {
1296
1376
  process.once("SIGTERM", close);
1297
1377
  });
1298
1378
  }
1299
- async function findViteApplicationRoots(root) {
1379
+ async function findViteApplicationRoots(root, startDirectory) {
1380
+ const applicationRoot = startDirectory ? await findViteApplicationRootFrom(startDirectory, root) : void 0;
1381
+ if (applicationRoot) return [applicationRoot];
1300
1382
  const directories = [root];
1301
1383
  const applicationRoots = [];
1302
1384
  while (directories.length > 0) {
@@ -1308,15 +1390,29 @@ async function findViteApplicationRoots(root) {
1308
1390
  if (!entry.isDirectory() || entry.name === "node_modules" || entry.name === ".git" || entry.name === "tests") {
1309
1391
  continue;
1310
1392
  }
1311
- directories.push(path7.join(directory, entry.name));
1393
+ directories.push(path8.join(directory, entry.name));
1312
1394
  }
1313
1395
  }
1314
1396
  return applicationRoots;
1315
1397
  }
1398
+ async function findViteApplicationRootFrom(startDirectory, configRoot) {
1399
+ let directory = path8.resolve(startDirectory);
1400
+ while (isWithin(configRoot, directory)) {
1401
+ if (await hasViteConfig(directory)) return directory;
1402
+ const parent = path8.dirname(directory);
1403
+ if (parent === directory) return void 0;
1404
+ directory = parent;
1405
+ }
1406
+ return void 0;
1407
+ }
1408
+ function isWithin(parent, target) {
1409
+ const relative2 = path8.relative(parent, target);
1410
+ return relative2 === "" || !relative2.startsWith("..") && !path8.isAbsolute(relative2);
1411
+ }
1316
1412
  async function hasViteConfig(directory) {
1317
1413
  for (const fileName of viteConfigFileNames) {
1318
1414
  try {
1319
- if ((await fs6.stat(path7.join(directory, fileName))).isFile()) return true;
1415
+ if ((await fs6.stat(path8.join(directory, fileName))).isFile()) return true;
1320
1416
  } catch {
1321
1417
  }
1322
1418
  }
@@ -1336,7 +1432,7 @@ var defaultSsrExternals = [
1336
1432
  ];
1337
1433
  function riebeckite(options = {}) {
1338
1434
  let resolvedConfig;
1339
- let contentWatchRoots;
1435
+ let application;
1340
1436
  const getConfig = () => {
1341
1437
  if (!resolvedConfig) {
1342
1438
  throw new Error("Riebeckite config has not been loaded yet.");
@@ -1347,19 +1443,23 @@ function riebeckite(options = {}) {
1347
1443
  {
1348
1444
  name: "riebeckite-host-integration",
1349
1445
  async config(userConfig) {
1350
- const root = userConfig.root ? path8.resolve(userConfig.root) : process.cwd();
1446
+ const root = userConfig.root ? path9.resolve(userConfig.root) : process.cwd();
1351
1447
  const appRoot = options.appRoot ?? root;
1352
- const application = await resolveHonoxApplication({
1448
+ const resolvedApplication = await resolveHonoxApplication({
1353
1449
  configRoot: options.configRoot ?? appRoot,
1354
1450
  configFile: options.configFile,
1355
1451
  appRoot,
1356
1452
  workspaceRoot: options.workspaceRoot
1357
1453
  });
1358
- resolvedConfig = application.config;
1359
- contentWatchRoots = application;
1454
+ resolvedConfig = resolvedApplication.config;
1455
+ application = resolvedApplication;
1360
1456
  writeRiebeckiteAssetEntries(resolvedConfig, {
1361
- pluginStyles: path8.join(appRoot, "app/.riebeckite/plugin-styles.css"),
1362
- themeStyles: path8.join(appRoot, "app/.riebeckite/theme-styles.css")
1457
+ frameworkStyles: path9.join(
1458
+ appRoot,
1459
+ "app/.riebeckite/framework-styles.css"
1460
+ ),
1461
+ pluginStyles: path9.join(appRoot, "app/.riebeckite/plugin-styles.css"),
1462
+ themeStyles: path9.join(appRoot, "app/.riebeckite/theme-styles.css")
1363
1463
  });
1364
1464
  return {
1365
1465
  define: {
@@ -1377,22 +1477,23 @@ function riebeckite(options = {}) {
1377
1477
  }
1378
1478
  },
1379
1479
  riebeckiteClientModule(getConfig),
1480
+ riebeckiteRuntimeModules(() => requireApplication(application)),
1380
1481
  riebeckiteContentAssets({
1381
- appRoot: () => requireContentWatchRoots(contentWatchRoots).appRoot,
1382
- contentRoot: () => requireContentWatchRoots(contentWatchRoots).contentRoot
1482
+ appRoot: () => requireApplication(application).appRoot,
1483
+ contentRoot: () => requireApplication(application).contentRoot
1383
1484
  }),
1384
1485
  riebeckiteContentWatch({
1385
- appRoot: () => requireContentWatchRoots(contentWatchRoots).appRoot,
1386
- contentRoot: () => requireContentWatchRoots(contentWatchRoots).contentRoot,
1486
+ appRoot: () => requireApplication(application).appRoot,
1487
+ contentRoot: () => requireApplication(application).contentRoot,
1387
1488
  exclude: () => getConfig().content.exclude ?? []
1388
1489
  })
1389
1490
  ];
1390
1491
  }
1391
- function requireContentWatchRoots(roots) {
1392
- if (!roots) {
1492
+ function requireApplication(application) {
1493
+ if (!application) {
1393
1494
  throw new Error("Riebeckite application has not been resolved yet.");
1394
1495
  }
1395
- return roots;
1496
+ return application;
1396
1497
  }
1397
1498
  function riebeckiteVite(options = {}) {
1398
1499
  const { ssg, ssrExternals, ...integration } = options;
@@ -0,0 +1 @@
1
+ export { resolveHonoxConfig } from "./src/resolved_config.js";
@@ -0,0 +1,16 @@
1
+ // src/resolved_config.ts
2
+ import path from "node:path";
3
+ function resolveHonoxConfig(config, appRoot) {
4
+ return {
5
+ ...config,
6
+ buildDirectory: path.join(appRoot, ".riebeckite"),
7
+ outputDirectory: path.join(appRoot, "dist"),
8
+ content: {
9
+ ...config.content,
10
+ directory: path.resolve(appRoot, config.content.directory)
11
+ }
12
+ };
13
+ }
14
+ export {
15
+ resolveHonoxConfig
16
+ };
package/dist/server.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export type { ResolvedContentRoute, ResolvedRiebeckiteRoute, } from "./src/content_route.js";
2
- export { contentRouteSsgParams, pluginPageSsgParams, resolveContentRoute, resolveRiebeckiteRoute, riebeckiteSsgParams, ssgEnumerableHandler, } from "./src/content_route.js";
1
+ export type { ResolvedContentRequest, ResolvedContentRoute, ResolvedRiebeckiteRoute, } from "./src/content_route.js";
2
+ export { applyRiebeckiteRouteContext, contentRouteSsgParams, pluginPageSsgParams, resolveContentRoute, resolveRiebeckiteContentRequest, resolveRiebeckiteHomeRequest, resolveRiebeckiteRoute, riebeckiteSsgParams, ssgEnumerableHandler, } from "./src/content_route.js";
3
3
  export { mountRiebeckiteEndpoints } from "./src/endpoints.js";
package/dist/server.js CHANGED
@@ -1,10 +1,54 @@
1
1
  // src/content_route.ts
2
- import { htmlOutputPath } from "@riebeckite/core";
2
+ import { getEntryLanguage, htmlOutputPath } from "@riebeckite/core";
3
3
  async function resolveRiebeckiteRoute(content, pathname) {
4
4
  const page = await content.resolvePage(pathname);
5
5
  if (page) return { kind: "page", page };
6
6
  return resolveContentRoute(await content.getManifest(), pathname);
7
7
  }
8
+ function applyRiebeckiteRouteContext(c, route) {
9
+ if (route.kind === "page") {
10
+ c.set("headTags", route.page.headTags ?? []);
11
+ c.set("htmlLanguage", route.page.language);
12
+ return;
13
+ }
14
+ if (route.kind === "content") {
15
+ c.set("headTags", route.entry.headTags ?? []);
16
+ c.set("htmlLanguage", getEntryLanguage(route.entry));
17
+ }
18
+ }
19
+ var contentExtensionPattern = /\.[a-zA-Z0-9]+$/;
20
+ async function resolveRouteRequest(c, content) {
21
+ const route = await resolveRiebeckiteRoute(content, c.req.path);
22
+ if (!route) return { kind: "response", response: c.notFound() };
23
+ if (route.kind === "redirect") {
24
+ return {
25
+ kind: "response",
26
+ response: c.redirect(route.location, route.status)
27
+ };
28
+ }
29
+ applyRiebeckiteRouteContext(c, route);
30
+ if (route.kind === "page") return { kind: "page", page: route.page };
31
+ const post = await content.getProcessedContent(route.entry.slug);
32
+ return { kind: "content", entry: route.entry, post };
33
+ }
34
+ async function resolveRiebeckiteContentRequest(c, content, parameter = "slug") {
35
+ const requestedSlug = c.req.param(parameter);
36
+ if (!requestedSlug) return { kind: "response", response: c.notFound() };
37
+ if (contentExtensionPattern.test(requestedSlug)) {
38
+ return { kind: "response", response: c.notFound() };
39
+ }
40
+ return resolveRouteRequest(c, content);
41
+ }
42
+ async function resolveRiebeckiteHomeRequest(c, content) {
43
+ const indexEntry = (await content.getManifest()).bySlug.get("index");
44
+ if (indexEntry && indexEntry.permalink !== "/") {
45
+ return {
46
+ kind: "response",
47
+ response: c.redirect(indexEntry.permalink, 308)
48
+ };
49
+ }
50
+ return resolveRouteRequest(c, content);
51
+ }
8
52
  async function pluginPageSsgParams(content, parameter = "slug") {
9
53
  const paths = await content.getPagePaths();
10
54
  return paths.filter((path) => path !== "/").map((path) => ({ [parameter]: path.replace(/^\/+/, "") }));
@@ -192,10 +236,13 @@ function findHeader(headers, name) {
192
236
  return key ? headers[key] : void 0;
193
237
  }
194
238
  export {
239
+ applyRiebeckiteRouteContext,
195
240
  contentRouteSsgParams,
196
241
  mountRiebeckiteEndpoints,
197
242
  pluginPageSsgParams,
198
243
  resolveContentRoute,
244
+ resolveRiebeckiteContentRequest,
245
+ resolveRiebeckiteHomeRequest,
199
246
  resolveRiebeckiteRoute,
200
247
  riebeckiteSsgParams,
201
248
  ssgEnumerableHandler
@@ -1,5 +1,6 @@
1
1
  import { type ResolvedRiebeckiteConfig } from "@riebeckite/core";
2
2
  export type GeneratedAssetPaths = {
3
+ frameworkStyles: string;
3
4
  pluginStyles: string;
4
5
  themeStyles: string;
5
6
  };
@@ -5,4 +5,4 @@ export type RiebeckiteConfigLoaderOptions = {
5
5
  workspaceRoot?: string;
6
6
  };
7
7
  export declare function loadRiebeckiteConfig(options: RiebeckiteConfigLoaderOptions): Promise<ResolvedRiebeckiteConfig>;
8
- export declare function resolveHonoxConfig(config: ResolvedRiebeckiteConfig, appRoot: string): ResolvedRiebeckiteConfig;
8
+ export { resolveHonoxConfig } from "./resolved_config.js";
@@ -1,4 +1,4 @@
1
- import type { ContentManager, ContentManifest, ContentManifestEntry, ResolvedPluginPage } from "@riebeckite/core";
1
+ import type { ContentManager, ContentManifest, ContentManifestEntry, PostContent, ResolvedPluginPage } from "@riebeckite/core";
2
2
  import type { Context, Handler, MiddlewareHandler } from "hono";
3
3
  export type ResolvedContentRoute = {
4
4
  kind: "content";
@@ -18,6 +18,48 @@ export type ResolvedRiebeckiteRoute = Exclude<ResolvedContentRoute, null> | {
18
18
  * HonoX routes themselves.
19
19
  */
20
20
  export declare function resolveRiebeckiteRoute(content: Pick<ContentManager, "resolvePage" | "getManifest">, pathname: string): Promise<ResolvedRiebeckiteRoute>;
21
+ /**
22
+ * The resolved route request a Site consumes from a catch-all content route.
23
+ *
24
+ * `content` carries everything the Site composition needs; `page` and
25
+ * `response` are framework mechanics the Site forwards without interpreting.
26
+ */
27
+ export type ResolvedContentRequest = {
28
+ kind: "content";
29
+ entry: ContentManifestEntry;
30
+ post: PostContent;
31
+ } | {
32
+ kind: "page";
33
+ page: ResolvedPluginPage;
34
+ } | {
35
+ kind: "response";
36
+ response: Response | Promise<Response>;
37
+ };
38
+ /**
39
+ * Assigns the resolved route metadata to the request context.
40
+ *
41
+ * Plugins and primitives read `htmlLanguage` and `headTags` from the context,
42
+ * so the resolver owns their values instead of each route rebuilding them.
43
+ */
44
+ export declare function applyRiebeckiteRouteContext(c: Context, route: Exclude<ResolvedRiebeckiteRoute, null>): void;
45
+ type RouteContent = Pick<ContentManager, "resolvePage" | "getManifest" | "getProcessedContent">;
46
+ /**
47
+ * Resolves a catch-all content request into what a Site should render.
48
+ *
49
+ * Owns the route mechanics a Site must not repeat: the wildcard and content
50
+ * extension guards, plugin page vs content resolution, redirect and not-found
51
+ * responses, context assignment, and loading the processed content.
52
+ */
53
+ export declare function resolveRiebeckiteContentRequest(c: Context, content: RouteContent, parameter?: string): Promise<ResolvedContentRequest>;
54
+ /**
55
+ * Resolves the `/` Homepage request into what a Site should render.
56
+ *
57
+ * Shares every catch-all mechanic with `resolveRiebeckiteContentRequest` but
58
+ * has no wildcard parameter to guard. It additionally owns the configured-index
59
+ * redirect: when the `index` content is permalinked away from `/`, the root
60
+ * forwards there instead of resolving another route.
61
+ */
62
+ export declare function resolveRiebeckiteHomeRequest(c: Context, content: RouteContent): Promise<ResolvedContentRequest>;
21
63
  /** Returns catch-all parameters for every plugin page registered for SSG. */
22
64
  export declare function pluginPageSsgParams(content: Pick<ContentManager, "getPagePaths">, parameter?: string): Promise<Record<string, string>[]>;
23
65
  export declare function riebeckiteSsgParams(content: Pick<ContentManager, "getManifest" | "getOutputChangeSet" | "getPagePaths">, parameter?: string): Promise<Record<string, string>[]>;
@@ -0,0 +1,7 @@
1
+ import type { ResolvedRiebeckiteConfig } from "@riebeckite/core";
2
+ /**
3
+ * Applies the application root to a resolved Riebeckite configuration. The
4
+ * generated site bootstrap imports this through `@riebeckite/honox/runtime`
5
+ * so the resolved config and content runtime stay framework-owned.
6
+ */
7
+ export declare function resolveHonoxConfig(config: ResolvedRiebeckiteConfig, appRoot: string): ResolvedRiebeckiteConfig;
@@ -0,0 +1,11 @@
1
+ import type { Plugin } from "vite";
2
+ import type { ResolvedHonoxApplication } from "./vite_runner.js";
3
+ export declare const riebeckiteConfigModuleId = "virtual:riebeckite/config";
4
+ export declare const riebeckiteContentModuleId = "virtual:riebeckite/content";
5
+ /**
6
+ * Provides the framework-owned bootstrap modules a generated site imports
7
+ * instead of keeping resolved-config and content-manager ceremony in
8
+ * `app/config.ts` and `app/content.ts`.
9
+ */
10
+ export declare function riebeckiteRuntimeModules(getApplication: () => ResolvedHonoxApplication): Plugin;
11
+ export declare function isRiebeckiteRuntimeModuleId(id: string | null | undefined): boolean;
@@ -1,4 +1,4 @@
1
- import type { ContentBodySlot, ContentBodySlots } from "@riebeckite/core";
1
+ import type { ContentBodySlot, ContentBodySlots, PluginHeadTag, ResolvedRiebeckiteConfig } from "@riebeckite/core";
2
2
  export type PrimitiveChildren = unknown;
3
3
  export type PrimitiveProps = {
4
4
  children?: PrimitiveChildren;
@@ -12,12 +12,7 @@ export type PrimitiveClassProps = {
12
12
  export type ArticleProps = PrimitiveProps & {
13
13
  "data-slot"?: string;
14
14
  };
15
- export type ArticleLayoutProps = PrimitiveProps & {
16
- /**
17
- * @deprecated Render aside content as children instead.
18
- */
19
- aside?: PrimitiveChildren;
20
- };
15
+ export type ArticleLayoutProps = PrimitiveProps;
21
16
  export type ArticleHeaderProps = (PrimitiveClassProps & {
22
17
  dangerouslySetInnerHTML: {
23
18
  __html: string;
@@ -27,19 +22,10 @@ export type ArticleHeaderProps = (PrimitiveClassProps & {
27
22
  dangerouslySetInnerHTML?: never;
28
23
  children?: PrimitiveChildren;
29
24
  });
30
- export type ArticleContentProps = (PrimitiveClassProps & {
31
- /**
32
- * @deprecated Render Markdown HTML with `ArticleBody` inside an
33
- * `ArticleContent` shell instead.
34
- */
35
- html: string;
36
- children?: never;
37
- "data-slot"?: string;
38
- }) | (PrimitiveClassProps & {
39
- html?: never;
25
+ export type ArticleContentProps = PrimitiveClassProps & {
40
26
  children?: PrimitiveChildren;
41
27
  "data-slot"?: string;
42
- });
28
+ };
43
29
  export type ArticleMetaProps = PrimitiveProps & {
44
30
  label?: string;
45
31
  };
@@ -79,5 +65,61 @@ export declare function ContentSlot(props: ContentSlotProps): import("hono/jsx/j
79
65
  * Owns the `.rb-article-content` Stable Hook so Sites never hand-write it.
80
66
  */
81
67
  export declare function ArticleBody(props: ArticleBodyProps): import("hono/jsx/jsx-dev-runtime").JSX.Element;
68
+ export type PageBodyProps = PrimitiveClassProps & {
69
+ html: string;
70
+ };
71
+ /**
72
+ * Renders framework-resolved Plugin Page HTML.
73
+ *
74
+ * Owns the raw injection of a `PluginPageType` body so Sites never hand-write
75
+ * `dangerouslySetInnerHTML` for a resolved page.
76
+ */
77
+ export declare function PageBody(props: PageBodyProps): import("hono/jsx/jsx-dev-runtime").JSX.Element;
82
78
  export declare function ArticleFooter(props: ArticleFooterProps): import("hono/jsx/jsx-dev-runtime").JSX.Element;
83
79
  export declare function Sidebar(props: SidebarProps): import("hono/jsx/jsx-dev-runtime").JSX.Element;
80
+ export type ThemeRootProps = {
81
+ theme: ResolvedRiebeckiteConfig["theme"];
82
+ lang?: string;
83
+ children?: PrimitiveChildren;
84
+ };
85
+ /**
86
+ * Derives the `<html>` attributes from a resolved theme.
87
+ *
88
+ * Combines the theme's custom `data-*` attributes with the reserved
89
+ * color-mode, name, typography, and article-layout attributes.
90
+ */
91
+ export declare function themeRootAttributes(theme: ResolvedRiebeckiteConfig["theme"]): Record<string, string | undefined>;
92
+ /**
93
+ * Root `<html>` element carrying the resolved theme's attributes.
94
+ *
95
+ * The Site keeps ownership of the document composition by rendering its own
96
+ * `<head>` and `<body>` as children.
97
+ */
98
+ export declare function ThemeRoot(props: ThemeRootProps): import("hono/jsx/jsx-dev-runtime").JSX.Element;
99
+ export type PluginHeadTagsProps = {
100
+ tags?: readonly PluginHeadTag[];
101
+ };
102
+ /**
103
+ * Renders Plugin-provided head tags as `meta` / `link` / `script` elements.
104
+ *
105
+ * The Site owns placement; this primitive owns the conversion and ordering.
106
+ */
107
+ export declare function PluginHeadTags(props: PluginHeadTagsProps): import("hono/jsx/jsx-dev-runtime").JSX.Element;
108
+ export type RiebeckiteHeadProps = {
109
+ title: string;
110
+ headTags?: readonly PluginHeadTag[];
111
+ faviconHref?: string | null;
112
+ colorModeScript?: boolean;
113
+ stylesheets?: readonly string[];
114
+ clientSrc?: string | null;
115
+ prod?: boolean;
116
+ children?: PrimitiveChildren;
117
+ };
118
+ /**
119
+ * Renders the standard Riebeckite `<head>` contents.
120
+ *
121
+ * Owns the charset, viewport, default title, favicon, color-mode bootstrap,
122
+ * stylesheet and client entries, and Plugin head tags. The Site keeps the
123
+ * surrounding `<head>` so it can add its own elements next to this primitive.
124
+ */
125
+ export declare function RiebeckiteHead(props: RiebeckiteHeadProps): import("hono/jsx/jsx-dev-runtime").JSX.Element;
@@ -1,5 +1,8 @@
1
1
  // src/ui/primitives.tsx
2
- import { jsx, jsxs } from "hono/jsx/jsx-runtime";
2
+ import { ColorModeScript } from "@riebeckite/plugin-color-mode";
3
+ import { Link, Script } from "honox/server";
4
+ import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
5
+ import { createElement } from "hono/jsx";
3
6
  function Article(props) {
4
7
  return /* @__PURE__ */ jsx(
5
8
  "article",
@@ -11,14 +14,11 @@ function Article(props) {
11
14
  );
12
15
  }
13
16
  function ArticleLayout(props) {
14
- return /* @__PURE__ */ jsxs(
17
+ return /* @__PURE__ */ jsx(
15
18
  "div",
16
19
  {
17
20
  class: joinClassNames("rb-article-layout", props.class, props.className),
18
- children: [
19
- props.children,
20
- props.aside
21
- ]
21
+ children: props.children
22
22
  }
23
23
  );
24
24
  }
@@ -56,16 +56,6 @@ function ArticleContent(props) {
56
56
  props.className
57
57
  );
58
58
  const dataSlot = props["data-slot"] ?? "article-body";
59
- if (props.html !== void 0) {
60
- return /* @__PURE__ */ jsx(
61
- "div",
62
- {
63
- class: className,
64
- "data-slot": dataSlot,
65
- dangerouslySetInnerHTML: { __html: props.html }
66
- }
67
- );
68
- }
69
59
  return /* @__PURE__ */ jsx("div", { class: className, "data-slot": dataSlot, children: props.children });
70
60
  }
71
61
  function hasSlot(slots, name) {
@@ -94,6 +84,15 @@ function ArticleBody(props) {
94
84
  }
95
85
  );
96
86
  }
87
+ function PageBody(props) {
88
+ return /* @__PURE__ */ jsx(
89
+ "div",
90
+ {
91
+ class: joinClassNames("rb-page-body", props.class, props.className),
92
+ dangerouslySetInnerHTML: { __html: props.html }
93
+ }
94
+ );
95
+ }
97
96
  function ArticleFooter(props) {
98
97
  return /* @__PURE__ */ jsx(
99
98
  "footer",
@@ -113,6 +112,55 @@ function Sidebar(props) {
113
112
  }
114
113
  );
115
114
  }
115
+ function themeRootAttributes(theme) {
116
+ return {
117
+ ...theme.attributes,
118
+ "data-theme": theme.colorMode === "system" ? void 0 : theme.colorMode,
119
+ "data-theme-name": theme.name,
120
+ "data-typography": theme.typography,
121
+ "data-article-layout": theme.articleLayout
122
+ };
123
+ }
124
+ function ThemeRoot(props) {
125
+ return /* @__PURE__ */ jsx("html", { lang: props.lang, ...themeRootAttributes(props.theme), children: props.children });
126
+ }
127
+ function PluginHeadTags(props) {
128
+ return /* @__PURE__ */ jsx(Fragment, { children: props.tags?.map(renderPluginHeadTag) });
129
+ }
130
+ function renderPluginHeadTag(tag, index) {
131
+ const key = `${tag.tag}-${index}`;
132
+ if (tag.tag === "meta") {
133
+ return /* @__PURE__ */ createElement("meta", { ...tag.attrs, key });
134
+ }
135
+ if (tag.tag === "link") {
136
+ return /* @__PURE__ */ createElement("link", { ...tag.attrs, key });
137
+ }
138
+ return /* @__PURE__ */ createElement(
139
+ "script",
140
+ {
141
+ ...tag.attrs,
142
+ key,
143
+ dangerouslySetInnerHTML: tag.children ? { __html: tag.children } : void 0
144
+ }
145
+ );
146
+ }
147
+ function RiebeckiteHead(props) {
148
+ const faviconHref = props.faviconHref === void 0 ? "/favicon.ico" : props.faviconHref;
149
+ const stylesheets = props.stylesheets ?? ["/app/style.css"];
150
+ const clientSrc = props.clientSrc === void 0 ? "/app/client.ts" : props.clientSrc;
151
+ const colorModeScript = props.colorModeScript ?? true;
152
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
153
+ /* @__PURE__ */ jsx("meta", { charset: "utf-8" }),
154
+ /* @__PURE__ */ jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0" }),
155
+ /* @__PURE__ */ jsx("title", { children: props.title }),
156
+ faviconHref ? /* @__PURE__ */ jsx("link", { rel: "icon", href: faviconHref }) : null,
157
+ colorModeScript ? /* @__PURE__ */ jsx(ColorModeScript, {}) : null,
158
+ stylesheets.map((href) => /* @__PURE__ */ jsx(Link, { href, rel: "stylesheet", prod: props.prod }, href)),
159
+ /* @__PURE__ */ jsx(PluginHeadTags, { tags: props.headTags }),
160
+ clientSrc ? /* @__PURE__ */ jsx(Script, { src: clientSrc, async: true, prod: props.prod }) : null,
161
+ props.children
162
+ ] });
163
+ }
116
164
  function joinClassNames(...classNames) {
117
165
  return classNames.filter(Boolean).join(" ");
118
166
  }
@@ -125,6 +173,11 @@ export {
125
173
  ArticleLayout,
126
174
  ArticleMeta,
127
175
  ContentSlot,
176
+ PageBody,
177
+ PluginHeadTags,
178
+ RiebeckiteHead,
128
179
  Sidebar,
129
- hasSlot
180
+ ThemeRoot,
181
+ hasSlot,
182
+ themeRootAttributes
130
183
  };
@@ -7,13 +7,15 @@ export type HonoxApplicationOptions = {
7
7
  export type ResolvedHonoxApplication = {
8
8
  config: ResolvedRiebeckiteConfig;
9
9
  configRoot: string;
10
+ configFile: string;
10
11
  appRoot: string;
11
12
  contentRoot: string;
12
13
  };
13
14
  export type ResolveHonoxApplicationOptions = RiebeckiteConfigLoaderOptions & {
14
15
  appRoot?: string;
16
+ startDirectory?: string;
15
17
  };
16
- export declare function resolveHonoxApplicationRoot(configRoot: string): Promise<string>;
18
+ export declare function resolveHonoxApplicationRoot(configRoot: string, startDirectory?: string): Promise<string>;
17
19
  export declare class HonoxApplicationRootError extends Error {
18
20
  constructor(message: string);
19
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riebeckite/honox",
3
- "version": "0.0.19",
3
+ "version": "0.0.21",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,23 +18,32 @@
18
18
  "source": "./server.ts",
19
19
  "default": "./dist/server.js"
20
20
  },
21
+ "./runtime": {
22
+ "types": "./dist/runtime.d.ts",
23
+ "import": "./dist/runtime.js",
24
+ "source": "./runtime.ts",
25
+ "default": "./dist/runtime.js"
26
+ },
21
27
  "./ui": {
22
28
  "types": "./dist/src/ui/primitives.d.ts",
23
29
  "import": "./dist/src/ui/primitives.js",
24
30
  "source": "./src/ui/primitives.tsx",
25
31
  "default": "./dist/src/ui/primitives.js"
26
- }
32
+ },
33
+ "./style.css": "./style.css"
27
34
  },
28
35
  "dependencies": {
29
36
  "@babel/parser": "^7.29.3",
30
37
  "esbuild": "^0.28.2",
31
38
  "hono": "^4.12.25",
39
+ "honox": "0.1.56",
32
40
  "vite": "^8.0.9",
33
- "@riebeckite/core": "0.0.19"
41
+ "@riebeckite/core": "0.0.21",
42
+ "@riebeckite/plugin-color-mode": "0.0.21"
34
43
  },
35
44
  "devDependencies": {
36
45
  "tsx": "^4.22.4",
37
- "@riebeckite/test": "0.0.19"
46
+ "@riebeckite/test": "0.0.21"
38
47
  },
39
48
  "publishConfig": {
40
49
  "access": "public"
@@ -43,7 +52,8 @@
43
52
  "LICENSE",
44
53
  "README.md",
45
54
  "README_ja.md",
46
- "dist"
55
+ "dist",
56
+ "style.css"
47
57
  ],
48
58
  "repository": {
49
59
  "type": "git",
@@ -64,7 +74,9 @@
64
74
  "engines": {
65
75
  "node": "^20.19.0 || >=22.12.0"
66
76
  },
67
- "sideEffects": false,
77
+ "sideEffects": [
78
+ "./style.css"
79
+ ],
68
80
  "scripts": {
69
81
  "build": "node ../../../scripts/build_package.mjs",
70
82
  "test": "node --import tsx --test \"test/*.test.ts\""
package/style.css ADDED
@@ -0,0 +1,68 @@
1
+ @layer components {
2
+ .rb-article-layout {
3
+ display: grid;
4
+ grid-template-columns: minmax(0, var(--rb-layout-article-max, 48rem));
5
+ justify-content: center;
6
+ column-gap: var(--rb-layout-gap, 2rem);
7
+ }
8
+
9
+ .rb-article-body {
10
+ min-width: 0;
11
+ }
12
+
13
+ .rb-page-body {
14
+ width: min(100% - 2rem, var(--rb-layout-page-max, 80rem));
15
+ min-width: 0;
16
+ margin-block: var(--rb-space-4, 2rem);
17
+ padding-inline: var(--rb-space-2, 1rem);
18
+ }
19
+
20
+ .rb-article-content :is(img, picture > img, figure img) {
21
+ display: block;
22
+ max-inline-size: 100%;
23
+ block-size: auto;
24
+ }
25
+
26
+ :is(:root, .rb-theme-root)[data-article-layout="full-width"]
27
+ .rb-article-layout {
28
+ grid-template-columns: minmax(0, 1fr);
29
+ }
30
+
31
+ @media (min-width: 88rem) {
32
+ :is(:root, .rb-theme-root)[data-article-layout="sidebar"]
33
+ .rb-article-layout {
34
+ grid-template-columns:
35
+ minmax(0, var(--rb-layout-article-max, 48rem))
36
+ var(--rb-layout-sidebar, 14rem);
37
+ }
38
+
39
+ :is(:root, .rb-theme-root)[data-article-layout="article"]
40
+ .rb-article-layout {
41
+ position: relative;
42
+ }
43
+
44
+ :is(:root, .rb-theme-root)[data-article-layout="article"] .rb-article-body,
45
+ :is(:root, .rb-theme-root)[data-article-layout="sidebar"] .rb-article-body {
46
+ grid-column: 1;
47
+ grid-row: 1;
48
+ }
49
+ }
50
+
51
+ .rb-skip-link {
52
+ position: absolute;
53
+ top: 0;
54
+ left: 0;
55
+ z-index: 100;
56
+ padding: var(--rb-space-2, 1rem) var(--rb-space-3, 1.5rem);
57
+ border: var(--rb-rule-width, 1px) solid var(--rb-color-border-strong);
58
+ border-radius: var(--rb-default-radius-sm, 0.25rem);
59
+ background: var(--rb-color-surface);
60
+ color: var(--rb-color-ink);
61
+ text-decoration: none;
62
+ transform: translateY(-200%);
63
+ }
64
+
65
+ .rb-skip-link:focus {
66
+ transform: translateY(0);
67
+ }
68
+ }