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 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 path4 from "path";
6
- import { fileURLToPath as fileURLToPath4 } from "url";
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 path from "path";
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 path.join(resolveHome(), "ui.json");
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 child = spawn("npx", ["-y", "diffwiki-app@latest", "--port", String(port)], {
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 fs2 from "fs";
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 = path2.resolve(opts.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 local = localAppExporter();
193
- const override = process.env.DIFFWIKI_APP_CMD ?? (local ? `node ${local}` : void 0);
194
- const [cmd, ...prefix] = override ? override.split(" ") : ["npx", "-y", "diffwiki-app@latest"];
195
- const args = [...prefix, "export", "--out", out, "--base", opts.base];
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
- args.push("--project");
200
- if (typeof opts.project === "string") args.push(opts.project);
220
+ appArgs.push("--project");
221
+ if (typeof opts.project === "string") appArgs.push(opts.project);
201
222
  }
202
- if (override) console.log(`Exporting via ${override}`);
223
+ if (source === "override") console.log(`Exporting via ${spec}`);
203
224
  await new Promise((resolve, reject) => {
204
- const child = spawn2(cmd, args, { stdio: "inherit", env: process.env });
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 { existsSync } from "fs";
213
- import fs3 from "fs/promises";
233
+ import fs2 from "fs/promises";
214
234
  import net from "net";
215
- import path3 from "path";
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 = path3.resolve(opts.cwd);
251
+ const root = path4.resolve(opts.cwd);
246
252
  const { home, entries } = await seedProjectHome(root);
247
- const { cmd, prefix } = launchCommand();
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 ${path3.join(root, "diffwiki.yaml")}`);
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, [...prefix, "--port", String(port)], {
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 fs3.rm(home, { recursive: true, force: true });
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 path4.resolve(path4.dirname(fileURLToPath4(import.meta.url)), "..", "templates");
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 (latest published diffwiki-app)").option("-p, --port <port>", "port to serve on", "4321").action(withErrors((opts) => upAction(opts)));
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 path5 from "path";
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 : path5.join(os.homedir(), ".diffwiki");
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 = path5.join(diffwikiHome(), "logs");
742
+ const logsDir = path6.join(diffwikiHome(), "logs");
737
743
  mkdirSync(logsDir, { recursive: true });
738
- const logFile = path5.join(logsDir, "diffwiki.log");
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
- createProgram().parseAsync(process.argv);
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.16.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.16.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, no References section
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
- ## <Topic Group>
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 of what this article covers.
38
- - [Article Title](./slug.md) — one-line description of what this article covers.
57
+ - [Article Title](./slug.md) — one-line description.
58
+ - [Article Title](./slug.md) — one-line description.
39
59
 
40
- ## <Second Topic Group>
60
+ ## References
41
61
 
42
- - [Article Title](./slug.md) — one-line description of what this article covers.
43
- - [Article Title](./slug.md) — one-line description of what this article covers.
62
+ - [Article Title](./slug.md) — one-line description.