@fumadocs/cli 0.2.1 → 1.0.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 +112 -91
- package/dist/build/index.js +264 -244
- package/dist/index.js +347 -784
- package/package.json +5 -5
- package/dist/chunk-DG6SFM2G.js +0 -19
package/dist/index.js
CHANGED
|
@@ -1,81 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
exists,
|
|
4
|
-
isRelative
|
|
5
|
-
} from "./chunk-DG6SFM2G.js";
|
|
6
2
|
|
|
7
3
|
// src/index.ts
|
|
8
|
-
import
|
|
9
|
-
import
|
|
4
|
+
import fs5 from "fs/promises";
|
|
5
|
+
import path4 from "path";
|
|
10
6
|
import { Command } from "commander";
|
|
11
|
-
import
|
|
12
|
-
import { isCancel as isCancel5, outro as outro4, select as select2 } from "@clack/prompts";
|
|
13
|
-
|
|
14
|
-
// src/commands/init.ts
|
|
15
|
-
import * as process2 from "process";
|
|
16
|
-
import path3 from "path";
|
|
17
|
-
import {
|
|
18
|
-
cancel,
|
|
19
|
-
confirm,
|
|
20
|
-
intro,
|
|
21
|
-
isCancel,
|
|
22
|
-
log,
|
|
23
|
-
note,
|
|
24
|
-
spinner
|
|
25
|
-
} from "@clack/prompts";
|
|
26
|
-
import picocolors from "picocolors";
|
|
27
|
-
import { x } from "tinyexec";
|
|
28
|
-
|
|
29
|
-
// src/utils/get-package-manager.ts
|
|
30
|
-
import { detect } from "package-manager-detector";
|
|
31
|
-
async function getPackageManager() {
|
|
32
|
-
const result = await detect();
|
|
33
|
-
return result?.name ?? "npm";
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// src/utils/is-src.ts
|
|
37
|
-
import path from "path";
|
|
38
|
-
async function isSrc() {
|
|
39
|
-
return exists("./src");
|
|
40
|
-
}
|
|
41
|
-
function resolveAppPath(filePath, src2) {
|
|
42
|
-
return src2 ? path.join("./src", filePath) : filePath;
|
|
43
|
-
}
|
|
7
|
+
import picocolors3 from "picocolors";
|
|
44
8
|
|
|
45
|
-
// src/
|
|
9
|
+
// src/utils/add/install-component.ts
|
|
10
|
+
import path2 from "path";
|
|
46
11
|
import fs from "fs/promises";
|
|
47
|
-
|
|
48
|
-
var defaultConfig = {
|
|
49
|
-
aliases: {
|
|
50
|
-
cn: src ? "./src/lib/utils.ts" : "./lib/utils.ts",
|
|
51
|
-
componentsDir: src ? "./src/components" : "./components",
|
|
52
|
-
uiDir: src ? "./src/components/ui" : "./components/ui",
|
|
53
|
-
libDir: src ? "./src/lib" : "./lib"
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
async function loadConfig(file = "./cli.json") {
|
|
57
|
-
try {
|
|
58
|
-
const content = await fs.readFile(file);
|
|
59
|
-
return JSON.parse(content.toString());
|
|
60
|
-
} catch {
|
|
61
|
-
return {};
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
async function initConfig(file = "./cli.json") {
|
|
65
|
-
if (await fs.stat(file).then(() => true).catch(() => false)) {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
await fs.writeFile(file, JSON.stringify(defaultConfig, null, 2));
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
12
|
+
import { confirm, isCancel, log, outro } from "@clack/prompts";
|
|
71
13
|
|
|
72
|
-
// src/utils/
|
|
73
|
-
import
|
|
14
|
+
// src/utils/typescript.ts
|
|
15
|
+
import { Project } from "ts-morph";
|
|
16
|
+
function createEmptyProject() {
|
|
17
|
+
return new Project({
|
|
18
|
+
compilerOptions: {}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
74
21
|
|
|
75
22
|
// src/constants.ts
|
|
76
23
|
var typescriptExtensions = [".ts", ".tsx", ".js", ".jsx"];
|
|
77
24
|
|
|
78
25
|
// src/utils/transform-references.ts
|
|
26
|
+
import path from "path";
|
|
79
27
|
function transformReferences(file, transform) {
|
|
80
28
|
for (const specifier of file.getImportStringLiterals()) {
|
|
81
29
|
const result = transform(specifier.getLiteralValue());
|
|
@@ -83,643 +31,267 @@ function transformReferences(file, transform) {
|
|
|
83
31
|
specifier.setLiteralValue(result);
|
|
84
32
|
}
|
|
85
33
|
}
|
|
86
|
-
function
|
|
87
|
-
const extname =
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
referenceFile,
|
|
94
|
-
typescriptExtensions.includes(extname) ? extname : void 0
|
|
95
|
-
)
|
|
96
|
-
)
|
|
97
|
-
).replaceAll(path2.sep, "/");
|
|
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, "/");
|
|
98
41
|
return importPath.startsWith("../") ? importPath : `./${importPath}`;
|
|
99
42
|
}
|
|
100
|
-
function resolveReference(ref, resolver) {
|
|
101
|
-
if (ref.startsWith("./") || ref.startsWith("../")) {
|
|
102
|
-
return {
|
|
103
|
-
type: "file",
|
|
104
|
-
path: path2.join(resolver.relativeTo, ref)
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
if (ref.startsWith("@/")) {
|
|
108
|
-
const rest = ref.slice("@/".length);
|
|
109
|
-
if (!resolver.alias) throw new Error("alias resolver is not configured");
|
|
110
|
-
return {
|
|
111
|
-
type: "file",
|
|
112
|
-
path: path2.join(resolver.alias.dir, rest)
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
if (ref.startsWith("@")) {
|
|
116
|
-
const segments = ref.split("/");
|
|
117
|
-
return {
|
|
118
|
-
type: "dep",
|
|
119
|
-
name: segments.slice(0, 2).join("/")
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
return {
|
|
123
|
-
type: "dep",
|
|
124
|
-
name: ref.split("/")[0]
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
43
|
|
|
128
|
-
// src/
|
|
129
|
-
import {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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);
|
|
134
87
|
}
|
|
135
88
|
|
|
136
|
-
// src/
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
picocolors.bgCyan(picocolors.black(picocolors.bold("Installing Plugins")))
|
|
140
|
-
);
|
|
141
|
-
const ctx = {
|
|
142
|
-
src: await isSrc(),
|
|
143
|
-
outFileMap: /* @__PURE__ */ new Map(),
|
|
144
|
-
...config
|
|
145
|
-
};
|
|
146
|
-
const files = await plugin.files(ctx);
|
|
89
|
+
// src/utils/add/install-component.ts
|
|
90
|
+
function createComponentInstaller(options) {
|
|
91
|
+
const { config, resolver } = options;
|
|
147
92
|
const project = createEmptyProject();
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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);
|
|
161
107
|
}
|
|
162
|
-
if (!value) continue;
|
|
163
108
|
}
|
|
164
|
-
|
|
165
|
-
overwrite: true
|
|
166
|
-
});
|
|
167
|
-
transformReferences(sourceFile, (specifier) => {
|
|
168
|
-
const resolved = resolveReference(specifier, {
|
|
169
|
-
alias: {
|
|
170
|
-
type: "append",
|
|
171
|
-
dir: ctx.src ? "src" : ""
|
|
172
|
-
},
|
|
173
|
-
relativeTo: path3.dirname(file)
|
|
174
|
-
});
|
|
175
|
-
if (resolved.type !== "file") return;
|
|
176
|
-
return toReferencePath(file, getOutputPath(resolved.path, ctx));
|
|
177
|
-
});
|
|
178
|
-
await sourceFile.save();
|
|
109
|
+
return Array.from(map.values());
|
|
179
110
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
+
})
|
|
210
164
|
);
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
|
220
175
|
});
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (ref.startsWith("components")) {
|
|
236
|
-
return path3.join(
|
|
237
|
-
config.aliases?.componentsDir ?? defaultConfig.aliases.componentsDir,
|
|
238
|
-
path3.relative("components", ref)
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
if (ref.startsWith("lib") || ref.startsWith("utils")) {
|
|
242
|
-
return path3.join(
|
|
243
|
-
config.aliases?.libDir ?? defaultConfig.aliases.libDir,
|
|
244
|
-
path3.relative("lib", ref)
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
return ref;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// src/utils/add/install-component.ts
|
|
251
|
-
import path4 from "path";
|
|
252
|
-
import fs2 from "fs/promises";
|
|
253
|
-
import { confirm as confirm2, isCancel as isCancel2, log as log2, outro } from "@clack/prompts";
|
|
254
|
-
var downloadedFiles = /* @__PURE__ */ new Set();
|
|
255
|
-
async function installComponent(name, resolver, config = {}) {
|
|
256
|
-
const project = createEmptyProject();
|
|
257
|
-
return downloadComponent(name, {
|
|
258
|
-
project,
|
|
259
|
-
config,
|
|
260
|
-
resolver
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
var downloadedComps = /* @__PURE__ */ new Map();
|
|
264
|
-
async function downloadComponent(name, ctx) {
|
|
265
|
-
const cached = downloadedComps.get(name);
|
|
266
|
-
if (cached) return cached;
|
|
267
|
-
const comp = await ctx.resolver(`${name}.json`);
|
|
268
|
-
if (!comp) return;
|
|
269
|
-
downloadedComps.set(name, comp);
|
|
270
|
-
for (const file of comp.files) {
|
|
271
|
-
if (downloadedFiles.has(file.path)) continue;
|
|
272
|
-
const outPath = resolveOutputPath(file.path, ctx.config);
|
|
273
|
-
const output = typescriptExtensions.includes(path4.extname(file.path)) ? transformTypeScript(outPath, file, ctx) : file.content;
|
|
274
|
-
let canWrite = true;
|
|
275
|
-
const requireOverride = await fs2.readFile(outPath).then((res) => res.toString() !== output).catch(() => false);
|
|
276
|
-
if (requireOverride) {
|
|
277
|
-
const value = await confirm2({
|
|
278
|
-
message: `Do you want to override ${outPath}?`
|
|
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
|
+
}
|
|
279
190
|
});
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
191
|
+
return sourceFile.getFullText();
|
|
192
|
+
},
|
|
193
|
+
resolveOutputPath(ref) {
|
|
194
|
+
if (ref.target) {
|
|
195
|
+
return path2.join(config.baseDir, ref.target);
|
|
283
196
|
}
|
|
284
|
-
|
|
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);
|
|
285
207
|
}
|
|
286
|
-
|
|
287
|
-
await fs2.mkdir(path4.dirname(outPath), { recursive: true });
|
|
288
|
-
await fs2.writeFile(outPath, output);
|
|
289
|
-
log2.step(`downloaded ${outPath}`);
|
|
290
|
-
}
|
|
291
|
-
downloadedFiles.add(file.path);
|
|
292
|
-
}
|
|
293
|
-
for (const sub of comp.subComponents) {
|
|
294
|
-
const downloaded = await downloadComponent(sub, ctx);
|
|
295
|
-
if (!downloaded) continue;
|
|
296
|
-
Object.assign(comp.dependencies, downloaded.dependencies);
|
|
297
|
-
Object.assign(comp.devDependencies, downloaded.devDependencies);
|
|
298
|
-
}
|
|
299
|
-
return comp;
|
|
300
|
-
}
|
|
301
|
-
function resolveOutputPath(ref, config) {
|
|
302
|
-
const sep = ref.indexOf(":");
|
|
303
|
-
if (sep === -1) return ref;
|
|
304
|
-
const namespace = ref.slice(0, sep), file = ref.slice(sep + 1);
|
|
305
|
-
if (namespace === "components") {
|
|
306
|
-
return path4.join(
|
|
307
|
-
config.aliases?.componentsDir ?? defaultConfig.aliases.componentsDir,
|
|
308
|
-
file
|
|
309
|
-
);
|
|
310
|
-
}
|
|
311
|
-
return path4.join(
|
|
312
|
-
config.aliases?.libDir ?? defaultConfig.aliases.libDir,
|
|
313
|
-
file
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
function transformTypeScript(filePath, file, ctx) {
|
|
317
|
-
const sourceFile = ctx.project.createSourceFile(filePath, file.content, {
|
|
318
|
-
overwrite: true
|
|
319
|
-
});
|
|
320
|
-
transformReferences(sourceFile, (specifier) => {
|
|
321
|
-
if (specifier in file.imports) {
|
|
322
|
-
const outputPath = resolveOutputPath(file.imports[specifier], ctx.config);
|
|
323
|
-
return toReferencePath(filePath, outputPath);
|
|
324
|
-
}
|
|
325
|
-
});
|
|
326
|
-
return sourceFile.getFullText();
|
|
208
|
+
};
|
|
327
209
|
}
|
|
328
210
|
function remoteResolver(url) {
|
|
329
211
|
return async (file) => {
|
|
330
212
|
const res = await fetch(`${url}/${file}`);
|
|
331
|
-
if (!res.ok)
|
|
332
|
-
|
|
213
|
+
if (!res.ok) {
|
|
214
|
+
throw new Error(`failed to fetch ${url}/${file}: ${res.statusText}`);
|
|
215
|
+
}
|
|
216
|
+
return await res.json();
|
|
333
217
|
};
|
|
334
218
|
}
|
|
335
219
|
function localResolver(dir) {
|
|
336
220
|
return async (file) => {
|
|
337
|
-
|
|
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
|
+
});
|
|
338
227
|
};
|
|
339
228
|
}
|
|
340
229
|
|
|
341
|
-
// src/
|
|
342
|
-
import path7 from "path";
|
|
343
|
-
import { log as log3 } from "@clack/prompts";
|
|
344
|
-
|
|
345
|
-
// src/generated.js
|
|
346
|
-
var generated = { "lib/i18n": "import type { I18nConfig } from 'fumadocs-core/i18n';\n\nexport const i18n: I18nConfig = {\n defaultLanguage: 'en',\n languages: ['en', 'cn'],\n};\n", "middleware": "import { createI18nMiddleware } from 'fumadocs-core/i18n';\nimport { i18n } from '@/lib/i18n';\n\nexport default createI18nMiddleware(i18n);\n\nexport const config = {\n // Matcher ignoring `/_next/` and `/api/`\n matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],\n};\n", "scripts/generate-docs": "import * as OpenAPI from 'fumadocs-openapi';\nimport { rimraf } from 'rimraf';\n\nconst out = './content/docs/(api)';\n\nasync function generate() {\n // clean generated files\n await rimraf(out, {\n filter(v) {\n return !v.endsWith('index.mdx') && !v.endsWith('meta.json');\n },\n });\n\n await OpenAPI.generateFiles({\n // input files\n input: ['./openapi.json'],\n output: out,\n });\n}\n\nvoid generate();\n" };
|
|
347
|
-
|
|
348
|
-
// src/utils/i18n/transform-layout-config.ts
|
|
230
|
+
// src/config.ts
|
|
349
231
|
import fs3 from "fs/promises";
|
|
350
|
-
import { SyntaxKind } from "ts-morph";
|
|
351
|
-
async function transformLayoutConfig(project, filePath) {
|
|
352
|
-
let content;
|
|
353
|
-
try {
|
|
354
|
-
content = await fs3.readFile(filePath).then((res) => res.toString());
|
|
355
|
-
} catch {
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
const sourceFile = project.createSourceFile(filePath, content, {
|
|
359
|
-
overwrite: true
|
|
360
|
-
});
|
|
361
|
-
const configExport = sourceFile.getDescendantsOfKind(SyntaxKind.VariableDeclaration).find((node) => node.getName() === "baseOptions");
|
|
362
|
-
if (!configExport) return;
|
|
363
|
-
const init2 = configExport.getInitializerIfKind(
|
|
364
|
-
SyntaxKind.ObjectLiteralExpression
|
|
365
|
-
);
|
|
366
|
-
if (!init2) return;
|
|
367
|
-
if (init2.getProperty("i18n")) return;
|
|
368
|
-
init2.addPropertyAssignment({
|
|
369
|
-
name: "i18n",
|
|
370
|
-
initializer: "true"
|
|
371
|
-
});
|
|
372
|
-
return sourceFile.save();
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// src/utils/move-files.ts
|
|
376
|
-
import fs4 from "fs/promises";
|
|
377
|
-
import path5 from "path";
|
|
378
|
-
var transformExtensions = [".js", ".ts", ".tsx", ".jsx"];
|
|
379
|
-
async function moveFiles(from, to, filter, project, src2, originalDir = from) {
|
|
380
|
-
function isIncluded(file) {
|
|
381
|
-
if (!transformExtensions.includes(path5.extname(file))) return false;
|
|
382
|
-
return filter(path5.resolve(file));
|
|
383
|
-
}
|
|
384
|
-
const stats = await fs4.lstat(from).catch(() => void 0);
|
|
385
|
-
if (!stats) return;
|
|
386
|
-
if (stats.isDirectory()) {
|
|
387
|
-
const items = await fs4.readdir(from);
|
|
388
|
-
await Promise.all(
|
|
389
|
-
items.map(async (item) => {
|
|
390
|
-
await moveFiles(
|
|
391
|
-
path5.join(from, item),
|
|
392
|
-
path5.join(to, item),
|
|
393
|
-
filter,
|
|
394
|
-
project,
|
|
395
|
-
src2,
|
|
396
|
-
originalDir
|
|
397
|
-
);
|
|
398
|
-
})
|
|
399
|
-
);
|
|
400
|
-
await fs4.rmdir(from).catch(() => {
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
if (!stats.isFile() || !isIncluded(from)) return;
|
|
404
|
-
const content = await fs4.readFile(from);
|
|
405
|
-
const sourceFile = project.createSourceFile(from, content.toString(), {
|
|
406
|
-
overwrite: true
|
|
407
|
-
});
|
|
408
|
-
transformReferences(sourceFile, (specifier) => {
|
|
409
|
-
const resolved = resolveReference(specifier, {
|
|
410
|
-
alias: {
|
|
411
|
-
type: "append",
|
|
412
|
-
dir: src2 ? "src" : ""
|
|
413
|
-
},
|
|
414
|
-
relativeTo: path5.dirname(from)
|
|
415
|
-
});
|
|
416
|
-
if (resolved.type !== "file") return;
|
|
417
|
-
if (
|
|
418
|
-
// ignore if the file is also moved
|
|
419
|
-
isRelative(originalDir, from) && isIncluded(resolved.path)
|
|
420
|
-
)
|
|
421
|
-
return;
|
|
422
|
-
return toReferencePath(to, resolved.path);
|
|
423
|
-
});
|
|
424
|
-
await sourceFile.save();
|
|
425
|
-
await fs4.mkdir(path5.dirname(to), { recursive: true });
|
|
426
|
-
await fs4.rename(from, to);
|
|
427
|
-
}
|
|
428
232
|
|
|
429
|
-
// src/utils/
|
|
430
|
-
import
|
|
431
|
-
import
|
|
432
|
-
|
|
433
|
-
async function transformSourceI18n(project, filePath, config) {
|
|
434
|
-
let content;
|
|
233
|
+
// src/utils/fs.ts
|
|
234
|
+
import fs2 from "fs/promises";
|
|
235
|
+
import path3 from "path";
|
|
236
|
+
async function exists(pathLike) {
|
|
435
237
|
try {
|
|
436
|
-
|
|
238
|
+
await fs2.access(pathLike);
|
|
239
|
+
return true;
|
|
437
240
|
} catch {
|
|
438
|
-
return;
|
|
241
|
+
return false;
|
|
439
242
|
}
|
|
440
|
-
const sourceFile = project.createSourceFile(filePath, content, {
|
|
441
|
-
overwrite: true
|
|
442
|
-
});
|
|
443
|
-
sourceFile.addImportDeclaration({
|
|
444
|
-
kind: StructureKind.ImportDeclaration,
|
|
445
|
-
moduleSpecifier: toReferencePath(
|
|
446
|
-
filePath,
|
|
447
|
-
path6.join(config.aliases?.libDir ?? defaultConfig.aliases.libDir, "i18n")
|
|
448
|
-
),
|
|
449
|
-
namedImports: ["i18n"]
|
|
450
|
-
});
|
|
451
|
-
const sourceExport = sourceFile.getDescendantsOfKind(SyntaxKind2.VariableDeclaration).find((node) => node.getName() === "source");
|
|
452
|
-
if (!sourceExport) return;
|
|
453
|
-
const loaderCall = sourceExport.getFirstDescendantByKind(
|
|
454
|
-
SyntaxKind2.ObjectLiteralExpression
|
|
455
|
-
);
|
|
456
|
-
if (!loaderCall || loaderCall.getProperty("i18n")) return;
|
|
457
|
-
loaderCall.addPropertyAssignment({
|
|
458
|
-
name: "i18n",
|
|
459
|
-
initializer: "i18n"
|
|
460
|
-
});
|
|
461
|
-
return sourceFile.save();
|
|
462
243
|
}
|
|
463
244
|
|
|
464
|
-
// src/utils/
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
var ScriptKind = ts.ScriptKind;
|
|
468
|
-
var SyntaxKind3 = ts.SyntaxKind;
|
|
469
|
-
async function transformRootLayout(project, filePath) {
|
|
470
|
-
let content;
|
|
471
|
-
try {
|
|
472
|
-
content = await fs6.readFile(filePath).then((res) => res.toString());
|
|
473
|
-
} catch {
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
const sourceFile = project.createSourceFile(filePath, content, {
|
|
477
|
-
overwrite: true,
|
|
478
|
-
scriptKind: ScriptKind.TSX
|
|
479
|
-
});
|
|
480
|
-
runTransform(sourceFile);
|
|
481
|
-
return sourceFile.save();
|
|
245
|
+
// src/utils/is-src.ts
|
|
246
|
+
async function isSrc() {
|
|
247
|
+
return exists("./src");
|
|
482
248
|
}
|
|
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
|
-
name: `{ params, children }`
|
|
249
|
+
|
|
250
|
+
// src/config.ts
|
|
251
|
+
import { z as z3 } from "zod";
|
|
252
|
+
function createConfigSchema(isSrc2) {
|
|
253
|
+
const defaultAliases = {
|
|
254
|
+
uiDir: "./components/ui",
|
|
255
|
+
componentsDir: "./components",
|
|
256
|
+
blockDir: "./components",
|
|
257
|
+
cssDir: "./styles",
|
|
258
|
+
libDir: "./lib"
|
|
259
|
+
};
|
|
260
|
+
return z3.object({
|
|
261
|
+
aliases: z3.object({
|
|
262
|
+
uiDir: z3.string().default(defaultAliases.uiDir),
|
|
263
|
+
componentsDir: z3.string().default(defaultAliases.uiDir),
|
|
264
|
+
blockDir: z3.string().default(defaultAliases.blockDir),
|
|
265
|
+
cssDir: z3.string().default(defaultAliases.componentsDir),
|
|
266
|
+
libDir: z3.string().default(defaultAliases.libDir)
|
|
267
|
+
}).default(defaultAliases),
|
|
268
|
+
baseDir: z3.string().default(isSrc2 ? "src" : ""),
|
|
269
|
+
commands: z3.object({
|
|
270
|
+
/**
|
|
271
|
+
* command to format output code automatically
|
|
272
|
+
*/
|
|
273
|
+
format: z3.string().optional()
|
|
274
|
+
}).default({})
|
|
510
275
|
});
|
|
511
276
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
}),
|
|
520
|
-
dependencies: [],
|
|
521
|
-
instructions: () => [
|
|
522
|
-
{
|
|
523
|
-
type: "title",
|
|
524
|
-
text: `1. Update the params of ${picocolors2.bold("page.tsx")} and ${picocolors2.bold("layout.tsx")}, and make them async if necessary.`
|
|
525
|
-
},
|
|
526
|
-
{
|
|
527
|
-
type: "code",
|
|
528
|
-
title: "layout.tsx",
|
|
529
|
-
code: `
|
|
530
|
-
export default async function Layout({
|
|
531
|
-
params,
|
|
532
|
-
}: {
|
|
533
|
-
${picocolors2.underline(picocolors2.bold("params: Promise<{ lang: string }>"))}
|
|
534
|
-
})
|
|
535
|
-
`.trim()
|
|
536
|
-
},
|
|
537
|
-
{
|
|
538
|
-
type: "code",
|
|
539
|
-
title: "page.tsx",
|
|
540
|
-
code: `
|
|
541
|
-
export default async function Page({
|
|
542
|
-
params,
|
|
543
|
-
}: {
|
|
544
|
-
${picocolors2.underline(picocolors2.bold("params: Promise<{ lang: string; slug?: string[] }>"))}
|
|
545
|
-
})
|
|
546
|
-
`.trim()
|
|
547
|
-
},
|
|
548
|
-
{
|
|
549
|
-
type: "title",
|
|
550
|
-
text: "2. Update references to your `source` object"
|
|
551
|
-
},
|
|
552
|
-
{
|
|
553
|
-
type: "text",
|
|
554
|
-
text: "You can follow the instructions in https://fumadocs.vercel.app/docs/ui/internationalization#source section."
|
|
555
|
-
}
|
|
556
|
-
],
|
|
557
|
-
async transform(ctx) {
|
|
558
|
-
const project = createEmptyProject();
|
|
559
|
-
await Promise.all([
|
|
560
|
-
transformLayoutConfig(
|
|
561
|
-
project,
|
|
562
|
-
resolveAppPath("./app/layout.config.tsx", ctx.src)
|
|
563
|
-
),
|
|
564
|
-
transformRootLayout(project, resolveAppPath("./app/layout.tsx", ctx.src)),
|
|
565
|
-
transformSourceI18n(
|
|
566
|
-
project,
|
|
567
|
-
path7.join(
|
|
568
|
-
ctx.aliases?.libDir ?? defaultConfig.aliases.libDir,
|
|
569
|
-
"source.ts"
|
|
570
|
-
),
|
|
571
|
-
ctx
|
|
572
|
-
)
|
|
573
|
-
]);
|
|
574
|
-
await moveFiles(
|
|
575
|
-
resolveAppPath("./app", ctx.src),
|
|
576
|
-
resolveAppPath("./app/[lang]", ctx.src),
|
|
577
|
-
(v) => {
|
|
578
|
-
const parsed = path7.parse(v);
|
|
579
|
-
if (parsed.ext === ".css") return false;
|
|
580
|
-
return parsed.name !== "layout.config" && !isRelative("./app/api", v);
|
|
581
|
-
},
|
|
582
|
-
project,
|
|
583
|
-
ctx.src
|
|
584
|
-
);
|
|
585
|
-
log3.success(
|
|
586
|
-
"Moved the ./app files to a [lang] route group, and modified your root layout to add `<I18nProvider />`."
|
|
587
|
-
);
|
|
588
|
-
},
|
|
589
|
-
transformRejected() {
|
|
590
|
-
log3.info(
|
|
591
|
-
`Please create a [lang] route group and move all special files into the folder.
|
|
592
|
-
See https://nextjs.org/docs/app/building-your-application/routing/internationalization for more info.`
|
|
593
|
-
);
|
|
594
|
-
}
|
|
595
|
-
};
|
|
596
|
-
|
|
597
|
-
// src/plugins/openapi.ts
|
|
598
|
-
import fs8 from "fs/promises";
|
|
599
|
-
import path8 from "path";
|
|
600
|
-
import { StructureKind as StructureKind3 } from "ts-morph";
|
|
601
|
-
|
|
602
|
-
// src/utils/transform-tailwind.ts
|
|
603
|
-
import fs7 from "fs/promises";
|
|
604
|
-
import { SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
605
|
-
import { log as log4 } from "@clack/prompts";
|
|
606
|
-
var tailwindConfigPaths = [
|
|
607
|
-
"tailwind.config.js",
|
|
608
|
-
"tailwind.config.mjs",
|
|
609
|
-
"tailwind.config.ts",
|
|
610
|
-
"tailwind.config.mts"
|
|
611
|
-
];
|
|
612
|
-
async function findTailwindConfig() {
|
|
613
|
-
for (const configPath of tailwindConfigPaths) {
|
|
614
|
-
if (await exists(configPath)) {
|
|
615
|
-
return configPath;
|
|
616
|
-
}
|
|
617
|
-
}
|
|
277
|
+
async function createOrLoadConfig(file = "./cli.json") {
|
|
278
|
+
const inited = await initConfig(file);
|
|
279
|
+
if (inited) return inited;
|
|
280
|
+
const content = (await fs3.readFile(file)).toString();
|
|
281
|
+
const src = await isSrc();
|
|
282
|
+
const configSchema = createConfigSchema(src);
|
|
283
|
+
return configSchema.parse(JSON.parse(content));
|
|
618
284
|
}
|
|
619
|
-
async function
|
|
620
|
-
|
|
621
|
-
if (!file) {
|
|
622
|
-
log4.error(
|
|
623
|
-
"Cannot find Tailwind CSS configuration file, Tailwind CSS is required for this."
|
|
624
|
-
);
|
|
285
|
+
async function initConfig(file = "./cli.json") {
|
|
286
|
+
if (await fs3.stat(file).then(() => true).catch(() => false)) {
|
|
625
287
|
return;
|
|
626
288
|
}
|
|
627
|
-
const
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
);
|
|
632
|
-
const exports = configFile.getExportAssignments();
|
|
633
|
-
if (exports.length === 0) return;
|
|
634
|
-
const contentNode = exports[0].getDescendantsOfKind(SyntaxKind4.PropertyAssignment).find((a) => a.getName() === "content");
|
|
635
|
-
if (!contentNode) throw new Error("No `content` detected");
|
|
636
|
-
const arr = contentNode.getFirstDescendantByKind(
|
|
637
|
-
SyntaxKind4.ArrayLiteralExpression
|
|
638
|
-
);
|
|
639
|
-
arr?.addElements(options.addContents.map((v) => JSON.stringify(v)));
|
|
640
|
-
await configFile.save();
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
// src/plugins/openapi.ts
|
|
644
|
-
var openapiPlugin = {
|
|
645
|
-
files: () => ({
|
|
646
|
-
"scripts/generate-docs.mjs": generated["scripts/generate-docs"]
|
|
647
|
-
}),
|
|
648
|
-
dependencies: ["fumadocs-openapi", "rimraf", "shiki"],
|
|
649
|
-
async transform(ctx) {
|
|
650
|
-
const project = createEmptyProject();
|
|
651
|
-
await Promise.all([
|
|
652
|
-
transformSource(project, ctx),
|
|
653
|
-
transformTailwind(project, {
|
|
654
|
-
addContents: [`./node_modules/fumadocs-openapi/dist/**/*.js`]
|
|
655
|
-
}),
|
|
656
|
-
addScript()
|
|
657
|
-
]);
|
|
658
|
-
},
|
|
659
|
-
instructions: async () => [
|
|
660
|
-
{
|
|
661
|
-
type: "text",
|
|
662
|
-
text: `I've made some changes to your Tailwind CSS config.
|
|
663
|
-
You can add the APIPage component to your page.tsx:`
|
|
664
|
-
},
|
|
665
|
-
{
|
|
666
|
-
type: "code",
|
|
667
|
-
title: "page.tsx",
|
|
668
|
-
code: `import defaultMdxComponents from 'fumadocs-ui/mdx';
|
|
669
|
-
import { openapi } from '@/lib/source';
|
|
670
|
-
|
|
671
|
-
<MDX
|
|
672
|
-
components={{
|
|
673
|
-
...defaultMdxComponents,
|
|
674
|
-
APIPage: openapi.APIPage,
|
|
675
|
-
}}
|
|
676
|
-
/>;`
|
|
677
|
-
},
|
|
678
|
-
{
|
|
679
|
-
type: "text",
|
|
680
|
-
text: `Paste your OpenAPI schema to ./openapi.json, and use this script to generate docs:`
|
|
681
|
-
},
|
|
682
|
-
{
|
|
683
|
-
type: "code",
|
|
684
|
-
title: "Terminal",
|
|
685
|
-
code: `${await getPackageManager()} run build:docs`
|
|
686
|
-
}
|
|
687
|
-
]
|
|
688
|
-
};
|
|
689
|
-
async function addScript() {
|
|
690
|
-
const content = await fs8.readFile("package.json");
|
|
691
|
-
const parsed = JSON.parse(content.toString());
|
|
692
|
-
if (typeof parsed.scripts !== "object") return;
|
|
693
|
-
parsed.scripts ??= {};
|
|
694
|
-
Object.assign(parsed.scripts ?? {}, {
|
|
695
|
-
"build:docs": "node ./scripts/generate-docs.mjs"
|
|
696
|
-
});
|
|
697
|
-
await fs8.writeFile("package.json", JSON.stringify(parsed, null, 2));
|
|
698
|
-
}
|
|
699
|
-
async function transformSource(project, config) {
|
|
700
|
-
const source = path8.join(
|
|
701
|
-
config.aliases?.libDir ?? defaultConfig.aliases.libDir,
|
|
702
|
-
"source.ts"
|
|
703
|
-
);
|
|
704
|
-
const content = await fs8.readFile(source).catch(() => "");
|
|
705
|
-
const file = project.createSourceFile(source, content.toString(), {
|
|
706
|
-
overwrite: true
|
|
707
|
-
});
|
|
708
|
-
file.addImportDeclaration({
|
|
709
|
-
kind: StructureKind3.ImportDeclaration,
|
|
710
|
-
namedImports: ["createOpenAPI"],
|
|
711
|
-
moduleSpecifier: "fumadocs-openapi/server"
|
|
712
|
-
});
|
|
713
|
-
file.addStatements(`export const openapi = createOpenAPI();`);
|
|
714
|
-
await file.save();
|
|
289
|
+
const src = await isSrc();
|
|
290
|
+
const defaultConfig = createConfigSchema(src).parse({});
|
|
291
|
+
await fs3.writeFile(file, JSON.stringify(defaultConfig, null, 2));
|
|
292
|
+
return defaultConfig;
|
|
715
293
|
}
|
|
716
294
|
|
|
717
|
-
// src/plugins/index.ts
|
|
718
|
-
var plugins = {
|
|
719
|
-
i18n: i18nPlugin,
|
|
720
|
-
openapi: openapiPlugin
|
|
721
|
-
};
|
|
722
|
-
|
|
723
295
|
// src/commands/file-tree.ts
|
|
724
296
|
var scanned = ["file", "directory", "link"];
|
|
725
297
|
function treeToMdx(input, noRoot = false) {
|
|
@@ -756,9 +328,9 @@ export default (${treeToMdx(input, noRoot)})`;
|
|
|
756
328
|
}
|
|
757
329
|
|
|
758
330
|
// src/utils/file-tree/run-tree.ts
|
|
759
|
-
import { x
|
|
331
|
+
import { x } from "tinyexec";
|
|
760
332
|
async function runTree(args) {
|
|
761
|
-
const out = await
|
|
333
|
+
const out = await x("tree", [args, "--gitignore", "--prune", "-J"]);
|
|
762
334
|
try {
|
|
763
335
|
return JSON.parse(out.stdout);
|
|
764
336
|
} catch (e) {
|
|
@@ -771,7 +343,7 @@ async function runTree(args) {
|
|
|
771
343
|
// package.json
|
|
772
344
|
var package_default = {
|
|
773
345
|
name: "@fumadocs/cli",
|
|
774
|
-
version: "0.
|
|
346
|
+
version: "1.0.1",
|
|
775
347
|
description: "The CLI tool for Fumadocs",
|
|
776
348
|
keywords: [
|
|
777
349
|
"NextJs",
|
|
@@ -801,7 +373,6 @@ var package_default = {
|
|
|
801
373
|
clean: "rimraf dist",
|
|
802
374
|
dev: "tsup --watch",
|
|
803
375
|
lint: "eslint .",
|
|
804
|
-
sync: "tsx ./scripts/sync.ts",
|
|
805
376
|
"types:check": "tsc --noEmit"
|
|
806
377
|
},
|
|
807
378
|
dependencies: {
|
|
@@ -810,13 +381,14 @@ var package_default = {
|
|
|
810
381
|
"package-manager-detector": "^1.3.0",
|
|
811
382
|
picocolors: "^1.1.1",
|
|
812
383
|
tinyexec: "^1.0.1",
|
|
813
|
-
"ts-morph": "^26.0.0"
|
|
384
|
+
"ts-morph": "^26.0.0",
|
|
385
|
+
zod: "^4.1.4"
|
|
814
386
|
},
|
|
815
387
|
devDependencies: {
|
|
816
|
-
"@types/node": "24.0
|
|
388
|
+
"@types/node": "24.3.0",
|
|
817
389
|
"eslint-config-custom": "workspace:*",
|
|
818
|
-
|
|
819
|
-
|
|
390
|
+
shadcn: "3.0.0",
|
|
391
|
+
tsconfig: "workspace:*"
|
|
820
392
|
},
|
|
821
393
|
publishConfig: {
|
|
822
394
|
access: "public"
|
|
@@ -825,37 +397,44 @@ var package_default = {
|
|
|
825
397
|
|
|
826
398
|
// src/commands/customise.ts
|
|
827
399
|
import {
|
|
828
|
-
cancel
|
|
829
|
-
confirm as
|
|
400
|
+
cancel,
|
|
401
|
+
confirm as confirm3,
|
|
830
402
|
group,
|
|
831
|
-
intro as
|
|
832
|
-
log as
|
|
403
|
+
intro as intro2,
|
|
404
|
+
log as log3,
|
|
833
405
|
outro as outro3,
|
|
834
406
|
select
|
|
835
407
|
} from "@clack/prompts";
|
|
836
|
-
import
|
|
408
|
+
import picocolors2 from "picocolors";
|
|
837
409
|
|
|
838
410
|
// src/commands/add.ts
|
|
839
411
|
import {
|
|
840
|
-
intro
|
|
841
|
-
isCancel as
|
|
842
|
-
log as
|
|
412
|
+
intro,
|
|
413
|
+
isCancel as isCancel3,
|
|
414
|
+
log as log2,
|
|
843
415
|
multiselect,
|
|
844
416
|
outro as outro2,
|
|
845
|
-
spinner as
|
|
417
|
+
spinner as spinner2
|
|
846
418
|
} from "@clack/prompts";
|
|
847
|
-
import
|
|
419
|
+
import picocolors from "picocolors";
|
|
420
|
+
|
|
421
|
+
// src/utils/get-package-manager.ts
|
|
422
|
+
import { detect } from "package-manager-detector";
|
|
423
|
+
async function getPackageManager() {
|
|
424
|
+
const result = await detect();
|
|
425
|
+
return result?.name ?? "npm";
|
|
426
|
+
}
|
|
848
427
|
|
|
849
428
|
// src/utils/add/install-deps.ts
|
|
850
|
-
import { confirm as
|
|
851
|
-
import { x as
|
|
429
|
+
import { confirm as confirm2, isCancel as isCancel2, spinner } from "@clack/prompts";
|
|
430
|
+
import { x as x2 } from "tinyexec";
|
|
852
431
|
|
|
853
432
|
// src/utils/add/get-deps.ts
|
|
854
|
-
import
|
|
433
|
+
import fs4 from "fs/promises";
|
|
855
434
|
async function getDeps() {
|
|
856
435
|
const dependencies = /* @__PURE__ */ new Map();
|
|
857
436
|
if (!await exists("package.json")) return dependencies;
|
|
858
|
-
const content = await
|
|
437
|
+
const content = await fs4.readFile("package.json");
|
|
859
438
|
const parsed = JSON.parse(content.toString());
|
|
860
439
|
if ("dependencies" in parsed && typeof parsed.dependencies === "object") {
|
|
861
440
|
const records = parsed.dependencies;
|
|
@@ -873,30 +452,27 @@ async function getDeps() {
|
|
|
873
452
|
}
|
|
874
453
|
|
|
875
454
|
// src/utils/add/install-deps.ts
|
|
876
|
-
async function installDeps(
|
|
455
|
+
async function installDeps(deps, devDeps) {
|
|
877
456
|
const installed = await getDeps();
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
for (const result of results) {
|
|
881
|
-
Object.assign(deps, result.dependencies);
|
|
882
|
-
Object.assign(devDeps, result.devDependencies);
|
|
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}`);
|
|
883
459
|
}
|
|
884
|
-
const items =
|
|
885
|
-
const devItems =
|
|
460
|
+
const items = toList(deps);
|
|
461
|
+
const devItems = toList(devDeps);
|
|
886
462
|
if (items.length > 0 || devItems.length > 0) {
|
|
887
463
|
const manager = await getPackageManager();
|
|
888
|
-
const value = await
|
|
464
|
+
const value = await confirm2({
|
|
889
465
|
message: `Do you want to install with ${manager}?
|
|
890
466
|
${[...items, ...devItems].map((v) => `- ${v}`).join("\n")}`
|
|
891
467
|
});
|
|
892
|
-
if (
|
|
468
|
+
if (isCancel2(value)) {
|
|
893
469
|
return;
|
|
894
470
|
}
|
|
895
471
|
if (value) {
|
|
896
|
-
const spin =
|
|
472
|
+
const spin = spinner();
|
|
897
473
|
spin.start("Installing dependencies...");
|
|
898
|
-
if (items.length > 0) await
|
|
899
|
-
if (devItems.length > 0) await
|
|
474
|
+
if (items.length > 0) await x2(manager, ["install", ...items]);
|
|
475
|
+
if (devItems.length > 0) await x2(manager, ["install", ...devItems, "-D"]);
|
|
900
476
|
spin.stop("Dependencies installed.");
|
|
901
477
|
}
|
|
902
478
|
}
|
|
@@ -904,56 +480,68 @@ ${[...items, ...devItems].map((v) => `- ${v}`).join("\n")}`
|
|
|
904
480
|
|
|
905
481
|
// src/commands/add.ts
|
|
906
482
|
async function add(input, resolver, config) {
|
|
483
|
+
const installer = createComponentInstaller({
|
|
484
|
+
resolver,
|
|
485
|
+
config
|
|
486
|
+
});
|
|
907
487
|
let target = input;
|
|
908
488
|
if (input.length === 0) {
|
|
909
|
-
const spin =
|
|
489
|
+
const spin = spinner2();
|
|
910
490
|
spin.start("fetching registry");
|
|
911
|
-
const
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
491
|
+
const indexes = validateRegistryIndex(
|
|
492
|
+
await resolver("_registry.json").catch((e) => {
|
|
493
|
+
log2.error(String(e));
|
|
494
|
+
process.exit(1);
|
|
495
|
+
})
|
|
496
|
+
);
|
|
497
|
+
spin.stop(picocolors.bold(picocolors.greenBright("registry fetched")));
|
|
917
498
|
const value = await multiselect({
|
|
918
499
|
message: "Select components to install",
|
|
919
|
-
options:
|
|
920
|
-
label: item.
|
|
500
|
+
options: indexes.map((item) => ({
|
|
501
|
+
label: item.title,
|
|
921
502
|
value: item.name,
|
|
922
503
|
hint: item.description
|
|
923
504
|
}))
|
|
924
505
|
});
|
|
925
|
-
if (
|
|
506
|
+
if (isCancel3(value)) {
|
|
926
507
|
outro2("Ended");
|
|
927
508
|
return;
|
|
928
509
|
}
|
|
929
510
|
target = value;
|
|
930
511
|
}
|
|
931
|
-
await install(target,
|
|
512
|
+
await install(target, installer);
|
|
932
513
|
}
|
|
933
|
-
async function install(target,
|
|
934
|
-
const
|
|
514
|
+
async function install(target, installer) {
|
|
515
|
+
const dependencies = {};
|
|
516
|
+
const devDependencies = {};
|
|
935
517
|
for (const name of target) {
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
518
|
+
intro(
|
|
519
|
+
picocolors.bold(
|
|
520
|
+
picocolors.inverse(picocolors.cyanBright(`Add Component: ${name}`))
|
|
939
521
|
)
|
|
940
522
|
);
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
523
|
+
try {
|
|
524
|
+
const output = await installer.install(name);
|
|
525
|
+
Object.assign(dependencies, output.dependencies);
|
|
526
|
+
Object.assign(devDependencies, output.devDependencies);
|
|
527
|
+
outro2(picocolors.bold(picocolors.greenBright(`${name} installed`)));
|
|
528
|
+
} catch (e) {
|
|
529
|
+
log2.error(String(e));
|
|
530
|
+
throw e;
|
|
945
531
|
}
|
|
946
|
-
outro2(picocolors3.bold(picocolors3.greenBright(`${name} installed`)));
|
|
947
|
-
outputs.push(output);
|
|
948
532
|
}
|
|
949
|
-
|
|
950
|
-
await installDeps(
|
|
951
|
-
outro2(
|
|
533
|
+
intro(picocolors.bold("New Dependencies"));
|
|
534
|
+
await installDeps(dependencies, devDependencies);
|
|
535
|
+
outro2(picocolors.bold(picocolors.greenBright("Successful")));
|
|
952
536
|
}
|
|
953
537
|
|
|
954
538
|
// src/commands/customise.ts
|
|
955
539
|
async function customise(resolver, config) {
|
|
956
|
-
|
|
540
|
+
intro2(picocolors2.bgBlack(picocolors2.whiteBright("Customise Fumadocs UI")));
|
|
541
|
+
const installer = createComponentInstaller({
|
|
542
|
+
resolver,
|
|
543
|
+
config
|
|
544
|
+
});
|
|
957
545
|
const result = await group(
|
|
958
546
|
{
|
|
959
547
|
target: () => select({
|
|
@@ -997,14 +585,14 @@ async function customise(resolver, config) {
|
|
|
997
585
|
page: async (v) => {
|
|
998
586
|
if (v.results.target !== "docs" || v.results.mode === "minimal")
|
|
999
587
|
return false;
|
|
1000
|
-
return
|
|
588
|
+
return confirm3({
|
|
1001
589
|
message: "Do you want to customise the page component too?"
|
|
1002
590
|
});
|
|
1003
591
|
}
|
|
1004
592
|
},
|
|
1005
593
|
{
|
|
1006
594
|
onCancel: () => {
|
|
1007
|
-
|
|
595
|
+
cancel("Installation Stopped.");
|
|
1008
596
|
process.exit(0);
|
|
1009
597
|
}
|
|
1010
598
|
}
|
|
@@ -1024,72 +612,47 @@ async function customise(resolver, config) {
|
|
|
1024
612
|
result.mode === "full-default" ? "layouts/docs" : "layouts/notebook"
|
|
1025
613
|
);
|
|
1026
614
|
}
|
|
1027
|
-
await install(targets,
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
"`fumadocs-ui/layouts/docs` -> `@/components/layouts/docs`"
|
|
1036
|
-
),
|
|
1037
|
-
pageAdded ? picocolors4.greenBright(
|
|
1038
|
-
"`fumadocs-ui/page` -> `@/components/layouts/page`"
|
|
1039
|
-
) : ""
|
|
1040
|
-
].join("\n")
|
|
1041
|
-
);
|
|
615
|
+
await install(targets, installer);
|
|
616
|
+
const maps = [
|
|
617
|
+
["fumadocs-ui/layouts/docs", "@/components/layout/docs"]
|
|
618
|
+
];
|
|
619
|
+
if (pageAdded) {
|
|
620
|
+
maps.push(["fumadocs-ui/page", "@/components/layout/page"]);
|
|
621
|
+
}
|
|
622
|
+
printNext(...maps);
|
|
1042
623
|
}
|
|
1043
624
|
if (result.target === "home") {
|
|
1044
|
-
await install(["layouts/home"],
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
625
|
+
await install(["layouts/home"], installer);
|
|
626
|
+
printNext(["fumadocs-ui/layouts/home", `@/components/layout/home`]);
|
|
627
|
+
}
|
|
628
|
+
outro3(picocolors2.bold("Have fun!"));
|
|
629
|
+
}
|
|
630
|
+
function printNext(...maps) {
|
|
631
|
+
intro2(picocolors2.bold("What is Next?"));
|
|
632
|
+
log3.info(
|
|
633
|
+
[
|
|
634
|
+
"You can check the installed components in `components`.",
|
|
635
|
+
picocolors2.dim("---"),
|
|
636
|
+
"Open your `layout.tsx` files, replace the imports of components:",
|
|
637
|
+
...maps.map(
|
|
638
|
+
([from, to]) => picocolors2.greenBright(`"${from}" -> "${to}"`)
|
|
639
|
+
)
|
|
640
|
+
].join("\n")
|
|
641
|
+
);
|
|
1058
642
|
}
|
|
1059
643
|
|
|
1060
644
|
// src/index.ts
|
|
1061
645
|
var program = new Command().option("--config <string>");
|
|
1062
|
-
program.name("fumadocs").description("CLI to setup Fumadocs, init a config
|
|
646
|
+
program.name("fumadocs").description("CLI to setup Fumadocs, init a config").version(package_default.version).action(async () => {
|
|
1063
647
|
if (await initConfig()) {
|
|
1064
|
-
console.log(
|
|
648
|
+
console.log(picocolors3.green("Initialized a `./cli.json` config file."));
|
|
1065
649
|
} else {
|
|
1066
|
-
console.log(
|
|
650
|
+
console.log(picocolors3.redBright("A config file already exists."));
|
|
1067
651
|
}
|
|
1068
652
|
});
|
|
1069
653
|
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) => {
|
|
1070
654
|
const resolver = getResolverFromDir(options.dir);
|
|
1071
|
-
await customise(resolver, await
|
|
1072
|
-
});
|
|
1073
|
-
program.command("init").description("init a new plugin to your docs").argument("[string]", "plugin name").action(async (str, { config }) => {
|
|
1074
|
-
const loadedConfig = await loadConfig(config);
|
|
1075
|
-
if (str) {
|
|
1076
|
-
const plugin = str in plugins ? plugins[str] : void 0;
|
|
1077
|
-
if (!plugin) throw new Error(`Plugin not found: ${str}`);
|
|
1078
|
-
await init(plugin, loadedConfig);
|
|
1079
|
-
return;
|
|
1080
|
-
}
|
|
1081
|
-
const value = await select2({
|
|
1082
|
-
message: "Select components to install",
|
|
1083
|
-
options: Object.keys(plugins).map((c) => ({
|
|
1084
|
-
label: c,
|
|
1085
|
-
value: c
|
|
1086
|
-
}))
|
|
1087
|
-
});
|
|
1088
|
-
if (isCancel5(value)) {
|
|
1089
|
-
outro4("Ended");
|
|
1090
|
-
return;
|
|
1091
|
-
}
|
|
1092
|
-
await init(plugins[value], loadedConfig);
|
|
655
|
+
await customise(resolver, await createOrLoadConfig(options.config));
|
|
1093
656
|
});
|
|
1094
657
|
var dirShortcuts = {
|
|
1095
658
|
":dev": "https://preview.fumadocs.dev/registry",
|
|
@@ -1098,7 +661,7 @@ var dirShortcuts = {
|
|
|
1098
661
|
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(
|
|
1099
662
|
async (input, options) => {
|
|
1100
663
|
const resolver = getResolverFromDir(options.dir);
|
|
1101
|
-
await add(input, resolver, await
|
|
664
|
+
await add(input, resolver, await createOrLoadConfig(options.config));
|
|
1102
665
|
}
|
|
1103
666
|
);
|
|
1104
667
|
program.command("tree").argument(
|
|
@@ -1118,10 +681,10 @@ program.command("tree").argument(
|
|
|
1118
681
|
} catch {
|
|
1119
682
|
nodes = await runTree(str ?? "./");
|
|
1120
683
|
}
|
|
1121
|
-
const out = js || output && jsExtensions.includes(
|
|
684
|
+
const out = js || output && jsExtensions.includes(path4.extname(output)) ? treeToJavaScript(nodes, noRoot, importName) : treeToMdx(nodes, noRoot);
|
|
1122
685
|
if (output) {
|
|
1123
|
-
await
|
|
1124
|
-
await
|
|
686
|
+
await fs5.mkdir(path4.dirname(output), { recursive: true });
|
|
687
|
+
await fs5.writeFile(output, out);
|
|
1125
688
|
} else {
|
|
1126
689
|
console.log(out);
|
|
1127
690
|
}
|