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