@fumadocs/cli 1.1.0 → 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 +485 -336
- package/dist/schema/default.json +1 -0
- package/dist/schema/src.json +1 -0
- package/package.json +5 -5
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.
|
|
130
|
+
version: "1.2.0",
|
|
347
131
|
description: "The CLI tool for Fumadocs",
|
|
348
132
|
keywords: [
|
|
349
133
|
"NextJs",
|
|
@@ -378,16 +162,16 @@ var package_default = {
|
|
|
378
162
|
dependencies: {
|
|
379
163
|
"@clack/prompts": "^0.11.0",
|
|
380
164
|
commander: "^14.0.2",
|
|
381
|
-
"package-manager-detector": "^1.
|
|
165
|
+
"package-manager-detector": "^1.6.0",
|
|
382
166
|
picocolors: "^1.1.1",
|
|
383
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.10.
|
|
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,20 +180,209 @@ var package_default = {
|
|
|
396
180
|
};
|
|
397
181
|
|
|
398
182
|
// src/commands/customise.ts
|
|
399
|
-
import { cancel, group, intro as intro2, log as
|
|
183
|
+
import { cancel, group, intro as intro2, log as log4, outro as outro3, select } from "@clack/prompts";
|
|
400
184
|
import picocolors2 from "picocolors";
|
|
401
185
|
|
|
402
186
|
// src/commands/add.ts
|
|
403
187
|
import {
|
|
404
188
|
intro,
|
|
405
189
|
isCancel as isCancel3,
|
|
406
|
-
log as
|
|
190
|
+
log as log3,
|
|
407
191
|
multiselect,
|
|
408
192
|
outro as outro2,
|
|
409
193
|
spinner as spinner2
|
|
410
194
|
} from "@clack/prompts";
|
|
411
195
|
import picocolors from "picocolors";
|
|
412
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
|
+
|
|
413
386
|
// src/utils/get-package-manager.ts
|
|
414
387
|
import { detect } from "package-manager-detector";
|
|
415
388
|
async function getPackageManager() {
|
|
@@ -417,95 +390,266 @@ async function getPackageManager() {
|
|
|
417
390
|
return result?.name ?? "npm";
|
|
418
391
|
}
|
|
419
392
|
|
|
420
|
-
// src/
|
|
421
|
-
import { confirm
|
|
393
|
+
// src/registry/installer/dep-manager.ts
|
|
394
|
+
import { confirm, isCancel, spinner } from "@clack/prompts";
|
|
422
395
|
import { x as x2 } from "tinyexec";
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
dependencies
|
|
435
|
-
|
|
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;
|
|
436
419
|
}
|
|
437
|
-
|
|
438
|
-
const
|
|
439
|
-
Object.entries(
|
|
440
|
-
dependencies.set(k, v);
|
|
441
|
-
});
|
|
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}`);
|
|
442
423
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
async function installDeps(deps, devDeps) {
|
|
448
|
-
const installed = await getDeps();
|
|
449
|
-
function toList(deps2) {
|
|
450
|
-
return Object.entries(deps2).filter(([k]) => !installed.has(k)).map(([k, v]) => v === null || v.length === 0 ? k : `${k}@${v}`);
|
|
451
|
-
}
|
|
452
|
-
const items = toList(deps);
|
|
453
|
-
const devItems = toList(devDeps);
|
|
454
|
-
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;
|
|
455
428
|
const manager = await getPackageManager();
|
|
456
|
-
const value = await
|
|
429
|
+
const value = await confirm({
|
|
457
430
|
message: `Do you want to install with ${manager}?
|
|
458
431
|
${[...items, ...devItems].map((v) => `- ${v}`).join("\n")}`
|
|
459
432
|
});
|
|
460
|
-
if (
|
|
433
|
+
if (isCancel(value) || !value) {
|
|
461
434
|
return;
|
|
462
435
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
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);
|
|
469
511
|
}
|
|
470
512
|
}
|
|
471
|
-
|
|
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
|
+
};
|
|
472
606
|
|
|
473
607
|
// src/commands/add.ts
|
|
474
|
-
async function add(input,
|
|
475
|
-
const
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
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];
|
|
480
613
|
if (input.length === 0) {
|
|
481
614
|
const spin = spinner2();
|
|
482
615
|
spin.start("fetching registry");
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
|
+
}
|
|
489
633
|
spin.stop(picocolors.bold(picocolors.greenBright("registry fetched")));
|
|
490
634
|
const value = await multiselect({
|
|
491
635
|
message: "Select components to install",
|
|
492
|
-
options
|
|
493
|
-
label: item.title,
|
|
494
|
-
value: item.name,
|
|
495
|
-
hint: item.description
|
|
496
|
-
}))
|
|
636
|
+
options
|
|
497
637
|
});
|
|
498
638
|
if (isCancel3(value)) {
|
|
499
639
|
outro2("Ended");
|
|
500
640
|
return;
|
|
501
641
|
}
|
|
502
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
|
+
);
|
|
503
649
|
}
|
|
504
650
|
await install(target, installer);
|
|
505
651
|
}
|
|
506
652
|
async function install(target, installer) {
|
|
507
|
-
const dependencies = {};
|
|
508
|
-
const devDependencies = {};
|
|
509
653
|
for (const name of target) {
|
|
510
654
|
intro(
|
|
511
655
|
picocolors.bold(
|
|
@@ -513,27 +657,24 @@ async function install(target, installer) {
|
|
|
513
657
|
)
|
|
514
658
|
);
|
|
515
659
|
try {
|
|
516
|
-
|
|
517
|
-
Object.assign(dependencies, output.dependencies);
|
|
518
|
-
Object.assign(devDependencies, output.devDependencies);
|
|
660
|
+
await installer.install(name);
|
|
519
661
|
outro2(picocolors.bold(picocolors.greenBright(`${name} installed`)));
|
|
520
662
|
} catch (e) {
|
|
521
|
-
|
|
663
|
+
log3.error(String(e));
|
|
522
664
|
throw e;
|
|
523
665
|
}
|
|
524
666
|
}
|
|
525
667
|
intro(picocolors.bold("New Dependencies"));
|
|
526
|
-
await installDeps(
|
|
668
|
+
await installer.installDeps();
|
|
669
|
+
await installer.onEnd();
|
|
527
670
|
outro2(picocolors.bold(picocolors.greenBright("Successful")));
|
|
528
671
|
}
|
|
529
672
|
|
|
530
673
|
// src/commands/customise.ts
|
|
531
|
-
async function customise(
|
|
674
|
+
async function customise(client) {
|
|
532
675
|
intro2(picocolors2.bgBlack(picocolors2.whiteBright("Customise Fumadocs UI")));
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
config
|
|
536
|
-
});
|
|
676
|
+
const config = client.config;
|
|
677
|
+
const installer = new ComponentInstaller(client);
|
|
537
678
|
const result = await group(
|
|
538
679
|
{
|
|
539
680
|
target: () => select({
|
|
@@ -582,13 +723,14 @@ async function customise(resolver, config) {
|
|
|
582
723
|
}
|
|
583
724
|
}
|
|
584
725
|
);
|
|
726
|
+
const registry = UIRegistries[config.uiLibrary];
|
|
585
727
|
if (result.target === "docs") {
|
|
586
728
|
const targets = [];
|
|
587
729
|
if (result.mode === "minimal") {
|
|
588
|
-
targets.push("layouts/docs-min");
|
|
730
|
+
targets.push("fumadocs/ui/layouts/docs-min");
|
|
589
731
|
} else {
|
|
590
732
|
targets.push(
|
|
591
|
-
result.mode === "full-default" ?
|
|
733
|
+
result.mode === "full-default" ? `${registry}/layouts/docs` : `${registry}/layouts/notebook`
|
|
592
734
|
);
|
|
593
735
|
}
|
|
594
736
|
await install(targets, installer);
|
|
@@ -605,14 +747,14 @@ async function customise(resolver, config) {
|
|
|
605
747
|
printNext(...maps);
|
|
606
748
|
}
|
|
607
749
|
if (result.target === "home") {
|
|
608
|
-
await install([
|
|
750
|
+
await install([`${registry}/layouts/home`], installer);
|
|
609
751
|
printNext(["fumadocs-ui/layouts/home", `@/components/layout/home`]);
|
|
610
752
|
}
|
|
611
753
|
outro3(picocolors2.bold("Have fun!"));
|
|
612
754
|
}
|
|
613
755
|
function printNext(...maps) {
|
|
614
756
|
intro2(picocolors2.bold("What is Next?"));
|
|
615
|
-
|
|
757
|
+
log4.info(
|
|
616
758
|
[
|
|
617
759
|
"You can check the installed components in `components`.",
|
|
618
760
|
picocolors2.dim("---"),
|
|
@@ -634,17 +776,24 @@ program.name("fumadocs").description("CLI to setup Fumadocs, init a config").ver
|
|
|
634
776
|
}
|
|
635
777
|
});
|
|
636
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) => {
|
|
637
|
-
|
|
638
|
-
|
|
779
|
+
await customise(
|
|
780
|
+
createClientFromDir(
|
|
781
|
+
options.dir,
|
|
782
|
+
await createOrLoadConfig(options.config)
|
|
783
|
+
)
|
|
784
|
+
);
|
|
639
785
|
});
|
|
640
786
|
var dirShortcuts = {
|
|
641
|
-
":
|
|
642
|
-
":
|
|
787
|
+
":preview": "https://preview.fumadocs.dev/registry",
|
|
788
|
+
":dev": "http://localhost:3000/registry"
|
|
643
789
|
};
|
|
644
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(
|
|
645
791
|
async (input, options) => {
|
|
646
|
-
const
|
|
647
|
-
|
|
792
|
+
const client = createClientFromDir(
|
|
793
|
+
options.dir,
|
|
794
|
+
await createOrLoadConfig(options.config)
|
|
795
|
+
);
|
|
796
|
+
await add(input, client);
|
|
648
797
|
}
|
|
649
798
|
);
|
|
650
799
|
program.command("tree").argument(
|
|
@@ -664,17 +813,17 @@ program.command("tree").argument(
|
|
|
664
813
|
} catch {
|
|
665
814
|
nodes = await runTree(str ?? "./");
|
|
666
815
|
}
|
|
667
|
-
const out = js || output && jsExtensions.includes(
|
|
816
|
+
const out = js || output && jsExtensions.includes(path5.extname(output)) ? treeToJavaScript(nodes, noRoot, importName) : treeToMdx(nodes, noRoot);
|
|
668
817
|
if (output) {
|
|
669
|
-
await
|
|
670
|
-
await
|
|
818
|
+
await fs6.mkdir(path5.dirname(output), { recursive: true });
|
|
819
|
+
await fs6.writeFile(output, out);
|
|
671
820
|
} else {
|
|
672
821
|
console.log(out);
|
|
673
822
|
}
|
|
674
823
|
}
|
|
675
824
|
);
|
|
676
|
-
function
|
|
825
|
+
function createClientFromDir(dir = "https://fumadocs.dev/registry", config) {
|
|
677
826
|
if (dir in dirShortcuts) dir = dirShortcuts[dir];
|
|
678
|
-
return dir.startsWith("http://") || dir.startsWith("https://") ?
|
|
827
|
+
return dir.startsWith("http://") || dir.startsWith("https://") ? new HttpRegistryClient(dir, config) : new LocalRegistryClient(dir, config);
|
|
679
828
|
}
|
|
680
829
|
program.parse();
|