@riebeckite/honox 0.0.14 → 0.0.16
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 +5 -4
- package/README_ja.md +5 -4
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1626 -182
- package/dist/server.d.ts +1 -1
- package/dist/server.js +30 -3
- package/dist/src/content_assets.d.ts +19 -0
- package/dist/src/content_route.d.ts +1 -0
- package/dist/src/content_watch.d.ts +11 -0
- package/dist/src/scaffold/app-templates.d.ts +1 -1
- package/dist/src/scaffold/deployment.d.ts +1 -0
- package/dist/src/scaffold/index.d.ts +1 -0
- package/dist/src/scaffold/next-steps.d.ts +7 -1
- package/dist/src/scaffold/templates.d.ts +4 -1
- package/dist/src/scaffold/version.d.ts +1 -1
- package/dist/src/scaffold/wrangler-defaults.d.ts +59 -0
- package/dist/src/ssg_plugin.d.ts +16 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -254,12 +254,51 @@ function isJavaScriptModule(filePath) {
|
|
|
254
254
|
return /\.[cm]?[jt]sx?$/.test(filePath);
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
-
// src/scaffold/
|
|
258
|
-
|
|
259
|
-
|
|
257
|
+
// src/scaffold/wrangler-defaults.ts
|
|
258
|
+
var WRANGLER_DEFAULTS = {
|
|
259
|
+
/** JSON schema for IDE support and validation */
|
|
260
|
+
$schema: "node_modules/wrangler/config-schema.json",
|
|
261
|
+
/** Worker name - becomes <name>.<account>.workers.dev */
|
|
262
|
+
name: "riebeckite-site",
|
|
263
|
+
/** Compatibility date - update when Workers runtime changes require it */
|
|
264
|
+
compatibility_date: "2026-06-09",
|
|
265
|
+
/** Required for Node.js APIs used by Riebeckite */
|
|
266
|
+
compatibility_flags: ["nodejs_compat"],
|
|
267
|
+
/** Static assets directory - Riebeckite builds to ./dist */
|
|
268
|
+
assets: { directory: "./dist" }
|
|
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
|
+
}
|
|
282
|
+
var GITHUB_ACTIONS_SECRETS = {
|
|
283
|
+
/** Cloudflare API token with Workers Scripts: Edit permission */
|
|
284
|
+
CLOUDFLARE_API_TOKEN: "CLOUDFLARE_API_TOKEN",
|
|
285
|
+
/** Cloudflare Account ID (found in Workers & Pages dashboard URL) */
|
|
286
|
+
CLOUDFLARE_ACCOUNT_ID: "CLOUDFLARE_ACCOUNT_ID",
|
|
287
|
+
/** Optional: Token for content repo to dispatch to site repo */
|
|
288
|
+
SITE_DISPATCH_TOKEN: "SITE_DISPATCH_TOKEN",
|
|
289
|
+
/** Optional: Token for site repo to read content repo */
|
|
290
|
+
RIEBECKITE_CONTENT_READ_TOKEN: "RIEBECKITE_CONTENT_READ_TOKEN"
|
|
291
|
+
};
|
|
292
|
+
var PACKAGE_MANAGER = "npm";
|
|
260
293
|
|
|
261
294
|
// src/scaffold/deployment.ts
|
|
262
295
|
var GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
296
|
+
var BUILD_STATE_CACHE_KEY_PREFIX = "riebeckite-build-v1";
|
|
297
|
+
var LEGACY_BUILD_CACHE_KEY_PREFIX = "riebeckite-content-v3";
|
|
298
|
+
var BUILD_STATE_CACHE_PATHS = [
|
|
299
|
+
".riebeckite/cache",
|
|
300
|
+
".riebeckite/build/content-state.json"
|
|
301
|
+
];
|
|
263
302
|
function assertGitHubRepository(value, option) {
|
|
264
303
|
if (!GITHUB_REPOSITORY_PATTERN.test(value)) {
|
|
265
304
|
throw new Error(
|
|
@@ -294,18 +333,14 @@ function deploymentTemplateFiles(options) {
|
|
|
294
333
|
] : []
|
|
295
334
|
];
|
|
296
335
|
}
|
|
336
|
+
function wranglerJsoncFile(workerName) {
|
|
337
|
+
return {
|
|
338
|
+
path: "wrangler.jsonc",
|
|
339
|
+
content: buildDefaultWranglerConfig(workerName)
|
|
340
|
+
};
|
|
341
|
+
}
|
|
297
342
|
function wranglerConfig() {
|
|
298
|
-
return
|
|
299
|
-
{
|
|
300
|
-
name: "riebeckite-site",
|
|
301
|
-
compatibility_date: "2026-03-10",
|
|
302
|
-
compatibility_flags: ["nodejs_compat"],
|
|
303
|
-
assets: { directory: "./dist" }
|
|
304
|
-
},
|
|
305
|
-
null,
|
|
306
|
-
2
|
|
307
|
-
)}
|
|
308
|
-
`;
|
|
343
|
+
return buildDefaultWranglerConfig(WRANGLER_DEFAULTS.name);
|
|
309
344
|
}
|
|
310
345
|
function deployWorkflow(contentRepository) {
|
|
311
346
|
const contentCheckout = contentRepository === void 0 ? "" : `
|
|
@@ -347,6 +382,16 @@ ${contentCheckout}
|
|
|
347
382
|
- name: Install dependencies
|
|
348
383
|
run: npm ci
|
|
349
384
|
|
|
385
|
+
- name: Restore Riebeckite build state
|
|
386
|
+
uses: actions/cache@v4
|
|
387
|
+
with:
|
|
388
|
+
path: |
|
|
389
|
+
${BUILD_STATE_CACHE_PATHS.map((entry) => ` ${entry}`).join("\n")}
|
|
390
|
+
key: ${BUILD_STATE_CACHE_KEY_PREFIX}-\${{ runner.os }}-\${{ hashFiles('package-lock.json') }}-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
391
|
+
restore-keys: |
|
|
392
|
+
${BUILD_STATE_CACHE_KEY_PREFIX}-\${{ runner.os }}-\${{ hashFiles('package-lock.json') }}-
|
|
393
|
+
${LEGACY_BUILD_CACHE_KEY_PREFIX}-\${{ runner.os }}-\${{ hashFiles('package-lock.json') }}
|
|
394
|
+
|
|
350
395
|
- name: Validate configuration and plugins
|
|
351
396
|
run: npm exec riebeckite check
|
|
352
397
|
|
|
@@ -356,8 +401,8 @@ ${contentCheckout}
|
|
|
356
401
|
- name: Deploy the built assets
|
|
357
402
|
uses: cloudflare/wrangler-action@v3
|
|
358
403
|
with:
|
|
359
|
-
apiToken: \${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
360
|
-
accountId: \${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
|
404
|
+
apiToken: \${{ secrets.${GITHUB_ACTIONS_SECRETS.CLOUDFLARE_API_TOKEN} }}
|
|
405
|
+
accountId: \${{ secrets.${GITHUB_ACTIONS_SECRETS.CLOUDFLARE_ACCOUNT_ID} }}
|
|
361
406
|
command: deploy
|
|
362
407
|
`;
|
|
363
408
|
}
|
|
@@ -377,7 +422,7 @@ jobs:
|
|
|
377
422
|
steps:
|
|
378
423
|
- name: Validate dispatch configuration
|
|
379
424
|
env:
|
|
380
|
-
SITE_DISPATCH_TOKEN: \${{ secrets.SITE_DISPATCH_TOKEN }}
|
|
425
|
+
SITE_DISPATCH_TOKEN: \${{ secrets.${GITHUB_ACTIONS_SECRETS.SITE_DISPATCH_TOKEN} }}
|
|
381
426
|
run: |
|
|
382
427
|
if [ -z "$SITE_DISPATCH_TOKEN" ]; then
|
|
383
428
|
echo "::error::SITE_DISPATCH_TOKEN is not configured. Add a token that can dispatch to ${siteRepository}."
|
|
@@ -387,7 +432,7 @@ jobs:
|
|
|
387
432
|
- name: Notify the site repository
|
|
388
433
|
uses: actions/github-script@v7
|
|
389
434
|
with:
|
|
390
|
-
github-token: \${{ secrets.SITE_DISPATCH_TOKEN }}
|
|
435
|
+
github-token: \${{ secrets.${GITHUB_ACTIONS_SECRETS.SITE_DISPATCH_TOKEN} }}
|
|
391
436
|
script: |
|
|
392
437
|
await github.rest.repos.createDispatchEvent({
|
|
393
438
|
owner: ${JSON.stringify(owner)},
|
|
@@ -397,6 +442,10 @@ jobs:
|
|
|
397
442
|
`;
|
|
398
443
|
}
|
|
399
444
|
|
|
445
|
+
// src/scaffold/index.ts
|
|
446
|
+
import fs8 from "node:fs/promises";
|
|
447
|
+
import path9 from "node:path";
|
|
448
|
+
|
|
400
449
|
// src/scaffold/presets.ts
|
|
401
450
|
var SCAFFOLD_LANGUAGES = [
|
|
402
451
|
"en",
|
|
@@ -924,10 +973,10 @@ function resolveScaffoldPreset(value) {
|
|
|
924
973
|
}
|
|
925
974
|
|
|
926
975
|
// src/scaffold/templates.ts
|
|
927
|
-
import
|
|
976
|
+
import fs7 from "node:fs";
|
|
928
977
|
|
|
929
978
|
// src/vite_plugin.ts
|
|
930
|
-
import
|
|
979
|
+
import path8 from "node:path";
|
|
931
980
|
|
|
932
981
|
// src/asset_entries.ts
|
|
933
982
|
import fs4 from "node:fs";
|
|
@@ -1037,11 +1086,288 @@ ${initializers.map((initializer) => ` ${initializer};`).join("\n")}
|
|
|
1037
1086
|
`;
|
|
1038
1087
|
}
|
|
1039
1088
|
|
|
1089
|
+
// src/content_assets.ts
|
|
1090
|
+
import fs5 from "node:fs/promises";
|
|
1091
|
+
import path6 from "node:path";
|
|
1092
|
+
import {
|
|
1093
|
+
isIgnoredContentPath as isIgnoredContentPath2,
|
|
1094
|
+
isImagePath as isImagePath2,
|
|
1095
|
+
normalizeContentPath as normalizeContentPath2
|
|
1096
|
+
} from "@riebeckite/core";
|
|
1097
|
+
|
|
1098
|
+
// src/content_watch.ts
|
|
1099
|
+
import path5 from "node:path";
|
|
1100
|
+
import {
|
|
1101
|
+
isAttachmentPath,
|
|
1102
|
+
isIgnoredContentPath,
|
|
1103
|
+
isImagePath,
|
|
1104
|
+
normalizeContentPath
|
|
1105
|
+
} from "@riebeckite/core";
|
|
1106
|
+
var CONTENT_RELOAD_DEBOUNCE_MS = 60;
|
|
1107
|
+
var configuredWatchers = /* @__PURE__ */ new WeakSet();
|
|
1108
|
+
function riebeckiteContentWatch(options) {
|
|
1109
|
+
return {
|
|
1110
|
+
name: "riebeckite-content-watch",
|
|
1111
|
+
apply: "serve",
|
|
1112
|
+
enforce: "post",
|
|
1113
|
+
configureServer(server2) {
|
|
1114
|
+
if (configuredWatchers.has(server2.watcher)) return;
|
|
1115
|
+
configuredWatchers.add(server2.watcher);
|
|
1116
|
+
const appRoot = options.appRoot();
|
|
1117
|
+
const contentRoot = options.contentRoot();
|
|
1118
|
+
const exclude = options.exclude();
|
|
1119
|
+
const isContentFile = (file) => isPathInsideDirectory(contentRoot, file);
|
|
1120
|
+
server2.watcher.add(contentRoot);
|
|
1121
|
+
suppressRestartOnContentEvents(server2, isContentFile);
|
|
1122
|
+
let timer;
|
|
1123
|
+
const invalidate = () => {
|
|
1124
|
+
if (timer) clearTimeout(timer);
|
|
1125
|
+
timer = setTimeout(() => {
|
|
1126
|
+
timer = void 0;
|
|
1127
|
+
invalidateApplicationRuntime(server2, appRoot);
|
|
1128
|
+
server2.hot.send({ type: "full-reload" });
|
|
1129
|
+
}, CONTENT_RELOAD_DEBOUNCE_MS);
|
|
1130
|
+
};
|
|
1131
|
+
server2.httpServer?.once("close", () => {
|
|
1132
|
+
if (timer) clearTimeout(timer);
|
|
1133
|
+
});
|
|
1134
|
+
const handle = (event) => (file) => {
|
|
1135
|
+
if (!isContentFile(file)) return;
|
|
1136
|
+
const logicalPath = toLogicalPath(contentRoot, file);
|
|
1137
|
+
if (isIgnoredContentPath(logicalPath, exclude)) return;
|
|
1138
|
+
if (event === "change" && isSiteAssetPath(logicalPath)) {
|
|
1139
|
+
server2.hot.send({ type: "full-reload" });
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
invalidate();
|
|
1143
|
+
};
|
|
1144
|
+
server2.watcher.on("add", handle("add"));
|
|
1145
|
+
server2.watcher.on("change", handle("change"));
|
|
1146
|
+
server2.watcher.on("unlink", handle("unlink"));
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
function isSiteAssetPath(logicalPath) {
|
|
1151
|
+
return isImagePath(logicalPath) || isAttachmentPath(logicalPath);
|
|
1152
|
+
}
|
|
1153
|
+
function isPathInsideDirectory(directory, file) {
|
|
1154
|
+
const root = normalizeForCompare(directory).replace(/\/+$/, "");
|
|
1155
|
+
if (!root) return false;
|
|
1156
|
+
const target = normalizeForCompare(file);
|
|
1157
|
+
return target === root || target.startsWith(`${root}/`);
|
|
1158
|
+
}
|
|
1159
|
+
function toLogicalPath(directory, file) {
|
|
1160
|
+
return normalizeContentPath(
|
|
1161
|
+
path5.relative(normalizeContentPath(directory), normalizeContentPath(file))
|
|
1162
|
+
);
|
|
1163
|
+
}
|
|
1164
|
+
function invalidateApplicationRuntime(server2, appRoot) {
|
|
1165
|
+
const moduleGraph = server2.environments?.ssr?.moduleGraph;
|
|
1166
|
+
if (!moduleGraph) return 0;
|
|
1167
|
+
const appDirectory = path5.join(appRoot, "app") + path5.sep;
|
|
1168
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1169
|
+
const timestamp = Date.now();
|
|
1170
|
+
let invalidated = 0;
|
|
1171
|
+
for (const mod of moduleGraph.idToModuleMap.values()) {
|
|
1172
|
+
const file = mod.file;
|
|
1173
|
+
if (!file || !isPathInsideDirectory(appDirectory, file)) continue;
|
|
1174
|
+
moduleGraph.invalidateModule(mod, seen, timestamp);
|
|
1175
|
+
invalidated += 1;
|
|
1176
|
+
}
|
|
1177
|
+
return invalidated;
|
|
1178
|
+
}
|
|
1179
|
+
function suppressRestartOnContentEvents(server2, isContentFile) {
|
|
1180
|
+
for (const event of ["add", "unlink"]) {
|
|
1181
|
+
const listeners = server2.watcher.rawListeners(event).slice();
|
|
1182
|
+
server2.watcher.removeAllListeners(event);
|
|
1183
|
+
for (const listener of listeners) {
|
|
1184
|
+
server2.watcher.on(event, (file) => {
|
|
1185
|
+
if (isContentFile(file)) return;
|
|
1186
|
+
listener(file);
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
function normalizeForCompare(value) {
|
|
1192
|
+
const normalized = normalizeContentPath(value);
|
|
1193
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
// src/content_assets.ts
|
|
1197
|
+
var IMAGE_CONTENT_TYPES = {
|
|
1198
|
+
png: "image/png",
|
|
1199
|
+
jpg: "image/jpeg",
|
|
1200
|
+
jpeg: "image/jpeg",
|
|
1201
|
+
gif: "image/gif",
|
|
1202
|
+
svg: "image/svg+xml",
|
|
1203
|
+
webp: "image/webp",
|
|
1204
|
+
bmp: "image/bmp"
|
|
1205
|
+
};
|
|
1206
|
+
function riebeckiteContentAssets(options) {
|
|
1207
|
+
return {
|
|
1208
|
+
name: "riebeckite-content-assets",
|
|
1209
|
+
apply: "serve",
|
|
1210
|
+
enforce: "pre",
|
|
1211
|
+
configureServer(server2) {
|
|
1212
|
+
const contentRoot = options.contentRoot();
|
|
1213
|
+
const entry = path6.join(options.appRoot(), "app", "server.ts");
|
|
1214
|
+
const base = server2.config.base;
|
|
1215
|
+
const publicDir = server2.config.publicDir;
|
|
1216
|
+
let cachedManifest;
|
|
1217
|
+
let cachedPaths = /* @__PURE__ */ new Set();
|
|
1218
|
+
const resolvePublicImagePaths = async () => {
|
|
1219
|
+
const module = await server2.ssrLoadModule(entry);
|
|
1220
|
+
const manifest = await module.content?.getManifest();
|
|
1221
|
+
if (!manifest) return /* @__PURE__ */ new Set();
|
|
1222
|
+
if (manifest !== cachedManifest) {
|
|
1223
|
+
cachedManifest = manifest;
|
|
1224
|
+
cachedPaths = collectPublicImagePaths(manifest);
|
|
1225
|
+
}
|
|
1226
|
+
return cachedPaths;
|
|
1227
|
+
};
|
|
1228
|
+
const middleware = async (request, response, next) => {
|
|
1229
|
+
try {
|
|
1230
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
1231
|
+
next();
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
const logicalPath = toContentPath(request.url, base);
|
|
1235
|
+
if (!logicalPath || !isImagePath2(logicalPath)) {
|
|
1236
|
+
next();
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
if (isIgnoredContentPath2(logicalPath)) {
|
|
1240
|
+
next();
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
const contentType = imageContentType(logicalPath);
|
|
1244
|
+
if (!contentType) {
|
|
1245
|
+
next();
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
const available = await resolvePublicImagePaths();
|
|
1249
|
+
if (!available.has(logicalPath)) {
|
|
1250
|
+
next();
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
const file = path6.join(contentRoot, logicalPath);
|
|
1254
|
+
if (!isPathInsideDirectory(contentRoot, file)) {
|
|
1255
|
+
next();
|
|
1256
|
+
return;
|
|
1257
|
+
}
|
|
1258
|
+
if (await hasSiteOwnedFile(publicDir, logicalPath)) {
|
|
1259
|
+
next();
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
const content = await fs5.readFile(file);
|
|
1263
|
+
response.statusCode = 200;
|
|
1264
|
+
response.setHeader("Content-Type", contentType);
|
|
1265
|
+
response.setHeader("Content-Length", String(content.byteLength));
|
|
1266
|
+
response.setHeader("Cache-Control", "no-cache");
|
|
1267
|
+
if (request.method === "HEAD") {
|
|
1268
|
+
response.end();
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
response.end(content);
|
|
1272
|
+
return;
|
|
1273
|
+
} catch {
|
|
1274
|
+
next();
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1277
|
+
server2.middlewares.use((request, response, next) => {
|
|
1278
|
+
void middleware(
|
|
1279
|
+
request,
|
|
1280
|
+
response,
|
|
1281
|
+
next
|
|
1282
|
+
);
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
function collectPublicImagePaths(manifest) {
|
|
1288
|
+
const paths2 = /* @__PURE__ */ new Set();
|
|
1289
|
+
for (const entry of manifest.publicEntries ?? []) {
|
|
1290
|
+
for (const asset of entry.assets ?? []) {
|
|
1291
|
+
const normalized = normalizeContentPath2(asset.path);
|
|
1292
|
+
if (isImagePath2(normalized)) paths2.add(normalized);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
return paths2;
|
|
1296
|
+
}
|
|
1297
|
+
async function collectSiteOwnedOutputPaths(publicDir) {
|
|
1298
|
+
const paths2 = /* @__PURE__ */ new Set();
|
|
1299
|
+
if (!publicDir) return paths2;
|
|
1300
|
+
const root = path6.resolve(publicDir);
|
|
1301
|
+
const walk = async (directory) => {
|
|
1302
|
+
const entries = await fs5.readdir(directory, { withFileTypes: true }).catch(() => void 0);
|
|
1303
|
+
if (!entries) return;
|
|
1304
|
+
for (const entry of entries) {
|
|
1305
|
+
const full = path6.join(directory, entry.name);
|
|
1306
|
+
if (entry.isDirectory()) {
|
|
1307
|
+
await walk(full);
|
|
1308
|
+
} else {
|
|
1309
|
+
paths2.add(path6.relative(root, full).replaceAll("\\", "/"));
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
await walk(root);
|
|
1314
|
+
return paths2;
|
|
1315
|
+
}
|
|
1316
|
+
function toContentPath(url, base) {
|
|
1317
|
+
if (!url) return void 0;
|
|
1318
|
+
const withoutQuery = url.split("?")[0]?.split("#")[0];
|
|
1319
|
+
if (!withoutQuery) return void 0;
|
|
1320
|
+
let decoded = withoutQuery;
|
|
1321
|
+
try {
|
|
1322
|
+
decoded = decodeURIComponent(withoutQuery);
|
|
1323
|
+
} catch {
|
|
1324
|
+
return void 0;
|
|
1325
|
+
}
|
|
1326
|
+
let pathname = normalizeContentPath2(decoded);
|
|
1327
|
+
if (base && base !== "/" && pathname.startsWith(base)) {
|
|
1328
|
+
pathname = normalizeContentPath2(pathname.slice(base.length));
|
|
1329
|
+
}
|
|
1330
|
+
const logicalPath = pathname.replace(/^\/+/, "");
|
|
1331
|
+
if (!logicalPath) return void 0;
|
|
1332
|
+
if (logicalPath.split("/").some((segment) => segment === "" || segment === "." || segment === "..")) {
|
|
1333
|
+
return void 0;
|
|
1334
|
+
}
|
|
1335
|
+
return logicalPath;
|
|
1336
|
+
}
|
|
1337
|
+
function imageContentType(logicalPath) {
|
|
1338
|
+
const extension = logicalPath.split("/").at(-1)?.split(".").at(-1);
|
|
1339
|
+
if (!extension) return void 0;
|
|
1340
|
+
return IMAGE_CONTENT_TYPES[extension.toLowerCase()];
|
|
1341
|
+
}
|
|
1342
|
+
async function hasSiteOwnedFile(publicDir, logicalPath) {
|
|
1343
|
+
if (!publicDir) return false;
|
|
1344
|
+
const file = path6.join(publicDir, logicalPath);
|
|
1345
|
+
try {
|
|
1346
|
+
return (await fs5.stat(file)).isFile();
|
|
1347
|
+
} catch {
|
|
1348
|
+
return false;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1040
1352
|
// src/ssg_plugin.ts
|
|
1041
|
-
import {
|
|
1353
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
1354
|
+
import {
|
|
1355
|
+
mkdir,
|
|
1356
|
+
readdir,
|
|
1357
|
+
readFile,
|
|
1358
|
+
rename as rename2,
|
|
1359
|
+
rm,
|
|
1360
|
+
stat,
|
|
1361
|
+
writeFile
|
|
1362
|
+
} from "node:fs/promises";
|
|
1363
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
1364
|
+
import { performance } from "node:perf_hooks";
|
|
1042
1365
|
import { defaultExtensionMap, toSSG } from "hono/ssg";
|
|
1043
|
-
import {
|
|
1366
|
+
import {
|
|
1367
|
+
createServer
|
|
1368
|
+
} from "vite";
|
|
1044
1369
|
var defaultEntry = "./src/index.tsx";
|
|
1370
|
+
var cloudflareWorkerAssetLimit = 25 * 1024 * 1024;
|
|
1045
1371
|
function riebeckiteSsg(options = {}) {
|
|
1046
1372
|
const virtualId = "virtual:riebeckite-ssg-void-entry";
|
|
1047
1373
|
const resolvedVirtualId = `\0${virtualId}`;
|
|
@@ -1049,7 +1375,7 @@ function riebeckiteSsg(options = {}) {
|
|
|
1049
1375
|
let resolvedConfig;
|
|
1050
1376
|
return {
|
|
1051
1377
|
name: "riebeckite-ssg",
|
|
1052
|
-
apply:
|
|
1378
|
+
apply: shouldApplyRiebeckiteSsg,
|
|
1053
1379
|
enforce: "post",
|
|
1054
1380
|
config() {
|
|
1055
1381
|
return {
|
|
@@ -1076,53 +1402,224 @@ function riebeckiteSsg(options = {}) {
|
|
|
1076
1402
|
"Riebeckite SSG could not resolve the Vite configuration."
|
|
1077
1403
|
);
|
|
1078
1404
|
}
|
|
1405
|
+
const timingsMs = {};
|
|
1079
1406
|
removeVirtualEntryChunk(bundle, resolvedVirtualId);
|
|
1080
|
-
const server2 = await
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1407
|
+
const server2 = await measure(
|
|
1408
|
+
timingsMs,
|
|
1409
|
+
"ssgSetup",
|
|
1410
|
+
() => createServer({
|
|
1411
|
+
root: config.root,
|
|
1412
|
+
define: config.define,
|
|
1413
|
+
resolve: {
|
|
1414
|
+
...config.resolve,
|
|
1415
|
+
builtins: [...config.resolve.builtins, /^node:/]
|
|
1416
|
+
},
|
|
1417
|
+
plugins: [],
|
|
1418
|
+
build: { ssr: true },
|
|
1419
|
+
mode: config.mode,
|
|
1420
|
+
server: { watch: null }
|
|
1421
|
+
})
|
|
1422
|
+
);
|
|
1091
1423
|
try {
|
|
1092
|
-
const module = await
|
|
1424
|
+
const module = await measure(
|
|
1425
|
+
timingsMs,
|
|
1426
|
+
"ssrModuleLoad",
|
|
1427
|
+
() => server2.ssrLoadModule(entry)
|
|
1428
|
+
);
|
|
1093
1429
|
const app = module.default;
|
|
1094
1430
|
if (!app) {
|
|
1095
1431
|
throw new Error(`Failed to find a default export from ${entry}.`);
|
|
1096
1432
|
}
|
|
1433
|
+
const outputChangeSet = await measure(
|
|
1434
|
+
timingsMs,
|
|
1435
|
+
"outputDependencyResolution",
|
|
1436
|
+
async () => module.content?.getOutputChangeSet()
|
|
1437
|
+
);
|
|
1438
|
+
const appFingerprint = await measure(
|
|
1439
|
+
timingsMs,
|
|
1440
|
+
"appFingerprint",
|
|
1441
|
+
() => computeAppFingerprint(config.root, config.configFile)
|
|
1442
|
+
);
|
|
1443
|
+
const outputCachePath = join(
|
|
1444
|
+
config.root,
|
|
1445
|
+
".riebeckite",
|
|
1446
|
+
"ssg-output-cache.json"
|
|
1447
|
+
);
|
|
1448
|
+
const previousOutputCache = await loadOutputCache(
|
|
1449
|
+
outputCachePath,
|
|
1450
|
+
timingsMs,
|
|
1451
|
+
(message) => this.warn(message)
|
|
1452
|
+
);
|
|
1453
|
+
const siteOwnedOutputPaths = await measure(
|
|
1454
|
+
timingsMs,
|
|
1455
|
+
"siteOwnedOutputs",
|
|
1456
|
+
() => collectSiteOwnedOutputPaths(config.publicDir)
|
|
1457
|
+
);
|
|
1458
|
+
const siteOutputCollisions = findBuildOutputSiteCollisions(
|
|
1459
|
+
Object.keys(bundle),
|
|
1460
|
+
siteOwnedOutputPaths
|
|
1461
|
+
);
|
|
1462
|
+
if (siteOutputCollisions.length > 0) {
|
|
1463
|
+
this.warn(
|
|
1464
|
+
`Build outputs take precedence over site public assets: ${siteOutputCollisions.join(", ")}`
|
|
1465
|
+
);
|
|
1466
|
+
}
|
|
1467
|
+
const canUseIncremental = outputChangeSet && !outputChangeSet.fullRegenerationRequired && previousOutputCache && previousOutputCache.appFingerprint === appFingerprint && outputChangeSet.unchanged.every(
|
|
1468
|
+
(output) => siteOwnedOutputPaths.has(output.path) || previousOutputCache.outputs[output.path]
|
|
1469
|
+
);
|
|
1470
|
+
const reusablePaths = new Set(
|
|
1471
|
+
canUseIncremental ? outputChangeSet.unchanged.map((output) => output.path) : []
|
|
1472
|
+
);
|
|
1473
|
+
const shadowedOutputs = /* @__PURE__ */ new Set();
|
|
1474
|
+
const skippedRouteCount = canUseIncremental ? outputChangeSet.unchanged.filter(
|
|
1475
|
+
(output) => output.kind !== "generated"
|
|
1476
|
+
).length : 0;
|
|
1097
1477
|
const generatedHtml = [];
|
|
1098
|
-
const
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1478
|
+
const nextOutputCache = {
|
|
1479
|
+
version: 1,
|
|
1480
|
+
appFingerprint,
|
|
1481
|
+
outputs: {}
|
|
1482
|
+
};
|
|
1483
|
+
const metrics = {
|
|
1484
|
+
candidateOutputCount: outputChangeSet?.candidateOutputCount ?? 0,
|
|
1485
|
+
affectedOutputCount: outputChangeSet?.affectedOutputCount ?? 0,
|
|
1486
|
+
removedOutputCount: outputChangeSet?.removedOutputCount ?? 0,
|
|
1487
|
+
unchangedOutputCount: outputChangeSet?.unchangedOutputCount ?? 0,
|
|
1488
|
+
renderedOutputCount: 0,
|
|
1489
|
+
emittedOutputCount: 0,
|
|
1490
|
+
reusedOutputCount: 0,
|
|
1491
|
+
deletedOutputCount: 0,
|
|
1492
|
+
shadowedOutputCount: 0,
|
|
1493
|
+
fullRegenerationRequired: !canUseIncremental,
|
|
1494
|
+
htmlOutputCount: 0,
|
|
1495
|
+
assetOutputCount: 0,
|
|
1496
|
+
cacheFileSizeBytes: previousOutputCache?.fileSizeBytes ?? null,
|
|
1497
|
+
cacheEntriesRead: previousOutputCache ? Object.keys(previousOutputCache.outputs).length : 0,
|
|
1498
|
+
cacheEntriesSerialized: 0,
|
|
1499
|
+
enumeratedRouteCount: 0,
|
|
1500
|
+
renderedRouteCount: 0,
|
|
1501
|
+
skippedRouteCount,
|
|
1502
|
+
timingsMs
|
|
1503
|
+
};
|
|
1504
|
+
process.env.RIEBECKITE_SSG_FULL_REGENERATION = canUseIncremental ? "" : "1";
|
|
1505
|
+
const outDir = resolve(config.root, config.build.outDir);
|
|
1506
|
+
const result = await measure(
|
|
1507
|
+
timingsMs,
|
|
1508
|
+
"toSSG",
|
|
1509
|
+
() => toSSG(
|
|
1510
|
+
app,
|
|
1511
|
+
{
|
|
1512
|
+
writeFile: async (filePath, data) => {
|
|
1513
|
+
const fileName = relative(outDir, filePath).replaceAll(
|
|
1514
|
+
"\\",
|
|
1515
|
+
"/"
|
|
1516
|
+
);
|
|
1517
|
+
metrics.enumeratedRouteCount += 1;
|
|
1518
|
+
if (siteOwnedOutputPaths.has(fileName)) {
|
|
1519
|
+
shadowedOutputs.add(fileName);
|
|
1520
|
+
return;
|
|
1521
|
+
}
|
|
1522
|
+
if (canUseIncremental && reusablePaths.has(fileName)) {
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
if (fileName.endsWith(".html") && typeof data === "string") {
|
|
1526
|
+
metrics.renderedOutputCount += 1;
|
|
1527
|
+
metrics.htmlOutputCount += 1;
|
|
1528
|
+
generatedHtml.push({ path: fileName, html: data });
|
|
1529
|
+
} else {
|
|
1530
|
+
metrics.assetOutputCount += 1;
|
|
1531
|
+
}
|
|
1532
|
+
this.emitFile({
|
|
1533
|
+
type: "asset",
|
|
1534
|
+
fileName,
|
|
1535
|
+
source: data
|
|
1536
|
+
});
|
|
1537
|
+
metrics.emittedOutputCount += 1;
|
|
1538
|
+
nextOutputCache.outputs[fileName] = encodeOutput(data);
|
|
1539
|
+
},
|
|
1540
|
+
async mkdir() {
|
|
1541
|
+
}
|
|
1542
|
+
},
|
|
1543
|
+
{
|
|
1544
|
+
dir: outDir,
|
|
1545
|
+
plugins: [...options.plugins ?? []],
|
|
1546
|
+
extensionMap: options.extensionMap ?? defaultExtensionMap
|
|
1547
|
+
}
|
|
1548
|
+
)
|
|
1549
|
+
);
|
|
1550
|
+
delete process.env.RIEBECKITE_SSG_FULL_REGENERATION;
|
|
1551
|
+
metrics.renderedRouteCount = result.files.length;
|
|
1552
|
+
if (!result.success) throw result.error;
|
|
1553
|
+
const emittedGeneratedOutputCount = await measure(
|
|
1554
|
+
timingsMs,
|
|
1555
|
+
"generatedOutputs",
|
|
1556
|
+
() => emitGeneratedOutputs(
|
|
1557
|
+
module,
|
|
1558
|
+
(asset) => {
|
|
1559
|
+
this.emitFile(asset);
|
|
1560
|
+
metrics.emittedOutputCount += 1;
|
|
1561
|
+
nextOutputCache.outputs[asset.fileName] = encodeOutput(
|
|
1562
|
+
asset.source
|
|
1563
|
+
);
|
|
1564
|
+
},
|
|
1565
|
+
canUseIncremental ? new Set(
|
|
1566
|
+
outputChangeSet.affected.filter((output) => output.kind === "generated").map((output) => output.path)
|
|
1567
|
+
) : void 0,
|
|
1568
|
+
siteOwnedOutputPaths,
|
|
1569
|
+
(path10) => shadowedOutputs.add(path10)
|
|
1570
|
+
)
|
|
1571
|
+
);
|
|
1572
|
+
if (!outputChangeSet)
|
|
1573
|
+
metrics.candidateOutputCount = result.files.length + emittedGeneratedOutputCount;
|
|
1574
|
+
if (canUseIncremental) {
|
|
1575
|
+
await measure(timingsMs, "unchangedOutputReuse", async () => {
|
|
1576
|
+
for (const output of outputChangeSet.unchanged) {
|
|
1577
|
+
const path10 = output.path;
|
|
1578
|
+
if (siteOwnedOutputPaths.has(path10)) {
|
|
1579
|
+
shadowedOutputs.add(path10);
|
|
1580
|
+
continue;
|
|
1108
1581
|
}
|
|
1582
|
+
const cached = previousOutputCache.outputs[path10];
|
|
1583
|
+
if (!cached) continue;
|
|
1109
1584
|
this.emitFile({
|
|
1110
1585
|
type: "asset",
|
|
1111
|
-
fileName,
|
|
1112
|
-
source:
|
|
1586
|
+
fileName: path10,
|
|
1587
|
+
source: decodeOutput(cached)
|
|
1113
1588
|
});
|
|
1114
|
-
|
|
1115
|
-
|
|
1589
|
+
metrics.emittedOutputCount += 1;
|
|
1590
|
+
metrics.reusedOutputCount += 1;
|
|
1591
|
+
nextOutputCache.outputs[path10] = cached;
|
|
1116
1592
|
}
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
if (outputChangeSet) {
|
|
1596
|
+
for (const output of outputChangeSet.removed) {
|
|
1597
|
+
if (siteOwnedOutputPaths.has(output.path)) continue;
|
|
1598
|
+
await rm(join(outDir, output.path), {
|
|
1599
|
+
force: true
|
|
1600
|
+
});
|
|
1601
|
+
metrics.deletedOutputCount += 1;
|
|
1122
1602
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1603
|
+
}
|
|
1604
|
+
metrics.shadowedOutputCount = shadowedOutputs.size;
|
|
1605
|
+
if (shadowedOutputs.size > 0) {
|
|
1606
|
+
this.warn(
|
|
1607
|
+
`Site public assets take precedence over generated outputs: ${[
|
|
1608
|
+
...shadowedOutputs
|
|
1609
|
+
].sort().join(", ")}`
|
|
1610
|
+
);
|
|
1611
|
+
}
|
|
1612
|
+
metrics.cacheEntriesSerialized = Object.keys(
|
|
1613
|
+
nextOutputCache.outputs
|
|
1614
|
+
).length;
|
|
1615
|
+
try {
|
|
1616
|
+
await saveOutputCache(outputCachePath, nextOutputCache, timingsMs);
|
|
1617
|
+
} catch (error) {
|
|
1618
|
+
this.warn(
|
|
1619
|
+
`SSG output cache could not be saved; the next build regenerates every output. ${error instanceof Error ? error.message : String(error)}`
|
|
1620
|
+
);
|
|
1621
|
+
}
|
|
1622
|
+
await writeSsgOutputMetrics(metrics);
|
|
1126
1623
|
inspectGeneratedHtmlPages(module, generatedHtml, {
|
|
1127
1624
|
warn: (message) => this.warn(message),
|
|
1128
1625
|
info: (message) => this.info(message)
|
|
@@ -1130,19 +1627,247 @@ function riebeckiteSsg(options = {}) {
|
|
|
1130
1627
|
} finally {
|
|
1131
1628
|
await server2.close();
|
|
1132
1629
|
}
|
|
1630
|
+
},
|
|
1631
|
+
async closeBundle() {
|
|
1632
|
+
if (!resolvedConfig) return;
|
|
1633
|
+
const outDir = resolve(resolvedConfig.root, resolvedConfig.build.outDir);
|
|
1634
|
+
const oversizedAssets = await findOversizedAssets(outDir);
|
|
1635
|
+
for (const asset of oversizedAssets) {
|
|
1636
|
+
this.warn(
|
|
1637
|
+
`Generated asset ${asset.path} is ${formatBytes(asset.size)}, exceeding Cloudflare Workers' ${formatBytes(cloudflareWorkerAssetLimit)} asset limit.`
|
|
1638
|
+
);
|
|
1639
|
+
}
|
|
1133
1640
|
}
|
|
1134
1641
|
};
|
|
1135
1642
|
}
|
|
1136
|
-
async function
|
|
1643
|
+
async function findOversizedAssets(directory, relativeDirectory = "") {
|
|
1644
|
+
const entries = await readdir(directory, { withFileTypes: true }).catch(
|
|
1645
|
+
(error) => {
|
|
1646
|
+
if (isMissingDirectoryError(error)) return [];
|
|
1647
|
+
throw error;
|
|
1648
|
+
}
|
|
1649
|
+
);
|
|
1650
|
+
const nestedAssets = await Promise.all(
|
|
1651
|
+
entries.map(async (entry) => {
|
|
1652
|
+
const path10 = join(directory, entry.name);
|
|
1653
|
+
const relativePath = join(relativeDirectory, entry.name).replaceAll(
|
|
1654
|
+
"\\",
|
|
1655
|
+
"/"
|
|
1656
|
+
);
|
|
1657
|
+
if (entry.isDirectory()) {
|
|
1658
|
+
return findOversizedAssets(path10, relativePath);
|
|
1659
|
+
}
|
|
1660
|
+
if (!entry.isFile()) return [];
|
|
1661
|
+
const size = (await stat(path10)).size;
|
|
1662
|
+
return size > cloudflareWorkerAssetLimit ? [{ path: relativePath, size }] : [];
|
|
1663
|
+
})
|
|
1664
|
+
);
|
|
1665
|
+
return nestedAssets.flat();
|
|
1666
|
+
}
|
|
1667
|
+
function isMissingDirectoryError(error) {
|
|
1668
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
1669
|
+
}
|
|
1670
|
+
function formatBytes(bytes) {
|
|
1671
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
|
|
1672
|
+
}
|
|
1673
|
+
function shouldApplyRiebeckiteSsg(_config, env) {
|
|
1674
|
+
return env.command === "build" && env.mode !== "client";
|
|
1675
|
+
}
|
|
1676
|
+
function findBuildOutputSiteCollisions(buildOutputPaths, siteOwnedOutputPaths) {
|
|
1677
|
+
const collisions = /* @__PURE__ */ new Set();
|
|
1678
|
+
for (const path10 of buildOutputPaths) {
|
|
1679
|
+
if (siteOwnedOutputPaths.has(path10)) collisions.add(path10);
|
|
1680
|
+
}
|
|
1681
|
+
return [...collisions].sort();
|
|
1682
|
+
}
|
|
1683
|
+
async function emitGeneratedOutputs(module, emit, affectedPaths, shadowedPaths, onShadowed) {
|
|
1137
1684
|
const content = module.content;
|
|
1138
|
-
if (!content) return;
|
|
1685
|
+
if (!content) return 0;
|
|
1139
1686
|
const manifest = await content.getManifest();
|
|
1687
|
+
let count = 0;
|
|
1140
1688
|
for (const output of manifest.generatedOutputs ?? []) {
|
|
1689
|
+
if (affectedPaths && !affectedPaths.has(output.path)) continue;
|
|
1690
|
+
if (shadowedPaths?.has(output.path)) {
|
|
1691
|
+
onShadowed?.(output.path);
|
|
1692
|
+
continue;
|
|
1693
|
+
}
|
|
1141
1694
|
emit({
|
|
1142
1695
|
type: "asset",
|
|
1143
1696
|
fileName: output.path,
|
|
1144
1697
|
source: output.content
|
|
1145
1698
|
});
|
|
1699
|
+
count += 1;
|
|
1700
|
+
}
|
|
1701
|
+
return count;
|
|
1702
|
+
}
|
|
1703
|
+
var APP_FINGERPRINT_IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
1704
|
+
"node_modules",
|
|
1705
|
+
".riebeckite",
|
|
1706
|
+
"dist"
|
|
1707
|
+
]);
|
|
1708
|
+
var APP_FINGERPRINT_CONFIG_FILES = [
|
|
1709
|
+
"riebeckite.config.ts",
|
|
1710
|
+
"riebeckite.config.mts",
|
|
1711
|
+
"riebeckite.config.cts",
|
|
1712
|
+
"riebeckite.config.js",
|
|
1713
|
+
"riebeckite.config.mjs",
|
|
1714
|
+
"riebeckite.config.cjs"
|
|
1715
|
+
];
|
|
1716
|
+
async function computeAppFingerprint(root, configFile) {
|
|
1717
|
+
const files = [];
|
|
1718
|
+
await collectAppFiles(join(root, "app"), files);
|
|
1719
|
+
if (configFile) files.push(configFile);
|
|
1720
|
+
for (const name of APP_FINGERPRINT_CONFIG_FILES) {
|
|
1721
|
+
files.push(join(root, name));
|
|
1722
|
+
}
|
|
1723
|
+
files.sort();
|
|
1724
|
+
const hash = createHash("sha256");
|
|
1725
|
+
for (const file of files) {
|
|
1726
|
+
let content;
|
|
1727
|
+
try {
|
|
1728
|
+
content = await readFile(file);
|
|
1729
|
+
} catch {
|
|
1730
|
+
continue;
|
|
1731
|
+
}
|
|
1732
|
+
hash.update(relative(root, file).replaceAll("\\", "/"));
|
|
1733
|
+
hash.update("\0");
|
|
1734
|
+
hash.update(content);
|
|
1735
|
+
hash.update("\0");
|
|
1736
|
+
}
|
|
1737
|
+
return hash.digest("hex");
|
|
1738
|
+
}
|
|
1739
|
+
async function collectAppFiles(directory, files) {
|
|
1740
|
+
const entries = await readdir(directory, { withFileTypes: true }).catch(
|
|
1741
|
+
() => void 0
|
|
1742
|
+
);
|
|
1743
|
+
if (!entries) return;
|
|
1744
|
+
for (const entry of entries) {
|
|
1745
|
+
const full = join(directory, entry.name);
|
|
1746
|
+
if (entry.isDirectory()) {
|
|
1747
|
+
if (APP_FINGERPRINT_IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
1748
|
+
await collectAppFiles(full, files);
|
|
1749
|
+
} else if (entry.isFile()) {
|
|
1750
|
+
files.push(full);
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
async function loadOutputCache(path10, timingsMs, log = () => {
|
|
1755
|
+
}) {
|
|
1756
|
+
let raw;
|
|
1757
|
+
try {
|
|
1758
|
+
raw = await measure(
|
|
1759
|
+
timingsMs,
|
|
1760
|
+
"outputCacheRead",
|
|
1761
|
+
() => readFile(path10, "utf8")
|
|
1762
|
+
);
|
|
1763
|
+
} catch (error) {
|
|
1764
|
+
if (isNotFoundError2(error)) return void 0;
|
|
1765
|
+
log(
|
|
1766
|
+
`SSG output cache could not be read; the build regenerates every output. ${error instanceof Error ? error.message : String(error)}`
|
|
1767
|
+
);
|
|
1768
|
+
return void 0;
|
|
1769
|
+
}
|
|
1770
|
+
let parsed;
|
|
1771
|
+
try {
|
|
1772
|
+
parsed = measureSync(timingsMs, "outputCacheParse", () => JSON.parse(raw));
|
|
1773
|
+
} catch (error) {
|
|
1774
|
+
log(
|
|
1775
|
+
`SSG output cache is corrupted and will be rebuilt. ${error instanceof Error ? error.message : String(error)}`
|
|
1776
|
+
);
|
|
1777
|
+
return void 0;
|
|
1778
|
+
}
|
|
1779
|
+
if (!isOutputCacheState(parsed)) {
|
|
1780
|
+
log("SSG output cache uses an unsupported format and will be rebuilt.");
|
|
1781
|
+
return void 0;
|
|
1782
|
+
}
|
|
1783
|
+
return timingsMs ? { ...parsed, fileSizeBytes: Buffer.byteLength(raw) } : parsed;
|
|
1784
|
+
}
|
|
1785
|
+
function isOutputCacheState(value) {
|
|
1786
|
+
if (typeof value !== "object" || value === null) return false;
|
|
1787
|
+
const candidate = value;
|
|
1788
|
+
return candidate.version === 1 && (candidate.appFingerprint === void 0 || typeof candidate.appFingerprint === "string") && isOutputCacheEntries(candidate.outputs);
|
|
1789
|
+
}
|
|
1790
|
+
function isOutputCacheEntries(value) {
|
|
1791
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1792
|
+
return false;
|
|
1793
|
+
}
|
|
1794
|
+
return Object.values(value).every(
|
|
1795
|
+
(entry) => typeof entry === "object" && entry !== null && typeof entry.source === "string" && (entry.encoding === "utf8" || entry.encoding === "base64")
|
|
1796
|
+
);
|
|
1797
|
+
}
|
|
1798
|
+
async function saveOutputCache(path10, state, timingsMs) {
|
|
1799
|
+
const directory = dirname(path10);
|
|
1800
|
+
await mkdir(directory, { recursive: true });
|
|
1801
|
+
const temporaryPath = `${path10}.${randomUUID()}.tmp`;
|
|
1802
|
+
const serialized = measureSync(
|
|
1803
|
+
timingsMs,
|
|
1804
|
+
"outputCacheSerialize",
|
|
1805
|
+
() => `${JSON.stringify(state)}
|
|
1806
|
+
`
|
|
1807
|
+
);
|
|
1808
|
+
try {
|
|
1809
|
+
await measure(
|
|
1810
|
+
timingsMs,
|
|
1811
|
+
"outputCacheWrite",
|
|
1812
|
+
() => writeFile(temporaryPath, serialized, "utf8")
|
|
1813
|
+
);
|
|
1814
|
+
await measure(
|
|
1815
|
+
timingsMs,
|
|
1816
|
+
"outputCacheRename",
|
|
1817
|
+
() => replaceFile(temporaryPath, path10)
|
|
1818
|
+
);
|
|
1819
|
+
} catch (error) {
|
|
1820
|
+
await rm(temporaryPath, { force: true }).catch(() => void 0);
|
|
1821
|
+
throw error;
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
async function replaceFile(temporaryPath, targetPath) {
|
|
1825
|
+
try {
|
|
1826
|
+
await rename2(temporaryPath, targetPath);
|
|
1827
|
+
} catch (error) {
|
|
1828
|
+
if (!isReplacementFailure(error)) throw error;
|
|
1829
|
+
await rm(targetPath, { force: true });
|
|
1830
|
+
await rename2(temporaryPath, targetPath);
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
function isReplacementFailure(error) {
|
|
1834
|
+
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
1835
|
+
return false;
|
|
1836
|
+
}
|
|
1837
|
+
const code = String(error.code);
|
|
1838
|
+
return code === "EPERM" || code === "EEXIST" || code === "EACCES";
|
|
1839
|
+
}
|
|
1840
|
+
function isNotFoundError2(error) {
|
|
1841
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
1842
|
+
}
|
|
1843
|
+
function encodeOutput(source) {
|
|
1844
|
+
return typeof source === "string" ? { source, encoding: "utf8" } : { source: Buffer.from(source).toString("base64"), encoding: "base64" };
|
|
1845
|
+
}
|
|
1846
|
+
function decodeOutput(entry) {
|
|
1847
|
+
return entry.encoding === "utf8" ? entry.source : Buffer.from(entry.source, "base64");
|
|
1848
|
+
}
|
|
1849
|
+
async function writeSsgOutputMetrics(metrics) {
|
|
1850
|
+
const file = process.env.RIEBECKITE_SSG_METRICS_FILE;
|
|
1851
|
+
if (!file) return;
|
|
1852
|
+
await writeFile(file, `${JSON.stringify(metrics)}
|
|
1853
|
+
`, "utf8");
|
|
1854
|
+
}
|
|
1855
|
+
async function measure(timingsMs, name, fn) {
|
|
1856
|
+
const start = performance.now();
|
|
1857
|
+
try {
|
|
1858
|
+
return await fn();
|
|
1859
|
+
} finally {
|
|
1860
|
+
if (timingsMs)
|
|
1861
|
+
timingsMs[name] = (timingsMs[name] ?? 0) + performance.now() - start;
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
function measureSync(timingsMs, name, fn) {
|
|
1865
|
+
const start = performance.now();
|
|
1866
|
+
try {
|
|
1867
|
+
return fn();
|
|
1868
|
+
} finally {
|
|
1869
|
+
if (timingsMs)
|
|
1870
|
+
timingsMs[name] = (timingsMs[name] ?? 0) + performance.now() - start;
|
|
1146
1871
|
}
|
|
1147
1872
|
}
|
|
1148
1873
|
function inspectGeneratedHtmlPages(module, pages, log) {
|
|
@@ -1192,8 +1917,8 @@ function createRiebeckiteSsg(options = {}) {
|
|
|
1192
1917
|
}
|
|
1193
1918
|
|
|
1194
1919
|
// src/vite_runner.ts
|
|
1195
|
-
import
|
|
1196
|
-
import
|
|
1920
|
+
import fs6 from "node:fs/promises";
|
|
1921
|
+
import path7 from "node:path";
|
|
1197
1922
|
import { build, createServer as createServer2 } from "vite";
|
|
1198
1923
|
var viteConfigFileNames = [
|
|
1199
1924
|
"vite.config.ts",
|
|
@@ -1219,8 +1944,8 @@ var HonoxApplicationRootError = class extends Error {
|
|
|
1219
1944
|
}
|
|
1220
1945
|
};
|
|
1221
1946
|
async function resolveHonoxApplication(options) {
|
|
1222
|
-
const configRoot =
|
|
1223
|
-
const appRoot = options.appRoot ?
|
|
1947
|
+
const configRoot = path7.resolve(options.configRoot);
|
|
1948
|
+
const appRoot = options.appRoot ? path7.resolve(options.appRoot) : await resolveHonoxApplicationRoot(configRoot);
|
|
1224
1949
|
const workspaceRoot = options.workspaceRoot ?? await findWorkspaceRoot(configRoot);
|
|
1225
1950
|
const rawConfig = await loadRiebeckiteConfig({
|
|
1226
1951
|
configRoot,
|
|
@@ -1237,37 +1962,53 @@ async function resolveHonoxApplication(options) {
|
|
|
1237
1962
|
}
|
|
1238
1963
|
async function findWorkspaceRoot(configRoot) {
|
|
1239
1964
|
try {
|
|
1240
|
-
const workspaceConfig =
|
|
1241
|
-
return (await
|
|
1965
|
+
const workspaceConfig = path7.join(configRoot, "pnpm-workspace.yaml");
|
|
1966
|
+
return (await fs6.stat(workspaceConfig)).isFile() ? configRoot : void 0;
|
|
1242
1967
|
} catch {
|
|
1243
1968
|
return void 0;
|
|
1244
1969
|
}
|
|
1245
1970
|
}
|
|
1246
1971
|
async function startHonoxDevServer(options) {
|
|
1247
|
-
const
|
|
1248
|
-
await
|
|
1249
|
-
|
|
1250
|
-
|
|
1972
|
+
const root = path7.resolve(options.root);
|
|
1973
|
+
await withWorkingDirectory(root, async () => {
|
|
1974
|
+
const server2 = await createServer2({ root });
|
|
1975
|
+
await server2.listen();
|
|
1976
|
+
server2.printUrls();
|
|
1977
|
+
await waitForShutdown(server2);
|
|
1978
|
+
});
|
|
1251
1979
|
}
|
|
1252
1980
|
async function buildHonoxApplication(options) {
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1981
|
+
const root = path7.resolve(options.root);
|
|
1982
|
+
await withWorkingDirectory(root, async () => {
|
|
1983
|
+
await runTraced(
|
|
1984
|
+
options.tracer,
|
|
1985
|
+
"integration.honox.client_build",
|
|
1986
|
+
() => build({ root, mode: "client" })
|
|
1987
|
+
);
|
|
1988
|
+
await runTraced(
|
|
1989
|
+
options.tracer,
|
|
1990
|
+
"integration.honox.server_build",
|
|
1991
|
+
() => build({ root })
|
|
1992
|
+
);
|
|
1993
|
+
});
|
|
1994
|
+
}
|
|
1995
|
+
async function withWorkingDirectory(root, task) {
|
|
1996
|
+
const previousDirectory = process.cwd();
|
|
1997
|
+
if (previousDirectory === root) return await task();
|
|
1998
|
+
process.chdir(root);
|
|
1999
|
+
try {
|
|
2000
|
+
return await task();
|
|
2001
|
+
} finally {
|
|
2002
|
+
process.chdir(previousDirectory);
|
|
2003
|
+
}
|
|
1263
2004
|
}
|
|
1264
2005
|
async function runTraced(tracer, name, buildTask) {
|
|
1265
2006
|
return tracer ? await tracer.span(name, {}, buildTask) : await buildTask();
|
|
1266
2007
|
}
|
|
1267
2008
|
async function waitForShutdown(server2) {
|
|
1268
|
-
await new Promise((
|
|
2009
|
+
await new Promise((resolve2) => {
|
|
1269
2010
|
const close = () => {
|
|
1270
|
-
void server2.close().finally(
|
|
2011
|
+
void server2.close().finally(resolve2);
|
|
1271
2012
|
};
|
|
1272
2013
|
process.once("SIGINT", close);
|
|
1273
2014
|
process.once("SIGTERM", close);
|
|
@@ -1280,12 +2021,12 @@ async function findViteApplicationRoots(root) {
|
|
|
1280
2021
|
const directory = directories.pop();
|
|
1281
2022
|
if (!directory) continue;
|
|
1282
2023
|
if (await hasViteConfig(directory)) applicationRoots.push(directory);
|
|
1283
|
-
const entries = await
|
|
2024
|
+
const entries = await fs6.readdir(directory, { withFileTypes: true });
|
|
1284
2025
|
for (const entry of entries) {
|
|
1285
2026
|
if (!entry.isDirectory() || entry.name === "node_modules" || entry.name === ".git" || entry.name === "tests") {
|
|
1286
2027
|
continue;
|
|
1287
2028
|
}
|
|
1288
|
-
directories.push(
|
|
2029
|
+
directories.push(path7.join(directory, entry.name));
|
|
1289
2030
|
}
|
|
1290
2031
|
}
|
|
1291
2032
|
return applicationRoots;
|
|
@@ -1293,7 +2034,7 @@ async function findViteApplicationRoots(root) {
|
|
|
1293
2034
|
async function hasViteConfig(directory) {
|
|
1294
2035
|
for (const fileName of viteConfigFileNames) {
|
|
1295
2036
|
try {
|
|
1296
|
-
if ((await
|
|
2037
|
+
if ((await fs6.stat(path7.join(directory, fileName))).isFile()) return true;
|
|
1297
2038
|
} catch {
|
|
1298
2039
|
}
|
|
1299
2040
|
}
|
|
@@ -1313,6 +2054,7 @@ var defaultSsrExternals = [
|
|
|
1313
2054
|
];
|
|
1314
2055
|
function riebeckite(options = {}) {
|
|
1315
2056
|
let resolvedConfig;
|
|
2057
|
+
let contentWatchRoots;
|
|
1316
2058
|
const getConfig = () => {
|
|
1317
2059
|
if (!resolvedConfig) {
|
|
1318
2060
|
throw new Error("Riebeckite config has not been loaded yet.");
|
|
@@ -1323,7 +2065,7 @@ function riebeckite(options = {}) {
|
|
|
1323
2065
|
{
|
|
1324
2066
|
name: "riebeckite-host-integration",
|
|
1325
2067
|
async config(userConfig) {
|
|
1326
|
-
const root = userConfig.root ?
|
|
2068
|
+
const root = userConfig.root ? path8.resolve(userConfig.root) : process.cwd();
|
|
1327
2069
|
const appRoot = options.appRoot ?? root;
|
|
1328
2070
|
const application = await resolveHonoxApplication({
|
|
1329
2071
|
configRoot: options.configRoot ?? appRoot,
|
|
@@ -1332,9 +2074,10 @@ function riebeckite(options = {}) {
|
|
|
1332
2074
|
workspaceRoot: options.workspaceRoot
|
|
1333
2075
|
});
|
|
1334
2076
|
resolvedConfig = application.config;
|
|
2077
|
+
contentWatchRoots = application;
|
|
1335
2078
|
writeRiebeckiteAssetEntries(resolvedConfig, {
|
|
1336
|
-
pluginStyles:
|
|
1337
|
-
themeStyles:
|
|
2079
|
+
pluginStyles: path8.join(appRoot, "app/.riebeckite/plugin-styles.css"),
|
|
2080
|
+
themeStyles: path8.join(appRoot, "app/.riebeckite/theme-styles.css")
|
|
1338
2081
|
});
|
|
1339
2082
|
return {
|
|
1340
2083
|
define: {
|
|
@@ -1351,9 +2094,24 @@ function riebeckite(options = {}) {
|
|
|
1351
2094
|
};
|
|
1352
2095
|
}
|
|
1353
2096
|
},
|
|
1354
|
-
riebeckiteClientModule(getConfig)
|
|
2097
|
+
riebeckiteClientModule(getConfig),
|
|
2098
|
+
riebeckiteContentAssets({
|
|
2099
|
+
appRoot: () => requireContentWatchRoots(contentWatchRoots).appRoot,
|
|
2100
|
+
contentRoot: () => requireContentWatchRoots(contentWatchRoots).contentRoot
|
|
2101
|
+
}),
|
|
2102
|
+
riebeckiteContentWatch({
|
|
2103
|
+
appRoot: () => requireContentWatchRoots(contentWatchRoots).appRoot,
|
|
2104
|
+
contentRoot: () => requireContentWatchRoots(contentWatchRoots).contentRoot,
|
|
2105
|
+
exclude: () => getConfig().content.exclude ?? []
|
|
2106
|
+
})
|
|
1355
2107
|
];
|
|
1356
2108
|
}
|
|
2109
|
+
function requireContentWatchRoots(roots) {
|
|
2110
|
+
if (!roots) {
|
|
2111
|
+
throw new Error("Riebeckite application has not been resolved yet.");
|
|
2112
|
+
}
|
|
2113
|
+
return roots;
|
|
2114
|
+
}
|
|
1357
2115
|
function riebeckiteVite(options = {}) {
|
|
1358
2116
|
const { ssg, ssrExternals, ...integration } = options;
|
|
1359
2117
|
return [
|
|
@@ -1380,6 +2138,17 @@ function riebeckiteSsrExternals(externals) {
|
|
|
1380
2138
|
}
|
|
1381
2139
|
|
|
1382
2140
|
// src/scaffold/app-templates.ts
|
|
2141
|
+
function hasScaffoldPlugin(preset, packageName) {
|
|
2142
|
+
return preset.plugins.some((plugin) => plugin.package === packageName);
|
|
2143
|
+
}
|
|
2144
|
+
function articleTitleHelper() {
|
|
2145
|
+
return `
|
|
2146
|
+
function getArticleTitle(slug: string, title: unknown): string {
|
|
2147
|
+
if (typeof title === "string" && title.trim().length > 0) return title;
|
|
2148
|
+
return slug.split("/").at(-1) ?? slug;
|
|
2149
|
+
}
|
|
2150
|
+
`;
|
|
2151
|
+
}
|
|
1383
2152
|
function server() {
|
|
1384
2153
|
return `import { mountRiebeckiteEndpoints } from "@riebeckite/honox/server";
|
|
1385
2154
|
import { createApp } from "honox/server";
|
|
@@ -1504,18 +2273,112 @@ function style(preset) {
|
|
|
1504
2273
|
" justify-content: space-between;",
|
|
1505
2274
|
" gap: 1rem;",
|
|
1506
2275
|
" padding: 1rem;",
|
|
1507
|
-
" border-bottom: 1px solid
|
|
2276
|
+
" border-bottom: 1px solid var(--rb-color-border);",
|
|
1508
2277
|
"}",
|
|
1509
2278
|
"",
|
|
1510
2279
|
".site-header__home {",
|
|
1511
2280
|
" display: inline-flex;",
|
|
1512
2281
|
" align-items: center;",
|
|
1513
2282
|
" gap: 0.5rem;",
|
|
1514
|
-
" color:
|
|
2283
|
+
" color: var(--rb-color-ink);",
|
|
1515
2284
|
" font-weight: 700;",
|
|
1516
2285
|
" text-decoration: none;",
|
|
1517
2286
|
"}",
|
|
1518
2287
|
"",
|
|
2288
|
+
".rb-nav {",
|
|
2289
|
+
" color: var(--rb-color-ink);",
|
|
2290
|
+
"}",
|
|
2291
|
+
"",
|
|
2292
|
+
".rb-nav__list {",
|
|
2293
|
+
" display: flex;",
|
|
2294
|
+
" align-items: center;",
|
|
2295
|
+
" gap: 0.75rem;",
|
|
2296
|
+
" margin: 0;",
|
|
2297
|
+
" padding: 0;",
|
|
2298
|
+
" list-style: none;",
|
|
2299
|
+
"}",
|
|
2300
|
+
"",
|
|
2301
|
+
".rb-nav__item {",
|
|
2302
|
+
" position: relative;",
|
|
2303
|
+
"}",
|
|
2304
|
+
"",
|
|
2305
|
+
".rb-nav__link {",
|
|
2306
|
+
" color: inherit;",
|
|
2307
|
+
" text-decoration: none;",
|
|
2308
|
+
"}",
|
|
2309
|
+
"",
|
|
2310
|
+
".rb-nav__link:hover,",
|
|
2311
|
+
".rb-nav__link:focus-visible,",
|
|
2312
|
+
".rb-nav__link--active {",
|
|
2313
|
+
" color: var(--rb-color-accent);",
|
|
2314
|
+
"}",
|
|
2315
|
+
"",
|
|
2316
|
+
".rb-nav__link:focus-visible,",
|
|
2317
|
+
".rb-nav__toggle:focus-visible {",
|
|
2318
|
+
" outline: 2px solid var(--rb-color-accent);",
|
|
2319
|
+
" outline-offset: 2px;",
|
|
2320
|
+
"}",
|
|
2321
|
+
"",
|
|
2322
|
+
".rb-nav__children {",
|
|
2323
|
+
" display: none;",
|
|
2324
|
+
" position: absolute;",
|
|
2325
|
+
" top: 100%;",
|
|
2326
|
+
" left: 0;",
|
|
2327
|
+
" min-width: max-content;",
|
|
2328
|
+
" padding: 0.5rem;",
|
|
2329
|
+
" background: var(--rb-color-surface);",
|
|
2330
|
+
" border: 1px solid var(--rb-color-border);",
|
|
2331
|
+
"}",
|
|
2332
|
+
"",
|
|
2333
|
+
".rb-nav__item:hover > .rb-nav__children,",
|
|
2334
|
+
".rb-nav__item:focus-within > .rb-nav__children {",
|
|
2335
|
+
" display: flex;",
|
|
2336
|
+
" flex-direction: column;",
|
|
2337
|
+
" align-items: stretch;",
|
|
2338
|
+
"}",
|
|
2339
|
+
"",
|
|
2340
|
+
".rb-nav__mobile {",
|
|
2341
|
+
" display: none;",
|
|
2342
|
+
"}",
|
|
2343
|
+
"",
|
|
2344
|
+
".rb-site-footer {",
|
|
2345
|
+
" width: min(100% - 2rem, 48rem);",
|
|
2346
|
+
" margin: 1rem auto 0;",
|
|
2347
|
+
" padding-block: 1rem;",
|
|
2348
|
+
" border-top: 1px solid var(--rb-color-border);",
|
|
2349
|
+
"}",
|
|
2350
|
+
"",
|
|
2351
|
+
"@media (max-width: 48rem) {",
|
|
2352
|
+
" .rb-site-header {",
|
|
2353
|
+
" flex-wrap: wrap;",
|
|
2354
|
+
" }",
|
|
2355
|
+
"",
|
|
2356
|
+
" .rb-site-header > .rb-nav {",
|
|
2357
|
+
" display: none;",
|
|
2358
|
+
" }",
|
|
2359
|
+
"",
|
|
2360
|
+
" .rb-nav__mobile {",
|
|
2361
|
+
" display: block;",
|
|
2362
|
+
" }",
|
|
2363
|
+
"",
|
|
2364
|
+
" .rb-nav__mobile .rb-nav__list {",
|
|
2365
|
+
" flex-direction: column;",
|
|
2366
|
+
" align-items: flex-start;",
|
|
2367
|
+
" padding-top: 0.75rem;",
|
|
2368
|
+
" }",
|
|
2369
|
+
"",
|
|
2370
|
+
" .rb-nav__mobile .rb-nav__children {",
|
|
2371
|
+
" display: flex;",
|
|
2372
|
+
" position: static;",
|
|
2373
|
+
" flex-direction: column;",
|
|
2374
|
+
" align-items: flex-start;",
|
|
2375
|
+
" gap: 0.5rem;",
|
|
2376
|
+
" margin-top: 0.5rem;",
|
|
2377
|
+
" background: transparent;",
|
|
2378
|
+
" border: 0;",
|
|
2379
|
+
" }",
|
|
2380
|
+
"}",
|
|
2381
|
+
"",
|
|
1519
2382
|
".site-header__logo {",
|
|
1520
2383
|
" width: 1.75rem;",
|
|
1521
2384
|
" height: 1.75rem;",
|
|
@@ -1532,6 +2395,56 @@ function style(preset) {
|
|
|
1532
2395
|
" padding: 2rem 1rem;",
|
|
1533
2396
|
" margin: 0 auto;",
|
|
1534
2397
|
"}",
|
|
2398
|
+
"",
|
|
2399
|
+
".article-shell__layout {",
|
|
2400
|
+
" display: grid;",
|
|
2401
|
+
" grid-template-columns: minmax(0, 1fr);",
|
|
2402
|
+
" justify-content: center;",
|
|
2403
|
+
" gap: var(--rb-layout-gap, 2rem);",
|
|
2404
|
+
"}",
|
|
2405
|
+
"",
|
|
2406
|
+
".site-article__aside {",
|
|
2407
|
+
" display: none;",
|
|
2408
|
+
"}",
|
|
2409
|
+
"",
|
|
2410
|
+
".site-article__footer {",
|
|
2411
|
+
" min-width: 0;",
|
|
2412
|
+
"}",
|
|
2413
|
+
"",
|
|
2414
|
+
"@media (min-width: 88rem) {",
|
|
2415
|
+
' :is(:root, .rb-theme-root)[data-article-layout="article"] .article-shell__layout {',
|
|
2416
|
+
" grid-template-columns: minmax(0, var(--rb-layout-article-max, 48rem));",
|
|
2417
|
+
" position: relative;",
|
|
2418
|
+
" }",
|
|
2419
|
+
"",
|
|
2420
|
+
' :is(:root, .rb-theme-root)[data-article-layout="article"] .article-shell__body {',
|
|
2421
|
+
" grid-column: 1;",
|
|
2422
|
+
" grid-row: 1;",
|
|
2423
|
+
" }",
|
|
2424
|
+
"",
|
|
2425
|
+
' :is(:root, .rb-theme-root)[data-article-layout="article"] .site-article__footer {',
|
|
2426
|
+
" grid-column: 1;",
|
|
2427
|
+
" }",
|
|
2428
|
+
"",
|
|
2429
|
+
' :is(:root, .rb-theme-root)[data-article-layout="article"] .site-article__aside {',
|
|
2430
|
+
" display: block;",
|
|
2431
|
+
" grid-column: 1;",
|
|
2432
|
+
" grid-row: 1;",
|
|
2433
|
+
" position: sticky;",
|
|
2434
|
+
" top: 4rem;",
|
|
2435
|
+
" align-self: start;",
|
|
2436
|
+
" width: var(--rb-layout-sidebar, 14rem);",
|
|
2437
|
+
" max-height: calc(100dvh - 8rem);",
|
|
2438
|
+
" margin-inline-start: calc(",
|
|
2439
|
+
" -1 * var(--rb-layout-sidebar, 14rem) -",
|
|
2440
|
+
" var(--rb-layout-gap, 2rem)",
|
|
2441
|
+
" );",
|
|
2442
|
+
" overflow: auto;",
|
|
2443
|
+
" padding-inline-end: 1rem;",
|
|
2444
|
+
" border-inline-end: 1px solid",
|
|
2445
|
+
" color-mix(in oklab, currentColor 18%, transparent);",
|
|
2446
|
+
" }",
|
|
2447
|
+
"}",
|
|
1535
2448
|
""
|
|
1536
2449
|
);
|
|
1537
2450
|
}
|
|
@@ -1548,13 +2461,16 @@ function style(preset) {
|
|
|
1548
2461
|
return [...imports, ...blocks].join("\n");
|
|
1549
2462
|
}
|
|
1550
2463
|
function siteHeader() {
|
|
1551
|
-
return `import {
|
|
2464
|
+
return `import type { NavigationItem } from "@riebeckite/core";
|
|
2465
|
+
import { ColorModeToggle } from "@riebeckite/plugin-color-mode";
|
|
1552
2466
|
import { config } from "../config";
|
|
1553
2467
|
|
|
1554
|
-
export function SiteHeader() {
|
|
2468
|
+
export function SiteHeader({ path }: { path: string }) {
|
|
2469
|
+
const navigation = (config.navigation?.header ?? []).filter((item) => item.href !== "/");
|
|
2470
|
+
|
|
1555
2471
|
return (
|
|
1556
|
-
<header class="site-header">
|
|
1557
|
-
<a href="/" class="site-header__home">
|
|
2472
|
+
<header class="site-header rb-site-header">
|
|
2473
|
+
<a href="/" class="site-header__home rb-site-header__home">
|
|
1558
2474
|
<img
|
|
1559
2475
|
src="/riebeckite-logo.png"
|
|
1560
2476
|
alt=""
|
|
@@ -1564,10 +2480,85 @@ export function SiteHeader() {
|
|
|
1564
2480
|
/>
|
|
1565
2481
|
{config.site.title}
|
|
1566
2482
|
</a>
|
|
2483
|
+
{navigation.length > 0 ? (
|
|
2484
|
+
<>
|
|
2485
|
+
<SiteNavigation items={navigation} path={path} />
|
|
2486
|
+
<details class="site-navigation__mobile rb-nav__mobile">
|
|
2487
|
+
<summary class="rb-nav__toggle">Menu</summary>
|
|
2488
|
+
<SiteNavigation items={navigation} path={path} />
|
|
2489
|
+
</details>
|
|
2490
|
+
</>
|
|
2491
|
+
) : null}
|
|
1567
2492
|
<ColorModeToggle />
|
|
1568
2493
|
</header>
|
|
1569
2494
|
);
|
|
1570
2495
|
}
|
|
2496
|
+
|
|
2497
|
+
export function SiteFooter({ path }: { path: string }) {
|
|
2498
|
+
const footerNavigation = config.navigation?.footer ?? [];
|
|
2499
|
+
if (footerNavigation.length === 0) return null;
|
|
2500
|
+
|
|
2501
|
+
return (
|
|
2502
|
+
<footer class="site-footer rb-site-footer">
|
|
2503
|
+
<SiteNavigation items={footerNavigation} path={path} />
|
|
2504
|
+
</footer>
|
|
2505
|
+
);
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2508
|
+
function SiteNavigation({
|
|
2509
|
+
items,
|
|
2510
|
+
path,
|
|
2511
|
+
}: {
|
|
2512
|
+
items: readonly NavigationItem[];
|
|
2513
|
+
path: string;
|
|
2514
|
+
}) {
|
|
2515
|
+
return (
|
|
2516
|
+
<nav class="site-navigation rb-nav" aria-label="Site navigation">
|
|
2517
|
+
<NavigationItems items={items} path={path} />
|
|
2518
|
+
</nav>
|
|
2519
|
+
);
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2522
|
+
function NavigationItems({
|
|
2523
|
+
items,
|
|
2524
|
+
path,
|
|
2525
|
+
isChildList = false,
|
|
2526
|
+
}: {
|
|
2527
|
+
items: readonly NavigationItem[];
|
|
2528
|
+
path: string;
|
|
2529
|
+
isChildList?: boolean;
|
|
2530
|
+
}) {
|
|
2531
|
+
return (
|
|
2532
|
+
<ul class={isChildList ? "site-navigation__list rb-nav__list rb-nav__children" : "site-navigation__list rb-nav__list"}>
|
|
2533
|
+
{items.map((item) => {
|
|
2534
|
+
const active = isActive(item.href, path);
|
|
2535
|
+
return (
|
|
2536
|
+
<li class="site-navigation__item rb-nav__item">
|
|
2537
|
+
<a
|
|
2538
|
+
href={item.href}
|
|
2539
|
+
class={active ? "site-navigation__link rb-nav__link rb-nav__link--active is-active" : "site-navigation__link rb-nav__link"}
|
|
2540
|
+
aria-current={active ? "page" : undefined}
|
|
2541
|
+
target={item.external ? "_blank" : undefined}
|
|
2542
|
+
rel={item.external ? "noreferrer" : undefined}
|
|
2543
|
+
>
|
|
2544
|
+
{item.label}
|
|
2545
|
+
</a>
|
|
2546
|
+
{item.children && item.children.length > 0 ? (
|
|
2547
|
+
<NavigationItems items={item.children} path={path} isChildList />
|
|
2548
|
+
) : null}
|
|
2549
|
+
</li>
|
|
2550
|
+
);
|
|
2551
|
+
})}
|
|
2552
|
+
</ul>
|
|
2553
|
+
);
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
function isActive(href: string, path: string): boolean {
|
|
2557
|
+
if (!href.startsWith("/")) return false;
|
|
2558
|
+
const target = href.replace(/\\/+$/, "") || "/";
|
|
2559
|
+
const current = path.replace(/^\\/[a-z]{2}(?:-[A-Z]{2})?(?=\\/|$)/, "").replace(/\\/+$/, "") || "/";
|
|
2560
|
+
return target === "/" ? current === target : current === target || current.startsWith(\`\${target}/\`);
|
|
2561
|
+
}
|
|
1571
2562
|
`;
|
|
1572
2563
|
}
|
|
1573
2564
|
function article() {
|
|
@@ -1576,23 +2567,81 @@ import { Article, ArticleContent, ArticleLayout } from "@riebeckite/honox/ui";
|
|
|
1576
2567
|
|
|
1577
2568
|
export function SiteArticle({
|
|
1578
2569
|
post,
|
|
1579
|
-
|
|
2570
|
+
propertiesHtml,
|
|
2571
|
+
afterHeaderHtml,
|
|
2572
|
+
afterMetaHtml,
|
|
2573
|
+
beforeContentHtml,
|
|
2574
|
+
afterContentHtml,
|
|
2575
|
+
asideHtml,
|
|
2576
|
+
footerHtml,
|
|
2577
|
+
asideContent,
|
|
2578
|
+
afterContent,
|
|
2579
|
+
footerContent,
|
|
1580
2580
|
}: {
|
|
1581
2581
|
post: PostContent;
|
|
1582
|
-
|
|
2582
|
+
propertiesHtml?: string;
|
|
2583
|
+
afterHeaderHtml?: string;
|
|
2584
|
+
afterMetaHtml?: string;
|
|
2585
|
+
beforeContentHtml?: string;
|
|
2586
|
+
afterContentHtml?: string;
|
|
2587
|
+
asideHtml?: string;
|
|
2588
|
+
footerHtml?: string;
|
|
2589
|
+
asideContent?: unknown;
|
|
2590
|
+
afterContent?: unknown;
|
|
2591
|
+
footerContent?: unknown;
|
|
1583
2592
|
}) {
|
|
1584
2593
|
return (
|
|
1585
2594
|
<Article class="site-article">
|
|
1586
2595
|
<ArticleLayout>
|
|
2596
|
+
{asideContent}
|
|
2597
|
+
{asideHtml ? (
|
|
2598
|
+
<div
|
|
2599
|
+
class="site-article__aside"
|
|
2600
|
+
dangerouslySetInnerHTML={{ __html: asideHtml }}
|
|
2601
|
+
/>
|
|
2602
|
+
) : null}
|
|
1587
2603
|
<ArticleContent>
|
|
1588
|
-
{
|
|
2604
|
+
{afterHeaderHtml ? (
|
|
2605
|
+
<div
|
|
2606
|
+
class="site-article__after-header"
|
|
2607
|
+
dangerouslySetInnerHTML={{ __html: afterHeaderHtml }}
|
|
2608
|
+
/>
|
|
2609
|
+
) : null}
|
|
2610
|
+
{propertiesHtml ? (
|
|
2611
|
+
<div
|
|
2612
|
+
class="article-properties"
|
|
2613
|
+
dangerouslySetInnerHTML={{ __html: propertiesHtml }}
|
|
2614
|
+
/>
|
|
2615
|
+
) : null}
|
|
2616
|
+
{afterMetaHtml ? (
|
|
1589
2617
|
<div
|
|
1590
2618
|
class="site-article__after-meta"
|
|
1591
|
-
dangerouslySetInnerHTML={{ __html:
|
|
2619
|
+
dangerouslySetInnerHTML={{ __html: afterMetaHtml }}
|
|
2620
|
+
/>
|
|
2621
|
+
) : null}
|
|
2622
|
+
{beforeContentHtml ? (
|
|
2623
|
+
<div
|
|
2624
|
+
class="site-article__before-content"
|
|
2625
|
+
dangerouslySetInnerHTML={{ __html: beforeContentHtml }}
|
|
1592
2626
|
/>
|
|
1593
2627
|
) : null}
|
|
1594
2628
|
<div dangerouslySetInnerHTML={{ __html: post.html ?? "" }} />
|
|
2629
|
+
{afterContent}
|
|
2630
|
+
{afterContentHtml ? (
|
|
2631
|
+
<div
|
|
2632
|
+
class="site-article__after-content"
|
|
2633
|
+
dangerouslySetInnerHTML={{ __html: afterContentHtml }}
|
|
2634
|
+
/>
|
|
2635
|
+
) : null}
|
|
1595
2636
|
</ArticleContent>
|
|
2637
|
+
{footerHtml || footerContent ? (
|
|
2638
|
+
<div class="site-article__footer">
|
|
2639
|
+
{footerHtml ? (
|
|
2640
|
+
<div dangerouslySetInnerHTML={{ __html: footerHtml }} />
|
|
2641
|
+
) : null}
|
|
2642
|
+
{footerContent}
|
|
2643
|
+
</div>
|
|
2644
|
+
) : null}
|
|
1596
2645
|
</ArticleLayout>
|
|
1597
2646
|
</Article>
|
|
1598
2647
|
);
|
|
@@ -1605,11 +2654,13 @@ function renderer(preset) {
|
|
|
1605
2654
|
const hasColorMode = preset.plugins.some(
|
|
1606
2655
|
(plugin) => plugin.package === "@riebeckite/plugin-color-mode"
|
|
1607
2656
|
);
|
|
2657
|
+
const hasSearch = hasScaffoldPlugin(preset, "@riebeckite/plugin-search");
|
|
1608
2658
|
return `import type { PluginHeadTag } from "@riebeckite/core";
|
|
1609
2659
|
import { jsxRenderer } from "hono/jsx-renderer";
|
|
1610
2660
|
import { Link, Script } from "honox/server";
|
|
1611
2661
|
${hasColorMode ? `import { ColorModeScript } from "@riebeckite/plugin-color-mode";
|
|
1612
|
-
` : ""}${
|
|
2662
|
+
` : ""}${hasSearch ? `import { SearchBar } from "@riebeckite/plugin-search";
|
|
2663
|
+
` : ""}${hasHeader ? `import { SiteFooter, SiteHeader } from "../components/site-header";
|
|
1613
2664
|
` : ""}import { config } from "../config";
|
|
1614
2665
|
|
|
1615
2666
|
function themeAttributes() {
|
|
@@ -1646,10 +2697,12 @@ ${hasColorMode ? ` <ColorModeScript />
|
|
|
1646
2697
|
{headTags.map(renderHeadTag)}
|
|
1647
2698
|
<Script src="/app/client.ts" async />
|
|
1648
2699
|
</head>
|
|
1649
|
-
<body class="riebeckite-page">
|
|
1650
|
-
${hasHeader ? ` <SiteHeader />
|
|
2700
|
+
<body class="riebeckite-page rb-site">
|
|
2701
|
+
${hasHeader ? ` <SiteHeader path={c.req.path} />
|
|
2702
|
+
` : ""}${hasSearch ? ` <SearchBar />
|
|
1651
2703
|
` : ""} {children}
|
|
1652
|
-
|
|
2704
|
+
${hasHeader ? ` <SiteFooter path={c.req.path} />
|
|
2705
|
+
` : ""} </body>
|
|
1653
2706
|
</html>
|
|
1654
2707
|
);
|
|
1655
2708
|
});
|
|
@@ -1689,18 +2742,103 @@ export default jsxRenderer(({ children }, c) => (
|
|
|
1689
2742
|
<Link href="/app/style.css" rel="stylesheet" />
|
|
1690
2743
|
<Script src="/app/client.ts" async />
|
|
1691
2744
|
</head>
|
|
1692
|
-
<body class="riebeckite-page">{children}</body>
|
|
2745
|
+
<body class="riebeckite-page rb-site">{children}</body>
|
|
1693
2746
|
</html>
|
|
1694
2747
|
));
|
|
1695
2748
|
`;
|
|
1696
2749
|
}
|
|
1697
2750
|
function indexRoute(preset) {
|
|
1698
2751
|
if (!preset.appFiles.includes("article")) return staticIndexRoute();
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
2752
|
+
const hasToc = hasScaffoldPlugin(preset, "@riebeckite/plugin-toc");
|
|
2753
|
+
const hasBacklinks = hasScaffoldPlugin(
|
|
2754
|
+
preset,
|
|
2755
|
+
"@riebeckite/plugin-backlinks"
|
|
2756
|
+
);
|
|
2757
|
+
const hasRecentPosts = hasScaffoldPlugin(
|
|
2758
|
+
preset,
|
|
2759
|
+
"@riebeckite/plugin-recent-posts"
|
|
2760
|
+
);
|
|
2761
|
+
const hasDailyNotes = hasScaffoldPlugin(
|
|
2762
|
+
preset,
|
|
2763
|
+
"@riebeckite/plugin-daily-notes"
|
|
2764
|
+
);
|
|
2765
|
+
const needsTitle = hasBacklinks || hasRecentPosts;
|
|
2766
|
+
const needsConfig = hasBacklinks || hasRecentPosts || hasDailyNotes;
|
|
2767
|
+
const importLines = [
|
|
2768
|
+
'import { createRoute } from "honox/factory";'
|
|
2769
|
+
];
|
|
2770
|
+
if (hasBacklinks) {
|
|
2771
|
+
importLines.push(
|
|
2772
|
+
'import { Backlinks, getPublishedBacklinks } from "@riebeckite/plugin-backlinks";'
|
|
2773
|
+
);
|
|
2774
|
+
}
|
|
2775
|
+
if (hasDailyNotes) {
|
|
2776
|
+
importLines.push(
|
|
2777
|
+
'import { DailyNotes, getDailyNotes } from "@riebeckite/plugin-daily-notes";'
|
|
2778
|
+
);
|
|
2779
|
+
}
|
|
2780
|
+
if (hasRecentPosts) {
|
|
2781
|
+
importLines.push(
|
|
2782
|
+
'import { RecentPosts, getRecentPosts } from "@riebeckite/plugin-recent-posts";'
|
|
2783
|
+
);
|
|
2784
|
+
}
|
|
2785
|
+
if (hasToc) {
|
|
2786
|
+
importLines.push(
|
|
2787
|
+
'import { extractTableOfContents, TableOfContents } from "@riebeckite/plugin-toc";'
|
|
2788
|
+
);
|
|
2789
|
+
}
|
|
2790
|
+
importLines.push('import { SiteArticle } from "../components/article";');
|
|
2791
|
+
if (needsConfig) importLines.push('import { config } from "../config";');
|
|
2792
|
+
importLines.push('import { content } from "../content";');
|
|
2793
|
+
const dataLines = [];
|
|
2794
|
+
if (hasBacklinks) {
|
|
2795
|
+
dataLines.push(
|
|
2796
|
+
' const backlinks = getPublishedBacklinks({ manifest, config, slug: "index", resolveTitle: getArticleTitle });'
|
|
2797
|
+
);
|
|
2798
|
+
}
|
|
2799
|
+
if (hasRecentPosts) {
|
|
2800
|
+
dataLines.push(
|
|
2801
|
+
" const recentPosts = await getRecentPosts({ posts: manifest.discoverableEntries, config, getProcessedContent: (slug) => content.getProcessedContent(slug), resolveTitle: getArticleTitle });"
|
|
2802
|
+
);
|
|
2803
|
+
}
|
|
2804
|
+
if (hasDailyNotes) {
|
|
2805
|
+
dataLines.push(" const dailyNotes = getDailyNotes({ manifest, config });");
|
|
2806
|
+
}
|
|
2807
|
+
if (hasToc) {
|
|
2808
|
+
dataLines.push(
|
|
2809
|
+
' const tableOfContents = extractTableOfContents(post.html ?? "");'
|
|
2810
|
+
);
|
|
2811
|
+
}
|
|
2812
|
+
const propLines = [
|
|
2813
|
+
" propertiesHtml={indexEntry?.bodySlots?.properties}",
|
|
2814
|
+
' afterHeaderHtml={indexEntry?.bodySlots?.["article.after-header"]}',
|
|
2815
|
+
' afterMetaHtml={indexEntry?.bodySlots?.["article.after-meta"]}',
|
|
2816
|
+
' beforeContentHtml={indexEntry?.bodySlots?.["article.before-content"]}',
|
|
2817
|
+
' afterContentHtml={indexEntry?.bodySlots?.["article.after-content"]}',
|
|
2818
|
+
' asideHtml={indexEntry?.bodySlots?.["article.aside"]}',
|
|
2819
|
+
' footerHtml={indexEntry?.bodySlots?.["article.footer"]}'
|
|
2820
|
+
];
|
|
2821
|
+
if (hasToc) {
|
|
2822
|
+
propLines.push(
|
|
2823
|
+
' asideContent={<TableOfContents className="table-of-contents--desktop" items={tableOfContents} />}'
|
|
2824
|
+
);
|
|
2825
|
+
}
|
|
2826
|
+
const afterChildren = [];
|
|
2827
|
+
if (hasRecentPosts) afterChildren.push("<RecentPosts posts={recentPosts} />");
|
|
2828
|
+
if (hasDailyNotes) afterChildren.push("<DailyNotes notes={dailyNotes} />");
|
|
2829
|
+
if (afterChildren.length > 0) {
|
|
2830
|
+
propLines.push(` afterContent={<>${afterChildren.join("")}</>}`);
|
|
2831
|
+
}
|
|
2832
|
+
if (hasBacklinks) {
|
|
2833
|
+
propLines.push(
|
|
2834
|
+
" footerContent={<Backlinks backlinks={backlinks} />}"
|
|
2835
|
+
);
|
|
2836
|
+
}
|
|
2837
|
+
const titleHelper = needsTitle ? articleTitleHelper() : "";
|
|
2838
|
+
const dataBlock = dataLines.length > 0 ? `
|
|
2839
|
+
${dataLines.join("\n")}
|
|
2840
|
+
` : "";
|
|
2841
|
+
return `${importLines.join("\n")}
|
|
1704
2842
|
|
|
1705
2843
|
export default createRoute(async (c) => {
|
|
1706
2844
|
const manifest = await content.getManifest();
|
|
@@ -1710,10 +2848,10 @@ export default createRoute(async (c) => {
|
|
|
1710
2848
|
}
|
|
1711
2849
|
|
|
1712
2850
|
const post = await content.getProcessedContent("index");
|
|
1713
|
-
if (
|
|
2851
|
+
if (indexEntry?.publishing?.routable === false) {
|
|
1714
2852
|
return c.notFound();
|
|
1715
2853
|
}
|
|
1716
|
-
|
|
2854
|
+
${dataBlock}
|
|
1717
2855
|
if (indexEntry) {
|
|
1718
2856
|
c.set("htmlLanguage", indexEntry.publicLocation.metadata?.["l10n.lang"]);
|
|
1719
2857
|
c.set("headTags", indexEntry.headTags ?? []);
|
|
@@ -1722,11 +2860,10 @@ export default createRoute(async (c) => {
|
|
|
1722
2860
|
return c.render(
|
|
1723
2861
|
<SiteArticle
|
|
1724
2862
|
post={post}
|
|
1725
|
-
|
|
2863
|
+
${propLines.join("\n")}
|
|
1726
2864
|
/>,
|
|
1727
2865
|
);
|
|
1728
|
-
})
|
|
1729
|
-
`;
|
|
2866
|
+
});${titleHelper}`;
|
|
1730
2867
|
}
|
|
1731
2868
|
function staticIndexRoute() {
|
|
1732
2869
|
return `import { createRoute } from "honox/factory";
|
|
@@ -1742,23 +2879,98 @@ export default createRoute((c) =>
|
|
|
1742
2879
|
);
|
|
1743
2880
|
`;
|
|
1744
2881
|
}
|
|
1745
|
-
function slugRoute() {
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
2882
|
+
function slugRoute(preset) {
|
|
2883
|
+
const hasToc = hasScaffoldPlugin(preset, "@riebeckite/plugin-toc");
|
|
2884
|
+
const hasBacklinks = hasScaffoldPlugin(
|
|
2885
|
+
preset,
|
|
2886
|
+
"@riebeckite/plugin-backlinks"
|
|
2887
|
+
);
|
|
2888
|
+
const hasLocalGraph = hasScaffoldPlugin(
|
|
2889
|
+
preset,
|
|
2890
|
+
"@riebeckite/plugin-local-graph"
|
|
2891
|
+
);
|
|
2892
|
+
const needsTitle = hasBacklinks || hasLocalGraph;
|
|
2893
|
+
const needsConfig = hasBacklinks || hasLocalGraph;
|
|
2894
|
+
const needsManifest = hasBacklinks || hasLocalGraph;
|
|
2895
|
+
const importLines = [
|
|
2896
|
+
"import {",
|
|
2897
|
+
" contentRouteSsgParams,",
|
|
2898
|
+
" pluginPageSsgParams,",
|
|
2899
|
+
" resolveRiebeckiteRoute,",
|
|
2900
|
+
'} from "@riebeckite/honox/server";'
|
|
2901
|
+
];
|
|
2902
|
+
if (hasBacklinks) {
|
|
2903
|
+
importLines.push(
|
|
2904
|
+
'import { Backlinks, getPublishedBacklinks } from "@riebeckite/plugin-backlinks";'
|
|
2905
|
+
);
|
|
2906
|
+
}
|
|
2907
|
+
if (hasLocalGraph) {
|
|
2908
|
+
importLines.push(
|
|
2909
|
+
'import { LocalGraph, getLocalGraph } from "@riebeckite/plugin-local-graph";'
|
|
2910
|
+
);
|
|
2911
|
+
}
|
|
2912
|
+
if (hasToc) {
|
|
2913
|
+
importLines.push(
|
|
2914
|
+
'import { extractTableOfContents, TableOfContents } from "@riebeckite/plugin-toc";'
|
|
2915
|
+
);
|
|
2916
|
+
}
|
|
2917
|
+
importLines.push('import { createRoute } from "honox/factory";');
|
|
2918
|
+
importLines.push('import { SiteArticle } from "../components/article";');
|
|
2919
|
+
if (needsConfig) importLines.push('import { config } from "../config";');
|
|
2920
|
+
importLines.push('import { content } from "../content";');
|
|
2921
|
+
const dataLines = [];
|
|
2922
|
+
if (needsManifest) {
|
|
2923
|
+
dataLines.push(" const manifest = await content.getManifest();");
|
|
2924
|
+
}
|
|
2925
|
+
if (hasBacklinks) {
|
|
2926
|
+
dataLines.push(
|
|
2927
|
+
" const backlinks = getPublishedBacklinks({ manifest, config, slug: route.entry.slug, resolveTitle: getArticleTitle });"
|
|
2928
|
+
);
|
|
2929
|
+
}
|
|
2930
|
+
if (hasLocalGraph) {
|
|
2931
|
+
dataLines.push(
|
|
2932
|
+
" const localGraph = getLocalGraph({ manifest, config, slug: route.entry.slug, resolveTitle: getArticleTitle });"
|
|
2933
|
+
);
|
|
2934
|
+
}
|
|
2935
|
+
if (hasToc) {
|
|
2936
|
+
dataLines.push(
|
|
2937
|
+
' const tableOfContents = extractTableOfContents(post.html ?? "");'
|
|
2938
|
+
);
|
|
2939
|
+
}
|
|
2940
|
+
const propLines = [
|
|
2941
|
+
" propertiesHtml={route.entry.bodySlots?.properties}",
|
|
2942
|
+
' afterHeaderHtml={route.entry.bodySlots?.["article.after-header"]}',
|
|
2943
|
+
' afterMetaHtml={route.entry.bodySlots?.["article.after-meta"]}',
|
|
2944
|
+
' beforeContentHtml={route.entry.bodySlots?.["article.before-content"]}',
|
|
2945
|
+
' afterContentHtml={route.entry.bodySlots?.["article.after-content"]}',
|
|
2946
|
+
' asideHtml={route.entry.bodySlots?.["article.aside"]}',
|
|
2947
|
+
' footerHtml={route.entry.bodySlots?.["article.footer"]}'
|
|
2948
|
+
];
|
|
2949
|
+
if (hasToc) {
|
|
2950
|
+
propLines.push(
|
|
2951
|
+
' asideContent={<TableOfContents className="table-of-contents--desktop" items={tableOfContents} />}'
|
|
2952
|
+
);
|
|
2953
|
+
}
|
|
2954
|
+
const footerChildren = [];
|
|
2955
|
+
if (hasLocalGraph) {
|
|
2956
|
+
footerChildren.push("{localGraph && <LocalGraph graph={localGraph} />}");
|
|
2957
|
+
}
|
|
2958
|
+
if (hasBacklinks) {
|
|
2959
|
+
footerChildren.push("<Backlinks backlinks={backlinks} />");
|
|
2960
|
+
}
|
|
2961
|
+
if (footerChildren.length > 0) {
|
|
2962
|
+
propLines.push(` footerContent={<>${footerChildren.join("")}</>}`);
|
|
2963
|
+
}
|
|
2964
|
+
const titleHelper = needsTitle ? articleTitleHelper() : "";
|
|
2965
|
+
const dataBlock = dataLines.length > 0 ? `
|
|
2966
|
+
${dataLines.join("\n")}
|
|
2967
|
+
` : "";
|
|
2968
|
+
return `${importLines.join("\n")}
|
|
1756
2969
|
|
|
1757
2970
|
export default createRoute(
|
|
1758
2971
|
contentRouteSsgParams("/:slug{.+}", async () => {
|
|
1759
2972
|
const manifest = await content.getManifest();
|
|
1760
|
-
const contentPaths = manifest.
|
|
1761
|
-
.filter((entry) => isPublished(config, entry.frontmatter))
|
|
2973
|
+
const contentPaths = manifest.publicEntries
|
|
1762
2974
|
.filter((entry) => entry.permalink !== "/")
|
|
1763
2975
|
.map((entry) => ({ slug: entry.permalink.replace(/^\\/+/, "") }));
|
|
1764
2976
|
return [...contentPaths, ...(await pluginPageSsgParams(content))];
|
|
@@ -1780,22 +2992,18 @@ export default createRoute(
|
|
|
1780
2992
|
}
|
|
1781
2993
|
|
|
1782
2994
|
const post = await content.getProcessedContent(route.entry.slug);
|
|
1783
|
-
|
|
1784
|
-
return c.notFound();
|
|
1785
|
-
}
|
|
1786
|
-
|
|
2995
|
+
${dataBlock}
|
|
1787
2996
|
c.set("htmlLanguage", route.entry.publicLocation.metadata?.["l10n.lang"]);
|
|
1788
2997
|
c.set("headTags", route.entry.headTags ?? []);
|
|
1789
2998
|
|
|
1790
2999
|
return c.render(
|
|
1791
3000
|
<SiteArticle
|
|
1792
3001
|
post={post}
|
|
1793
|
-
|
|
3002
|
+
${propLines.join("\n")}
|
|
1794
3003
|
/>,
|
|
1795
3004
|
);
|
|
1796
3005
|
},
|
|
1797
|
-
)
|
|
1798
|
-
`;
|
|
3006
|
+
);${titleHelper}`;
|
|
1799
3007
|
}
|
|
1800
3008
|
|
|
1801
3009
|
// src/scaffold/localized-content.ts
|
|
@@ -2160,7 +3368,7 @@ function localizedContentFiles(variables, preset) {
|
|
|
2160
3368
|
(language) => themesContent(language, preset)
|
|
2161
3369
|
);
|
|
2162
3370
|
} else if (page === "guide") {
|
|
2163
|
-
pushEnglishOnly("guide", guideContent);
|
|
3371
|
+
pushEnglishOnly("guide", () => guideContent(preset));
|
|
2164
3372
|
} else if (page === "examples") {
|
|
2165
3373
|
pushLocalized(
|
|
2166
3374
|
"examples",
|
|
@@ -2354,10 +3562,50 @@ var INDEX = {
|
|
|
2354
3562
|
de: "Übersetzte Seiten folgen der Konvention `<base>.<lang>.md` neben der Standarddatei (z. B. `about.ja.md`). Das l10n-Plugin liefert sie unter `/lang/`-Pfaden und verlinkt sie automatisch.",
|
|
2355
3563
|
fr: "Les pages localisées suivent la convention `<base>.<lang>.md` à côté du fichier par défaut (ex. `about.ja.md`). Le plugin l10n les sert sous des chemins `/lang/` et les relie automatiquement.",
|
|
2356
3564
|
ko: "번역 페이지는 기본 파일 옆에 `<base>.<lang>.md` 규칙으로 둡니다(예: `about.ja.md`). l10n 플러그인이 `/lang/` 경로로 서빙하며 자동으로 링크합니다."
|
|
3565
|
+
},
|
|
3566
|
+
editFileNote: {
|
|
3567
|
+
en: "This page is `content/index.md`. Open that file, change it, and save — the browser updates while you write.",
|
|
3568
|
+
ja: "このページは `content/index.md` です。ファイルを開いて書き換えると、書いている途中でもブラウザの表示が更新されます。",
|
|
3569
|
+
"zh-CN": "这个页面来自 `content/index.md`。打开这个文件修改并保存,浏览器会随你的输入更新。",
|
|
3570
|
+
es: "Esta página viene de `content/index.md`. Abre ese archivo, edítalo y guarda: el navegador se actualiza mientras escribes.",
|
|
3571
|
+
de: "Diese Seite stammt aus `content/index.md`. Öffne die Datei, ändere sie und speichere — der Browser aktualisiert sich beim Schreiben.",
|
|
3572
|
+
fr: "Cette page provient de `content/index.md`. Ouvrez ce fichier, modifiez-le et enregistrez — le navigateur se met à jour pendant que vous écrivez.",
|
|
3573
|
+
ko: "이 페이지는 `content/index.md`에서 만들어집니다. 그 파일을 열어 수정하고 저장하면 글을 쓰는 동안 브라우저가 갱신됩니다."
|
|
3574
|
+
},
|
|
3575
|
+
linkLine: {
|
|
3576
|
+
en: "Link pages with a WikiLink, for example `[[guide]]`.",
|
|
3577
|
+
ja: "ページ同士は WikiLink でリンクできます(例:`[[guide]]`)。",
|
|
3578
|
+
"zh-CN": "用 WikiLink 连接页面,例如 `[[guide]]`。",
|
|
3579
|
+
es: "Enlaza páginas con un WikiLink, por ejemplo `[[guide]]`.",
|
|
3580
|
+
de: "Verbinde Seiten mit einem WikiLink, zum Beispiel `[[guide]]`.",
|
|
3581
|
+
fr: "Reliez les pages avec un WikiLink, par exemple `[[guide]]`.",
|
|
3582
|
+
ko: "WikiLink로 페이지를 연결하세요. 예: `[[guide]]`."
|
|
2357
3583
|
}
|
|
2358
3584
|
};
|
|
3585
|
+
function starterIndexContent(title, language) {
|
|
3586
|
+
return [
|
|
3587
|
+
frontmatter(),
|
|
3588
|
+
heading(1, title),
|
|
3589
|
+
"",
|
|
3590
|
+
read(INDEX.lead, language),
|
|
3591
|
+
"",
|
|
3592
|
+
read(INDEX.editFileNote, language),
|
|
3593
|
+
"",
|
|
3594
|
+
heading(2, read(INDEX.editHeading, language)),
|
|
3595
|
+
read(INDEX.editLead, language),
|
|
3596
|
+
"",
|
|
3597
|
+
read(INDEX.linkLine, language),
|
|
3598
|
+
"",
|
|
3599
|
+
heading(2, read(INDEX.exploreHeading, language)),
|
|
3600
|
+
"",
|
|
3601
|
+
"- [Getting started](/guide)",
|
|
3602
|
+
"- [[examples]]",
|
|
3603
|
+
""
|
|
3604
|
+
].join("\n");
|
|
3605
|
+
}
|
|
2359
3606
|
function indexContent(variables, preset, language) {
|
|
2360
3607
|
const title = variables.title;
|
|
3608
|
+
if (preset.name === "starter") return starterIndexContent(title, language);
|
|
2361
3609
|
return [
|
|
2362
3610
|
frontmatter(),
|
|
2363
3611
|
heading(1, title),
|
|
@@ -2371,10 +3619,6 @@ function indexContent(variables, preset, language) {
|
|
|
2371
3619
|
`- [${read(INDEX.exploreThemes, language)}](/framework/themes)`,
|
|
2372
3620
|
"- [Working examples](/examples/)",
|
|
2373
3621
|
"- [Plugin reference](/reference/plugins/)"
|
|
2374
|
-
] : preset.name === "starter" ? [
|
|
2375
|
-
heading(2, read(INDEX.exploreHeading, language)),
|
|
2376
|
-
"- [Getting started](/guide/)",
|
|
2377
|
-
"- [Example notes](/examples/)"
|
|
2378
3622
|
] : [],
|
|
2379
3623
|
heading(2, read(INDEX.editHeading, language)),
|
|
2380
3624
|
read(INDEX.editLead, language),
|
|
@@ -2426,7 +3670,7 @@ var THEMES_COPY = {
|
|
|
2426
3670
|
es: "Este starter usa `@riebeckite/theme-default`. Para probar otro tema:",
|
|
2427
3671
|
de: "Dieser Starter verwendet `@riebeckite/theme-default`. Um ein anderes Theme zu testen:",
|
|
2428
3672
|
fr: "Ce starter utilise `@riebeckite/theme-default`. Pour essayer un autre thème :",
|
|
2429
|
-
ko: "이 스타터는 `@riebeckite/theme-default`를 사용합니다. 다른 테마를
|
|
3673
|
+
ko: "이 스타터는 `@riebeckite/theme-default`를 사용합니다. 다른 테마를 시도하려면:"
|
|
2430
3674
|
},
|
|
2431
3675
|
switchStep1: {
|
|
2432
3676
|
en: "Install the package:",
|
|
@@ -2435,7 +3679,7 @@ var THEMES_COPY = {
|
|
|
2435
3679
|
es: "Instala el paquete:",
|
|
2436
3680
|
de: "Installiere das Paket:",
|
|
2437
3681
|
fr: "Installez le paquet :",
|
|
2438
|
-
ko: "패키지를
|
|
3682
|
+
ko: "패키지를 설치합니다:"
|
|
2439
3683
|
},
|
|
2440
3684
|
switchStep2: {
|
|
2441
3685
|
en: "Point `theme` at the new factory in `riebeckite.config.ts`:",
|
|
@@ -2444,7 +3688,7 @@ var THEMES_COPY = {
|
|
|
2444
3688
|
es: "Apunta `theme` al nuevo factory en `riebeckite.config.ts`:",
|
|
2445
3689
|
de: "Setze `theme` in `riebeckite.config.ts` auf den neuen Factory:",
|
|
2446
3690
|
fr: "Pointez `theme` vers le nouveau factory dans `riebeckite.config.ts` :",
|
|
2447
|
-
ko: "`riebeckite.config.ts`의 `theme`를 새 팩토리로
|
|
3691
|
+
ko: "`riebeckite.config.ts`의 `theme`를 새 팩토리로 변경합니다:"
|
|
2448
3692
|
},
|
|
2449
3693
|
tableHeading: {
|
|
2450
3694
|
en: "The bundled themes",
|
|
@@ -2664,7 +3908,7 @@ var PLUGINS_COPY = {
|
|
|
2664
3908
|
es: "Instala el paquete:",
|
|
2665
3909
|
de: "Installiere das Paket:",
|
|
2666
3910
|
fr: "Installez le paquet :",
|
|
2667
|
-
ko: "패키지를
|
|
3911
|
+
ko: "패키지를 설치합니다:"
|
|
2668
3912
|
},
|
|
2669
3913
|
installStep2: {
|
|
2670
3914
|
en: "Register it in the `plugins` array of `riebeckite.config.ts`:",
|
|
@@ -2673,7 +3917,7 @@ var PLUGINS_COPY = {
|
|
|
2673
3917
|
es: "Regístralo en el array `plugins` de `riebeckite.config.ts`:",
|
|
2674
3918
|
de: "Registriere es im `plugins`-Array der `riebeckite.config.ts`:",
|
|
2675
3919
|
fr: "Enregistrez-le dans le tableau `plugins` de `riebeckite.config.ts` :",
|
|
2676
|
-
ko: "`riebeckite.config.ts`의 `plugins` 배열에
|
|
3920
|
+
ko: "`riebeckite.config.ts`의 `plugins` 배열에 등록합니다:"
|
|
2677
3921
|
},
|
|
2678
3922
|
fullList: {
|
|
2679
3923
|
en: "The complete plugin index lives in the repository:",
|
|
@@ -2682,7 +3926,7 @@ var PLUGINS_COPY = {
|
|
|
2682
3926
|
es: "El índice completo de plugins vive en el repositorio:",
|
|
2683
3927
|
de: "Der vollständige Plugin-Index liegt im Repository:",
|
|
2684
3928
|
fr: "L'index complet des plugins se trouve dans le dépôt :",
|
|
2685
|
-
ko: "전체 플러그인 목록은 저장소에
|
|
3929
|
+
ko: "전체 플러그인 목록은 저장소에 있습니다:"
|
|
2686
3930
|
},
|
|
2687
3931
|
fullListLabel: {
|
|
2688
3932
|
en: "Riebeckite plugins on GitHub",
|
|
@@ -3188,7 +4432,65 @@ function pluginsContent(language) {
|
|
|
3188
4432
|
lines.push("");
|
|
3189
4433
|
return lines.join("\n");
|
|
3190
4434
|
}
|
|
3191
|
-
function
|
|
4435
|
+
function starterGuideContent() {
|
|
4436
|
+
return [
|
|
4437
|
+
frontmatter(),
|
|
4438
|
+
heading(1, "Getting started"),
|
|
4439
|
+
"",
|
|
4440
|
+
"This site was generated from a Riebeckite scaffold preset.",
|
|
4441
|
+
"Everything below lives in this repository, ready to edit.",
|
|
4442
|
+
"",
|
|
4443
|
+
heading(2, "Run the site"),
|
|
4444
|
+
"",
|
|
4445
|
+
codeBlock("sh", ["npm install", "npm exec riebeckite dev"].join("\n")),
|
|
4446
|
+
"",
|
|
4447
|
+
"Open the URL printed in the terminal. The page reloads every time",
|
|
4448
|
+
"you save a Markdown file.",
|
|
4449
|
+
"",
|
|
4450
|
+
heading(2, "Add a page"),
|
|
4451
|
+
"",
|
|
4452
|
+
"Create `content/hello.md`:",
|
|
4453
|
+
"",
|
|
4454
|
+
codeBlock(
|
|
4455
|
+
"md",
|
|
4456
|
+
[
|
|
4457
|
+
"---",
|
|
4458
|
+
"publish: true",
|
|
4459
|
+
"---",
|
|
4460
|
+
"",
|
|
4461
|
+
"# Hello",
|
|
4462
|
+
"",
|
|
4463
|
+
"This is my second page."
|
|
4464
|
+
].join("\n")
|
|
4465
|
+
),
|
|
4466
|
+
"",
|
|
4467
|
+
"The dev server serves it at `/hello`.",
|
|
4468
|
+
"",
|
|
4469
|
+
heading(2, "Link the pages"),
|
|
4470
|
+
"",
|
|
4471
|
+
"Write `[[hello]]` anywhere and it becomes a link to that page.",
|
|
4472
|
+
"",
|
|
4473
|
+
heading(2, "Translate a page"),
|
|
4474
|
+
"",
|
|
4475
|
+
"Copy a file next to the original with the language suffix, for",
|
|
4476
|
+
"example `hello.ja.md`. The language switcher picks it up.",
|
|
4477
|
+
"",
|
|
4478
|
+
heading(2, "Build"),
|
|
4479
|
+
"",
|
|
4480
|
+
codeBlock("sh", "npm exec riebeckite build"),
|
|
4481
|
+
"",
|
|
4482
|
+
"The static site is written to `dist/`.",
|
|
4483
|
+
"",
|
|
4484
|
+
heading(2, "Extend"),
|
|
4485
|
+
"",
|
|
4486
|
+
"Plugins and themes are registered in `riebeckite.config.ts`.",
|
|
4487
|
+
"Install a package, import its factory, and add it to the `plugins`",
|
|
4488
|
+
"array, or point `theme` at a new theme factory.",
|
|
4489
|
+
""
|
|
4490
|
+
].join("\n");
|
|
4491
|
+
}
|
|
4492
|
+
function guideContent(preset) {
|
|
4493
|
+
if (preset.name !== "showcase") return starterGuideContent();
|
|
3192
4494
|
return [
|
|
3193
4495
|
frontmatter(),
|
|
3194
4496
|
heading(1, "Getting started"),
|
|
@@ -3213,9 +4515,9 @@ function guideContent() {
|
|
|
3213
4515
|
codeBlock(
|
|
3214
4516
|
"sh",
|
|
3215
4517
|
[
|
|
3216
|
-
"
|
|
3217
|
-
"
|
|
3218
|
-
"
|
|
4518
|
+
"npm install",
|
|
4519
|
+
"npm exec riebeckite dev",
|
|
4520
|
+
"npm exec riebeckite build"
|
|
3219
4521
|
].join("\n")
|
|
3220
4522
|
),
|
|
3221
4523
|
"",
|
|
@@ -3253,7 +4555,77 @@ var SHOWCASE_GUIDE_DEMOS = [
|
|
|
3253
4555
|
"@riebeckite/plugin-canvas",
|
|
3254
4556
|
"@riebeckite/plugin-flashcards"
|
|
3255
4557
|
];
|
|
4558
|
+
var STARTER_EXAMPLES = {
|
|
4559
|
+
heading: {
|
|
4560
|
+
en: "Example",
|
|
4561
|
+
ja: "サンプル"
|
|
4562
|
+
},
|
|
4563
|
+
intro: {
|
|
4564
|
+
en: "A small page showing what Riebeckite Markdown can do. Copy any section into your own files.",
|
|
4565
|
+
ja: "Riebeckite の Markdown でできることをまとめた小さなページです。必要なセクションを自分のファイルにコピーできます。"
|
|
4566
|
+
},
|
|
4567
|
+
linksHeading: {
|
|
4568
|
+
en: "Links",
|
|
4569
|
+
ja: "リンク"
|
|
4570
|
+
},
|
|
4571
|
+
linksBody: {
|
|
4572
|
+
en: "WikiLinks connect pages: [[guide]] opens Getting started. Regular Markdown links work as well — [Home](/) returns to the home page.",
|
|
4573
|
+
ja: "WikiLink でページをつなげます:[[guide]] は Getting started を開きます。通常の Markdown リンクも使えます — [Home](/) でホームに戻れます。"
|
|
4574
|
+
},
|
|
4575
|
+
codeHeading: {
|
|
4576
|
+
en: "Code",
|
|
4577
|
+
ja: "コード"
|
|
4578
|
+
},
|
|
4579
|
+
codeIntro: {
|
|
4580
|
+
en: "A fenced code block keeps its formatting and gains a toolbar from the code plugins:",
|
|
4581
|
+
ja: "フェンス付きコードブロックは書式を保ったまま表示され、コード系プラグインでツールバーが付きます:"
|
|
4582
|
+
},
|
|
4583
|
+
frontmatterHeading: {
|
|
4584
|
+
en: "Frontmatter",
|
|
4585
|
+
ja: "フロントマター"
|
|
4586
|
+
},
|
|
4587
|
+
frontmatterBody: {
|
|
4588
|
+
en: "The block between the `---` lines at the top of a file controls the page. `publish: true` keeps it in the built site:",
|
|
4589
|
+
ja: "ファイル先頭の `---` に挟まれた部分がページを制御します。`publish: true` を書き続けるとビルド対象に残ります:"
|
|
4590
|
+
}
|
|
4591
|
+
};
|
|
4592
|
+
function starterExamplesContent(language) {
|
|
4593
|
+
return [
|
|
4594
|
+
frontmatter(),
|
|
4595
|
+
heading(1, readSummary(STARTER_EXAMPLES.heading, language)),
|
|
4596
|
+
"",
|
|
4597
|
+
readSummary(STARTER_EXAMPLES.intro, language),
|
|
4598
|
+
"",
|
|
4599
|
+
heading(2, readSummary(STARTER_EXAMPLES.linksHeading, language)),
|
|
4600
|
+
"",
|
|
4601
|
+
readSummary(STARTER_EXAMPLES.linksBody, language),
|
|
4602
|
+
"",
|
|
4603
|
+
heading(2, readSummary(STARTER_EXAMPLES.codeHeading, language)),
|
|
4604
|
+
"",
|
|
4605
|
+
readSummary(STARTER_EXAMPLES.codeIntro, language),
|
|
4606
|
+
"",
|
|
4607
|
+
codeBlock(
|
|
4608
|
+
"ts",
|
|
4609
|
+
[
|
|
4610
|
+
"export function hello(): string {",
|
|
4611
|
+
' return "Hello from Riebeckite";',
|
|
4612
|
+
"}"
|
|
4613
|
+
].join("\n")
|
|
4614
|
+
),
|
|
4615
|
+
"",
|
|
4616
|
+
heading(2, readSummary(STARTER_EXAMPLES.frontmatterHeading, language)),
|
|
4617
|
+
"",
|
|
4618
|
+
readSummary(STARTER_EXAMPLES.frontmatterBody, language),
|
|
4619
|
+
"",
|
|
4620
|
+
codeBlock(
|
|
4621
|
+
"yaml",
|
|
4622
|
+
["---", "publish: true", "title: Example", "---"].join("\n")
|
|
4623
|
+
),
|
|
4624
|
+
""
|
|
4625
|
+
].join("\n");
|
|
4626
|
+
}
|
|
3256
4627
|
function examplesContent(preset, language) {
|
|
4628
|
+
if (preset.name === "starter") return starterExamplesContent(language);
|
|
3257
4629
|
const lines = [
|
|
3258
4630
|
frontmatter(),
|
|
3259
4631
|
heading(1, readSummary(EXAMPLES_COPY.heading, language)),
|
|
@@ -3308,7 +4680,7 @@ function referencePluginsContent(preset, variables) {
|
|
|
3308
4680
|
"Every registered plugin is listed below with the Markdown that triggers it,",
|
|
3309
4681
|
"or with the Markdown/frontmatter/content shape it reads when the plugin is",
|
|
3310
4682
|
"site-wide rather than block-based. Paste the examples into files under",
|
|
3311
|
-
"`content/` and run `
|
|
4683
|
+
"`content/` and run `npm exec riebeckite dev` to inspect the rendered result.",
|
|
3312
4684
|
""
|
|
3313
4685
|
);
|
|
3314
4686
|
for (const plugin of preset.plugins) {
|
|
@@ -3373,12 +4745,12 @@ function referenceThemesContent(preset) {
|
|
|
3373
4745
|
}
|
|
3374
4746
|
|
|
3375
4747
|
// src/scaffold/version.ts
|
|
3376
|
-
var RIEBECKITE_VERSION = "^0.0.
|
|
4748
|
+
var RIEBECKITE_VERSION = "^0.0.16";
|
|
3377
4749
|
|
|
3378
4750
|
// src/scaffold/templates.ts
|
|
3379
|
-
function siteTemplateFiles(preset, variables) {
|
|
4751
|
+
function siteTemplateFiles(preset, variables, options = {}) {
|
|
3380
4752
|
return [
|
|
3381
|
-
{ path: "package.json", content: packageJson(preset, variables) },
|
|
4753
|
+
{ path: "package.json", content: packageJson(preset, variables, options) },
|
|
3382
4754
|
{
|
|
3383
4755
|
path: "riebeckite.config.ts",
|
|
3384
4756
|
content: riebeckiteConfig(preset, variables)
|
|
@@ -3403,7 +4775,7 @@ function readPackageAsset(fileName) {
|
|
|
3403
4775
|
new URL(`../assets/${fileName}`, import.meta.url)
|
|
3404
4776
|
];
|
|
3405
4777
|
for (const candidate of candidates) {
|
|
3406
|
-
if (
|
|
4778
|
+
if (fs7.existsSync(candidate)) return fs7.readFileSync(candidate);
|
|
3407
4779
|
}
|
|
3408
4780
|
throw new Error(`Missing Riebeckite scaffold asset: ${fileName}`);
|
|
3409
4781
|
}
|
|
@@ -3432,7 +4804,7 @@ function appFiles(preset) {
|
|
|
3432
4804
|
style: () => style(preset),
|
|
3433
4805
|
renderer: () => renderer(preset),
|
|
3434
4806
|
index: () => indexRoute(preset),
|
|
3435
|
-
slug: () => slugRoute(),
|
|
4807
|
+
slug: () => slugRoute(preset),
|
|
3436
4808
|
header: () => siteHeader(),
|
|
3437
4809
|
article: () => article()
|
|
3438
4810
|
};
|
|
@@ -3459,7 +4831,7 @@ function appFiles(preset) {
|
|
|
3459
4831
|
...has("article") ? [{ path: "app/components/article.tsx", content: get("article") }] : []
|
|
3460
4832
|
];
|
|
3461
4833
|
}
|
|
3462
|
-
function packageJson(preset, variables) {
|
|
4834
|
+
function packageJson(preset, variables, options) {
|
|
3463
4835
|
const dependencies = {
|
|
3464
4836
|
"@riebeckite/core": RIEBECKITE_VERSION,
|
|
3465
4837
|
"@riebeckite/honox": RIEBECKITE_VERSION,
|
|
@@ -3491,7 +4863,8 @@ function packageJson(preset, variables) {
|
|
|
3491
4863
|
"@riebeckite/cli": RIEBECKITE_VERSION,
|
|
3492
4864
|
"@types/node": "^24.5.2",
|
|
3493
4865
|
typescript: "^5.0.0",
|
|
3494
|
-
vite: "^8.0.9"
|
|
4866
|
+
vite: "^8.0.9",
|
|
4867
|
+
...options.cloudflareWorkers ? { wrangler: WRANGLER_VERSION } : {}
|
|
3495
4868
|
}
|
|
3496
4869
|
},
|
|
3497
4870
|
null,
|
|
@@ -3521,6 +4894,7 @@ function riebeckiteConfig(preset, variables) {
|
|
|
3521
4894
|
lines.push(" content: {");
|
|
3522
4895
|
lines.push(' directory: "content",');
|
|
3523
4896
|
lines.push(" },");
|
|
4897
|
+
for (const line of navigationConfig(preset)) lines.push(line);
|
|
3524
4898
|
if (preset.theme) {
|
|
3525
4899
|
const theme = preset.theme;
|
|
3526
4900
|
lines.push(
|
|
@@ -3536,6 +4910,25 @@ function riebeckiteConfig(preset, variables) {
|
|
|
3536
4910
|
return `${lines.join("\n")}
|
|
3537
4911
|
`;
|
|
3538
4912
|
}
|
|
4913
|
+
function navigationConfig(preset) {
|
|
4914
|
+
if (preset.name === "starter") {
|
|
4915
|
+
return [
|
|
4916
|
+
" navigation: {",
|
|
4917
|
+
' header: [{ label: "Guide", href: "/guide" }, { label: "Examples", href: "/examples" }, { label: "Notes", href: "/notes/planning", children: [{ label: "Planning", href: "/notes/planning" }, { label: "Writing", href: "/notes/writing" }] }],',
|
|
4918
|
+
' footer: [{ label: "Guide", href: "/guide" }, { label: "Examples", href: "/examples" }],',
|
|
4919
|
+
" },"
|
|
4920
|
+
];
|
|
4921
|
+
}
|
|
4922
|
+
if (preset.name === "showcase") {
|
|
4923
|
+
return [
|
|
4924
|
+
" navigation: {",
|
|
4925
|
+
' header: [{ label: "Guide", href: "/guide" }, { label: "Examples", href: "/examples" }, { label: "Framework", href: "/framework/plugins", children: [{ label: "Plugins", href: "/framework/plugins" }, { label: "Themes", href: "/framework/themes" }] }],',
|
|
4926
|
+
' footer: [{ label: "Guide", href: "/guide" }, { label: "Examples", href: "/examples" }],',
|
|
4927
|
+
" },"
|
|
4928
|
+
];
|
|
4929
|
+
}
|
|
4930
|
+
return [];
|
|
4931
|
+
}
|
|
3539
4932
|
var OPTION_DEPTH = {
|
|
3540
4933
|
empty: 0,
|
|
3541
4934
|
minimal: 0,
|
|
@@ -3688,7 +5081,7 @@ var README_WORDS = {
|
|
|
3688
5081
|
demoPages: "Try the demos",
|
|
3689
5082
|
demoPagesBody: "The generated site ships content pages that exercise these features:",
|
|
3690
5083
|
copyDemos: "Copy-paste demos",
|
|
3691
|
-
copyDemosBody: "Paste any of these snippets into a Markdown file under `content/` and run `
|
|
5084
|
+
copyDemosBody: "Paste any of these snippets into a Markdown file under `content/` and run `npm exec riebeckite dev`. Each one renders through a plugin this preset registers."
|
|
3692
5085
|
},
|
|
3693
5086
|
ja: {
|
|
3694
5087
|
included: "含まれているもの",
|
|
@@ -3712,7 +5105,7 @@ var README_WORDS = {
|
|
|
3712
5105
|
demoPages: "デモを試す",
|
|
3713
5106
|
demoPagesBody: "生成されたサイトには、各機能を体験できるコンテンツページが含まれています:",
|
|
3714
5107
|
copyDemos: "コピーして使えるデモ",
|
|
3715
|
-
copyDemosBody: "以下のスニペットを `content/` 配下の Markdown ファイルに貼り付けて `
|
|
5108
|
+
copyDemosBody: "以下のスニペットを `content/` 配下の Markdown ファイルに貼り付けて `npm exec riebeckite dev` を実行してください。それぞれ、このプリセットが登録しているプラグインでレンダリングされます。"
|
|
3716
5109
|
}
|
|
3717
5110
|
};
|
|
3718
5111
|
function readme(preset, variables) {
|
|
@@ -3745,13 +5138,23 @@ function readmeEn(preset, variables) {
|
|
|
3745
5138
|
lines.push(`## ${words.commands}`, "");
|
|
3746
5139
|
lines.push(
|
|
3747
5140
|
"```sh",
|
|
3748
|
-
"
|
|
3749
|
-
"
|
|
3750
|
-
"
|
|
3751
|
-
"
|
|
5141
|
+
"npm install",
|
|
5142
|
+
"npm exec riebeckite check",
|
|
5143
|
+
"npm exec riebeckite dev",
|
|
5144
|
+
"npm exec riebeckite build",
|
|
3752
5145
|
"```",
|
|
3753
5146
|
""
|
|
3754
5147
|
);
|
|
5148
|
+
if (preset.name !== "empty") {
|
|
5149
|
+
lines.push(
|
|
5150
|
+
"## First edits",
|
|
5151
|
+
"",
|
|
5152
|
+
"- `content/index.md`: the first published page.",
|
|
5153
|
+
"- `riebeckite.config.ts`: set `site.title`, `site.baseUrl`, and `content.directory`.",
|
|
5154
|
+
"- `public/favicon.ico`: replace the site icon when you are ready.",
|
|
5155
|
+
""
|
|
5156
|
+
);
|
|
5157
|
+
}
|
|
3755
5158
|
if (preset.readme === "rich") {
|
|
3756
5159
|
lines.push(...demoPagesLines(preset, variables, words, "en"));
|
|
3757
5160
|
lines.push(...copyPasteDemoLines(preset, words, "en"));
|
|
@@ -3793,13 +5196,23 @@ function readmeJa(preset, variables) {
|
|
|
3793
5196
|
lines.push(`## ${words.commands}`, "");
|
|
3794
5197
|
lines.push(
|
|
3795
5198
|
"```sh",
|
|
3796
|
-
"
|
|
3797
|
-
"
|
|
3798
|
-
"
|
|
3799
|
-
"
|
|
5199
|
+
"npm install",
|
|
5200
|
+
"npm exec riebeckite check",
|
|
5201
|
+
"npm exec riebeckite dev",
|
|
5202
|
+
"npm exec riebeckite build",
|
|
3800
5203
|
"```",
|
|
3801
5204
|
""
|
|
3802
5205
|
);
|
|
5206
|
+
if (preset.name !== "empty") {
|
|
5207
|
+
lines.push(
|
|
5208
|
+
"## 最初に編集する場所",
|
|
5209
|
+
"",
|
|
5210
|
+
"- `content/index.md`: 最初に公開されるページです。",
|
|
5211
|
+
"- `riebeckite.config.ts`: `site.title`、`site.baseUrl`、`content.directory` を設定します。",
|
|
5212
|
+
"- `public/favicon.ico`: 必要になったらサイトアイコンを置き換えます。",
|
|
5213
|
+
""
|
|
5214
|
+
);
|
|
5215
|
+
}
|
|
3803
5216
|
if (preset.readme === "rich") {
|
|
3804
5217
|
lines.push(...demoPagesLines(preset, variables, words, "ja"));
|
|
3805
5218
|
lines.push(...copyPasteDemoLines(preset, words, "ja"));
|
|
@@ -4263,12 +5676,18 @@ var ScaffoldSiteError = class extends Error {
|
|
|
4263
5676
|
}
|
|
4264
5677
|
};
|
|
4265
5678
|
async function scaffoldRiebeckiteSite(options) {
|
|
4266
|
-
const targetDirectory =
|
|
5679
|
+
const targetDirectory = path9.resolve(options.targetDirectory);
|
|
4267
5680
|
const preset = resolveScaffoldPreset(options.preset);
|
|
4268
5681
|
validateDeploymentOptions(options);
|
|
4269
5682
|
const files = [
|
|
4270
|
-
...siteTemplateFiles(preset, templateVariables(options, targetDirectory)
|
|
4271
|
-
|
|
5683
|
+
...siteTemplateFiles(preset, templateVariables(options, targetDirectory), {
|
|
5684
|
+
cloudflareWorkers: options.cloudflareWorkers
|
|
5685
|
+
}),
|
|
5686
|
+
...options.githubActions ? deploymentTemplateFiles(deploymentOptions(options)) : options.cloudflareWorkers ? [
|
|
5687
|
+
wranglerJsoncFile(
|
|
5688
|
+
workerNameFromDirectory(path9.basename(targetDirectory))
|
|
5689
|
+
)
|
|
5690
|
+
] : []
|
|
4272
5691
|
];
|
|
4273
5692
|
await assertTargetWritable(
|
|
4274
5693
|
targetDirectory,
|
|
@@ -4276,9 +5695,9 @@ async function scaffoldRiebeckiteSite(options) {
|
|
|
4276
5695
|
options.overwrite ?? false
|
|
4277
5696
|
);
|
|
4278
5697
|
for (const file of files) {
|
|
4279
|
-
const filePath =
|
|
4280
|
-
await
|
|
4281
|
-
await
|
|
5698
|
+
const filePath = path9.join(targetDirectory, file.path);
|
|
5699
|
+
await fs8.mkdir(path9.dirname(filePath), { recursive: true });
|
|
5700
|
+
await fs8.writeFile(filePath, file.content);
|
|
4282
5701
|
}
|
|
4283
5702
|
return { targetDirectory, files: files.map((file) => file.path) };
|
|
4284
5703
|
}
|
|
@@ -4314,7 +5733,7 @@ function validateDeploymentOptions(options) {
|
|
|
4314
5733
|
}
|
|
4315
5734
|
function templateVariables(options, targetDirectory) {
|
|
4316
5735
|
const name = normalizePackageName(
|
|
4317
|
-
options.name ??
|
|
5736
|
+
options.name ?? path9.basename(targetDirectory)
|
|
4318
5737
|
);
|
|
4319
5738
|
return {
|
|
4320
5739
|
name,
|
|
@@ -4327,9 +5746,9 @@ function templateVariables(options, targetDirectory) {
|
|
|
4327
5746
|
async function assertTargetWritable(targetDirectory, files, overwrite) {
|
|
4328
5747
|
let entries;
|
|
4329
5748
|
try {
|
|
4330
|
-
entries = await
|
|
5749
|
+
entries = await fs8.readdir(targetDirectory);
|
|
4331
5750
|
} catch (error) {
|
|
4332
|
-
if (
|
|
5751
|
+
if (isNotFoundError3(error)) return;
|
|
4333
5752
|
throw error;
|
|
4334
5753
|
}
|
|
4335
5754
|
const conflicts = entries.filter(
|
|
@@ -4347,30 +5766,54 @@ function normalizePackageName(value) {
|
|
|
4347
5766
|
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[-_.]+/, "").replace(/[-_.]+$/, "");
|
|
4348
5767
|
return normalized || "riebeckite-site";
|
|
4349
5768
|
}
|
|
4350
|
-
function
|
|
5769
|
+
function isNotFoundError3(error) {
|
|
4351
5770
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
4352
5771
|
}
|
|
4353
5772
|
|
|
4354
5773
|
// src/scaffold/next-steps.ts
|
|
4355
|
-
function formatScaffoldNextSteps(relativeDirectory) {
|
|
5774
|
+
function formatScaffoldNextSteps(relativeDirectory, options = {}) {
|
|
4356
5775
|
const lines = ["Next steps:"];
|
|
4357
5776
|
if (relativeDirectory !== ".") {
|
|
4358
5777
|
lines.push(` cd ${relativeDirectory}`);
|
|
4359
5778
|
}
|
|
4360
|
-
lines.push("
|
|
4361
|
-
lines.push("
|
|
4362
|
-
lines.push("
|
|
4363
|
-
lines.push("
|
|
5779
|
+
lines.push(" npm install");
|
|
5780
|
+
lines.push(" npm exec riebeckite check");
|
|
5781
|
+
lines.push(" npm exec riebeckite dev");
|
|
5782
|
+
lines.push(" npm exec riebeckite build");
|
|
5783
|
+
if (options.editFile !== void 0) {
|
|
5784
|
+
lines.push("");
|
|
5785
|
+
lines.push("Then edit:");
|
|
5786
|
+
lines.push(` ${options.editFile}`);
|
|
5787
|
+
lines.push("");
|
|
5788
|
+
lines.push("Common first settings:");
|
|
5789
|
+
lines.push(" riebeckite.config.ts: site.title, site.baseUrl, content.directory");
|
|
5790
|
+
}
|
|
5791
|
+
if (options.externalContent === true) {
|
|
5792
|
+
lines.push("");
|
|
5793
|
+
lines.push("Operational content lives in the content repository. CI checks it out to content/.");
|
|
5794
|
+
lines.push(
|
|
5795
|
+
"Copy github/notify-site.yml to the content repository as .github/workflows/notify-site.yml."
|
|
5796
|
+
);
|
|
5797
|
+
}
|
|
4364
5798
|
return lines.join("\n");
|
|
4365
5799
|
}
|
|
4366
5800
|
export {
|
|
5801
|
+
DEFAULT_WORKER_NAME,
|
|
5802
|
+
GITHUB_ACTIONS_SECRETS,
|
|
5803
|
+
PACKAGE_MANAGER,
|
|
4367
5804
|
SCAFFOLD_DEFAULT_PRESET,
|
|
4368
5805
|
SCAFFOLD_PRESET_NAMES,
|
|
4369
5806
|
ScaffoldSiteError,
|
|
5807
|
+
WORKER_NAME_MAX_LENGTH,
|
|
5808
|
+
WRANGLER_DEFAULTS,
|
|
5809
|
+
WRANGLER_VERSION,
|
|
5810
|
+
assertGitHubRepository,
|
|
5811
|
+
buildDefaultWranglerConfig,
|
|
4370
5812
|
buildHonoxApplication,
|
|
4371
5813
|
createRiebeckiteSsg,
|
|
4372
5814
|
defaultSsgEntry,
|
|
4373
5815
|
defaultSsrExternals,
|
|
5816
|
+
deploymentTemplateFiles,
|
|
4374
5817
|
empty,
|
|
4375
5818
|
formatScaffoldNextSteps,
|
|
4376
5819
|
isScaffoldPresetName,
|
|
@@ -4389,5 +5832,6 @@ export {
|
|
|
4389
5832
|
scaffoldRiebeckiteSite,
|
|
4390
5833
|
showcase,
|
|
4391
5834
|
startHonoxDevServer,
|
|
4392
|
-
starter
|
|
5835
|
+
starter,
|
|
5836
|
+
workerNameFromDirectory
|
|
4393
5837
|
};
|