@riebeckite/honox 0.0.13 → 0.0.15
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 +1 -1
- package/dist/index.js +1324 -148
- package/dist/server.d.ts +1 -1
- package/dist/server.js +24 -1
- 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/next-steps.d.ts +7 -1
- package/dist/src/scaffold/version.d.ts +1 -1
- package/dist/src/scaffold/wrangler-defaults.d.ts +54 -0
- package/dist/src/ssg_plugin.d.ts +16 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -255,11 +255,41 @@ function isJavaScriptModule(filePath) {
|
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
// src/scaffold/index.ts
|
|
258
|
-
import
|
|
259
|
-
import
|
|
258
|
+
import fs8 from "node:fs/promises";
|
|
259
|
+
import path9 from "node:path";
|
|
260
|
+
|
|
261
|
+
// src/scaffold/wrangler-defaults.ts
|
|
262
|
+
var WRANGLER_DEFAULTS = {
|
|
263
|
+
/** JSON schema for IDE support and validation */
|
|
264
|
+
$schema: "node_modules/wrangler/config-schema.json",
|
|
265
|
+
/** Worker name - becomes <name>.<account>.workers.dev */
|
|
266
|
+
name: "riebeckite-site",
|
|
267
|
+
/** Compatibility date - update when Workers runtime changes require it */
|
|
268
|
+
compatibility_date: "2026-06-09",
|
|
269
|
+
/** Required for Node.js APIs used by Riebeckite */
|
|
270
|
+
compatibility_flags: ["nodejs_compat"],
|
|
271
|
+
/** Static assets directory - Riebeckite builds to ./dist */
|
|
272
|
+
assets: { directory: "./dist" }
|
|
273
|
+
};
|
|
274
|
+
var GITHUB_ACTIONS_SECRETS = {
|
|
275
|
+
/** Cloudflare API token with Workers Scripts: Edit permission */
|
|
276
|
+
CLOUDFLARE_API_TOKEN: "CLOUDFLARE_API_TOKEN",
|
|
277
|
+
/** Cloudflare Account ID (found in Workers & Pages dashboard URL) */
|
|
278
|
+
CLOUDFLARE_ACCOUNT_ID: "CLOUDFLARE_ACCOUNT_ID",
|
|
279
|
+
/** Optional: Token for content repo to dispatch to site repo */
|
|
280
|
+
SITE_DISPATCH_TOKEN: "SITE_DISPATCH_TOKEN",
|
|
281
|
+
/** Optional: Token for site repo to read content repo */
|
|
282
|
+
RIEBECKITE_CONTENT_READ_TOKEN: "RIEBECKITE_CONTENT_READ_TOKEN"
|
|
283
|
+
};
|
|
260
284
|
|
|
261
285
|
// src/scaffold/deployment.ts
|
|
262
286
|
var GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
287
|
+
var BUILD_STATE_CACHE_KEY_PREFIX = "riebeckite-build-v1";
|
|
288
|
+
var LEGACY_BUILD_CACHE_KEY_PREFIX = "riebeckite-content-v3";
|
|
289
|
+
var BUILD_STATE_CACHE_PATHS = [
|
|
290
|
+
".riebeckite/cache",
|
|
291
|
+
".riebeckite/build/content-state.json"
|
|
292
|
+
];
|
|
263
293
|
function assertGitHubRepository(value, option) {
|
|
264
294
|
if (!GITHUB_REPOSITORY_PATTERN.test(value)) {
|
|
265
295
|
throw new Error(
|
|
@@ -295,16 +325,7 @@ function deploymentTemplateFiles(options) {
|
|
|
295
325
|
];
|
|
296
326
|
}
|
|
297
327
|
function wranglerConfig() {
|
|
298
|
-
return `${JSON.stringify(
|
|
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
|
-
)}
|
|
328
|
+
return `${JSON.stringify(WRANGLER_DEFAULTS, null, 2)}
|
|
308
329
|
`;
|
|
309
330
|
}
|
|
310
331
|
function deployWorkflow(contentRepository) {
|
|
@@ -347,6 +368,16 @@ ${contentCheckout}
|
|
|
347
368
|
- name: Install dependencies
|
|
348
369
|
run: npm ci
|
|
349
370
|
|
|
371
|
+
- name: Restore Riebeckite build state
|
|
372
|
+
uses: actions/cache@v4
|
|
373
|
+
with:
|
|
374
|
+
path: |
|
|
375
|
+
${BUILD_STATE_CACHE_PATHS.map((entry) => ` ${entry}`).join("\n")}
|
|
376
|
+
key: ${BUILD_STATE_CACHE_KEY_PREFIX}-\${{ runner.os }}-\${{ hashFiles('package-lock.json') }}-\${{ github.run_id }}-\${{ github.run_attempt }}
|
|
377
|
+
restore-keys: |
|
|
378
|
+
${BUILD_STATE_CACHE_KEY_PREFIX}-\${{ runner.os }}-\${{ hashFiles('package-lock.json') }}-
|
|
379
|
+
${LEGACY_BUILD_CACHE_KEY_PREFIX}-\${{ runner.os }}-\${{ hashFiles('package-lock.json') }}
|
|
380
|
+
|
|
350
381
|
- name: Validate configuration and plugins
|
|
351
382
|
run: npm exec riebeckite check
|
|
352
383
|
|
|
@@ -356,8 +387,8 @@ ${contentCheckout}
|
|
|
356
387
|
- name: Deploy the built assets
|
|
357
388
|
uses: cloudflare/wrangler-action@v3
|
|
358
389
|
with:
|
|
359
|
-
apiToken: \${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
360
|
-
accountId: \${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
|
390
|
+
apiToken: \${{ secrets.${GITHUB_ACTIONS_SECRETS.CLOUDFLARE_API_TOKEN} }}
|
|
391
|
+
accountId: \${{ secrets.${GITHUB_ACTIONS_SECRETS.CLOUDFLARE_ACCOUNT_ID} }}
|
|
361
392
|
command: deploy
|
|
362
393
|
`;
|
|
363
394
|
}
|
|
@@ -377,7 +408,7 @@ jobs:
|
|
|
377
408
|
steps:
|
|
378
409
|
- name: Validate dispatch configuration
|
|
379
410
|
env:
|
|
380
|
-
SITE_DISPATCH_TOKEN: \${{ secrets.SITE_DISPATCH_TOKEN }}
|
|
411
|
+
SITE_DISPATCH_TOKEN: \${{ secrets.${GITHUB_ACTIONS_SECRETS.SITE_DISPATCH_TOKEN} }}
|
|
381
412
|
run: |
|
|
382
413
|
if [ -z "$SITE_DISPATCH_TOKEN" ]; then
|
|
383
414
|
echo "::error::SITE_DISPATCH_TOKEN is not configured. Add a token that can dispatch to ${siteRepository}."
|
|
@@ -387,7 +418,7 @@ jobs:
|
|
|
387
418
|
- name: Notify the site repository
|
|
388
419
|
uses: actions/github-script@v7
|
|
389
420
|
with:
|
|
390
|
-
github-token: \${{ secrets.SITE_DISPATCH_TOKEN }}
|
|
421
|
+
github-token: \${{ secrets.${GITHUB_ACTIONS_SECRETS.SITE_DISPATCH_TOKEN} }}
|
|
391
422
|
script: |
|
|
392
423
|
await github.rest.repos.createDispatchEvent({
|
|
393
424
|
owner: ${JSON.stringify(owner)},
|
|
@@ -924,10 +955,10 @@ function resolveScaffoldPreset(value) {
|
|
|
924
955
|
}
|
|
925
956
|
|
|
926
957
|
// src/scaffold/templates.ts
|
|
927
|
-
import
|
|
958
|
+
import fs7 from "node:fs";
|
|
928
959
|
|
|
929
960
|
// src/vite_plugin.ts
|
|
930
|
-
import
|
|
961
|
+
import path8 from "node:path";
|
|
931
962
|
|
|
932
963
|
// src/asset_entries.ts
|
|
933
964
|
import fs4 from "node:fs";
|
|
@@ -1037,11 +1068,288 @@ ${initializers.map((initializer) => ` ${initializer};`).join("\n")}
|
|
|
1037
1068
|
`;
|
|
1038
1069
|
}
|
|
1039
1070
|
|
|
1071
|
+
// src/content_assets.ts
|
|
1072
|
+
import fs5 from "node:fs/promises";
|
|
1073
|
+
import path6 from "node:path";
|
|
1074
|
+
import {
|
|
1075
|
+
isIgnoredContentPath as isIgnoredContentPath2,
|
|
1076
|
+
isImagePath as isImagePath2,
|
|
1077
|
+
normalizeContentPath as normalizeContentPath2
|
|
1078
|
+
} from "@riebeckite/core";
|
|
1079
|
+
|
|
1080
|
+
// src/content_watch.ts
|
|
1081
|
+
import path5 from "node:path";
|
|
1082
|
+
import {
|
|
1083
|
+
isAttachmentPath,
|
|
1084
|
+
isIgnoredContentPath,
|
|
1085
|
+
isImagePath,
|
|
1086
|
+
normalizeContentPath
|
|
1087
|
+
} from "@riebeckite/core";
|
|
1088
|
+
var CONTENT_RELOAD_DEBOUNCE_MS = 60;
|
|
1089
|
+
var configuredWatchers = /* @__PURE__ */ new WeakSet();
|
|
1090
|
+
function riebeckiteContentWatch(options) {
|
|
1091
|
+
return {
|
|
1092
|
+
name: "riebeckite-content-watch",
|
|
1093
|
+
apply: "serve",
|
|
1094
|
+
enforce: "post",
|
|
1095
|
+
configureServer(server2) {
|
|
1096
|
+
if (configuredWatchers.has(server2.watcher)) return;
|
|
1097
|
+
configuredWatchers.add(server2.watcher);
|
|
1098
|
+
const appRoot = options.appRoot();
|
|
1099
|
+
const contentRoot = options.contentRoot();
|
|
1100
|
+
const exclude = options.exclude();
|
|
1101
|
+
const isContentFile = (file) => isPathInsideDirectory(contentRoot, file);
|
|
1102
|
+
server2.watcher.add(contentRoot);
|
|
1103
|
+
suppressRestartOnContentEvents(server2, isContentFile);
|
|
1104
|
+
let timer;
|
|
1105
|
+
const invalidate = () => {
|
|
1106
|
+
if (timer) clearTimeout(timer);
|
|
1107
|
+
timer = setTimeout(() => {
|
|
1108
|
+
timer = void 0;
|
|
1109
|
+
invalidateApplicationRuntime(server2, appRoot);
|
|
1110
|
+
server2.hot.send({ type: "full-reload" });
|
|
1111
|
+
}, CONTENT_RELOAD_DEBOUNCE_MS);
|
|
1112
|
+
};
|
|
1113
|
+
server2.httpServer?.once("close", () => {
|
|
1114
|
+
if (timer) clearTimeout(timer);
|
|
1115
|
+
});
|
|
1116
|
+
const handle = (event) => (file) => {
|
|
1117
|
+
if (!isContentFile(file)) return;
|
|
1118
|
+
const logicalPath = toLogicalPath(contentRoot, file);
|
|
1119
|
+
if (isIgnoredContentPath(logicalPath, exclude)) return;
|
|
1120
|
+
if (event === "change" && isSiteAssetPath(logicalPath)) {
|
|
1121
|
+
server2.hot.send({ type: "full-reload" });
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
invalidate();
|
|
1125
|
+
};
|
|
1126
|
+
server2.watcher.on("add", handle("add"));
|
|
1127
|
+
server2.watcher.on("change", handle("change"));
|
|
1128
|
+
server2.watcher.on("unlink", handle("unlink"));
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
function isSiteAssetPath(logicalPath) {
|
|
1133
|
+
return isImagePath(logicalPath) || isAttachmentPath(logicalPath);
|
|
1134
|
+
}
|
|
1135
|
+
function isPathInsideDirectory(directory, file) {
|
|
1136
|
+
const root = normalizeForCompare(directory).replace(/\/+$/, "");
|
|
1137
|
+
if (!root) return false;
|
|
1138
|
+
const target = normalizeForCompare(file);
|
|
1139
|
+
return target === root || target.startsWith(`${root}/`);
|
|
1140
|
+
}
|
|
1141
|
+
function toLogicalPath(directory, file) {
|
|
1142
|
+
return normalizeContentPath(
|
|
1143
|
+
path5.relative(normalizeContentPath(directory), normalizeContentPath(file))
|
|
1144
|
+
);
|
|
1145
|
+
}
|
|
1146
|
+
function invalidateApplicationRuntime(server2, appRoot) {
|
|
1147
|
+
const moduleGraph = server2.environments?.ssr?.moduleGraph;
|
|
1148
|
+
if (!moduleGraph) return 0;
|
|
1149
|
+
const appDirectory = path5.join(appRoot, "app") + path5.sep;
|
|
1150
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1151
|
+
const timestamp = Date.now();
|
|
1152
|
+
let invalidated = 0;
|
|
1153
|
+
for (const mod of moduleGraph.idToModuleMap.values()) {
|
|
1154
|
+
const file = mod.file;
|
|
1155
|
+
if (!file || !isPathInsideDirectory(appDirectory, file)) continue;
|
|
1156
|
+
moduleGraph.invalidateModule(mod, seen, timestamp);
|
|
1157
|
+
invalidated += 1;
|
|
1158
|
+
}
|
|
1159
|
+
return invalidated;
|
|
1160
|
+
}
|
|
1161
|
+
function suppressRestartOnContentEvents(server2, isContentFile) {
|
|
1162
|
+
for (const event of ["add", "unlink"]) {
|
|
1163
|
+
const listeners = server2.watcher.rawListeners(event).slice();
|
|
1164
|
+
server2.watcher.removeAllListeners(event);
|
|
1165
|
+
for (const listener of listeners) {
|
|
1166
|
+
server2.watcher.on(event, (file) => {
|
|
1167
|
+
if (isContentFile(file)) return;
|
|
1168
|
+
listener(file);
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
function normalizeForCompare(value) {
|
|
1174
|
+
const normalized = normalizeContentPath(value);
|
|
1175
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
// src/content_assets.ts
|
|
1179
|
+
var IMAGE_CONTENT_TYPES = {
|
|
1180
|
+
png: "image/png",
|
|
1181
|
+
jpg: "image/jpeg",
|
|
1182
|
+
jpeg: "image/jpeg",
|
|
1183
|
+
gif: "image/gif",
|
|
1184
|
+
svg: "image/svg+xml",
|
|
1185
|
+
webp: "image/webp",
|
|
1186
|
+
bmp: "image/bmp"
|
|
1187
|
+
};
|
|
1188
|
+
function riebeckiteContentAssets(options) {
|
|
1189
|
+
return {
|
|
1190
|
+
name: "riebeckite-content-assets",
|
|
1191
|
+
apply: "serve",
|
|
1192
|
+
enforce: "pre",
|
|
1193
|
+
configureServer(server2) {
|
|
1194
|
+
const contentRoot = options.contentRoot();
|
|
1195
|
+
const entry = path6.join(options.appRoot(), "app", "server.ts");
|
|
1196
|
+
const base = server2.config.base;
|
|
1197
|
+
const publicDir = server2.config.publicDir;
|
|
1198
|
+
let cachedManifest;
|
|
1199
|
+
let cachedPaths = /* @__PURE__ */ new Set();
|
|
1200
|
+
const resolvePublicImagePaths = async () => {
|
|
1201
|
+
const module = await server2.ssrLoadModule(entry);
|
|
1202
|
+
const manifest = await module.content?.getManifest();
|
|
1203
|
+
if (!manifest) return /* @__PURE__ */ new Set();
|
|
1204
|
+
if (manifest !== cachedManifest) {
|
|
1205
|
+
cachedManifest = manifest;
|
|
1206
|
+
cachedPaths = collectPublicImagePaths(manifest);
|
|
1207
|
+
}
|
|
1208
|
+
return cachedPaths;
|
|
1209
|
+
};
|
|
1210
|
+
const middleware = async (request, response, next) => {
|
|
1211
|
+
try {
|
|
1212
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
1213
|
+
next();
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
const logicalPath = toContentPath(request.url, base);
|
|
1217
|
+
if (!logicalPath || !isImagePath2(logicalPath)) {
|
|
1218
|
+
next();
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
if (isIgnoredContentPath2(logicalPath)) {
|
|
1222
|
+
next();
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
const contentType = imageContentType(logicalPath);
|
|
1226
|
+
if (!contentType) {
|
|
1227
|
+
next();
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
const available = await resolvePublicImagePaths();
|
|
1231
|
+
if (!available.has(logicalPath)) {
|
|
1232
|
+
next();
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
const file = path6.join(contentRoot, logicalPath);
|
|
1236
|
+
if (!isPathInsideDirectory(contentRoot, file)) {
|
|
1237
|
+
next();
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
if (await hasSiteOwnedFile(publicDir, logicalPath)) {
|
|
1241
|
+
next();
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
const content = await fs5.readFile(file);
|
|
1245
|
+
response.statusCode = 200;
|
|
1246
|
+
response.setHeader("Content-Type", contentType);
|
|
1247
|
+
response.setHeader("Content-Length", String(content.byteLength));
|
|
1248
|
+
response.setHeader("Cache-Control", "no-cache");
|
|
1249
|
+
if (request.method === "HEAD") {
|
|
1250
|
+
response.end();
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
response.end(content);
|
|
1254
|
+
return;
|
|
1255
|
+
} catch {
|
|
1256
|
+
next();
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
server2.middlewares.use((request, response, next) => {
|
|
1260
|
+
void middleware(
|
|
1261
|
+
request,
|
|
1262
|
+
response,
|
|
1263
|
+
next
|
|
1264
|
+
);
|
|
1265
|
+
});
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
function collectPublicImagePaths(manifest) {
|
|
1270
|
+
const paths2 = /* @__PURE__ */ new Set();
|
|
1271
|
+
for (const entry of manifest.publicEntries ?? []) {
|
|
1272
|
+
for (const asset of entry.assets ?? []) {
|
|
1273
|
+
const normalized = normalizeContentPath2(asset.path);
|
|
1274
|
+
if (isImagePath2(normalized)) paths2.add(normalized);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
return paths2;
|
|
1278
|
+
}
|
|
1279
|
+
async function collectSiteOwnedOutputPaths(publicDir) {
|
|
1280
|
+
const paths2 = /* @__PURE__ */ new Set();
|
|
1281
|
+
if (!publicDir) return paths2;
|
|
1282
|
+
const root = path6.resolve(publicDir);
|
|
1283
|
+
const walk = async (directory) => {
|
|
1284
|
+
const entries = await fs5.readdir(directory, { withFileTypes: true }).catch(() => void 0);
|
|
1285
|
+
if (!entries) return;
|
|
1286
|
+
for (const entry of entries) {
|
|
1287
|
+
const full = path6.join(directory, entry.name);
|
|
1288
|
+
if (entry.isDirectory()) {
|
|
1289
|
+
await walk(full);
|
|
1290
|
+
} else {
|
|
1291
|
+
paths2.add(path6.relative(root, full).replaceAll("\\", "/"));
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
};
|
|
1295
|
+
await walk(root);
|
|
1296
|
+
return paths2;
|
|
1297
|
+
}
|
|
1298
|
+
function toContentPath(url, base) {
|
|
1299
|
+
if (!url) return void 0;
|
|
1300
|
+
const withoutQuery = url.split("?")[0]?.split("#")[0];
|
|
1301
|
+
if (!withoutQuery) return void 0;
|
|
1302
|
+
let decoded = withoutQuery;
|
|
1303
|
+
try {
|
|
1304
|
+
decoded = decodeURIComponent(withoutQuery);
|
|
1305
|
+
} catch {
|
|
1306
|
+
return void 0;
|
|
1307
|
+
}
|
|
1308
|
+
let pathname = normalizeContentPath2(decoded);
|
|
1309
|
+
if (base && base !== "/" && pathname.startsWith(base)) {
|
|
1310
|
+
pathname = normalizeContentPath2(pathname.slice(base.length));
|
|
1311
|
+
}
|
|
1312
|
+
const logicalPath = pathname.replace(/^\/+/, "");
|
|
1313
|
+
if (!logicalPath) return void 0;
|
|
1314
|
+
if (logicalPath.split("/").some((segment) => segment === "" || segment === "." || segment === "..")) {
|
|
1315
|
+
return void 0;
|
|
1316
|
+
}
|
|
1317
|
+
return logicalPath;
|
|
1318
|
+
}
|
|
1319
|
+
function imageContentType(logicalPath) {
|
|
1320
|
+
const extension = logicalPath.split("/").at(-1)?.split(".").at(-1);
|
|
1321
|
+
if (!extension) return void 0;
|
|
1322
|
+
return IMAGE_CONTENT_TYPES[extension.toLowerCase()];
|
|
1323
|
+
}
|
|
1324
|
+
async function hasSiteOwnedFile(publicDir, logicalPath) {
|
|
1325
|
+
if (!publicDir) return false;
|
|
1326
|
+
const file = path6.join(publicDir, logicalPath);
|
|
1327
|
+
try {
|
|
1328
|
+
return (await fs5.stat(file)).isFile();
|
|
1329
|
+
} catch {
|
|
1330
|
+
return false;
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1040
1334
|
// src/ssg_plugin.ts
|
|
1041
|
-
import {
|
|
1335
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
1336
|
+
import {
|
|
1337
|
+
mkdir,
|
|
1338
|
+
readdir,
|
|
1339
|
+
readFile,
|
|
1340
|
+
rename as rename2,
|
|
1341
|
+
rm,
|
|
1342
|
+
stat,
|
|
1343
|
+
writeFile
|
|
1344
|
+
} from "node:fs/promises";
|
|
1345
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
1346
|
+
import { performance } from "node:perf_hooks";
|
|
1042
1347
|
import { defaultExtensionMap, toSSG } from "hono/ssg";
|
|
1043
|
-
import {
|
|
1348
|
+
import {
|
|
1349
|
+
createServer
|
|
1350
|
+
} from "vite";
|
|
1044
1351
|
var defaultEntry = "./src/index.tsx";
|
|
1352
|
+
var cloudflareWorkerAssetLimit = 25 * 1024 * 1024;
|
|
1045
1353
|
function riebeckiteSsg(options = {}) {
|
|
1046
1354
|
const virtualId = "virtual:riebeckite-ssg-void-entry";
|
|
1047
1355
|
const resolvedVirtualId = `\0${virtualId}`;
|
|
@@ -1049,7 +1357,7 @@ function riebeckiteSsg(options = {}) {
|
|
|
1049
1357
|
let resolvedConfig;
|
|
1050
1358
|
return {
|
|
1051
1359
|
name: "riebeckite-ssg",
|
|
1052
|
-
apply:
|
|
1360
|
+
apply: shouldApplyRiebeckiteSsg,
|
|
1053
1361
|
enforce: "post",
|
|
1054
1362
|
config() {
|
|
1055
1363
|
return {
|
|
@@ -1076,53 +1384,224 @@ function riebeckiteSsg(options = {}) {
|
|
|
1076
1384
|
"Riebeckite SSG could not resolve the Vite configuration."
|
|
1077
1385
|
);
|
|
1078
1386
|
}
|
|
1387
|
+
const timingsMs = {};
|
|
1079
1388
|
removeVirtualEntryChunk(bundle, resolvedVirtualId);
|
|
1080
|
-
const server2 = await
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1389
|
+
const server2 = await measure(
|
|
1390
|
+
timingsMs,
|
|
1391
|
+
"ssgSetup",
|
|
1392
|
+
() => createServer({
|
|
1393
|
+
root: config.root,
|
|
1394
|
+
define: config.define,
|
|
1395
|
+
resolve: {
|
|
1396
|
+
...config.resolve,
|
|
1397
|
+
builtins: [...config.resolve.builtins, /^node:/]
|
|
1398
|
+
},
|
|
1399
|
+
plugins: [],
|
|
1400
|
+
build: { ssr: true },
|
|
1401
|
+
mode: config.mode,
|
|
1402
|
+
server: { watch: null }
|
|
1403
|
+
})
|
|
1404
|
+
);
|
|
1091
1405
|
try {
|
|
1092
|
-
const module = await
|
|
1406
|
+
const module = await measure(
|
|
1407
|
+
timingsMs,
|
|
1408
|
+
"ssrModuleLoad",
|
|
1409
|
+
() => server2.ssrLoadModule(entry)
|
|
1410
|
+
);
|
|
1093
1411
|
const app = module.default;
|
|
1094
1412
|
if (!app) {
|
|
1095
1413
|
throw new Error(`Failed to find a default export from ${entry}.`);
|
|
1096
1414
|
}
|
|
1415
|
+
const outputChangeSet = await measure(
|
|
1416
|
+
timingsMs,
|
|
1417
|
+
"outputDependencyResolution",
|
|
1418
|
+
async () => module.content?.getOutputChangeSet()
|
|
1419
|
+
);
|
|
1420
|
+
const appFingerprint = await measure(
|
|
1421
|
+
timingsMs,
|
|
1422
|
+
"appFingerprint",
|
|
1423
|
+
() => computeAppFingerprint(config.root, config.configFile)
|
|
1424
|
+
);
|
|
1425
|
+
const outputCachePath = join(
|
|
1426
|
+
config.root,
|
|
1427
|
+
".riebeckite",
|
|
1428
|
+
"ssg-output-cache.json"
|
|
1429
|
+
);
|
|
1430
|
+
const previousOutputCache = await loadOutputCache(
|
|
1431
|
+
outputCachePath,
|
|
1432
|
+
timingsMs,
|
|
1433
|
+
(message) => this.warn(message)
|
|
1434
|
+
);
|
|
1435
|
+
const siteOwnedOutputPaths = await measure(
|
|
1436
|
+
timingsMs,
|
|
1437
|
+
"siteOwnedOutputs",
|
|
1438
|
+
() => collectSiteOwnedOutputPaths(config.publicDir)
|
|
1439
|
+
);
|
|
1440
|
+
const siteOutputCollisions = findBuildOutputSiteCollisions(
|
|
1441
|
+
Object.keys(bundle),
|
|
1442
|
+
siteOwnedOutputPaths
|
|
1443
|
+
);
|
|
1444
|
+
if (siteOutputCollisions.length > 0) {
|
|
1445
|
+
this.warn(
|
|
1446
|
+
`Build outputs take precedence over site public assets: ${siteOutputCollisions.join(", ")}`
|
|
1447
|
+
);
|
|
1448
|
+
}
|
|
1449
|
+
const canUseIncremental = outputChangeSet && !outputChangeSet.fullRegenerationRequired && previousOutputCache && previousOutputCache.appFingerprint === appFingerprint && outputChangeSet.unchanged.every(
|
|
1450
|
+
(output) => siteOwnedOutputPaths.has(output.path) || previousOutputCache.outputs[output.path]
|
|
1451
|
+
);
|
|
1452
|
+
const reusablePaths = new Set(
|
|
1453
|
+
canUseIncremental ? outputChangeSet.unchanged.map((output) => output.path) : []
|
|
1454
|
+
);
|
|
1455
|
+
const shadowedOutputs = /* @__PURE__ */ new Set();
|
|
1456
|
+
const skippedRouteCount = canUseIncremental ? outputChangeSet.unchanged.filter(
|
|
1457
|
+
(output) => output.kind !== "generated"
|
|
1458
|
+
).length : 0;
|
|
1097
1459
|
const generatedHtml = [];
|
|
1098
|
-
const
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1460
|
+
const nextOutputCache = {
|
|
1461
|
+
version: 1,
|
|
1462
|
+
appFingerprint,
|
|
1463
|
+
outputs: {}
|
|
1464
|
+
};
|
|
1465
|
+
const metrics = {
|
|
1466
|
+
candidateOutputCount: outputChangeSet?.candidateOutputCount ?? 0,
|
|
1467
|
+
affectedOutputCount: outputChangeSet?.affectedOutputCount ?? 0,
|
|
1468
|
+
removedOutputCount: outputChangeSet?.removedOutputCount ?? 0,
|
|
1469
|
+
unchangedOutputCount: outputChangeSet?.unchangedOutputCount ?? 0,
|
|
1470
|
+
renderedOutputCount: 0,
|
|
1471
|
+
emittedOutputCount: 0,
|
|
1472
|
+
reusedOutputCount: 0,
|
|
1473
|
+
deletedOutputCount: 0,
|
|
1474
|
+
shadowedOutputCount: 0,
|
|
1475
|
+
fullRegenerationRequired: !canUseIncremental,
|
|
1476
|
+
htmlOutputCount: 0,
|
|
1477
|
+
assetOutputCount: 0,
|
|
1478
|
+
cacheFileSizeBytes: previousOutputCache?.fileSizeBytes ?? null,
|
|
1479
|
+
cacheEntriesRead: previousOutputCache ? Object.keys(previousOutputCache.outputs).length : 0,
|
|
1480
|
+
cacheEntriesSerialized: 0,
|
|
1481
|
+
enumeratedRouteCount: 0,
|
|
1482
|
+
renderedRouteCount: 0,
|
|
1483
|
+
skippedRouteCount,
|
|
1484
|
+
timingsMs
|
|
1485
|
+
};
|
|
1486
|
+
process.env.RIEBECKITE_SSG_FULL_REGENERATION = canUseIncremental ? "" : "1";
|
|
1487
|
+
const outDir = resolve(config.root, config.build.outDir);
|
|
1488
|
+
const result = await measure(
|
|
1489
|
+
timingsMs,
|
|
1490
|
+
"toSSG",
|
|
1491
|
+
() => toSSG(
|
|
1492
|
+
app,
|
|
1493
|
+
{
|
|
1494
|
+
writeFile: async (filePath, data) => {
|
|
1495
|
+
const fileName = relative(outDir, filePath).replaceAll(
|
|
1496
|
+
"\\",
|
|
1497
|
+
"/"
|
|
1498
|
+
);
|
|
1499
|
+
metrics.enumeratedRouteCount += 1;
|
|
1500
|
+
if (siteOwnedOutputPaths.has(fileName)) {
|
|
1501
|
+
shadowedOutputs.add(fileName);
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
if (canUseIncremental && reusablePaths.has(fileName)) {
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1507
|
+
if (fileName.endsWith(".html") && typeof data === "string") {
|
|
1508
|
+
metrics.renderedOutputCount += 1;
|
|
1509
|
+
metrics.htmlOutputCount += 1;
|
|
1510
|
+
generatedHtml.push({ path: fileName, html: data });
|
|
1511
|
+
} else {
|
|
1512
|
+
metrics.assetOutputCount += 1;
|
|
1513
|
+
}
|
|
1514
|
+
this.emitFile({
|
|
1515
|
+
type: "asset",
|
|
1516
|
+
fileName,
|
|
1517
|
+
source: data
|
|
1518
|
+
});
|
|
1519
|
+
metrics.emittedOutputCount += 1;
|
|
1520
|
+
nextOutputCache.outputs[fileName] = encodeOutput(data);
|
|
1521
|
+
},
|
|
1522
|
+
async mkdir() {
|
|
1523
|
+
}
|
|
1524
|
+
},
|
|
1525
|
+
{
|
|
1526
|
+
dir: outDir,
|
|
1527
|
+
plugins: [...options.plugins ?? []],
|
|
1528
|
+
extensionMap: options.extensionMap ?? defaultExtensionMap
|
|
1529
|
+
}
|
|
1530
|
+
)
|
|
1531
|
+
);
|
|
1532
|
+
delete process.env.RIEBECKITE_SSG_FULL_REGENERATION;
|
|
1533
|
+
metrics.renderedRouteCount = result.files.length;
|
|
1534
|
+
if (!result.success) throw result.error;
|
|
1535
|
+
const emittedGeneratedOutputCount = await measure(
|
|
1536
|
+
timingsMs,
|
|
1537
|
+
"generatedOutputs",
|
|
1538
|
+
() => emitGeneratedOutputs(
|
|
1539
|
+
module,
|
|
1540
|
+
(asset) => {
|
|
1541
|
+
this.emitFile(asset);
|
|
1542
|
+
metrics.emittedOutputCount += 1;
|
|
1543
|
+
nextOutputCache.outputs[asset.fileName] = encodeOutput(
|
|
1544
|
+
asset.source
|
|
1545
|
+
);
|
|
1546
|
+
},
|
|
1547
|
+
canUseIncremental ? new Set(
|
|
1548
|
+
outputChangeSet.affected.filter((output) => output.kind === "generated").map((output) => output.path)
|
|
1549
|
+
) : void 0,
|
|
1550
|
+
siteOwnedOutputPaths,
|
|
1551
|
+
(path10) => shadowedOutputs.add(path10)
|
|
1552
|
+
)
|
|
1553
|
+
);
|
|
1554
|
+
if (!outputChangeSet)
|
|
1555
|
+
metrics.candidateOutputCount = result.files.length + emittedGeneratedOutputCount;
|
|
1556
|
+
if (canUseIncremental) {
|
|
1557
|
+
await measure(timingsMs, "unchangedOutputReuse", async () => {
|
|
1558
|
+
for (const output of outputChangeSet.unchanged) {
|
|
1559
|
+
const path10 = output.path;
|
|
1560
|
+
if (siteOwnedOutputPaths.has(path10)) {
|
|
1561
|
+
shadowedOutputs.add(path10);
|
|
1562
|
+
continue;
|
|
1108
1563
|
}
|
|
1564
|
+
const cached = previousOutputCache.outputs[path10];
|
|
1565
|
+
if (!cached) continue;
|
|
1109
1566
|
this.emitFile({
|
|
1110
1567
|
type: "asset",
|
|
1111
|
-
fileName,
|
|
1112
|
-
source:
|
|
1568
|
+
fileName: path10,
|
|
1569
|
+
source: decodeOutput(cached)
|
|
1113
1570
|
});
|
|
1114
|
-
|
|
1115
|
-
|
|
1571
|
+
metrics.emittedOutputCount += 1;
|
|
1572
|
+
metrics.reusedOutputCount += 1;
|
|
1573
|
+
nextOutputCache.outputs[path10] = cached;
|
|
1116
1574
|
}
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1577
|
+
if (outputChangeSet) {
|
|
1578
|
+
for (const output of outputChangeSet.removed) {
|
|
1579
|
+
if (siteOwnedOutputPaths.has(output.path)) continue;
|
|
1580
|
+
await rm(join(outDir, output.path), {
|
|
1581
|
+
force: true
|
|
1582
|
+
});
|
|
1583
|
+
metrics.deletedOutputCount += 1;
|
|
1122
1584
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1585
|
+
}
|
|
1586
|
+
metrics.shadowedOutputCount = shadowedOutputs.size;
|
|
1587
|
+
if (shadowedOutputs.size > 0) {
|
|
1588
|
+
this.warn(
|
|
1589
|
+
`Site public assets take precedence over generated outputs: ${[
|
|
1590
|
+
...shadowedOutputs
|
|
1591
|
+
].sort().join(", ")}`
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
metrics.cacheEntriesSerialized = Object.keys(
|
|
1595
|
+
nextOutputCache.outputs
|
|
1596
|
+
).length;
|
|
1597
|
+
try {
|
|
1598
|
+
await saveOutputCache(outputCachePath, nextOutputCache, timingsMs);
|
|
1599
|
+
} catch (error) {
|
|
1600
|
+
this.warn(
|
|
1601
|
+
`SSG output cache could not be saved; the next build regenerates every output. ${error instanceof Error ? error.message : String(error)}`
|
|
1602
|
+
);
|
|
1603
|
+
}
|
|
1604
|
+
await writeSsgOutputMetrics(metrics);
|
|
1126
1605
|
inspectGeneratedHtmlPages(module, generatedHtml, {
|
|
1127
1606
|
warn: (message) => this.warn(message),
|
|
1128
1607
|
info: (message) => this.info(message)
|
|
@@ -1130,19 +1609,247 @@ function riebeckiteSsg(options = {}) {
|
|
|
1130
1609
|
} finally {
|
|
1131
1610
|
await server2.close();
|
|
1132
1611
|
}
|
|
1612
|
+
},
|
|
1613
|
+
async closeBundle() {
|
|
1614
|
+
if (!resolvedConfig) return;
|
|
1615
|
+
const outDir = resolve(resolvedConfig.root, resolvedConfig.build.outDir);
|
|
1616
|
+
const oversizedAssets = await findOversizedAssets(outDir);
|
|
1617
|
+
for (const asset of oversizedAssets) {
|
|
1618
|
+
this.warn(
|
|
1619
|
+
`Generated asset ${asset.path} is ${formatBytes(asset.size)}, exceeding Cloudflare Workers' ${formatBytes(cloudflareWorkerAssetLimit)} asset limit.`
|
|
1620
|
+
);
|
|
1621
|
+
}
|
|
1133
1622
|
}
|
|
1134
1623
|
};
|
|
1135
1624
|
}
|
|
1136
|
-
async function
|
|
1625
|
+
async function findOversizedAssets(directory, relativeDirectory = "") {
|
|
1626
|
+
const entries = await readdir(directory, { withFileTypes: true }).catch(
|
|
1627
|
+
(error) => {
|
|
1628
|
+
if (isMissingDirectoryError(error)) return [];
|
|
1629
|
+
throw error;
|
|
1630
|
+
}
|
|
1631
|
+
);
|
|
1632
|
+
const nestedAssets = await Promise.all(
|
|
1633
|
+
entries.map(async (entry) => {
|
|
1634
|
+
const path10 = join(directory, entry.name);
|
|
1635
|
+
const relativePath = join(relativeDirectory, entry.name).replaceAll(
|
|
1636
|
+
"\\",
|
|
1637
|
+
"/"
|
|
1638
|
+
);
|
|
1639
|
+
if (entry.isDirectory()) {
|
|
1640
|
+
return findOversizedAssets(path10, relativePath);
|
|
1641
|
+
}
|
|
1642
|
+
if (!entry.isFile()) return [];
|
|
1643
|
+
const size = (await stat(path10)).size;
|
|
1644
|
+
return size > cloudflareWorkerAssetLimit ? [{ path: relativePath, size }] : [];
|
|
1645
|
+
})
|
|
1646
|
+
);
|
|
1647
|
+
return nestedAssets.flat();
|
|
1648
|
+
}
|
|
1649
|
+
function isMissingDirectoryError(error) {
|
|
1650
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
1651
|
+
}
|
|
1652
|
+
function formatBytes(bytes) {
|
|
1653
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
|
|
1654
|
+
}
|
|
1655
|
+
function shouldApplyRiebeckiteSsg(_config, env) {
|
|
1656
|
+
return env.command === "build" && env.mode !== "client";
|
|
1657
|
+
}
|
|
1658
|
+
function findBuildOutputSiteCollisions(buildOutputPaths, siteOwnedOutputPaths) {
|
|
1659
|
+
const collisions = /* @__PURE__ */ new Set();
|
|
1660
|
+
for (const path10 of buildOutputPaths) {
|
|
1661
|
+
if (siteOwnedOutputPaths.has(path10)) collisions.add(path10);
|
|
1662
|
+
}
|
|
1663
|
+
return [...collisions].sort();
|
|
1664
|
+
}
|
|
1665
|
+
async function emitGeneratedOutputs(module, emit, affectedPaths, shadowedPaths, onShadowed) {
|
|
1137
1666
|
const content = module.content;
|
|
1138
|
-
if (!content) return;
|
|
1667
|
+
if (!content) return 0;
|
|
1139
1668
|
const manifest = await content.getManifest();
|
|
1669
|
+
let count = 0;
|
|
1140
1670
|
for (const output of manifest.generatedOutputs ?? []) {
|
|
1671
|
+
if (affectedPaths && !affectedPaths.has(output.path)) continue;
|
|
1672
|
+
if (shadowedPaths?.has(output.path)) {
|
|
1673
|
+
onShadowed?.(output.path);
|
|
1674
|
+
continue;
|
|
1675
|
+
}
|
|
1141
1676
|
emit({
|
|
1142
1677
|
type: "asset",
|
|
1143
1678
|
fileName: output.path,
|
|
1144
1679
|
source: output.content
|
|
1145
1680
|
});
|
|
1681
|
+
count += 1;
|
|
1682
|
+
}
|
|
1683
|
+
return count;
|
|
1684
|
+
}
|
|
1685
|
+
var APP_FINGERPRINT_IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
1686
|
+
"node_modules",
|
|
1687
|
+
".riebeckite",
|
|
1688
|
+
"dist"
|
|
1689
|
+
]);
|
|
1690
|
+
var APP_FINGERPRINT_CONFIG_FILES = [
|
|
1691
|
+
"riebeckite.config.ts",
|
|
1692
|
+
"riebeckite.config.mts",
|
|
1693
|
+
"riebeckite.config.cts",
|
|
1694
|
+
"riebeckite.config.js",
|
|
1695
|
+
"riebeckite.config.mjs",
|
|
1696
|
+
"riebeckite.config.cjs"
|
|
1697
|
+
];
|
|
1698
|
+
async function computeAppFingerprint(root, configFile) {
|
|
1699
|
+
const files = [];
|
|
1700
|
+
await collectAppFiles(join(root, "app"), files);
|
|
1701
|
+
if (configFile) files.push(configFile);
|
|
1702
|
+
for (const name of APP_FINGERPRINT_CONFIG_FILES) {
|
|
1703
|
+
files.push(join(root, name));
|
|
1704
|
+
}
|
|
1705
|
+
files.sort();
|
|
1706
|
+
const hash = createHash("sha256");
|
|
1707
|
+
for (const file of files) {
|
|
1708
|
+
let content;
|
|
1709
|
+
try {
|
|
1710
|
+
content = await readFile(file);
|
|
1711
|
+
} catch {
|
|
1712
|
+
continue;
|
|
1713
|
+
}
|
|
1714
|
+
hash.update(relative(root, file).replaceAll("\\", "/"));
|
|
1715
|
+
hash.update("\0");
|
|
1716
|
+
hash.update(content);
|
|
1717
|
+
hash.update("\0");
|
|
1718
|
+
}
|
|
1719
|
+
return hash.digest("hex");
|
|
1720
|
+
}
|
|
1721
|
+
async function collectAppFiles(directory, files) {
|
|
1722
|
+
const entries = await readdir(directory, { withFileTypes: true }).catch(
|
|
1723
|
+
() => void 0
|
|
1724
|
+
);
|
|
1725
|
+
if (!entries) return;
|
|
1726
|
+
for (const entry of entries) {
|
|
1727
|
+
const full = join(directory, entry.name);
|
|
1728
|
+
if (entry.isDirectory()) {
|
|
1729
|
+
if (APP_FINGERPRINT_IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
1730
|
+
await collectAppFiles(full, files);
|
|
1731
|
+
} else if (entry.isFile()) {
|
|
1732
|
+
files.push(full);
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
async function loadOutputCache(path10, timingsMs, log = () => {
|
|
1737
|
+
}) {
|
|
1738
|
+
let raw;
|
|
1739
|
+
try {
|
|
1740
|
+
raw = await measure(
|
|
1741
|
+
timingsMs,
|
|
1742
|
+
"outputCacheRead",
|
|
1743
|
+
() => readFile(path10, "utf8")
|
|
1744
|
+
);
|
|
1745
|
+
} catch (error) {
|
|
1746
|
+
if (isNotFoundError2(error)) return void 0;
|
|
1747
|
+
log(
|
|
1748
|
+
`SSG output cache could not be read; the build regenerates every output. ${error instanceof Error ? error.message : String(error)}`
|
|
1749
|
+
);
|
|
1750
|
+
return void 0;
|
|
1751
|
+
}
|
|
1752
|
+
let parsed;
|
|
1753
|
+
try {
|
|
1754
|
+
parsed = measureSync(timingsMs, "outputCacheParse", () => JSON.parse(raw));
|
|
1755
|
+
} catch (error) {
|
|
1756
|
+
log(
|
|
1757
|
+
`SSG output cache is corrupted and will be rebuilt. ${error instanceof Error ? error.message : String(error)}`
|
|
1758
|
+
);
|
|
1759
|
+
return void 0;
|
|
1760
|
+
}
|
|
1761
|
+
if (!isOutputCacheState(parsed)) {
|
|
1762
|
+
log("SSG output cache uses an unsupported format and will be rebuilt.");
|
|
1763
|
+
return void 0;
|
|
1764
|
+
}
|
|
1765
|
+
return timingsMs ? { ...parsed, fileSizeBytes: Buffer.byteLength(raw) } : parsed;
|
|
1766
|
+
}
|
|
1767
|
+
function isOutputCacheState(value) {
|
|
1768
|
+
if (typeof value !== "object" || value === null) return false;
|
|
1769
|
+
const candidate = value;
|
|
1770
|
+
return candidate.version === 1 && (candidate.appFingerprint === void 0 || typeof candidate.appFingerprint === "string") && isOutputCacheEntries(candidate.outputs);
|
|
1771
|
+
}
|
|
1772
|
+
function isOutputCacheEntries(value) {
|
|
1773
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1774
|
+
return false;
|
|
1775
|
+
}
|
|
1776
|
+
return Object.values(value).every(
|
|
1777
|
+
(entry) => typeof entry === "object" && entry !== null && typeof entry.source === "string" && (entry.encoding === "utf8" || entry.encoding === "base64")
|
|
1778
|
+
);
|
|
1779
|
+
}
|
|
1780
|
+
async function saveOutputCache(path10, state, timingsMs) {
|
|
1781
|
+
const directory = dirname(path10);
|
|
1782
|
+
await mkdir(directory, { recursive: true });
|
|
1783
|
+
const temporaryPath = `${path10}.${randomUUID()}.tmp`;
|
|
1784
|
+
const serialized = measureSync(
|
|
1785
|
+
timingsMs,
|
|
1786
|
+
"outputCacheSerialize",
|
|
1787
|
+
() => `${JSON.stringify(state)}
|
|
1788
|
+
`
|
|
1789
|
+
);
|
|
1790
|
+
try {
|
|
1791
|
+
await measure(
|
|
1792
|
+
timingsMs,
|
|
1793
|
+
"outputCacheWrite",
|
|
1794
|
+
() => writeFile(temporaryPath, serialized, "utf8")
|
|
1795
|
+
);
|
|
1796
|
+
await measure(
|
|
1797
|
+
timingsMs,
|
|
1798
|
+
"outputCacheRename",
|
|
1799
|
+
() => replaceFile(temporaryPath, path10)
|
|
1800
|
+
);
|
|
1801
|
+
} catch (error) {
|
|
1802
|
+
await rm(temporaryPath, { force: true }).catch(() => void 0);
|
|
1803
|
+
throw error;
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
async function replaceFile(temporaryPath, targetPath) {
|
|
1807
|
+
try {
|
|
1808
|
+
await rename2(temporaryPath, targetPath);
|
|
1809
|
+
} catch (error) {
|
|
1810
|
+
if (!isReplacementFailure(error)) throw error;
|
|
1811
|
+
await rm(targetPath, { force: true });
|
|
1812
|
+
await rename2(temporaryPath, targetPath);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
function isReplacementFailure(error) {
|
|
1816
|
+
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
1817
|
+
return false;
|
|
1818
|
+
}
|
|
1819
|
+
const code = String(error.code);
|
|
1820
|
+
return code === "EPERM" || code === "EEXIST" || code === "EACCES";
|
|
1821
|
+
}
|
|
1822
|
+
function isNotFoundError2(error) {
|
|
1823
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
1824
|
+
}
|
|
1825
|
+
function encodeOutput(source) {
|
|
1826
|
+
return typeof source === "string" ? { source, encoding: "utf8" } : { source: Buffer.from(source).toString("base64"), encoding: "base64" };
|
|
1827
|
+
}
|
|
1828
|
+
function decodeOutput(entry) {
|
|
1829
|
+
return entry.encoding === "utf8" ? entry.source : Buffer.from(entry.source, "base64");
|
|
1830
|
+
}
|
|
1831
|
+
async function writeSsgOutputMetrics(metrics) {
|
|
1832
|
+
const file = process.env.RIEBECKITE_SSG_METRICS_FILE;
|
|
1833
|
+
if (!file) return;
|
|
1834
|
+
await writeFile(file, `${JSON.stringify(metrics)}
|
|
1835
|
+
`, "utf8");
|
|
1836
|
+
}
|
|
1837
|
+
async function measure(timingsMs, name, fn) {
|
|
1838
|
+
const start = performance.now();
|
|
1839
|
+
try {
|
|
1840
|
+
return await fn();
|
|
1841
|
+
} finally {
|
|
1842
|
+
if (timingsMs)
|
|
1843
|
+
timingsMs[name] = (timingsMs[name] ?? 0) + performance.now() - start;
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
function measureSync(timingsMs, name, fn) {
|
|
1847
|
+
const start = performance.now();
|
|
1848
|
+
try {
|
|
1849
|
+
return fn();
|
|
1850
|
+
} finally {
|
|
1851
|
+
if (timingsMs)
|
|
1852
|
+
timingsMs[name] = (timingsMs[name] ?? 0) + performance.now() - start;
|
|
1146
1853
|
}
|
|
1147
1854
|
}
|
|
1148
1855
|
function inspectGeneratedHtmlPages(module, pages, log) {
|
|
@@ -1192,8 +1899,8 @@ function createRiebeckiteSsg(options = {}) {
|
|
|
1192
1899
|
}
|
|
1193
1900
|
|
|
1194
1901
|
// src/vite_runner.ts
|
|
1195
|
-
import
|
|
1196
|
-
import
|
|
1902
|
+
import fs6 from "node:fs/promises";
|
|
1903
|
+
import path7 from "node:path";
|
|
1197
1904
|
import { build, createServer as createServer2 } from "vite";
|
|
1198
1905
|
var viteConfigFileNames = [
|
|
1199
1906
|
"vite.config.ts",
|
|
@@ -1219,8 +1926,8 @@ var HonoxApplicationRootError = class extends Error {
|
|
|
1219
1926
|
}
|
|
1220
1927
|
};
|
|
1221
1928
|
async function resolveHonoxApplication(options) {
|
|
1222
|
-
const configRoot =
|
|
1223
|
-
const appRoot = options.appRoot ?
|
|
1929
|
+
const configRoot = path7.resolve(options.configRoot);
|
|
1930
|
+
const appRoot = options.appRoot ? path7.resolve(options.appRoot) : await resolveHonoxApplicationRoot(configRoot);
|
|
1224
1931
|
const workspaceRoot = options.workspaceRoot ?? await findWorkspaceRoot(configRoot);
|
|
1225
1932
|
const rawConfig = await loadRiebeckiteConfig({
|
|
1226
1933
|
configRoot,
|
|
@@ -1237,8 +1944,8 @@ async function resolveHonoxApplication(options) {
|
|
|
1237
1944
|
}
|
|
1238
1945
|
async function findWorkspaceRoot(configRoot) {
|
|
1239
1946
|
try {
|
|
1240
|
-
const workspaceConfig =
|
|
1241
|
-
return (await
|
|
1947
|
+
const workspaceConfig = path7.join(configRoot, "pnpm-workspace.yaml");
|
|
1948
|
+
return (await fs6.stat(workspaceConfig)).isFile() ? configRoot : void 0;
|
|
1242
1949
|
} catch {
|
|
1243
1950
|
return void 0;
|
|
1244
1951
|
}
|
|
@@ -1265,9 +1972,9 @@ async function runTraced(tracer, name, buildTask) {
|
|
|
1265
1972
|
return tracer ? await tracer.span(name, {}, buildTask) : await buildTask();
|
|
1266
1973
|
}
|
|
1267
1974
|
async function waitForShutdown(server2) {
|
|
1268
|
-
await new Promise((
|
|
1975
|
+
await new Promise((resolve2) => {
|
|
1269
1976
|
const close = () => {
|
|
1270
|
-
void server2.close().finally(
|
|
1977
|
+
void server2.close().finally(resolve2);
|
|
1271
1978
|
};
|
|
1272
1979
|
process.once("SIGINT", close);
|
|
1273
1980
|
process.once("SIGTERM", close);
|
|
@@ -1280,12 +1987,12 @@ async function findViteApplicationRoots(root) {
|
|
|
1280
1987
|
const directory = directories.pop();
|
|
1281
1988
|
if (!directory) continue;
|
|
1282
1989
|
if (await hasViteConfig(directory)) applicationRoots.push(directory);
|
|
1283
|
-
const entries = await
|
|
1990
|
+
const entries = await fs6.readdir(directory, { withFileTypes: true });
|
|
1284
1991
|
for (const entry of entries) {
|
|
1285
1992
|
if (!entry.isDirectory() || entry.name === "node_modules" || entry.name === ".git" || entry.name === "tests") {
|
|
1286
1993
|
continue;
|
|
1287
1994
|
}
|
|
1288
|
-
directories.push(
|
|
1995
|
+
directories.push(path7.join(directory, entry.name));
|
|
1289
1996
|
}
|
|
1290
1997
|
}
|
|
1291
1998
|
return applicationRoots;
|
|
@@ -1293,7 +2000,7 @@ async function findViteApplicationRoots(root) {
|
|
|
1293
2000
|
async function hasViteConfig(directory) {
|
|
1294
2001
|
for (const fileName of viteConfigFileNames) {
|
|
1295
2002
|
try {
|
|
1296
|
-
if ((await
|
|
2003
|
+
if ((await fs6.stat(path7.join(directory, fileName))).isFile()) return true;
|
|
1297
2004
|
} catch {
|
|
1298
2005
|
}
|
|
1299
2006
|
}
|
|
@@ -1313,6 +2020,7 @@ var defaultSsrExternals = [
|
|
|
1313
2020
|
];
|
|
1314
2021
|
function riebeckite(options = {}) {
|
|
1315
2022
|
let resolvedConfig;
|
|
2023
|
+
let contentWatchRoots;
|
|
1316
2024
|
const getConfig = () => {
|
|
1317
2025
|
if (!resolvedConfig) {
|
|
1318
2026
|
throw new Error("Riebeckite config has not been loaded yet.");
|
|
@@ -1323,7 +2031,7 @@ function riebeckite(options = {}) {
|
|
|
1323
2031
|
{
|
|
1324
2032
|
name: "riebeckite-host-integration",
|
|
1325
2033
|
async config(userConfig) {
|
|
1326
|
-
const root = userConfig.root ?
|
|
2034
|
+
const root = userConfig.root ? path8.resolve(userConfig.root) : process.cwd();
|
|
1327
2035
|
const appRoot = options.appRoot ?? root;
|
|
1328
2036
|
const application = await resolveHonoxApplication({
|
|
1329
2037
|
configRoot: options.configRoot ?? appRoot,
|
|
@@ -1332,9 +2040,10 @@ function riebeckite(options = {}) {
|
|
|
1332
2040
|
workspaceRoot: options.workspaceRoot
|
|
1333
2041
|
});
|
|
1334
2042
|
resolvedConfig = application.config;
|
|
2043
|
+
contentWatchRoots = application;
|
|
1335
2044
|
writeRiebeckiteAssetEntries(resolvedConfig, {
|
|
1336
|
-
pluginStyles:
|
|
1337
|
-
themeStyles:
|
|
2045
|
+
pluginStyles: path8.join(appRoot, "app/.riebeckite/plugin-styles.css"),
|
|
2046
|
+
themeStyles: path8.join(appRoot, "app/.riebeckite/theme-styles.css")
|
|
1338
2047
|
});
|
|
1339
2048
|
return {
|
|
1340
2049
|
define: {
|
|
@@ -1351,9 +2060,24 @@ function riebeckite(options = {}) {
|
|
|
1351
2060
|
};
|
|
1352
2061
|
}
|
|
1353
2062
|
},
|
|
1354
|
-
riebeckiteClientModule(getConfig)
|
|
2063
|
+
riebeckiteClientModule(getConfig),
|
|
2064
|
+
riebeckiteContentAssets({
|
|
2065
|
+
appRoot: () => requireContentWatchRoots(contentWatchRoots).appRoot,
|
|
2066
|
+
contentRoot: () => requireContentWatchRoots(contentWatchRoots).contentRoot
|
|
2067
|
+
}),
|
|
2068
|
+
riebeckiteContentWatch({
|
|
2069
|
+
appRoot: () => requireContentWatchRoots(contentWatchRoots).appRoot,
|
|
2070
|
+
contentRoot: () => requireContentWatchRoots(contentWatchRoots).contentRoot,
|
|
2071
|
+
exclude: () => getConfig().content.exclude ?? []
|
|
2072
|
+
})
|
|
1355
2073
|
];
|
|
1356
2074
|
}
|
|
2075
|
+
function requireContentWatchRoots(roots) {
|
|
2076
|
+
if (!roots) {
|
|
2077
|
+
throw new Error("Riebeckite application has not been resolved yet.");
|
|
2078
|
+
}
|
|
2079
|
+
return roots;
|
|
2080
|
+
}
|
|
1357
2081
|
function riebeckiteVite(options = {}) {
|
|
1358
2082
|
const { ssg, ssrExternals, ...integration } = options;
|
|
1359
2083
|
return [
|
|
@@ -1380,6 +2104,17 @@ function riebeckiteSsrExternals(externals) {
|
|
|
1380
2104
|
}
|
|
1381
2105
|
|
|
1382
2106
|
// src/scaffold/app-templates.ts
|
|
2107
|
+
function hasScaffoldPlugin(preset, packageName) {
|
|
2108
|
+
return preset.plugins.some((plugin) => plugin.package === packageName);
|
|
2109
|
+
}
|
|
2110
|
+
function articleTitleHelper() {
|
|
2111
|
+
return `
|
|
2112
|
+
function getArticleTitle(slug: string, title: unknown): string {
|
|
2113
|
+
if (typeof title === "string" && title.trim().length > 0) return title;
|
|
2114
|
+
return slug.split("/").at(-1) ?? slug;
|
|
2115
|
+
}
|
|
2116
|
+
`;
|
|
2117
|
+
}
|
|
1383
2118
|
function server() {
|
|
1384
2119
|
return `import { mountRiebeckiteEndpoints } from "@riebeckite/honox/server";
|
|
1385
2120
|
import { createApp } from "honox/server";
|
|
@@ -1532,6 +2267,56 @@ function style(preset) {
|
|
|
1532
2267
|
" padding: 2rem 1rem;",
|
|
1533
2268
|
" margin: 0 auto;",
|
|
1534
2269
|
"}",
|
|
2270
|
+
"",
|
|
2271
|
+
".article-shell__layout {",
|
|
2272
|
+
" display: grid;",
|
|
2273
|
+
" grid-template-columns: minmax(0, 1fr);",
|
|
2274
|
+
" justify-content: center;",
|
|
2275
|
+
" gap: var(--rb-layout-gap, 2rem);",
|
|
2276
|
+
"}",
|
|
2277
|
+
"",
|
|
2278
|
+
".site-article__aside {",
|
|
2279
|
+
" display: none;",
|
|
2280
|
+
"}",
|
|
2281
|
+
"",
|
|
2282
|
+
".site-article__footer {",
|
|
2283
|
+
" min-width: 0;",
|
|
2284
|
+
"}",
|
|
2285
|
+
"",
|
|
2286
|
+
"@media (min-width: 88rem) {",
|
|
2287
|
+
' :is(:root, .rb-theme-root)[data-article-layout="article"] .article-shell__layout {',
|
|
2288
|
+
" grid-template-columns: minmax(0, var(--rb-layout-article-max, 48rem));",
|
|
2289
|
+
" position: relative;",
|
|
2290
|
+
" }",
|
|
2291
|
+
"",
|
|
2292
|
+
' :is(:root, .rb-theme-root)[data-article-layout="article"] .article-shell__body {',
|
|
2293
|
+
" grid-column: 1;",
|
|
2294
|
+
" grid-row: 1;",
|
|
2295
|
+
" }",
|
|
2296
|
+
"",
|
|
2297
|
+
' :is(:root, .rb-theme-root)[data-article-layout="article"] .site-article__footer {',
|
|
2298
|
+
" grid-column: 1;",
|
|
2299
|
+
" }",
|
|
2300
|
+
"",
|
|
2301
|
+
' :is(:root, .rb-theme-root)[data-article-layout="article"] .site-article__aside {',
|
|
2302
|
+
" display: block;",
|
|
2303
|
+
" grid-column: 1;",
|
|
2304
|
+
" grid-row: 1;",
|
|
2305
|
+
" position: sticky;",
|
|
2306
|
+
" top: 4rem;",
|
|
2307
|
+
" align-self: start;",
|
|
2308
|
+
" width: var(--rb-layout-sidebar, 14rem);",
|
|
2309
|
+
" max-height: calc(100dvh - 8rem);",
|
|
2310
|
+
" margin-inline-start: calc(",
|
|
2311
|
+
" -1 * var(--rb-layout-sidebar, 14rem) -",
|
|
2312
|
+
" var(--rb-layout-gap, 2rem)",
|
|
2313
|
+
" );",
|
|
2314
|
+
" overflow: auto;",
|
|
2315
|
+
" padding-inline-end: 1rem;",
|
|
2316
|
+
" border-inline-end: 1px solid",
|
|
2317
|
+
" color-mix(in oklab, currentColor 18%, transparent);",
|
|
2318
|
+
" }",
|
|
2319
|
+
"}",
|
|
1535
2320
|
""
|
|
1536
2321
|
);
|
|
1537
2322
|
}
|
|
@@ -1576,23 +2361,81 @@ import { Article, ArticleContent, ArticleLayout } from "@riebeckite/honox/ui";
|
|
|
1576
2361
|
|
|
1577
2362
|
export function SiteArticle({
|
|
1578
2363
|
post,
|
|
1579
|
-
|
|
2364
|
+
propertiesHtml,
|
|
2365
|
+
afterHeaderHtml,
|
|
2366
|
+
afterMetaHtml,
|
|
2367
|
+
beforeContentHtml,
|
|
2368
|
+
afterContentHtml,
|
|
2369
|
+
asideHtml,
|
|
2370
|
+
footerHtml,
|
|
2371
|
+
asideContent,
|
|
2372
|
+
afterContent,
|
|
2373
|
+
footerContent,
|
|
1580
2374
|
}: {
|
|
1581
2375
|
post: PostContent;
|
|
1582
|
-
|
|
2376
|
+
propertiesHtml?: string;
|
|
2377
|
+
afterHeaderHtml?: string;
|
|
2378
|
+
afterMetaHtml?: string;
|
|
2379
|
+
beforeContentHtml?: string;
|
|
2380
|
+
afterContentHtml?: string;
|
|
2381
|
+
asideHtml?: string;
|
|
2382
|
+
footerHtml?: string;
|
|
2383
|
+
asideContent?: unknown;
|
|
2384
|
+
afterContent?: unknown;
|
|
2385
|
+
footerContent?: unknown;
|
|
1583
2386
|
}) {
|
|
1584
2387
|
return (
|
|
1585
2388
|
<Article class="site-article">
|
|
1586
2389
|
<ArticleLayout>
|
|
2390
|
+
{asideContent}
|
|
2391
|
+
{asideHtml ? (
|
|
2392
|
+
<div
|
|
2393
|
+
class="site-article__aside"
|
|
2394
|
+
dangerouslySetInnerHTML={{ __html: asideHtml }}
|
|
2395
|
+
/>
|
|
2396
|
+
) : null}
|
|
1587
2397
|
<ArticleContent>
|
|
1588
|
-
{
|
|
2398
|
+
{afterHeaderHtml ? (
|
|
2399
|
+
<div
|
|
2400
|
+
class="site-article__after-header"
|
|
2401
|
+
dangerouslySetInnerHTML={{ __html: afterHeaderHtml }}
|
|
2402
|
+
/>
|
|
2403
|
+
) : null}
|
|
2404
|
+
{propertiesHtml ? (
|
|
2405
|
+
<div
|
|
2406
|
+
class="article-properties"
|
|
2407
|
+
dangerouslySetInnerHTML={{ __html: propertiesHtml }}
|
|
2408
|
+
/>
|
|
2409
|
+
) : null}
|
|
2410
|
+
{afterMetaHtml ? (
|
|
1589
2411
|
<div
|
|
1590
2412
|
class="site-article__after-meta"
|
|
1591
|
-
dangerouslySetInnerHTML={{ __html:
|
|
2413
|
+
dangerouslySetInnerHTML={{ __html: afterMetaHtml }}
|
|
2414
|
+
/>
|
|
2415
|
+
) : null}
|
|
2416
|
+
{beforeContentHtml ? (
|
|
2417
|
+
<div
|
|
2418
|
+
class="site-article__before-content"
|
|
2419
|
+
dangerouslySetInnerHTML={{ __html: beforeContentHtml }}
|
|
1592
2420
|
/>
|
|
1593
2421
|
) : null}
|
|
1594
2422
|
<div dangerouslySetInnerHTML={{ __html: post.html ?? "" }} />
|
|
2423
|
+
{afterContent}
|
|
2424
|
+
{afterContentHtml ? (
|
|
2425
|
+
<div
|
|
2426
|
+
class="site-article__after-content"
|
|
2427
|
+
dangerouslySetInnerHTML={{ __html: afterContentHtml }}
|
|
2428
|
+
/>
|
|
2429
|
+
) : null}
|
|
1595
2430
|
</ArticleContent>
|
|
2431
|
+
{footerHtml || footerContent ? (
|
|
2432
|
+
<div class="site-article__footer">
|
|
2433
|
+
{footerHtml ? (
|
|
2434
|
+
<div dangerouslySetInnerHTML={{ __html: footerHtml }} />
|
|
2435
|
+
) : null}
|
|
2436
|
+
{footerContent}
|
|
2437
|
+
</div>
|
|
2438
|
+
) : null}
|
|
1596
2439
|
</ArticleLayout>
|
|
1597
2440
|
</Article>
|
|
1598
2441
|
);
|
|
@@ -1605,10 +2448,12 @@ function renderer(preset) {
|
|
|
1605
2448
|
const hasColorMode = preset.plugins.some(
|
|
1606
2449
|
(plugin) => plugin.package === "@riebeckite/plugin-color-mode"
|
|
1607
2450
|
);
|
|
2451
|
+
const hasSearch = hasScaffoldPlugin(preset, "@riebeckite/plugin-search");
|
|
1608
2452
|
return `import type { PluginHeadTag } from "@riebeckite/core";
|
|
1609
2453
|
import { jsxRenderer } from "hono/jsx-renderer";
|
|
1610
2454
|
import { Link, Script } from "honox/server";
|
|
1611
2455
|
${hasColorMode ? `import { ColorModeScript } from "@riebeckite/plugin-color-mode";
|
|
2456
|
+
` : ""}${hasSearch ? `import { SearchBar } from "@riebeckite/plugin-search";
|
|
1612
2457
|
` : ""}${hasHeader ? `import { SiteHeader } from "../components/site-header";
|
|
1613
2458
|
` : ""}import { config } from "../config";
|
|
1614
2459
|
|
|
@@ -1648,6 +2493,7 @@ ${hasColorMode ? ` <ColorModeScript />
|
|
|
1648
2493
|
</head>
|
|
1649
2494
|
<body class="riebeckite-page">
|
|
1650
2495
|
${hasHeader ? ` <SiteHeader />
|
|
2496
|
+
` : ""}${hasSearch ? ` <SearchBar />
|
|
1651
2497
|
` : ""} {children}
|
|
1652
2498
|
</body>
|
|
1653
2499
|
</html>
|
|
@@ -1696,11 +2542,96 @@ export default jsxRenderer(({ children }, c) => (
|
|
|
1696
2542
|
}
|
|
1697
2543
|
function indexRoute(preset) {
|
|
1698
2544
|
if (!preset.appFiles.includes("article")) return staticIndexRoute();
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
2545
|
+
const hasToc = hasScaffoldPlugin(preset, "@riebeckite/plugin-toc");
|
|
2546
|
+
const hasBacklinks = hasScaffoldPlugin(
|
|
2547
|
+
preset,
|
|
2548
|
+
"@riebeckite/plugin-backlinks"
|
|
2549
|
+
);
|
|
2550
|
+
const hasRecentPosts = hasScaffoldPlugin(
|
|
2551
|
+
preset,
|
|
2552
|
+
"@riebeckite/plugin-recent-posts"
|
|
2553
|
+
);
|
|
2554
|
+
const hasDailyNotes = hasScaffoldPlugin(
|
|
2555
|
+
preset,
|
|
2556
|
+
"@riebeckite/plugin-daily-notes"
|
|
2557
|
+
);
|
|
2558
|
+
const needsTitle = hasBacklinks || hasRecentPosts;
|
|
2559
|
+
const needsConfig = hasBacklinks || hasRecentPosts || hasDailyNotes;
|
|
2560
|
+
const importLines = [
|
|
2561
|
+
'import { createRoute } from "honox/factory";'
|
|
2562
|
+
];
|
|
2563
|
+
if (hasBacklinks) {
|
|
2564
|
+
importLines.push(
|
|
2565
|
+
'import { Backlinks, getPublishedBacklinks } from "@riebeckite/plugin-backlinks";'
|
|
2566
|
+
);
|
|
2567
|
+
}
|
|
2568
|
+
if (hasDailyNotes) {
|
|
2569
|
+
importLines.push(
|
|
2570
|
+
'import { DailyNotes, getDailyNotes } from "@riebeckite/plugin-daily-notes";'
|
|
2571
|
+
);
|
|
2572
|
+
}
|
|
2573
|
+
if (hasRecentPosts) {
|
|
2574
|
+
importLines.push(
|
|
2575
|
+
'import { RecentPosts, getRecentPosts } from "@riebeckite/plugin-recent-posts";'
|
|
2576
|
+
);
|
|
2577
|
+
}
|
|
2578
|
+
if (hasToc) {
|
|
2579
|
+
importLines.push(
|
|
2580
|
+
'import { extractTableOfContents, TableOfContents } from "@riebeckite/plugin-toc";'
|
|
2581
|
+
);
|
|
2582
|
+
}
|
|
2583
|
+
importLines.push('import { SiteArticle } from "../components/article";');
|
|
2584
|
+
if (needsConfig) importLines.push('import { config } from "../config";');
|
|
2585
|
+
importLines.push('import { content } from "../content";');
|
|
2586
|
+
const dataLines = [];
|
|
2587
|
+
if (hasBacklinks) {
|
|
2588
|
+
dataLines.push(
|
|
2589
|
+
' const backlinks = getPublishedBacklinks({ manifest, config, slug: "index", resolveTitle: getArticleTitle });'
|
|
2590
|
+
);
|
|
2591
|
+
}
|
|
2592
|
+
if (hasRecentPosts) {
|
|
2593
|
+
dataLines.push(
|
|
2594
|
+
" const recentPosts = await getRecentPosts({ posts: manifest.discoverableEntries, config, getProcessedContent: (slug) => content.getProcessedContent(slug), resolveTitle: getArticleTitle });"
|
|
2595
|
+
);
|
|
2596
|
+
}
|
|
2597
|
+
if (hasDailyNotes) {
|
|
2598
|
+
dataLines.push(" const dailyNotes = getDailyNotes({ manifest, config });");
|
|
2599
|
+
}
|
|
2600
|
+
if (hasToc) {
|
|
2601
|
+
dataLines.push(
|
|
2602
|
+
' const tableOfContents = extractTableOfContents(post.html ?? "");'
|
|
2603
|
+
);
|
|
2604
|
+
}
|
|
2605
|
+
const propLines = [
|
|
2606
|
+
" propertiesHtml={indexEntry?.bodySlots?.properties}",
|
|
2607
|
+
' afterHeaderHtml={indexEntry?.bodySlots?.["article.after-header"]}',
|
|
2608
|
+
' afterMetaHtml={indexEntry?.bodySlots?.["article.after-meta"]}',
|
|
2609
|
+
' beforeContentHtml={indexEntry?.bodySlots?.["article.before-content"]}',
|
|
2610
|
+
' afterContentHtml={indexEntry?.bodySlots?.["article.after-content"]}',
|
|
2611
|
+
' asideHtml={indexEntry?.bodySlots?.["article.aside"]}',
|
|
2612
|
+
' footerHtml={indexEntry?.bodySlots?.["article.footer"]}'
|
|
2613
|
+
];
|
|
2614
|
+
if (hasToc) {
|
|
2615
|
+
propLines.push(
|
|
2616
|
+
' asideContent={<TableOfContents className="table-of-contents--desktop" items={tableOfContents} />}'
|
|
2617
|
+
);
|
|
2618
|
+
}
|
|
2619
|
+
const afterChildren = [];
|
|
2620
|
+
if (hasRecentPosts) afterChildren.push("<RecentPosts posts={recentPosts} />");
|
|
2621
|
+
if (hasDailyNotes) afterChildren.push("<DailyNotes notes={dailyNotes} />");
|
|
2622
|
+
if (afterChildren.length > 0) {
|
|
2623
|
+
propLines.push(` afterContent={<>${afterChildren.join("")}</>}`);
|
|
2624
|
+
}
|
|
2625
|
+
if (hasBacklinks) {
|
|
2626
|
+
propLines.push(
|
|
2627
|
+
" footerContent={<Backlinks backlinks={backlinks} />}"
|
|
2628
|
+
);
|
|
2629
|
+
}
|
|
2630
|
+
const titleHelper = needsTitle ? articleTitleHelper() : "";
|
|
2631
|
+
const dataBlock = dataLines.length > 0 ? `
|
|
2632
|
+
${dataLines.join("\n")}
|
|
2633
|
+
` : "";
|
|
2634
|
+
return `${importLines.join("\n")}
|
|
1704
2635
|
|
|
1705
2636
|
export default createRoute(async (c) => {
|
|
1706
2637
|
const manifest = await content.getManifest();
|
|
@@ -1710,10 +2641,10 @@ export default createRoute(async (c) => {
|
|
|
1710
2641
|
}
|
|
1711
2642
|
|
|
1712
2643
|
const post = await content.getProcessedContent("index");
|
|
1713
|
-
if (
|
|
2644
|
+
if (indexEntry?.publishing?.routable === false) {
|
|
1714
2645
|
return c.notFound();
|
|
1715
2646
|
}
|
|
1716
|
-
|
|
2647
|
+
${dataBlock}
|
|
1717
2648
|
if (indexEntry) {
|
|
1718
2649
|
c.set("htmlLanguage", indexEntry.publicLocation.metadata?.["l10n.lang"]);
|
|
1719
2650
|
c.set("headTags", indexEntry.headTags ?? []);
|
|
@@ -1722,11 +2653,10 @@ export default createRoute(async (c) => {
|
|
|
1722
2653
|
return c.render(
|
|
1723
2654
|
<SiteArticle
|
|
1724
2655
|
post={post}
|
|
1725
|
-
|
|
2656
|
+
${propLines.join("\n")}
|
|
1726
2657
|
/>,
|
|
1727
2658
|
);
|
|
1728
|
-
})
|
|
1729
|
-
`;
|
|
2659
|
+
});${titleHelper}`;
|
|
1730
2660
|
}
|
|
1731
2661
|
function staticIndexRoute() {
|
|
1732
2662
|
return `import { createRoute } from "honox/factory";
|
|
@@ -1742,23 +2672,98 @@ export default createRoute((c) =>
|
|
|
1742
2672
|
);
|
|
1743
2673
|
`;
|
|
1744
2674
|
}
|
|
1745
|
-
function slugRoute() {
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
2675
|
+
function slugRoute(preset) {
|
|
2676
|
+
const hasToc = hasScaffoldPlugin(preset, "@riebeckite/plugin-toc");
|
|
2677
|
+
const hasBacklinks = hasScaffoldPlugin(
|
|
2678
|
+
preset,
|
|
2679
|
+
"@riebeckite/plugin-backlinks"
|
|
2680
|
+
);
|
|
2681
|
+
const hasLocalGraph = hasScaffoldPlugin(
|
|
2682
|
+
preset,
|
|
2683
|
+
"@riebeckite/plugin-local-graph"
|
|
2684
|
+
);
|
|
2685
|
+
const needsTitle = hasBacklinks || hasLocalGraph;
|
|
2686
|
+
const needsConfig = hasBacklinks || hasLocalGraph;
|
|
2687
|
+
const needsManifest = hasBacklinks || hasLocalGraph;
|
|
2688
|
+
const importLines = [
|
|
2689
|
+
"import {",
|
|
2690
|
+
" contentRouteSsgParams,",
|
|
2691
|
+
" pluginPageSsgParams,",
|
|
2692
|
+
" resolveRiebeckiteRoute,",
|
|
2693
|
+
'} from "@riebeckite/honox/server";'
|
|
2694
|
+
];
|
|
2695
|
+
if (hasBacklinks) {
|
|
2696
|
+
importLines.push(
|
|
2697
|
+
'import { Backlinks, getPublishedBacklinks } from "@riebeckite/plugin-backlinks";'
|
|
2698
|
+
);
|
|
2699
|
+
}
|
|
2700
|
+
if (hasLocalGraph) {
|
|
2701
|
+
importLines.push(
|
|
2702
|
+
'import { LocalGraph, getLocalGraph } from "@riebeckite/plugin-local-graph";'
|
|
2703
|
+
);
|
|
2704
|
+
}
|
|
2705
|
+
if (hasToc) {
|
|
2706
|
+
importLines.push(
|
|
2707
|
+
'import { extractTableOfContents, TableOfContents } from "@riebeckite/plugin-toc";'
|
|
2708
|
+
);
|
|
2709
|
+
}
|
|
2710
|
+
importLines.push('import { createRoute } from "honox/factory";');
|
|
2711
|
+
importLines.push('import { SiteArticle } from "../components/article";');
|
|
2712
|
+
if (needsConfig) importLines.push('import { config } from "../config";');
|
|
2713
|
+
importLines.push('import { content } from "../content";');
|
|
2714
|
+
const dataLines = [];
|
|
2715
|
+
if (needsManifest) {
|
|
2716
|
+
dataLines.push(" const manifest = await content.getManifest();");
|
|
2717
|
+
}
|
|
2718
|
+
if (hasBacklinks) {
|
|
2719
|
+
dataLines.push(
|
|
2720
|
+
" const backlinks = getPublishedBacklinks({ manifest, config, slug: route.entry.slug, resolveTitle: getArticleTitle });"
|
|
2721
|
+
);
|
|
2722
|
+
}
|
|
2723
|
+
if (hasLocalGraph) {
|
|
2724
|
+
dataLines.push(
|
|
2725
|
+
" const localGraph = getLocalGraph({ manifest, config, slug: route.entry.slug, resolveTitle: getArticleTitle });"
|
|
2726
|
+
);
|
|
2727
|
+
}
|
|
2728
|
+
if (hasToc) {
|
|
2729
|
+
dataLines.push(
|
|
2730
|
+
' const tableOfContents = extractTableOfContents(post.html ?? "");'
|
|
2731
|
+
);
|
|
2732
|
+
}
|
|
2733
|
+
const propLines = [
|
|
2734
|
+
" propertiesHtml={route.entry.bodySlots?.properties}",
|
|
2735
|
+
' afterHeaderHtml={route.entry.bodySlots?.["article.after-header"]}',
|
|
2736
|
+
' afterMetaHtml={route.entry.bodySlots?.["article.after-meta"]}',
|
|
2737
|
+
' beforeContentHtml={route.entry.bodySlots?.["article.before-content"]}',
|
|
2738
|
+
' afterContentHtml={route.entry.bodySlots?.["article.after-content"]}',
|
|
2739
|
+
' asideHtml={route.entry.bodySlots?.["article.aside"]}',
|
|
2740
|
+
' footerHtml={route.entry.bodySlots?.["article.footer"]}'
|
|
2741
|
+
];
|
|
2742
|
+
if (hasToc) {
|
|
2743
|
+
propLines.push(
|
|
2744
|
+
' asideContent={<TableOfContents className="table-of-contents--desktop" items={tableOfContents} />}'
|
|
2745
|
+
);
|
|
2746
|
+
}
|
|
2747
|
+
const footerChildren = [];
|
|
2748
|
+
if (hasLocalGraph) {
|
|
2749
|
+
footerChildren.push("{localGraph && <LocalGraph graph={localGraph} />}");
|
|
2750
|
+
}
|
|
2751
|
+
if (hasBacklinks) {
|
|
2752
|
+
footerChildren.push("<Backlinks backlinks={backlinks} />");
|
|
2753
|
+
}
|
|
2754
|
+
if (footerChildren.length > 0) {
|
|
2755
|
+
propLines.push(` footerContent={<>${footerChildren.join("")}</>}`);
|
|
2756
|
+
}
|
|
2757
|
+
const titleHelper = needsTitle ? articleTitleHelper() : "";
|
|
2758
|
+
const dataBlock = dataLines.length > 0 ? `
|
|
2759
|
+
${dataLines.join("\n")}
|
|
2760
|
+
` : "";
|
|
2761
|
+
return `${importLines.join("\n")}
|
|
1756
2762
|
|
|
1757
2763
|
export default createRoute(
|
|
1758
2764
|
contentRouteSsgParams("/:slug{.+}", async () => {
|
|
1759
2765
|
const manifest = await content.getManifest();
|
|
1760
|
-
const contentPaths = manifest.
|
|
1761
|
-
.filter((entry) => isPublished(config, entry.frontmatter))
|
|
2766
|
+
const contentPaths = manifest.publicEntries
|
|
1762
2767
|
.filter((entry) => entry.permalink !== "/")
|
|
1763
2768
|
.map((entry) => ({ slug: entry.permalink.replace(/^\\/+/, "") }));
|
|
1764
2769
|
return [...contentPaths, ...(await pluginPageSsgParams(content))];
|
|
@@ -1780,22 +2785,18 @@ export default createRoute(
|
|
|
1780
2785
|
}
|
|
1781
2786
|
|
|
1782
2787
|
const post = await content.getProcessedContent(route.entry.slug);
|
|
1783
|
-
|
|
1784
|
-
return c.notFound();
|
|
1785
|
-
}
|
|
1786
|
-
|
|
2788
|
+
${dataBlock}
|
|
1787
2789
|
c.set("htmlLanguage", route.entry.publicLocation.metadata?.["l10n.lang"]);
|
|
1788
2790
|
c.set("headTags", route.entry.headTags ?? []);
|
|
1789
2791
|
|
|
1790
2792
|
return c.render(
|
|
1791
2793
|
<SiteArticle
|
|
1792
2794
|
post={post}
|
|
1793
|
-
|
|
2795
|
+
${propLines.join("\n")}
|
|
1794
2796
|
/>,
|
|
1795
2797
|
);
|
|
1796
2798
|
},
|
|
1797
|
-
)
|
|
1798
|
-
`;
|
|
2799
|
+
);${titleHelper}`;
|
|
1799
2800
|
}
|
|
1800
2801
|
|
|
1801
2802
|
// src/scaffold/localized-content.ts
|
|
@@ -2160,7 +3161,7 @@ function localizedContentFiles(variables, preset) {
|
|
|
2160
3161
|
(language) => themesContent(language, preset)
|
|
2161
3162
|
);
|
|
2162
3163
|
} else if (page === "guide") {
|
|
2163
|
-
pushEnglishOnly("guide", guideContent);
|
|
3164
|
+
pushEnglishOnly("guide", () => guideContent(preset));
|
|
2164
3165
|
} else if (page === "examples") {
|
|
2165
3166
|
pushLocalized(
|
|
2166
3167
|
"examples",
|
|
@@ -2354,10 +3355,50 @@ var INDEX = {
|
|
|
2354
3355
|
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
3356
|
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
3357
|
ko: "번역 페이지는 기본 파일 옆에 `<base>.<lang>.md` 규칙으로 둡니다(예: `about.ja.md`). l10n 플러그인이 `/lang/` 경로로 서빙하며 자동으로 링크합니다."
|
|
3358
|
+
},
|
|
3359
|
+
editFileNote: {
|
|
3360
|
+
en: "This page is `content/index.md`. Open that file, change it, and save — the browser updates while you write.",
|
|
3361
|
+
ja: "このページは `content/index.md` です。ファイルを開いて書き換えると、書いている途中でもブラウザの表示が更新されます。",
|
|
3362
|
+
"zh-CN": "这个页面来自 `content/index.md`。打开这个文件修改并保存,浏览器会随你的输入更新。",
|
|
3363
|
+
es: "Esta página viene de `content/index.md`. Abre ese archivo, edítalo y guarda: el navegador se actualiza mientras escribes.",
|
|
3364
|
+
de: "Diese Seite stammt aus `content/index.md`. Öffne die Datei, ändere sie und speichere — der Browser aktualisiert sich beim Schreiben.",
|
|
3365
|
+
fr: "Cette page provient de `content/index.md`. Ouvrez ce fichier, modifiez-le et enregistrez — le navigateur se met à jour pendant que vous écrivez.",
|
|
3366
|
+
ko: "이 페이지는 `content/index.md`에서 만들어집니다. 그 파일을 열어 수정하고 저장하면 글을 쓰는 동안 브라우저가 갱신됩니다."
|
|
3367
|
+
},
|
|
3368
|
+
linkLine: {
|
|
3369
|
+
en: "Link pages with a WikiLink, for example `[[guide]]`.",
|
|
3370
|
+
ja: "ページ同士は WikiLink でリンクできます(例:`[[guide]]`)。",
|
|
3371
|
+
"zh-CN": "用 WikiLink 连接页面,例如 `[[guide]]`。",
|
|
3372
|
+
es: "Enlaza páginas con un WikiLink, por ejemplo `[[guide]]`.",
|
|
3373
|
+
de: "Verbinde Seiten mit einem WikiLink, zum Beispiel `[[guide]]`.",
|
|
3374
|
+
fr: "Reliez les pages avec un WikiLink, par exemple `[[guide]]`.",
|
|
3375
|
+
ko: "WikiLink로 페이지를 연결하세요. 예: `[[guide]]`."
|
|
2357
3376
|
}
|
|
2358
3377
|
};
|
|
3378
|
+
function starterIndexContent(title, language) {
|
|
3379
|
+
return [
|
|
3380
|
+
frontmatter(),
|
|
3381
|
+
heading(1, title),
|
|
3382
|
+
"",
|
|
3383
|
+
read(INDEX.lead, language),
|
|
3384
|
+
"",
|
|
3385
|
+
read(INDEX.editFileNote, language),
|
|
3386
|
+
"",
|
|
3387
|
+
heading(2, read(INDEX.editHeading, language)),
|
|
3388
|
+
read(INDEX.editLead, language),
|
|
3389
|
+
"",
|
|
3390
|
+
read(INDEX.linkLine, language),
|
|
3391
|
+
"",
|
|
3392
|
+
heading(2, read(INDEX.exploreHeading, language)),
|
|
3393
|
+
"",
|
|
3394
|
+
"- [Getting started](/guide)",
|
|
3395
|
+
"- [[examples]]",
|
|
3396
|
+
""
|
|
3397
|
+
].join("\n");
|
|
3398
|
+
}
|
|
2359
3399
|
function indexContent(variables, preset, language) {
|
|
2360
3400
|
const title = variables.title;
|
|
3401
|
+
if (preset.name === "starter") return starterIndexContent(title, language);
|
|
2361
3402
|
return [
|
|
2362
3403
|
frontmatter(),
|
|
2363
3404
|
heading(1, title),
|
|
@@ -2371,10 +3412,6 @@ function indexContent(variables, preset, language) {
|
|
|
2371
3412
|
`- [${read(INDEX.exploreThemes, language)}](/framework/themes)`,
|
|
2372
3413
|
"- [Working examples](/examples/)",
|
|
2373
3414
|
"- [Plugin reference](/reference/plugins/)"
|
|
2374
|
-
] : preset.name === "starter" ? [
|
|
2375
|
-
heading(2, read(INDEX.exploreHeading, language)),
|
|
2376
|
-
"- [Getting started](/guide/)",
|
|
2377
|
-
"- [Example notes](/examples/)"
|
|
2378
3415
|
] : [],
|
|
2379
3416
|
heading(2, read(INDEX.editHeading, language)),
|
|
2380
3417
|
read(INDEX.editLead, language),
|
|
@@ -2426,7 +3463,7 @@ var THEMES_COPY = {
|
|
|
2426
3463
|
es: "Este starter usa `@riebeckite/theme-default`. Para probar otro tema:",
|
|
2427
3464
|
de: "Dieser Starter verwendet `@riebeckite/theme-default`. Um ein anderes Theme zu testen:",
|
|
2428
3465
|
fr: "Ce starter utilise `@riebeckite/theme-default`. Pour essayer un autre thème :",
|
|
2429
|
-
ko: "이 스타터는 `@riebeckite/theme-default`를 사용합니다. 다른 테마를
|
|
3466
|
+
ko: "이 스타터는 `@riebeckite/theme-default`를 사용합니다. 다른 테마를 시도하려면:"
|
|
2430
3467
|
},
|
|
2431
3468
|
switchStep1: {
|
|
2432
3469
|
en: "Install the package:",
|
|
@@ -2435,7 +3472,7 @@ var THEMES_COPY = {
|
|
|
2435
3472
|
es: "Instala el paquete:",
|
|
2436
3473
|
de: "Installiere das Paket:",
|
|
2437
3474
|
fr: "Installez le paquet :",
|
|
2438
|
-
ko: "패키지를
|
|
3475
|
+
ko: "패키지를 설치합니다:"
|
|
2439
3476
|
},
|
|
2440
3477
|
switchStep2: {
|
|
2441
3478
|
en: "Point `theme` at the new factory in `riebeckite.config.ts`:",
|
|
@@ -2444,7 +3481,7 @@ var THEMES_COPY = {
|
|
|
2444
3481
|
es: "Apunta `theme` al nuevo factory en `riebeckite.config.ts`:",
|
|
2445
3482
|
de: "Setze `theme` in `riebeckite.config.ts` auf den neuen Factory:",
|
|
2446
3483
|
fr: "Pointez `theme` vers le nouveau factory dans `riebeckite.config.ts` :",
|
|
2447
|
-
ko: "`riebeckite.config.ts`의 `theme`를 새 팩토리로
|
|
3484
|
+
ko: "`riebeckite.config.ts`의 `theme`를 새 팩토리로 변경합니다:"
|
|
2448
3485
|
},
|
|
2449
3486
|
tableHeading: {
|
|
2450
3487
|
en: "The bundled themes",
|
|
@@ -2664,7 +3701,7 @@ var PLUGINS_COPY = {
|
|
|
2664
3701
|
es: "Instala el paquete:",
|
|
2665
3702
|
de: "Installiere das Paket:",
|
|
2666
3703
|
fr: "Installez le paquet :",
|
|
2667
|
-
ko: "패키지를
|
|
3704
|
+
ko: "패키지를 설치합니다:"
|
|
2668
3705
|
},
|
|
2669
3706
|
installStep2: {
|
|
2670
3707
|
en: "Register it in the `plugins` array of `riebeckite.config.ts`:",
|
|
@@ -2673,7 +3710,7 @@ var PLUGINS_COPY = {
|
|
|
2673
3710
|
es: "Regístralo en el array `plugins` de `riebeckite.config.ts`:",
|
|
2674
3711
|
de: "Registriere es im `plugins`-Array der `riebeckite.config.ts`:",
|
|
2675
3712
|
fr: "Enregistrez-le dans le tableau `plugins` de `riebeckite.config.ts` :",
|
|
2676
|
-
ko: "`riebeckite.config.ts`의 `plugins` 배열에
|
|
3713
|
+
ko: "`riebeckite.config.ts`의 `plugins` 배열에 등록합니다:"
|
|
2677
3714
|
},
|
|
2678
3715
|
fullList: {
|
|
2679
3716
|
en: "The complete plugin index lives in the repository:",
|
|
@@ -2682,7 +3719,7 @@ var PLUGINS_COPY = {
|
|
|
2682
3719
|
es: "El índice completo de plugins vive en el repositorio:",
|
|
2683
3720
|
de: "Der vollständige Plugin-Index liegt im Repository:",
|
|
2684
3721
|
fr: "L'index complet des plugins se trouve dans le dépôt :",
|
|
2685
|
-
ko: "전체 플러그인 목록은 저장소에
|
|
3722
|
+
ko: "전체 플러그인 목록은 저장소에 있습니다:"
|
|
2686
3723
|
},
|
|
2687
3724
|
fullListLabel: {
|
|
2688
3725
|
en: "Riebeckite plugins on GitHub",
|
|
@@ -3188,7 +4225,65 @@ function pluginsContent(language) {
|
|
|
3188
4225
|
lines.push("");
|
|
3189
4226
|
return lines.join("\n");
|
|
3190
4227
|
}
|
|
3191
|
-
function
|
|
4228
|
+
function starterGuideContent() {
|
|
4229
|
+
return [
|
|
4230
|
+
frontmatter(),
|
|
4231
|
+
heading(1, "Getting started"),
|
|
4232
|
+
"",
|
|
4233
|
+
"This site was generated from a Riebeckite scaffold preset.",
|
|
4234
|
+
"Everything below lives in this repository, ready to edit.",
|
|
4235
|
+
"",
|
|
4236
|
+
heading(2, "Run the site"),
|
|
4237
|
+
"",
|
|
4238
|
+
codeBlock("sh", ["npm install", "npm exec riebeckite dev"].join("\n")),
|
|
4239
|
+
"",
|
|
4240
|
+
"Open the URL printed in the terminal. The page reloads every time",
|
|
4241
|
+
"you save a Markdown file.",
|
|
4242
|
+
"",
|
|
4243
|
+
heading(2, "Add a page"),
|
|
4244
|
+
"",
|
|
4245
|
+
"Create `content/hello.md`:",
|
|
4246
|
+
"",
|
|
4247
|
+
codeBlock(
|
|
4248
|
+
"md",
|
|
4249
|
+
[
|
|
4250
|
+
"---",
|
|
4251
|
+
"publish: true",
|
|
4252
|
+
"---",
|
|
4253
|
+
"",
|
|
4254
|
+
"# Hello",
|
|
4255
|
+
"",
|
|
4256
|
+
"This is my second page."
|
|
4257
|
+
].join("\n")
|
|
4258
|
+
),
|
|
4259
|
+
"",
|
|
4260
|
+
"The dev server serves it at `/hello`.",
|
|
4261
|
+
"",
|
|
4262
|
+
heading(2, "Link the pages"),
|
|
4263
|
+
"",
|
|
4264
|
+
"Write `[[hello]]` anywhere and it becomes a link to that page.",
|
|
4265
|
+
"",
|
|
4266
|
+
heading(2, "Translate a page"),
|
|
4267
|
+
"",
|
|
4268
|
+
"Copy a file next to the original with the language suffix, for",
|
|
4269
|
+
"example `hello.ja.md`. The language switcher picks it up.",
|
|
4270
|
+
"",
|
|
4271
|
+
heading(2, "Build"),
|
|
4272
|
+
"",
|
|
4273
|
+
codeBlock("sh", "npm exec riebeckite build"),
|
|
4274
|
+
"",
|
|
4275
|
+
"The static site is written to `dist/`.",
|
|
4276
|
+
"",
|
|
4277
|
+
heading(2, "Extend"),
|
|
4278
|
+
"",
|
|
4279
|
+
"Plugins and themes are registered in `riebeckite.config.ts`.",
|
|
4280
|
+
"Install a package, import its factory, and add it to the `plugins`",
|
|
4281
|
+
"array, or point `theme` at a new theme factory.",
|
|
4282
|
+
""
|
|
4283
|
+
].join("\n");
|
|
4284
|
+
}
|
|
4285
|
+
function guideContent(preset) {
|
|
4286
|
+
if (preset.name !== "showcase") return starterGuideContent();
|
|
3192
4287
|
return [
|
|
3193
4288
|
frontmatter(),
|
|
3194
4289
|
heading(1, "Getting started"),
|
|
@@ -3213,9 +4308,9 @@ function guideContent() {
|
|
|
3213
4308
|
codeBlock(
|
|
3214
4309
|
"sh",
|
|
3215
4310
|
[
|
|
3216
|
-
"
|
|
3217
|
-
"
|
|
3218
|
-
"
|
|
4311
|
+
"npm install",
|
|
4312
|
+
"npm exec riebeckite dev",
|
|
4313
|
+
"npm exec riebeckite build"
|
|
3219
4314
|
].join("\n")
|
|
3220
4315
|
),
|
|
3221
4316
|
"",
|
|
@@ -3253,7 +4348,77 @@ var SHOWCASE_GUIDE_DEMOS = [
|
|
|
3253
4348
|
"@riebeckite/plugin-canvas",
|
|
3254
4349
|
"@riebeckite/plugin-flashcards"
|
|
3255
4350
|
];
|
|
4351
|
+
var STARTER_EXAMPLES = {
|
|
4352
|
+
heading: {
|
|
4353
|
+
en: "Example",
|
|
4354
|
+
ja: "サンプル"
|
|
4355
|
+
},
|
|
4356
|
+
intro: {
|
|
4357
|
+
en: "A small page showing what Riebeckite Markdown can do. Copy any section into your own files.",
|
|
4358
|
+
ja: "Riebeckite の Markdown でできることをまとめた小さなページです。必要なセクションを自分のファイルにコピーできます。"
|
|
4359
|
+
},
|
|
4360
|
+
linksHeading: {
|
|
4361
|
+
en: "Links",
|
|
4362
|
+
ja: "リンク"
|
|
4363
|
+
},
|
|
4364
|
+
linksBody: {
|
|
4365
|
+
en: "WikiLinks connect pages: [[guide]] opens Getting started. Regular Markdown links work as well — [Home](/) returns to the home page.",
|
|
4366
|
+
ja: "WikiLink でページをつなげます:[[guide]] は Getting started を開きます。通常の Markdown リンクも使えます — [Home](/) でホームに戻れます。"
|
|
4367
|
+
},
|
|
4368
|
+
codeHeading: {
|
|
4369
|
+
en: "Code",
|
|
4370
|
+
ja: "コード"
|
|
4371
|
+
},
|
|
4372
|
+
codeIntro: {
|
|
4373
|
+
en: "A fenced code block keeps its formatting and gains a toolbar from the code plugins:",
|
|
4374
|
+
ja: "フェンス付きコードブロックは書式を保ったまま表示され、コード系プラグインでツールバーが付きます:"
|
|
4375
|
+
},
|
|
4376
|
+
frontmatterHeading: {
|
|
4377
|
+
en: "Frontmatter",
|
|
4378
|
+
ja: "フロントマター"
|
|
4379
|
+
},
|
|
4380
|
+
frontmatterBody: {
|
|
4381
|
+
en: "The block between the `---` lines at the top of a file controls the page. `publish: true` keeps it in the built site:",
|
|
4382
|
+
ja: "ファイル先頭の `---` に挟まれた部分がページを制御します。`publish: true` を書き続けるとビルド対象に残ります:"
|
|
4383
|
+
}
|
|
4384
|
+
};
|
|
4385
|
+
function starterExamplesContent(language) {
|
|
4386
|
+
return [
|
|
4387
|
+
frontmatter(),
|
|
4388
|
+
heading(1, readSummary(STARTER_EXAMPLES.heading, language)),
|
|
4389
|
+
"",
|
|
4390
|
+
readSummary(STARTER_EXAMPLES.intro, language),
|
|
4391
|
+
"",
|
|
4392
|
+
heading(2, readSummary(STARTER_EXAMPLES.linksHeading, language)),
|
|
4393
|
+
"",
|
|
4394
|
+
readSummary(STARTER_EXAMPLES.linksBody, language),
|
|
4395
|
+
"",
|
|
4396
|
+
heading(2, readSummary(STARTER_EXAMPLES.codeHeading, language)),
|
|
4397
|
+
"",
|
|
4398
|
+
readSummary(STARTER_EXAMPLES.codeIntro, language),
|
|
4399
|
+
"",
|
|
4400
|
+
codeBlock(
|
|
4401
|
+
"ts",
|
|
4402
|
+
[
|
|
4403
|
+
"export function hello(): string {",
|
|
4404
|
+
' return "Hello from Riebeckite";',
|
|
4405
|
+
"}"
|
|
4406
|
+
].join("\n")
|
|
4407
|
+
),
|
|
4408
|
+
"",
|
|
4409
|
+
heading(2, readSummary(STARTER_EXAMPLES.frontmatterHeading, language)),
|
|
4410
|
+
"",
|
|
4411
|
+
readSummary(STARTER_EXAMPLES.frontmatterBody, language),
|
|
4412
|
+
"",
|
|
4413
|
+
codeBlock(
|
|
4414
|
+
"yaml",
|
|
4415
|
+
["---", "publish: true", "title: Example", "---"].join("\n")
|
|
4416
|
+
),
|
|
4417
|
+
""
|
|
4418
|
+
].join("\n");
|
|
4419
|
+
}
|
|
3256
4420
|
function examplesContent(preset, language) {
|
|
4421
|
+
if (preset.name === "starter") return starterExamplesContent(language);
|
|
3257
4422
|
const lines = [
|
|
3258
4423
|
frontmatter(),
|
|
3259
4424
|
heading(1, readSummary(EXAMPLES_COPY.heading, language)),
|
|
@@ -3308,7 +4473,7 @@ function referencePluginsContent(preset, variables) {
|
|
|
3308
4473
|
"Every registered plugin is listed below with the Markdown that triggers it,",
|
|
3309
4474
|
"or with the Markdown/frontmatter/content shape it reads when the plugin is",
|
|
3310
4475
|
"site-wide rather than block-based. Paste the examples into files under",
|
|
3311
|
-
"`content/` and run `
|
|
4476
|
+
"`content/` and run `npm exec riebeckite dev` to inspect the rendered result.",
|
|
3312
4477
|
""
|
|
3313
4478
|
);
|
|
3314
4479
|
for (const plugin of preset.plugins) {
|
|
@@ -3373,7 +4538,7 @@ function referenceThemesContent(preset) {
|
|
|
3373
4538
|
}
|
|
3374
4539
|
|
|
3375
4540
|
// src/scaffold/version.ts
|
|
3376
|
-
var RIEBECKITE_VERSION = "^0.0.
|
|
4541
|
+
var RIEBECKITE_VERSION = "^0.0.15";
|
|
3377
4542
|
|
|
3378
4543
|
// src/scaffold/templates.ts
|
|
3379
4544
|
function siteTemplateFiles(preset, variables) {
|
|
@@ -3403,7 +4568,7 @@ function readPackageAsset(fileName) {
|
|
|
3403
4568
|
new URL(`../assets/${fileName}`, import.meta.url)
|
|
3404
4569
|
];
|
|
3405
4570
|
for (const candidate of candidates) {
|
|
3406
|
-
if (
|
|
4571
|
+
if (fs7.existsSync(candidate)) return fs7.readFileSync(candidate);
|
|
3407
4572
|
}
|
|
3408
4573
|
throw new Error(`Missing Riebeckite scaffold asset: ${fileName}`);
|
|
3409
4574
|
}
|
|
@@ -3432,7 +4597,7 @@ function appFiles(preset) {
|
|
|
3432
4597
|
style: () => style(preset),
|
|
3433
4598
|
renderer: () => renderer(preset),
|
|
3434
4599
|
index: () => indexRoute(preset),
|
|
3435
|
-
slug: () => slugRoute(),
|
|
4600
|
+
slug: () => slugRoute(preset),
|
|
3436
4601
|
header: () => siteHeader(),
|
|
3437
4602
|
article: () => article()
|
|
3438
4603
|
};
|
|
@@ -3688,7 +4853,7 @@ var README_WORDS = {
|
|
|
3688
4853
|
demoPages: "Try the demos",
|
|
3689
4854
|
demoPagesBody: "The generated site ships content pages that exercise these features:",
|
|
3690
4855
|
copyDemos: "Copy-paste demos",
|
|
3691
|
-
copyDemosBody: "Paste any of these snippets into a Markdown file under `content/` and run `
|
|
4856
|
+
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
4857
|
},
|
|
3693
4858
|
ja: {
|
|
3694
4859
|
included: "含まれているもの",
|
|
@@ -3712,7 +4877,7 @@ var README_WORDS = {
|
|
|
3712
4877
|
demoPages: "デモを試す",
|
|
3713
4878
|
demoPagesBody: "生成されたサイトには、各機能を体験できるコンテンツページが含まれています:",
|
|
3714
4879
|
copyDemos: "コピーして使えるデモ",
|
|
3715
|
-
copyDemosBody: "以下のスニペットを `content/` 配下の Markdown ファイルに貼り付けて `
|
|
4880
|
+
copyDemosBody: "以下のスニペットを `content/` 配下の Markdown ファイルに貼り付けて `npm exec riebeckite dev` を実行してください。それぞれ、このプリセットが登録しているプラグインでレンダリングされます。"
|
|
3716
4881
|
}
|
|
3717
4882
|
};
|
|
3718
4883
|
function readme(preset, variables) {
|
|
@@ -3745,10 +4910,10 @@ function readmeEn(preset, variables) {
|
|
|
3745
4910
|
lines.push(`## ${words.commands}`, "");
|
|
3746
4911
|
lines.push(
|
|
3747
4912
|
"```sh",
|
|
3748
|
-
"
|
|
3749
|
-
"
|
|
3750
|
-
"
|
|
3751
|
-
"
|
|
4913
|
+
"npm install",
|
|
4914
|
+
"npm exec riebeckite check",
|
|
4915
|
+
"npm exec riebeckite dev",
|
|
4916
|
+
"npm exec riebeckite build",
|
|
3752
4917
|
"```",
|
|
3753
4918
|
""
|
|
3754
4919
|
);
|
|
@@ -3793,10 +4958,10 @@ function readmeJa(preset, variables) {
|
|
|
3793
4958
|
lines.push(`## ${words.commands}`, "");
|
|
3794
4959
|
lines.push(
|
|
3795
4960
|
"```sh",
|
|
3796
|
-
"
|
|
3797
|
-
"
|
|
3798
|
-
"
|
|
3799
|
-
"
|
|
4961
|
+
"npm install",
|
|
4962
|
+
"npm exec riebeckite check",
|
|
4963
|
+
"npm exec riebeckite dev",
|
|
4964
|
+
"npm exec riebeckite build",
|
|
3800
4965
|
"```",
|
|
3801
4966
|
""
|
|
3802
4967
|
);
|
|
@@ -4263,7 +5428,7 @@ var ScaffoldSiteError = class extends Error {
|
|
|
4263
5428
|
}
|
|
4264
5429
|
};
|
|
4265
5430
|
async function scaffoldRiebeckiteSite(options) {
|
|
4266
|
-
const targetDirectory =
|
|
5431
|
+
const targetDirectory = path9.resolve(options.targetDirectory);
|
|
4267
5432
|
const preset = resolveScaffoldPreset(options.preset);
|
|
4268
5433
|
validateDeploymentOptions(options);
|
|
4269
5434
|
const files = [
|
|
@@ -4276,9 +5441,9 @@ async function scaffoldRiebeckiteSite(options) {
|
|
|
4276
5441
|
options.overwrite ?? false
|
|
4277
5442
|
);
|
|
4278
5443
|
for (const file of files) {
|
|
4279
|
-
const filePath =
|
|
4280
|
-
await
|
|
4281
|
-
await
|
|
5444
|
+
const filePath = path9.join(targetDirectory, file.path);
|
|
5445
|
+
await fs8.mkdir(path9.dirname(filePath), { recursive: true });
|
|
5446
|
+
await fs8.writeFile(filePath, file.content);
|
|
4282
5447
|
}
|
|
4283
5448
|
return { targetDirectory, files: files.map((file) => file.path) };
|
|
4284
5449
|
}
|
|
@@ -4314,7 +5479,7 @@ function validateDeploymentOptions(options) {
|
|
|
4314
5479
|
}
|
|
4315
5480
|
function templateVariables(options, targetDirectory) {
|
|
4316
5481
|
const name = normalizePackageName(
|
|
4317
|
-
options.name ??
|
|
5482
|
+
options.name ?? path9.basename(targetDirectory)
|
|
4318
5483
|
);
|
|
4319
5484
|
return {
|
|
4320
5485
|
name,
|
|
@@ -4327,9 +5492,9 @@ function templateVariables(options, targetDirectory) {
|
|
|
4327
5492
|
async function assertTargetWritable(targetDirectory, files, overwrite) {
|
|
4328
5493
|
let entries;
|
|
4329
5494
|
try {
|
|
4330
|
-
entries = await
|
|
5495
|
+
entries = await fs8.readdir(targetDirectory);
|
|
4331
5496
|
} catch (error) {
|
|
4332
|
-
if (
|
|
5497
|
+
if (isNotFoundError3(error)) return;
|
|
4333
5498
|
throw error;
|
|
4334
5499
|
}
|
|
4335
5500
|
const conflicts = entries.filter(
|
|
@@ -4347,20 +5512,31 @@ function normalizePackageName(value) {
|
|
|
4347
5512
|
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[-_.]+/, "").replace(/[-_.]+$/, "");
|
|
4348
5513
|
return normalized || "riebeckite-site";
|
|
4349
5514
|
}
|
|
4350
|
-
function
|
|
5515
|
+
function isNotFoundError3(error) {
|
|
4351
5516
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
4352
5517
|
}
|
|
4353
5518
|
|
|
4354
5519
|
// src/scaffold/next-steps.ts
|
|
4355
|
-
function formatScaffoldNextSteps(relativeDirectory) {
|
|
5520
|
+
function formatScaffoldNextSteps(relativeDirectory, options = {}) {
|
|
4356
5521
|
const lines = ["Next steps:"];
|
|
4357
5522
|
if (relativeDirectory !== ".") {
|
|
4358
5523
|
lines.push(` cd ${relativeDirectory}`);
|
|
4359
5524
|
}
|
|
4360
|
-
lines.push("
|
|
4361
|
-
lines.push("
|
|
4362
|
-
lines.push("
|
|
4363
|
-
lines.push("
|
|
5525
|
+
lines.push(" npm install");
|
|
5526
|
+
lines.push(" npm exec riebeckite check");
|
|
5527
|
+
lines.push(" npm exec riebeckite dev");
|
|
5528
|
+
lines.push(" npm exec riebeckite build");
|
|
5529
|
+
if (options.editFile !== void 0) {
|
|
5530
|
+
lines.push("");
|
|
5531
|
+
lines.push("Then edit:");
|
|
5532
|
+
lines.push(` ${options.editFile}`);
|
|
5533
|
+
}
|
|
5534
|
+
if (options.externalContent === true) {
|
|
5535
|
+
lines.push("");
|
|
5536
|
+
lines.push(
|
|
5537
|
+
"Copy github/notify-site.yml to the content repository as .github/workflows/notify-site.yml."
|
|
5538
|
+
);
|
|
5539
|
+
}
|
|
4364
5540
|
return lines.join("\n");
|
|
4365
5541
|
}
|
|
4366
5542
|
export {
|