diffwiki 0.16.0 → 0.17.0-rc.202608201146.531910e
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/bin.js +62 -55
- package/package.json +2 -2
- package/templates/collection-index-template.md +27 -8
package/dist/bin.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/bin.ts
|
|
4
|
+
import { ensureHome } from "diffwiki-core";
|
|
5
|
+
|
|
3
6
|
// src/cli.ts
|
|
4
7
|
import { spawn as spawn4 } from "child_process";
|
|
5
|
-
import
|
|
6
|
-
import { fileURLToPath as
|
|
8
|
+
import path5 from "path";
|
|
9
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7
10
|
import { Command, Option } from "commander";
|
|
8
11
|
import {
|
|
9
12
|
addExternalCollection,
|
|
@@ -72,12 +75,40 @@ var splitTags = (raw) => raw.flatMap((t) => t.split(",")).map((t) => t.trim()).f
|
|
|
72
75
|
import { spawn } from "child_process";
|
|
73
76
|
import { platform } from "os";
|
|
74
77
|
import fs from "fs/promises";
|
|
75
|
-
import
|
|
78
|
+
import path2 from "path";
|
|
76
79
|
import http from "http";
|
|
77
80
|
import { resolveHome } from "diffwiki-core";
|
|
81
|
+
|
|
82
|
+
// src/app.ts
|
|
83
|
+
import { existsSync } from "fs";
|
|
84
|
+
import path from "path";
|
|
85
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
86
|
+
var APP_SPEC = `diffwiki-app@${version}`;
|
|
87
|
+
function workspaceAppBin(localBin) {
|
|
88
|
+
const candidates = [
|
|
89
|
+
path.resolve(path.dirname(fileURLToPath2(import.meta.url)), "../../../apps/wiki/bin", localBin),
|
|
90
|
+
path.resolve(process.cwd(), "apps/wiki/bin", localBin)
|
|
91
|
+
];
|
|
92
|
+
return candidates.find((p) => existsSync(p));
|
|
93
|
+
}
|
|
94
|
+
function resolveAppLaunch(opts = {}) {
|
|
95
|
+
const localBin = opts.localBin ?? "wiki-server.mjs";
|
|
96
|
+
const override = process.env.DIFFWIKI_APP_CMD;
|
|
97
|
+
if (override) {
|
|
98
|
+
const [cmd, ...args] = override.split(" ");
|
|
99
|
+
return { cmd, args, source: "override", spec: override };
|
|
100
|
+
}
|
|
101
|
+
const spec = process.env.DIFFWIKI_APP_SPEC;
|
|
102
|
+
if (spec) return { cmd: "npx", args: ["-y", spec], source: "spec", spec };
|
|
103
|
+
const local = workspaceAppBin(localBin);
|
|
104
|
+
if (local) return { cmd: "node", args: [local], source: "local", spec: local };
|
|
105
|
+
return { cmd: "npx", args: ["-y", APP_SPEC], source: "npx", spec: APP_SPEC };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/ui-server.ts
|
|
78
109
|
var DEFAULT_PORT = 4321;
|
|
79
110
|
function statePath() {
|
|
80
|
-
return
|
|
111
|
+
return path2.join(resolveHome(), "ui.json");
|
|
81
112
|
}
|
|
82
113
|
async function readState() {
|
|
83
114
|
try {
|
|
@@ -133,7 +164,8 @@ function waitForServer(url, timeoutMs = 3e4) {
|
|
|
133
164
|
});
|
|
134
165
|
}
|
|
135
166
|
async function startUi(port = DEFAULT_PORT) {
|
|
136
|
-
const
|
|
167
|
+
const { cmd, args } = resolveAppLaunch();
|
|
168
|
+
const child = spawn(cmd, [...args, "--port", String(port)], {
|
|
137
169
|
env: { ...process.env, PORT: String(port), HOST: "127.0.0.1" },
|
|
138
170
|
detached: true,
|
|
139
171
|
stdio: "ignore"
|
|
@@ -168,20 +200,11 @@ async function stopUi() {
|
|
|
168
200
|
|
|
169
201
|
// src/exporter.ts
|
|
170
202
|
import { spawn as spawn2 } from "child_process";
|
|
171
|
-
import
|
|
172
|
-
import path2 from "path";
|
|
173
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
203
|
+
import path3 from "path";
|
|
174
204
|
import { listCollections, resolveCollectionSelection, InvalidExportSelectionError } from "diffwiki-core";
|
|
175
205
|
var splitGlobs = (v) => (v ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
176
|
-
function localAppExporter() {
|
|
177
|
-
const candidates = [
|
|
178
|
-
path2.resolve(path2.dirname(fileURLToPath2(import.meta.url)), "../../../apps/wiki/bin/export.mjs"),
|
|
179
|
-
path2.resolve(process.cwd(), "apps/wiki/bin/export.mjs")
|
|
180
|
-
];
|
|
181
|
-
return candidates.find((p) => fs2.existsSync(p));
|
|
182
|
-
}
|
|
183
206
|
async function runExport(opts) {
|
|
184
|
-
const out =
|
|
207
|
+
const out = path3.resolve(opts.out);
|
|
185
208
|
const filter = splitGlobs(opts.filter);
|
|
186
209
|
const exclude = splitGlobs(opts.exclude);
|
|
187
210
|
if (!opts.project && (filter.length > 0 || exclude.length > 0)) {
|
|
@@ -189,19 +212,17 @@ async function runExport(opts) {
|
|
|
189
212
|
const { errors } = resolveCollectionSelection(names, { filter, exclude });
|
|
190
213
|
if (errors.length > 0) throw new InvalidExportSelectionError(errors);
|
|
191
214
|
}
|
|
192
|
-
const
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (opts.filter) args.push("--filter", opts.filter);
|
|
197
|
-
if (opts.exclude) args.push("--exclude", opts.exclude);
|
|
215
|
+
const { cmd, args, source, spec } = resolveAppLaunch({ localBin: "export.mjs" });
|
|
216
|
+
const appArgs = [...args, "export", "--out", out, "--base", opts.base];
|
|
217
|
+
if (opts.filter) appArgs.push("--filter", opts.filter);
|
|
218
|
+
if (opts.exclude) appArgs.push("--exclude", opts.exclude);
|
|
198
219
|
if (opts.project) {
|
|
199
|
-
|
|
200
|
-
if (typeof opts.project === "string")
|
|
220
|
+
appArgs.push("--project");
|
|
221
|
+
if (typeof opts.project === "string") appArgs.push(opts.project);
|
|
201
222
|
}
|
|
202
|
-
if (override) console.log(`Exporting via ${
|
|
223
|
+
if (source === "override") console.log(`Exporting via ${spec}`);
|
|
203
224
|
await new Promise((resolve, reject) => {
|
|
204
|
-
const child = spawn2(cmd,
|
|
225
|
+
const child = spawn2(cmd, appArgs, { stdio: "inherit", env: process.env });
|
|
205
226
|
child.on("error", reject);
|
|
206
227
|
child.on("close", (code) => code === 0 ? resolve() : reject(new Error(`export failed (exit ${code})`)));
|
|
207
228
|
});
|
|
@@ -209,12 +230,10 @@ async function runExport(opts) {
|
|
|
209
230
|
}
|
|
210
231
|
|
|
211
232
|
// src/preview.ts
|
|
212
|
-
import
|
|
213
|
-
import fs3 from "fs/promises";
|
|
233
|
+
import fs2 from "fs/promises";
|
|
214
234
|
import net from "net";
|
|
215
|
-
import
|
|
235
|
+
import path4 from "path";
|
|
216
236
|
import { spawn as spawn3 } from "child_process";
|
|
217
|
-
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
218
237
|
import { seedProjectHome } from "diffwiki-core";
|
|
219
238
|
async function findFreePort(start, host = "127.0.0.1") {
|
|
220
239
|
const isFree = (port) => new Promise((resolve) => {
|
|
@@ -228,29 +247,16 @@ async function findFreePort(start, host = "127.0.0.1") {
|
|
|
228
247
|
}
|
|
229
248
|
throw new Error(`No free port available in ${start}..${start + 99}`);
|
|
230
249
|
}
|
|
231
|
-
function launchCommand() {
|
|
232
|
-
const override = process.env.DIFFWIKI_APP_CMD;
|
|
233
|
-
if (override) {
|
|
234
|
-
const [cmd, ...prefix] = override.split(" ");
|
|
235
|
-
return { cmd, prefix };
|
|
236
|
-
}
|
|
237
|
-
const candidates = [
|
|
238
|
-
path3.resolve(path3.dirname(fileURLToPath3(import.meta.url)), "../../../apps/wiki/bin/wiki-server.mjs"),
|
|
239
|
-
path3.resolve(process.cwd(), "apps/wiki/bin/wiki-server.mjs")
|
|
240
|
-
];
|
|
241
|
-
const local = candidates.find((p) => existsSync(p));
|
|
242
|
-
return local ? { cmd: "node", prefix: [local] } : { cmd: "npx", prefix: ["-y", "diffwiki-app@latest"] };
|
|
243
|
-
}
|
|
244
250
|
async function runPreview(opts) {
|
|
245
|
-
const root =
|
|
251
|
+
const root = path4.resolve(opts.cwd);
|
|
246
252
|
const { home, entries } = await seedProjectHome(root);
|
|
247
|
-
const { cmd,
|
|
253
|
+
const { cmd, args } = resolveAppLaunch();
|
|
248
254
|
const port = await findFreePort(opts.port);
|
|
249
255
|
const url = `http://127.0.0.1:${port}`;
|
|
250
|
-
console.log(`Previewing ${entries.length} collection(s) from ${
|
|
256
|
+
console.log(`Previewing ${entries.length} collection(s) from ${path4.join(root, "diffwiki.yaml")}`);
|
|
251
257
|
if (port !== opts.port) console.log(`Port ${opts.port} is in use \u2014 using ${port} instead.`);
|
|
252
258
|
console.log(`Starting UI at ${url} \u2026 (Ctrl-C to stop)`);
|
|
253
|
-
const child = spawn3(cmd, [...
|
|
259
|
+
const child = spawn3(cmd, [...args, "--port", String(port)], {
|
|
254
260
|
env: {
|
|
255
261
|
...process.env,
|
|
256
262
|
DIFFWIKI_HOME: home,
|
|
@@ -289,7 +295,7 @@ async function runPreview(opts) {
|
|
|
289
295
|
const finish = () => {
|
|
290
296
|
process.off("SIGINT", stop);
|
|
291
297
|
process.off("SIGTERM", stop);
|
|
292
|
-
void
|
|
298
|
+
void fs2.rm(home, { recursive: true, force: true });
|
|
293
299
|
resolve();
|
|
294
300
|
};
|
|
295
301
|
child.on("close", finish);
|
|
@@ -493,7 +499,7 @@ async function doctorAction(opts) {
|
|
|
493
499
|
}
|
|
494
500
|
}
|
|
495
501
|
function bundledTemplatesDir() {
|
|
496
|
-
return
|
|
502
|
+
return path5.resolve(path5.dirname(fileURLToPath3(import.meta.url)), "..", "templates");
|
|
497
503
|
}
|
|
498
504
|
async function templatesInitAction() {
|
|
499
505
|
const { seeded, skipped, dir } = await seedTemplates(bundledTemplatesDir());
|
|
@@ -686,7 +692,7 @@ function createProgram() {
|
|
|
686
692
|
);
|
|
687
693
|
site.command("preview").description("Preview the site from the repo diffwiki.yaml (ignores global collections)").option("-p, --port <port>", "port to serve on", "4321").action(withErrors((opts) => previewAction(opts)));
|
|
688
694
|
const ui = program.command("ui").description("Run the local diffwiki app server");
|
|
689
|
-
ui.command("up").description("Start the diffwiki UI (
|
|
695
|
+
ui.command("up").description("Start the diffwiki UI (runs the matching published diffwiki-app)").option("-p, --port <port>", "port to serve on", "4321").action(withErrors((opts) => upAction(opts)));
|
|
690
696
|
ui.command("down").description("Stop the running diffwiki UI").action(withErrors(downAction));
|
|
691
697
|
ui.command("status").description("Show whether the diffwiki UI is running").action(withErrors(statusAction));
|
|
692
698
|
ui.command("open").description("Open the diffwiki UI in the browser (starting it if needed)").option("-p, --port <port>", "port to serve on", "4321").action(withErrors((opts) => openAction(opts)));
|
|
@@ -709,14 +715,14 @@ function createProgram() {
|
|
|
709
715
|
// src/logging.ts
|
|
710
716
|
import { mkdirSync } from "fs";
|
|
711
717
|
import os from "os";
|
|
712
|
-
import
|
|
718
|
+
import path6 from "path";
|
|
713
719
|
import { configureSync, getConfig } from "@logtape/logtape";
|
|
714
720
|
import { getRotatingFileSink } from "@logtape/file";
|
|
715
721
|
import { resolveLogLevel } from "diffwiki-core";
|
|
716
722
|
var configured = false;
|
|
717
723
|
function diffwikiHome() {
|
|
718
724
|
const override = process.env.DIFFWIKI_HOME;
|
|
719
|
-
return override && override.length > 0 ? override :
|
|
725
|
+
return override && override.length > 0 ? override : path6.join(os.homedir(), ".diffwiki");
|
|
720
726
|
}
|
|
721
727
|
function resolveCliLevel(argv) {
|
|
722
728
|
if (argv.includes("-v") || argv.includes("--verbose")) return "debug";
|
|
@@ -733,9 +739,9 @@ function configureCliLogging(argv = process.argv) {
|
|
|
733
739
|
if (configured || getConfig() != null) return;
|
|
734
740
|
configured = true;
|
|
735
741
|
const level = resolveCliLevel(argv);
|
|
736
|
-
const logsDir =
|
|
742
|
+
const logsDir = path6.join(diffwikiHome(), "logs");
|
|
737
743
|
mkdirSync(logsDir, { recursive: true });
|
|
738
|
-
const logFile =
|
|
744
|
+
const logFile = path6.join(logsDir, "diffwiki.log");
|
|
739
745
|
const stderrSink = (record) => {
|
|
740
746
|
process.stderr.write(formatLine(record) + "\n");
|
|
741
747
|
};
|
|
@@ -754,4 +760,5 @@ function configureCliLogging(argv = process.argv) {
|
|
|
754
760
|
|
|
755
761
|
// src/bin.ts
|
|
756
762
|
configureCliLogging(process.argv);
|
|
757
|
-
|
|
763
|
+
await ensureHome();
|
|
764
|
+
await createProgram().parseAsync(process.argv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "diffwiki",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0-rc.202608201146.531910e",
|
|
4
4
|
"description": "diffwiki command-line interface",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@logtape/logtape": "^2.2.4",
|
|
19
19
|
"@logtape/file": "^2.2.4",
|
|
20
20
|
"commander": "^13.0.0",
|
|
21
|
-
"diffwiki-core": "0.
|
|
21
|
+
"diffwiki-core": "0.17.0-rc.202608201146.531910e"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^22.0.0"
|
|
@@ -7,11 +7,15 @@ budget: [100, 500]
|
|
|
7
7
|
<!--
|
|
8
8
|
Use this shape for a collection's root index file. An index groups articles into topics, provides a one-line description for each, and links to them. It does not teach, demonstrate, or narrate.
|
|
9
9
|
|
|
10
|
+
Pick an organisation pattern:
|
|
11
|
+
- Subtopic headers — use when the collection spans multiple distinct subjects. One H2 per subject.
|
|
12
|
+
- Shape-based sections — use when the collection covers a single topic but mixes article types. Use H2s: Guides, Recipes, References (omit any that are empty).
|
|
13
|
+
|
|
10
14
|
Authoring rules:
|
|
11
15
|
- Adhere to the template structure
|
|
12
16
|
- One H2 per topic group; keep the group label short (2–4 words)
|
|
13
17
|
- One bullet per article: [Title](./slug.md) — one-line description
|
|
14
|
-
- No prose paragraphs, no examples, no code blocks
|
|
18
|
+
- No prose paragraphs, no examples, no code blocks
|
|
15
19
|
- Keep descriptions to a single clause — enough to distinguish siblings
|
|
16
20
|
- Do not copy over html comments or the template specific yaml frontmatter above this section. Write a diffwiki article yaml frontmatter as shown below this comment.
|
|
17
21
|
- Use concise descriptive titles in article frontmatter
|
|
@@ -26,18 +30,33 @@ tags:
|
|
|
26
30
|
|
|
27
31
|
- collection-index
|
|
28
32
|
- TAG1
|
|
29
|
-
- TAG2
|
|
30
33
|
audience: public
|
|
31
34
|
status: published
|
|
32
35
|
|
|
33
36
|
---
|
|
34
37
|
|
|
35
|
-
|
|
38
|
+
<!-- Pattern A: subtopic headers — use when the collection spans multiple subjects -->
|
|
39
|
+
|
|
40
|
+
## <Subject Area>
|
|
41
|
+
|
|
42
|
+
- [Article Title](./slug.md) — one-line description.
|
|
43
|
+
- [Article Title](./slug.md) — one-line description.
|
|
44
|
+
|
|
45
|
+
## <Second Subject Area>
|
|
46
|
+
|
|
47
|
+
- [Article Title](./slug.md) — one-line description.
|
|
48
|
+
|
|
49
|
+
<!-- Pattern B: shape-based sections — use when the collection is single-topic with mixed article types -->
|
|
50
|
+
|
|
51
|
+
## Guides
|
|
52
|
+
|
|
53
|
+
- [Article Title](./slug.md) — one-line description.
|
|
54
|
+
|
|
55
|
+
## Recipes
|
|
36
56
|
|
|
37
|
-
- [Article Title](./slug.md) — one-line description
|
|
38
|
-
- [Article Title](./slug.md) — one-line description
|
|
57
|
+
- [Article Title](./slug.md) — one-line description.
|
|
58
|
+
- [Article Title](./slug.md) — one-line description.
|
|
39
59
|
|
|
40
|
-
##
|
|
60
|
+
## References
|
|
41
61
|
|
|
42
|
-
- [Article Title](./slug.md) — one-line description
|
|
43
|
-
- [Article Title](./slug.md) — one-line description of what this article covers.
|
|
62
|
+
- [Article Title](./slug.md) — one-line description.
|