diffwiki-core 0.17.0 → 0.17.1
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 +93 -69
- package/dist/index.d.cts +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +77 -55
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -137,7 +137,7 @@ function isMissing(err) {
|
|
|
137
137
|
}
|
|
138
138
|
async function readRegistry() {
|
|
139
139
|
try {
|
|
140
|
-
const parsed = JSON.parse(await
|
|
140
|
+
const parsed = JSON.parse(await import_promises3.default.readFile(registryPath(), "utf8"));
|
|
141
141
|
return { version: parsed.version ?? 1, collections: parsed.collections ?? [] };
|
|
142
142
|
} catch (err) {
|
|
143
143
|
if (isMissing(err)) return { version: 1, collections: [] };
|
|
@@ -145,8 +145,8 @@ async function readRegistry() {
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
async function writeRegistry(registry) {
|
|
148
|
-
await
|
|
149
|
-
await
|
|
148
|
+
await import_promises3.default.mkdir(resolveHome(), { recursive: true });
|
|
149
|
+
await import_promises3.default.writeFile(registryPath(), `${JSON.stringify(registry, null, 2)}
|
|
150
150
|
`, "utf8");
|
|
151
151
|
}
|
|
152
152
|
async function registerCollection(entry) {
|
|
@@ -164,11 +164,11 @@ async function unregisterCollection(name) {
|
|
|
164
164
|
registry.collections = registry.collections.filter((c) => c.name !== name);
|
|
165
165
|
await writeRegistry(registry);
|
|
166
166
|
}
|
|
167
|
-
var
|
|
167
|
+
var import_promises3;
|
|
168
168
|
var init_registry = __esm({
|
|
169
169
|
"src/registry.ts"() {
|
|
170
170
|
"use strict";
|
|
171
|
-
|
|
171
|
+
import_promises3 = __toESM(require("fs/promises"), 1);
|
|
172
172
|
init_paths();
|
|
173
173
|
}
|
|
174
174
|
});
|
|
@@ -179,7 +179,7 @@ function isMissing2(err) {
|
|
|
179
179
|
}
|
|
180
180
|
async function readPluginRegistry() {
|
|
181
181
|
try {
|
|
182
|
-
const parsed = JSON.parse(await
|
|
182
|
+
const parsed = JSON.parse(await import_promises4.default.readFile(pluginsRegistryPath(), "utf8"));
|
|
183
183
|
return { version: parsed.version ?? 1, plugins: parsed.plugins ?? [] };
|
|
184
184
|
} catch (err) {
|
|
185
185
|
if (isMissing2(err)) return { version: 1, plugins: [] };
|
|
@@ -187,8 +187,8 @@ async function readPluginRegistry() {
|
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
async function writePluginRegistry(registry) {
|
|
190
|
-
await
|
|
191
|
-
await
|
|
190
|
+
await import_promises4.default.mkdir(resolveHome(), { recursive: true });
|
|
191
|
+
await import_promises4.default.writeFile(pluginsRegistryPath(), `${JSON.stringify(registry, null, 2)}
|
|
192
192
|
`, "utf8");
|
|
193
193
|
}
|
|
194
194
|
async function upsertPlugin(record) {
|
|
@@ -219,11 +219,11 @@ async function findPluginRecord(name) {
|
|
|
219
219
|
const registry = await readPluginRegistry();
|
|
220
220
|
return registry.plugins.find((p) => p.name === name);
|
|
221
221
|
}
|
|
222
|
-
var
|
|
222
|
+
var import_promises4;
|
|
223
223
|
var init_registry2 = __esm({
|
|
224
224
|
"src/plugins/registry.ts"() {
|
|
225
225
|
"use strict";
|
|
226
|
-
|
|
226
|
+
import_promises4 = __toESM(require("fs/promises"), 1);
|
|
227
227
|
init_paths();
|
|
228
228
|
}
|
|
229
229
|
});
|
|
@@ -534,7 +534,7 @@ async function findCollectionById(id) {
|
|
|
534
534
|
async function createCollection(name, opts) {
|
|
535
535
|
if (await findCollection(name)) throw new CollectionExistsError(name);
|
|
536
536
|
const dir = collectionDir(name);
|
|
537
|
-
await
|
|
537
|
+
await import_promises5.default.mkdir(dir, { recursive: true });
|
|
538
538
|
const entry = {
|
|
539
539
|
name,
|
|
540
540
|
type: "global",
|
|
@@ -551,16 +551,16 @@ async function removeCollection(name) {
|
|
|
551
551
|
if (!entry) throw new CollectionNotFoundError(name);
|
|
552
552
|
await unregisterCollection(name);
|
|
553
553
|
if (entry.type === "global") {
|
|
554
|
-
await
|
|
554
|
+
await import_promises5.default.rm(entry.path, { recursive: true, force: true });
|
|
555
555
|
}
|
|
556
556
|
void fireHook("collection-removed", name);
|
|
557
557
|
return entry;
|
|
558
558
|
}
|
|
559
|
-
var
|
|
559
|
+
var import_promises5;
|
|
560
560
|
var init_collections = __esm({
|
|
561
561
|
"src/collections.ts"() {
|
|
562
562
|
"use strict";
|
|
563
|
-
|
|
563
|
+
import_promises5 = __toESM(require("fs/promises"), 1);
|
|
564
564
|
init_paths();
|
|
565
565
|
init_registry();
|
|
566
566
|
init_errors();
|
|
@@ -603,6 +603,7 @@ __export(index_exports, {
|
|
|
603
603
|
doctor: () => doctor,
|
|
604
604
|
emitPluginEvent: () => emitPluginEvent,
|
|
605
605
|
ensureGitRepo: () => ensureGitRepo,
|
|
606
|
+
ensureHome: () => ensureHome,
|
|
606
607
|
ensurePluginRoot: () => ensurePluginRoot,
|
|
607
608
|
fileTimestamps: () => fileTimestamps,
|
|
608
609
|
findCollection: () => findCollection,
|
|
@@ -693,8 +694,30 @@ init_types();
|
|
|
693
694
|
init_errors();
|
|
694
695
|
init_paths();
|
|
695
696
|
|
|
696
|
-
// src/
|
|
697
|
+
// src/home.ts
|
|
697
698
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
699
|
+
init_paths();
|
|
700
|
+
async function seedIfAbsent(file, contents) {
|
|
701
|
+
try {
|
|
702
|
+
await import_promises.default.writeFile(file, contents(), { encoding: "utf8", flag: "wx" });
|
|
703
|
+
} catch (err) {
|
|
704
|
+
if (err.code !== "EEXIST") throw err;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
async function ensureHome() {
|
|
708
|
+
await import_promises.default.mkdir(resolveHome(), { recursive: true });
|
|
709
|
+
await Promise.all([
|
|
710
|
+
import_promises.default.mkdir(collectionsDir(), { recursive: true }),
|
|
711
|
+
import_promises.default.mkdir(pluginsDir(), { recursive: true }),
|
|
712
|
+
import_promises.default.mkdir(templatesDir(), { recursive: true })
|
|
713
|
+
]);
|
|
714
|
+
await seedIfAbsent(registryPath(), () => `${JSON.stringify({ version: 1, collections: [] }, null, 2)}
|
|
715
|
+
`);
|
|
716
|
+
await seedIfAbsent(configPath(), () => "{}\n");
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// src/templates.ts
|
|
720
|
+
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
698
721
|
var import_node_path2 = __toESM(require("path"), 1);
|
|
699
722
|
init_paths();
|
|
700
723
|
|
|
@@ -778,34 +801,34 @@ function serializeArticle(article) {
|
|
|
778
801
|
// src/templates.ts
|
|
779
802
|
async function seedTemplates(srcDir) {
|
|
780
803
|
const dir = templatesDir();
|
|
781
|
-
await
|
|
782
|
-
const files = (await
|
|
804
|
+
await import_promises2.default.mkdir(dir, { recursive: true });
|
|
805
|
+
const files = (await import_promises2.default.readdir(srcDir)).filter((f) => f.endsWith(".md")).sort();
|
|
783
806
|
const seeded = [];
|
|
784
807
|
const skipped = [];
|
|
785
808
|
for (const file of files) {
|
|
786
809
|
const dest = import_node_path2.default.join(dir, file);
|
|
787
810
|
try {
|
|
788
|
-
await
|
|
811
|
+
await import_promises2.default.access(dest);
|
|
789
812
|
skipped.push(file);
|
|
790
813
|
continue;
|
|
791
814
|
} catch {
|
|
792
815
|
}
|
|
793
|
-
await
|
|
816
|
+
await import_promises2.default.copyFile(import_node_path2.default.join(srcDir, file), dest);
|
|
794
817
|
seeded.push(file);
|
|
795
818
|
}
|
|
796
819
|
return { seeded, skipped, dir };
|
|
797
820
|
}
|
|
798
821
|
async function listTemplateShapes(srcDir) {
|
|
799
|
-
const files = await
|
|
822
|
+
const files = await import_promises2.default.readdir(srcDir);
|
|
800
823
|
return files.filter((f) => f.endsWith("-template.md")).map((f) => f.replace(/-template\.md$/, "")).sort();
|
|
801
824
|
}
|
|
802
825
|
async function listTemplates(srcDir) {
|
|
803
|
-
const files = (await
|
|
826
|
+
const files = (await import_promises2.default.readdir(srcDir)).filter((f) => f.endsWith("-template.md")).sort();
|
|
804
827
|
const out = [];
|
|
805
828
|
for (const file of files) {
|
|
806
829
|
let data = {};
|
|
807
830
|
try {
|
|
808
|
-
({ data } = parseFrontmatter(await
|
|
831
|
+
({ data } = parseFrontmatter(await import_promises2.default.readFile(import_node_path2.default.join(srcDir, file), "utf8")));
|
|
809
832
|
} catch {
|
|
810
833
|
}
|
|
811
834
|
const shape = typeof data.shape === "string" && data.shape ? data.shape : file.replace(/-template\.md$/, "");
|
|
@@ -817,7 +840,7 @@ async function listTemplates(srcDir) {
|
|
|
817
840
|
}
|
|
818
841
|
|
|
819
842
|
// src/config.ts
|
|
820
|
-
var
|
|
843
|
+
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
821
844
|
init_paths();
|
|
822
845
|
init_collections();
|
|
823
846
|
init_errors();
|
|
@@ -828,15 +851,15 @@ function isMissing3(err) {
|
|
|
828
851
|
}
|
|
829
852
|
async function readConfig() {
|
|
830
853
|
try {
|
|
831
|
-
return JSON.parse(await
|
|
854
|
+
return JSON.parse(await import_promises6.default.readFile(configPath(), "utf8"));
|
|
832
855
|
} catch (err) {
|
|
833
856
|
if (isMissing3(err)) return {};
|
|
834
857
|
throw err;
|
|
835
858
|
}
|
|
836
859
|
}
|
|
837
860
|
async function writeConfig(config) {
|
|
838
|
-
await
|
|
839
|
-
await
|
|
861
|
+
await import_promises6.default.mkdir(resolveHome(), { recursive: true });
|
|
862
|
+
await import_promises6.default.writeFile(configPath(), `${JSON.stringify(config, null, 2)}
|
|
840
863
|
`, "utf8");
|
|
841
864
|
}
|
|
842
865
|
async function setConfigValue(key, value) {
|
|
@@ -885,7 +908,7 @@ function slugifyPath(input) {
|
|
|
885
908
|
init_collections();
|
|
886
909
|
|
|
887
910
|
// src/articles.ts
|
|
888
|
-
var
|
|
911
|
+
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
889
912
|
var import_node_path4 = __toESM(require("path"), 1);
|
|
890
913
|
init_collections();
|
|
891
914
|
init_errors();
|
|
@@ -936,8 +959,8 @@ async function createArticle(opts) {
|
|
|
936
959
|
const filePath = import_node_path4.default.join(dir, `${slugifyPath(title)}.md`);
|
|
937
960
|
const body = opts.body ?? `# ${title}
|
|
938
961
|
`;
|
|
939
|
-
await
|
|
940
|
-
await
|
|
962
|
+
await import_promises7.default.mkdir(import_node_path4.default.dirname(filePath), { recursive: true });
|
|
963
|
+
await import_promises7.default.writeFile(
|
|
941
964
|
filePath,
|
|
942
965
|
serializeArticle({ title, tags: opts.tags, body, audience: opts.audience, status: opts.status }),
|
|
943
966
|
"utf8"
|
|
@@ -950,7 +973,7 @@ async function loadArticle(target) {
|
|
|
950
973
|
const { dir } = await resolveCollectionDir(collection);
|
|
951
974
|
const filePath = import_node_path4.default.join(dir, ensureMdPath(file));
|
|
952
975
|
try {
|
|
953
|
-
return { filePath, dir, raw: await
|
|
976
|
+
return { filePath, dir, raw: await import_promises7.default.readFile(filePath, "utf8"), collection };
|
|
954
977
|
} catch (err) {
|
|
955
978
|
if (err.code === "ENOENT") throw new ArticleNotFoundError(target);
|
|
956
979
|
throw err;
|
|
@@ -959,7 +982,7 @@ async function loadArticle(target) {
|
|
|
959
982
|
async function updateArticleBody(target, body) {
|
|
960
983
|
const { filePath, dir, raw, collection } = await loadArticle(target);
|
|
961
984
|
const parsed = parseArticle(raw);
|
|
962
|
-
await
|
|
985
|
+
await import_promises7.default.writeFile(
|
|
963
986
|
filePath,
|
|
964
987
|
serializeArticle({
|
|
965
988
|
title: parsed.title,
|
|
@@ -977,7 +1000,7 @@ async function mutateTags(target, fn) {
|
|
|
977
1000
|
const { filePath, dir, raw, collection } = await loadArticle(target);
|
|
978
1001
|
const parsed = parseArticle(raw);
|
|
979
1002
|
const tags = fn(parsed.tags);
|
|
980
|
-
await
|
|
1003
|
+
await import_promises7.default.writeFile(
|
|
981
1004
|
filePath,
|
|
982
1005
|
serializeArticle({
|
|
983
1006
|
title: parsed.title,
|
|
@@ -1006,7 +1029,7 @@ async function setArticleMeta(target, meta) {
|
|
|
1006
1029
|
const parsed = parseArticle(raw);
|
|
1007
1030
|
const audience = meta.audience ?? parsed.audience;
|
|
1008
1031
|
const status = meta.status ?? parsed.status;
|
|
1009
|
-
await
|
|
1032
|
+
await import_promises7.default.writeFile(
|
|
1010
1033
|
filePath,
|
|
1011
1034
|
serializeArticle({ title: parsed.title, tags: parsed.tags, audience, status, body: parsed.body }),
|
|
1012
1035
|
"utf8"
|
|
@@ -1016,13 +1039,13 @@ async function setArticleMeta(target, meta) {
|
|
|
1016
1039
|
}
|
|
1017
1040
|
async function removeArticle(target) {
|
|
1018
1041
|
const { filePath, dir, collection } = await loadArticle(target);
|
|
1019
|
-
await
|
|
1042
|
+
await import_promises7.default.rm(filePath);
|
|
1020
1043
|
void fireHook2("article-removed", collection, import_node_path4.default.relative(dir, filePath));
|
|
1021
1044
|
return filePath;
|
|
1022
1045
|
}
|
|
1023
1046
|
|
|
1024
1047
|
// src/repo.ts
|
|
1025
|
-
var
|
|
1048
|
+
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
1026
1049
|
var import_node_path6 = __toESM(require("path"), 1);
|
|
1027
1050
|
var import_node_child_process3 = require("child_process");
|
|
1028
1051
|
var import_node_util2 = require("util");
|
|
@@ -1031,7 +1054,7 @@ init_collections();
|
|
|
1031
1054
|
init_registry();
|
|
1032
1055
|
|
|
1033
1056
|
// src/git.ts
|
|
1034
|
-
var
|
|
1057
|
+
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
1035
1058
|
var import_node_path5 = __toESM(require("path"), 1);
|
|
1036
1059
|
var import_node_child_process2 = require("child_process");
|
|
1037
1060
|
var import_node_util = require("util");
|
|
@@ -1113,7 +1136,7 @@ async function fileTimestamps(absPath) {
|
|
|
1113
1136
|
let updated = gitUpdated;
|
|
1114
1137
|
if (!created || !updated) {
|
|
1115
1138
|
try {
|
|
1116
|
-
const st = await
|
|
1139
|
+
const st = await import_promises8.default.stat(absPath);
|
|
1117
1140
|
if (!updated) updated = st.mtime.toISOString();
|
|
1118
1141
|
if (!created) created = isEpochish(st.birthtime) ? st.mtime.toISOString() : st.birthtime.toISOString();
|
|
1119
1142
|
} catch {
|
|
@@ -1138,7 +1161,7 @@ async function detectRepoName(cwd) {
|
|
|
1138
1161
|
}
|
|
1139
1162
|
async function readRepoConfig(cwd) {
|
|
1140
1163
|
try {
|
|
1141
|
-
const raw = await
|
|
1164
|
+
const raw = await import_promises9.default.readFile(import_node_path6.default.join(cwd, REPO_CONFIG_FILE), "utf8");
|
|
1142
1165
|
const parsed = (0, import_yaml2.parse)(raw) ?? {};
|
|
1143
1166
|
if (!parsed.path) return void 0;
|
|
1144
1167
|
return {
|
|
@@ -1168,9 +1191,9 @@ async function initRepoWiki(opts) {
|
|
|
1168
1191
|
n += 1;
|
|
1169
1192
|
}
|
|
1170
1193
|
}
|
|
1171
|
-
await
|
|
1194
|
+
await import_promises9.default.mkdir(absWiki, { recursive: true });
|
|
1172
1195
|
const config = { collection: name, path: wikiPath };
|
|
1173
|
-
await
|
|
1196
|
+
await import_promises9.default.writeFile(import_node_path6.default.join(cwd, REPO_CONFIG_FILE), (0, import_yaml2.stringify)(config), "utf8");
|
|
1174
1197
|
const entry = {
|
|
1175
1198
|
name,
|
|
1176
1199
|
type: "repo",
|
|
@@ -1239,7 +1262,7 @@ async function collectionGitStatus(entry) {
|
|
|
1239
1262
|
}
|
|
1240
1263
|
|
|
1241
1264
|
// src/project.ts
|
|
1242
|
-
var
|
|
1265
|
+
var import_promises10 = __toESM(require("fs/promises"), 1);
|
|
1243
1266
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
1244
1267
|
var import_node_path7 = __toESM(require("path"), 1);
|
|
1245
1268
|
var import_yaml3 = require("yaml");
|
|
@@ -1296,7 +1319,7 @@ function normaliseContext(raw) {
|
|
|
1296
1319
|
async function readProjectConfig(dir) {
|
|
1297
1320
|
let raw;
|
|
1298
1321
|
try {
|
|
1299
|
-
raw = await
|
|
1322
|
+
raw = await import_promises10.default.readFile(import_node_path7.default.join(dir, PROJECT_CONFIG_FILE), "utf8");
|
|
1300
1323
|
} catch (err) {
|
|
1301
1324
|
if (err.code === "ENOENT") return void 0;
|
|
1302
1325
|
throw err;
|
|
@@ -1331,13 +1354,13 @@ async function setContextCollections(dir, collections) {
|
|
|
1331
1354
|
const file = import_node_path7.default.join(dir, PROJECT_CONFIG_FILE);
|
|
1332
1355
|
let raw = "";
|
|
1333
1356
|
try {
|
|
1334
|
-
raw = await
|
|
1357
|
+
raw = await import_promises10.default.readFile(file, "utf8");
|
|
1335
1358
|
} catch (err) {
|
|
1336
1359
|
if (err.code !== "ENOENT") throw err;
|
|
1337
1360
|
}
|
|
1338
1361
|
const doc = (0, import_yaml3.parseDocument)(raw);
|
|
1339
1362
|
doc.setIn(["context", "collections"], collections);
|
|
1340
|
-
await
|
|
1363
|
+
await import_promises10.default.writeFile(file, doc.toString(), "utf8");
|
|
1341
1364
|
return file;
|
|
1342
1365
|
}
|
|
1343
1366
|
async function resolveProjectCollections(dir) {
|
|
@@ -1360,8 +1383,8 @@ async function seedProjectHome(dir) {
|
|
|
1360
1383
|
if (entries.length === 0) {
|
|
1361
1384
|
throw new Error(`No collections to render from diffwiki.yaml in ${import_node_path7.default.resolve(dir)}`);
|
|
1362
1385
|
}
|
|
1363
|
-
const home = await
|
|
1364
|
-
await
|
|
1386
|
+
const home = await import_promises10.default.mkdtemp(import_node_path7.default.join(import_node_os2.default.tmpdir(), "diffwiki-project-"));
|
|
1387
|
+
await import_promises10.default.writeFile(
|
|
1365
1388
|
import_node_path7.default.join(home, "registry.json"),
|
|
1366
1389
|
`${JSON.stringify({ version: 1, collections: entries }, null, 2)}
|
|
1367
1390
|
`,
|
|
@@ -1374,7 +1397,7 @@ async function seedProjectHome(dir) {
|
|
|
1374
1397
|
init_collections();
|
|
1375
1398
|
|
|
1376
1399
|
// src/browse.ts
|
|
1377
|
-
var
|
|
1400
|
+
var import_promises11 = __toESM(require("fs/promises"), 1);
|
|
1378
1401
|
var import_node_path8 = __toESM(require("path"), 1);
|
|
1379
1402
|
init_collections();
|
|
1380
1403
|
init_errors();
|
|
@@ -1421,7 +1444,7 @@ function buildArticleTree(relPaths) {
|
|
|
1421
1444
|
async function collectRelPaths(dir, rel = "") {
|
|
1422
1445
|
let dirents;
|
|
1423
1446
|
try {
|
|
1424
|
-
dirents = await
|
|
1447
|
+
dirents = await import_promises11.default.readdir(dir, { withFileTypes: true });
|
|
1425
1448
|
} catch {
|
|
1426
1449
|
return [];
|
|
1427
1450
|
}
|
|
@@ -1439,7 +1462,7 @@ async function collectRelPaths(dir, rel = "") {
|
|
|
1439
1462
|
}
|
|
1440
1463
|
async function readdirSafe(dir) {
|
|
1441
1464
|
try {
|
|
1442
|
-
return await
|
|
1465
|
+
return await import_promises11.default.readdir(dir);
|
|
1443
1466
|
} catch {
|
|
1444
1467
|
return [];
|
|
1445
1468
|
}
|
|
@@ -1457,7 +1480,7 @@ var CHEAP_META_BYTES = 8192;
|
|
|
1457
1480
|
async function cheapMeta(absPath) {
|
|
1458
1481
|
let handle;
|
|
1459
1482
|
try {
|
|
1460
|
-
handle = await
|
|
1483
|
+
handle = await import_promises11.default.open(absPath, "r");
|
|
1461
1484
|
const buf = Buffer.alloc(CHEAP_META_BYTES);
|
|
1462
1485
|
const { bytesRead } = await handle.read(buf, 0, CHEAP_META_BYTES, 0);
|
|
1463
1486
|
const snippet = buf.subarray(0, bytesRead).toString("utf8");
|
|
@@ -1548,7 +1571,7 @@ async function readArticle(coll, slug) {
|
|
|
1548
1571
|
const candidate = import_node_path8.default.resolve(collDir, rel);
|
|
1549
1572
|
if (!inJail(candidate)) throw new InvalidTargetError(slug, "path traversal detected");
|
|
1550
1573
|
try {
|
|
1551
|
-
await
|
|
1574
|
+
await import_promises11.default.access(candidate);
|
|
1552
1575
|
abs = candidate;
|
|
1553
1576
|
break;
|
|
1554
1577
|
} catch {
|
|
@@ -1565,7 +1588,7 @@ async function readArticle(coll, slug) {
|
|
|
1565
1588
|
}
|
|
1566
1589
|
}
|
|
1567
1590
|
if (!abs) throw new ArticleNotFoundError(`${coll}/${safeSlug}`);
|
|
1568
|
-
const parsed = parseArticle(await
|
|
1591
|
+
const parsed = parseArticle(await import_promises11.default.readFile(abs, "utf8"));
|
|
1569
1592
|
const title = parsed.title?.trim() || (safeSlug ? safeSlug.split("/").pop() ?? safeSlug : coll);
|
|
1570
1593
|
return {
|
|
1571
1594
|
coll,
|
|
@@ -2012,7 +2035,7 @@ function resolveCollectionSelection(all, sel = {}) {
|
|
|
2012
2035
|
}
|
|
2013
2036
|
|
|
2014
2037
|
// src/query.ts
|
|
2015
|
-
var
|
|
2038
|
+
var import_promises12 = __toESM(require("fs/promises"), 1);
|
|
2016
2039
|
var import_node_path10 = __toESM(require("path"), 1);
|
|
2017
2040
|
init_collections();
|
|
2018
2041
|
init_errors();
|
|
@@ -2088,7 +2111,7 @@ var log2 = getLogger("query");
|
|
|
2088
2111
|
async function collectMarkdownFiles(dir) {
|
|
2089
2112
|
let entries;
|
|
2090
2113
|
try {
|
|
2091
|
-
entries = await
|
|
2114
|
+
entries = await import_promises12.default.readdir(dir, { withFileTypes: true });
|
|
2092
2115
|
} catch {
|
|
2093
2116
|
return [];
|
|
2094
2117
|
}
|
|
@@ -2126,7 +2149,7 @@ async function nativeSearch(term, collections, collection, hideSystem = false) {
|
|
|
2126
2149
|
let tags = [];
|
|
2127
2150
|
let body = "";
|
|
2128
2151
|
try {
|
|
2129
|
-
const parsed = parseArticle(await
|
|
2152
|
+
const parsed = parseArticle(await import_promises12.default.readFile(full, "utf8"));
|
|
2130
2153
|
if (hideSystem && parsed.audience === "system") continue;
|
|
2131
2154
|
if (parsed.title) title = parsed.title;
|
|
2132
2155
|
tags = parsed.tags;
|
|
@@ -2184,7 +2207,7 @@ async function dropSystemHits(hits) {
|
|
|
2184
2207
|
const kept = [];
|
|
2185
2208
|
for (const hit of hits) {
|
|
2186
2209
|
try {
|
|
2187
|
-
if (parseArticle(await
|
|
2210
|
+
if (parseArticle(await import_promises12.default.readFile(hit.path, "utf8")).audience === "system") continue;
|
|
2188
2211
|
} catch {
|
|
2189
2212
|
}
|
|
2190
2213
|
kept.push(hit);
|
|
@@ -2232,14 +2255,14 @@ async function query(term, opts) {
|
|
|
2232
2255
|
}
|
|
2233
2256
|
|
|
2234
2257
|
// src/search.ts
|
|
2235
|
-
var
|
|
2258
|
+
var import_promises13 = __toESM(require("fs/promises"), 1);
|
|
2236
2259
|
var import_node_path11 = __toESM(require("path"), 1);
|
|
2237
2260
|
init_collections();
|
|
2238
2261
|
var isStatic2 = () => process.env.DIFFWIKI_STATIC === "1";
|
|
2239
2262
|
async function readArticleParts(collDir, slug) {
|
|
2240
2263
|
for (const ext of [".mdx", ".md"]) {
|
|
2241
2264
|
try {
|
|
2242
|
-
const parsed = parseArticle(await
|
|
2265
|
+
const parsed = parseArticle(await import_promises13.default.readFile(import_node_path11.default.join(collDir, `${slug}${ext}`), "utf8"));
|
|
2243
2266
|
return { tags: parsed.tags, body: parsed.body };
|
|
2244
2267
|
} catch {
|
|
2245
2268
|
}
|
|
@@ -2289,13 +2312,13 @@ async function buildSearchIndex(collection) {
|
|
|
2289
2312
|
}
|
|
2290
2313
|
|
|
2291
2314
|
// src/doctor.ts
|
|
2292
|
-
var
|
|
2315
|
+
var import_promises15 = __toESM(require("fs/promises"), 1);
|
|
2293
2316
|
init_paths();
|
|
2294
2317
|
init_registry();
|
|
2295
2318
|
init_collections();
|
|
2296
2319
|
|
|
2297
2320
|
// src/plugins/manager.ts
|
|
2298
|
-
var
|
|
2321
|
+
var import_promises14 = __toESM(require("fs/promises"), 1);
|
|
2299
2322
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
2300
2323
|
var import_node_path13 = __toESM(require("path"), 1);
|
|
2301
2324
|
init_paths();
|
|
@@ -2324,7 +2347,7 @@ async function runProcess(file, args, opts) {
|
|
|
2324
2347
|
// src/plugins/manager.ts
|
|
2325
2348
|
async function readPackageJson(file) {
|
|
2326
2349
|
try {
|
|
2327
|
-
return JSON.parse(await
|
|
2350
|
+
return JSON.parse(await import_promises14.default.readFile(file, "utf8"));
|
|
2328
2351
|
} catch {
|
|
2329
2352
|
return null;
|
|
2330
2353
|
}
|
|
@@ -2340,7 +2363,7 @@ function binOf(manifest) {
|
|
|
2340
2363
|
async function pluginManifestDirs(nodeModules) {
|
|
2341
2364
|
let entries;
|
|
2342
2365
|
try {
|
|
2343
|
-
entries = await
|
|
2366
|
+
entries = await import_promises14.default.readdir(nodeModules);
|
|
2344
2367
|
} catch {
|
|
2345
2368
|
return [];
|
|
2346
2369
|
}
|
|
@@ -2350,7 +2373,7 @@ async function pluginManifestDirs(nodeModules) {
|
|
|
2350
2373
|
if (entry.startsWith("@")) {
|
|
2351
2374
|
let scoped;
|
|
2352
2375
|
try {
|
|
2353
|
-
scoped = await
|
|
2376
|
+
scoped = await import_promises14.default.readdir(import_node_path13.default.join(nodeModules, entry));
|
|
2354
2377
|
} catch {
|
|
2355
2378
|
continue;
|
|
2356
2379
|
}
|
|
@@ -2366,11 +2389,11 @@ async function pluginManifestDirs(nodeModules) {
|
|
|
2366
2389
|
}
|
|
2367
2390
|
async function ensurePluginRoot() {
|
|
2368
2391
|
const dir = pluginsDir();
|
|
2369
|
-
await
|
|
2392
|
+
await import_promises14.default.mkdir(dir, { recursive: true });
|
|
2370
2393
|
const pkgPath = import_node_path13.default.join(dir, "package.json");
|
|
2371
2394
|
if (await readPackageJson(pkgPath) === null) {
|
|
2372
2395
|
const pkg = { name: "diffwiki-plugins", private: true, version: "0.0.0" };
|
|
2373
|
-
await
|
|
2396
|
+
await import_promises14.default.writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
2374
2397
|
`, "utf8");
|
|
2375
2398
|
}
|
|
2376
2399
|
return dir;
|
|
@@ -2541,7 +2564,7 @@ init_host();
|
|
|
2541
2564
|
init_types();
|
|
2542
2565
|
async function exists(target) {
|
|
2543
2566
|
try {
|
|
2544
|
-
await
|
|
2567
|
+
await import_promises15.default.access(target);
|
|
2545
2568
|
return true;
|
|
2546
2569
|
} catch {
|
|
2547
2570
|
return false;
|
|
@@ -2647,7 +2670,7 @@ async function appendPluginDiagnostics(out, config) {
|
|
|
2647
2670
|
}
|
|
2648
2671
|
|
|
2649
2672
|
// src/lint.ts
|
|
2650
|
-
var
|
|
2673
|
+
var import_promises16 = __toESM(require("fs/promises"), 1);
|
|
2651
2674
|
var import_node_path14 = __toESM(require("path"), 1);
|
|
2652
2675
|
init_collections();
|
|
2653
2676
|
init_errors();
|
|
@@ -2742,7 +2765,7 @@ function lintArticle(article) {
|
|
|
2742
2765
|
async function walkMarkdown(dir) {
|
|
2743
2766
|
let entries;
|
|
2744
2767
|
try {
|
|
2745
|
-
entries = await
|
|
2768
|
+
entries = await import_promises16.default.readdir(dir, { withFileTypes: true });
|
|
2746
2769
|
} catch {
|
|
2747
2770
|
return [];
|
|
2748
2771
|
}
|
|
@@ -2782,7 +2805,7 @@ async function lintCollections(scope) {
|
|
|
2782
2805
|
const label = `${coll.name}/${rel}`;
|
|
2783
2806
|
let findings = [];
|
|
2784
2807
|
try {
|
|
2785
|
-
const parsed = parseArticle(await
|
|
2808
|
+
const parsed = parseArticle(await import_promises16.default.readFile(full, "utf8"));
|
|
2786
2809
|
findings = lintArticle(parsed);
|
|
2787
2810
|
} catch (err) {
|
|
2788
2811
|
findings = [{ level: "error", rule: "parse", message: `could not parse: ${err.message}` }];
|
|
@@ -2888,6 +2911,7 @@ init_registry2();
|
|
|
2888
2911
|
doctor,
|
|
2889
2912
|
emitPluginEvent,
|
|
2890
2913
|
ensureGitRepo,
|
|
2914
|
+
ensureHome,
|
|
2891
2915
|
ensurePluginRoot,
|
|
2892
2916
|
fileTimestamps,
|
|
2893
2917
|
findCollection,
|
package/dist/index.d.cts
CHANGED
|
@@ -46,6 +46,18 @@ declare function collectionDir(name: string): string;
|
|
|
46
46
|
declare function pluginsDir(): string;
|
|
47
47
|
declare function pluginsRegistryPath(): string;
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Initialize the global diffwiki home (`~/.diffwiki`, or `DIFFWIKI_HOME`).
|
|
51
|
+
* Creates the directory tree (`collections/`, `plugins/`, `templates/`) and
|
|
52
|
+
* seeds empty-but-valid `registry.json` and `config.json` when absent.
|
|
53
|
+
* Idempotent — never overwrites existing files or user data.
|
|
54
|
+
*
|
|
55
|
+
* The CLI calls this at startup so the FIRST command a user runs — e.g.
|
|
56
|
+
* `npx diffwiki collection add` — leaves a fully initialized home behind, not
|
|
57
|
+
* just the one file that command happened to write.
|
|
58
|
+
*/
|
|
59
|
+
declare function ensureHome(): Promise<void>;
|
|
60
|
+
|
|
49
61
|
/**
|
|
50
62
|
* Authoring-template seeding.
|
|
51
63
|
*
|
|
@@ -795,4 +807,4 @@ declare function findEnabledPlugin(kind: PluginKind, name?: string): Promise<Plu
|
|
|
795
807
|
/** Find a plugin record by name (regardless of enabled state). */
|
|
796
808
|
declare function findPluginRecord(name: string): Promise<PluginRecord | undefined>;
|
|
797
809
|
|
|
798
|
-
export { type AddExternalOptions, Article, ArticleContent, ArticleNode, ArticleNotFoundError, Audience, CollectionAlreadyAddedError, CollectionEntry, CollectionExistsError, type CollectionGitStatus, CollectionInfo, CollectionNotFoundError, type CollectionSelection, type CollectionSelectionResult, CollectionTree, Config, Diagnostic, DiffwikiError, type DiffwikiLogLevel, type EditTarget, type FileTimestamps, type InitOptions, type InstallOutcome, InvalidExportSelectionError, InvalidTargetError, type KnownPlugin, LinkGraph, type LintFileResult, type LintFinding, type LintLevel, type LintResult, LocalGraph, NoDefaultCollectionError, PROJECT_CONFIG_FILE, PageData, ParsedArticle, PluginError, PluginEvent, PluginKind, PluginRecord, PluginRegistryFile, ProjectConfig, PublishStatus, QueryHit, QueryOptions, REPO_CONFIG_FILE, Registry, type RenderContext, type RenderedArticle, RepoConfig, SearchDoc, SearchMode, type SearchOutcome, SearchProvider, type SeedResult, type SetupOutcome, type SetupStep, SiteConfig, type TemplateInfo, TocItem, TreeNode, type UpstreamStatus, WorktreeNotAllowedError, addExternalCollection, addTags, buildArticleTree, buildCollections, buildLinkGraph, buildNavTree, buildSearchIndex, buildSiteConfig, collectionDir, collectionGitStatus, collectionMeta, collectionsDir, configPath, createArticle, createCollection, currentBranch, deregisterPlugin, doctor, emitPluginEvent, ensureGitRepo, ensurePluginRoot, fileTimestamps, findCollection, findCollectionById, findEnabledPlugin, findPluginRecord, getConfigValue, getLogger, gitToplevel, initRepoWiki, installPlugin, invalidateLinkGraph, isLinkedWorktree, lintArticle, lintCollections, listArticleTree, listAvailablePlugins, listCollections, listEnabledPlugins, listPlugins, listSearchModes, listTemplateShapes, listTemplates, loadSearchProvider, localGraph, nativeSearch, parseAddTarget, parsePathTarget, pluginsDir, pluginsRegistryPath, query, readArticle, readConfig, readContextCollections, readPluginRegistry, readProjectConfig, readRegistry, readRepoConfig, readSiteConfig, registerCollection, registerPlugin, registryPath, removeArticle, removeCollection, removePlugin, removeTags, renderArticle, repairPlugins, repoId, repoRemoteName, resolveCollectionSelection, resolveCwdCollection, resolveDefaultSearchMode, resolveEditTarget, resolveEditorCommand, resolveHome, resolveLogLevel, resolvePage, resolveProjectCollections, search, seedProjectHome, seedTemplates, setArticleMeta, setConfigValue, setContextCollections, setTags, showSystemEnabled, slugify, slugifyPath, templatesDir, toTreeNodes, unregisterCollection, updateArticleBody, upsertPlugin, upstreamStatus, writeConfig, writePluginRegistry, writeRegistry };
|
|
810
|
+
export { type AddExternalOptions, Article, ArticleContent, ArticleNode, ArticleNotFoundError, Audience, CollectionAlreadyAddedError, CollectionEntry, CollectionExistsError, type CollectionGitStatus, CollectionInfo, CollectionNotFoundError, type CollectionSelection, type CollectionSelectionResult, CollectionTree, Config, Diagnostic, DiffwikiError, type DiffwikiLogLevel, type EditTarget, type FileTimestamps, type InitOptions, type InstallOutcome, InvalidExportSelectionError, InvalidTargetError, type KnownPlugin, LinkGraph, type LintFileResult, type LintFinding, type LintLevel, type LintResult, LocalGraph, NoDefaultCollectionError, PROJECT_CONFIG_FILE, PageData, ParsedArticle, PluginError, PluginEvent, PluginKind, PluginRecord, PluginRegistryFile, ProjectConfig, PublishStatus, QueryHit, QueryOptions, REPO_CONFIG_FILE, Registry, type RenderContext, type RenderedArticle, RepoConfig, SearchDoc, SearchMode, type SearchOutcome, SearchProvider, type SeedResult, type SetupOutcome, type SetupStep, SiteConfig, type TemplateInfo, TocItem, TreeNode, type UpstreamStatus, WorktreeNotAllowedError, addExternalCollection, addTags, buildArticleTree, buildCollections, buildLinkGraph, buildNavTree, buildSearchIndex, buildSiteConfig, collectionDir, collectionGitStatus, collectionMeta, collectionsDir, configPath, createArticle, createCollection, currentBranch, deregisterPlugin, doctor, emitPluginEvent, ensureGitRepo, ensureHome, ensurePluginRoot, fileTimestamps, findCollection, findCollectionById, findEnabledPlugin, findPluginRecord, getConfigValue, getLogger, gitToplevel, initRepoWiki, installPlugin, invalidateLinkGraph, isLinkedWorktree, lintArticle, lintCollections, listArticleTree, listAvailablePlugins, listCollections, listEnabledPlugins, listPlugins, listSearchModes, listTemplateShapes, listTemplates, loadSearchProvider, localGraph, nativeSearch, parseAddTarget, parsePathTarget, pluginsDir, pluginsRegistryPath, query, readArticle, readConfig, readContextCollections, readPluginRegistry, readProjectConfig, readRegistry, readRepoConfig, readSiteConfig, registerCollection, registerPlugin, registryPath, removeArticle, removeCollection, removePlugin, removeTags, renderArticle, repairPlugins, repoId, repoRemoteName, resolveCollectionSelection, resolveCwdCollection, resolveDefaultSearchMode, resolveEditTarget, resolveEditorCommand, resolveHome, resolveLogLevel, resolvePage, resolveProjectCollections, search, seedProjectHome, seedTemplates, setArticleMeta, setConfigValue, setContextCollections, setTags, showSystemEnabled, slugify, slugifyPath, templatesDir, toTreeNodes, unregisterCollection, updateArticleBody, upsertPlugin, upstreamStatus, writeConfig, writePluginRegistry, writeRegistry };
|
package/dist/index.d.ts
CHANGED
|
@@ -46,6 +46,18 @@ declare function collectionDir(name: string): string;
|
|
|
46
46
|
declare function pluginsDir(): string;
|
|
47
47
|
declare function pluginsRegistryPath(): string;
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Initialize the global diffwiki home (`~/.diffwiki`, or `DIFFWIKI_HOME`).
|
|
51
|
+
* Creates the directory tree (`collections/`, `plugins/`, `templates/`) and
|
|
52
|
+
* seeds empty-but-valid `registry.json` and `config.json` when absent.
|
|
53
|
+
* Idempotent — never overwrites existing files or user data.
|
|
54
|
+
*
|
|
55
|
+
* The CLI calls this at startup so the FIRST command a user runs — e.g.
|
|
56
|
+
* `npx diffwiki collection add` — leaves a fully initialized home behind, not
|
|
57
|
+
* just the one file that command happened to write.
|
|
58
|
+
*/
|
|
59
|
+
declare function ensureHome(): Promise<void>;
|
|
60
|
+
|
|
49
61
|
/**
|
|
50
62
|
* Authoring-template seeding.
|
|
51
63
|
*
|
|
@@ -795,4 +807,4 @@ declare function findEnabledPlugin(kind: PluginKind, name?: string): Promise<Plu
|
|
|
795
807
|
/** Find a plugin record by name (regardless of enabled state). */
|
|
796
808
|
declare function findPluginRecord(name: string): Promise<PluginRecord | undefined>;
|
|
797
809
|
|
|
798
|
-
export { type AddExternalOptions, Article, ArticleContent, ArticleNode, ArticleNotFoundError, Audience, CollectionAlreadyAddedError, CollectionEntry, CollectionExistsError, type CollectionGitStatus, CollectionInfo, CollectionNotFoundError, type CollectionSelection, type CollectionSelectionResult, CollectionTree, Config, Diagnostic, DiffwikiError, type DiffwikiLogLevel, type EditTarget, type FileTimestamps, type InitOptions, type InstallOutcome, InvalidExportSelectionError, InvalidTargetError, type KnownPlugin, LinkGraph, type LintFileResult, type LintFinding, type LintLevel, type LintResult, LocalGraph, NoDefaultCollectionError, PROJECT_CONFIG_FILE, PageData, ParsedArticle, PluginError, PluginEvent, PluginKind, PluginRecord, PluginRegistryFile, ProjectConfig, PublishStatus, QueryHit, QueryOptions, REPO_CONFIG_FILE, Registry, type RenderContext, type RenderedArticle, RepoConfig, SearchDoc, SearchMode, type SearchOutcome, SearchProvider, type SeedResult, type SetupOutcome, type SetupStep, SiteConfig, type TemplateInfo, TocItem, TreeNode, type UpstreamStatus, WorktreeNotAllowedError, addExternalCollection, addTags, buildArticleTree, buildCollections, buildLinkGraph, buildNavTree, buildSearchIndex, buildSiteConfig, collectionDir, collectionGitStatus, collectionMeta, collectionsDir, configPath, createArticle, createCollection, currentBranch, deregisterPlugin, doctor, emitPluginEvent, ensureGitRepo, ensurePluginRoot, fileTimestamps, findCollection, findCollectionById, findEnabledPlugin, findPluginRecord, getConfigValue, getLogger, gitToplevel, initRepoWiki, installPlugin, invalidateLinkGraph, isLinkedWorktree, lintArticle, lintCollections, listArticleTree, listAvailablePlugins, listCollections, listEnabledPlugins, listPlugins, listSearchModes, listTemplateShapes, listTemplates, loadSearchProvider, localGraph, nativeSearch, parseAddTarget, parsePathTarget, pluginsDir, pluginsRegistryPath, query, readArticle, readConfig, readContextCollections, readPluginRegistry, readProjectConfig, readRegistry, readRepoConfig, readSiteConfig, registerCollection, registerPlugin, registryPath, removeArticle, removeCollection, removePlugin, removeTags, renderArticle, repairPlugins, repoId, repoRemoteName, resolveCollectionSelection, resolveCwdCollection, resolveDefaultSearchMode, resolveEditTarget, resolveEditorCommand, resolveHome, resolveLogLevel, resolvePage, resolveProjectCollections, search, seedProjectHome, seedTemplates, setArticleMeta, setConfigValue, setContextCollections, setTags, showSystemEnabled, slugify, slugifyPath, templatesDir, toTreeNodes, unregisterCollection, updateArticleBody, upsertPlugin, upstreamStatus, writeConfig, writePluginRegistry, writeRegistry };
|
|
810
|
+
export { type AddExternalOptions, Article, ArticleContent, ArticleNode, ArticleNotFoundError, Audience, CollectionAlreadyAddedError, CollectionEntry, CollectionExistsError, type CollectionGitStatus, CollectionInfo, CollectionNotFoundError, type CollectionSelection, type CollectionSelectionResult, CollectionTree, Config, Diagnostic, DiffwikiError, type DiffwikiLogLevel, type EditTarget, type FileTimestamps, type InitOptions, type InstallOutcome, InvalidExportSelectionError, InvalidTargetError, type KnownPlugin, LinkGraph, type LintFileResult, type LintFinding, type LintLevel, type LintResult, LocalGraph, NoDefaultCollectionError, PROJECT_CONFIG_FILE, PageData, ParsedArticle, PluginError, PluginEvent, PluginKind, PluginRecord, PluginRegistryFile, ProjectConfig, PublishStatus, QueryHit, QueryOptions, REPO_CONFIG_FILE, Registry, type RenderContext, type RenderedArticle, RepoConfig, SearchDoc, SearchMode, type SearchOutcome, SearchProvider, type SeedResult, type SetupOutcome, type SetupStep, SiteConfig, type TemplateInfo, TocItem, TreeNode, type UpstreamStatus, WorktreeNotAllowedError, addExternalCollection, addTags, buildArticleTree, buildCollections, buildLinkGraph, buildNavTree, buildSearchIndex, buildSiteConfig, collectionDir, collectionGitStatus, collectionMeta, collectionsDir, configPath, createArticle, createCollection, currentBranch, deregisterPlugin, doctor, emitPluginEvent, ensureGitRepo, ensureHome, ensurePluginRoot, fileTimestamps, findCollection, findCollectionById, findEnabledPlugin, findPluginRecord, getConfigValue, getLogger, gitToplevel, initRepoWiki, installPlugin, invalidateLinkGraph, isLinkedWorktree, lintArticle, lintCollections, listArticleTree, listAvailablePlugins, listCollections, listEnabledPlugins, listPlugins, listSearchModes, listTemplateShapes, listTemplates, loadSearchProvider, localGraph, nativeSearch, parseAddTarget, parsePathTarget, pluginsDir, pluginsRegistryPath, query, readArticle, readConfig, readContextCollections, readPluginRegistry, readProjectConfig, readRegistry, readRepoConfig, readSiteConfig, registerCollection, registerPlugin, registryPath, removeArticle, removeCollection, removePlugin, removeTags, renderArticle, repairPlugins, repoId, repoRemoteName, resolveCollectionSelection, resolveCwdCollection, resolveDefaultSearchMode, resolveEditTarget, resolveEditorCommand, resolveHome, resolveLogLevel, resolvePage, resolveProjectCollections, search, seedProjectHome, seedTemplates, setArticleMeta, setConfigValue, setContextCollections, setTags, showSystemEnabled, slugify, slugifyPath, templatesDir, toTreeNodes, unregisterCollection, updateArticleBody, upsertPlugin, upstreamStatus, writeConfig, writePluginRegistry, writeRegistry };
|
package/dist/index.js
CHANGED
|
@@ -54,39 +54,60 @@ import {
|
|
|
54
54
|
writeRegistry
|
|
55
55
|
} from "./chunk-TXM4OGBA.js";
|
|
56
56
|
|
|
57
|
-
// src/
|
|
57
|
+
// src/home.ts
|
|
58
58
|
import fs from "fs/promises";
|
|
59
|
+
async function seedIfAbsent(file, contents) {
|
|
60
|
+
try {
|
|
61
|
+
await fs.writeFile(file, contents(), { encoding: "utf8", flag: "wx" });
|
|
62
|
+
} catch (err) {
|
|
63
|
+
if (err.code !== "EEXIST") throw err;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function ensureHome() {
|
|
67
|
+
await fs.mkdir(resolveHome(), { recursive: true });
|
|
68
|
+
await Promise.all([
|
|
69
|
+
fs.mkdir(collectionsDir(), { recursive: true }),
|
|
70
|
+
fs.mkdir(pluginsDir(), { recursive: true }),
|
|
71
|
+
fs.mkdir(templatesDir(), { recursive: true })
|
|
72
|
+
]);
|
|
73
|
+
await seedIfAbsent(registryPath(), () => `${JSON.stringify({ version: 1, collections: [] }, null, 2)}
|
|
74
|
+
`);
|
|
75
|
+
await seedIfAbsent(configPath(), () => "{}\n");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/templates.ts
|
|
79
|
+
import fs2 from "fs/promises";
|
|
59
80
|
import path from "path";
|
|
60
81
|
async function seedTemplates(srcDir) {
|
|
61
82
|
const dir = templatesDir();
|
|
62
|
-
await
|
|
63
|
-
const files = (await
|
|
83
|
+
await fs2.mkdir(dir, { recursive: true });
|
|
84
|
+
const files = (await fs2.readdir(srcDir)).filter((f) => f.endsWith(".md")).sort();
|
|
64
85
|
const seeded = [];
|
|
65
86
|
const skipped = [];
|
|
66
87
|
for (const file of files) {
|
|
67
88
|
const dest = path.join(dir, file);
|
|
68
89
|
try {
|
|
69
|
-
await
|
|
90
|
+
await fs2.access(dest);
|
|
70
91
|
skipped.push(file);
|
|
71
92
|
continue;
|
|
72
93
|
} catch {
|
|
73
94
|
}
|
|
74
|
-
await
|
|
95
|
+
await fs2.copyFile(path.join(srcDir, file), dest);
|
|
75
96
|
seeded.push(file);
|
|
76
97
|
}
|
|
77
98
|
return { seeded, skipped, dir };
|
|
78
99
|
}
|
|
79
100
|
async function listTemplateShapes(srcDir) {
|
|
80
|
-
const files = await
|
|
101
|
+
const files = await fs2.readdir(srcDir);
|
|
81
102
|
return files.filter((f) => f.endsWith("-template.md")).map((f) => f.replace(/-template\.md$/, "")).sort();
|
|
82
103
|
}
|
|
83
104
|
async function listTemplates(srcDir) {
|
|
84
|
-
const files = (await
|
|
105
|
+
const files = (await fs2.readdir(srcDir)).filter((f) => f.endsWith("-template.md")).sort();
|
|
85
106
|
const out = [];
|
|
86
107
|
for (const file of files) {
|
|
87
108
|
let data = {};
|
|
88
109
|
try {
|
|
89
|
-
({ data } = parseFrontmatter(await
|
|
110
|
+
({ data } = parseFrontmatter(await fs2.readFile(path.join(srcDir, file), "utf8")));
|
|
90
111
|
} catch {
|
|
91
112
|
}
|
|
92
113
|
const shape = typeof data.shape === "string" && data.shape ? data.shape : file.replace(/-template\.md$/, "");
|
|
@@ -98,21 +119,21 @@ async function listTemplates(srcDir) {
|
|
|
98
119
|
}
|
|
99
120
|
|
|
100
121
|
// src/config.ts
|
|
101
|
-
import
|
|
122
|
+
import fs3 from "fs/promises";
|
|
102
123
|
function isMissing(err) {
|
|
103
124
|
return err?.code === "ENOENT";
|
|
104
125
|
}
|
|
105
126
|
async function readConfig() {
|
|
106
127
|
try {
|
|
107
|
-
return JSON.parse(await
|
|
128
|
+
return JSON.parse(await fs3.readFile(configPath(), "utf8"));
|
|
108
129
|
} catch (err) {
|
|
109
130
|
if (isMissing(err)) return {};
|
|
110
131
|
throw err;
|
|
111
132
|
}
|
|
112
133
|
}
|
|
113
134
|
async function writeConfig(config) {
|
|
114
|
-
await
|
|
115
|
-
await
|
|
135
|
+
await fs3.mkdir(resolveHome(), { recursive: true });
|
|
136
|
+
await fs3.writeFile(configPath(), `${JSON.stringify(config, null, 2)}
|
|
116
137
|
`, "utf8");
|
|
117
138
|
}
|
|
118
139
|
async function setConfigValue(key, value) {
|
|
@@ -155,7 +176,7 @@ function slugifyPath(input) {
|
|
|
155
176
|
}
|
|
156
177
|
|
|
157
178
|
// src/articles.ts
|
|
158
|
-
import
|
|
179
|
+
import fs4 from "fs/promises";
|
|
159
180
|
import path2 from "path";
|
|
160
181
|
async function fireHook(event, collection, relPath) {
|
|
161
182
|
try {
|
|
@@ -204,8 +225,8 @@ async function createArticle(opts) {
|
|
|
204
225
|
const filePath = path2.join(dir, `${slugifyPath(title)}.md`);
|
|
205
226
|
const body = opts.body ?? `# ${title}
|
|
206
227
|
`;
|
|
207
|
-
await
|
|
208
|
-
await
|
|
228
|
+
await fs4.mkdir(path2.dirname(filePath), { recursive: true });
|
|
229
|
+
await fs4.writeFile(
|
|
209
230
|
filePath,
|
|
210
231
|
serializeArticle({ title, tags: opts.tags, body, audience: opts.audience, status: opts.status }),
|
|
211
232
|
"utf8"
|
|
@@ -218,7 +239,7 @@ async function loadArticle(target) {
|
|
|
218
239
|
const { dir } = await resolveCollectionDir(collection);
|
|
219
240
|
const filePath = path2.join(dir, ensureMdPath(file));
|
|
220
241
|
try {
|
|
221
|
-
return { filePath, dir, raw: await
|
|
242
|
+
return { filePath, dir, raw: await fs4.readFile(filePath, "utf8"), collection };
|
|
222
243
|
} catch (err) {
|
|
223
244
|
if (err.code === "ENOENT") throw new ArticleNotFoundError(target);
|
|
224
245
|
throw err;
|
|
@@ -227,7 +248,7 @@ async function loadArticle(target) {
|
|
|
227
248
|
async function updateArticleBody(target, body) {
|
|
228
249
|
const { filePath, dir, raw, collection } = await loadArticle(target);
|
|
229
250
|
const parsed = parseArticle(raw);
|
|
230
|
-
await
|
|
251
|
+
await fs4.writeFile(
|
|
231
252
|
filePath,
|
|
232
253
|
serializeArticle({
|
|
233
254
|
title: parsed.title,
|
|
@@ -245,7 +266,7 @@ async function mutateTags(target, fn) {
|
|
|
245
266
|
const { filePath, dir, raw, collection } = await loadArticle(target);
|
|
246
267
|
const parsed = parseArticle(raw);
|
|
247
268
|
const tags = fn(parsed.tags);
|
|
248
|
-
await
|
|
269
|
+
await fs4.writeFile(
|
|
249
270
|
filePath,
|
|
250
271
|
serializeArticle({
|
|
251
272
|
title: parsed.title,
|
|
@@ -274,7 +295,7 @@ async function setArticleMeta(target, meta) {
|
|
|
274
295
|
const parsed = parseArticle(raw);
|
|
275
296
|
const audience = meta.audience ?? parsed.audience;
|
|
276
297
|
const status = meta.status ?? parsed.status;
|
|
277
|
-
await
|
|
298
|
+
await fs4.writeFile(
|
|
278
299
|
filePath,
|
|
279
300
|
serializeArticle({ title: parsed.title, tags: parsed.tags, audience, status, body: parsed.body }),
|
|
280
301
|
"utf8"
|
|
@@ -284,20 +305,20 @@ async function setArticleMeta(target, meta) {
|
|
|
284
305
|
}
|
|
285
306
|
async function removeArticle(target) {
|
|
286
307
|
const { filePath, dir, collection } = await loadArticle(target);
|
|
287
|
-
await
|
|
308
|
+
await fs4.rm(filePath);
|
|
288
309
|
void fireHook("article-removed", collection, path2.relative(dir, filePath));
|
|
289
310
|
return filePath;
|
|
290
311
|
}
|
|
291
312
|
|
|
292
313
|
// src/repo.ts
|
|
293
|
-
import
|
|
314
|
+
import fs6 from "fs/promises";
|
|
294
315
|
import path4 from "path";
|
|
295
316
|
import { execFile as execFile2 } from "child_process";
|
|
296
317
|
import { promisify as promisify2 } from "util";
|
|
297
318
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
298
319
|
|
|
299
320
|
// src/git.ts
|
|
300
|
-
import
|
|
321
|
+
import fs5 from "fs/promises";
|
|
301
322
|
import path3 from "path";
|
|
302
323
|
import { execFile } from "child_process";
|
|
303
324
|
import { promisify } from "util";
|
|
@@ -378,7 +399,7 @@ async function fileTimestamps(absPath) {
|
|
|
378
399
|
let updated = gitUpdated;
|
|
379
400
|
if (!created || !updated) {
|
|
380
401
|
try {
|
|
381
|
-
const st = await
|
|
402
|
+
const st = await fs5.stat(absPath);
|
|
382
403
|
if (!updated) updated = st.mtime.toISOString();
|
|
383
404
|
if (!created) created = isEpochish(st.birthtime) ? st.mtime.toISOString() : st.birthtime.toISOString();
|
|
384
405
|
} catch {
|
|
@@ -402,7 +423,7 @@ async function detectRepoName(cwd) {
|
|
|
402
423
|
}
|
|
403
424
|
async function readRepoConfig(cwd) {
|
|
404
425
|
try {
|
|
405
|
-
const raw = await
|
|
426
|
+
const raw = await fs6.readFile(path4.join(cwd, REPO_CONFIG_FILE), "utf8");
|
|
406
427
|
const parsed = parseYaml(raw) ?? {};
|
|
407
428
|
if (!parsed.path) return void 0;
|
|
408
429
|
return {
|
|
@@ -432,9 +453,9 @@ async function initRepoWiki(opts) {
|
|
|
432
453
|
n += 1;
|
|
433
454
|
}
|
|
434
455
|
}
|
|
435
|
-
await
|
|
456
|
+
await fs6.mkdir(absWiki, { recursive: true });
|
|
436
457
|
const config = { collection: name, path: wikiPath };
|
|
437
|
-
await
|
|
458
|
+
await fs6.writeFile(path4.join(cwd, REPO_CONFIG_FILE), stringifyYaml(config), "utf8");
|
|
438
459
|
const entry = {
|
|
439
460
|
name,
|
|
440
461
|
type: "repo",
|
|
@@ -503,7 +524,7 @@ async function collectionGitStatus(entry) {
|
|
|
503
524
|
}
|
|
504
525
|
|
|
505
526
|
// src/project.ts
|
|
506
|
-
import
|
|
527
|
+
import fs7 from "fs/promises";
|
|
507
528
|
import os from "os";
|
|
508
529
|
import path5 from "path";
|
|
509
530
|
import { parse as parseYaml2, parseDocument } from "yaml";
|
|
@@ -560,7 +581,7 @@ function normaliseContext(raw) {
|
|
|
560
581
|
async function readProjectConfig(dir) {
|
|
561
582
|
let raw;
|
|
562
583
|
try {
|
|
563
|
-
raw = await
|
|
584
|
+
raw = await fs7.readFile(path5.join(dir, PROJECT_CONFIG_FILE), "utf8");
|
|
564
585
|
} catch (err) {
|
|
565
586
|
if (err.code === "ENOENT") return void 0;
|
|
566
587
|
throw err;
|
|
@@ -595,13 +616,13 @@ async function setContextCollections(dir, collections) {
|
|
|
595
616
|
const file = path5.join(dir, PROJECT_CONFIG_FILE);
|
|
596
617
|
let raw = "";
|
|
597
618
|
try {
|
|
598
|
-
raw = await
|
|
619
|
+
raw = await fs7.readFile(file, "utf8");
|
|
599
620
|
} catch (err) {
|
|
600
621
|
if (err.code !== "ENOENT") throw err;
|
|
601
622
|
}
|
|
602
623
|
const doc = parseDocument(raw);
|
|
603
624
|
doc.setIn(["context", "collections"], collections);
|
|
604
|
-
await
|
|
625
|
+
await fs7.writeFile(file, doc.toString(), "utf8");
|
|
605
626
|
return file;
|
|
606
627
|
}
|
|
607
628
|
async function resolveProjectCollections(dir) {
|
|
@@ -624,8 +645,8 @@ async function seedProjectHome(dir) {
|
|
|
624
645
|
if (entries.length === 0) {
|
|
625
646
|
throw new Error(`No collections to render from diffwiki.yaml in ${path5.resolve(dir)}`);
|
|
626
647
|
}
|
|
627
|
-
const home = await
|
|
628
|
-
await
|
|
648
|
+
const home = await fs7.mkdtemp(path5.join(os.tmpdir(), "diffwiki-project-"));
|
|
649
|
+
await fs7.writeFile(
|
|
629
650
|
path5.join(home, "registry.json"),
|
|
630
651
|
`${JSON.stringify({ version: 1, collections: entries }, null, 2)}
|
|
631
652
|
`,
|
|
@@ -635,7 +656,7 @@ async function seedProjectHome(dir) {
|
|
|
635
656
|
}
|
|
636
657
|
|
|
637
658
|
// src/browse.ts
|
|
638
|
-
import
|
|
659
|
+
import fs8 from "fs/promises";
|
|
639
660
|
import path6 from "path";
|
|
640
661
|
var INDEX_RE = /^(index|readme)\.mdx?$/i;
|
|
641
662
|
function buildArticleTree(relPaths) {
|
|
@@ -680,7 +701,7 @@ function buildArticleTree(relPaths) {
|
|
|
680
701
|
async function collectRelPaths(dir, rel = "") {
|
|
681
702
|
let dirents;
|
|
682
703
|
try {
|
|
683
|
-
dirents = await
|
|
704
|
+
dirents = await fs8.readdir(dir, { withFileTypes: true });
|
|
684
705
|
} catch {
|
|
685
706
|
return [];
|
|
686
707
|
}
|
|
@@ -698,7 +719,7 @@ async function collectRelPaths(dir, rel = "") {
|
|
|
698
719
|
}
|
|
699
720
|
async function readdirSafe(dir) {
|
|
700
721
|
try {
|
|
701
|
-
return await
|
|
722
|
+
return await fs8.readdir(dir);
|
|
702
723
|
} catch {
|
|
703
724
|
return [];
|
|
704
725
|
}
|
|
@@ -716,7 +737,7 @@ var CHEAP_META_BYTES = 8192;
|
|
|
716
737
|
async function cheapMeta(absPath) {
|
|
717
738
|
let handle;
|
|
718
739
|
try {
|
|
719
|
-
handle = await
|
|
740
|
+
handle = await fs8.open(absPath, "r");
|
|
720
741
|
const buf = Buffer.alloc(CHEAP_META_BYTES);
|
|
721
742
|
const { bytesRead } = await handle.read(buf, 0, CHEAP_META_BYTES, 0);
|
|
722
743
|
const snippet = buf.subarray(0, bytesRead).toString("utf8");
|
|
@@ -807,7 +828,7 @@ async function readArticle(coll, slug) {
|
|
|
807
828
|
const candidate = path6.resolve(collDir, rel);
|
|
808
829
|
if (!inJail(candidate)) throw new InvalidTargetError(slug, "path traversal detected");
|
|
809
830
|
try {
|
|
810
|
-
await
|
|
831
|
+
await fs8.access(candidate);
|
|
811
832
|
abs = candidate;
|
|
812
833
|
break;
|
|
813
834
|
} catch {
|
|
@@ -824,7 +845,7 @@ async function readArticle(coll, slug) {
|
|
|
824
845
|
}
|
|
825
846
|
}
|
|
826
847
|
if (!abs) throw new ArticleNotFoundError(`${coll}/${safeSlug}`);
|
|
827
|
-
const parsed = parseArticle(await
|
|
848
|
+
const parsed = parseArticle(await fs8.readFile(abs, "utf8"));
|
|
828
849
|
const title = parsed.title?.trim() || (safeSlug ? safeSlug.split("/").pop() ?? safeSlug : coll);
|
|
829
850
|
return {
|
|
830
851
|
coll,
|
|
@@ -1268,13 +1289,13 @@ function resolveCollectionSelection(all, sel = {}) {
|
|
|
1268
1289
|
}
|
|
1269
1290
|
|
|
1270
1291
|
// src/query.ts
|
|
1271
|
-
import
|
|
1292
|
+
import fs9 from "fs/promises";
|
|
1272
1293
|
import path8 from "path";
|
|
1273
1294
|
var log2 = getLogger("query");
|
|
1274
1295
|
async function collectMarkdownFiles(dir) {
|
|
1275
1296
|
let entries;
|
|
1276
1297
|
try {
|
|
1277
|
-
entries = await
|
|
1298
|
+
entries = await fs9.readdir(dir, { withFileTypes: true });
|
|
1278
1299
|
} catch {
|
|
1279
1300
|
return [];
|
|
1280
1301
|
}
|
|
@@ -1312,7 +1333,7 @@ async function nativeSearch(term, collections, collection, hideSystem = false) {
|
|
|
1312
1333
|
let tags = [];
|
|
1313
1334
|
let body = "";
|
|
1314
1335
|
try {
|
|
1315
|
-
const parsed = parseArticle(await
|
|
1336
|
+
const parsed = parseArticle(await fs9.readFile(full, "utf8"));
|
|
1316
1337
|
if (hideSystem && parsed.audience === "system") continue;
|
|
1317
1338
|
if (parsed.title) title = parsed.title;
|
|
1318
1339
|
tags = parsed.tags;
|
|
@@ -1370,7 +1391,7 @@ async function dropSystemHits(hits) {
|
|
|
1370
1391
|
const kept = [];
|
|
1371
1392
|
for (const hit of hits) {
|
|
1372
1393
|
try {
|
|
1373
|
-
if (parseArticle(await
|
|
1394
|
+
if (parseArticle(await fs9.readFile(hit.path, "utf8")).audience === "system") continue;
|
|
1374
1395
|
} catch {
|
|
1375
1396
|
}
|
|
1376
1397
|
kept.push(hit);
|
|
@@ -1418,13 +1439,13 @@ async function query(term, opts) {
|
|
|
1418
1439
|
}
|
|
1419
1440
|
|
|
1420
1441
|
// src/search.ts
|
|
1421
|
-
import
|
|
1442
|
+
import fs10 from "fs/promises";
|
|
1422
1443
|
import path9 from "path";
|
|
1423
1444
|
var isStatic2 = () => process.env.DIFFWIKI_STATIC === "1";
|
|
1424
1445
|
async function readArticleParts(collDir, slug) {
|
|
1425
1446
|
for (const ext of [".mdx", ".md"]) {
|
|
1426
1447
|
try {
|
|
1427
|
-
const parsed = parseArticle(await
|
|
1448
|
+
const parsed = parseArticle(await fs10.readFile(path9.join(collDir, `${slug}${ext}`), "utf8"));
|
|
1428
1449
|
return { tags: parsed.tags, body: parsed.body };
|
|
1429
1450
|
} catch {
|
|
1430
1451
|
}
|
|
@@ -1474,15 +1495,15 @@ async function buildSearchIndex(collection) {
|
|
|
1474
1495
|
}
|
|
1475
1496
|
|
|
1476
1497
|
// src/doctor.ts
|
|
1477
|
-
import
|
|
1498
|
+
import fs12 from "fs/promises";
|
|
1478
1499
|
|
|
1479
1500
|
// src/plugins/manager.ts
|
|
1480
|
-
import
|
|
1501
|
+
import fs11 from "fs/promises";
|
|
1481
1502
|
import os2 from "os";
|
|
1482
1503
|
import path10 from "path";
|
|
1483
1504
|
async function readPackageJson(file) {
|
|
1484
1505
|
try {
|
|
1485
|
-
return JSON.parse(await
|
|
1506
|
+
return JSON.parse(await fs11.readFile(file, "utf8"));
|
|
1486
1507
|
} catch {
|
|
1487
1508
|
return null;
|
|
1488
1509
|
}
|
|
@@ -1498,7 +1519,7 @@ function binOf(manifest) {
|
|
|
1498
1519
|
async function pluginManifestDirs(nodeModules) {
|
|
1499
1520
|
let entries;
|
|
1500
1521
|
try {
|
|
1501
|
-
entries = await
|
|
1522
|
+
entries = await fs11.readdir(nodeModules);
|
|
1502
1523
|
} catch {
|
|
1503
1524
|
return [];
|
|
1504
1525
|
}
|
|
@@ -1508,7 +1529,7 @@ async function pluginManifestDirs(nodeModules) {
|
|
|
1508
1529
|
if (entry.startsWith("@")) {
|
|
1509
1530
|
let scoped;
|
|
1510
1531
|
try {
|
|
1511
|
-
scoped = await
|
|
1532
|
+
scoped = await fs11.readdir(path10.join(nodeModules, entry));
|
|
1512
1533
|
} catch {
|
|
1513
1534
|
continue;
|
|
1514
1535
|
}
|
|
@@ -1524,11 +1545,11 @@ async function pluginManifestDirs(nodeModules) {
|
|
|
1524
1545
|
}
|
|
1525
1546
|
async function ensurePluginRoot() {
|
|
1526
1547
|
const dir = pluginsDir();
|
|
1527
|
-
await
|
|
1548
|
+
await fs11.mkdir(dir, { recursive: true });
|
|
1528
1549
|
const pkgPath = path10.join(dir, "package.json");
|
|
1529
1550
|
if (await readPackageJson(pkgPath) === null) {
|
|
1530
1551
|
const pkg = { name: "diffwiki-plugins", private: true, version: "0.0.0" };
|
|
1531
|
-
await
|
|
1552
|
+
await fs11.writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
1532
1553
|
`, "utf8");
|
|
1533
1554
|
}
|
|
1534
1555
|
return dir;
|
|
@@ -1697,7 +1718,7 @@ async function listPlugins() {
|
|
|
1697
1718
|
// src/doctor.ts
|
|
1698
1719
|
async function exists(target) {
|
|
1699
1720
|
try {
|
|
1700
|
-
await
|
|
1721
|
+
await fs12.access(target);
|
|
1701
1722
|
return true;
|
|
1702
1723
|
} catch {
|
|
1703
1724
|
return false;
|
|
@@ -1803,7 +1824,7 @@ async function appendPluginDiagnostics(out, config) {
|
|
|
1803
1824
|
}
|
|
1804
1825
|
|
|
1805
1826
|
// src/lint.ts
|
|
1806
|
-
import
|
|
1827
|
+
import fs13 from "fs/promises";
|
|
1807
1828
|
import path11 from "path";
|
|
1808
1829
|
var BUDGETS = {
|
|
1809
1830
|
recipe: { min: 50, max: 1e3 },
|
|
@@ -1896,7 +1917,7 @@ function lintArticle(article) {
|
|
|
1896
1917
|
async function walkMarkdown(dir) {
|
|
1897
1918
|
let entries;
|
|
1898
1919
|
try {
|
|
1899
|
-
entries = await
|
|
1920
|
+
entries = await fs13.readdir(dir, { withFileTypes: true });
|
|
1900
1921
|
} catch {
|
|
1901
1922
|
return [];
|
|
1902
1923
|
}
|
|
@@ -1936,7 +1957,7 @@ async function lintCollections(scope) {
|
|
|
1936
1957
|
const label = `${coll.name}/${rel}`;
|
|
1937
1958
|
let findings = [];
|
|
1938
1959
|
try {
|
|
1939
|
-
const parsed = parseArticle(await
|
|
1960
|
+
const parsed = parseArticle(await fs13.readFile(full, "utf8"));
|
|
1940
1961
|
findings = lintArticle(parsed);
|
|
1941
1962
|
} catch (err) {
|
|
1942
1963
|
findings = [{ level: "error", rule: "parse", message: `could not parse: ${err.message}` }];
|
|
@@ -2028,6 +2049,7 @@ export {
|
|
|
2028
2049
|
doctor,
|
|
2029
2050
|
emitPluginEvent,
|
|
2030
2051
|
ensureGitRepo,
|
|
2052
|
+
ensureHome,
|
|
2031
2053
|
ensurePluginRoot,
|
|
2032
2054
|
fileTimestamps,
|
|
2033
2055
|
findCollection,
|