@xyd-js/cli 0.0.0-build
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/LICENSE +21 -0
- package/dist/build.d.ts +2 -0
- package/dist/build.js +5 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1549 -0
- package/package.json +42 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1549 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __export = (target, all) => {
|
|
4
|
+
for (var name in all)
|
|
5
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// src/cli.ts
|
|
9
|
+
import semver from "semver";
|
|
10
|
+
import updateNotifier from "update-notifier";
|
|
11
|
+
|
|
12
|
+
// src/const.ts
|
|
13
|
+
var MIN_NODE_VERSION = 22;
|
|
14
|
+
|
|
15
|
+
// src/utils.ts
|
|
16
|
+
import { readFileSync } from "fs";
|
|
17
|
+
import { fileURLToPath } from "url";
|
|
18
|
+
import { dirname, join } from "path";
|
|
19
|
+
import colors from "picocolors";
|
|
20
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
21
|
+
var __dirname = dirname(__filename);
|
|
22
|
+
var packageJsonPath = join(__dirname, "..", "package.json");
|
|
23
|
+
var packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
24
|
+
function getPackageJson() {
|
|
25
|
+
return packageJson;
|
|
26
|
+
}
|
|
27
|
+
function getVersion() {
|
|
28
|
+
return packageJson.version;
|
|
29
|
+
}
|
|
30
|
+
function printHelp() {
|
|
31
|
+
console.log(`
|
|
32
|
+
${colors.blueBright(cliSpec.name)} \u2014 ${cliSpec.description}
|
|
33
|
+
`);
|
|
34
|
+
console.log(`${colors.underline("Usage")}:
|
|
35
|
+
${cliSpec.usage}
|
|
36
|
+
`);
|
|
37
|
+
console.log(`${colors.underline("Global Flags")}:`);
|
|
38
|
+
for (const [flag, meta] of Object.entries(cliSpec.globalFlags)) {
|
|
39
|
+
let flagDisplay;
|
|
40
|
+
if (meta.alias) {
|
|
41
|
+
flagDisplay = ` -${meta.alias}, --${flag}`;
|
|
42
|
+
} else {
|
|
43
|
+
flagDisplay = ` --${flag}`;
|
|
44
|
+
}
|
|
45
|
+
console.log(`${flagDisplay.padEnd(20)} ${meta.description}`);
|
|
46
|
+
}
|
|
47
|
+
console.log(`
|
|
48
|
+
${colors.underline("Commands")}:`);
|
|
49
|
+
for (const [cmd, meta] of Object.entries(cliSpec.commands)) {
|
|
50
|
+
console.log(` ${cmd.padEnd(10)} ${meta.description}`);
|
|
51
|
+
}
|
|
52
|
+
console.log(`
|
|
53
|
+
Use \`--help\` with a command for more info.
|
|
54
|
+
`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/spec.ts
|
|
58
|
+
var cliSpec = {
|
|
59
|
+
name: "xyd",
|
|
60
|
+
version: getVersion(),
|
|
61
|
+
description: "Docs platform for future dev",
|
|
62
|
+
usage: "xyd [globalFlags] [command]",
|
|
63
|
+
commands: {
|
|
64
|
+
dev: {
|
|
65
|
+
description: "Run your docs locally in development mode",
|
|
66
|
+
usage: "xyd dev [flags]"
|
|
67
|
+
},
|
|
68
|
+
build: {
|
|
69
|
+
description: "Build your docs",
|
|
70
|
+
usage: "xyd build [flags]"
|
|
71
|
+
},
|
|
72
|
+
install: {
|
|
73
|
+
description: "Install the xyd framework manually",
|
|
74
|
+
usage: "xyd install [flags]"
|
|
75
|
+
},
|
|
76
|
+
migrateme: {
|
|
77
|
+
description: "Migrate your docs to the new xyd framework",
|
|
78
|
+
usage: "xyd migrateme <resource> [flags]"
|
|
79
|
+
},
|
|
80
|
+
components: {
|
|
81
|
+
description: "Manage xyd components",
|
|
82
|
+
usage: "xyd components <subcommand> [args] [flags]"
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
globalFlags: {
|
|
86
|
+
"help": {
|
|
87
|
+
alias: "h",
|
|
88
|
+
type: "Boolean",
|
|
89
|
+
description: "Print this help message and exit"
|
|
90
|
+
},
|
|
91
|
+
"version": {
|
|
92
|
+
alias: "v",
|
|
93
|
+
type: "Boolean",
|
|
94
|
+
description: "Print the CLI version and exit"
|
|
95
|
+
},
|
|
96
|
+
"verbose": {
|
|
97
|
+
type: "Boolean",
|
|
98
|
+
description: "Enable verbose output"
|
|
99
|
+
},
|
|
100
|
+
"port": {
|
|
101
|
+
alias: "p",
|
|
102
|
+
type: "Number",
|
|
103
|
+
description: "Port to run the dev server on"
|
|
104
|
+
},
|
|
105
|
+
"logLevel": {
|
|
106
|
+
alias: "l",
|
|
107
|
+
type: "String",
|
|
108
|
+
description: "Set logging level (e.g. info, debug)"
|
|
109
|
+
},
|
|
110
|
+
"debug": {
|
|
111
|
+
type: "Boolean",
|
|
112
|
+
description: "Enable debug output"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// src/args.ts
|
|
118
|
+
import arg from "arg";
|
|
119
|
+
function parseArgs(argv) {
|
|
120
|
+
const argSpec = Object.fromEntries(
|
|
121
|
+
Object.entries(cliSpec.globalFlags).flatMap(([flag, meta]) => {
|
|
122
|
+
const type = getArgType(meta.type);
|
|
123
|
+
const flagWithPrefix = `--${flag}`;
|
|
124
|
+
const entries = [[flagWithPrefix, type]];
|
|
125
|
+
if (meta.alias) entries.push([`-${meta.alias}`, flagWithPrefix]);
|
|
126
|
+
return entries;
|
|
127
|
+
})
|
|
128
|
+
);
|
|
129
|
+
const parsed = arg(argSpec, { argv });
|
|
130
|
+
const commands = parsed._;
|
|
131
|
+
const globalFlags = Object.fromEntries(
|
|
132
|
+
Object.entries(parsed).filter(([key]) => key !== "_").map(([key, value]) => [key.replace(/^--/, ""), value])
|
|
133
|
+
);
|
|
134
|
+
return { globalFlags, commands };
|
|
135
|
+
}
|
|
136
|
+
function getArgType(flagType) {
|
|
137
|
+
switch (flagType) {
|
|
138
|
+
case "Boolean":
|
|
139
|
+
return Boolean;
|
|
140
|
+
case "Number":
|
|
141
|
+
return Number;
|
|
142
|
+
case "String":
|
|
143
|
+
return String;
|
|
144
|
+
default:
|
|
145
|
+
return String;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/commands/index.ts
|
|
150
|
+
var commands_exports = {};
|
|
151
|
+
__export(commands_exports, {
|
|
152
|
+
build: () => build,
|
|
153
|
+
components: () => components,
|
|
154
|
+
dev: () => dev2,
|
|
155
|
+
install: () => install2,
|
|
156
|
+
migrateme: () => migrateme
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// src/commands/build.ts
|
|
160
|
+
import { spawn } from "child_process";
|
|
161
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
162
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
163
|
+
async function build() {
|
|
164
|
+
const __filename2 = fileURLToPath2(import.meta.url);
|
|
165
|
+
const __dirname2 = dirname2(__filename2);
|
|
166
|
+
const buildScript = join2(__dirname2, "..", "dist", "build.js");
|
|
167
|
+
return new Promise((resolve2, reject) => {
|
|
168
|
+
const args = process.argv.slice(2);
|
|
169
|
+
const child = spawn("node", [buildScript, ...args], {
|
|
170
|
+
stdio: "inherit",
|
|
171
|
+
// This will show output in stdout
|
|
172
|
+
shell: true,
|
|
173
|
+
env: {
|
|
174
|
+
NODE_ENV: "production",
|
|
175
|
+
...process.env
|
|
176
|
+
// Pass through all existing environment variables
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
child.on("close", (code) => {
|
|
180
|
+
if (code === 0) {
|
|
181
|
+
resolve2(void 0);
|
|
182
|
+
} else {
|
|
183
|
+
reject(new Error(`Build process exited with code ${code}`));
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
child.on("error", (err) => {
|
|
187
|
+
reject(err);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/commands/dev.ts
|
|
193
|
+
import * as documan from "@xyd-js/documan";
|
|
194
|
+
async function dev2(options = {}) {
|
|
195
|
+
await documan.dev(options);
|
|
196
|
+
await new Promise(() => {
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/commands/install.ts
|
|
201
|
+
import * as documan2 from "@xyd-js/documan";
|
|
202
|
+
async function install2() {
|
|
203
|
+
await documan2.install();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/commands/migrateme/migrateme.ts
|
|
207
|
+
import { existsSync as existsSync3 } from "fs";
|
|
208
|
+
|
|
209
|
+
// src/commands/migrateme/utils.ts
|
|
210
|
+
import { cwd } from "process";
|
|
211
|
+
import { writeFile, mkdir, rm, readdir } from "fs/promises";
|
|
212
|
+
import { isAbsolute, join as join3, resolve } from "path";
|
|
213
|
+
import { exec } from "child_process";
|
|
214
|
+
import { promisify } from "util";
|
|
215
|
+
import { homedir } from "os";
|
|
216
|
+
function isGitHubRepo(url) {
|
|
217
|
+
const githubRawPattern = /^https:\/\/raw\.githubusercontent\.com\/([^\/]+)\/([^\/]+)\/([^\/]+)\/(.+)$/;
|
|
218
|
+
const githubRepoPattern = /^https:\/\/github\.com\/([^\/]+)\/([^\/]+)(?:\/.*)?$/;
|
|
219
|
+
return githubRawPattern.test(url) || githubRepoPattern.test(url);
|
|
220
|
+
}
|
|
221
|
+
function parseGitHubUrl(url) {
|
|
222
|
+
const rawUrlMatch = url.match(/^https:\/\/raw\.githubusercontent\.com\/([^\/]+)\/([^\/]+)\/([^\/]+)\/(.+)$/);
|
|
223
|
+
if (rawUrlMatch) {
|
|
224
|
+
const [, owner2, repo2, branch2, filePath] = rawUrlMatch;
|
|
225
|
+
return { owner: owner2, repo: repo2, branch: branch2, directory: filePath.split("/").slice(0, -1).join("/") };
|
|
226
|
+
}
|
|
227
|
+
const repoUrlMatch = url.match(/^https:\/\/github\.com\/([^\/]+)\/([^\/]+)(?:\/.*)?$/);
|
|
228
|
+
if (!repoUrlMatch) {
|
|
229
|
+
throw new Error("Invalid GitHub URL format");
|
|
230
|
+
}
|
|
231
|
+
const [, owner, repo] = repoUrlMatch;
|
|
232
|
+
const urlPath = url.replace(`https://github.com/${owner}/${repo}`, "");
|
|
233
|
+
const pathParts = urlPath.split("/").filter((part) => part.length > 0);
|
|
234
|
+
let branch = "main";
|
|
235
|
+
let directory;
|
|
236
|
+
if (pathParts.length >= 2 && pathParts[0] === "tree") {
|
|
237
|
+
branch = pathParts[1];
|
|
238
|
+
if (pathParts.length > 2) {
|
|
239
|
+
directory = pathParts.slice(2).join("/");
|
|
240
|
+
}
|
|
241
|
+
} else if (pathParts.length >= 1) {
|
|
242
|
+
branch = pathParts[0];
|
|
243
|
+
if (pathParts.length > 1) {
|
|
244
|
+
directory = pathParts.slice(1).join("/");
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return { owner, repo, branch, directory };
|
|
248
|
+
}
|
|
249
|
+
async function downloadGitHubRepo(docsUrl, flags) {
|
|
250
|
+
try {
|
|
251
|
+
console.log("GitHub repo detected, downloading entire repository...");
|
|
252
|
+
const { owner, repo, branch, directory } = parseGitHubUrl(docsUrl);
|
|
253
|
+
const repoUrl = `https://github.com/${owner}/${repo}`;
|
|
254
|
+
const downloadUrl = `https://github.com/${owner}/${repo}/archive/refs/heads/${branch}.zip`;
|
|
255
|
+
console.log(`Repository: ${repoUrl}`);
|
|
256
|
+
console.log(`Branch: ${branch}`);
|
|
257
|
+
if (directory) {
|
|
258
|
+
console.log(`Directory: ${directory}`);
|
|
259
|
+
}
|
|
260
|
+
console.log(`Download URL: ${downloadUrl}`);
|
|
261
|
+
const response = await fetch(downloadUrl);
|
|
262
|
+
if (!response.ok) {
|
|
263
|
+
throw new Error(`Failed to download repository: ${response.statusText}`);
|
|
264
|
+
}
|
|
265
|
+
const filename = `${repo}-${branch}.zip`;
|
|
266
|
+
console.log(`Downloading ${filename}...`);
|
|
267
|
+
const saveDir = flags.dir || cwd();
|
|
268
|
+
const savePath = join3(saveDir, filename);
|
|
269
|
+
try {
|
|
270
|
+
await mkdir(saveDir, { recursive: true });
|
|
271
|
+
} catch (error) {
|
|
272
|
+
}
|
|
273
|
+
const buffer = await response.arrayBuffer();
|
|
274
|
+
await writeFile(savePath, Buffer.from(buffer));
|
|
275
|
+
console.log(`Successfully downloaded repository: ${filename}`);
|
|
276
|
+
console.log(`Saved to: ${savePath}`);
|
|
277
|
+
console.log("Extracting ZIP file...");
|
|
278
|
+
const extractDir = join3(saveDir, `${repo}-${branch}`);
|
|
279
|
+
try {
|
|
280
|
+
await rm(extractDir, { recursive: true, force: true });
|
|
281
|
+
} catch (error) {
|
|
282
|
+
}
|
|
283
|
+
const execAsync2 = promisify(exec);
|
|
284
|
+
try {
|
|
285
|
+
console.log("Starting extraction...");
|
|
286
|
+
const tempExtractDir = join3(saveDir, "temp-extract");
|
|
287
|
+
console.log(`Extracting to temp dir: ${tempExtractDir}`);
|
|
288
|
+
await execAsync2(`unzip -o -q "${savePath}" -d "${tempExtractDir}"`);
|
|
289
|
+
console.log("ZIP extraction completed");
|
|
290
|
+
const tempFiles = await readdir(tempExtractDir, { withFileTypes: true });
|
|
291
|
+
const repoDir = tempFiles.find((file) => file.isDirectory());
|
|
292
|
+
if (repoDir) {
|
|
293
|
+
const sourceDir = join3(tempExtractDir, repoDir.name);
|
|
294
|
+
if (directory) {
|
|
295
|
+
const targetDir = join3(sourceDir, directory);
|
|
296
|
+
console.log(`Moving files from ${targetDir} to ${saveDir}`);
|
|
297
|
+
try {
|
|
298
|
+
await execAsync2(`test -d "${targetDir}"`);
|
|
299
|
+
} catch (error) {
|
|
300
|
+
throw new Error(`Directory '${directory}' not found in repository`);
|
|
301
|
+
}
|
|
302
|
+
await execAsync2(`cp -r "${targetDir}"/* "${saveDir}/"`);
|
|
303
|
+
console.log(`Files from '${directory}' directory copied successfully`);
|
|
304
|
+
} else {
|
|
305
|
+
console.log(`Moving files from ${sourceDir} to ${saveDir}`);
|
|
306
|
+
await execAsync2(`cp -r "${sourceDir}"/* "${saveDir}/"`);
|
|
307
|
+
console.log("Files copied successfully");
|
|
308
|
+
}
|
|
309
|
+
await rm(tempExtractDir, { recursive: true, force: true });
|
|
310
|
+
console.log("Temp directory cleaned up");
|
|
311
|
+
} else {
|
|
312
|
+
throw new Error("Could not find repository directory in ZIP");
|
|
313
|
+
}
|
|
314
|
+
} catch (error) {
|
|
315
|
+
console.error("Error during extraction:", error);
|
|
316
|
+
try {
|
|
317
|
+
console.log("Trying tar fallback...");
|
|
318
|
+
await execAsync2(`tar -xf "${savePath}" --strip-components=1 -C "${saveDir}"`);
|
|
319
|
+
console.log("Tar extraction completed");
|
|
320
|
+
} catch (tarError) {
|
|
321
|
+
console.warn("Could not extract ZIP file automatically. Please extract manually.");
|
|
322
|
+
console.log(`ZIP file saved at: ${savePath}`);
|
|
323
|
+
return saveDir;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
await rm(savePath);
|
|
327
|
+
console.log(`Successfully extracted to: ${saveDir}`);
|
|
328
|
+
return saveDir;
|
|
329
|
+
} catch (error) {
|
|
330
|
+
console.error("Error downloading repository:", error);
|
|
331
|
+
throw error;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function resolveResourcePath(resource) {
|
|
335
|
+
if (resource === "." || resource === "./") {
|
|
336
|
+
return cwd();
|
|
337
|
+
}
|
|
338
|
+
if (resource.startsWith("~/")) {
|
|
339
|
+
return resolve(homedir(), resource.slice(2));
|
|
340
|
+
}
|
|
341
|
+
if (isAbsolute(resource)) {
|
|
342
|
+
return resource;
|
|
343
|
+
}
|
|
344
|
+
return resolve(cwd(), resource);
|
|
345
|
+
}
|
|
346
|
+
async function cleanDirectory(dirPath) {
|
|
347
|
+
try {
|
|
348
|
+
console.log("Cleaning folder before processing...");
|
|
349
|
+
const files = await readdir(dirPath, { withFileTypes: true });
|
|
350
|
+
for (const file of files) {
|
|
351
|
+
const filePath = join3(dirPath, file.name);
|
|
352
|
+
if (file.isDirectory()) {
|
|
353
|
+
await rm(filePath, { recursive: true, force: true });
|
|
354
|
+
} else {
|
|
355
|
+
await rm(filePath, { force: true });
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
console.log("Directory cleaned successfully");
|
|
359
|
+
} catch (error) {
|
|
360
|
+
console.warn("Warning: Could not clean directory:", error);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// src/commands/migrateme/cli.ts
|
|
365
|
+
import { cwd as cwd2 } from "process";
|
|
366
|
+
import readline from "readline";
|
|
367
|
+
async function askForConfirmation(question) {
|
|
368
|
+
const rl = readline.createInterface({
|
|
369
|
+
input: process.stdin,
|
|
370
|
+
output: process.stdout
|
|
371
|
+
});
|
|
372
|
+
return new Promise((resolve2) => {
|
|
373
|
+
const coloredQuestion = `\x1B[36m${question}\x1B[0m (y/n): `;
|
|
374
|
+
rl.question(coloredQuestion, (answer) => {
|
|
375
|
+
rl.close();
|
|
376
|
+
const normalizedAnswer = answer.toLowerCase().trim();
|
|
377
|
+
resolve2(normalizedAnswer === "y" || normalizedAnswer === "yes");
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
async function askForClean(migratemeFlags) {
|
|
382
|
+
const saveDir = migratemeFlags.dir || cwd2();
|
|
383
|
+
const shouldClean = await askForConfirmation("Should the folder be cleaned before processing?");
|
|
384
|
+
if (shouldClean) {
|
|
385
|
+
await cleanDirectory(saveDir);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
async function askForStart() {
|
|
389
|
+
const shouldStart = await askForConfirmation("Do you want to start the migration?");
|
|
390
|
+
if (!shouldStart) {
|
|
391
|
+
console.log("Migration cancelled");
|
|
392
|
+
process.exit(0);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/commands/migrateme/migration.ts
|
|
397
|
+
import { readdir as readdir3 } from "fs/promises";
|
|
398
|
+
import { existsSync as existsSync2 } from "fs";
|
|
399
|
+
import { promisify as promisify3 } from "util";
|
|
400
|
+
import { exec as exec3 } from "child_process";
|
|
401
|
+
|
|
402
|
+
// src/commands/migrateme/mintlify/mintlify.ts
|
|
403
|
+
import { join as join4, relative, sep } from "path";
|
|
404
|
+
import { exec as exec2 } from "child_process";
|
|
405
|
+
import { promisify as promisify2 } from "util";
|
|
406
|
+
import { existsSync } from "fs";
|
|
407
|
+
import { readdir as readdir2, readFile, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
408
|
+
import { createProcessor } from "@mdx-js/mdx";
|
|
409
|
+
import { remark } from "remark";
|
|
410
|
+
import remarkDirective from "remark-directive";
|
|
411
|
+
import remarkStringify from "remark-stringify";
|
|
412
|
+
async function isMintlify(docsPath, fileName) {
|
|
413
|
+
if (!MintlifyMigrator.validMintlifyJsonFile(fileName)) return false;
|
|
414
|
+
console.log("Found docs.json file, checking if it's Mintlify...");
|
|
415
|
+
const docsJsonPath = join4(docsPath, fileName);
|
|
416
|
+
const content = await readFile(docsJsonPath, "utf-8");
|
|
417
|
+
let data = null;
|
|
418
|
+
try {
|
|
419
|
+
data = JSON.parse(content);
|
|
420
|
+
} catch (_) {
|
|
421
|
+
}
|
|
422
|
+
if (MintlifyMigrator.isMintlifyJson(data)) {
|
|
423
|
+
console.log("\u2705 Mintlify framework detected!");
|
|
424
|
+
return true;
|
|
425
|
+
} else {
|
|
426
|
+
console.log("\u274C docs.json found but not a valid Mintlify configuration");
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
async function mintlifyMigrator(docsPath, options) {
|
|
431
|
+
return new MintlifyMigrator(docsPath, options).run();
|
|
432
|
+
}
|
|
433
|
+
var sh = promisify2(exec2);
|
|
434
|
+
var Logger = class {
|
|
435
|
+
constructor(enabled = true, verbose = false) {
|
|
436
|
+
this.enabled = enabled;
|
|
437
|
+
this.stepCount = 0;
|
|
438
|
+
this.totalSteps = 0;
|
|
439
|
+
this.errors = [];
|
|
440
|
+
this.docsPath = "";
|
|
441
|
+
this.info = (...args) => this.enabled && console.log(" \u2139\uFE0F ", ...args);
|
|
442
|
+
this.warn = (...args) => this.enabled && console.warn(" \u26A0\uFE0F ", ...args);
|
|
443
|
+
this.error = (...args) => {
|
|
444
|
+
console.error(" \u274C", ...args);
|
|
445
|
+
this.errors.push({ error: args.join(" ") });
|
|
446
|
+
};
|
|
447
|
+
this.errorVerbose = (error, context) => {
|
|
448
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
449
|
+
const fullError = error instanceof Error ? error.stack || String(error) : String(error);
|
|
450
|
+
console.error(" \u274C", context ? `${context}: ${errorMessage}` : errorMessage);
|
|
451
|
+
if (this.verbose && fullError !== errorMessage) {
|
|
452
|
+
console.error(" \u{1F50D} Full error details:");
|
|
453
|
+
console.error(" " + fullError.split("\n").join("\n "));
|
|
454
|
+
}
|
|
455
|
+
let file;
|
|
456
|
+
if (context) {
|
|
457
|
+
const fileMatch = context.match(/Failed to migrate (.+)$/);
|
|
458
|
+
if (fileMatch) {
|
|
459
|
+
file = fileMatch[1];
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
this.errors.push({ file, error: context ? `${context}: ${errorMessage}` : errorMessage });
|
|
463
|
+
};
|
|
464
|
+
this.debug = (...args) => this.enabled && console.log(" \u{1F50D}", ...args);
|
|
465
|
+
this.success = (...args) => this.enabled && console.log(" \u2705", ...args);
|
|
466
|
+
this.progress = (...args) => this.enabled && console.log(" \u{1F4DD}", ...args);
|
|
467
|
+
this.file = (...args) => this.enabled && console.log(" \u{1F4C4}", ...args);
|
|
468
|
+
this.resource = (...args) => this.enabled && console.log(" \u{1F5BC}\uFE0F ", ...args);
|
|
469
|
+
this.details = (summary, content) => {
|
|
470
|
+
if (!this.verbose) return;
|
|
471
|
+
console.log(` \u{1F4CB} ${summary}:`);
|
|
472
|
+
const lines = Array.isArray(content) ? content : content.split("\n");
|
|
473
|
+
lines.forEach((line) => {
|
|
474
|
+
console.log(" " + line);
|
|
475
|
+
});
|
|
476
|
+
console.log(" ");
|
|
477
|
+
};
|
|
478
|
+
this.verbose = verbose;
|
|
479
|
+
}
|
|
480
|
+
setDocsPath(docsPath) {
|
|
481
|
+
this.docsPath = docsPath;
|
|
482
|
+
}
|
|
483
|
+
startMigration(totalSteps) {
|
|
484
|
+
this.totalSteps = totalSteps;
|
|
485
|
+
this.stepCount = 0;
|
|
486
|
+
this.errors = [];
|
|
487
|
+
console.log("\n\u{1F680} Starting Mintlify to xyd migration...");
|
|
488
|
+
console.log("=".repeat(50));
|
|
489
|
+
}
|
|
490
|
+
step(message) {
|
|
491
|
+
this.stepCount++;
|
|
492
|
+
const progress = `[${this.stepCount}/${this.totalSteps}]`;
|
|
493
|
+
console.log(`
|
|
494
|
+
${progress} ${message}`);
|
|
495
|
+
}
|
|
496
|
+
addError(file, error) {
|
|
497
|
+
this.errors.push({ file, error });
|
|
498
|
+
}
|
|
499
|
+
complete() {
|
|
500
|
+
console.log("\n" + "=".repeat(50));
|
|
501
|
+
if (this.errors.length > 0) {
|
|
502
|
+
console.log("\u26A0\uFE0F Migration completed with errors:");
|
|
503
|
+
console.log("=".repeat(50));
|
|
504
|
+
console.log(` ${this.errors.length} error(s) encountered:`);
|
|
505
|
+
this.errors.forEach((err, index) => {
|
|
506
|
+
if (err.file) {
|
|
507
|
+
console.log(` ${index + 1}. File: ${err.file}`);
|
|
508
|
+
console.log(` Error: ${err.error}`);
|
|
509
|
+
} else {
|
|
510
|
+
console.log(` ${index + 1}. ${err.error}`);
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
console.log("\n Some files may not have been migrated correctly.");
|
|
514
|
+
console.log(" Please review the errors above and fix any issues.");
|
|
515
|
+
if (!this.verbose) {
|
|
516
|
+
console.log(" Run with --verbose flag to see detailed error information.");
|
|
517
|
+
}
|
|
518
|
+
} else {
|
|
519
|
+
console.log("\u{1F389} Migration completed successfully!");
|
|
520
|
+
}
|
|
521
|
+
console.log("\n\u{1F4D6} Next steps:");
|
|
522
|
+
console.log(` \u2022 cd "${this.docsPath}"`);
|
|
523
|
+
console.log(" \u2022 Run `xyd` to start the development server");
|
|
524
|
+
console.log(" \u2022 Run `xyd build` to build static HTML files");
|
|
525
|
+
console.log(" \u2022 Visit http://localhost:3000 to preview your docs");
|
|
526
|
+
console.log("\n\u{1F4A1} Learn more:");
|
|
527
|
+
console.log(" \u2022 Visit https://xyd.dev for documentation and examples");
|
|
528
|
+
console.log(" \u2022 \u2B50 Star us on GitHub: https://github.com/livesession/xyd");
|
|
529
|
+
console.log(" \u2022 \u{1F680} Help us grow by giving us a star! \u2728");
|
|
530
|
+
console.log("=".repeat(50) + "\n");
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
var MintlifyMigrator = class {
|
|
534
|
+
constructor(docsPath, options = {}) {
|
|
535
|
+
this.docsPath = docsPath;
|
|
536
|
+
this.options = options;
|
|
537
|
+
this.docsJsonPath = "";
|
|
538
|
+
this.publicDir = "";
|
|
539
|
+
this.logger = new Logger(true, options.verbose || false);
|
|
540
|
+
this.logger.setDocsPath(docsPath);
|
|
541
|
+
}
|
|
542
|
+
static isMintlifyJson(data) {
|
|
543
|
+
return !!(data && typeof data === "object" && data.$schema === "https://mintlify.com/docs.json");
|
|
544
|
+
}
|
|
545
|
+
async run() {
|
|
546
|
+
this.logger.startMigration(5);
|
|
547
|
+
this.logger.step("\u{1F50D}Analyzing Mintlify configuration");
|
|
548
|
+
this.docsJsonPath = join4(this.docsPath, "docs.json");
|
|
549
|
+
try {
|
|
550
|
+
if (!existsSync(this.docsJsonPath)) {
|
|
551
|
+
this.docsJsonPath = join4(this.docsPath, "mint.json");
|
|
552
|
+
}
|
|
553
|
+
} catch (_) {
|
|
554
|
+
this.docsJsonPath = join4(this.docsPath, "mint.json");
|
|
555
|
+
}
|
|
556
|
+
this.docsJson = JSON.parse(await readFile(this.docsJsonPath, "utf-8"));
|
|
557
|
+
if (!this.docsJson) {
|
|
558
|
+
this.logger.error("Invalid Mintlify configuration: missing or incorrect.");
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
this.logger.success("Configuration file found and parsed");
|
|
562
|
+
this.logger.step("\u2699\uFE0FPreparing xyd settings and directories");
|
|
563
|
+
this.xydSettings = this.preSettings(this.docsJson);
|
|
564
|
+
await this.ensurePublicDir();
|
|
565
|
+
this.logger.success("Settings prepared and public directory ready");
|
|
566
|
+
this.logger.step("\u{1F504}Converting Mintlify configuration to xyd format");
|
|
567
|
+
await this.mintlifyDocsJsonToXydSettings();
|
|
568
|
+
this.logger.success("Configuration converted successfully");
|
|
569
|
+
this.logger.step("\u{1F4BE}Writing xyd configuration file");
|
|
570
|
+
await writeFile2(join4(this.docsPath, "docs.json"), JSON.stringify(this.xydSettings, null, 2));
|
|
571
|
+
this.logger.success("Configuration file written");
|
|
572
|
+
this.logger.step("\u{1F4DD}Migrating content files (MDX \u2192 Markdown)");
|
|
573
|
+
await this.migrateContent();
|
|
574
|
+
this.logger.success("Content migration completed");
|
|
575
|
+
this.logger.complete();
|
|
576
|
+
}
|
|
577
|
+
// ---------------------------------------------------------------------------
|
|
578
|
+
// Stage 0: bootstrap
|
|
579
|
+
// ---------------------------------------------------------------------------
|
|
580
|
+
preSettings(docsJson) {
|
|
581
|
+
let theme = "gusto";
|
|
582
|
+
if (docsJson.theme && docsJson.theme === "maple") {
|
|
583
|
+
theme = "gusto";
|
|
584
|
+
} else if (docsJson.theme && docsJson.theme === "mint") {
|
|
585
|
+
theme = "solar";
|
|
586
|
+
}
|
|
587
|
+
return {
|
|
588
|
+
theme: {
|
|
589
|
+
name: theme,
|
|
590
|
+
icons: {
|
|
591
|
+
library: [
|
|
592
|
+
{
|
|
593
|
+
name: "fa6-solid",
|
|
594
|
+
// TODO: mintlify supports lucide icons too
|
|
595
|
+
default: true
|
|
596
|
+
}
|
|
597
|
+
]
|
|
598
|
+
},
|
|
599
|
+
writer: {
|
|
600
|
+
maxTocDepth: 4
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
webeditor: {
|
|
604
|
+
header: []
|
|
605
|
+
},
|
|
606
|
+
navigation: {
|
|
607
|
+
tabs: [],
|
|
608
|
+
sidebar: []
|
|
609
|
+
},
|
|
610
|
+
components: {}
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
async ensurePublicDir() {
|
|
614
|
+
this.publicDir = join4(this.docsPath, "public");
|
|
615
|
+
try {
|
|
616
|
+
await readdir2(this.publicDir);
|
|
617
|
+
this.logger.info("Public directory already exists");
|
|
618
|
+
} catch {
|
|
619
|
+
this.logger.progress("Creating public directory...");
|
|
620
|
+
await sh(`mkdir -p "${this.publicDir}"`);
|
|
621
|
+
this.logger.success("Public directory created");
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
// ---------------------------------------------------------------------------
|
|
625
|
+
// Stage 1: docs.json → settings
|
|
626
|
+
// ---------------------------------------------------------------------------
|
|
627
|
+
async mintlifyDocsJsonToXydSettings() {
|
|
628
|
+
this.migrateNavigation();
|
|
629
|
+
this.migrateColors();
|
|
630
|
+
await this.migrateLogo();
|
|
631
|
+
await this.migrateFavicon();
|
|
632
|
+
await this.migratePublicResources();
|
|
633
|
+
this.migrateNavbarLinks();
|
|
634
|
+
this.migrateFooter();
|
|
635
|
+
this.migrateRedirects();
|
|
636
|
+
this.migrateSeo();
|
|
637
|
+
}
|
|
638
|
+
// ---------------------------------------------------------------------------
|
|
639
|
+
// Navigation
|
|
640
|
+
// ---------------------------------------------------------------------------
|
|
641
|
+
migrateNavigation() {
|
|
642
|
+
const docsJson = this.docsJson;
|
|
643
|
+
const xydSettings = this.xydSettings;
|
|
644
|
+
const migrateNav = (navs) => {
|
|
645
|
+
for (const nav of navs) {
|
|
646
|
+
const hasHref = "href" in nav && nav.href;
|
|
647
|
+
const hasGroups = "groups" in nav && nav.groups || "pages" in nav && nav.pages && this.hasNestedGroups(nav.pages);
|
|
648
|
+
const hasPages = "pages" in nav && nav.pages;
|
|
649
|
+
let title = "";
|
|
650
|
+
if ("group" in nav) title = nav.group;
|
|
651
|
+
else if ("tab" in nav) title = nav.tab;
|
|
652
|
+
else if ("dropdown" in nav) title = nav.dropdown;
|
|
653
|
+
else if ("anchor" in nav) title = nav.anchor;
|
|
654
|
+
const headerEntry = {
|
|
655
|
+
title,
|
|
656
|
+
icon: "icon" in nav ? typeof nav.icon === "string" ? nav.icon : nav.icon?.name : ""
|
|
657
|
+
};
|
|
658
|
+
if (hasHref && !hasGroups && !hasPages) {
|
|
659
|
+
headerEntry.href = nav.href;
|
|
660
|
+
headerEntry.float = "right";
|
|
661
|
+
} else if (hasGroups) {
|
|
662
|
+
let route = title.toLowerCase().replace(/\s+/g, "-");
|
|
663
|
+
if ("pages" in nav && nav.pages && !("groups" in nav && nav.groups)) {
|
|
664
|
+
if (this.isFlatStructure(nav.pages)) {
|
|
665
|
+
headerEntry.page = "";
|
|
666
|
+
const flatSidebarPages = [];
|
|
667
|
+
for (const pageOrGroup of nav.pages) {
|
|
668
|
+
if (typeof pageOrGroup === "string") {
|
|
669
|
+
if (pageOrGroup === "index") {
|
|
670
|
+
this.logger.warn("currently skipping index page in sidebar");
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
flatSidebarPages.push(pageOrGroup);
|
|
674
|
+
} else if (typeof pageOrGroup === "object" && pageOrGroup.group && pageOrGroup.pages) {
|
|
675
|
+
for (const nestedPage of pageOrGroup.pages) {
|
|
676
|
+
if (nestedPage === "index") {
|
|
677
|
+
this.logger.warn("currently skipping index page in sidebar");
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
flatSidebarPages.push(nestedPage);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
xydSettings.navigation.sidebar.push(...flatSidebarPages);
|
|
685
|
+
xydSettings.navigation.tabs.push(headerEntry);
|
|
686
|
+
continue;
|
|
687
|
+
} else {
|
|
688
|
+
const firstPage = nav.pages[0];
|
|
689
|
+
if (typeof firstPage === "string") route = firstPage;
|
|
690
|
+
else if (typeof firstPage === "object" && firstPage.pages && firstPage.pages[0])
|
|
691
|
+
route = firstPage.pages[0];
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
headerEntry.page = route;
|
|
695
|
+
const sidebarPages = [];
|
|
696
|
+
if ("groups" in nav && nav.groups) {
|
|
697
|
+
for (const group of nav.groups ?? []) {
|
|
698
|
+
if ("pages" in group && group.pages) {
|
|
699
|
+
const pages = [];
|
|
700
|
+
for (const pageOrGroup of group.pages) {
|
|
701
|
+
if (pageOrGroup === "index") {
|
|
702
|
+
this.logger.warn("currently skipping index page in sidebar");
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
pages.push(pageOrGroup);
|
|
706
|
+
}
|
|
707
|
+
sidebarPages.push({ group: group.group, pages });
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
} else if ("pages" in nav && nav.pages) {
|
|
711
|
+
for (const pageOrGroup of nav.pages) {
|
|
712
|
+
if (typeof pageOrGroup === "string") {
|
|
713
|
+
if (pageOrGroup === "index") {
|
|
714
|
+
this.logger.warn("currently skipping index page in sidebar");
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
sidebarPages.push(pageOrGroup);
|
|
718
|
+
} else if (typeof pageOrGroup === "object" && pageOrGroup.group && pageOrGroup.pages) {
|
|
719
|
+
sidebarPages.push({ group: pageOrGroup.group, pages: pageOrGroup.pages });
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
const isValidRoute = this.validateRoutePages(route, sidebarPages);
|
|
724
|
+
if (isValidRoute) {
|
|
725
|
+
xydSettings.navigation.sidebar.push({ route, pages: sidebarPages });
|
|
726
|
+
} else {
|
|
727
|
+
xydSettings.navigation.sidebar.push(...sidebarPages);
|
|
728
|
+
headerEntry.page = "";
|
|
729
|
+
const firstPage = this.findFirstPage(sidebarPages);
|
|
730
|
+
if (firstPage) {
|
|
731
|
+
headerEntry.href = "/" + firstPage;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
} else if (hasPages && !hasGroups) {
|
|
735
|
+
headerEntry.page = "";
|
|
736
|
+
if ("pages" in nav && nav.pages && nav.pages.length > 0) {
|
|
737
|
+
const firstPage = nav.pages[0];
|
|
738
|
+
if (typeof firstPage === "string") {
|
|
739
|
+
headerEntry.href = firstPage;
|
|
740
|
+
}
|
|
741
|
+
} else {
|
|
742
|
+
}
|
|
743
|
+
xydSettings.navigation.sidebar.push(title.toLowerCase().replace(/\s+/g, "-"));
|
|
744
|
+
}
|
|
745
|
+
xydSettings.navigation.tabs.push(headerEntry);
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
if ("groups" in docsJson.navigation) {
|
|
749
|
+
this.logger.progress("Migrating navigation groups...");
|
|
750
|
+
migrateNav(docsJson.navigation.groups);
|
|
751
|
+
this.logger.success("Navigation groups migrated");
|
|
752
|
+
}
|
|
753
|
+
if ("tabs" in docsJson.navigation) {
|
|
754
|
+
this.logger.progress("Migrating navigation tabs...");
|
|
755
|
+
migrateNav(docsJson.navigation.tabs);
|
|
756
|
+
this.logger.success("Navigation tabs migrated");
|
|
757
|
+
}
|
|
758
|
+
if ("anchors" in docsJson.navigation) {
|
|
759
|
+
this.logger.progress("Migrating navigation anchors...");
|
|
760
|
+
migrateNav(docsJson.navigation.anchors);
|
|
761
|
+
this.logger.success("Navigation anchors migrated");
|
|
762
|
+
}
|
|
763
|
+
if ("dropdowns" in docsJson.navigation) {
|
|
764
|
+
this.logger.progress("Migrating navigation dropdowns...");
|
|
765
|
+
migrateNav(docsJson.navigation.dropdowns);
|
|
766
|
+
this.logger.success("Navigation dropdowns migrated");
|
|
767
|
+
}
|
|
768
|
+
if ("versions" in docsJson.navigation) {
|
|
769
|
+
this.logger.warn("Version navigation is not currently supported");
|
|
770
|
+
this.logger.info("Only the first version navigation will be created");
|
|
771
|
+
this.logger.info("\u{1F4CB} Roadmap: https://github.com/orgs/livesession/projects/4");
|
|
772
|
+
if (docsJson.navigation?.versions?.length) {
|
|
773
|
+
const firstVersion = docsJson.navigation.versions[0];
|
|
774
|
+
if ("groups" in firstVersion && firstVersion.groups) {
|
|
775
|
+
this.logger.progress("Migrating first version navigation...");
|
|
776
|
+
migrateNav([firstVersion]);
|
|
777
|
+
this.logger.success("First version navigation migrated");
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
// ---------------------------------------------------------------------------
|
|
783
|
+
// Theme & branding
|
|
784
|
+
// ---------------------------------------------------------------------------
|
|
785
|
+
migrateColors() {
|
|
786
|
+
var _a;
|
|
787
|
+
const { docsJson, xydSettings } = this;
|
|
788
|
+
if (!docsJson.colors) return;
|
|
789
|
+
this.logger.progress("Migrating theme colors...");
|
|
790
|
+
(_a = xydSettings.theme).appearance || (_a.appearance = {});
|
|
791
|
+
xydSettings.theme.appearance.colors = {
|
|
792
|
+
primary: docsJson.colors.primary,
|
|
793
|
+
dark: docsJson.colors.dark,
|
|
794
|
+
light: docsJson.colors.light
|
|
795
|
+
};
|
|
796
|
+
this.logger.success("Theme colors migrated");
|
|
797
|
+
}
|
|
798
|
+
async migrateLogo() {
|
|
799
|
+
const { docsJson } = this;
|
|
800
|
+
if (!docsJson.logo) return;
|
|
801
|
+
this.logger.progress("Migrating logo...");
|
|
802
|
+
if (typeof docsJson.logo === "string") {
|
|
803
|
+
this.xydSettings.theme.logo = await this.moveResourceToPublic(docsJson.logo);
|
|
804
|
+
} else if (typeof docsJson.logo === "object") {
|
|
805
|
+
this.xydSettings.theme.logo = {
|
|
806
|
+
light: await this.moveResourceToPublic(docsJson.logo.light),
|
|
807
|
+
dark: await this.moveResourceToPublic(docsJson.logo.dark),
|
|
808
|
+
href: docsJson.logo.href
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
this.logger.success("Logo migrated");
|
|
812
|
+
}
|
|
813
|
+
async migrateFavicon() {
|
|
814
|
+
const { docsJson } = this;
|
|
815
|
+
if (!docsJson.favicon) return;
|
|
816
|
+
this.logger.progress("Migrating favicon...");
|
|
817
|
+
if (typeof docsJson.favicon === "string") {
|
|
818
|
+
this.xydSettings.theme.favicon = await this.moveResourceToPublic(docsJson.favicon);
|
|
819
|
+
} else if (typeof docsJson.favicon === "object") {
|
|
820
|
+
const lightFavicon = await this.moveResourceToPublic(docsJson.favicon.light);
|
|
821
|
+
const darkFavicon = await this.moveResourceToPublic(docsJson.favicon.dark);
|
|
822
|
+
this.xydSettings.theme.favicon = lightFavicon || darkFavicon;
|
|
823
|
+
}
|
|
824
|
+
this.logger.success("Favicon migrated");
|
|
825
|
+
}
|
|
826
|
+
migrateNavbarLinks() {
|
|
827
|
+
const { docsJson, xydSettings } = this;
|
|
828
|
+
if (!docsJson.navbar?.links) return;
|
|
829
|
+
this.logger.progress("Migrating navbar links...");
|
|
830
|
+
xydSettings.webeditor.header = docsJson.navbar.links.map((link) => ({
|
|
831
|
+
title: link.label,
|
|
832
|
+
href: link.href,
|
|
833
|
+
icon: typeof link.icon === "string" ? link.icon : link.icon?.name,
|
|
834
|
+
float: "right"
|
|
835
|
+
}));
|
|
836
|
+
this.logger.success("Navbar links migrated");
|
|
837
|
+
}
|
|
838
|
+
migrateFooter() {
|
|
839
|
+
const { docsJson, xydSettings } = this;
|
|
840
|
+
if (!docsJson.footer) return;
|
|
841
|
+
this.logger.progress("Migrating footer...");
|
|
842
|
+
const xydSocials = {};
|
|
843
|
+
if (docsJson.footer.socials) {
|
|
844
|
+
const supported = [
|
|
845
|
+
"x",
|
|
846
|
+
"facebook",
|
|
847
|
+
"youtube",
|
|
848
|
+
"discord",
|
|
849
|
+
"slack",
|
|
850
|
+
"github",
|
|
851
|
+
"linkedin",
|
|
852
|
+
"instagram",
|
|
853
|
+
"hackernews",
|
|
854
|
+
"medium",
|
|
855
|
+
"telegram",
|
|
856
|
+
"bluesky",
|
|
857
|
+
"reddit"
|
|
858
|
+
];
|
|
859
|
+
for (const [platform, url] of Object.entries(docsJson.footer.socials)) {
|
|
860
|
+
if (supported.includes(platform)) xydSocials[platform] = url;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
xydSettings.components.footer = {
|
|
864
|
+
social: Object.keys(xydSocials).length > 0 ? xydSocials : void 0,
|
|
865
|
+
links: docsJson.footer.links?.map((group) => ({
|
|
866
|
+
header: group.header || "",
|
|
867
|
+
items: group.items.map((item) => ({ label: item.label, href: item.href }))
|
|
868
|
+
}))
|
|
869
|
+
};
|
|
870
|
+
this.logger.success("Footer migrated");
|
|
871
|
+
}
|
|
872
|
+
migrateRedirects() {
|
|
873
|
+
if (this.docsJson.redirects) {
|
|
874
|
+
this.logger.warn("Redirects are not currently supported in xyd");
|
|
875
|
+
this.logger.info("Redirects will be skipped");
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
migrateSeo() {
|
|
879
|
+
if (!this.docsJson.seo) return;
|
|
880
|
+
this.logger.progress("Migrating SEO settings...");
|
|
881
|
+
this.xydSettings.seo = { metatags: this.docsJson.seo.metatags || void 0 };
|
|
882
|
+
this.logger.success("SEO settings migrated");
|
|
883
|
+
}
|
|
884
|
+
// ---------------------------------------------------------------------------
|
|
885
|
+
// Assets
|
|
886
|
+
// ---------------------------------------------------------------------------
|
|
887
|
+
async moveResourceToPublic(resourcePath) {
|
|
888
|
+
if (!resourcePath) return "";
|
|
889
|
+
const cleanPath = resourcePath.startsWith("/") ? resourcePath.slice(1) : resourcePath;
|
|
890
|
+
if (cleanPath.startsWith("public/")) return `/${cleanPath}`;
|
|
891
|
+
const sourcePath = join4(this.docsPath, cleanPath);
|
|
892
|
+
try {
|
|
893
|
+
await readFile(sourcePath);
|
|
894
|
+
const relativePath = cleanPath;
|
|
895
|
+
const destPath = join4(this.publicDir, relativePath);
|
|
896
|
+
const destDir = destPath.substring(0, destPath.lastIndexOf("/"));
|
|
897
|
+
try {
|
|
898
|
+
await readdir2(destDir);
|
|
899
|
+
} catch {
|
|
900
|
+
await sh(`mkdir -p "${destDir}"`);
|
|
901
|
+
}
|
|
902
|
+
await sh(`mv "${sourcePath}" "${destPath}"`);
|
|
903
|
+
this.logger.resource(`Moved: ${cleanPath}`);
|
|
904
|
+
return `/public/${relativePath}`;
|
|
905
|
+
} catch (_) {
|
|
906
|
+
this.logger.info(`Resource not found or already in public: ${cleanPath}`);
|
|
907
|
+
return resourcePath;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
async migratePublicResources() {
|
|
911
|
+
this.logger.progress("Scanning for image resources...");
|
|
912
|
+
const imageExtensions = /* @__PURE__ */ new Set([".svg", ".png", ".jpg", ".jpeg", ".gif", ".webp", ".ico"]);
|
|
913
|
+
let movedCount = 0;
|
|
914
|
+
let skippedCount = 0;
|
|
915
|
+
const scan = async (currentDir) => {
|
|
916
|
+
const items = await readdir2(currentDir, { withFileTypes: true });
|
|
917
|
+
for (const item of items) {
|
|
918
|
+
const fullPath = join4(currentDir, item.name);
|
|
919
|
+
if (item.isDirectory()) {
|
|
920
|
+
if (!(/* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", "public"])).has(item.name)) {
|
|
921
|
+
await scan(fullPath);
|
|
922
|
+
}
|
|
923
|
+
} else if (item.isFile()) {
|
|
924
|
+
const dot = item.name.lastIndexOf(".");
|
|
925
|
+
const ext = dot >= 0 ? item.name.toLowerCase().substring(dot) : "";
|
|
926
|
+
if (!imageExtensions.has(ext)) continue;
|
|
927
|
+
try {
|
|
928
|
+
const rel = relative(this.docsPath, fullPath).split(sep).join("/");
|
|
929
|
+
const destPath = join4(this.publicDir, rel);
|
|
930
|
+
const destDir = destPath.substring(0, destPath.lastIndexOf("/"));
|
|
931
|
+
try {
|
|
932
|
+
await readdir2(destDir);
|
|
933
|
+
} catch {
|
|
934
|
+
await sh(`mkdir -p "${destDir}"`);
|
|
935
|
+
}
|
|
936
|
+
try {
|
|
937
|
+
await readFile(destPath);
|
|
938
|
+
this.logger.info(`Image already exists in public: ${rel}`);
|
|
939
|
+
skippedCount++;
|
|
940
|
+
} catch {
|
|
941
|
+
await sh(`mv "${fullPath}" "${destPath}"`);
|
|
942
|
+
this.logger.resource(`Moved: ${rel}`);
|
|
943
|
+
movedCount++;
|
|
944
|
+
}
|
|
945
|
+
} catch (error) {
|
|
946
|
+
this.logger.errorVerbose(error, `Error moving image ${item.name}`);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
await scan(this.docsPath);
|
|
952
|
+
if (movedCount > 0 || skippedCount > 0) {
|
|
953
|
+
this.logger.success(`Public resources migrated: ${movedCount} moved, ${skippedCount} already existed`);
|
|
954
|
+
} else {
|
|
955
|
+
this.logger.info("No image resources found to migrate");
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
// ---------------------------------------------------------------------------
|
|
959
|
+
// Content (.mdx → .md)
|
|
960
|
+
// ---------------------------------------------------------------------------
|
|
961
|
+
async migrateContent() {
|
|
962
|
+
this.logger.progress("Scanning for MDX files...");
|
|
963
|
+
const mdxFiles = await this.findMdxFiles(this.docsPath);
|
|
964
|
+
this.logger.info(`Found ${mdxFiles.length} MDX files to migrate`);
|
|
965
|
+
let successCount = 0;
|
|
966
|
+
let errorCount = 0;
|
|
967
|
+
for (const mdxFile of mdxFiles) {
|
|
968
|
+
try {
|
|
969
|
+
await this.migrateMdxFile(mdxFile);
|
|
970
|
+
successCount++;
|
|
971
|
+
} catch (error) {
|
|
972
|
+
errorCount++;
|
|
973
|
+
const relativePath = relative(this.docsPath, mdxFile);
|
|
974
|
+
this.logger.errorVerbose(error, `Failed to migrate ${relativePath}`);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
if (errorCount > 0) {
|
|
978
|
+
this.logger.warn(`Content migration completed with ${errorCount} error(s)`);
|
|
979
|
+
this.logger.success(`${successCount} files migrated successfully`);
|
|
980
|
+
} else {
|
|
981
|
+
this.logger.success(`All ${successCount} files migrated successfully`);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
async findMdxFiles(dir) {
|
|
985
|
+
const found = [];
|
|
986
|
+
const scan = async (currentDir) => {
|
|
987
|
+
const items = await readdir2(currentDir, { withFileTypes: true });
|
|
988
|
+
for (const item of items) {
|
|
989
|
+
const fullPath = join4(currentDir, item.name);
|
|
990
|
+
if (item.isDirectory()) {
|
|
991
|
+
if (!["node_modules", ".git", "dist", "build"].includes(item.name)) {
|
|
992
|
+
await scan(fullPath);
|
|
993
|
+
}
|
|
994
|
+
} else if (item.isFile() && item.name.endsWith(".mdx")) {
|
|
995
|
+
found.push(fullPath);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
await scan(dir);
|
|
1000
|
+
return found;
|
|
1001
|
+
}
|
|
1002
|
+
async migrateMdxFile(filePath) {
|
|
1003
|
+
const content = await readFile(filePath, "utf-8");
|
|
1004
|
+
try {
|
|
1005
|
+
const converted = await this.convertMDXComponents(content, filePath);
|
|
1006
|
+
const newFilePath = filePath.replace(/\.mdx$/, ".md");
|
|
1007
|
+
await writeFile2(newFilePath, converted);
|
|
1008
|
+
await rm2(filePath);
|
|
1009
|
+
this.logger.file(`Migrated: ${relative(this.docsPath, filePath)}`);
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
const relativePath = relative(this.docsPath, filePath);
|
|
1012
|
+
this.logger.warn(`Using fallback conversion for ${relativePath}`);
|
|
1013
|
+
const fallbackContent = content.replace(/<[^>]*\/>/g, "").replace(/<[^>]*>.*?<\/[^>]*>/g, "").replace(/import\s+.*?from\s+['"][^'"]*['"];?\s*/g, "").replace(/\n\s*\n\s*\n/g, "\n\n").trim();
|
|
1014
|
+
const newFilePath = filePath.replace(/\.mdx$/, ".md");
|
|
1015
|
+
await writeFile2(newFilePath, fallbackContent);
|
|
1016
|
+
await rm2(filePath);
|
|
1017
|
+
this.logger.file(`Migrated (fallback): ${relativePath}`);
|
|
1018
|
+
throw error;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
async convertMDXComponents(content, filePath) {
|
|
1022
|
+
try {
|
|
1023
|
+
let hasFrontmatter = content.startsWith("---");
|
|
1024
|
+
let frontmatter = "";
|
|
1025
|
+
let body = content;
|
|
1026
|
+
if (hasFrontmatter) {
|
|
1027
|
+
const end = content.indexOf("---", 3);
|
|
1028
|
+
if (end !== -1) {
|
|
1029
|
+
frontmatter = content.substring(0, end + 3);
|
|
1030
|
+
body = content.substring(end + 3);
|
|
1031
|
+
}
|
|
1032
|
+
} else {
|
|
1033
|
+
frontmatter = "---\ntitle: TODO\n---";
|
|
1034
|
+
hasFrontmatter = true;
|
|
1035
|
+
}
|
|
1036
|
+
const processor = createProcessor({ jsx: true, development: false });
|
|
1037
|
+
const ast = await processor.parse(body);
|
|
1038
|
+
const originalMdxString = body;
|
|
1039
|
+
let title = "";
|
|
1040
|
+
let description = "";
|
|
1041
|
+
if (hasFrontmatter) {
|
|
1042
|
+
const fm = frontmatter.replace(/^---\n/, "").replace(/\n---$/, "");
|
|
1043
|
+
for (const line of fm.split("\n")) {
|
|
1044
|
+
if (line.startsWith("title:")) {
|
|
1045
|
+
title = line.replace("title:", "").trim();
|
|
1046
|
+
title = title.replace(/^[\'\"`]|[\'\"`]$/g, "");
|
|
1047
|
+
title = title.replace(/[\\`*_{}\[\]()#+\-!]/g, "\\$&");
|
|
1048
|
+
} else if (line.startsWith("description:")) {
|
|
1049
|
+
description = line.replace("description:", "").trim();
|
|
1050
|
+
description = description.replace(/^[\'\"`]|[\'\"`]$/g, "");
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
await this.transformMintlifyComponents(ast, originalMdxString);
|
|
1055
|
+
if (title || description) {
|
|
1056
|
+
const nodes = [];
|
|
1057
|
+
if (title) nodes.push({ type: "heading", depth: 1, children: [{ type: "text", value: title }] });
|
|
1058
|
+
if (description)
|
|
1059
|
+
nodes.push({
|
|
1060
|
+
type: "containerDirective",
|
|
1061
|
+
name: "subtitle",
|
|
1062
|
+
children: [{ type: "text", value: description }]
|
|
1063
|
+
});
|
|
1064
|
+
ast.children = [...nodes, ...ast.children];
|
|
1065
|
+
}
|
|
1066
|
+
const markdown = remark().use(remarkDirective).use(remarkStringify, {
|
|
1067
|
+
bullet: "-",
|
|
1068
|
+
fences: true,
|
|
1069
|
+
listItemIndent: "one",
|
|
1070
|
+
incrementListMarker: true,
|
|
1071
|
+
quote: '"',
|
|
1072
|
+
emphasis: "*",
|
|
1073
|
+
strong: "*",
|
|
1074
|
+
tightDefinitions: true
|
|
1075
|
+
}).stringify(ast).replace(/ /g, " ").replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/\\\[/g, "[").replace(/\\\]/g, "]").replace(/\\\(/g, "(").replace(/\\\)/g, ")");
|
|
1076
|
+
return frontmatter + "\n\n" + markdown;
|
|
1077
|
+
} catch (error) {
|
|
1078
|
+
const relativePath = relative(this.docsPath, filePath);
|
|
1079
|
+
if (this.options.verbose) {
|
|
1080
|
+
this.logger.errorVerbose(error, `MDX transformation error in ${relativePath}`);
|
|
1081
|
+
} else {
|
|
1082
|
+
this.logger.warn(`Failed to transform MDX components in ${relativePath}, using fallback`);
|
|
1083
|
+
}
|
|
1084
|
+
throw error;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
// ---------------------------------------------------------------------------
|
|
1088
|
+
// AST transforms
|
|
1089
|
+
// ---------------------------------------------------------------------------
|
|
1090
|
+
convertLightDarkImage(node) {
|
|
1091
|
+
const className = node.attributes?.find((a) => a.name === "className")?.value || "";
|
|
1092
|
+
let src = node.attributes?.find((a) => a.name === "src")?.value || "";
|
|
1093
|
+
const alt = node.attributes?.find((a) => a.name === "alt")?.value || "";
|
|
1094
|
+
if (className.includes("dark:hidden") || className.includes("dark:block")) {
|
|
1095
|
+
let colorScheme = "";
|
|
1096
|
+
if (className.includes("dark:hidden")) colorScheme = "light";
|
|
1097
|
+
else if (className.includes("dark:block")) colorScheme = "dark";
|
|
1098
|
+
if (src.startsWith("/") && !src.startsWith("/public/") && !src.startsWith("http://") && !src.startsWith("https://")) {
|
|
1099
|
+
src = `/public${src}`;
|
|
1100
|
+
}
|
|
1101
|
+
return { colorScheme, src, alt };
|
|
1102
|
+
}
|
|
1103
|
+
return null;
|
|
1104
|
+
}
|
|
1105
|
+
async transformMintlifyComponents(ast, originalMdxString) {
|
|
1106
|
+
const includeMapping = {};
|
|
1107
|
+
const transformNode = async (node) => {
|
|
1108
|
+
if (!node) return null;
|
|
1109
|
+
const processedChildren = [];
|
|
1110
|
+
if (node.children) {
|
|
1111
|
+
for (const child of node.children) {
|
|
1112
|
+
const processed = await transformNode(child);
|
|
1113
|
+
if (!processed) continue;
|
|
1114
|
+
if (Array.isArray(processed)) processedChildren.push(...processed);
|
|
1115
|
+
else processedChildren.push(processed);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
if (node.type === "text" && node.value) {
|
|
1119
|
+
node.value = node.value.replace(/ /g, " ").replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'");
|
|
1120
|
+
}
|
|
1121
|
+
if (node.type === "mdxjsEsm") {
|
|
1122
|
+
const importStatement = node.value || "";
|
|
1123
|
+
const importMatch = importStatement.match(/import\s+(\w+)\s+from\s+["']([^"']+)["']/);
|
|
1124
|
+
if (importMatch) {
|
|
1125
|
+
const [, componentName, importPath] = importMatch;
|
|
1126
|
+
let xydPath = importPath;
|
|
1127
|
+
if (importPath.startsWith("./") || importPath.startsWith("/")) {
|
|
1128
|
+
xydPath = importPath.replace(/^\.?\//, "~/").replace(/\.mdx$/, ".md");
|
|
1129
|
+
}
|
|
1130
|
+
includeMapping[componentName] = xydPath;
|
|
1131
|
+
return null;
|
|
1132
|
+
}
|
|
1133
|
+
return { type: "html", value: `<!-- Import statement not supported: ${importStatement} -->` };
|
|
1134
|
+
}
|
|
1135
|
+
if (node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement" || node.type === "mdxFlowExpression") {
|
|
1136
|
+
const componentName = node.name;
|
|
1137
|
+
const children = processedChildren;
|
|
1138
|
+
switch (componentName) {
|
|
1139
|
+
case "img": {
|
|
1140
|
+
const imageData = this.convertLightDarkImage(node);
|
|
1141
|
+
if (imageData) {
|
|
1142
|
+
return {
|
|
1143
|
+
type: "html",
|
|
1144
|
+
value: `<img src="${imageData.src}" alt="${imageData.alt}" data-color-scheme="${imageData.colorScheme}" />`
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
break;
|
|
1148
|
+
}
|
|
1149
|
+
case "Columns":
|
|
1150
|
+
case "CardGroup": {
|
|
1151
|
+
const colsAttr = node.attributes?.find((a) => a.name === "cols");
|
|
1152
|
+
let cols = "2";
|
|
1153
|
+
if (typeof colsAttr?.value === "string") cols = colsAttr.value;
|
|
1154
|
+
else if (typeof colsAttr?.value?.value === "string") cols = colsAttr.value.value;
|
|
1155
|
+
const listItems = children.map((child) => ({
|
|
1156
|
+
type: "listItem",
|
|
1157
|
+
children: [{ type: "paragraph", children: [child] }]
|
|
1158
|
+
}));
|
|
1159
|
+
const list = {
|
|
1160
|
+
type: "list",
|
|
1161
|
+
ordered: false,
|
|
1162
|
+
children: [{ type: "listItem", children: listItems }]
|
|
1163
|
+
};
|
|
1164
|
+
return { type: "containerDirective", name: "grid", attributes: { cols }, children: [list] };
|
|
1165
|
+
}
|
|
1166
|
+
case "Card": {
|
|
1167
|
+
const title = node.attributes?.find((a) => a.name === "title")?.value || "";
|
|
1168
|
+
const href = node.attributes?.find((a) => a.name === "href")?.value || "";
|
|
1169
|
+
const icon = node.attributes?.find((a) => a.name === "icon")?.value || "";
|
|
1170
|
+
const iconType = node.attributes?.find((a) => a.name === "iconType")?.value || "";
|
|
1171
|
+
const description = node.attributes?.find((a) => a.name === "description")?.value || "";
|
|
1172
|
+
const imgSrc = node.attributes?.find((a) => a.name === "imgSrc")?.value || "";
|
|
1173
|
+
const cardAttributes = { kind: "secondary" };
|
|
1174
|
+
if (title) cardAttributes.title = title;
|
|
1175
|
+
if (href) cardAttributes.href = href;
|
|
1176
|
+
if (icon) cardAttributes.icon = icon;
|
|
1177
|
+
if (iconType) cardAttributes.iconType = iconType;
|
|
1178
|
+
if (description) cardAttributes.description = description;
|
|
1179
|
+
if (imgSrc) cardAttributes.imgSrc = imgSrc;
|
|
1180
|
+
return { type: "containerDirective", name: "guide-card", attributes: cardAttributes, children };
|
|
1181
|
+
}
|
|
1182
|
+
case "Frame": {
|
|
1183
|
+
let pictureHtml = "<picture>\n";
|
|
1184
|
+
if (node.children) {
|
|
1185
|
+
for (const child of node.children) {
|
|
1186
|
+
if (child.type === "mdxJsxFlowElement" && child.name === "img") {
|
|
1187
|
+
const imageData = this.convertLightDarkImage(child);
|
|
1188
|
+
if (!imageData) continue;
|
|
1189
|
+
pictureHtml += ` <img src="${imageData.src}" alt="${imageData.alt}" data-color-scheme="${imageData.colorScheme}" />
|
|
1190
|
+
`;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
pictureHtml += "</picture>";
|
|
1195
|
+
return { type: "html", value: pictureHtml };
|
|
1196
|
+
}
|
|
1197
|
+
case "Note":
|
|
1198
|
+
case "Info":
|
|
1199
|
+
case "Warning":
|
|
1200
|
+
case "Tip":
|
|
1201
|
+
case "Danger": {
|
|
1202
|
+
let kind = void 0;
|
|
1203
|
+
switch (componentName) {
|
|
1204
|
+
case "Note":
|
|
1205
|
+
kind = "note";
|
|
1206
|
+
break;
|
|
1207
|
+
case "Warning":
|
|
1208
|
+
kind = "warning";
|
|
1209
|
+
break;
|
|
1210
|
+
case "Tip":
|
|
1211
|
+
kind = "tip";
|
|
1212
|
+
break;
|
|
1213
|
+
case "Check":
|
|
1214
|
+
kind = "check";
|
|
1215
|
+
break;
|
|
1216
|
+
case "Danger":
|
|
1217
|
+
kind = "danger";
|
|
1218
|
+
break;
|
|
1219
|
+
}
|
|
1220
|
+
return {
|
|
1221
|
+
type: "containerDirective",
|
|
1222
|
+
name: "callout",
|
|
1223
|
+
attributes: { kind },
|
|
1224
|
+
children: [{ type: "paragraph", children }]
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
case "Steps": {
|
|
1228
|
+
return {
|
|
1229
|
+
type: "containerDirective",
|
|
1230
|
+
name: "steps",
|
|
1231
|
+
attributes: { kind: "secondary" },
|
|
1232
|
+
children: [{ type: "list", ordered: true, start: 1, children }]
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
case "Step": {
|
|
1236
|
+
const stepTitle = node.attributes?.find((a) => a.name === "title")?.value || "";
|
|
1237
|
+
const stepIcon = node.attributes?.find((a) => a.name === "icon")?.value || "";
|
|
1238
|
+
const attrs = [];
|
|
1239
|
+
if (stepIcon) attrs.push({ name: "icon", value: stepIcon });
|
|
1240
|
+
if (stepTitle) attrs.push({ name: "title", value: stepTitle });
|
|
1241
|
+
const listItemChildren = [];
|
|
1242
|
+
if (attrs.length) listItemChildren.push({
|
|
1243
|
+
type: "text",
|
|
1244
|
+
value: `[${attrs.map((a) => `${a.name}="${a.value}"`).join(" ")}] `
|
|
1245
|
+
});
|
|
1246
|
+
listItemChildren.push(...children);
|
|
1247
|
+
return { type: "listItem", children: [{ type: "paragraph", children: listItemChildren }] };
|
|
1248
|
+
}
|
|
1249
|
+
case "Tabs": {
|
|
1250
|
+
return {
|
|
1251
|
+
type: "containerDirective",
|
|
1252
|
+
name: "tabs",
|
|
1253
|
+
attributes: { kind: "secondary" },
|
|
1254
|
+
children: [{ type: "list", ordered: true, start: 1, children }]
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
case "Tab": {
|
|
1258
|
+
const tabTitle = node.attributes?.find((a) => a.name === "title")?.value || "";
|
|
1259
|
+
const parts = [];
|
|
1260
|
+
if (tabTitle) {
|
|
1261
|
+
const typeValue = String(tabTitle).toLowerCase();
|
|
1262
|
+
parts.push({ type: "text", value: `[${tabTitle}](type=${typeValue})` });
|
|
1263
|
+
parts.push({ type: "text", value: "\n" });
|
|
1264
|
+
}
|
|
1265
|
+
parts.push(...children);
|
|
1266
|
+
return { type: "listItem", children: [{ type: "paragraph", children: parts }] };
|
|
1267
|
+
}
|
|
1268
|
+
default: {
|
|
1269
|
+
if (includeMapping[componentName]) {
|
|
1270
|
+
return {
|
|
1271
|
+
type: "paragraph",
|
|
1272
|
+
children: [{ type: "text", value: `@include "${includeMapping[componentName]}"` }]
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
const textContent = children.filter((child) => child.type === "text" || child.type === "paragraph").map((child) => {
|
|
1276
|
+
if (child.type === "text") return child.value;
|
|
1277
|
+
if (child.type === "paragraph" && child.children) {
|
|
1278
|
+
return child.children.filter((c) => c.type === "text").map((c) => c.value).join(" ");
|
|
1279
|
+
}
|
|
1280
|
+
return "";
|
|
1281
|
+
}).join(" ").trim();
|
|
1282
|
+
const comment = textContent ? `<!-- ${componentName}: ${textContent} -->` : `<!-- ${componentName} component not supported -->`;
|
|
1283
|
+
return { type: "html", value: comment };
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
node.children = processedChildren;
|
|
1288
|
+
return node;
|
|
1289
|
+
};
|
|
1290
|
+
const transformed = await transformNode(ast);
|
|
1291
|
+
return transformed || { type: "root", children: [] };
|
|
1292
|
+
}
|
|
1293
|
+
// ---------------------------------------------------------------------------
|
|
1294
|
+
// Small utilities (ported from original helpers)
|
|
1295
|
+
// ---------------------------------------------------------------------------
|
|
1296
|
+
hasNestedGroups(pages) {
|
|
1297
|
+
for (const page of pages) {
|
|
1298
|
+
if (typeof page === "object" && page.group) return true;
|
|
1299
|
+
}
|
|
1300
|
+
return false;
|
|
1301
|
+
}
|
|
1302
|
+
isFlatStructure(pages) {
|
|
1303
|
+
const prefixes = /* @__PURE__ */ new Set();
|
|
1304
|
+
let hasStringPages = false;
|
|
1305
|
+
let hasGroupPages = false;
|
|
1306
|
+
for (const page of pages) {
|
|
1307
|
+
if (typeof page === "string") {
|
|
1308
|
+
hasStringPages = true;
|
|
1309
|
+
const parts = page.split("/");
|
|
1310
|
+
if (parts.length > 1) prefixes.add(parts[0]);
|
|
1311
|
+
} else if (typeof page === "object" && page.pages) {
|
|
1312
|
+
hasGroupPages = true;
|
|
1313
|
+
for (const nested of page.pages) {
|
|
1314
|
+
if (typeof nested === "string") {
|
|
1315
|
+
const parts = nested.split("/");
|
|
1316
|
+
if (parts.length > 1) prefixes.add(parts[0]);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
return prefixes.size > 1 || hasStringPages && hasGroupPages || prefixes.size === 1 && pages.some((p) => typeof p === "string" && !p.includes("/"));
|
|
1322
|
+
}
|
|
1323
|
+
findFirstPage(pages) {
|
|
1324
|
+
for (const page of pages) {
|
|
1325
|
+
if (typeof page === "string") return page;
|
|
1326
|
+
else if (typeof page === "object" && page.pages) {
|
|
1327
|
+
const first = this.findFirstPage(page.pages);
|
|
1328
|
+
if (first) return first;
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
return null;
|
|
1332
|
+
}
|
|
1333
|
+
validateRoutePages(route, pages) {
|
|
1334
|
+
if (!route) return true;
|
|
1335
|
+
const check = (page) => {
|
|
1336
|
+
if (typeof page === "string") return page.startsWith(route + "/") || page === route;
|
|
1337
|
+
else if (typeof page === "object" && page.pages) return page.pages.every((nested) => check(nested));
|
|
1338
|
+
return false;
|
|
1339
|
+
};
|
|
1340
|
+
return pages.every(check);
|
|
1341
|
+
}
|
|
1342
|
+
};
|
|
1343
|
+
// ------ static helpers kept public-ish for unit reuse ------
|
|
1344
|
+
MintlifyMigrator.validMintlifyJsonFile = (fileName) => fileName === "docs.json" || fileName === "mint.json";
|
|
1345
|
+
|
|
1346
|
+
// src/commands/migrateme/nextra/nextra.ts
|
|
1347
|
+
function nextraMigrator(docsPath) {
|
|
1348
|
+
throw new Error("Nextra migration is not implemented yet");
|
|
1349
|
+
console.log("Migrating Nextra repository...");
|
|
1350
|
+
}
|
|
1351
|
+
async function isNextra(docsPath, fileName) {
|
|
1352
|
+
return false;
|
|
1353
|
+
if (fileName === "nextra.config.js" || fileName === "nextra.config.ts") {
|
|
1354
|
+
console.log("\u{1F50D} Nextra framework detected!");
|
|
1355
|
+
return true;
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
// src/commands/migrateme/docusaururs/docusaururs.ts
|
|
1360
|
+
function docusaurusMigrator(docsPath) {
|
|
1361
|
+
throw new Error("Docusaurus migration is not implemented yet");
|
|
1362
|
+
console.log("Migrating Docusaurus repository...");
|
|
1363
|
+
}
|
|
1364
|
+
async function isDocusaurus(docsPath, fileName) {
|
|
1365
|
+
return false;
|
|
1366
|
+
if (fileName === "docusaurus.config.js" || fileName === "docusaurus.config.ts") {
|
|
1367
|
+
console.log("\u{1F50D} Docusaurus framework detected!");
|
|
1368
|
+
return true;
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// src/commands/migrateme/vitepress/vitepress.ts
|
|
1373
|
+
function vitepressMigrator(docsPath) {
|
|
1374
|
+
throw new Error("VitePress migration is not implemented yet");
|
|
1375
|
+
console.log("Migrating VitePress repository...");
|
|
1376
|
+
}
|
|
1377
|
+
async function isVitepress(docsPath, fileName) {
|
|
1378
|
+
return false;
|
|
1379
|
+
if (fileName === "docs/.vitepress/config.js" || fileName === "docs/.vitepress/config.ts") {
|
|
1380
|
+
console.log("\u{1F50D} VitePress framework detected!");
|
|
1381
|
+
return true;
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
// src/commands/migrateme/migration.ts
|
|
1386
|
+
var execAsync = promisify3(exec3);
|
|
1387
|
+
async function migration(docsPath) {
|
|
1388
|
+
console.log("Detecting framework in repository...");
|
|
1389
|
+
try {
|
|
1390
|
+
const files = await readdir3(docsPath, { withFileTypes: true });
|
|
1391
|
+
for (const file of files) {
|
|
1392
|
+
if (!file.isFile()) {
|
|
1393
|
+
continue;
|
|
1394
|
+
}
|
|
1395
|
+
const fileNameLower = file.name.toLowerCase();
|
|
1396
|
+
const framework = await detectFrameworkByFile(fileNameLower, docsPath);
|
|
1397
|
+
if (framework) {
|
|
1398
|
+
await frameworkMigration(framework, docsPath);
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
console.log("\u274C No supported documentation framework detected in root directory");
|
|
1403
|
+
console.log("\u{1F4CB} Supported frameworks: Mintlify, Nextra, Docusaurus, VuePress");
|
|
1404
|
+
} catch (error) {
|
|
1405
|
+
console.error("Error detecting framework:", error);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
async function detectFrameworkByFile(fileName, docsPath) {
|
|
1409
|
+
if (await isMintlify(docsPath, fileName)) {
|
|
1410
|
+
return "mintlify" /* MINTLIFY */;
|
|
1411
|
+
}
|
|
1412
|
+
if (await isNextra(docsPath, fileName)) {
|
|
1413
|
+
return "nextra" /* NEXTRA */;
|
|
1414
|
+
}
|
|
1415
|
+
if (await isDocusaurus(docsPath, fileName)) {
|
|
1416
|
+
return "docusaurus" /* DOCUSAURUS */;
|
|
1417
|
+
}
|
|
1418
|
+
if (await isVitepress(docsPath, fileName)) {
|
|
1419
|
+
return "vitepress" /* VITEPRESS */;
|
|
1420
|
+
}
|
|
1421
|
+
return null;
|
|
1422
|
+
}
|
|
1423
|
+
async function frameworkMigration(framework, docsPath, outDir = false) {
|
|
1424
|
+
if (outDir) {
|
|
1425
|
+
docsPath = await cloneDocsPath(docsPath, outDir);
|
|
1426
|
+
}
|
|
1427
|
+
switch (framework) {
|
|
1428
|
+
case "mintlify" /* MINTLIFY */:
|
|
1429
|
+
return await mintlifyMigrator(docsPath);
|
|
1430
|
+
case "nextra" /* NEXTRA */:
|
|
1431
|
+
return await nextraMigrator(docsPath);
|
|
1432
|
+
case "docusaurus" /* DOCUSAURUS */:
|
|
1433
|
+
return await docusaurusMigrator(docsPath);
|
|
1434
|
+
case "vitepress" /* VITEPRESS */:
|
|
1435
|
+
return await vitepressMigrator(docsPath);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
async function cloneDocsPath(docsPath, outDir = true) {
|
|
1439
|
+
if (typeof outDir === "boolean") {
|
|
1440
|
+
outDir = `${docsPath}_${Math.random().toString(36).substring(2, 8)}`;
|
|
1441
|
+
}
|
|
1442
|
+
docsPath = await cloneDirectory(docsPath, outDir);
|
|
1443
|
+
console.log(`Cloned docsPath to: ${docsPath}`);
|
|
1444
|
+
return docsPath;
|
|
1445
|
+
}
|
|
1446
|
+
async function cloneDirectory(src, dest = "") {
|
|
1447
|
+
if (existsSync2(dest)) {
|
|
1448
|
+
await execAsync(`rm -rf "${dest}"`);
|
|
1449
|
+
}
|
|
1450
|
+
await execAsync(`cp -R "${src}" "${dest}"`);
|
|
1451
|
+
return dest;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
// src/commands/migrateme/migrateme.ts
|
|
1455
|
+
async function migrateme(resource, migratemeFlags, globalFlags) {
|
|
1456
|
+
if (!resource || typeof resource !== "string") {
|
|
1457
|
+
console.error("No resource provided");
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
if (isGitHubRepo(resource)) {
|
|
1461
|
+
await askForClean(migratemeFlags);
|
|
1462
|
+
const extractDir = await downloadGitHubRepo(resource, migratemeFlags);
|
|
1463
|
+
await migration(extractDir);
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
const docsPath = resolveResourcePath(resource);
|
|
1467
|
+
if (existsSync3(docsPath)) {
|
|
1468
|
+
await askForStart();
|
|
1469
|
+
await migration(docsPath);
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
console.error("No support for this resource yet");
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// src/commands/components.ts
|
|
1476
|
+
import colors2 from "picocolors";
|
|
1477
|
+
import { componentsInstall } from "@xyd-js/documan";
|
|
1478
|
+
async function components(args, flags) {
|
|
1479
|
+
const subcommand = args[0];
|
|
1480
|
+
if (subcommand === "install") {
|
|
1481
|
+
const componentName = args[1];
|
|
1482
|
+
const success = await componentsInstall(componentName);
|
|
1483
|
+
if (!success) {
|
|
1484
|
+
process.exit(1);
|
|
1485
|
+
}
|
|
1486
|
+
} else {
|
|
1487
|
+
console.error(colors2.red(`Error: Unknown subcommand '${subcommand}'`));
|
|
1488
|
+
console.log("Available subcommands: install");
|
|
1489
|
+
process.exit(1);
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// src/cli.ts
|
|
1494
|
+
async function cli(argv = process.argv.slice(2)) {
|
|
1495
|
+
if (!prerequisites()) {
|
|
1496
|
+
process.exit(1);
|
|
1497
|
+
}
|
|
1498
|
+
updateNotify();
|
|
1499
|
+
process.env.XYD_CLI = "true";
|
|
1500
|
+
const { globalFlags, commands } = parseArgs(argv);
|
|
1501
|
+
if (globalFlags.help) {
|
|
1502
|
+
return printHelp();
|
|
1503
|
+
}
|
|
1504
|
+
if (globalFlags.version) {
|
|
1505
|
+
return console.log(cliSpec.version);
|
|
1506
|
+
}
|
|
1507
|
+
if (!globalFlags.verbose) {
|
|
1508
|
+
console.debug = () => {
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
const globalCommand = commands[0] || "dev";
|
|
1512
|
+
if (!cliSpec.commands[globalCommand]) {
|
|
1513
|
+
console.error(`Unknown command: ${globalCommand}`);
|
|
1514
|
+
printHelp();
|
|
1515
|
+
process.exit(1);
|
|
1516
|
+
}
|
|
1517
|
+
const commandArgs = commands.slice(1);
|
|
1518
|
+
switch (globalCommand) {
|
|
1519
|
+
case "components":
|
|
1520
|
+
await components(commandArgs, globalFlags);
|
|
1521
|
+
break;
|
|
1522
|
+
default:
|
|
1523
|
+
await commands_exports[globalCommand](...commandArgs, globalFlags);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
function prerequisites() {
|
|
1527
|
+
const nodeVersion = process.versions.node;
|
|
1528
|
+
if (semver.major(nodeVersion) < MIN_NODE_VERSION) {
|
|
1529
|
+
console.warn(`\u26A0\uFE0F Node ${nodeVersion} is too old. xyd requires Node >= 22.`);
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
return true;
|
|
1533
|
+
}
|
|
1534
|
+
function updateNotify() {
|
|
1535
|
+
const packageJson2 = getPackageJson();
|
|
1536
|
+
const notifier = updateNotifier({ pkg: packageJson2 });
|
|
1537
|
+
notifier.notify();
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
// index.ts
|
|
1541
|
+
cli().then(
|
|
1542
|
+
() => {
|
|
1543
|
+
process.exit(0);
|
|
1544
|
+
},
|
|
1545
|
+
(error) => {
|
|
1546
|
+
if (error) console.error(error);
|
|
1547
|
+
process.exit(1);
|
|
1548
|
+
}
|
|
1549
|
+
);
|