create-thally-docs 0.10.78 → 0.10.80
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-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-VGE5L66Z.js +349 -0
- package/dist/{chunk-W5WQ2V7P.js → chunk-WWIATVAD.js} +14 -4
- package/dist/index.js +134 -50
- package/dist/migrate/index.d.ts +3 -1
- 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 +8 -3
- package/dist/chunk-QLMRRNLL.js +0 -162
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
findPublicSpecs,
|
|
4
|
+
shadowNote
|
|
5
|
+
} from "./chunk-KDLP27DS.js";
|
|
6
|
+
import {
|
|
7
|
+
initGit,
|
|
8
|
+
installDeps,
|
|
9
|
+
runProjectCommand,
|
|
10
|
+
scaffold
|
|
11
|
+
} from "./chunk-KGKSB3PU.js";
|
|
12
|
+
import {
|
|
13
|
+
pruneMissingSiteLinks
|
|
14
|
+
} from "./chunk-4VKRBJV2.js";
|
|
15
|
+
import {
|
|
16
|
+
terminal
|
|
17
|
+
} from "./chunk-L2YCVQBR.js";
|
|
18
|
+
|
|
19
|
+
// src/migrate/index.ts
|
|
20
|
+
import { existsSync as existsSync2, mkdirSync, mkdtempSync, readFileSync as readFileSync2, rmSync, writeFileSync } from "fs";
|
|
21
|
+
import { tmpdir } from "os";
|
|
22
|
+
import { setImmediate } from "timers/promises";
|
|
23
|
+
import { basename, dirname, isAbsolute, join as join2, relative, resolve, sep } from "path";
|
|
24
|
+
import {
|
|
25
|
+
cloneGitHubRepository,
|
|
26
|
+
importSourceRef,
|
|
27
|
+
hydrateRemoteApiSpecs,
|
|
28
|
+
migrateUrl,
|
|
29
|
+
parseGitHubRepositoryUrl,
|
|
30
|
+
sourceRefOversizeReason
|
|
31
|
+
} from "@thallylabs/migrate";
|
|
32
|
+
|
|
33
|
+
// src/migration-work.ts
|
|
34
|
+
import { Worker } from "worker_threads";
|
|
35
|
+
async function runMigrationWork(request, options) {
|
|
36
|
+
const worker = new Worker(options.workerUrl ?? new URL("./migration-worker.js", import.meta.url), {
|
|
37
|
+
workerData: request,
|
|
38
|
+
stdout: true,
|
|
39
|
+
stderr: true
|
|
40
|
+
});
|
|
41
|
+
worker.stdout.resume();
|
|
42
|
+
worker.stderr.resume();
|
|
43
|
+
try {
|
|
44
|
+
return await new Promise((complete, reject) => {
|
|
45
|
+
worker.once("message", (response) => {
|
|
46
|
+
if (response.error) reject(response.error);
|
|
47
|
+
else complete(response.result);
|
|
48
|
+
});
|
|
49
|
+
worker.once("error", reject);
|
|
50
|
+
worker.once("exit", (code) => reject(new Error(`Migration worker exited before returning a result (exit ${code}).`)));
|
|
51
|
+
});
|
|
52
|
+
} finally {
|
|
53
|
+
await worker.terminate();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function convertRepository(options, workerOptions = {}) {
|
|
57
|
+
return runMigrationWork({ operation: "repository", args: [options] }, workerOptions);
|
|
58
|
+
}
|
|
59
|
+
function renderFiles(bundle, options, workerOptions = {}) {
|
|
60
|
+
return runMigrationWork({ operation: "render", args: [bundle, options] }, workerOptions);
|
|
61
|
+
}
|
|
62
|
+
function discoverUrl(options, workerOptions = {}) {
|
|
63
|
+
return runMigrationWork({ operation: "url", args: [options] }, workerOptions);
|
|
64
|
+
}
|
|
65
|
+
function checkContent(projectDir, workerOptions = {}) {
|
|
66
|
+
return runMigrationWork({ operation: "check", args: [projectDir] }, workerOptions);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/migrate/validate.ts
|
|
70
|
+
import { existsSync, readFileSync } from "fs";
|
|
71
|
+
import { join } from "path";
|
|
72
|
+
async function validateMigration(projectDir, skip = false, installationFailed = false) {
|
|
73
|
+
const result = { content: "skipped", build: "skipped", messages: [], diagnostics: [] };
|
|
74
|
+
if (skip) {
|
|
75
|
+
result.messages.push("Validation was explicitly skipped; this import is not verified.");
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const isQuiet = terminal.isRich && !terminal.isVerbose;
|
|
80
|
+
await terminal.step("Validate content", async () => {
|
|
81
|
+
const checked = await checkContent(projectDir);
|
|
82
|
+
const code = checked.code;
|
|
83
|
+
result.diagnostics = checked.diagnostics;
|
|
84
|
+
if (!isQuiet) {
|
|
85
|
+
for (const issue of checked.diagnostics) {
|
|
86
|
+
const location = issue.file ? `file=${issue.file}${issue.line ? `,line=${issue.line}` : ""}` : "";
|
|
87
|
+
console.log(`::${issue.severity} ${location}::${issue.message}`);
|
|
88
|
+
}
|
|
89
|
+
const errors = checked.diagnostics.filter((issue) => issue.severity === "error").length;
|
|
90
|
+
const warnings = checked.diagnostics.length - errors;
|
|
91
|
+
console.log(`
|
|
92
|
+
thally check: ${errors} error(s), ${warnings} warning(s)`);
|
|
93
|
+
}
|
|
94
|
+
if (isQuiet) {
|
|
95
|
+
for (const issue of result.diagnostics) {
|
|
96
|
+
result.messages.push(`${issue.file ? `${issue.file}${issue.line ? `:${issue.line}` : ""}: ` : ""}${issue.message}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (code !== 0) throw new Error("Review the content diagnostics; source links are not silently rewritten.");
|
|
100
|
+
}, "Content validated");
|
|
101
|
+
result.content = "passed";
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if ([130, 143].includes(error.exitCode ?? 0)) throw error;
|
|
104
|
+
result.content = "failed";
|
|
105
|
+
result.messages.push(`Content validation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
106
|
+
}
|
|
107
|
+
if (installationFailed) {
|
|
108
|
+
result.build = "failed";
|
|
109
|
+
result.messages.push("Dependency installation failed; production build was not attempted. Review the installation output before retrying.");
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
const packagePath = join(projectDir, "package.json");
|
|
113
|
+
try {
|
|
114
|
+
const manifest = existsSync(packagePath) ? JSON.parse(readFileSync(packagePath, "utf8")) : void 0;
|
|
115
|
+
if (!manifest?.scripts?.build) {
|
|
116
|
+
result.messages.push("No build script is available; production rendering is not verified.");
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
await terminal.step("Validate production build", (update) => runProjectCommand("npm", ["run", "build"], projectDir, 10 * 60 * 1e3, (message) => update(`Production build: ${message}`)), "Production build validated");
|
|
121
|
+
result.build = "passed";
|
|
122
|
+
} catch (error) {
|
|
123
|
+
const failure = error;
|
|
124
|
+
if (failure.output?.trim()) terminal.error(failure.output.trimEnd());
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
} catch (error) {
|
|
128
|
+
if ([130, 143].includes(error.exitCode ?? 0)) throw error;
|
|
129
|
+
result.build = "failed";
|
|
130
|
+
result.messages.push(`Production build failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/migrate/index.ts
|
|
136
|
+
function projectPath(projectDir, candidate) {
|
|
137
|
+
const target = resolve(projectDir, candidate);
|
|
138
|
+
const fromRoot = relative(resolve(projectDir), target);
|
|
139
|
+
if (isAbsolute(fromRoot) || fromRoot === ".." || fromRoot.startsWith(`..${sep}`)) {
|
|
140
|
+
throw new Error(`Generated migration path escapes the project: ${candidate}`);
|
|
141
|
+
}
|
|
142
|
+
return target;
|
|
143
|
+
}
|
|
144
|
+
function ignoreQuarantineDirectory(projectDir) {
|
|
145
|
+
const ignorePath = projectPath(projectDir, ".gitignore");
|
|
146
|
+
const existing = existsSync2(ignorePath) ? readFileSync2(ignorePath, "utf8") : "";
|
|
147
|
+
const rules = existing.split(/\r?\n/).map((line) => line.trim()).filter((line) => /^!?\/?migration-quarantine(?:\/(?:\*\*?)?)?$/.test(line));
|
|
148
|
+
if (rules.length > 0 && !rules[rules.length - 1].startsWith("!")) return;
|
|
149
|
+
const separator = existing === "" || existing.endsWith("\n") ? "" : "\n";
|
|
150
|
+
writeFileSync(ignorePath, `${existing}${separator}
|
|
151
|
+
# Access-restricted pages withheld by migration: local only, never commit or deploy
|
|
152
|
+
/migration-quarantine/
|
|
153
|
+
`);
|
|
154
|
+
}
|
|
155
|
+
function readExistingConfig(projectDir) {
|
|
156
|
+
const configPath = projectPath(projectDir, "docs.json");
|
|
157
|
+
if (!existsSync2(configPath)) return void 0;
|
|
158
|
+
return JSON.parse(readFileSync2(configPath, "utf8"));
|
|
159
|
+
}
|
|
160
|
+
function resetFreshMigrationContent(projectDir) {
|
|
161
|
+
const contentDirectory = projectPath(projectDir, "src/content");
|
|
162
|
+
rmSync(contentDirectory, { recursive: true, force: true });
|
|
163
|
+
mkdirSync(contentDirectory, { recursive: true });
|
|
164
|
+
for (const sampleSpec of ["openapi.yaml", "openapi.json"]) {
|
|
165
|
+
rmSync(projectPath(projectDir, sampleSpec), { force: true });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
async function fetchSourceRefs(mappings, warnings) {
|
|
169
|
+
const imports = [];
|
|
170
|
+
for (const mapping of mappings) {
|
|
171
|
+
const root = mkdtempSync(join2(tmpdir(), "thally-source-ref-"));
|
|
172
|
+
try {
|
|
173
|
+
const source = parseGitHubRepositoryUrl(`https://github.com/${mapping.repo}`);
|
|
174
|
+
const tooLarge = await sourceRefOversizeReason(mapping.repo);
|
|
175
|
+
if (tooLarge) throw new Error(tooLarge);
|
|
176
|
+
const cloneDir = join2(root, "repository");
|
|
177
|
+
await cloneGitHubRepository(source, cloneDir, warnings, { skipSubmodules: true });
|
|
178
|
+
imports.push(importSourceRef(mapping, cloneDir));
|
|
179
|
+
} catch (error) {
|
|
180
|
+
warnings.push({ code: "fetch-failed", message: `sourceRef ${mapping.repo} was not imported: ${error instanceof Error ? error.message : String(error)}` });
|
|
181
|
+
} finally {
|
|
182
|
+
rmSync(root, { recursive: true, force: true });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return imports;
|
|
186
|
+
}
|
|
187
|
+
async function discoverMigration(options) {
|
|
188
|
+
const url = new URL(options.sourceUrl);
|
|
189
|
+
if (url.hostname.toLowerCase() !== "github.com") {
|
|
190
|
+
if (options.sourceRefs?.length) terminal.warn("--source-ref applies only to GitHub repository sources and was ignored.");
|
|
191
|
+
return terminal.step("Discovering public documentation", () => {
|
|
192
|
+
const discovery = {
|
|
193
|
+
sourceUrl: options.sourceUrl,
|
|
194
|
+
platform: options.platform,
|
|
195
|
+
maxPages: options.maxPages
|
|
196
|
+
};
|
|
197
|
+
return options.fetcher ? migrateUrl({ ...discovery, fetcher: options.fetcher }) : discoverUrl(discovery);
|
|
198
|
+
}, "Public documentation discovered");
|
|
199
|
+
}
|
|
200
|
+
const source = parseGitHubRepositoryUrl(options.sourceUrl);
|
|
201
|
+
if (options.branch) source.branch = options.branch;
|
|
202
|
+
const temporaryRoot = mkdtempSync(join2(tmpdir(), "thally-migrate-"));
|
|
203
|
+
const cloneDir = join2(temporaryRoot, "repository");
|
|
204
|
+
try {
|
|
205
|
+
const cloneWarnings = [];
|
|
206
|
+
await terminal.step(`Cloning ${source.owner}/${source.repo}`, () => cloneGitHubRepository(source, cloneDir, cloneWarnings), "Source repository cloned");
|
|
207
|
+
const sourceRefs = options.sourceRefs?.length ? await terminal.step("Importing sourceRef repositories", () => fetchSourceRefs(options.sourceRefs ?? [], cloneWarnings), "sourceRef repositories imported") : [];
|
|
208
|
+
const bundle = await terminal.step("Converting repository documentation", () => convertRepository({
|
|
209
|
+
sourceRefs,
|
|
210
|
+
repositoryDir: cloneDir,
|
|
211
|
+
sourceUrl: options.sourceUrl,
|
|
212
|
+
docsDir: options.docsDir ?? (source.docsDir || void 0),
|
|
213
|
+
platform: options.platform
|
|
214
|
+
}), "Repository documentation converted");
|
|
215
|
+
const hydrated = await terminal.step("Loading remote API specifications", () => hydrateRemoteApiSpecs(bundle, options.fetcher), "Remote API specifications loaded");
|
|
216
|
+
return cloneWarnings.length > 0 ? { ...hydrated, warnings: [...cloneWarnings, ...hydrated.warnings] } : hydrated;
|
|
217
|
+
} finally {
|
|
218
|
+
rmSync(temporaryRoot, { recursive: true, force: true });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function publicSpecWarning(projectDir, bundle) {
|
|
222
|
+
const { specs, skipped } = findPublicSpecs(projectDir);
|
|
223
|
+
if (specs.length === 0 && skipped.length === 0) return null;
|
|
224
|
+
const migrated = new Set(bundle.assets.filter((asset) => asset.projectRelative).map((asset) => basename(asset.path)));
|
|
225
|
+
const lines = [
|
|
226
|
+
...specs.map((spec) => `${spec.path}${migrated.has(basename(spec.path)) ? " (same file name as a migrated spec)" : ""}${spec.hasHiddenOperations ? " (contains x-excluded/x-hidden operations)" : ""}${shadowNote(spec.urlPath) ? ` (answers /${spec.urlPath} in place of the filtered specification)` : ""}`),
|
|
227
|
+
...skipped.map((path) => `${path} (too large to inspect)`)
|
|
228
|
+
];
|
|
229
|
+
return {
|
|
230
|
+
code: "unsupported-config",
|
|
231
|
+
message: `Existing OpenAPI files under public/ are served publicly as-is: ${lines.join("; ")}. Migrated specs are written to openapi/, and re-running migration does not remove old copies from public/. Delete any old copy manually (nothing was deleted), or its hidden operations stay downloadable.`
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
async function migrateDocs(options) {
|
|
235
|
+
const projectDir = resolve(options.projectDir);
|
|
236
|
+
const bundle = await discoverMigration(options);
|
|
237
|
+
if (!options.into) {
|
|
238
|
+
terminal.detail("Project", projectDir);
|
|
239
|
+
await scaffold({
|
|
240
|
+
projectDir,
|
|
241
|
+
projectName: options.projectName ?? bundle.site?.name ?? "My Docs",
|
|
242
|
+
description: bundle.site?.description ?? `Documentation migrated from ${new URL(options.sourceUrl).hostname}`,
|
|
243
|
+
brandPreset: "primary",
|
|
244
|
+
// Source provenance is not the destination repository. Migrated pages
|
|
245
|
+
// live at new paths, so source URLs cannot power edit/issue actions.
|
|
246
|
+
repoUrl: "",
|
|
247
|
+
doInstall: false,
|
|
248
|
+
colors: bundle.site?.colors
|
|
249
|
+
});
|
|
250
|
+
resetFreshMigrationContent(projectDir);
|
|
251
|
+
pruneMissingSiteLinks(projectDir, new Set(bundle.pages.map((page) => page.id)));
|
|
252
|
+
} else if (!existsSync2(projectDir)) {
|
|
253
|
+
throw new Error(`Project directory "${projectDir}" does not exist. Use without --into to scaffold a new one.`);
|
|
254
|
+
}
|
|
255
|
+
if (!options.into) {
|
|
256
|
+
const starterConfig = readExistingConfig(projectDir);
|
|
257
|
+
if (starterConfig?.markdown) bundle.docsConfig.markdown ??= starterConfig.markdown;
|
|
258
|
+
if (!bundle.docsConfig.navbar?.primary) {
|
|
259
|
+
bundle.docsConfig.navbar = { ...bundle.docsConfig.navbar, primary: null };
|
|
260
|
+
}
|
|
261
|
+
bundle.docsConfig.i18n ??= { defaultLocale: "en", locales: [{ code: "en", label: "English" }] };
|
|
262
|
+
}
|
|
263
|
+
if (options.into) {
|
|
264
|
+
const stale = publicSpecWarning(projectDir, bundle);
|
|
265
|
+
if (stale) bundle.warnings.unshift(stale);
|
|
266
|
+
}
|
|
267
|
+
const rendered = await terminal.step("Rendering documentation", () => renderFiles(bundle, {
|
|
268
|
+
existingConfig: options.into ? readExistingConfig(projectDir) : void 0,
|
|
269
|
+
existingComponentRegistry: existsSync2(projectPath(projectDir, "src/mdx/custom-components.tsx")) ? readFileSync2(projectPath(projectDir, "src/mdx/custom-components.tsx"), "utf8") : void 0
|
|
270
|
+
}), "Documentation rendered");
|
|
271
|
+
await terminal.step("Writing documentation", async (update) => {
|
|
272
|
+
let written = 0;
|
|
273
|
+
for (const file of rendered) {
|
|
274
|
+
const destination = projectPath(projectDir, file.path);
|
|
275
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
276
|
+
writeFileSync(destination, file.content);
|
|
277
|
+
written += 1;
|
|
278
|
+
if (written % 32 === 0) {
|
|
279
|
+
update(`Writing documentation (${written}/${rendered.length} files)`);
|
|
280
|
+
await setImmediate();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (bundle.quarantinedFiles?.length) ignoreQuarantineDirectory(projectDir);
|
|
284
|
+
}, `Imported ${bundle.pages.length} pages and ${bundle.assets.length} assets from ${bundle.platform}`);
|
|
285
|
+
const format = (warning) => `${warning.message}${warning.source ? ` (${warning.source})` : ""}`;
|
|
286
|
+
for (const warning of bundle.warnings.filter((item) => item.code !== "gated-page")) terminal.warn(format(warning));
|
|
287
|
+
const gatedWarnings = bundle.warnings.filter((item) => item.code === "gated-page");
|
|
288
|
+
if (gatedWarnings.length > 0) {
|
|
289
|
+
terminal.warn("ACCESS-RESTRICTED CONTENT \u2014 review before publishing");
|
|
290
|
+
for (const warning of gatedWarnings) terminal.warn(format(warning));
|
|
291
|
+
if (bundle.droppedGatedPages) {
|
|
292
|
+
terminal.warn(`${bundle.droppedGatedPages} access-restricted page(s) were dropped by the file limit: not published and not saved under migration-quarantine/; recover them from the source repository.`);
|
|
293
|
+
}
|
|
294
|
+
if (bundle.quarantinedFiles?.length || gatedWarnings.some((warning) => !/dashboard access settings/i.test(warning.message))) {
|
|
295
|
+
terminal.warn("Before publishing, review migration-quarantine/assets/ and the dashboard access settings of the source site.");
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
let installationFailed = false;
|
|
299
|
+
if (!options.skipValidation) terminal.info("Migration validation runs project code locally, including imported MDX and components.");
|
|
300
|
+
if (!options.into && !options.skipValidation) {
|
|
301
|
+
try {
|
|
302
|
+
await installDeps(projectDir);
|
|
303
|
+
} catch (error) {
|
|
304
|
+
const exitCode = error.exitCode;
|
|
305
|
+
if (exitCode === 130 || exitCode === 143) throw error;
|
|
306
|
+
installationFailed = true;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
terminal.info("Validating imported documentation...");
|
|
310
|
+
const validation = await validateMigration(projectDir, options.skipValidation, installationFailed);
|
|
311
|
+
const quarantinedPages = (bundle.quarantinedFiles ?? []).filter((file) => /\.mdx?$/i.test(file.path)).length;
|
|
312
|
+
const reportPath = projectPath(projectDir, "migration-report.json");
|
|
313
|
+
writeFileSync(reportPath, `${JSON.stringify({
|
|
314
|
+
version: 1,
|
|
315
|
+
sourceUrl: `${new URL(options.sourceUrl).origin}${new URL(options.sourceUrl).pathname}`,
|
|
316
|
+
platform: bundle.platform,
|
|
317
|
+
pages: bundle.pages.length,
|
|
318
|
+
assets: bundle.assets.length,
|
|
319
|
+
components: bundle.componentFiles?.length ?? 0,
|
|
320
|
+
sourceRefs: bundle.sourceRefs ?? [],
|
|
321
|
+
quarantined: quarantinedPages,
|
|
322
|
+
quarantinedAssets: (bundle.quarantinedFiles?.length ?? 0) - quarantinedPages,
|
|
323
|
+
droppedGatedPages: bundle.droppedGatedPages ?? 0,
|
|
324
|
+
warnings: bundle.warnings,
|
|
325
|
+
validation
|
|
326
|
+
}, null, 2)}
|
|
327
|
+
`);
|
|
328
|
+
for (const message of validation.messages) terminal.warn(message);
|
|
329
|
+
terminal.detail("Migration report", reportPath);
|
|
330
|
+
if (validation.content === "passed" && validation.build === "passed") {
|
|
331
|
+
terminal.success(`Content and production build passed.${bundle.warnings.length ? " Review the migration warnings for compatibility limitations." : ""}`);
|
|
332
|
+
} else {
|
|
333
|
+
terminal.warn("Import retained, but validation is incomplete. Do not publish without reviewing the report.");
|
|
334
|
+
}
|
|
335
|
+
if (!options.into) await initGit(projectDir);
|
|
336
|
+
return {
|
|
337
|
+
pagesWritten: bundle.pages.length,
|
|
338
|
+
assetsWritten: bundle.assets.length,
|
|
339
|
+
projectDir,
|
|
340
|
+
platform: bundle.platform,
|
|
341
|
+
warnings: bundle.warnings,
|
|
342
|
+
validation,
|
|
343
|
+
reportPath
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export {
|
|
348
|
+
migrateDocs
|
|
349
|
+
};
|
|
@@ -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
|
});
|