diffwiki-core 0.7.0 → 0.8.0-rc.202607252213.c724769
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +329 -82
- package/dist/index.d.cts +123 -2
- package/dist/index.d.ts +123 -2
- package/dist/index.js +320 -80
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -570,6 +570,7 @@ __export(index_exports, {
|
|
|
570
570
|
InvalidTargetError: () => InvalidTargetError,
|
|
571
571
|
NATIVE_ENGINE: () => NATIVE_ENGINE,
|
|
572
572
|
NoDefaultCollectionError: () => NoDefaultCollectionError,
|
|
573
|
+
PROJECT_CONFIG_FILE: () => PROJECT_CONFIG_FILE,
|
|
573
574
|
PluginError: () => PluginError,
|
|
574
575
|
REPO_CONFIG_FILE: () => REPO_CONFIG_FILE,
|
|
575
576
|
WorktreeNotAllowedError: () => WorktreeNotAllowedError,
|
|
@@ -580,8 +581,10 @@ __export(index_exports, {
|
|
|
580
581
|
buildLinkGraph: () => buildLinkGraph,
|
|
581
582
|
buildNavTree: () => buildNavTree,
|
|
582
583
|
buildSearchIndex: () => buildSearchIndex,
|
|
584
|
+
buildSite: () => buildSite,
|
|
583
585
|
collectionDir: () => collectionDir,
|
|
584
586
|
collectionGitStatus: () => collectionGitStatus,
|
|
587
|
+
collectionMeta: () => collectionMeta,
|
|
585
588
|
collectionsDir: () => collectionsDir,
|
|
586
589
|
configPath: () => configPath,
|
|
587
590
|
createArticle: () => createArticle,
|
|
@@ -623,8 +626,10 @@ __export(index_exports, {
|
|
|
623
626
|
readArticle: () => readArticle,
|
|
624
627
|
readConfig: () => readConfig,
|
|
625
628
|
readPluginRegistry: () => readPluginRegistry,
|
|
629
|
+
readProjectConfig: () => readProjectConfig,
|
|
626
630
|
readRegistry: () => readRegistry,
|
|
627
631
|
readRepoConfig: () => readRepoConfig,
|
|
632
|
+
readSiteConfig: () => readSiteConfig,
|
|
628
633
|
registerCollection: () => registerCollection,
|
|
629
634
|
registerPlugin: () => registerPlugin,
|
|
630
635
|
registryPath: () => registryPath,
|
|
@@ -641,7 +646,9 @@ __export(index_exports, {
|
|
|
641
646
|
resolveHome: () => resolveHome,
|
|
642
647
|
resolveLogLevel: () => resolveLogLevel,
|
|
643
648
|
resolvePage: () => resolvePage,
|
|
649
|
+
resolveProjectCollections: () => resolveProjectCollections,
|
|
644
650
|
search: () => search,
|
|
651
|
+
seedProjectHome: () => seedProjectHome,
|
|
645
652
|
serializeArticle: () => serializeArticle,
|
|
646
653
|
setConfigValue: () => setConfigValue,
|
|
647
654
|
setTags: () => setTags,
|
|
@@ -729,22 +736,39 @@ var YAML_ENGINE = {
|
|
|
729
736
|
stringify: (data) => (0, import_yaml.stringify)(data)
|
|
730
737
|
};
|
|
731
738
|
var MATTER_OPTS = { engines: { yaml: YAML_ENGINE } };
|
|
739
|
+
function coerceAudience(value) {
|
|
740
|
+
return value === "private" ? "private" : "public";
|
|
741
|
+
}
|
|
742
|
+
function coerceStatus(value) {
|
|
743
|
+
return value === "draft" ? "draft" : "published";
|
|
744
|
+
}
|
|
732
745
|
function parseFallback(raw) {
|
|
733
746
|
const fenceRe = /^---[ \t]*\n([\s\S]*?)\n---[ \t]*\n/;
|
|
734
747
|
const fenceMatch = fenceRe.exec(raw);
|
|
735
748
|
let title = "";
|
|
749
|
+
let audience = "public";
|
|
750
|
+
let status = "published";
|
|
736
751
|
let body;
|
|
752
|
+
const clean = (s) => s.trim().replace(/^["']|["']$/g, "");
|
|
737
753
|
if (fenceMatch) {
|
|
738
754
|
const inner = fenceMatch[1];
|
|
739
755
|
const titleMatch = /^title:\s*(.+)$/m.exec(inner);
|
|
740
756
|
if (titleMatch) {
|
|
741
|
-
title = titleMatch[1]
|
|
757
|
+
title = clean(titleMatch[1]);
|
|
758
|
+
}
|
|
759
|
+
const audienceMatch = /^audience:\s*(.+)$/m.exec(inner);
|
|
760
|
+
if (audienceMatch) {
|
|
761
|
+
audience = coerceAudience(clean(audienceMatch[1]));
|
|
762
|
+
}
|
|
763
|
+
const statusMatch = /^status:\s*(.+)$/m.exec(inner);
|
|
764
|
+
if (statusMatch) {
|
|
765
|
+
status = coerceStatus(clean(statusMatch[1]));
|
|
742
766
|
}
|
|
743
767
|
body = raw.slice(fenceMatch[0].length);
|
|
744
768
|
} else {
|
|
745
769
|
body = raw;
|
|
746
770
|
}
|
|
747
|
-
return { title, tags: [], body: body.replace(/^\n+/, "") };
|
|
771
|
+
return { title, tags: [], audience, status, body: body.replace(/^\n+/, "") };
|
|
748
772
|
}
|
|
749
773
|
function parseArticle(raw) {
|
|
750
774
|
try {
|
|
@@ -752,13 +776,17 @@ function parseArticle(raw) {
|
|
|
752
776
|
const title = typeof data.title === "string" ? data.title : "";
|
|
753
777
|
const tags = Array.isArray(data.tags) ? data.tags.map((t) => String(t)) : [];
|
|
754
778
|
const created = typeof data.created === "string" ? data.created : void 0;
|
|
755
|
-
|
|
779
|
+
const audience = coerceAudience(data.audience);
|
|
780
|
+
const status = coerceStatus(data.status);
|
|
781
|
+
return { title, tags, created, audience, status, body: content.replace(/^\n+/, "") };
|
|
756
782
|
} catch {
|
|
757
783
|
return parseFallback(raw);
|
|
758
784
|
}
|
|
759
785
|
}
|
|
760
786
|
function serializeArticle(article) {
|
|
761
787
|
const front = { title: article.title, tags: article.tags };
|
|
788
|
+
if (article.audience === "private") front.audience = "private";
|
|
789
|
+
if (article.status === "draft") front.status = "draft";
|
|
762
790
|
return import_gray_matter.default.stringify(article.body, front, MATTER_OPTS);
|
|
763
791
|
}
|
|
764
792
|
|
|
@@ -839,7 +867,17 @@ async function loadArticle(target) {
|
|
|
839
867
|
async function updateArticleBody(target, body) {
|
|
840
868
|
const { filePath, dir, raw, collection } = await loadArticle(target);
|
|
841
869
|
const parsed = parseArticle(raw);
|
|
842
|
-
await import_promises5.default.writeFile(
|
|
870
|
+
await import_promises5.default.writeFile(
|
|
871
|
+
filePath,
|
|
872
|
+
serializeArticle({
|
|
873
|
+
title: parsed.title,
|
|
874
|
+
tags: parsed.tags,
|
|
875
|
+
audience: parsed.audience,
|
|
876
|
+
status: parsed.status,
|
|
877
|
+
body
|
|
878
|
+
}),
|
|
879
|
+
"utf8"
|
|
880
|
+
);
|
|
843
881
|
void fireHook2("article-updated", collection, import_node_path3.default.relative(dir, filePath));
|
|
844
882
|
return { title: parsed.title, tags: parsed.tags, path: filePath, collection };
|
|
845
883
|
}
|
|
@@ -847,7 +885,17 @@ async function mutateTags(target, fn) {
|
|
|
847
885
|
const { filePath, dir, raw, collection } = await loadArticle(target);
|
|
848
886
|
const parsed = parseArticle(raw);
|
|
849
887
|
const tags = fn(parsed.tags);
|
|
850
|
-
await import_promises5.default.writeFile(
|
|
888
|
+
await import_promises5.default.writeFile(
|
|
889
|
+
filePath,
|
|
890
|
+
serializeArticle({
|
|
891
|
+
title: parsed.title,
|
|
892
|
+
tags,
|
|
893
|
+
audience: parsed.audience,
|
|
894
|
+
status: parsed.status,
|
|
895
|
+
body: parsed.body
|
|
896
|
+
}),
|
|
897
|
+
"utf8"
|
|
898
|
+
);
|
|
851
899
|
void fireHook2("article-updated", collection, import_node_path3.default.relative(dir, filePath));
|
|
852
900
|
return { title: parsed.title, tags, path: filePath, collection };
|
|
853
901
|
}
|
|
@@ -1085,12 +1133,114 @@ async function collectionGitStatus(entry) {
|
|
|
1085
1133
|
return { branch: s.branch, ahead: s.ahead, behind: s.behind, hasUpstream: s.hasUpstream };
|
|
1086
1134
|
}
|
|
1087
1135
|
|
|
1136
|
+
// src/project.ts
|
|
1137
|
+
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
1138
|
+
var import_node_os2 = __toESM(require("os"), 1);
|
|
1139
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
1140
|
+
var import_yaml3 = require("yaml");
|
|
1141
|
+
var PROJECT_CONFIG_FILE = "diffwiki.yaml";
|
|
1142
|
+
function normaliseSite(raw) {
|
|
1143
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
1144
|
+
const r = raw;
|
|
1145
|
+
const title = typeof r.title === "string" ? r.title : void 0;
|
|
1146
|
+
const logo = typeof r.logo === "string" ? r.logo : void 0;
|
|
1147
|
+
const repo = typeof r.repo === "string" ? r.repo : void 0;
|
|
1148
|
+
let home;
|
|
1149
|
+
if (r.home && typeof r.home === "object") {
|
|
1150
|
+
const h = r.home;
|
|
1151
|
+
const tagline = typeof h.tagline === "string" ? h.tagline : void 0;
|
|
1152
|
+
const cards = [];
|
|
1153
|
+
if (Array.isArray(h.cards)) {
|
|
1154
|
+
for (const c of h.cards) {
|
|
1155
|
+
if (!c || typeof c !== "object") continue;
|
|
1156
|
+
const cc = c;
|
|
1157
|
+
if (typeof cc.title === "string" && typeof cc.link === "string") {
|
|
1158
|
+
cards.push({
|
|
1159
|
+
title: cc.title,
|
|
1160
|
+
link: cc.link,
|
|
1161
|
+
...typeof cc.icon === "string" ? { icon: cc.icon } : {},
|
|
1162
|
+
...typeof cc.subtitle === "string" ? { subtitle: cc.subtitle } : {}
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
home = { ...tagline !== void 0 ? { tagline } : {}, ...cards.length > 0 ? { cards } : {} };
|
|
1168
|
+
if (Object.keys(home).length === 0) home = void 0;
|
|
1169
|
+
}
|
|
1170
|
+
if (title === void 0 && logo === void 0 && repo === void 0 && home === void 0) return void 0;
|
|
1171
|
+
return {
|
|
1172
|
+
...title !== void 0 ? { title } : {},
|
|
1173
|
+
...logo !== void 0 ? { logo } : {},
|
|
1174
|
+
...repo !== void 0 ? { repo } : {},
|
|
1175
|
+
...home !== void 0 ? { home } : {}
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
async function readProjectConfig(dir) {
|
|
1179
|
+
let raw;
|
|
1180
|
+
try {
|
|
1181
|
+
raw = await import_promises8.default.readFile(import_node_path6.default.join(dir, PROJECT_CONFIG_FILE), "utf8");
|
|
1182
|
+
} catch (err) {
|
|
1183
|
+
if (err.code === "ENOENT") return void 0;
|
|
1184
|
+
throw err;
|
|
1185
|
+
}
|
|
1186
|
+
const parsed = (0, import_yaml3.parse)(raw) ?? {};
|
|
1187
|
+
const collections = [];
|
|
1188
|
+
if (Array.isArray(parsed.collections)) {
|
|
1189
|
+
for (const c of parsed.collections) {
|
|
1190
|
+
if (!c || typeof c !== "object") continue;
|
|
1191
|
+
const cc = c;
|
|
1192
|
+
if (typeof cc.name === "string" && typeof cc.path === "string") {
|
|
1193
|
+
collections.push({
|
|
1194
|
+
name: cc.name,
|
|
1195
|
+
path: cc.path,
|
|
1196
|
+
...typeof cc.order === "number" ? { order: cc.order } : {}
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
collections.sort((a, b) => (a.order ?? Number.MAX_SAFE_INTEGER) - (b.order ?? Number.MAX_SAFE_INTEGER));
|
|
1201
|
+
} else if (typeof parsed.path === "string") {
|
|
1202
|
+
const name = typeof parsed.collection === "string" ? parsed.collection : import_node_path6.default.basename(import_node_path6.default.resolve(dir));
|
|
1203
|
+
collections.push({ name, path: parsed.path });
|
|
1204
|
+
}
|
|
1205
|
+
const site = normaliseSite(parsed.site);
|
|
1206
|
+
return { ...site ? { site } : {}, collections };
|
|
1207
|
+
}
|
|
1208
|
+
async function resolveProjectCollections(dir) {
|
|
1209
|
+
const config = await readProjectConfig(dir);
|
|
1210
|
+
if (!config) return [];
|
|
1211
|
+
const root = import_node_path6.default.resolve(dir);
|
|
1212
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1213
|
+
return config.collections.map((c) => ({
|
|
1214
|
+
name: c.name,
|
|
1215
|
+
type: "repo",
|
|
1216
|
+
path: import_node_path6.default.resolve(root, c.path),
|
|
1217
|
+
createdAt
|
|
1218
|
+
}));
|
|
1219
|
+
}
|
|
1220
|
+
async function readSiteConfig(dir) {
|
|
1221
|
+
return (await readProjectConfig(dir))?.site ?? {};
|
|
1222
|
+
}
|
|
1223
|
+
async function seedProjectHome(dir) {
|
|
1224
|
+
const entries = await resolveProjectCollections(dir);
|
|
1225
|
+
if (entries.length === 0) {
|
|
1226
|
+
throw new Error(`No collections to render from diffwiki.yaml in ${import_node_path6.default.resolve(dir)}`);
|
|
1227
|
+
}
|
|
1228
|
+
const home = await import_promises8.default.mkdtemp(import_node_path6.default.join(import_node_os2.default.tmpdir(), "diffwiki-project-"));
|
|
1229
|
+
await import_promises8.default.writeFile(
|
|
1230
|
+
import_node_path6.default.join(home, "registry.json"),
|
|
1231
|
+
`${JSON.stringify({ version: 1, collections: entries }, null, 2)}
|
|
1232
|
+
`,
|
|
1233
|
+
"utf8"
|
|
1234
|
+
);
|
|
1235
|
+
return home;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1088
1238
|
// src/site.ts
|
|
1089
1239
|
init_collections();
|
|
1090
1240
|
|
|
1091
1241
|
// src/browse.ts
|
|
1092
|
-
var
|
|
1093
|
-
var
|
|
1242
|
+
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
1243
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
1094
1244
|
init_collections();
|
|
1095
1245
|
init_errors();
|
|
1096
1246
|
var INDEX_RE = /^(index|readme)\.mdx?$/i;
|
|
@@ -1136,7 +1286,7 @@ function buildArticleTree(relPaths) {
|
|
|
1136
1286
|
async function collectRelPaths(dir, rel = "") {
|
|
1137
1287
|
let dirents;
|
|
1138
1288
|
try {
|
|
1139
|
-
dirents = await
|
|
1289
|
+
dirents = await import_promises9.default.readdir(dir, { withFileTypes: true });
|
|
1140
1290
|
} catch {
|
|
1141
1291
|
return [];
|
|
1142
1292
|
}
|
|
@@ -1145,7 +1295,7 @@ async function collectRelPaths(dir, rel = "") {
|
|
|
1145
1295
|
if (d.name.startsWith(".")) continue;
|
|
1146
1296
|
const childRel = rel ? `${rel}/${d.name}` : d.name;
|
|
1147
1297
|
if (d.isDirectory()) {
|
|
1148
|
-
out.push(...await collectRelPaths(
|
|
1298
|
+
out.push(...await collectRelPaths(import_node_path7.default.join(dir, d.name), childRel));
|
|
1149
1299
|
} else if (/\.(mdx?)$/.test(d.name)) {
|
|
1150
1300
|
out.push(childRel);
|
|
1151
1301
|
}
|
|
@@ -1154,7 +1304,7 @@ async function collectRelPaths(dir, rel = "") {
|
|
|
1154
1304
|
}
|
|
1155
1305
|
async function readdirSafe(dir) {
|
|
1156
1306
|
try {
|
|
1157
|
-
return await
|
|
1307
|
+
return await import_promises9.default.readdir(dir);
|
|
1158
1308
|
} catch {
|
|
1159
1309
|
return [];
|
|
1160
1310
|
}
|
|
@@ -1168,20 +1318,30 @@ function pickIndexFile(names) {
|
|
|
1168
1318
|
};
|
|
1169
1319
|
return names.filter((n) => INDEX_RE.test(n)).sort((a, b) => rank(a) - rank(b))[0];
|
|
1170
1320
|
}
|
|
1171
|
-
|
|
1321
|
+
var CHEAP_META_BYTES = 8192;
|
|
1322
|
+
async function cheapMeta(absPath) {
|
|
1172
1323
|
let handle;
|
|
1173
1324
|
try {
|
|
1174
|
-
handle = await
|
|
1175
|
-
const buf = Buffer.alloc(
|
|
1176
|
-
const { bytesRead } = await handle.read(buf, 0,
|
|
1325
|
+
handle = await import_promises9.default.open(absPath, "r");
|
|
1326
|
+
const buf = Buffer.alloc(CHEAP_META_BYTES);
|
|
1327
|
+
const { bytesRead } = await handle.read(buf, 0, CHEAP_META_BYTES, 0);
|
|
1177
1328
|
const snippet = buf.subarray(0, bytesRead).toString("utf8");
|
|
1178
|
-
const
|
|
1179
|
-
if (
|
|
1329
|
+
const fence = /^---[ \t]*\n([\s\S]*?)(?:\n---[ \t]*\n|$)/.exec(snippet);
|
|
1330
|
+
if (!fence) return {};
|
|
1331
|
+
const inner = fence[1];
|
|
1332
|
+
const clean = (s) => s.trim().replace(/^["']|["']$/g, "");
|
|
1333
|
+
const titleM = /^title:\s*(.+)$/m.exec(inner);
|
|
1334
|
+
const audienceM = /^audience:\s*(.+)$/m.exec(inner);
|
|
1335
|
+
const statusM = /^status:\s*(.+)$/m.exec(inner);
|
|
1336
|
+
const title = titleM ? clean(titleM[1]) : void 0;
|
|
1337
|
+
const audience = audienceM && clean(audienceM[1]) === "private" ? "private" : void 0;
|
|
1338
|
+
const status = statusM && clean(statusM[1]) === "draft" ? "draft" : void 0;
|
|
1339
|
+
return { title, audience, status };
|
|
1180
1340
|
} catch {
|
|
1181
1341
|
} finally {
|
|
1182
1342
|
await handle?.close();
|
|
1183
1343
|
}
|
|
1184
|
-
return
|
|
1344
|
+
return {};
|
|
1185
1345
|
}
|
|
1186
1346
|
async function enrichTitles(nodes, collDir) {
|
|
1187
1347
|
return Promise.all(
|
|
@@ -1189,67 +1349,97 @@ async function enrichTitles(nodes, collDir) {
|
|
|
1189
1349
|
if (node.children) {
|
|
1190
1350
|
const children = await enrichTitles(node.children, collDir);
|
|
1191
1351
|
let title = node.title;
|
|
1352
|
+
let audience = node.audience;
|
|
1353
|
+
let status = node.status;
|
|
1192
1354
|
if (node.slug) {
|
|
1193
|
-
const dir =
|
|
1355
|
+
const dir = import_node_path7.default.join(collDir, node.slug);
|
|
1194
1356
|
const idx = pickIndexFile(await readdirSafe(dir));
|
|
1195
|
-
if (idx)
|
|
1357
|
+
if (idx) {
|
|
1358
|
+
const meta = await cheapMeta(import_node_path7.default.join(dir, idx));
|
|
1359
|
+
title = meta.title ?? title;
|
|
1360
|
+
audience = meta.audience ?? audience;
|
|
1361
|
+
status = meta.status ?? status;
|
|
1362
|
+
}
|
|
1196
1363
|
}
|
|
1197
|
-
return { ...node, title, children };
|
|
1364
|
+
return { ...node, title, audience, status, children };
|
|
1198
1365
|
}
|
|
1199
1366
|
for (const ext of [".mdx", ".md"]) {
|
|
1200
|
-
const
|
|
1201
|
-
if (title
|
|
1367
|
+
const meta = await cheapMeta(import_node_path7.default.join(collDir, `${node.slug}${ext}`));
|
|
1368
|
+
if (meta.title || meta.audience || meta.status) {
|
|
1369
|
+
return {
|
|
1370
|
+
...node,
|
|
1371
|
+
title: meta.title ?? node.title,
|
|
1372
|
+
audience: meta.audience ?? node.audience,
|
|
1373
|
+
status: meta.status ?? node.status
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1202
1376
|
}
|
|
1203
1377
|
return node;
|
|
1204
1378
|
})
|
|
1205
1379
|
);
|
|
1206
1380
|
}
|
|
1381
|
+
async function collectionMeta(collDir) {
|
|
1382
|
+
const idx = pickIndexFile(await readdirSafe(collDir));
|
|
1383
|
+
if (!idx) return {};
|
|
1384
|
+
const meta = await cheapMeta(import_node_path7.default.join(collDir, idx));
|
|
1385
|
+
return { title: meta.title, audience: meta.audience, status: meta.status };
|
|
1386
|
+
}
|
|
1207
1387
|
async function listArticleTree(coll) {
|
|
1208
1388
|
const entry = await findCollection(coll);
|
|
1209
1389
|
if (!entry) throw new CollectionNotFoundError(coll);
|
|
1210
1390
|
const rels = await collectRelPaths(entry.path);
|
|
1211
|
-
return enrichTitles(buildArticleTree(rels),
|
|
1391
|
+
return enrichTitles(buildArticleTree(rels), import_node_path7.default.resolve(entry.path));
|
|
1212
1392
|
}
|
|
1213
1393
|
async function readArticle(coll, slug) {
|
|
1214
1394
|
const entry = await findCollection(coll);
|
|
1215
1395
|
if (!entry) throw new CollectionNotFoundError(coll);
|
|
1216
|
-
const collDir =
|
|
1396
|
+
const collDir = import_node_path7.default.resolve(entry.path);
|
|
1217
1397
|
const normalized = slug.replace(/\\/g, "/");
|
|
1218
1398
|
if (normalized.split("/").some((s) => s === "..")) {
|
|
1219
1399
|
throw new InvalidTargetError(slug, "path traversal detected");
|
|
1220
1400
|
}
|
|
1221
1401
|
const safeSlug = normalized.split("/").filter((s) => s !== "" && s !== ".").join("/");
|
|
1222
|
-
const inJail = (p) => p === collDir || p.startsWith(collDir +
|
|
1402
|
+
const inJail = (p) => p === collDir || p.startsWith(collDir + import_node_path7.default.sep);
|
|
1223
1403
|
let abs;
|
|
1224
1404
|
let isIndex = false;
|
|
1225
1405
|
for (const rel of safeSlug ? [`${safeSlug}.mdx`, `${safeSlug}.md`] : []) {
|
|
1226
|
-
const candidate =
|
|
1406
|
+
const candidate = import_node_path7.default.resolve(collDir, rel);
|
|
1227
1407
|
if (!inJail(candidate)) throw new InvalidTargetError(slug, "path traversal detected");
|
|
1228
1408
|
try {
|
|
1229
|
-
await
|
|
1409
|
+
await import_promises9.default.access(candidate);
|
|
1230
1410
|
abs = candidate;
|
|
1231
1411
|
break;
|
|
1232
1412
|
} catch {
|
|
1233
1413
|
}
|
|
1234
1414
|
}
|
|
1235
1415
|
if (!abs) {
|
|
1236
|
-
const dir =
|
|
1416
|
+
const dir = import_node_path7.default.resolve(collDir, safeSlug);
|
|
1237
1417
|
if (inJail(dir)) {
|
|
1238
1418
|
const idx = pickIndexFile(await readdirSafe(dir));
|
|
1239
1419
|
if (idx) {
|
|
1240
|
-
abs =
|
|
1420
|
+
abs = import_node_path7.default.join(dir, idx);
|
|
1241
1421
|
isIndex = true;
|
|
1242
1422
|
}
|
|
1243
1423
|
}
|
|
1244
1424
|
}
|
|
1245
1425
|
if (!abs) throw new ArticleNotFoundError(`${coll}/${safeSlug}`);
|
|
1246
|
-
const parsed = parseArticle(await
|
|
1426
|
+
const parsed = parseArticle(await import_promises9.default.readFile(abs, "utf8"));
|
|
1247
1427
|
const title = parsed.title?.trim() || (safeSlug ? safeSlug.split("/").pop() ?? safeSlug : coll);
|
|
1248
|
-
return {
|
|
1428
|
+
return {
|
|
1429
|
+
coll,
|
|
1430
|
+
slug: safeSlug,
|
|
1431
|
+
title,
|
|
1432
|
+
tags: parsed.tags,
|
|
1433
|
+
audience: parsed.audience,
|
|
1434
|
+
status: parsed.status,
|
|
1435
|
+
body: parsed.body,
|
|
1436
|
+
path: abs,
|
|
1437
|
+
isIndex
|
|
1438
|
+
};
|
|
1249
1439
|
}
|
|
1250
1440
|
|
|
1251
1441
|
// src/render.ts
|
|
1252
|
-
var
|
|
1442
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
1253
1443
|
var import_unified = require("unified");
|
|
1254
1444
|
var import_remark_parse = __toESM(require("remark-parse"), 1);
|
|
1255
1445
|
var import_remark_gfm = __toESM(require("remark-gfm"), 1);
|
|
@@ -1266,7 +1456,7 @@ function isNonRelativeHref(href) {
|
|
|
1266
1456
|
return /^[a-z][a-z0-9+.-]*:/i.test(href);
|
|
1267
1457
|
}
|
|
1268
1458
|
function rehypeRewriteLinks(ctx) {
|
|
1269
|
-
const slugDir = ctx.isIndex ? ctx.slug || "." :
|
|
1459
|
+
const slugDir = ctx.isIndex ? ctx.slug || "." : import_node_path8.default.posix.dirname(ctx.slug || ".");
|
|
1270
1460
|
return (tree) => {
|
|
1271
1461
|
(0, import_unist_util_visit.visit)(tree, "element", (node) => {
|
|
1272
1462
|
if (node.tagName !== "a") return;
|
|
@@ -1276,12 +1466,17 @@ function rehypeRewriteLinks(ctx) {
|
|
|
1276
1466
|
const suffix = suffixMatch ? suffixMatch[0] : "";
|
|
1277
1467
|
const pathPart = suffix ? raw.slice(0, raw.length - suffix.length) : raw;
|
|
1278
1468
|
if (pathPart === "") return;
|
|
1279
|
-
const joined =
|
|
1280
|
-
const normalized =
|
|
1469
|
+
const joined = import_node_path8.default.posix.join(slugDir === "." ? "" : slugDir, pathPart);
|
|
1470
|
+
const normalized = import_node_path8.default.posix.normalize(joined);
|
|
1471
|
+
const toRoute = (p) => p.replace(/\.(mdx?)$/i, "").replace(/(^|\/)(index|readme)$/i, "$1").replace(/\/+$/, "").replace(/^\/+/, "");
|
|
1472
|
+
if (normalized.startsWith("../")) {
|
|
1473
|
+
const sibling = normalized.slice(3);
|
|
1474
|
+
if (sibling === "" || sibling.startsWith("..")) return;
|
|
1475
|
+
node.properties = { ...node.properties, href: `/${toRoute(sibling)}${suffix}` };
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1281
1478
|
if (normalized.startsWith("..")) return;
|
|
1282
|
-
|
|
1283
|
-
resolved = resolved.replace(/(^|\/)(index|readme)$/i, "$1");
|
|
1284
|
-
resolved = resolved.replace(/\/+$/, "").replace(/^\/+/, "");
|
|
1479
|
+
const resolved = toRoute(normalized);
|
|
1285
1480
|
node.properties = {
|
|
1286
1481
|
...node.properties,
|
|
1287
1482
|
href: `/${ctx.coll}${resolved ? `/${resolved}` : ""}${suffix}`
|
|
@@ -1496,13 +1691,22 @@ function localGraph(graph, focusId, include, siblingCap = 12) {
|
|
|
1496
1691
|
init_errors();
|
|
1497
1692
|
var isStatic = () => process.env.DIFFWIKI_STATIC === "1";
|
|
1498
1693
|
function toTreeNodes(coll, nodes) {
|
|
1499
|
-
|
|
1694
|
+
const visible = isStatic() ? nodes.filter((n) => n.audience !== "private" && n.status !== "draft") : nodes;
|
|
1695
|
+
return visible.map(
|
|
1500
1696
|
(n) => n.children ? {
|
|
1501
1697
|
name: n.name,
|
|
1502
1698
|
title: n.title ?? n.name,
|
|
1503
1699
|
path: n.slug ? `/${coll}/${n.slug}` : void 0,
|
|
1700
|
+
...n.audience ? { audience: n.audience } : {},
|
|
1701
|
+
...n.status ? { status: n.status } : {},
|
|
1504
1702
|
children: toTreeNodes(coll, n.children)
|
|
1505
|
-
} : {
|
|
1703
|
+
} : {
|
|
1704
|
+
name: n.name,
|
|
1705
|
+
title: n.title ?? n.name,
|
|
1706
|
+
path: `/${coll}/${n.slug}`,
|
|
1707
|
+
...n.audience ? { audience: n.audience } : {},
|
|
1708
|
+
...n.status ? { status: n.status } : {}
|
|
1709
|
+
}
|
|
1506
1710
|
);
|
|
1507
1711
|
}
|
|
1508
1712
|
async function gitInfo(e) {
|
|
@@ -1515,23 +1719,42 @@ async function gitInfo(e) {
|
|
|
1515
1719
|
}
|
|
1516
1720
|
async function buildCollections() {
|
|
1517
1721
|
const entries = await listCollections();
|
|
1518
|
-
|
|
1519
|
-
entries.map(async (e) =>
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1722
|
+
const infos = await Promise.all(
|
|
1723
|
+
entries.map(async (e) => {
|
|
1724
|
+
const { title, audience, status } = await collectionMeta(e.path);
|
|
1725
|
+
return {
|
|
1726
|
+
name: e.name,
|
|
1727
|
+
type: e.type,
|
|
1728
|
+
path: e.path,
|
|
1729
|
+
...title ? { title } : {},
|
|
1730
|
+
...await gitInfo(e),
|
|
1731
|
+
...audience ? { audience } : {},
|
|
1732
|
+
...status ? { status } : {}
|
|
1733
|
+
};
|
|
1734
|
+
})
|
|
1525
1735
|
);
|
|
1736
|
+
return isStatic() ? infos.filter((c) => c.audience !== "private" && c.status !== "draft") : infos;
|
|
1526
1737
|
}
|
|
1527
1738
|
async function buildNavTree() {
|
|
1528
1739
|
const entries = await listCollections();
|
|
1529
|
-
|
|
1530
|
-
entries.map(async (e) =>
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1740
|
+
const trees = await Promise.all(
|
|
1741
|
+
entries.map(async (e) => {
|
|
1742
|
+
const { title, audience, status } = await collectionMeta(e.path);
|
|
1743
|
+
return {
|
|
1744
|
+
collection: {
|
|
1745
|
+
name: e.name,
|
|
1746
|
+
type: e.type,
|
|
1747
|
+
path: e.path,
|
|
1748
|
+
...title ? { title } : {},
|
|
1749
|
+
...await gitInfo(e),
|
|
1750
|
+
...audience ? { audience } : {},
|
|
1751
|
+
...status ? { status } : {}
|
|
1752
|
+
},
|
|
1753
|
+
nodes: toTreeNodes(e.name, await listArticleTree(e.name))
|
|
1754
|
+
};
|
|
1755
|
+
})
|
|
1534
1756
|
);
|
|
1757
|
+
return isStatic() ? trees.filter((t) => t.collection.audience !== "private" && t.collection.status !== "draft") : trees;
|
|
1535
1758
|
}
|
|
1536
1759
|
function findNode(nodes, slug) {
|
|
1537
1760
|
for (const n of nodes) {
|
|
@@ -1559,6 +1782,8 @@ async function resolvePage(coll, slug, opts) {
|
|
|
1559
1782
|
slug: art.slug,
|
|
1560
1783
|
title: art.title,
|
|
1561
1784
|
tags: art.tags,
|
|
1785
|
+
...art.audience === "private" ? { audience: art.audience } : {},
|
|
1786
|
+
...art.status === "draft" ? { status: art.status } : {},
|
|
1562
1787
|
html,
|
|
1563
1788
|
toc,
|
|
1564
1789
|
path: `/${art.coll}/${art.slug}`,
|
|
@@ -1593,6 +1818,15 @@ async function resolvePage(coll, slug, opts) {
|
|
|
1593
1818
|
};
|
|
1594
1819
|
}
|
|
1595
1820
|
}
|
|
1821
|
+
async function buildSite() {
|
|
1822
|
+
const dir = process.env.DIFFWIKI_PROJECT_DIR;
|
|
1823
|
+
if (!dir) return {};
|
|
1824
|
+
try {
|
|
1825
|
+
return await readSiteConfig(dir);
|
|
1826
|
+
} catch {
|
|
1827
|
+
return {};
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1596
1830
|
|
|
1597
1831
|
// src/select.ts
|
|
1598
1832
|
function globToRegExp(glob) {
|
|
@@ -1622,8 +1856,8 @@ function resolveCollectionSelection(all, sel = {}) {
|
|
|
1622
1856
|
}
|
|
1623
1857
|
|
|
1624
1858
|
// src/query.ts
|
|
1625
|
-
var
|
|
1626
|
-
var
|
|
1859
|
+
var import_promises10 = __toESM(require("fs/promises"), 1);
|
|
1860
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
1627
1861
|
init_collections();
|
|
1628
1862
|
init_errors();
|
|
1629
1863
|
init_logger();
|
|
@@ -1692,13 +1926,13 @@ var log2 = getLogger("query");
|
|
|
1692
1926
|
async function collectMarkdownFiles(dir) {
|
|
1693
1927
|
let entries;
|
|
1694
1928
|
try {
|
|
1695
|
-
entries = await
|
|
1929
|
+
entries = await import_promises10.default.readdir(dir, { withFileTypes: true });
|
|
1696
1930
|
} catch {
|
|
1697
1931
|
return [];
|
|
1698
1932
|
}
|
|
1699
1933
|
const files = [];
|
|
1700
1934
|
for (const entry of entries) {
|
|
1701
|
-
const full =
|
|
1935
|
+
const full = import_node_path9.default.join(dir, entry.name);
|
|
1702
1936
|
if (entry.isDirectory()) {
|
|
1703
1937
|
files.push(...await collectMarkdownFiles(full));
|
|
1704
1938
|
} else if (entry.isFile() && /\.mdx?$/.test(entry.name)) {
|
|
@@ -1717,12 +1951,12 @@ async function nativeSearch(term, collections, collection) {
|
|
|
1717
1951
|
const docs = [];
|
|
1718
1952
|
for (const entry of targets) {
|
|
1719
1953
|
for (const full of await collectMarkdownFiles(entry.path)) {
|
|
1720
|
-
const fallbackTitle =
|
|
1954
|
+
const fallbackTitle = import_node_path9.default.basename(full).replace(/\.mdx?$/, "");
|
|
1721
1955
|
let title = fallbackTitle;
|
|
1722
1956
|
let tags = [];
|
|
1723
1957
|
let body = "";
|
|
1724
1958
|
try {
|
|
1725
|
-
const parsed = parseArticle(await
|
|
1959
|
+
const parsed = parseArticle(await import_promises10.default.readFile(full, "utf8"));
|
|
1726
1960
|
if (parsed.title) title = parsed.title;
|
|
1727
1961
|
tags = parsed.tags;
|
|
1728
1962
|
body = parsed.body;
|
|
@@ -1805,13 +2039,14 @@ async function query(term, opts) {
|
|
|
1805
2039
|
}
|
|
1806
2040
|
|
|
1807
2041
|
// src/search.ts
|
|
1808
|
-
var
|
|
1809
|
-
var
|
|
2042
|
+
var import_promises11 = __toESM(require("fs/promises"), 1);
|
|
2043
|
+
var import_node_path10 = __toESM(require("path"), 1);
|
|
1810
2044
|
init_collections();
|
|
2045
|
+
var isStatic2 = () => process.env.DIFFWIKI_STATIC === "1";
|
|
1811
2046
|
async function readArticleParts(collDir, slug) {
|
|
1812
2047
|
for (const ext of [".mdx", ".md"]) {
|
|
1813
2048
|
try {
|
|
1814
|
-
const parsed = parseArticle(await
|
|
2049
|
+
const parsed = parseArticle(await import_promises11.default.readFile(import_node_path10.default.join(collDir, `${slug}${ext}`), "utf8"));
|
|
1815
2050
|
return { tags: parsed.tags, body: parsed.body };
|
|
1816
2051
|
} catch {
|
|
1817
2052
|
}
|
|
@@ -1820,6 +2055,7 @@ async function readArticleParts(collDir, slug) {
|
|
|
1820
2055
|
}
|
|
1821
2056
|
async function flatten(coll, collDir, nodes, out) {
|
|
1822
2057
|
for (const n of nodes) {
|
|
2058
|
+
if (isStatic2() && (n.audience === "private" || n.status === "draft")) continue;
|
|
1823
2059
|
if (n.slug && !n.children) {
|
|
1824
2060
|
const title = n.title ?? n.name;
|
|
1825
2061
|
const { tags, body } = await readArticleParts(collDir, n.slug);
|
|
@@ -1845,7 +2081,11 @@ async function buildSearchIndex(collection) {
|
|
|
1845
2081
|
for (const e of entries) {
|
|
1846
2082
|
try {
|
|
1847
2083
|
const collEntry = await findCollection(e.name);
|
|
1848
|
-
const collDir =
|
|
2084
|
+
const collDir = import_node_path10.default.resolve(collEntry?.path ?? e.path);
|
|
2085
|
+
if (isStatic2()) {
|
|
2086
|
+
const cm = await collectionMeta(collDir);
|
|
2087
|
+
if (cm.audience === "private" || cm.status === "draft") continue;
|
|
2088
|
+
}
|
|
1849
2089
|
await flatten(e.name, collDir, await listArticleTree(e.name), out);
|
|
1850
2090
|
} catch {
|
|
1851
2091
|
}
|
|
@@ -1854,15 +2094,15 @@ async function buildSearchIndex(collection) {
|
|
|
1854
2094
|
}
|
|
1855
2095
|
|
|
1856
2096
|
// src/doctor.ts
|
|
1857
|
-
var
|
|
2097
|
+
var import_promises13 = __toESM(require("fs/promises"), 1);
|
|
1858
2098
|
init_paths();
|
|
1859
2099
|
init_registry();
|
|
1860
2100
|
init_collections();
|
|
1861
2101
|
|
|
1862
2102
|
// src/plugins/manager.ts
|
|
1863
|
-
var
|
|
1864
|
-
var
|
|
1865
|
-
var
|
|
2103
|
+
var import_promises12 = __toESM(require("fs/promises"), 1);
|
|
2104
|
+
var import_node_os3 = __toESM(require("os"), 1);
|
|
2105
|
+
var import_node_path12 = __toESM(require("path"), 1);
|
|
1866
2106
|
init_paths();
|
|
1867
2107
|
init_errors();
|
|
1868
2108
|
init_collections();
|
|
@@ -1873,7 +2113,7 @@ init_host();
|
|
|
1873
2113
|
var import_node_child_process4 = require("child_process");
|
|
1874
2114
|
var import_node_util3 = require("util");
|
|
1875
2115
|
var import_node_fs = require("fs");
|
|
1876
|
-
var
|
|
2116
|
+
var import_node_path11 = require("path");
|
|
1877
2117
|
var import_node_url = require("url");
|
|
1878
2118
|
var execFileAsync3 = (0, import_node_util3.promisify)(import_node_child_process4.execFile);
|
|
1879
2119
|
async function runProcess(file, args, opts) {
|
|
@@ -1889,7 +2129,7 @@ async function runProcess(file, args, opts) {
|
|
|
1889
2129
|
// src/plugins/manager.ts
|
|
1890
2130
|
async function readPackageJson(file) {
|
|
1891
2131
|
try {
|
|
1892
|
-
return JSON.parse(await
|
|
2132
|
+
return JSON.parse(await import_promises12.default.readFile(file, "utf8"));
|
|
1893
2133
|
} catch {
|
|
1894
2134
|
return null;
|
|
1895
2135
|
}
|
|
@@ -1905,7 +2145,7 @@ function binOf(manifest) {
|
|
|
1905
2145
|
async function pluginManifestDirs(nodeModules) {
|
|
1906
2146
|
let entries;
|
|
1907
2147
|
try {
|
|
1908
|
-
entries = await
|
|
2148
|
+
entries = await import_promises12.default.readdir(nodeModules);
|
|
1909
2149
|
} catch {
|
|
1910
2150
|
return [];
|
|
1911
2151
|
}
|
|
@@ -1915,44 +2155,44 @@ async function pluginManifestDirs(nodeModules) {
|
|
|
1915
2155
|
if (entry.startsWith("@")) {
|
|
1916
2156
|
let scoped;
|
|
1917
2157
|
try {
|
|
1918
|
-
scoped = await
|
|
2158
|
+
scoped = await import_promises12.default.readdir(import_node_path12.default.join(nodeModules, entry));
|
|
1919
2159
|
} catch {
|
|
1920
2160
|
continue;
|
|
1921
2161
|
}
|
|
1922
2162
|
for (const pkg of scoped) {
|
|
1923
2163
|
if (pkg.startsWith(".")) continue;
|
|
1924
|
-
dirs.push(
|
|
2164
|
+
dirs.push(import_node_path12.default.join(nodeModules, entry, pkg));
|
|
1925
2165
|
}
|
|
1926
2166
|
} else {
|
|
1927
|
-
dirs.push(
|
|
2167
|
+
dirs.push(import_node_path12.default.join(nodeModules, entry));
|
|
1928
2168
|
}
|
|
1929
2169
|
}
|
|
1930
2170
|
return dirs;
|
|
1931
2171
|
}
|
|
1932
2172
|
async function ensurePluginRoot() {
|
|
1933
2173
|
const dir = pluginsDir();
|
|
1934
|
-
await
|
|
1935
|
-
const pkgPath =
|
|
2174
|
+
await import_promises12.default.mkdir(dir, { recursive: true });
|
|
2175
|
+
const pkgPath = import_node_path12.default.join(dir, "package.json");
|
|
1936
2176
|
if (await readPackageJson(pkgPath) === null) {
|
|
1937
2177
|
const pkg = { name: "diffwiki-plugins", private: true, version: "0.0.0" };
|
|
1938
|
-
await
|
|
2178
|
+
await import_promises12.default.writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
1939
2179
|
`, "utf8");
|
|
1940
2180
|
}
|
|
1941
2181
|
return dir;
|
|
1942
2182
|
}
|
|
1943
2183
|
async function asSearchPackage(pkgDir) {
|
|
1944
|
-
const manifest = await readPackageJson(
|
|
2184
|
+
const manifest = await readPackageJson(import_node_path12.default.join(pkgDir, "package.json"));
|
|
1945
2185
|
if (!manifest?.name || manifest.diffwiki?.kind !== "search") return null;
|
|
1946
2186
|
const bin = binOf(manifest);
|
|
1947
2187
|
if (!bin) return null;
|
|
1948
|
-
return { name: manifest.name, version: manifest.version, binAbs:
|
|
2188
|
+
return { name: manifest.name, version: manifest.version, binAbs: import_node_path12.default.resolve(pkgDir, bin) };
|
|
1949
2189
|
}
|
|
1950
2190
|
async function packageNameFromSpec(spec) {
|
|
1951
2191
|
const looksLikePath = spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("~") || spec.startsWith("file:");
|
|
1952
2192
|
if (looksLikePath) {
|
|
1953
2193
|
const raw = spec.startsWith("file:") ? spec.slice("file:".length) : spec;
|
|
1954
|
-
const abs =
|
|
1955
|
-
return (await readPackageJson(
|
|
2194
|
+
const abs = import_node_path12.default.resolve(raw.replace(/^~(?=$|\/)/, import_node_os3.default.homedir()));
|
|
2195
|
+
return (await readPackageJson(import_node_path12.default.join(abs, "package.json")))?.name ?? null;
|
|
1956
2196
|
}
|
|
1957
2197
|
if (/^(git\+|git:|https?:|ssh:)/.test(spec)) return null;
|
|
1958
2198
|
if (spec.startsWith("@")) {
|
|
@@ -1963,10 +2203,10 @@ async function packageNameFromSpec(spec) {
|
|
|
1963
2203
|
return at <= 0 ? spec : spec.slice(0, at);
|
|
1964
2204
|
}
|
|
1965
2205
|
async function resolveSearchPackage(dir, name) {
|
|
1966
|
-
return asSearchPackage(
|
|
2206
|
+
return asSearchPackage(import_node_path12.default.join(dir, "node_modules", ...name.split("/")));
|
|
1967
2207
|
}
|
|
1968
2208
|
async function discoverSearchPackage(dir) {
|
|
1969
|
-
const nodeModules =
|
|
2209
|
+
const nodeModules = import_node_path12.default.join(dir, "node_modules");
|
|
1970
2210
|
for (const pkgDir of await pluginManifestDirs(nodeModules)) {
|
|
1971
2211
|
const found = await asSearchPackage(pkgDir);
|
|
1972
2212
|
if (found) return found;
|
|
@@ -2098,7 +2338,7 @@ init_host();
|
|
|
2098
2338
|
init_types();
|
|
2099
2339
|
async function exists(target) {
|
|
2100
2340
|
try {
|
|
2101
|
-
await
|
|
2341
|
+
await import_promises13.default.access(target);
|
|
2102
2342
|
return true;
|
|
2103
2343
|
} catch {
|
|
2104
2344
|
return false;
|
|
@@ -2244,6 +2484,7 @@ init_registry2();
|
|
|
2244
2484
|
InvalidTargetError,
|
|
2245
2485
|
NATIVE_ENGINE,
|
|
2246
2486
|
NoDefaultCollectionError,
|
|
2487
|
+
PROJECT_CONFIG_FILE,
|
|
2247
2488
|
PluginError,
|
|
2248
2489
|
REPO_CONFIG_FILE,
|
|
2249
2490
|
WorktreeNotAllowedError,
|
|
@@ -2254,8 +2495,10 @@ init_registry2();
|
|
|
2254
2495
|
buildLinkGraph,
|
|
2255
2496
|
buildNavTree,
|
|
2256
2497
|
buildSearchIndex,
|
|
2498
|
+
buildSite,
|
|
2257
2499
|
collectionDir,
|
|
2258
2500
|
collectionGitStatus,
|
|
2501
|
+
collectionMeta,
|
|
2259
2502
|
collectionsDir,
|
|
2260
2503
|
configPath,
|
|
2261
2504
|
createArticle,
|
|
@@ -2297,8 +2540,10 @@ init_registry2();
|
|
|
2297
2540
|
readArticle,
|
|
2298
2541
|
readConfig,
|
|
2299
2542
|
readPluginRegistry,
|
|
2543
|
+
readProjectConfig,
|
|
2300
2544
|
readRegistry,
|
|
2301
2545
|
readRepoConfig,
|
|
2546
|
+
readSiteConfig,
|
|
2302
2547
|
registerCollection,
|
|
2303
2548
|
registerPlugin,
|
|
2304
2549
|
registryPath,
|
|
@@ -2315,7 +2560,9 @@ init_registry2();
|
|
|
2315
2560
|
resolveHome,
|
|
2316
2561
|
resolveLogLevel,
|
|
2317
2562
|
resolvePage,
|
|
2563
|
+
resolveProjectCollections,
|
|
2318
2564
|
search,
|
|
2565
|
+
seedProjectHome,
|
|
2319
2566
|
serializeArticle,
|
|
2320
2567
|
setConfigValue,
|
|
2321
2568
|
setTags,
|