@fumadocs/cli 1.0.3 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build/index.d.ts +110 -90
- package/dist/build/index.js +260 -334
- package/dist/index.js +496 -364
- package/dist/schema/default.json +1 -0
- package/dist/schema/src.json +1 -0
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1,241 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import fs6 from "fs/promises";
|
|
5
|
+
import path5 from "path";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import picocolors3 from "picocolors";
|
|
8
8
|
|
|
9
|
-
// src/utils/add/install-component.ts
|
|
10
|
-
import path2 from "path";
|
|
11
|
-
import fs from "fs/promises";
|
|
12
|
-
import { confirm, isCancel, log, outro } from "@clack/prompts";
|
|
13
|
-
|
|
14
|
-
// src/utils/typescript.ts
|
|
15
|
-
import { Project } from "ts-morph";
|
|
16
|
-
function createEmptyProject() {
|
|
17
|
-
return new Project({
|
|
18
|
-
compilerOptions: {}
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// src/constants.ts
|
|
23
|
-
var typescriptExtensions = [".ts", ".tsx", ".js", ".jsx"];
|
|
24
|
-
|
|
25
|
-
// src/utils/transform-references.ts
|
|
26
|
-
import path from "path";
|
|
27
|
-
function transformReferences(file, transform) {
|
|
28
|
-
for (const specifier of file.getImportStringLiterals()) {
|
|
29
|
-
const result = transform(specifier.getLiteralValue());
|
|
30
|
-
if (!result) continue;
|
|
31
|
-
specifier.setLiteralValue(result);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
function toImportSpecifier(sourceFile, referenceFile) {
|
|
35
|
-
const extname = path.extname(referenceFile);
|
|
36
|
-
const removeExt = typescriptExtensions.includes(extname);
|
|
37
|
-
const importPath = path.relative(
|
|
38
|
-
path.dirname(sourceFile),
|
|
39
|
-
removeExt ? referenceFile.substring(0, referenceFile.length - extname.length) : referenceFile
|
|
40
|
-
).replaceAll(path.sep, "/");
|
|
41
|
-
return importPath.startsWith("../") ? importPath : `./${importPath}`;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// src/registry/schema.ts
|
|
45
|
-
import { z } from "zod";
|
|
46
|
-
var namespaces = [
|
|
47
|
-
"components",
|
|
48
|
-
"lib",
|
|
49
|
-
"css",
|
|
50
|
-
"route",
|
|
51
|
-
"ui",
|
|
52
|
-
"block"
|
|
53
|
-
];
|
|
54
|
-
var indexSchema = z.object({
|
|
55
|
-
name: z.string(),
|
|
56
|
-
title: z.string().optional(),
|
|
57
|
-
description: z.string().optional()
|
|
58
|
-
});
|
|
59
|
-
var fileSchema = z.object({
|
|
60
|
-
type: z.literal(namespaces),
|
|
61
|
-
path: z.string(),
|
|
62
|
-
target: z.string().optional(),
|
|
63
|
-
content: z.string()
|
|
64
|
-
});
|
|
65
|
-
var componentSchema = z.object({
|
|
66
|
-
name: z.string(),
|
|
67
|
-
title: z.string().optional(),
|
|
68
|
-
description: z.string().optional(),
|
|
69
|
-
files: z.array(fileSchema),
|
|
70
|
-
dependencies: z.record(z.string(), z.string().or(z.null())),
|
|
71
|
-
devDependencies: z.record(z.string(), z.string().or(z.null())),
|
|
72
|
-
subComponents: z.array(z.string()).default([])
|
|
73
|
-
});
|
|
74
|
-
var rootSchema = z.object({
|
|
75
|
-
name: z.string(),
|
|
76
|
-
index: z.array(indexSchema),
|
|
77
|
-
components: z.array(componentSchema)
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// src/registry/client.ts
|
|
81
|
-
import { z as z2 } from "zod";
|
|
82
|
-
function validateRegistryIndex(indexes) {
|
|
83
|
-
return z2.array(indexSchema).parse(indexes);
|
|
84
|
-
}
|
|
85
|
-
function validateRegistryComponent(component) {
|
|
86
|
-
return componentSchema.parse(component);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// src/utils/add/install-component.ts
|
|
90
|
-
function createComponentInstaller(options) {
|
|
91
|
-
const { config, resolver } = options;
|
|
92
|
-
const project = createEmptyProject();
|
|
93
|
-
const installedFiles = /* @__PURE__ */ new Set();
|
|
94
|
-
const downloadedComps = /* @__PURE__ */ new Map();
|
|
95
|
-
function buildFileList(downloaded) {
|
|
96
|
-
const map = /* @__PURE__ */ new Map();
|
|
97
|
-
for (const item of downloaded) {
|
|
98
|
-
for (const file of item.files) {
|
|
99
|
-
const filePath = file.target ?? file.path;
|
|
100
|
-
if (map.has(filePath)) {
|
|
101
|
-
console.warn(
|
|
102
|
-
`noticed duplicated output file for ${filePath}, ignoring for now.`
|
|
103
|
-
);
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
map.set(filePath, file);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return Array.from(map.values());
|
|
110
|
-
}
|
|
111
|
-
return {
|
|
112
|
-
async install(name) {
|
|
113
|
-
const downloaded = await this.download(name);
|
|
114
|
-
const dependencies = {};
|
|
115
|
-
const devDependencies = {};
|
|
116
|
-
for (const item of downloaded) {
|
|
117
|
-
Object.assign(dependencies, item.dependencies);
|
|
118
|
-
Object.assign(devDependencies, item.devDependencies);
|
|
119
|
-
}
|
|
120
|
-
const fileList = buildFileList(downloaded);
|
|
121
|
-
for (const file of fileList) {
|
|
122
|
-
const filePath = file.target ?? file.path;
|
|
123
|
-
if (installedFiles.has(filePath)) continue;
|
|
124
|
-
const outPath = this.resolveOutputPath(file);
|
|
125
|
-
const output = typescriptExtensions.includes(path2.extname(filePath)) ? this.transform(outPath, file, fileList) : file.content;
|
|
126
|
-
const status = await fs.readFile(outPath).then((res) => {
|
|
127
|
-
if (res.toString() === output) return "ignore";
|
|
128
|
-
return "need-update";
|
|
129
|
-
}).catch(() => "write");
|
|
130
|
-
installedFiles.add(filePath);
|
|
131
|
-
if (status === "ignore") continue;
|
|
132
|
-
if (status === "need-update") {
|
|
133
|
-
const override = await confirm({
|
|
134
|
-
message: `Do you want to override ${outPath}?`,
|
|
135
|
-
initialValue: false
|
|
136
|
-
});
|
|
137
|
-
if (isCancel(override)) {
|
|
138
|
-
outro("Ended");
|
|
139
|
-
process.exit(0);
|
|
140
|
-
}
|
|
141
|
-
if (!override) continue;
|
|
142
|
-
}
|
|
143
|
-
await fs.mkdir(path2.dirname(outPath), { recursive: true });
|
|
144
|
-
await fs.writeFile(outPath, output);
|
|
145
|
-
log.step(`downloaded ${outPath}`);
|
|
146
|
-
}
|
|
147
|
-
return {
|
|
148
|
-
dependencies,
|
|
149
|
-
devDependencies
|
|
150
|
-
};
|
|
151
|
-
},
|
|
152
|
-
/**
|
|
153
|
-
* return a list of components, merged with child components.
|
|
154
|
-
*/
|
|
155
|
-
async download(name) {
|
|
156
|
-
const cached = downloadedComps.get(name);
|
|
157
|
-
if (cached) return cached;
|
|
158
|
-
const comp = validateRegistryComponent(
|
|
159
|
-
await resolver(`${name}.json`).catch((e) => {
|
|
160
|
-
log.error(`component ${name} not found:`);
|
|
161
|
-
log.error(String(e));
|
|
162
|
-
process.exit(1);
|
|
163
|
-
})
|
|
164
|
-
);
|
|
165
|
-
const result = [comp];
|
|
166
|
-
downloadedComps.set(name, result);
|
|
167
|
-
for (const sub of comp.subComponents) {
|
|
168
|
-
result.push(...await this.download(sub));
|
|
169
|
-
}
|
|
170
|
-
return result;
|
|
171
|
-
},
|
|
172
|
-
transform(filePath, file, fileList) {
|
|
173
|
-
const sourceFile = project.createSourceFile(filePath, file.content, {
|
|
174
|
-
overwrite: true
|
|
175
|
-
});
|
|
176
|
-
transformReferences(sourceFile, (specifier) => {
|
|
177
|
-
const prefix = "@/";
|
|
178
|
-
if (specifier.startsWith(prefix)) {
|
|
179
|
-
const lookup = specifier.substring(prefix.length);
|
|
180
|
-
const target = fileList.find((item) => {
|
|
181
|
-
const filePath2 = item.target ?? item.path;
|
|
182
|
-
return filePath2 === lookup;
|
|
183
|
-
});
|
|
184
|
-
if (!target) {
|
|
185
|
-
console.warn(`cannot find the referenced file of ${specifier}`);
|
|
186
|
-
return specifier;
|
|
187
|
-
}
|
|
188
|
-
return toImportSpecifier(filePath, this.resolveOutputPath(target));
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
return sourceFile.getFullText();
|
|
192
|
-
},
|
|
193
|
-
resolveOutputPath(ref) {
|
|
194
|
-
if (ref.target) {
|
|
195
|
-
return path2.join(config.baseDir, ref.target);
|
|
196
|
-
}
|
|
197
|
-
const base = path2.basename(ref.path);
|
|
198
|
-
const dir = {
|
|
199
|
-
components: config.aliases.componentsDir,
|
|
200
|
-
block: config.aliases.blockDir,
|
|
201
|
-
ui: config.aliases.uiDir,
|
|
202
|
-
css: config.aliases.cssDir,
|
|
203
|
-
lib: config.aliases.libDir,
|
|
204
|
-
route: "./"
|
|
205
|
-
}[ref.type];
|
|
206
|
-
return path2.join(config.baseDir, dir, base);
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
function remoteResolver(url) {
|
|
211
|
-
return async (file) => {
|
|
212
|
-
const res = await fetch(`${url}/${file}`);
|
|
213
|
-
if (!res.ok) {
|
|
214
|
-
throw new Error(`failed to fetch ${url}/${file}: ${res.statusText}`);
|
|
215
|
-
}
|
|
216
|
-
return await res.json();
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
function localResolver(dir) {
|
|
220
|
-
return async (file) => {
|
|
221
|
-
const filePath = path2.join(dir, file);
|
|
222
|
-
return await fs.readFile(filePath).then((res) => JSON.parse(res.toString())).catch((e) => {
|
|
223
|
-
throw new Error(`failed to resolve local file "${filePath}"`, {
|
|
224
|
-
cause: e
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
9
|
// src/config.ts
|
|
231
|
-
import
|
|
10
|
+
import fs2 from "fs/promises";
|
|
232
11
|
|
|
233
12
|
// src/utils/fs.ts
|
|
234
|
-
import
|
|
235
|
-
import
|
|
13
|
+
import fs from "fs/promises";
|
|
14
|
+
import path from "path";
|
|
236
15
|
async function exists(pathLike) {
|
|
237
16
|
try {
|
|
238
|
-
await
|
|
17
|
+
await fs.access(pathLike);
|
|
239
18
|
return true;
|
|
240
19
|
} catch {
|
|
241
20
|
return false;
|
|
@@ -248,7 +27,7 @@ async function isSrc() {
|
|
|
248
27
|
}
|
|
249
28
|
|
|
250
29
|
// src/config.ts
|
|
251
|
-
import { z
|
|
30
|
+
import { z } from "zod";
|
|
252
31
|
function createConfigSchema(isSrc2) {
|
|
253
32
|
const defaultAliases = {
|
|
254
33
|
uiDir: "./components/ui",
|
|
@@ -257,38 +36,43 @@ function createConfigSchema(isSrc2) {
|
|
|
257
36
|
cssDir: "./styles",
|
|
258
37
|
libDir: "./lib"
|
|
259
38
|
};
|
|
260
|
-
return
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
39
|
+
return z.object({
|
|
40
|
+
$schema: z.string().default(
|
|
41
|
+
isSrc2 ? "node_modules/@fumadocs/cli/dist/schema/src.json" : "node_modules/@fumadocs/cli/dist/schema/default.json"
|
|
42
|
+
).optional(),
|
|
43
|
+
aliases: z.object({
|
|
44
|
+
uiDir: z.string().default(defaultAliases.uiDir),
|
|
45
|
+
componentsDir: z.string().default(defaultAliases.uiDir),
|
|
46
|
+
blockDir: z.string().default(defaultAliases.blockDir),
|
|
47
|
+
cssDir: z.string().default(defaultAliases.componentsDir),
|
|
48
|
+
libDir: z.string().default(defaultAliases.libDir)
|
|
267
49
|
}).default(defaultAliases),
|
|
268
|
-
baseDir:
|
|
269
|
-
|
|
50
|
+
baseDir: z.string().default(isSrc2 ? "src" : ""),
|
|
51
|
+
uiLibrary: z.enum(["radix-ui", "base-ui"]).default("radix-ui"),
|
|
52
|
+
commands: z.object({
|
|
270
53
|
/**
|
|
271
54
|
* command to format output code automatically
|
|
272
55
|
*/
|
|
273
|
-
format:
|
|
56
|
+
format: z.string().optional()
|
|
274
57
|
}).default({})
|
|
275
58
|
});
|
|
276
59
|
}
|
|
277
60
|
async function createOrLoadConfig(file = "./cli.json") {
|
|
278
61
|
const inited = await initConfig(file);
|
|
279
62
|
if (inited) return inited;
|
|
280
|
-
const content = (await
|
|
63
|
+
const content = (await fs2.readFile(file)).toString();
|
|
281
64
|
const src = await isSrc();
|
|
282
65
|
const configSchema = createConfigSchema(src);
|
|
283
66
|
return configSchema.parse(JSON.parse(content));
|
|
284
67
|
}
|
|
285
|
-
async function initConfig(file = "./cli.json") {
|
|
286
|
-
if (await
|
|
68
|
+
async function initConfig(file = "./cli.json", src) {
|
|
69
|
+
if (await fs2.stat(file).then(() => true).catch(() => false)) {
|
|
287
70
|
return;
|
|
288
71
|
}
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
72
|
+
const defaultConfig = createConfigSchema(src ?? await isSrc()).parse(
|
|
73
|
+
{}
|
|
74
|
+
);
|
|
75
|
+
await fs2.writeFile(file, JSON.stringify(defaultConfig, null, 2));
|
|
292
76
|
return defaultConfig;
|
|
293
77
|
}
|
|
294
78
|
|
|
@@ -343,7 +127,7 @@ async function runTree(args) {
|
|
|
343
127
|
// package.json
|
|
344
128
|
var package_default = {
|
|
345
129
|
name: "@fumadocs/cli",
|
|
346
|
-
version: "1.0
|
|
130
|
+
version: "1.2.0",
|
|
347
131
|
description: "The CLI tool for Fumadocs",
|
|
348
132
|
keywords: [
|
|
349
133
|
"NextJs",
|
|
@@ -377,17 +161,17 @@ var package_default = {
|
|
|
377
161
|
},
|
|
378
162
|
dependencies: {
|
|
379
163
|
"@clack/prompts": "^0.11.0",
|
|
380
|
-
commander: "^14.0.
|
|
381
|
-
"package-manager-detector": "^1.
|
|
164
|
+
commander: "^14.0.2",
|
|
165
|
+
"package-manager-detector": "^1.6.0",
|
|
382
166
|
picocolors: "^1.1.1",
|
|
383
|
-
tinyexec: "^1.0.
|
|
167
|
+
tinyexec: "^1.0.2",
|
|
384
168
|
"ts-morph": "^27.0.2",
|
|
385
|
-
zod: "^4.1
|
|
169
|
+
zod: "^4.2.1"
|
|
386
170
|
},
|
|
387
171
|
devDependencies: {
|
|
388
|
-
"@types/node": "24.
|
|
172
|
+
"@types/node": "24.10.2",
|
|
389
173
|
"eslint-config-custom": "workspace:*",
|
|
390
|
-
shadcn: "3.
|
|
174
|
+
shadcn: "3.6.2",
|
|
391
175
|
tsconfig: "workspace:*"
|
|
392
176
|
},
|
|
393
177
|
publishConfig: {
|
|
@@ -396,28 +180,209 @@ var package_default = {
|
|
|
396
180
|
};
|
|
397
181
|
|
|
398
182
|
// src/commands/customise.ts
|
|
399
|
-
import {
|
|
400
|
-
cancel,
|
|
401
|
-
confirm as confirm3,
|
|
402
|
-
group,
|
|
403
|
-
intro as intro2,
|
|
404
|
-
log as log3,
|
|
405
|
-
outro as outro3,
|
|
406
|
-
select
|
|
407
|
-
} from "@clack/prompts";
|
|
183
|
+
import { cancel, group, intro as intro2, log as log4, outro as outro3, select } from "@clack/prompts";
|
|
408
184
|
import picocolors2 from "picocolors";
|
|
409
185
|
|
|
410
186
|
// src/commands/add.ts
|
|
411
187
|
import {
|
|
412
188
|
intro,
|
|
413
189
|
isCancel as isCancel3,
|
|
414
|
-
log as
|
|
190
|
+
log as log3,
|
|
415
191
|
multiselect,
|
|
416
192
|
outro as outro2,
|
|
417
193
|
spinner as spinner2
|
|
418
194
|
} from "@clack/prompts";
|
|
419
195
|
import picocolors from "picocolors";
|
|
420
196
|
|
|
197
|
+
// src/registry/installer/index.ts
|
|
198
|
+
import path4 from "path";
|
|
199
|
+
import fs5 from "fs/promises";
|
|
200
|
+
import { confirm as confirm2, isCancel as isCancel2, log as log2, outro } from "@clack/prompts";
|
|
201
|
+
|
|
202
|
+
// src/utils/typescript.ts
|
|
203
|
+
import { Project } from "ts-morph";
|
|
204
|
+
function createEmptyProject() {
|
|
205
|
+
return new Project({
|
|
206
|
+
compilerOptions: {}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/constants.ts
|
|
211
|
+
var typescriptExtensions = [".ts", ".tsx", ".js", ".jsx"];
|
|
212
|
+
|
|
213
|
+
// src/utils/ast.ts
|
|
214
|
+
import path2 from "path";
|
|
215
|
+
function toImportSpecifier(sourceFile, referenceFile) {
|
|
216
|
+
const extname = path2.extname(referenceFile);
|
|
217
|
+
const removeExt = typescriptExtensions.includes(extname);
|
|
218
|
+
let importPath = path2.relative(
|
|
219
|
+
path2.dirname(sourceFile),
|
|
220
|
+
removeExt ? referenceFile.substring(0, referenceFile.length - extname.length) : referenceFile
|
|
221
|
+
).replaceAll(path2.sep, "/");
|
|
222
|
+
if (removeExt && importPath.endsWith("/index")) {
|
|
223
|
+
importPath = importPath.slice(0, -"/index".length);
|
|
224
|
+
}
|
|
225
|
+
return importPath.startsWith("../") ? importPath : `./${importPath}`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/registry/schema.ts
|
|
229
|
+
import { z as z2 } from "zod";
|
|
230
|
+
var namespaces = [
|
|
231
|
+
"components",
|
|
232
|
+
"lib",
|
|
233
|
+
"css",
|
|
234
|
+
"route",
|
|
235
|
+
"ui",
|
|
236
|
+
"block"
|
|
237
|
+
];
|
|
238
|
+
var indexSchema = z2.object({
|
|
239
|
+
name: z2.string(),
|
|
240
|
+
title: z2.string().optional(),
|
|
241
|
+
description: z2.string().optional()
|
|
242
|
+
});
|
|
243
|
+
var fileSchema = z2.object({
|
|
244
|
+
type: z2.literal(namespaces),
|
|
245
|
+
path: z2.string(),
|
|
246
|
+
target: z2.string().optional(),
|
|
247
|
+
content: z2.string()
|
|
248
|
+
});
|
|
249
|
+
var httpSubComponent = z2.object({
|
|
250
|
+
type: z2.literal("http"),
|
|
251
|
+
baseUrl: z2.string(),
|
|
252
|
+
component: z2.string()
|
|
253
|
+
});
|
|
254
|
+
var componentSchema = z2.object({
|
|
255
|
+
name: z2.string(),
|
|
256
|
+
title: z2.string().optional(),
|
|
257
|
+
description: z2.string().optional(),
|
|
258
|
+
files: z2.array(fileSchema),
|
|
259
|
+
dependencies: z2.record(z2.string(), z2.string().or(z2.null())),
|
|
260
|
+
devDependencies: z2.record(z2.string(), z2.string().or(z2.null())),
|
|
261
|
+
/**
|
|
262
|
+
* list of sub components, either local (component name) or remote (registry info & component name)
|
|
263
|
+
*/
|
|
264
|
+
subComponents: z2.array(z2.string().or(httpSubComponent)).default([])
|
|
265
|
+
});
|
|
266
|
+
var registryInfoSchema = z2.object({
|
|
267
|
+
/**
|
|
268
|
+
* define used variables, variables can be referenced in the import specifiers of component files.
|
|
269
|
+
*/
|
|
270
|
+
variables: z2.record(
|
|
271
|
+
z2.string(),
|
|
272
|
+
z2.object({
|
|
273
|
+
description: z2.string().optional(),
|
|
274
|
+
default: z2.unknown().optional()
|
|
275
|
+
})
|
|
276
|
+
).optional(),
|
|
277
|
+
/**
|
|
278
|
+
* provide variables to sub components
|
|
279
|
+
*/
|
|
280
|
+
env: z2.record(z2.string(), z2.unknown()).optional(),
|
|
281
|
+
indexes: z2.array(indexSchema).default([]),
|
|
282
|
+
registries: z2.array(z2.string()).optional()
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// src/registry/client.ts
|
|
286
|
+
import path3 from "path";
|
|
287
|
+
import fs3 from "fs/promises";
|
|
288
|
+
import { log } from "@clack/prompts";
|
|
289
|
+
|
|
290
|
+
// src/utils/cache.ts
|
|
291
|
+
var AsyncCache = class {
|
|
292
|
+
constructor() {
|
|
293
|
+
this.store = /* @__PURE__ */ new Map();
|
|
294
|
+
}
|
|
295
|
+
cached(key, fn) {
|
|
296
|
+
let cached = this.store.get(key);
|
|
297
|
+
if (cached !== void 0) return cached;
|
|
298
|
+
cached = fn();
|
|
299
|
+
this.store.set(key, cached);
|
|
300
|
+
return cached;
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
// src/registry/client.ts
|
|
305
|
+
var fetchCache = new AsyncCache();
|
|
306
|
+
var HttpRegistryClient = class _HttpRegistryClient {
|
|
307
|
+
constructor(baseUrl, config) {
|
|
308
|
+
this.baseUrl = baseUrl;
|
|
309
|
+
this.config = config;
|
|
310
|
+
this.registryId = baseUrl;
|
|
311
|
+
}
|
|
312
|
+
async fetchRegistryInfo(baseUrl = this.baseUrl) {
|
|
313
|
+
const url = new URL("_registry.json", `${baseUrl}/`);
|
|
314
|
+
return fetchCache.cached(url.href, async () => {
|
|
315
|
+
const res = await fetch(url);
|
|
316
|
+
if (!res.ok) {
|
|
317
|
+
throw new Error(`failed to fetch ${url.href}: ${res.statusText}`);
|
|
318
|
+
}
|
|
319
|
+
return registryInfoSchema.parse(await res.json());
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
async fetchComponent(name) {
|
|
323
|
+
const url = new URL(`${name}.json`, `${this.baseUrl}/`);
|
|
324
|
+
return fetchCache.cached(url.href, async () => {
|
|
325
|
+
const res = await fetch(`${this.baseUrl}/${name}.json`);
|
|
326
|
+
if (!res.ok) {
|
|
327
|
+
log.error(`component ${name} not found at ${url.href}`);
|
|
328
|
+
throw new Error(await res.text());
|
|
329
|
+
}
|
|
330
|
+
return componentSchema.parse(await res.json());
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
async hasComponent(name) {
|
|
334
|
+
const url = new URL(`${name}.json`, `${this.baseUrl}/`);
|
|
335
|
+
const res = await fetch(url, { method: "HEAD" });
|
|
336
|
+
return res.ok;
|
|
337
|
+
}
|
|
338
|
+
createLinkedRegistryClient(name) {
|
|
339
|
+
return new _HttpRegistryClient(`${this.baseUrl}/${name}`, this.config);
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
var LocalRegistryClient = class _LocalRegistryClient {
|
|
343
|
+
constructor(dir, config) {
|
|
344
|
+
this.dir = dir;
|
|
345
|
+
this.config = config;
|
|
346
|
+
this.registryId = dir;
|
|
347
|
+
}
|
|
348
|
+
async fetchRegistryInfo(dir = this.dir) {
|
|
349
|
+
if (this.registryInfo) return this.registryInfo;
|
|
350
|
+
const filePath = path3.join(dir, "_registry.json");
|
|
351
|
+
const out = await fs3.readFile(filePath).then((res) => JSON.parse(res.toString())).catch((e) => {
|
|
352
|
+
throw new Error(`failed to resolve local file "${filePath}"`, {
|
|
353
|
+
cause: e
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
return this.registryInfo = registryInfoSchema.parse(out);
|
|
357
|
+
}
|
|
358
|
+
async fetchComponent(name) {
|
|
359
|
+
const filePath = path3.join(this.dir, `${name}.json`);
|
|
360
|
+
const out = await fs3.readFile(filePath).then((res) => JSON.parse(res.toString())).catch((e) => {
|
|
361
|
+
log.error(`component ${name} not found at ${filePath}`);
|
|
362
|
+
throw e;
|
|
363
|
+
});
|
|
364
|
+
return componentSchema.parse(out);
|
|
365
|
+
}
|
|
366
|
+
async hasComponent(name) {
|
|
367
|
+
const filePath = path3.join(this.dir, `${name}.json`);
|
|
368
|
+
try {
|
|
369
|
+
await fs3.stat(filePath);
|
|
370
|
+
return true;
|
|
371
|
+
} catch {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
createLinkedRegistryClient(name) {
|
|
376
|
+
return new _LocalRegistryClient(path3.join(this.dir, name), this.config);
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
// src/registry/installer/index.ts
|
|
381
|
+
import { x as x3 } from "tinyexec";
|
|
382
|
+
|
|
383
|
+
// src/registry/installer/dep-manager.ts
|
|
384
|
+
import fs4 from "fs/promises";
|
|
385
|
+
|
|
421
386
|
// src/utils/get-package-manager.ts
|
|
422
387
|
import { detect } from "package-manager-detector";
|
|
423
388
|
async function getPackageManager() {
|
|
@@ -425,95 +390,266 @@ async function getPackageManager() {
|
|
|
425
390
|
return result?.name ?? "npm";
|
|
426
391
|
}
|
|
427
392
|
|
|
428
|
-
// src/
|
|
429
|
-
import { confirm
|
|
393
|
+
// src/registry/installer/dep-manager.ts
|
|
394
|
+
import { confirm, isCancel, spinner } from "@clack/prompts";
|
|
430
395
|
import { x as x2 } from "tinyexec";
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
dependencies
|
|
443
|
-
|
|
396
|
+
var DependencyManager = class {
|
|
397
|
+
/**
|
|
398
|
+
* Get dependencies from `package.json`
|
|
399
|
+
*/
|
|
400
|
+
async getDeps() {
|
|
401
|
+
if (this.cachedInstalledDeps) return this.cachedInstalledDeps;
|
|
402
|
+
const dependencies = /* @__PURE__ */ new Map();
|
|
403
|
+
if (!await exists("package.json")) return dependencies;
|
|
404
|
+
const content = await fs4.readFile("package.json");
|
|
405
|
+
const parsed = JSON.parse(content.toString());
|
|
406
|
+
if ("dependencies" in parsed && typeof parsed.dependencies === "object") {
|
|
407
|
+
const records = parsed.dependencies;
|
|
408
|
+
for (const [k, v] of Object.entries(records)) {
|
|
409
|
+
dependencies.set(k, v);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if ("devDependencies" in parsed && typeof parsed.devDependencies === "object") {
|
|
413
|
+
const records = parsed.devDependencies;
|
|
414
|
+
for (const [k, v] of Object.entries(records)) {
|
|
415
|
+
dependencies.set(k, v);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return this.cachedInstalledDeps = dependencies;
|
|
444
419
|
}
|
|
445
|
-
|
|
446
|
-
const
|
|
447
|
-
Object.entries(
|
|
448
|
-
dependencies.set(k, v);
|
|
449
|
-
});
|
|
420
|
+
async resolveInstallDependencies(deps) {
|
|
421
|
+
const cachedInstalledDeps = await this.getDeps();
|
|
422
|
+
return Object.entries(deps).filter(([k]) => !cachedInstalledDeps.has(k)).map(([k, v]) => v === null || v.length === 0 ? k : `${k}@${v}`);
|
|
450
423
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
async function installDeps(deps, devDeps) {
|
|
456
|
-
const installed = await getDeps();
|
|
457
|
-
function toList(deps2) {
|
|
458
|
-
return Object.entries(deps2).filter(([k]) => !installed.has(k)).map(([k, v]) => v === null || v.length === 0 ? k : `${k}@${v}`);
|
|
459
|
-
}
|
|
460
|
-
const items = toList(deps);
|
|
461
|
-
const devItems = toList(devDeps);
|
|
462
|
-
if (items.length > 0 || devItems.length > 0) {
|
|
424
|
+
async installDeps(deps, devDeps) {
|
|
425
|
+
const items = await this.resolveInstallDependencies(deps);
|
|
426
|
+
const devItems = await this.resolveInstallDependencies(devDeps);
|
|
427
|
+
if (items.length === 0 && devItems.length === 0) return;
|
|
463
428
|
const manager = await getPackageManager();
|
|
464
|
-
const value = await
|
|
429
|
+
const value = await confirm({
|
|
465
430
|
message: `Do you want to install with ${manager}?
|
|
466
431
|
${[...items, ...devItems].map((v) => `- ${v}`).join("\n")}`
|
|
467
432
|
});
|
|
468
|
-
if (
|
|
433
|
+
if (isCancel(value) || !value) {
|
|
469
434
|
return;
|
|
470
435
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
436
|
+
const spin = spinner();
|
|
437
|
+
spin.start("Installing dependencies...");
|
|
438
|
+
if (items.length > 0) await x2(manager, ["install", ...items]);
|
|
439
|
+
if (devItems.length > 0) await x2(manager, ["install", ...devItems, "-D"]);
|
|
440
|
+
spin.stop("Dependencies installed.");
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
// src/registry/installer/index.ts
|
|
445
|
+
var ComponentInstaller = class {
|
|
446
|
+
constructor(rootClient, plugins = []) {
|
|
447
|
+
this.rootClient = rootClient;
|
|
448
|
+
this.plugins = plugins;
|
|
449
|
+
this.project = createEmptyProject();
|
|
450
|
+
this.installedFiles = /* @__PURE__ */ new Set();
|
|
451
|
+
this.downloadCache = new AsyncCache();
|
|
452
|
+
this.dependencies = {};
|
|
453
|
+
this.devDependencies = {};
|
|
454
|
+
this.pathToFileCache = new AsyncCache();
|
|
455
|
+
}
|
|
456
|
+
async install(name) {
|
|
457
|
+
let downloaded;
|
|
458
|
+
const info = await this.rootClient.fetchRegistryInfo();
|
|
459
|
+
for (const registry of info.registries ?? []) {
|
|
460
|
+
if (name.startsWith(`${registry}/`)) {
|
|
461
|
+
downloaded = await this.download(
|
|
462
|
+
name.slice(registry.length + 1),
|
|
463
|
+
this.rootClient.createLinkedRegistryClient(registry)
|
|
464
|
+
);
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
downloaded ??= await this.download(name, this.rootClient);
|
|
469
|
+
for (const item of downloaded) {
|
|
470
|
+
Object.assign(this.dependencies, item.dependencies);
|
|
471
|
+
Object.assign(this.devDependencies, item.devDependencies);
|
|
472
|
+
}
|
|
473
|
+
for (const comp of downloaded) {
|
|
474
|
+
for (const file of comp.files) {
|
|
475
|
+
const outPath = this.resolveOutputPath(file);
|
|
476
|
+
if (this.installedFiles.has(outPath)) continue;
|
|
477
|
+
this.installedFiles.add(outPath);
|
|
478
|
+
const output = typescriptExtensions.includes(path4.extname(outPath)) ? await this.transform(name, file, comp, downloaded) : file.content;
|
|
479
|
+
const status = await fs5.readFile(outPath).then((res) => {
|
|
480
|
+
if (res.toString() === output) return "ignore";
|
|
481
|
+
return "need-update";
|
|
482
|
+
}).catch(() => "write");
|
|
483
|
+
if (status === "ignore") continue;
|
|
484
|
+
if (status === "need-update") {
|
|
485
|
+
const override = await confirm2({
|
|
486
|
+
message: `Do you want to override ${outPath}?`,
|
|
487
|
+
initialValue: false
|
|
488
|
+
});
|
|
489
|
+
if (isCancel2(override)) {
|
|
490
|
+
outro("Ended");
|
|
491
|
+
process.exit(0);
|
|
492
|
+
}
|
|
493
|
+
if (!override) continue;
|
|
494
|
+
}
|
|
495
|
+
await fs5.mkdir(path4.dirname(outPath), { recursive: true });
|
|
496
|
+
await fs5.writeFile(outPath, output);
|
|
497
|
+
log2.step(`downloaded ${outPath}`);
|
|
498
|
+
}
|
|
477
499
|
}
|
|
478
500
|
}
|
|
479
|
-
|
|
501
|
+
async installDeps() {
|
|
502
|
+
await new DependencyManager().installDeps(
|
|
503
|
+
this.dependencies,
|
|
504
|
+
this.devDependencies
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
async onEnd() {
|
|
508
|
+
const config = this.rootClient.config;
|
|
509
|
+
if (config.commands.format) {
|
|
510
|
+
await x3(config.commands.format);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* return a list of components, merged with child components & variables.
|
|
515
|
+
*/
|
|
516
|
+
async download(name, client, contextVariables) {
|
|
517
|
+
const hash = `${client.registryId} ${name}`;
|
|
518
|
+
const info = await client.fetchRegistryInfo();
|
|
519
|
+
const variables = { ...contextVariables, ...info.env };
|
|
520
|
+
for (const [k, v] of Object.entries(info.variables ?? {})) {
|
|
521
|
+
variables[k] ??= v.default;
|
|
522
|
+
}
|
|
523
|
+
const out = await this.downloadCache.cached(hash, async () => {
|
|
524
|
+
const comp = await client.fetchComponent(name);
|
|
525
|
+
const result = [comp];
|
|
526
|
+
this.downloadCache.store.set(hash, result);
|
|
527
|
+
const child = await Promise.all(
|
|
528
|
+
comp.subComponents.map((sub) => {
|
|
529
|
+
if (typeof sub === "string") return this.download(sub, client);
|
|
530
|
+
const baseUrl = this.rootClient instanceof HttpRegistryClient ? new URL(sub.baseUrl, `${this.rootClient.baseUrl}/`).href : sub.baseUrl;
|
|
531
|
+
return this.download(
|
|
532
|
+
sub.component,
|
|
533
|
+
new HttpRegistryClient(baseUrl, client.config),
|
|
534
|
+
variables
|
|
535
|
+
);
|
|
536
|
+
})
|
|
537
|
+
);
|
|
538
|
+
for (const sub of child) result.push(...sub);
|
|
539
|
+
return result;
|
|
540
|
+
});
|
|
541
|
+
return out.map((file) => ({ ...file, variables }));
|
|
542
|
+
}
|
|
543
|
+
async transform(taskId, file, component, allComponents) {
|
|
544
|
+
const filePath = this.resolveOutputPath(file);
|
|
545
|
+
const sourceFile = this.project.createSourceFile(filePath, file.content, {
|
|
546
|
+
overwrite: true
|
|
547
|
+
});
|
|
548
|
+
const prefix = "@/";
|
|
549
|
+
const variables = Object.entries(component.variables ?? {});
|
|
550
|
+
const pathToFile = await this.pathToFileCache.cached(taskId, () => {
|
|
551
|
+
const map = /* @__PURE__ */ new Map();
|
|
552
|
+
for (const comp of allComponents) {
|
|
553
|
+
for (const file2 of comp.files) map.set(file2.target ?? file2.path, file2);
|
|
554
|
+
}
|
|
555
|
+
return map;
|
|
556
|
+
});
|
|
557
|
+
for (const specifier of sourceFile.getImportStringLiterals()) {
|
|
558
|
+
for (const [k, v] of variables) {
|
|
559
|
+
specifier.setLiteralValue(
|
|
560
|
+
specifier.getLiteralValue().replaceAll(`<${k}>`, v)
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
if (specifier.getLiteralValue().startsWith(prefix)) {
|
|
564
|
+
const lookup = specifier.getLiteralValue().substring(prefix.length);
|
|
565
|
+
const target = pathToFile.get(lookup);
|
|
566
|
+
if (target) {
|
|
567
|
+
specifier.setLiteralValue(
|
|
568
|
+
toImportSpecifier(filePath, this.resolveOutputPath(target))
|
|
569
|
+
);
|
|
570
|
+
} else {
|
|
571
|
+
console.warn(`cannot find the referenced file of ${specifier}`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
for (const plugin of this.plugins) {
|
|
576
|
+
await plugin.transform?.({
|
|
577
|
+
file: sourceFile,
|
|
578
|
+
componentFile: file,
|
|
579
|
+
component
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
return sourceFile.getFullText();
|
|
583
|
+
}
|
|
584
|
+
resolveOutputPath(file) {
|
|
585
|
+
const config = this.rootClient.config;
|
|
586
|
+
const dir = {
|
|
587
|
+
components: config.aliases.componentsDir,
|
|
588
|
+
block: config.aliases.blockDir,
|
|
589
|
+
ui: config.aliases.uiDir,
|
|
590
|
+
css: config.aliases.cssDir,
|
|
591
|
+
lib: config.aliases.libDir,
|
|
592
|
+
route: "./"
|
|
593
|
+
}[file.type];
|
|
594
|
+
if (file.target) {
|
|
595
|
+
return path4.join(config.baseDir, file.target.replace("<dir>", dir));
|
|
596
|
+
}
|
|
597
|
+
return path4.join(config.baseDir, dir, path4.basename(file.path));
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
// src/commands/shared.ts
|
|
602
|
+
var UIRegistries = {
|
|
603
|
+
"base-ui": "fumadocs/base-ui",
|
|
604
|
+
"radix-ui": "fumadocs/radix-ui"
|
|
605
|
+
};
|
|
480
606
|
|
|
481
607
|
// src/commands/add.ts
|
|
482
|
-
async function add(input,
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
let target = input;
|
|
608
|
+
async function add(input, client) {
|
|
609
|
+
const config = client.config;
|
|
610
|
+
let target;
|
|
611
|
+
const installer = new ComponentInstaller(client);
|
|
612
|
+
const registry = UIRegistries[config.uiLibrary];
|
|
488
613
|
if (input.length === 0) {
|
|
489
614
|
const spin = spinner2();
|
|
490
615
|
spin.start("fetching registry");
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
616
|
+
const info = await client.fetchRegistryInfo();
|
|
617
|
+
const options = [];
|
|
618
|
+
for (const item of info.indexes) {
|
|
619
|
+
options.push({
|
|
620
|
+
label: item.title ?? item.name,
|
|
621
|
+
value: item.name,
|
|
622
|
+
hint: item.description
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
const { indexes } = await client.createLinkedRegistryClient(registry).fetchRegistryInfo();
|
|
626
|
+
for (const item of indexes) {
|
|
627
|
+
options.push({
|
|
628
|
+
label: item.title ?? item.name,
|
|
629
|
+
value: `${registry}/${item.name}`,
|
|
630
|
+
hint: item.description
|
|
631
|
+
});
|
|
632
|
+
}
|
|
497
633
|
spin.stop(picocolors.bold(picocolors.greenBright("registry fetched")));
|
|
498
634
|
const value = await multiselect({
|
|
499
635
|
message: "Select components to install",
|
|
500
|
-
options
|
|
501
|
-
label: item.title,
|
|
502
|
-
value: item.name,
|
|
503
|
-
hint: item.description
|
|
504
|
-
}))
|
|
636
|
+
options
|
|
505
637
|
});
|
|
506
638
|
if (isCancel3(value)) {
|
|
507
639
|
outro2("Ended");
|
|
508
640
|
return;
|
|
509
641
|
}
|
|
510
642
|
target = value;
|
|
643
|
+
} else {
|
|
644
|
+
target = await Promise.all(
|
|
645
|
+
input.map(
|
|
646
|
+
async (item) => await client.hasComponent(item) ? item : `${registry}/${item}`
|
|
647
|
+
)
|
|
648
|
+
);
|
|
511
649
|
}
|
|
512
650
|
await install(target, installer);
|
|
513
651
|
}
|
|
514
652
|
async function install(target, installer) {
|
|
515
|
-
const dependencies = {};
|
|
516
|
-
const devDependencies = {};
|
|
517
653
|
for (const name of target) {
|
|
518
654
|
intro(
|
|
519
655
|
picocolors.bold(
|
|
@@ -521,27 +657,24 @@ async function install(target, installer) {
|
|
|
521
657
|
)
|
|
522
658
|
);
|
|
523
659
|
try {
|
|
524
|
-
|
|
525
|
-
Object.assign(dependencies, output.dependencies);
|
|
526
|
-
Object.assign(devDependencies, output.devDependencies);
|
|
660
|
+
await installer.install(name);
|
|
527
661
|
outro2(picocolors.bold(picocolors.greenBright(`${name} installed`)));
|
|
528
662
|
} catch (e) {
|
|
529
|
-
|
|
663
|
+
log3.error(String(e));
|
|
530
664
|
throw e;
|
|
531
665
|
}
|
|
532
666
|
}
|
|
533
667
|
intro(picocolors.bold("New Dependencies"));
|
|
534
|
-
await installDeps(
|
|
668
|
+
await installer.installDeps();
|
|
669
|
+
await installer.onEnd();
|
|
535
670
|
outro2(picocolors.bold(picocolors.greenBright("Successful")));
|
|
536
671
|
}
|
|
537
672
|
|
|
538
673
|
// src/commands/customise.ts
|
|
539
|
-
async function customise(
|
|
674
|
+
async function customise(client) {
|
|
540
675
|
intro2(picocolors2.bgBlack(picocolors2.whiteBright("Customise Fumadocs UI")));
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
config
|
|
544
|
-
});
|
|
676
|
+
const config = client.config;
|
|
677
|
+
const installer = new ComponentInstaller(client);
|
|
545
678
|
const result = await group(
|
|
546
679
|
{
|
|
547
680
|
target: () => select({
|
|
@@ -581,13 +714,6 @@ async function customise(resolver, config) {
|
|
|
581
714
|
}
|
|
582
715
|
]
|
|
583
716
|
});
|
|
584
|
-
},
|
|
585
|
-
page: async (v) => {
|
|
586
|
-
if (v.results.target !== "docs" || v.results.mode === "minimal")
|
|
587
|
-
return false;
|
|
588
|
-
return confirm3({
|
|
589
|
-
message: "Do you want to customise the page component too?"
|
|
590
|
-
});
|
|
591
717
|
}
|
|
592
718
|
},
|
|
593
719
|
{
|
|
@@ -597,39 +723,38 @@ async function customise(resolver, config) {
|
|
|
597
723
|
}
|
|
598
724
|
}
|
|
599
725
|
);
|
|
726
|
+
const registry = UIRegistries[config.uiLibrary];
|
|
600
727
|
if (result.target === "docs") {
|
|
601
728
|
const targets = [];
|
|
602
|
-
let pageAdded = false;
|
|
603
729
|
if (result.mode === "minimal") {
|
|
604
|
-
targets.push("layouts/docs-min");
|
|
605
|
-
pageAdded = true;
|
|
730
|
+
targets.push("fumadocs/ui/layouts/docs-min");
|
|
606
731
|
} else {
|
|
607
|
-
if (result.page) {
|
|
608
|
-
targets.push("layouts/page");
|
|
609
|
-
pageAdded = true;
|
|
610
|
-
}
|
|
611
732
|
targets.push(
|
|
612
|
-
result.mode === "full-default" ?
|
|
733
|
+
result.mode === "full-default" ? `${registry}/layouts/docs` : `${registry}/layouts/notebook`
|
|
613
734
|
);
|
|
614
735
|
}
|
|
615
736
|
await install(targets, installer);
|
|
616
|
-
const maps = [
|
|
617
|
-
["fumadocs-ui/layouts/
|
|
737
|
+
const maps = result.mode === "full-notebook" ? [
|
|
738
|
+
["fumadocs-ui/layouts/notebook", "@/components/layout/notebook"],
|
|
739
|
+
[
|
|
740
|
+
"fumadocs-ui/layouts/notebook/page",
|
|
741
|
+
"@/components/layout/notebook/page"
|
|
742
|
+
]
|
|
743
|
+
] : [
|
|
744
|
+
["fumadocs-ui/layouts/docs", "@/components/layout/docs"],
|
|
745
|
+
["fumadocs-ui/layouts/docs/page", "@/components/layout/docs/page"]
|
|
618
746
|
];
|
|
619
|
-
if (pageAdded) {
|
|
620
|
-
maps.push(["fumadocs-ui/page", "@/components/layout/page"]);
|
|
621
|
-
}
|
|
622
747
|
printNext(...maps);
|
|
623
748
|
}
|
|
624
749
|
if (result.target === "home") {
|
|
625
|
-
await install([
|
|
750
|
+
await install([`${registry}/layouts/home`], installer);
|
|
626
751
|
printNext(["fumadocs-ui/layouts/home", `@/components/layout/home`]);
|
|
627
752
|
}
|
|
628
753
|
outro3(picocolors2.bold("Have fun!"));
|
|
629
754
|
}
|
|
630
755
|
function printNext(...maps) {
|
|
631
756
|
intro2(picocolors2.bold("What is Next?"));
|
|
632
|
-
|
|
757
|
+
log4.info(
|
|
633
758
|
[
|
|
634
759
|
"You can check the installed components in `components`.",
|
|
635
760
|
picocolors2.dim("---"),
|
|
@@ -651,17 +776,24 @@ program.name("fumadocs").description("CLI to setup Fumadocs, init a config").ver
|
|
|
651
776
|
}
|
|
652
777
|
});
|
|
653
778
|
program.command("customise").alias("customize").description("simple way to customise layouts with Fumadocs UI").option("--dir <string>", "the root url or directory to resolve registry").action(async (options) => {
|
|
654
|
-
|
|
655
|
-
|
|
779
|
+
await customise(
|
|
780
|
+
createClientFromDir(
|
|
781
|
+
options.dir,
|
|
782
|
+
await createOrLoadConfig(options.config)
|
|
783
|
+
)
|
|
784
|
+
);
|
|
656
785
|
});
|
|
657
786
|
var dirShortcuts = {
|
|
658
|
-
":
|
|
659
|
-
":
|
|
787
|
+
":preview": "https://preview.fumadocs.dev/registry",
|
|
788
|
+
":dev": "http://localhost:3000/registry"
|
|
660
789
|
};
|
|
661
790
|
program.command("add").description("add a new component to your docs").argument("[components...]", "components to download").option("--dir <string>", "the root url or directory to resolve registry").action(
|
|
662
791
|
async (input, options) => {
|
|
663
|
-
const
|
|
664
|
-
|
|
792
|
+
const client = createClientFromDir(
|
|
793
|
+
options.dir,
|
|
794
|
+
await createOrLoadConfig(options.config)
|
|
795
|
+
);
|
|
796
|
+
await add(input, client);
|
|
665
797
|
}
|
|
666
798
|
);
|
|
667
799
|
program.command("tree").argument(
|
|
@@ -681,17 +813,17 @@ program.command("tree").argument(
|
|
|
681
813
|
} catch {
|
|
682
814
|
nodes = await runTree(str ?? "./");
|
|
683
815
|
}
|
|
684
|
-
const out = js || output && jsExtensions.includes(
|
|
816
|
+
const out = js || output && jsExtensions.includes(path5.extname(output)) ? treeToJavaScript(nodes, noRoot, importName) : treeToMdx(nodes, noRoot);
|
|
685
817
|
if (output) {
|
|
686
|
-
await
|
|
687
|
-
await
|
|
818
|
+
await fs6.mkdir(path5.dirname(output), { recursive: true });
|
|
819
|
+
await fs6.writeFile(output, out);
|
|
688
820
|
} else {
|
|
689
821
|
console.log(out);
|
|
690
822
|
}
|
|
691
823
|
}
|
|
692
824
|
);
|
|
693
|
-
function
|
|
825
|
+
function createClientFromDir(dir = "https://fumadocs.dev/registry", config) {
|
|
694
826
|
if (dir in dirShortcuts) dir = dirShortcuts[dir];
|
|
695
|
-
return dir.startsWith("http://") || dir.startsWith("https://") ?
|
|
827
|
+
return dir.startsWith("http://") || dir.startsWith("https://") ? new HttpRegistryClient(dir, config) : new LocalRegistryClient(dir, config);
|
|
696
828
|
}
|
|
697
829
|
program.parse();
|