create-thally-docs 0.10.78 → 0.10.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/dist/{chunk-VGGE6JIL.js → chunk-DIX4EUI4.js} +33 -354
- package/dist/chunk-IZQHRVPV.js +324 -0
- package/dist/chunk-J7255UQA.js +34 -0
- package/dist/chunk-KDLP27DS.js +87 -0
- package/dist/chunk-KGKSB3PU.js +222 -0
- package/dist/chunk-L2YCVQBR.js +249 -0
- package/dist/{chunk-W5WQ2V7P.js → chunk-WWIATVAD.js} +14 -4
- package/dist/index.js +112 -47
- package/dist/migrate/index.js +6 -3
- package/dist/migration-worker.d.ts +2 -0
- package/dist/migration-worker.js +37 -0
- package/dist/scaffold.d.ts +6 -0
- package/dist/scaffold.js +4 -2
- package/dist/starter-update.js +2 -1
- package/dist/terminal.d.ts +75 -0
- package/dist/terminal.js +17 -0
- package/package.json +7 -2
- package/dist/chunk-QLMRRNLL.js +0 -162
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/terminal.ts
|
|
4
|
+
import { intro, outro, spinner } from "@clack/prompts";
|
|
5
|
+
import { stripVTControlCharacters } from "util";
|
|
6
|
+
var PRODUCT_TAGLINE = "Product knowledge, kept in step with your code.";
|
|
7
|
+
var OLIVE = { rgb: "38;2;174;187;106", palette: "38;5;143" };
|
|
8
|
+
var OLIVE_EDGE = { rgb: "38;2;115;121;56", palette: "38;5;101" };
|
|
9
|
+
function oliveCode(env, edge = false) {
|
|
10
|
+
const color = edge ? OLIVE_EDGE : OLIVE;
|
|
11
|
+
return env.COLORTERM === "truecolor" || env.COLORTERM === "24bit" || ["iTerm.app", "vscode", "WezTerm"].includes(env.TERM_PROGRAM ?? "") ? color.rgb : color.palette;
|
|
12
|
+
}
|
|
13
|
+
var accentUsers = 0;
|
|
14
|
+
var restoreAccent;
|
|
15
|
+
function acquireTerminalAccent() {
|
|
16
|
+
if (!terminal.isRich) return () => {
|
|
17
|
+
};
|
|
18
|
+
if (accentUsers === 0) {
|
|
19
|
+
const output = process.stdout;
|
|
20
|
+
const previous = output.write;
|
|
21
|
+
const writeOriginal = previous.bind(output);
|
|
22
|
+
const opening = `\x1B[${oliveCode(process.env)}m`;
|
|
23
|
+
const themed = function(chunk, encodingOrCallback, callback) {
|
|
24
|
+
const text = typeof chunk === "string" ? chunk.replace(/\x1b\[(?:32|35|36)m/g, opening) : chunk;
|
|
25
|
+
return typeof encodingOrCallback === "string" ? writeOriginal(text, encodingOrCallback, callback) : writeOriginal(text, encodingOrCallback);
|
|
26
|
+
};
|
|
27
|
+
output.write = themed;
|
|
28
|
+
restoreAccent = () => {
|
|
29
|
+
if (output.write === themed) output.write = previous;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
accentUsers += 1;
|
|
33
|
+
let released = false;
|
|
34
|
+
return () => {
|
|
35
|
+
if (released) return;
|
|
36
|
+
released = true;
|
|
37
|
+
accentUsers -= 1;
|
|
38
|
+
if (accentUsers === 0) {
|
|
39
|
+
restoreAccent?.();
|
|
40
|
+
restoreAccent = void 0;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
var WORDMARK = [
|
|
45
|
+
"\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557",
|
|
46
|
+
"\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D",
|
|
47
|
+
" \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2554\u255D ",
|
|
48
|
+
" \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2554\u255D ",
|
|
49
|
+
" \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 ",
|
|
50
|
+
" \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D "
|
|
51
|
+
];
|
|
52
|
+
var SMALL_WORDMARK = [
|
|
53
|
+
"\u2580\u2588\u2580 \u2588 \u2588 \u2584\u2580\u2588 \u2588 \u2588 \u2588 \u2588",
|
|
54
|
+
" \u2588 \u2588\u2580\u2588 \u2588\u2580\u2588 \u2588 \u2588 \u2588 ",
|
|
55
|
+
" \u2580 \u2580 \u2580 \u2580 \u2580 \u2580\u2580\u2580 \u2580\u2580\u2580 \u2580 "
|
|
56
|
+
];
|
|
57
|
+
function terminalText(value) {
|
|
58
|
+
return stripVTControlCharacters(value).replace(/[\u0000-\u0009\u000b-\u001f\u007f-\u009f]/g, "");
|
|
59
|
+
}
|
|
60
|
+
function createTerminal(options = {}) {
|
|
61
|
+
const output = options.stdout ?? process.stdout;
|
|
62
|
+
const errors = options.stderr ?? process.stderr;
|
|
63
|
+
const environment = () => options.env ?? process.env;
|
|
64
|
+
const arguments_ = () => options.argv ?? process.argv.slice(2);
|
|
65
|
+
const isRich = () => Boolean(output.isTTY) && !environment().CI && environment().TERM !== "dumb" && environment().NO_COLOR === void 0;
|
|
66
|
+
const canAnimate = () => isRich() && Boolean(process.stdin.isTTY) && output === process.stdout && errors === process.stderr && !arguments_().includes("--verbose");
|
|
67
|
+
const paint = (text, code, rich = isRich()) => rich ? `\x1B[${code}m${text}\x1B[0m` : text;
|
|
68
|
+
const accent = (text, edge = false) => paint(text, oliveCode(environment(), edge));
|
|
69
|
+
const write = (text) => output.write(`${text}
|
|
70
|
+
`);
|
|
71
|
+
const wordmark = () => {
|
|
72
|
+
const columns = output.columns ?? 80;
|
|
73
|
+
const lines = columns >= Math.max(...WORDMARK.map((line) => line.length)) + 2 ? WORDMARK : SMALL_WORDMARK;
|
|
74
|
+
if (!isRich() || columns < Math.max(...lines.map((line) => line.length)) + 2) {
|
|
75
|
+
write(` ${accent("THALLY")}`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const lettering = lines.map((line) => line.split(/(█+)/).map((part) => accent(part, !part.includes("\u2588"))).join(""));
|
|
79
|
+
for (const line of lettering) write(` ${line}`);
|
|
80
|
+
};
|
|
81
|
+
const diagnostic = (message, isError) => {
|
|
82
|
+
const rich = isRich() && Boolean(errors.isTTY);
|
|
83
|
+
const symbol = isError ? rich ? "\u25A0" : "[error]" : rich ? "\u25B2" : "[warn]";
|
|
84
|
+
const line = ` ${paint(symbol, isError ? 31 : 33, rich)} ${terminalText(message)}`;
|
|
85
|
+
if (errors === process.stderr) console.warn(line);
|
|
86
|
+
else errors.write(`${line}
|
|
87
|
+
`);
|
|
88
|
+
};
|
|
89
|
+
const render = {
|
|
90
|
+
/** Whether interactive decoration is supported by the current output. */
|
|
91
|
+
get isRich() {
|
|
92
|
+
return isRich();
|
|
93
|
+
},
|
|
94
|
+
/** Whether the caller requested detailed subprocess output. */
|
|
95
|
+
get isVerbose() {
|
|
96
|
+
return arguments_().includes("--verbose");
|
|
97
|
+
},
|
|
98
|
+
/** Introduce a command with the responsive wordmark and its immediate task. */
|
|
99
|
+
intro(command, version, subtitle = PRODUCT_TAGLINE) {
|
|
100
|
+
const heading = `${terminalText(command)}${version ? ` ${isRich() ? "\xB7" : "-"} v${terminalText(version)}` : ""}`;
|
|
101
|
+
write("");
|
|
102
|
+
wordmark();
|
|
103
|
+
write("");
|
|
104
|
+
write(` ${PRODUCT_TAGLINE}`);
|
|
105
|
+
if (subtitle !== PRODUCT_TAGLINE) write(` ${paint(terminalText(subtitle), 2)}`);
|
|
106
|
+
write("");
|
|
107
|
+
if (isRich() && output === process.stdout) intro(accent(heading));
|
|
108
|
+
else write(` ${accent(heading)}`);
|
|
109
|
+
write("");
|
|
110
|
+
},
|
|
111
|
+
/** Display a source, destination, or result with a consistent label. */
|
|
112
|
+
detail(label, value) {
|
|
113
|
+
write(` ${paint(terminalText(label).padEnd(10), 2)} ${terminalText(value)}`);
|
|
114
|
+
},
|
|
115
|
+
/** Display a neutral fact without implying success. */
|
|
116
|
+
info(message) {
|
|
117
|
+
write(` ${paint(isRich() ? "\u2502" : "-", 2)} ${terminalText(message)}`);
|
|
118
|
+
},
|
|
119
|
+
/** Record a completed operation in the retained transcript. */
|
|
120
|
+
success(message) {
|
|
121
|
+
write(` ${accent(isRich() ? "\u2713" : "[ok]")} ${terminalText(message)}`);
|
|
122
|
+
},
|
|
123
|
+
/** Keep warnings visible on stderr even when successful task logs are quiet. */
|
|
124
|
+
warn(message) {
|
|
125
|
+
diagnostic(message, false);
|
|
126
|
+
},
|
|
127
|
+
/** Keep errors visible on stderr and separate from machine-readable stdout. */
|
|
128
|
+
error(message) {
|
|
129
|
+
diagnostic(message, true);
|
|
130
|
+
},
|
|
131
|
+
/** Group next steps or results without a fixed-width box. */
|
|
132
|
+
section(title, lines) {
|
|
133
|
+
write("");
|
|
134
|
+
write(` ${paint(terminalText(title), 1)}`);
|
|
135
|
+
for (const line of lines) write(` ${terminalText(line)}`);
|
|
136
|
+
write("");
|
|
137
|
+
},
|
|
138
|
+
/** Make the next action prominent while retaining one intact shell command. */
|
|
139
|
+
nextAction(title, command) {
|
|
140
|
+
const cleanTitle = terminalText(title);
|
|
141
|
+
const cleanCommand = terminalText(command);
|
|
142
|
+
write("");
|
|
143
|
+
if (!isRich()) {
|
|
144
|
+
write(` Next action: ${cleanTitle}`);
|
|
145
|
+
write(" Copy and run this command:");
|
|
146
|
+
write(` ${cleanCommand}`);
|
|
147
|
+
write("");
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const label = `NEXT \u2192 ${cleanTitle}`;
|
|
151
|
+
write(` ${paint(paint(paint(` ${label} `, 1), 30), oliveCode(environment()).replace(/^38;/, "48;"))}`);
|
|
152
|
+
write("");
|
|
153
|
+
write(" Copy and run to open your local preview:");
|
|
154
|
+
const canFrame = !cleanCommand.includes("\n") && cleanCommand.length + 8 <= (output.columns ?? 80);
|
|
155
|
+
if (canFrame) {
|
|
156
|
+
const width = cleanCommand.length + 4;
|
|
157
|
+
write(` ${accent(`\u256D${"\u2500".repeat(width)}\u256E`)}`);
|
|
158
|
+
write(` ${accent("\u2502")}${" ".repeat(width)}${accent("\u2502")}`);
|
|
159
|
+
write(` ${accent("\u2502")} ${accent(paint(cleanCommand, 1))} ${accent("\u2502")}`);
|
|
160
|
+
write(` ${accent("\u2502")}${" ".repeat(width)}${accent("\u2502")}`);
|
|
161
|
+
write(` ${accent(`\u2570${"\u2500".repeat(width)}\u256F`)}`);
|
|
162
|
+
} else write(` ${accent(paint(cleanCommand, 1))}`);
|
|
163
|
+
write("");
|
|
164
|
+
},
|
|
165
|
+
/** Finish a command without masking warnings or incomplete validation. */
|
|
166
|
+
outro(message) {
|
|
167
|
+
if (isRich() && output === process.stdout) outro(terminalText(message));
|
|
168
|
+
else {
|
|
169
|
+
write("");
|
|
170
|
+
write(` ${terminalText(message)}`);
|
|
171
|
+
write("");
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
/** Render progress, retain the outcome, and always release Clack's cursor hooks. */
|
|
175
|
+
async step(label, action, completedLabel = label) {
|
|
176
|
+
const progress = canAnimate() ? spinner({ indicator: "timer" }) : void 0;
|
|
177
|
+
const releaseAccent = progress ? acquireTerminalAccent() : void 0;
|
|
178
|
+
const cleanLabel = terminalText(label).replace(/\n/g, " ");
|
|
179
|
+
const limit = Math.max(8, (output.columns ?? 80) - 16);
|
|
180
|
+
const fit = (message) => Array.from(terminalText(message).replace(/\n/g, " ")).slice(0, limit).join("");
|
|
181
|
+
let activity = cleanLabel;
|
|
182
|
+
const began = Date.now();
|
|
183
|
+
const update = (message) => {
|
|
184
|
+
const next = terminalText(message).replace(/\n/g, " ");
|
|
185
|
+
if (next === activity) return;
|
|
186
|
+
activity = next;
|
|
187
|
+
if (progress) progress.message(fit(next));
|
|
188
|
+
else render.info(next);
|
|
189
|
+
};
|
|
190
|
+
const heartbeat = !progress ? setInterval(() => {
|
|
191
|
+
render.info(`${activity} (${Math.floor((Date.now() - began) / 1e3)}s elapsed)`);
|
|
192
|
+
}, 15e3) : void 0;
|
|
193
|
+
const cancel = () => {
|
|
194
|
+
progress?.stop("Cancelled", 1);
|
|
195
|
+
releaseAccent?.();
|
|
196
|
+
process.exit(130);
|
|
197
|
+
};
|
|
198
|
+
const terminate = () => {
|
|
199
|
+
progress?.stop("Cancelled", 1);
|
|
200
|
+
releaseAccent?.();
|
|
201
|
+
process.exit(143);
|
|
202
|
+
};
|
|
203
|
+
if (progress) {
|
|
204
|
+
process.once("SIGINT", cancel);
|
|
205
|
+
process.once("SIGTERM", terminate);
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
if (progress) progress.start(fit(cleanLabel));
|
|
209
|
+
else render.info(cleanLabel);
|
|
210
|
+
const result = await action(update);
|
|
211
|
+
if (progress) progress.stop(terminalText(completedLabel));
|
|
212
|
+
else render.success(completedLabel);
|
|
213
|
+
return result;
|
|
214
|
+
} catch (error) {
|
|
215
|
+
if (progress) progress.stop(`${cleanLabel}: failed`, 2);
|
|
216
|
+
else render.error(`${cleanLabel}: failed`);
|
|
217
|
+
throw error;
|
|
218
|
+
} finally {
|
|
219
|
+
releaseAccent?.();
|
|
220
|
+
if (heartbeat) clearInterval(heartbeat);
|
|
221
|
+
if (progress) {
|
|
222
|
+
process.removeListener("SIGINT", cancel);
|
|
223
|
+
process.removeListener("SIGTERM", terminate);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
return render;
|
|
229
|
+
}
|
|
230
|
+
var terminal = createTerminal();
|
|
231
|
+
function formatHelp(text) {
|
|
232
|
+
if (!terminal.isRich) return text;
|
|
233
|
+
const accent = `\x1B[${oliveCode(process.env)}m`;
|
|
234
|
+
return text.split("\n").map((line) => {
|
|
235
|
+
const heading = line.trimStart();
|
|
236
|
+
if (heading.length < 2 || !heading.endsWith(":")) return line;
|
|
237
|
+
const indentation = line.slice(0, line.length - heading.length);
|
|
238
|
+
return `${indentation}${accent}${heading}\x1B[0m`;
|
|
239
|
+
}).join("\n");
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export {
|
|
243
|
+
PRODUCT_TAGLINE,
|
|
244
|
+
acquireTerminalAccent,
|
|
245
|
+
terminalText,
|
|
246
|
+
createTerminal,
|
|
247
|
+
terminal,
|
|
248
|
+
formatHelp
|
|
249
|
+
};
|
|
@@ -5,12 +5,14 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
STABLE_SCAFFOLD_RELEASE
|
|
7
7
|
} from "./chunk-BP2REWTO.js";
|
|
8
|
+
import {
|
|
9
|
+
terminal
|
|
10
|
+
} from "./chunk-L2YCVQBR.js";
|
|
8
11
|
|
|
9
12
|
// src/download.ts
|
|
10
13
|
import { Readable, Transform, pipeline } from "stream";
|
|
11
14
|
import { promisify } from "util";
|
|
12
15
|
import {
|
|
13
|
-
cpSync,
|
|
14
16
|
lstatSync,
|
|
15
17
|
mkdtempSync,
|
|
16
18
|
readdirSync,
|
|
@@ -20,6 +22,7 @@ import {
|
|
|
20
22
|
} from "fs";
|
|
21
23
|
import { tmpdir } from "os";
|
|
22
24
|
import { dirname, join } from "path";
|
|
25
|
+
import { cp } from "fs/promises";
|
|
23
26
|
import { extract } from "tar";
|
|
24
27
|
var pipelineAsync = promisify(pipeline);
|
|
25
28
|
var STARTER_FETCH_TIMEOUT_MS = 15e3;
|
|
@@ -86,9 +89,16 @@ function compressedArchiveLimit() {
|
|
|
86
89
|
}
|
|
87
90
|
async function downloadStarter(targetDir, siteName, release = STABLE_SCAFFOLD_RELEASE, options = {}) {
|
|
88
91
|
if (options.announce !== false) {
|
|
89
|
-
|
|
90
|
-
|
|
92
|
+
await terminal.step(
|
|
93
|
+
`Downloading starter for ${siteName?.trim() || "your docs site"}`,
|
|
94
|
+
() => extractStarter(targetDir, release),
|
|
95
|
+
"Starter downloaded"
|
|
96
|
+
);
|
|
97
|
+
return;
|
|
91
98
|
}
|
|
99
|
+
await extractStarter(targetDir, release);
|
|
100
|
+
}
|
|
101
|
+
async function extractStarter(targetDir, release) {
|
|
92
102
|
const targetEntry = lstatSync(targetDir);
|
|
93
103
|
if (!targetEntry.isDirectory() || targetEntry.isSymbolicLink()) {
|
|
94
104
|
throw new Error("The Thally starter target must be a regular directory.");
|
|
@@ -137,7 +147,7 @@ async function downloadStarter(targetDir, siteName, release = STABLE_SCAFFOLD_RE
|
|
|
137
147
|
);
|
|
138
148
|
try {
|
|
139
149
|
for (const entry of readdirSync(stagingDir)) {
|
|
140
|
-
|
|
150
|
+
await cp(join(stagingDir, entry), join(deliveryDir, entry), {
|
|
141
151
|
recursive: true,
|
|
142
152
|
errorOnExist: true
|
|
143
153
|
});
|
package/dist/index.js
CHANGED
|
@@ -1,27 +1,88 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
migrateDocs
|
|
3
|
+
migrateDocs
|
|
4
|
+
} from "./chunk-IZQHRVPV.js";
|
|
5
|
+
import {
|
|
4
6
|
parseFrontmatter,
|
|
5
7
|
runCheck
|
|
6
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-DIX4EUI4.js";
|
|
9
|
+
import "./chunk-KDLP27DS.js";
|
|
7
10
|
import {
|
|
8
11
|
logo,
|
|
12
|
+
previewCommand,
|
|
9
13
|
scaffold,
|
|
10
14
|
slugify,
|
|
11
15
|
success
|
|
12
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-KGKSB3PU.js";
|
|
13
17
|
import "./chunk-4VKRBJV2.js";
|
|
14
|
-
import "./chunk-
|
|
18
|
+
import "./chunk-J7255UQA.js";
|
|
19
|
+
import "./chunk-WWIATVAD.js";
|
|
15
20
|
import "./chunk-RI56XMBI.js";
|
|
16
21
|
import "./chunk-BP2REWTO.js";
|
|
22
|
+
import {
|
|
23
|
+
acquireTerminalAccent,
|
|
24
|
+
formatHelp,
|
|
25
|
+
terminal
|
|
26
|
+
} from "./chunk-L2YCVQBR.js";
|
|
17
27
|
|
|
18
28
|
// src/index.ts
|
|
19
29
|
import { existsSync as existsSync2, readdirSync } from "fs";
|
|
20
30
|
import { createRequire } from "module";
|
|
21
31
|
import { resolve as resolve2 } from "path";
|
|
22
32
|
|
|
33
|
+
// src/interactive-prompts.ts
|
|
34
|
+
import * as clack from "@clack/prompts";
|
|
35
|
+
import * as inquirer from "@inquirer/prompts";
|
|
36
|
+
async function withAccent(prompt) {
|
|
37
|
+
const release = acquireTerminalAccent();
|
|
38
|
+
try {
|
|
39
|
+
return await prompt();
|
|
40
|
+
} finally {
|
|
41
|
+
release();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function answerOrCancel(answer) {
|
|
45
|
+
if (clack.isCancel(answer)) {
|
|
46
|
+
throw Object.assign(new Error("Cancelled."), { exitCode: 130 });
|
|
47
|
+
}
|
|
48
|
+
return answer;
|
|
49
|
+
}
|
|
50
|
+
async function input2(options) {
|
|
51
|
+
if (!terminal.isRich) return inquirer.input(options);
|
|
52
|
+
return answerOrCancel(await withAccent(() => clack.text({
|
|
53
|
+
message: options.message.trim(),
|
|
54
|
+
initialValue: options.default,
|
|
55
|
+
// Clack otherwise finalizes an empty answer as undefined. Optional fields
|
|
56
|
+
// such as the repository URL must keep Inquirer's empty-string contract.
|
|
57
|
+
defaultValue: "",
|
|
58
|
+
validate: options.validate ? (value) => {
|
|
59
|
+
const result = options.validate(value);
|
|
60
|
+
return result === true ? void 0 : result;
|
|
61
|
+
} : void 0
|
|
62
|
+
})));
|
|
63
|
+
}
|
|
64
|
+
async function select3(options) {
|
|
65
|
+
if (!terminal.isRich) return inquirer.select(options);
|
|
66
|
+
return answerOrCancel(await withAccent(() => clack.select({
|
|
67
|
+
message: options.message.trim(),
|
|
68
|
+
initialValue: options.default,
|
|
69
|
+
// Every existing choice has a label, including nonprimitive values such as
|
|
70
|
+
// undefined; Clack's conditional Option type cannot infer this for a generic.
|
|
71
|
+
options: options.choices.map((choice) => ({
|
|
72
|
+
label: choice.name,
|
|
73
|
+
value: choice.value
|
|
74
|
+
}))
|
|
75
|
+
})));
|
|
76
|
+
}
|
|
77
|
+
async function confirm3(options) {
|
|
78
|
+
if (!terminal.isRich) return inquirer.confirm(options);
|
|
79
|
+
return answerOrCancel(await withAccent(() => clack.confirm({
|
|
80
|
+
message: options.message.trim(),
|
|
81
|
+
initialValue: options.default ?? true
|
|
82
|
+
})));
|
|
83
|
+
}
|
|
84
|
+
|
|
23
85
|
// src/prompts.ts
|
|
24
|
-
import { confirm, input, select } from "@inquirer/prompts";
|
|
25
86
|
import { basename, resolve } from "path";
|
|
26
87
|
import {
|
|
27
88
|
parseGitHubRepositoryUrl
|
|
@@ -43,7 +104,7 @@ async function gatherMigrationPlatform(value, useDefaults) {
|
|
|
43
104
|
);
|
|
44
105
|
return void 0;
|
|
45
106
|
}
|
|
46
|
-
return
|
|
107
|
+
return select3({
|
|
47
108
|
message: " Which platform currently hosts these docs?",
|
|
48
109
|
choices: [
|
|
49
110
|
{ name: "Mintlify", value: "mintlify" },
|
|
@@ -69,7 +130,7 @@ async function resolveAutoDetectedMigrationSource(sourceUrl, shouldPrompt) {
|
|
|
69
130
|
console.warn(`
|
|
70
131
|
\u26A0 ${LIVE_URL_MIGRATION_WARNING}`);
|
|
71
132
|
if (!shouldPrompt) return sourceUrl;
|
|
72
|
-
const sourcePreference = await
|
|
133
|
+
const sourcePreference = await select3({
|
|
73
134
|
message: " Which source should Thally migrate?",
|
|
74
135
|
choices: [
|
|
75
136
|
{
|
|
@@ -84,14 +145,14 @@ async function resolveAutoDetectedMigrationSource(sourceUrl, shouldPrompt) {
|
|
|
84
145
|
default: "github"
|
|
85
146
|
});
|
|
86
147
|
if (sourcePreference === "github") {
|
|
87
|
-
const repositoryUrl = await
|
|
148
|
+
const repositoryUrl = await input2({
|
|
88
149
|
message: " GitHub repository URL:",
|
|
89
150
|
validate: validateGitHubRepositorySource
|
|
90
151
|
});
|
|
91
152
|
parseGitHubRepositoryUrl(repositoryUrl);
|
|
92
153
|
return repositoryUrl;
|
|
93
154
|
}
|
|
94
|
-
const hasAcceptedAlignment = await
|
|
155
|
+
const hasAcceptedAlignment = await confirm3({
|
|
95
156
|
message: " Continue knowing the migration may need manual alignment, using the Thally MCP afterward if needed?",
|
|
96
157
|
default: false
|
|
97
158
|
});
|
|
@@ -119,23 +180,23 @@ async function gatherAnswers(dirArg, useDefaults, installPreference) {
|
|
|
119
180
|
} else if (useDefaults) {
|
|
120
181
|
projectDir = resolve("my-docs");
|
|
121
182
|
} else {
|
|
122
|
-
const dirName = await
|
|
183
|
+
const dirName = await input2({
|
|
123
184
|
message: " Project directory:",
|
|
124
185
|
default: "my-docs"
|
|
125
186
|
});
|
|
126
187
|
projectDir = resolve(dirName);
|
|
127
188
|
}
|
|
128
189
|
const defaultName = basename(projectDir).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
129
|
-
const projectName = useDefaults ? defaultName : await
|
|
190
|
+
const projectName = useDefaults ? defaultName : await input2({
|
|
130
191
|
message: " Project name:",
|
|
131
192
|
default: defaultName
|
|
132
193
|
});
|
|
133
194
|
const defaultDesc = `Documentation for ${projectName}.`;
|
|
134
|
-
const description = useDefaults ? defaultDesc : await
|
|
195
|
+
const description = useDefaults ? defaultDesc : await input2({
|
|
135
196
|
message: " Description:",
|
|
136
197
|
default: defaultDesc
|
|
137
198
|
});
|
|
138
|
-
const brandPreset = useDefaults ? "primary" : await
|
|
199
|
+
const brandPreset = useDefaults ? "primary" : await select3({
|
|
139
200
|
message: " Brand preset:",
|
|
140
201
|
choices: [
|
|
141
202
|
{ name: "primary", value: "primary" },
|
|
@@ -143,18 +204,18 @@ async function gatherAnswers(dirArg, useDefaults, installPreference) {
|
|
|
143
204
|
],
|
|
144
205
|
default: "primary"
|
|
145
206
|
});
|
|
146
|
-
const repoUrl = useDefaults ? "" : await
|
|
207
|
+
const repoUrl = useDefaults ? "" : await input2({
|
|
147
208
|
message: " GitHub repo URL (optional):",
|
|
148
209
|
default: ""
|
|
149
210
|
});
|
|
150
211
|
let trackRepos;
|
|
151
212
|
if (!useDefaults) {
|
|
152
|
-
const enableTrack = await
|
|
213
|
+
const enableTrack = await input2({
|
|
153
214
|
message: " Keep your docs in sync automatically with Thally Track? When a PR merges in a repo you list,\n the docs agent drafts the doc updates as a PR for you to review. (y/N):",
|
|
154
215
|
default: "N"
|
|
155
216
|
});
|
|
156
217
|
if (enableTrack.trim().toLowerCase() === "y") {
|
|
157
|
-
const reposInput = await
|
|
218
|
+
const reposInput = await input2({
|
|
158
219
|
message: " Which repo(s) should Thally watch? (comma-separated owner/repo or GitHub URLs, e.g. acme/api,acme/web):",
|
|
159
220
|
default: ""
|
|
160
221
|
});
|
|
@@ -165,7 +226,7 @@ async function gatherAnswers(dirArg, useDefaults, installPreference) {
|
|
|
165
226
|
}
|
|
166
227
|
let doInstall = installPreference ?? false;
|
|
167
228
|
if (!useDefaults && installPreference === void 0) {
|
|
168
|
-
const shouldInstall = await
|
|
229
|
+
const shouldInstall = await input2({
|
|
169
230
|
message: " Install dependencies now? (y/N):",
|
|
170
231
|
default: "N"
|
|
171
232
|
});
|
|
@@ -173,12 +234,12 @@ async function gatherAnswers(dirArg, useDefaults, installPreference) {
|
|
|
173
234
|
}
|
|
174
235
|
let i18nLocales;
|
|
175
236
|
if (!useDefaults) {
|
|
176
|
-
const enableI18n = await
|
|
237
|
+
const enableI18n = await input2({
|
|
177
238
|
message: " Add another documentation language? (y/N):",
|
|
178
239
|
default: "N"
|
|
179
240
|
});
|
|
180
241
|
if (enableI18n.toLowerCase() === "y") {
|
|
181
|
-
const localesInput = await
|
|
242
|
+
const localesInput = await input2({
|
|
182
243
|
message: " Which additional locales? (comma-separated codes, e.g. fr,de,ja):",
|
|
183
244
|
default: "fr"
|
|
184
245
|
});
|
|
@@ -212,7 +273,7 @@ import { parseGitHubRepositoryUrl as parseGitHubRepositoryUrl2 } from "@thallyla
|
|
|
212
273
|
// src/translate.ts
|
|
213
274
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
214
275
|
import { join, dirname } from "path";
|
|
215
|
-
import { input as
|
|
276
|
+
import { input as input3 } from "@inquirer/prompts";
|
|
216
277
|
import Anthropic from "@anthropic-ai/sdk";
|
|
217
278
|
import pLimit from "p-limit";
|
|
218
279
|
|
|
@@ -480,7 +541,7 @@ ${examples.maskedSource}`
|
|
|
480
541
|
}
|
|
481
542
|
]
|
|
482
543
|
});
|
|
483
|
-
const
|
|
544
|
+
const text2 = message.content.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
484
545
|
const values = [];
|
|
485
546
|
for (let start = 0; start < examples.segments.length; start += 32) {
|
|
486
547
|
const sourceSegments = examples.segments.slice(start, start + 32);
|
|
@@ -498,7 +559,7 @@ ${examples.maskedSource}`
|
|
|
498
559
|
}
|
|
499
560
|
values.push(...translated);
|
|
500
561
|
}
|
|
501
|
-
return examples.restore(
|
|
562
|
+
return examples.restore(text2.trim(), values);
|
|
502
563
|
}
|
|
503
564
|
async function runTranslateCommand(locale, pages, force, apiKey, model, yes, projectDir) {
|
|
504
565
|
const config = readDocsJson(projectDir);
|
|
@@ -569,11 +630,11 @@ async function runTranslateCommand(locale, pages, force, apiKey, model, yes, pro
|
|
|
569
630
|
}
|
|
570
631
|
console.log("");
|
|
571
632
|
if (!yes) {
|
|
572
|
-
const
|
|
633
|
+
const confirm4 = await input3({
|
|
573
634
|
message: " Proceed? (Y/n):",
|
|
574
635
|
default: "Y"
|
|
575
636
|
});
|
|
576
|
-
if (
|
|
637
|
+
if (confirm4.toLowerCase() === "n") {
|
|
577
638
|
console.log("\n Aborted.");
|
|
578
639
|
return;
|
|
579
640
|
}
|
|
@@ -632,7 +693,7 @@ var valueFlags = /* @__PURE__ */ new Set([
|
|
|
632
693
|
"--platform"
|
|
633
694
|
]);
|
|
634
695
|
var commandFlags = {
|
|
635
|
-
scaffold: /* @__PURE__ */ new Set(["--help", "-h", "--yes", "-y", "--install", "--no-install"]),
|
|
696
|
+
scaffold: /* @__PURE__ */ new Set(["--help", "-h", "--yes", "-y", "--install", "--no-install", "--verbose"]),
|
|
636
697
|
migrate: /* @__PURE__ */ new Set([
|
|
637
698
|
"--help",
|
|
638
699
|
"-h",
|
|
@@ -643,6 +704,7 @@ var commandFlags = {
|
|
|
643
704
|
"--max-pages",
|
|
644
705
|
"--platform",
|
|
645
706
|
"--skip-validation",
|
|
707
|
+
"--verbose",
|
|
646
708
|
"--yes",
|
|
647
709
|
"-y"
|
|
648
710
|
]),
|
|
@@ -673,6 +735,7 @@ Scaffold options:
|
|
|
673
735
|
-y, --yes Accept defaults and skip interactive prompts
|
|
674
736
|
--install Install project dependencies after scaffolding
|
|
675
737
|
--no-install Skip dependency installation without prompting
|
|
738
|
+
--verbose Show detailed installation and Git output
|
|
676
739
|
-h, --help Show this help
|
|
677
740
|
-v, -V, --version Show the installed version
|
|
678
741
|
|
|
@@ -690,6 +753,7 @@ Options:
|
|
|
690
753
|
--max-pages <count> Limit a public URL crawl to 1-1000 pages
|
|
691
754
|
--platform <name> Use mintlify, docusaurus, fern, or auto
|
|
692
755
|
--skip-validation Import only; explicitly skip content and build verification
|
|
756
|
+
--verbose Show detailed installation, build, and Git output
|
|
693
757
|
--api-key <key> Anthropic API key for non-Markdown conversion
|
|
694
758
|
-y, --yes Skip interactive prompts
|
|
695
759
|
-h, --help Show this help
|
|
@@ -743,7 +807,7 @@ function resolveCommand(subcommand) {
|
|
|
743
807
|
return "scaffold";
|
|
744
808
|
}
|
|
745
809
|
function printHelp(command) {
|
|
746
|
-
console.log(command === "scaffold" ? mainHelp : commandHelp[command]);
|
|
810
|
+
console.log(formatHelp(command === "scaffold" ? mainHelp : commandHelp[command]));
|
|
747
811
|
}
|
|
748
812
|
function validateFlags(command) {
|
|
749
813
|
const unknownFlag = flags.find((flag) => !commandFlags[command].has(flag));
|
|
@@ -772,10 +836,11 @@ async function runMigrateCommand() {
|
|
|
772
836
|
const branch = getFlagValue("--branch");
|
|
773
837
|
const docsDir = getFlagValue("--docs-dir");
|
|
774
838
|
const yes = flags.includes("--yes") || flags.includes("-y");
|
|
839
|
+
terminal.intro("migrate", packageMetadata.version, "Bring your docs to Thally. Built for people and AI tools.");
|
|
775
840
|
const platformFlag = getFlagValue("--platform");
|
|
776
841
|
const platform = await gatherMigrationPlatform(platformFlag, yes);
|
|
777
842
|
if (platform === void 0) {
|
|
778
|
-
const shouldPromptForSource = platformFlag === void 0 && !yes;
|
|
843
|
+
const shouldPromptForSource = platformFlag === void 0 && !yes && Boolean(process.stdin.isTTY);
|
|
779
844
|
sourceUrl = await resolveAutoDetectedMigrationSource(sourceUrl, shouldPromptForSource);
|
|
780
845
|
source = new URL(sourceUrl);
|
|
781
846
|
}
|
|
@@ -797,15 +862,11 @@ async function runMigrateCommand() {
|
|
|
797
862
|
console.error("\n \u274C --max-pages must be an integer between 1 and 1000.");
|
|
798
863
|
process.exit(1);
|
|
799
864
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
if (branch) console.log(` Branch: ${branch}`);
|
|
806
|
-
if (docsDir) console.log(` Docs dir: ${docsDir}`);
|
|
807
|
-
console.log(` Platform: ${platform ?? "auto-detect"}`);
|
|
808
|
-
console.log("");
|
|
865
|
+
terminal.detail("Source", `${source.origin}${source.pathname}`);
|
|
866
|
+
terminal.detail("Output", projectDir);
|
|
867
|
+
if (branch) terminal.detail("Branch", branch);
|
|
868
|
+
if (docsDir) terminal.detail("Docs dir", docsDir);
|
|
869
|
+
terminal.detail("Platform", platform ?? "auto-detect");
|
|
809
870
|
const result = await migrateDocs({
|
|
810
871
|
sourceUrl,
|
|
811
872
|
projectDir,
|
|
@@ -818,14 +879,20 @@ async function runMigrateCommand() {
|
|
|
818
879
|
yes,
|
|
819
880
|
skipValidation: flags.includes("--skip-validation")
|
|
820
881
|
});
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
882
|
+
terminal.section("Migration summary", [
|
|
883
|
+
`${result.pagesWritten} pages \xB7 ${result.assetsWritten} assets \xB7 ${result.platform}`,
|
|
884
|
+
`Content: ${result.validation.content} \xB7 Build: ${result.validation.build}`,
|
|
885
|
+
`${result.warnings.length} migration warning(s)`
|
|
886
|
+
]);
|
|
887
|
+
terminal.detail("Output", result.projectDir);
|
|
888
|
+
terminal.detail("Report", result.reportPath);
|
|
889
|
+
const isVerified = result.validation.content === "passed" && result.validation.build === "passed";
|
|
890
|
+
terminal.outro(isVerified ? "Migration verified. Review any warnings before publishing." : "Import saved. Validation is incomplete; review the migration report.");
|
|
891
|
+
terminal.nextAction("Preview locally", previewCommand(result.projectDir, !existsSync2(resolve2(result.projectDir, "node_modules"))));
|
|
825
892
|
if (result.validation.content === "failed" || result.validation.build === "failed") process.exitCode = 1;
|
|
826
893
|
}
|
|
827
894
|
async function runScaffoldCommand() {
|
|
828
|
-
const useDefaults = flags.includes("--yes") || flags.includes("-y");
|
|
895
|
+
const useDefaults = flags.includes("--yes") || flags.includes("-y") || !process.stdin.isTTY;
|
|
829
896
|
const installPreference = flags.includes("--install") ? true : flags.includes("--no-install") ? false : void 0;
|
|
830
897
|
const dirArg = positional[0];
|
|
831
898
|
if (dirArg) {
|
|
@@ -873,9 +940,7 @@ async function runTranslateSubcommand() {
|
|
|
873
940
|
const model = getFlagValue("--model") ?? "claude-sonnet-4-6";
|
|
874
941
|
const yes = flags.includes("--yes") || flags.includes("-y");
|
|
875
942
|
const projectDir = resolve2(positional[1] ?? ".");
|
|
876
|
-
|
|
877
|
-
console.log(" \u{1F310} Thally Translate");
|
|
878
|
-
console.log("");
|
|
943
|
+
terminal.intro("translate", packageMetadata.version, "Make your docs available in another language.");
|
|
879
944
|
await runTranslateCommand(locale, pages, force, apiKey, model, yes, projectDir);
|
|
880
945
|
}
|
|
881
946
|
async function main() {
|
|
@@ -897,11 +962,11 @@ async function main() {
|
|
|
897
962
|
} else if (subcommand === "translate") {
|
|
898
963
|
await runTranslateSubcommand();
|
|
899
964
|
} else {
|
|
900
|
-
logo();
|
|
965
|
+
logo(packageMetadata.version);
|
|
901
966
|
await runScaffoldCommand();
|
|
902
967
|
}
|
|
903
968
|
}
|
|
904
969
|
main().catch((err) => {
|
|
905
|
-
|
|
906
|
-
process.exit(1);
|
|
970
|
+
terminal.error(err.message);
|
|
971
|
+
process.exit(err.exitCode ?? 1);
|
|
907
972
|
});
|