@fumadocs/cli 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build/index.d.ts +19 -11
- package/dist/build/index.js +73 -51
- package/dist/{chunk-WBCEM7RC.js → chunk-DG6SFM2G.js} +2 -2
- package/dist/index.js +293 -306
- package/package.json +9 -11
package/dist/build/index.d.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import * as ts_morph from 'ts-morph';
|
|
2
2
|
|
|
3
|
+
type ImportPathMap = Record<string, string | {
|
|
4
|
+
type: 'component';
|
|
5
|
+
name: string;
|
|
6
|
+
file: string;
|
|
7
|
+
/**
|
|
8
|
+
* Registry of the component, refer to the current registry if not specified
|
|
9
|
+
*/
|
|
10
|
+
registry?: string;
|
|
11
|
+
} | {
|
|
12
|
+
type: 'dependency';
|
|
13
|
+
name: string;
|
|
14
|
+
}>;
|
|
3
15
|
interface Component {
|
|
4
16
|
name: string;
|
|
5
17
|
description?: string;
|
|
@@ -12,17 +24,9 @@ interface Component {
|
|
|
12
24
|
*/
|
|
13
25
|
unlisted?: boolean;
|
|
14
26
|
/**
|
|
15
|
-
* Map imported file paths
|
|
27
|
+
* Map imported file paths, extended from registry `mapImportPath` if defined.
|
|
16
28
|
*/
|
|
17
|
-
mapImportPath?:
|
|
18
|
-
type: 'component';
|
|
19
|
-
name: string;
|
|
20
|
-
file: string;
|
|
21
|
-
/**
|
|
22
|
-
* Registry of the component, refer to the current registry if not specified
|
|
23
|
-
*/
|
|
24
|
-
registry?: string;
|
|
25
|
-
}>;
|
|
29
|
+
mapImportPath?: ImportPathMap;
|
|
26
30
|
}
|
|
27
31
|
type NamespaceType = 'components' | 'hooks' | 'lib';
|
|
28
32
|
interface PackageJson {
|
|
@@ -52,6 +56,10 @@ interface Registry {
|
|
|
52
56
|
tsconfigPath?: string;
|
|
53
57
|
packageJson?: string | PackageJson;
|
|
54
58
|
components: Component[];
|
|
59
|
+
/**
|
|
60
|
+
* Map import paths of components
|
|
61
|
+
*/
|
|
62
|
+
mapImportPath?: ImportPathMap;
|
|
55
63
|
dependencies?: Record<string, {
|
|
56
64
|
type: 'runtime' | 'dev';
|
|
57
65
|
version?: string;
|
|
@@ -122,4 +130,4 @@ declare function writeOutput(dir: string, out: Output, options?: {
|
|
|
122
130
|
log?: boolean;
|
|
123
131
|
}): Promise<void>;
|
|
124
132
|
|
|
125
|
-
export { type Component, type ComponentBuilder, type DependencyInfo, type NamespaceType, type Output, type OutputComponent, type OutputFile, type OutputIndex, type PackageJson, type Registry, build, createComponentBuilder, writeOutput };
|
|
133
|
+
export { type Component, type ComponentBuilder, type DependencyInfo, type ImportPathMap, type NamespaceType, type Output, type OutputComponent, type OutputFile, type OutputIndex, type PackageJson, type Registry, build, createComponentBuilder, writeOutput };
|
package/dist/build/index.js
CHANGED
|
@@ -1,34 +1,49 @@
|
|
|
1
1
|
import {
|
|
2
2
|
exists
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-DG6SFM2G.js";
|
|
4
4
|
|
|
5
5
|
// src/build/index.ts
|
|
6
|
-
import * as fs3 from "
|
|
7
|
-
import * as path4 from "
|
|
6
|
+
import * as fs3 from "fs/promises";
|
|
7
|
+
import * as path4 from "path";
|
|
8
8
|
import picocolors from "picocolors";
|
|
9
9
|
|
|
10
10
|
// src/build/build-registry.ts
|
|
11
|
-
import * as fs2 from "
|
|
12
|
-
import * as path3 from "
|
|
11
|
+
import * as fs2 from "fs/promises";
|
|
12
|
+
import * as path3 from "path";
|
|
13
13
|
|
|
14
14
|
// src/build/build-file.ts
|
|
15
|
-
import * as path from "
|
|
15
|
+
import * as path from "path";
|
|
16
|
+
import { ts } from "ts-morph";
|
|
16
17
|
async function buildFile(inputPath, outputPath, builder, comp, onReference) {
|
|
17
18
|
const out = {
|
|
18
19
|
imports: {},
|
|
19
20
|
content: "",
|
|
20
21
|
path: outputPath
|
|
21
22
|
};
|
|
22
|
-
|
|
23
|
+
const importMap = {
|
|
24
|
+
...builder.registry.mapImportPath,
|
|
25
|
+
...comp.mapImportPath
|
|
26
|
+
};
|
|
27
|
+
function process2(specifier, getSpecifiedFile) {
|
|
23
28
|
let specifiedFile = getSpecifiedFile();
|
|
24
29
|
if (!specifiedFile) return;
|
|
25
30
|
const name = specifiedFile.isInNodeModules() ? builder.resolveDep(specifier.getLiteralValue()).name : path.relative(builder.registryDir, specifiedFile.getFilePath());
|
|
26
|
-
if (
|
|
27
|
-
const resolver =
|
|
31
|
+
if (name in importMap) {
|
|
32
|
+
const resolver = importMap[name];
|
|
28
33
|
if (typeof resolver === "string") {
|
|
29
34
|
specifier.setLiteralValue(resolver);
|
|
30
35
|
specifiedFile = getSpecifiedFile();
|
|
31
36
|
if (!specifiedFile) return;
|
|
37
|
+
} else if (resolver.type === "dependency") {
|
|
38
|
+
const info = builder.resolveDep(resolver.name);
|
|
39
|
+
const value2 = onReference({
|
|
40
|
+
type: "dependency",
|
|
41
|
+
name: info.name,
|
|
42
|
+
version: info.version ?? "",
|
|
43
|
+
isDev: info.type === "dev"
|
|
44
|
+
});
|
|
45
|
+
if (value2) out.imports[specifier.getLiteralValue()] = value2;
|
|
46
|
+
return;
|
|
32
47
|
} else {
|
|
33
48
|
const sub2 = builder.getComponentByName(
|
|
34
49
|
resolver.name,
|
|
@@ -80,7 +95,7 @@ async function buildFile(inputPath, outputPath, builder, comp, onReference) {
|
|
|
80
95
|
}
|
|
81
96
|
const sourceFile = await builder.createSourceFile(inputPath);
|
|
82
97
|
for (const item of sourceFile.getImportDeclarations()) {
|
|
83
|
-
|
|
98
|
+
process2(
|
|
84
99
|
item.getModuleSpecifier(),
|
|
85
100
|
() => item.getModuleSpecifierSourceFile()
|
|
86
101
|
);
|
|
@@ -88,14 +103,25 @@ async function buildFile(inputPath, outputPath, builder, comp, onReference) {
|
|
|
88
103
|
for (const item of sourceFile.getExportDeclarations()) {
|
|
89
104
|
const specifier = item.getModuleSpecifier();
|
|
90
105
|
if (!specifier) continue;
|
|
91
|
-
|
|
106
|
+
process2(specifier, () => item.getModuleSpecifierSourceFile());
|
|
107
|
+
}
|
|
108
|
+
const calls = sourceFile.getDescendantsOfKind(ts.SyntaxKind.CallExpression);
|
|
109
|
+
for (const expression of calls) {
|
|
110
|
+
if (expression.getExpression().isKind(ts.SyntaxKind.ImportKeyword) && expression.getArguments().length === 1) {
|
|
111
|
+
const argument = expression.getArguments()[0];
|
|
112
|
+
if (!argument.isKind(ts.SyntaxKind.StringLiteral)) continue;
|
|
113
|
+
process2(
|
|
114
|
+
argument,
|
|
115
|
+
() => argument.getSymbol()?.getDeclarations()[0].getSourceFile()
|
|
116
|
+
);
|
|
117
|
+
}
|
|
92
118
|
}
|
|
93
119
|
out.content = sourceFile.getFullText();
|
|
94
120
|
return out;
|
|
95
121
|
}
|
|
96
122
|
|
|
97
123
|
// src/build/component-builder.ts
|
|
98
|
-
import path2 from "
|
|
124
|
+
import path2 from "path";
|
|
99
125
|
import { Project } from "ts-morph";
|
|
100
126
|
import * as fs from "fs/promises";
|
|
101
127
|
function createComponentBuilder(registry, packageJson) {
|
|
@@ -254,7 +280,6 @@ async function buildComponent(component, builder) {
|
|
|
254
280
|
const subComponents = /* @__PURE__ */ new Set();
|
|
255
281
|
const devDependencies = /* @__PURE__ */ new Map();
|
|
256
282
|
const dependencies = /* @__PURE__ */ new Map();
|
|
257
|
-
const files = [];
|
|
258
283
|
async function build2(file) {
|
|
259
284
|
let inputPath;
|
|
260
285
|
let outputPath;
|
|
@@ -272,57 +297,54 @@ async function buildComponent(component, builder) {
|
|
|
272
297
|
inputPath = path3.join(builder.registryDir, file.in);
|
|
273
298
|
outputPath = file.out;
|
|
274
299
|
}
|
|
275
|
-
if (processedFiles.has(inputPath)) return;
|
|
300
|
+
if (processedFiles.has(inputPath)) return [];
|
|
276
301
|
processedFiles.add(inputPath);
|
|
277
302
|
const queue = [];
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
(reference)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
303
|
+
const result = await buildFile(
|
|
304
|
+
inputPath,
|
|
305
|
+
outputPath,
|
|
306
|
+
builder,
|
|
307
|
+
component,
|
|
308
|
+
(reference) => {
|
|
309
|
+
if (reference.type === "file") {
|
|
310
|
+
queue.push(path3.relative(builder.registryDir, reference.file));
|
|
311
|
+
return builder.resolveOutputPath(reference.file);
|
|
312
|
+
}
|
|
313
|
+
if (reference.type === "sub-component") {
|
|
314
|
+
const resolved = reference.resolved;
|
|
315
|
+
subComponents.add(resolved.component.name);
|
|
316
|
+
if (resolved.type === "remote") {
|
|
317
|
+
return reference.targetFile;
|
|
288
318
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
319
|
+
for (const childFile of resolved.component.files) {
|
|
320
|
+
if (typeof childFile === "string" && childFile === reference.targetFile) {
|
|
321
|
+
return builder.resolveOutputPath(
|
|
322
|
+
childFile,
|
|
323
|
+
reference.resolved.registryName
|
|
324
|
+
);
|
|
294
325
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
return builder.resolveOutputPath(
|
|
298
|
-
childFile,
|
|
299
|
-
reference.resolved.registryName
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
if (typeof childFile === "object" && childFile.in === reference.targetFile) {
|
|
303
|
-
return childFile.out;
|
|
304
|
-
}
|
|
326
|
+
if (typeof childFile === "object" && childFile.in === reference.targetFile) {
|
|
327
|
+
return childFile.out;
|
|
305
328
|
}
|
|
306
|
-
throw new Error(
|
|
307
|
-
`Failed to find sub component ${resolved.component.name}'s ${reference.targetFile} referenced by ${inputPath}`
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
if (reference.type === "dependency") {
|
|
311
|
-
if (reference.isDev)
|
|
312
|
-
devDependencies.set(reference.name, reference.version);
|
|
313
|
-
else dependencies.set(reference.name, reference.version);
|
|
314
329
|
}
|
|
330
|
+
throw new Error(
|
|
331
|
+
`Failed to find sub component ${resolved.component.name}'s ${reference.targetFile} referenced by ${inputPath}`
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
if (reference.type === "dependency") {
|
|
335
|
+
if (reference.isDev)
|
|
336
|
+
devDependencies.set(reference.name, reference.version);
|
|
337
|
+
else dependencies.set(reference.name, reference.version);
|
|
315
338
|
}
|
|
316
|
-
|
|
339
|
+
}
|
|
317
340
|
);
|
|
318
|
-
await Promise.all(queue.map(build2));
|
|
341
|
+
return [result, ...(await Promise.all(queue.map(build2))).flat()];
|
|
319
342
|
}
|
|
320
|
-
await Promise.all(component.files.map(build2));
|
|
321
343
|
return [
|
|
322
344
|
component,
|
|
323
345
|
{
|
|
324
346
|
name: component.name,
|
|
325
|
-
files,
|
|
347
|
+
files: (await Promise.all(component.files.map(build2))).flat(),
|
|
326
348
|
subComponents: Array.from(subComponents),
|
|
327
349
|
dependencies: Object.fromEntries(dependencies),
|
|
328
350
|
devDependencies: Object.fromEntries(devDependencies)
|
package/dist/index.js
CHANGED
|
@@ -2,36 +2,29 @@
|
|
|
2
2
|
import {
|
|
3
3
|
exists,
|
|
4
4
|
isRelative
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-DG6SFM2G.js";
|
|
6
6
|
|
|
7
7
|
// src/index.ts
|
|
8
|
-
import fs10 from "
|
|
9
|
-
import path9 from "
|
|
8
|
+
import fs10 from "fs/promises";
|
|
9
|
+
import path9 from "path";
|
|
10
10
|
import { Command } from "commander";
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
isCancel as isCancel3,
|
|
14
|
-
log as log6,
|
|
15
|
-
multiselect,
|
|
16
|
-
outro as outro2,
|
|
17
|
-
select as select2,
|
|
18
|
-
spinner as spinner3
|
|
19
|
-
} from "@clack/prompts";
|
|
11
|
+
import picocolors5 from "picocolors";
|
|
12
|
+
import { isCancel as isCancel5, outro as outro4, select as select2 } from "@clack/prompts";
|
|
20
13
|
|
|
21
14
|
// src/commands/init.ts
|
|
22
|
-
import * as process2 from "
|
|
23
|
-
import path3 from "
|
|
15
|
+
import * as process2 from "process";
|
|
16
|
+
import path3 from "path";
|
|
24
17
|
import {
|
|
25
|
-
|
|
18
|
+
cancel,
|
|
26
19
|
confirm,
|
|
20
|
+
intro,
|
|
27
21
|
isCancel,
|
|
28
|
-
cancel,
|
|
29
|
-
spinner,
|
|
30
22
|
log,
|
|
31
|
-
note
|
|
23
|
+
note,
|
|
24
|
+
spinner
|
|
32
25
|
} from "@clack/prompts";
|
|
33
26
|
import picocolors from "picocolors";
|
|
34
|
-
import {
|
|
27
|
+
import { x } from "tinyexec";
|
|
35
28
|
|
|
36
29
|
// src/utils/get-package-manager.ts
|
|
37
30
|
import { detect } from "package-manager-detector";
|
|
@@ -41,7 +34,7 @@ async function getPackageManager() {
|
|
|
41
34
|
}
|
|
42
35
|
|
|
43
36
|
// src/utils/is-src.ts
|
|
44
|
-
import path from "
|
|
37
|
+
import path from "path";
|
|
45
38
|
async function isSrc() {
|
|
46
39
|
return exists("./src");
|
|
47
40
|
}
|
|
@@ -49,11 +42,8 @@ function resolveAppPath(filePath, src2) {
|
|
|
49
42
|
return src2 ? path.join("./src", filePath) : filePath;
|
|
50
43
|
}
|
|
51
44
|
|
|
52
|
-
// src/utils/transform-references.ts
|
|
53
|
-
import path2 from "node:path";
|
|
54
|
-
|
|
55
45
|
// src/config.ts
|
|
56
|
-
import fs from "
|
|
46
|
+
import fs from "fs/promises";
|
|
57
47
|
var src = await isSrc();
|
|
58
48
|
var defaultConfig = {
|
|
59
49
|
aliases: {
|
|
@@ -72,44 +62,23 @@ async function loadConfig(file = "./cli.json") {
|
|
|
72
62
|
}
|
|
73
63
|
}
|
|
74
64
|
async function initConfig(file = "./cli.json") {
|
|
65
|
+
if (await fs.stat(file).then(() => true).catch(() => false)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
75
68
|
await fs.writeFile(file, JSON.stringify(defaultConfig, null, 2));
|
|
69
|
+
return true;
|
|
76
70
|
}
|
|
77
71
|
|
|
72
|
+
// src/utils/transform-references.ts
|
|
73
|
+
import path2 from "path";
|
|
74
|
+
|
|
78
75
|
// src/constants.ts
|
|
79
76
|
var typescriptExtensions = [".ts", ".tsx", ".js", ".jsx"];
|
|
80
77
|
|
|
81
78
|
// src/utils/transform-references.ts
|
|
82
|
-
function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return config.aliases?.cn ?? defaultConfig.aliases.cn;
|
|
86
|
-
}
|
|
87
|
-
if (ref.startsWith("components")) {
|
|
88
|
-
return path2.join(
|
|
89
|
-
config.aliases?.componentsDir ?? defaultConfig.aliases.componentsDir,
|
|
90
|
-
path2.relative("components", ref)
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
if (ref.startsWith("lib") || ref.startsWith("utils")) {
|
|
94
|
-
return path2.join(
|
|
95
|
-
config.aliases?.libDir ?? defaultConfig.aliases.libDir,
|
|
96
|
-
path2.relative("lib", ref)
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
return ref;
|
|
100
|
-
}
|
|
101
|
-
async function transformReferences(file, resolver, transform) {
|
|
102
|
-
for (const item of file.getImportDeclarations()) {
|
|
103
|
-
const ref = item.getModuleSpecifier().getLiteralValue();
|
|
104
|
-
const result = await transform(resolveReference(ref, resolver), ref);
|
|
105
|
-
if (!result) continue;
|
|
106
|
-
item.getModuleSpecifier().setLiteralValue(result);
|
|
107
|
-
}
|
|
108
|
-
for (const item of file.getExportDeclarations()) {
|
|
109
|
-
const specifier = item.getModuleSpecifier();
|
|
110
|
-
if (!specifier) continue;
|
|
111
|
-
const ref = specifier.getLiteralValue();
|
|
112
|
-
const result = await transform(resolveReference(ref, resolver), ref);
|
|
79
|
+
function transformReferences(file, transform) {
|
|
80
|
+
for (const specifier of file.getImportStringLiterals()) {
|
|
81
|
+
const result = transform(specifier.getLiteralValue());
|
|
113
82
|
if (!result) continue;
|
|
114
83
|
specifier.setLiteralValue(result);
|
|
115
84
|
}
|
|
@@ -195,20 +164,17 @@ async function init(plugin, config = {}) {
|
|
|
195
164
|
const sourceFile = project.createSourceFile(file, content, {
|
|
196
165
|
overwrite: true
|
|
197
166
|
});
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
{
|
|
167
|
+
transformReferences(sourceFile, (specifier) => {
|
|
168
|
+
const resolved = resolveReference(specifier, {
|
|
201
169
|
alias: {
|
|
202
170
|
type: "append",
|
|
203
171
|
dir: ctx.src ? "src" : ""
|
|
204
172
|
},
|
|
205
173
|
relativeTo: path3.dirname(file)
|
|
206
|
-
}
|
|
207
|
-
(resolved)
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
);
|
|
174
|
+
});
|
|
175
|
+
if (resolved.type !== "file") return;
|
|
176
|
+
return toReferencePath(file, getOutputPath(resolved.path, ctx));
|
|
177
|
+
});
|
|
212
178
|
await sourceFile.save();
|
|
213
179
|
}
|
|
214
180
|
if (plugin.dependencies.length > 0) {
|
|
@@ -223,7 +189,7 @@ async function init(plugin, config = {}) {
|
|
|
223
189
|
if (value) {
|
|
224
190
|
const spin = spinner();
|
|
225
191
|
spin.start("Installing dependencies");
|
|
226
|
-
await
|
|
192
|
+
await x(manager, ["install", ...plugin.dependencies]);
|
|
227
193
|
spin.stop("Successfully installed.");
|
|
228
194
|
}
|
|
229
195
|
}
|
|
@@ -261,76 +227,38 @@ prettier . --write`,
|
|
|
261
227
|
}
|
|
262
228
|
}
|
|
263
229
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
import { log as log2, confirm as confirm2, isCancel as isCancel2, outro, spinner as spinner2, intro as intro2 } from "@clack/prompts";
|
|
269
|
-
import picocolors2 from "picocolors";
|
|
270
|
-
import { execa as execa2 } from "execa";
|
|
271
|
-
|
|
272
|
-
// src/utils/add/get-dependencies.ts
|
|
273
|
-
import fs2 from "node:fs/promises";
|
|
274
|
-
async function getDependencies() {
|
|
275
|
-
const dependencies = /* @__PURE__ */ new Map();
|
|
276
|
-
if (!await exists("package.json")) return dependencies;
|
|
277
|
-
const content = await fs2.readFile("package.json");
|
|
278
|
-
const parsed = JSON.parse(content.toString());
|
|
279
|
-
if ("dependencies" in parsed && typeof parsed.dependencies === "object") {
|
|
280
|
-
const records = parsed.dependencies;
|
|
281
|
-
Object.entries(records).forEach(([k, v]) => {
|
|
282
|
-
dependencies.set(k, v);
|
|
283
|
-
});
|
|
230
|
+
function getOutputPath(ref, config) {
|
|
231
|
+
if (path3.isAbsolute(ref)) throw new Error(`path cannot be absolute: ${ref}`);
|
|
232
|
+
if (ref === "utils/cn" || ref === "utils/cn.ts") {
|
|
233
|
+
return config.aliases?.cn ?? defaultConfig.aliases.cn;
|
|
284
234
|
}
|
|
285
|
-
if (
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
235
|
+
if (ref.startsWith("components")) {
|
|
236
|
+
return path3.join(
|
|
237
|
+
config.aliases?.componentsDir ?? defaultConfig.aliases.componentsDir,
|
|
238
|
+
path3.relative("components", ref)
|
|
239
|
+
);
|
|
290
240
|
}
|
|
291
|
-
|
|
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;
|
|
292
248
|
}
|
|
293
249
|
|
|
294
|
-
// src/
|
|
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";
|
|
295
254
|
var downloadedFiles = /* @__PURE__ */ new Set();
|
|
296
|
-
async function
|
|
297
|
-
intro2(
|
|
298
|
-
picocolors2.bold(
|
|
299
|
-
picocolors2.inverse(picocolors2.cyanBright(`Add Component: ${name}`))
|
|
300
|
-
)
|
|
301
|
-
);
|
|
255
|
+
async function installComponent(name, resolver, config = {}) {
|
|
302
256
|
const project = createEmptyProject();
|
|
303
|
-
|
|
257
|
+
return downloadComponent(name, {
|
|
304
258
|
project,
|
|
305
259
|
config,
|
|
306
260
|
resolver
|
|
307
261
|
});
|
|
308
|
-
if (!result) {
|
|
309
|
-
log2.error(`Component: ${name} not found`);
|
|
310
|
-
process.exit(0);
|
|
311
|
-
}
|
|
312
|
-
const installed = await getDependencies();
|
|
313
|
-
const deps = Object.entries(result.dependencies).filter(([k]) => !installed.has(k)).map(([k, v]) => v.length === 0 ? k : `${k}@${v}`);
|
|
314
|
-
const devDeps = Object.entries(result.devDependencies).filter(([k]) => !installed.has(k)).map(([k, v]) => v.length === 0 ? k : `${k}@${v}`);
|
|
315
|
-
if (deps.length > 0 || devDeps.length > 0) {
|
|
316
|
-
const manager = await getPackageManager();
|
|
317
|
-
const value = await confirm2({
|
|
318
|
-
message: `This component requires dependencies (${[...deps, ...devDeps].join(" ")}), install them with ${manager}?`
|
|
319
|
-
});
|
|
320
|
-
if (isCancel2(value)) {
|
|
321
|
-
outro(picocolors2.bold(picocolors2.greenBright("Component downloaded")));
|
|
322
|
-
process.exit(0);
|
|
323
|
-
}
|
|
324
|
-
if (value) {
|
|
325
|
-
const spin = spinner2();
|
|
326
|
-
spin.start("Installing dependencies...");
|
|
327
|
-
if (deps.length > 0) await execa2(manager, ["install", ...deps]);
|
|
328
|
-
if (devDeps.length > 0)
|
|
329
|
-
await execa2(manager, ["install", ...devDeps, "-D"]);
|
|
330
|
-
spin.stop("Dependencies installed.");
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
outro(picocolors2.bold(picocolors2.greenBright("Component installed")));
|
|
334
262
|
}
|
|
335
263
|
var downloadedComps = /* @__PURE__ */ new Map();
|
|
336
264
|
async function downloadComponent(name, ctx) {
|
|
@@ -344,7 +272,7 @@ async function downloadComponent(name, ctx) {
|
|
|
344
272
|
const outPath = resolveOutputPath(file.path, ctx.config);
|
|
345
273
|
const output = typescriptExtensions.includes(path4.extname(file.path)) ? transformTypeScript(outPath, file, ctx) : file.content;
|
|
346
274
|
let canWrite = true;
|
|
347
|
-
const requireOverride = await
|
|
275
|
+
const requireOverride = await fs2.readFile(outPath).then((res) => res.toString() !== output).catch(() => false);
|
|
348
276
|
if (requireOverride) {
|
|
349
277
|
const value = await confirm2({
|
|
350
278
|
message: `Do you want to override ${outPath}?`
|
|
@@ -356,8 +284,8 @@ async function downloadComponent(name, ctx) {
|
|
|
356
284
|
canWrite = value;
|
|
357
285
|
}
|
|
358
286
|
if (canWrite) {
|
|
359
|
-
await
|
|
360
|
-
await
|
|
287
|
+
await fs2.mkdir(path4.dirname(outPath), { recursive: true });
|
|
288
|
+
await fs2.writeFile(outPath, output);
|
|
361
289
|
log2.step(`downloaded ${outPath}`);
|
|
362
290
|
}
|
|
363
291
|
downloadedFiles.add(file.path);
|
|
@@ -389,22 +317,12 @@ function transformTypeScript(filePath, file, ctx) {
|
|
|
389
317
|
const sourceFile = ctx.project.createSourceFile(filePath, file.content, {
|
|
390
318
|
overwrite: true
|
|
391
319
|
});
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
item.getModuleSpecifier().setLiteralValue(toReferencePath(filePath, outputPath));
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
for (const item of sourceFile.getExportDeclarations()) {
|
|
400
|
-
const specifier = item.getModuleSpecifier();
|
|
401
|
-
if (!specifier) continue;
|
|
402
|
-
const ref = specifier.getLiteralValue();
|
|
403
|
-
if (ref in file.imports) {
|
|
404
|
-
const outputPath = resolveOutputPath(file.imports[ref], ctx.config);
|
|
405
|
-
specifier.setLiteralValue(toReferencePath(filePath, outputPath));
|
|
320
|
+
transformReferences(sourceFile, (specifier) => {
|
|
321
|
+
if (specifier in file.imports) {
|
|
322
|
+
const outputPath = resolveOutputPath(file.imports[specifier], ctx.config);
|
|
323
|
+
return toReferencePath(filePath, outputPath);
|
|
406
324
|
}
|
|
407
|
-
}
|
|
325
|
+
});
|
|
408
326
|
return sourceFile.getFullText();
|
|
409
327
|
}
|
|
410
328
|
function remoteResolver(url) {
|
|
@@ -416,75 +334,24 @@ function remoteResolver(url) {
|
|
|
416
334
|
}
|
|
417
335
|
function localResolver(dir) {
|
|
418
336
|
return async (file) => {
|
|
419
|
-
return await
|
|
337
|
+
return await fs2.readFile(path4.join(dir, file)).then((res) => JSON.parse(res.toString())).catch(() => void 0);
|
|
420
338
|
};
|
|
421
339
|
}
|
|
422
340
|
|
|
423
|
-
// src/plugins/og-image.ts
|
|
424
|
-
import picocolors3 from "picocolors";
|
|
425
|
-
|
|
426
|
-
// src/generated.js
|
|
427
|
-
var generated = { "lib/metadata": "import { createMetadataImage } from 'fumadocs-core/server';\nimport { source } from '@/lib/source';\n\nexport const metadataImage = createMetadataImage({\n imageRoute: '/docs-og',\n source,\n});\n", "app/docs-og/[...slug]/route": "import { generateOGImage } from 'fumadocs-ui/og';\nimport { metadataImage } from '@/lib/metadata';\n\nexport const GET = metadataImage.createAPI((page) => {\n return generateOGImage({\n title: page.data.title,\n description: page.data.description,\n site: 'My App',\n });\n});\n\nexport function generateStaticParams() {\n return metadataImage.generateParams();\n}\n", "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 { rimrafSync } from 'rimraf';\n\nconst out = './content/docs/(api)';\n\n// clean generated files\nrimrafSync(out, {\n filter(v) {\n return !v.endsWith('index.mdx') && !v.endsWith('meta.json');\n },\n});\n\nvoid OpenAPI.generateFiles({\n // input files\n input: ['./openapi.json'],\n output: out,\n groupBy: 'tag',\n});\n" };
|
|
428
|
-
|
|
429
|
-
// src/plugins/og-image.ts
|
|
430
|
-
function isI18nEnabled(ctx) {
|
|
431
|
-
return exists(getOutputPath("lib/i18n.ts", ctx));
|
|
432
|
-
}
|
|
433
|
-
var ogImagePlugin = {
|
|
434
|
-
files: async (ctx) => {
|
|
435
|
-
const route = await isI18nEnabled(ctx) ? "app/[lang]/docs-og/[...slug]/route.tsx" : "app/docs-og/[...slug]/route.tsx";
|
|
436
|
-
return {
|
|
437
|
-
"lib/metadata.ts": generated["lib/metadata"],
|
|
438
|
-
[ctx.src ? `src/${route}` : route]: generated["app/docs-og/[...slug]/route"]
|
|
439
|
-
};
|
|
440
|
-
},
|
|
441
|
-
dependencies: [],
|
|
442
|
-
instructions: (ctx) => [
|
|
443
|
-
{
|
|
444
|
-
type: "text",
|
|
445
|
-
text: picocolors3.cyanBright(picocolors3.bold("Import the utils like:"))
|
|
446
|
-
},
|
|
447
|
-
{
|
|
448
|
-
type: "code",
|
|
449
|
-
title: "ts",
|
|
450
|
-
code: 'import { metadataImage } from "@/lib/metadata";'
|
|
451
|
-
},
|
|
452
|
-
{
|
|
453
|
-
type: "text",
|
|
454
|
-
text: picocolors3.cyanBright(
|
|
455
|
-
picocolors3.bold("Add the images to your metadata:")
|
|
456
|
-
)
|
|
457
|
-
},
|
|
458
|
-
{
|
|
459
|
-
type: "code",
|
|
460
|
-
title: resolveAppPath("app/docs/[[...slug]]/page.tsx", ctx.src),
|
|
461
|
-
code: `
|
|
462
|
-
export function generateMetadata({ params }: { params: { slug?: string[] } }) {
|
|
463
|
-
const page = source.getPage(params.slug);
|
|
464
|
-
|
|
465
|
-
if (!page) notFound();
|
|
466
|
-
|
|
467
|
-
${picocolors3.bold(picocolors3.underline("return metadataImage.withImage(page.slugs, {"))}
|
|
468
|
-
title: page.data.title,
|
|
469
|
-
description: page.data.description,
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
`.trim()
|
|
473
|
-
}
|
|
474
|
-
]
|
|
475
|
-
};
|
|
476
|
-
|
|
477
341
|
// src/plugins/i18n.ts
|
|
478
|
-
import path7 from "
|
|
342
|
+
import path7 from "path";
|
|
479
343
|
import { log as log3 } from "@clack/prompts";
|
|
480
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
|
+
|
|
481
348
|
// src/utils/i18n/transform-layout-config.ts
|
|
482
|
-
import
|
|
349
|
+
import fs3 from "fs/promises";
|
|
483
350
|
import { SyntaxKind } from "ts-morph";
|
|
484
351
|
async function transformLayoutConfig(project, filePath) {
|
|
485
352
|
let content;
|
|
486
353
|
try {
|
|
487
|
-
content = await
|
|
354
|
+
content = await fs3.readFile(filePath).then((res) => res.toString());
|
|
488
355
|
} catch {
|
|
489
356
|
return;
|
|
490
357
|
}
|
|
@@ -506,18 +373,18 @@ async function transformLayoutConfig(project, filePath) {
|
|
|
506
373
|
}
|
|
507
374
|
|
|
508
375
|
// src/utils/move-files.ts
|
|
509
|
-
import
|
|
510
|
-
import path5 from "
|
|
376
|
+
import fs4 from "fs/promises";
|
|
377
|
+
import path5 from "path";
|
|
511
378
|
var transformExtensions = [".js", ".ts", ".tsx", ".jsx"];
|
|
512
379
|
async function moveFiles(from, to, filter, project, src2, originalDir = from) {
|
|
513
380
|
function isIncluded(file) {
|
|
514
381
|
if (!transformExtensions.includes(path5.extname(file))) return false;
|
|
515
382
|
return filter(path5.resolve(file));
|
|
516
383
|
}
|
|
517
|
-
const stats = await
|
|
384
|
+
const stats = await fs4.lstat(from).catch(() => void 0);
|
|
518
385
|
if (!stats) return;
|
|
519
386
|
if (stats.isDirectory()) {
|
|
520
|
-
const items = await
|
|
387
|
+
const items = await fs4.readdir(from);
|
|
521
388
|
await Promise.all(
|
|
522
389
|
items.map(async (item) => {
|
|
523
390
|
await moveFiles(
|
|
@@ -530,46 +397,43 @@ async function moveFiles(from, to, filter, project, src2, originalDir = from) {
|
|
|
530
397
|
);
|
|
531
398
|
})
|
|
532
399
|
);
|
|
533
|
-
await
|
|
400
|
+
await fs4.rmdir(from).catch(() => {
|
|
534
401
|
});
|
|
535
402
|
}
|
|
536
|
-
if (!stats.isFile() || !
|
|
537
|
-
const content = await
|
|
403
|
+
if (!stats.isFile() || !isIncluded(from)) return;
|
|
404
|
+
const content = await fs4.readFile(from);
|
|
538
405
|
const sourceFile = project.createSourceFile(from, content.toString(), {
|
|
539
406
|
overwrite: true
|
|
540
407
|
});
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
{
|
|
408
|
+
transformReferences(sourceFile, (specifier) => {
|
|
409
|
+
const resolved = resolveReference(specifier, {
|
|
544
410
|
alias: {
|
|
545
411
|
type: "append",
|
|
546
412
|
dir: src2 ? "src" : ""
|
|
547
413
|
},
|
|
548
414
|
relativeTo: path5.dirname(from)
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
if
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
}
|
|
559
|
-
);
|
|
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
|
+
});
|
|
560
424
|
await sourceFile.save();
|
|
561
|
-
await
|
|
562
|
-
await
|
|
425
|
+
await fs4.mkdir(path5.dirname(to), { recursive: true });
|
|
426
|
+
await fs4.rename(from, to);
|
|
563
427
|
}
|
|
564
428
|
|
|
565
429
|
// src/utils/i18n/transform-source-i18n.ts
|
|
566
|
-
import * as
|
|
567
|
-
import path6 from "
|
|
430
|
+
import * as fs5 from "fs/promises";
|
|
431
|
+
import path6 from "path";
|
|
568
432
|
import { StructureKind, SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
569
433
|
async function transformSourceI18n(project, filePath, config) {
|
|
570
434
|
let content;
|
|
571
435
|
try {
|
|
572
|
-
content = await
|
|
436
|
+
content = await fs5.readFile(filePath).then((res) => res.toString());
|
|
573
437
|
} catch {
|
|
574
438
|
return;
|
|
575
439
|
}
|
|
@@ -598,14 +462,14 @@ async function transformSourceI18n(project, filePath, config) {
|
|
|
598
462
|
}
|
|
599
463
|
|
|
600
464
|
// src/utils/i18n/transform-root-layout.ts
|
|
601
|
-
import
|
|
465
|
+
import fs6 from "fs/promises";
|
|
602
466
|
import { StructureKind as StructureKind2, ts } from "ts-morph";
|
|
603
467
|
var ScriptKind = ts.ScriptKind;
|
|
604
468
|
var SyntaxKind3 = ts.SyntaxKind;
|
|
605
469
|
async function transformRootLayout(project, filePath) {
|
|
606
470
|
let content;
|
|
607
471
|
try {
|
|
608
|
-
content = await
|
|
472
|
+
content = await fs6.readFile(filePath).then((res) => res.toString());
|
|
609
473
|
} catch {
|
|
610
474
|
return;
|
|
611
475
|
}
|
|
@@ -647,7 +511,7 @@ function runTransform(sourceFile) {
|
|
|
647
511
|
}
|
|
648
512
|
|
|
649
513
|
// src/plugins/i18n.ts
|
|
650
|
-
import
|
|
514
|
+
import picocolors2 from "picocolors";
|
|
651
515
|
var i18nPlugin = {
|
|
652
516
|
files: ({ src: src2 }) => ({
|
|
653
517
|
"lib/i18n.ts": generated["lib/i18n"],
|
|
@@ -657,7 +521,7 @@ var i18nPlugin = {
|
|
|
657
521
|
instructions: () => [
|
|
658
522
|
{
|
|
659
523
|
type: "title",
|
|
660
|
-
text: `1. Update the params of ${
|
|
524
|
+
text: `1. Update the params of ${picocolors2.bold("page.tsx")} and ${picocolors2.bold("layout.tsx")}, and make them async if necessary.`
|
|
661
525
|
},
|
|
662
526
|
{
|
|
663
527
|
type: "code",
|
|
@@ -666,7 +530,7 @@ var i18nPlugin = {
|
|
|
666
530
|
export default async function Layout({
|
|
667
531
|
params,
|
|
668
532
|
}: {
|
|
669
|
-
${
|
|
533
|
+
${picocolors2.underline(picocolors2.bold("params: Promise<{ lang: string }>"))}
|
|
670
534
|
})
|
|
671
535
|
`.trim()
|
|
672
536
|
},
|
|
@@ -677,7 +541,7 @@ export default async function Layout({
|
|
|
677
541
|
export default async function Page({
|
|
678
542
|
params,
|
|
679
543
|
}: {
|
|
680
|
-
${
|
|
544
|
+
${picocolors2.underline(picocolors2.bold("params: Promise<{ lang: string; slug?: string[] }>"))}
|
|
681
545
|
})
|
|
682
546
|
`.trim()
|
|
683
547
|
},
|
|
@@ -731,12 +595,12 @@ See https://nextjs.org/docs/app/building-your-application/routing/internationali
|
|
|
731
595
|
};
|
|
732
596
|
|
|
733
597
|
// src/plugins/openapi.ts
|
|
734
|
-
import
|
|
735
|
-
import path8 from "
|
|
598
|
+
import fs8 from "fs/promises";
|
|
599
|
+
import path8 from "path";
|
|
736
600
|
import { StructureKind as StructureKind3 } from "ts-morph";
|
|
737
601
|
|
|
738
602
|
// src/utils/transform-tailwind.ts
|
|
739
|
-
import
|
|
603
|
+
import fs7 from "fs/promises";
|
|
740
604
|
import { SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
741
605
|
import { log as log4 } from "@clack/prompts";
|
|
742
606
|
var tailwindConfigPaths = [
|
|
@@ -762,7 +626,7 @@ async function transformTailwind(project, options) {
|
|
|
762
626
|
}
|
|
763
627
|
const configFile = project.createSourceFile(
|
|
764
628
|
file,
|
|
765
|
-
await
|
|
629
|
+
await fs7.readFile(file).then((res) => res.toString()),
|
|
766
630
|
{ overwrite: true }
|
|
767
631
|
);
|
|
768
632
|
const exports = configFile.getExportAssignments();
|
|
@@ -823,21 +687,21 @@ import { openapi } from '@/lib/source';
|
|
|
823
687
|
]
|
|
824
688
|
};
|
|
825
689
|
async function addScript() {
|
|
826
|
-
const content = await
|
|
690
|
+
const content = await fs8.readFile("package.json");
|
|
827
691
|
const parsed = JSON.parse(content.toString());
|
|
828
692
|
if (typeof parsed.scripts !== "object") return;
|
|
829
693
|
parsed.scripts ??= {};
|
|
830
694
|
Object.assign(parsed.scripts ?? {}, {
|
|
831
695
|
"build:docs": "node ./scripts/generate-docs.mjs"
|
|
832
696
|
});
|
|
833
|
-
await
|
|
697
|
+
await fs8.writeFile("package.json", JSON.stringify(parsed, null, 2));
|
|
834
698
|
}
|
|
835
699
|
async function transformSource(project, config) {
|
|
836
700
|
const source = path8.join(
|
|
837
701
|
config.aliases?.libDir ?? defaultConfig.aliases.libDir,
|
|
838
702
|
"source.ts"
|
|
839
703
|
);
|
|
840
|
-
const content = await
|
|
704
|
+
const content = await fs8.readFile(source).catch(() => "");
|
|
841
705
|
const file = project.createSourceFile(source, content.toString(), {
|
|
842
706
|
overwrite: true
|
|
843
707
|
});
|
|
@@ -852,7 +716,6 @@ async function transformSource(project, config) {
|
|
|
852
716
|
|
|
853
717
|
// src/plugins/index.ts
|
|
854
718
|
var plugins = {
|
|
855
|
-
"og-image": ogImagePlugin,
|
|
856
719
|
i18n: i18nPlugin,
|
|
857
720
|
openapi: openapiPlugin
|
|
858
721
|
};
|
|
@@ -893,9 +756,9 @@ export default (${treeToMdx(input, noRoot)})`;
|
|
|
893
756
|
}
|
|
894
757
|
|
|
895
758
|
// src/utils/file-tree/run-tree.ts
|
|
896
|
-
import {
|
|
759
|
+
import { x as x2 } from "tinyexec";
|
|
897
760
|
async function runTree(args) {
|
|
898
|
-
const out = await
|
|
761
|
+
const out = await x2("tree", [args, "--gitignore", "--prune", "-J"]);
|
|
899
762
|
try {
|
|
900
763
|
return JSON.parse(out.stdout);
|
|
901
764
|
} catch (e) {
|
|
@@ -908,7 +771,7 @@ async function runTree(args) {
|
|
|
908
771
|
// package.json
|
|
909
772
|
var package_default = {
|
|
910
773
|
name: "@fumadocs/cli",
|
|
911
|
-
version: "0.
|
|
774
|
+
version: "0.2.0",
|
|
912
775
|
description: "The CLI tool for Fumadocs",
|
|
913
776
|
keywords: [
|
|
914
777
|
"NextJs",
|
|
@@ -942,21 +805,19 @@ var package_default = {
|
|
|
942
805
|
"types:check": "tsc --noEmit"
|
|
943
806
|
},
|
|
944
807
|
dependencies: {
|
|
945
|
-
"@clack/prompts": "^0.10.
|
|
946
|
-
commander: "^
|
|
947
|
-
|
|
948
|
-
"package-manager-detector": "^1.1.0",
|
|
808
|
+
"@clack/prompts": "^0.10.1",
|
|
809
|
+
commander: "^14.0.0",
|
|
810
|
+
"package-manager-detector": "^1.3.0",
|
|
949
811
|
picocolors: "^1.1.1",
|
|
950
|
-
|
|
812
|
+
tinyexec: "^1.0.1",
|
|
813
|
+
"ts-morph": "^26.0.0"
|
|
951
814
|
},
|
|
952
815
|
devDependencies: {
|
|
953
|
-
"@types/
|
|
954
|
-
"@types/
|
|
955
|
-
"@types/react": "^19.0.12",
|
|
816
|
+
"@types/node": "22.15.19",
|
|
817
|
+
"@types/react": "^19.1.4",
|
|
956
818
|
"eslint-config-custom": "workspace:*",
|
|
957
|
-
"fast-glob": "^3.3.3",
|
|
958
819
|
tsconfig: "workspace:*",
|
|
959
|
-
tsx: "^4.19.
|
|
820
|
+
tsx: "^4.19.4"
|
|
960
821
|
},
|
|
961
822
|
publishConfig: {
|
|
962
823
|
access: "public"
|
|
@@ -964,10 +825,136 @@ var package_default = {
|
|
|
964
825
|
};
|
|
965
826
|
|
|
966
827
|
// src/commands/customise.ts
|
|
967
|
-
import {
|
|
968
|
-
|
|
828
|
+
import {
|
|
829
|
+
cancel as cancel2,
|
|
830
|
+
confirm as confirm4,
|
|
831
|
+
group,
|
|
832
|
+
intro as intro3,
|
|
833
|
+
log as log6,
|
|
834
|
+
outro as outro3,
|
|
835
|
+
select
|
|
836
|
+
} from "@clack/prompts";
|
|
837
|
+
import picocolors4 from "picocolors";
|
|
838
|
+
|
|
839
|
+
// src/commands/add.ts
|
|
840
|
+
import {
|
|
841
|
+
intro as intro2,
|
|
842
|
+
isCancel as isCancel4,
|
|
843
|
+
log as log5,
|
|
844
|
+
multiselect,
|
|
845
|
+
outro as outro2,
|
|
846
|
+
spinner as spinner3
|
|
847
|
+
} from "@clack/prompts";
|
|
848
|
+
import picocolors3 from "picocolors";
|
|
849
|
+
|
|
850
|
+
// src/utils/add/install-deps.ts
|
|
851
|
+
import { confirm as confirm3, isCancel as isCancel3, spinner as spinner2 } from "@clack/prompts";
|
|
852
|
+
import { x as x3 } from "tinyexec";
|
|
853
|
+
|
|
854
|
+
// src/utils/add/get-deps.ts
|
|
855
|
+
import fs9 from "fs/promises";
|
|
856
|
+
async function getDeps() {
|
|
857
|
+
const dependencies = /* @__PURE__ */ new Map();
|
|
858
|
+
if (!await exists("package.json")) return dependencies;
|
|
859
|
+
const content = await fs9.readFile("package.json");
|
|
860
|
+
const parsed = JSON.parse(content.toString());
|
|
861
|
+
if ("dependencies" in parsed && typeof parsed.dependencies === "object") {
|
|
862
|
+
const records = parsed.dependencies;
|
|
863
|
+
Object.entries(records).forEach(([k, v]) => {
|
|
864
|
+
dependencies.set(k, v);
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
if ("devDependencies" in parsed && typeof parsed.devDependencies === "object") {
|
|
868
|
+
const records = parsed.devDependencies;
|
|
869
|
+
Object.entries(records).forEach(([k, v]) => {
|
|
870
|
+
dependencies.set(k, v);
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
return dependencies;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// src/utils/add/install-deps.ts
|
|
877
|
+
async function installDeps(results) {
|
|
878
|
+
const installed = await getDeps();
|
|
879
|
+
const deps = {};
|
|
880
|
+
const devDeps = {};
|
|
881
|
+
for (const result of results) {
|
|
882
|
+
Object.assign(deps, result.dependencies);
|
|
883
|
+
Object.assign(devDeps, result.devDependencies);
|
|
884
|
+
}
|
|
885
|
+
const items = Object.entries(deps).filter(([k]) => !installed.has(k)).map(([k, v]) => v.length === 0 ? k : `${k}@${v}`);
|
|
886
|
+
const devItems = Object.entries(devDeps).filter(([k]) => !installed.has(k)).map(([k, v]) => v.length === 0 ? k : `${k}@${v}`);
|
|
887
|
+
if (items.length > 0 || devItems.length > 0) {
|
|
888
|
+
const manager = await getPackageManager();
|
|
889
|
+
const value = await confirm3({
|
|
890
|
+
message: `Do you want to install with ${manager}?
|
|
891
|
+
${[...items, ...devItems].map((v) => `- ${v}`).join("\n")}`
|
|
892
|
+
});
|
|
893
|
+
if (isCancel3(value)) {
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
if (value) {
|
|
897
|
+
const spin = spinner2();
|
|
898
|
+
spin.start("Installing dependencies...");
|
|
899
|
+
if (items.length > 0) await x3(manager, ["install", ...items]);
|
|
900
|
+
if (devItems.length > 0) await x3(manager, ["install", ...devItems, "-D"]);
|
|
901
|
+
spin.stop("Dependencies installed.");
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// src/commands/add.ts
|
|
907
|
+
async function add(input, resolver, config) {
|
|
908
|
+
let target = input;
|
|
909
|
+
if (input.length === 0) {
|
|
910
|
+
const spin = spinner3();
|
|
911
|
+
spin.start("fetching registry");
|
|
912
|
+
const registry = await resolver("_registry.json");
|
|
913
|
+
spin.stop(picocolors3.bold(picocolors3.greenBright("registry fetched")));
|
|
914
|
+
if (!registry) {
|
|
915
|
+
log5.error(`Failed to fetch '_registry.json' file from registry`);
|
|
916
|
+
throw new Error(`Failed to fetch registry`);
|
|
917
|
+
}
|
|
918
|
+
const value = await multiselect({
|
|
919
|
+
message: "Select components to install",
|
|
920
|
+
options: registry.map((item) => ({
|
|
921
|
+
label: item.name,
|
|
922
|
+
value: item.name,
|
|
923
|
+
hint: item.description
|
|
924
|
+
}))
|
|
925
|
+
});
|
|
926
|
+
if (isCancel4(value)) {
|
|
927
|
+
outro2("Ended");
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
target = value;
|
|
931
|
+
}
|
|
932
|
+
await install(target, resolver, config);
|
|
933
|
+
}
|
|
934
|
+
async function install(target, resolver, config) {
|
|
935
|
+
const outputs = [];
|
|
936
|
+
for (const name of target) {
|
|
937
|
+
intro2(
|
|
938
|
+
picocolors3.bold(
|
|
939
|
+
picocolors3.inverse(picocolors3.cyanBright(`Add Component: ${name}`))
|
|
940
|
+
)
|
|
941
|
+
);
|
|
942
|
+
const output = await installComponent(name, resolver, config);
|
|
943
|
+
if (!output) {
|
|
944
|
+
log5.error(`Failed to install ${name}: not found`);
|
|
945
|
+
continue;
|
|
946
|
+
}
|
|
947
|
+
outro2(picocolors3.bold(picocolors3.greenBright(`${name} installed`)));
|
|
948
|
+
outputs.push(output);
|
|
949
|
+
}
|
|
950
|
+
intro2(picocolors3.bold("New Dependencies"));
|
|
951
|
+
await installDeps(outputs);
|
|
952
|
+
outro2(picocolors3.bold(picocolors3.greenBright("Successful")));
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
// src/commands/customise.ts
|
|
969
956
|
async function customise(resolver, config) {
|
|
970
|
-
intro3(
|
|
957
|
+
intro3(picocolors4.bgBlack(picocolors4.whiteBright("Customise Fumadocs UI")));
|
|
971
958
|
const result = await group(
|
|
972
959
|
{
|
|
973
960
|
target: () => select({
|
|
@@ -977,6 +964,11 @@ async function customise(resolver, config) {
|
|
|
977
964
|
label: "Docs Layout",
|
|
978
965
|
value: "docs",
|
|
979
966
|
hint: "main UI of your docs"
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
label: "Home Layout",
|
|
970
|
+
value: "home",
|
|
971
|
+
hint: "the navbar for your other pages"
|
|
980
972
|
}
|
|
981
973
|
]
|
|
982
974
|
}),
|
|
@@ -1006,7 +998,7 @@ async function customise(resolver, config) {
|
|
|
1006
998
|
page: async (v) => {
|
|
1007
999
|
if (v.results.target !== "docs" || v.results.mode === "minimal")
|
|
1008
1000
|
return false;
|
|
1009
|
-
return
|
|
1001
|
+
return confirm4({
|
|
1010
1002
|
message: "Do you want to customise the page component too?"
|
|
1011
1003
|
});
|
|
1012
1004
|
}
|
|
@@ -1018,39 +1010,62 @@ async function customise(resolver, config) {
|
|
|
1018
1010
|
}
|
|
1019
1011
|
}
|
|
1020
1012
|
);
|
|
1021
|
-
if (result.target === "docs"
|
|
1022
|
-
|
|
1013
|
+
if (result.target === "docs") {
|
|
1014
|
+
const targets = [];
|
|
1015
|
+
let pageAdded = false;
|
|
1023
1016
|
if (result.mode === "minimal") {
|
|
1024
|
-
|
|
1017
|
+
targets.push("layouts/docs-min");
|
|
1018
|
+
pageAdded = true;
|
|
1025
1019
|
} else {
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1020
|
+
if (result.page) {
|
|
1021
|
+
targets.push("layouts/page");
|
|
1022
|
+
pageAdded = true;
|
|
1023
|
+
}
|
|
1024
|
+
targets.push(
|
|
1025
|
+
result.mode === "full-default" ? "layouts/docs" : "layouts/notebook"
|
|
1030
1026
|
);
|
|
1031
1027
|
}
|
|
1032
|
-
|
|
1028
|
+
await install(targets, resolver, config);
|
|
1029
|
+
intro3(picocolors4.bold("What is Next?"));
|
|
1030
|
+
log6.info(
|
|
1033
1031
|
[
|
|
1034
|
-
picocolors5.bold("What is Next?"),
|
|
1035
1032
|
"You can check the installed components in `components/layouts`.",
|
|
1036
|
-
|
|
1033
|
+
picocolors4.dim("---"),
|
|
1037
1034
|
"Open your `layout.tsx` files, replace the imports of components:",
|
|
1038
|
-
|
|
1035
|
+
picocolors4.greenBright(
|
|
1039
1036
|
"`fumadocs-ui/layouts/docs` -> `@/components/layouts/docs`"
|
|
1040
1037
|
),
|
|
1041
|
-
|
|
1038
|
+
pageAdded ? picocolors4.greenBright(
|
|
1042
1039
|
"`fumadocs-ui/page` -> `@/components/layouts/page`"
|
|
1043
1040
|
) : ""
|
|
1044
1041
|
].join("\n")
|
|
1045
1042
|
);
|
|
1046
1043
|
}
|
|
1044
|
+
if (result.target === "home") {
|
|
1045
|
+
await install(["layouts/home"], resolver, config);
|
|
1046
|
+
intro3(picocolors4.bold("What is Next?"));
|
|
1047
|
+
log6.info(
|
|
1048
|
+
[
|
|
1049
|
+
"You can check the installed components in `components/layouts`.",
|
|
1050
|
+
picocolors4.dim("---"),
|
|
1051
|
+
"Open your `layout.tsx` files, replace the imports of components:",
|
|
1052
|
+
picocolors4.greenBright(
|
|
1053
|
+
"`fumadocs-ui/layouts/home` -> `@/components/layouts/home`"
|
|
1054
|
+
)
|
|
1055
|
+
].join("\n")
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
1058
|
+
outro3(picocolors4.bold("Have fun!"));
|
|
1047
1059
|
}
|
|
1048
1060
|
|
|
1049
1061
|
// src/index.ts
|
|
1050
1062
|
var program = new Command().option("--config <string>");
|
|
1051
1063
|
program.name("fumadocs").description("CLI to setup Fumadocs, init a config ").version(package_default.version).action(async () => {
|
|
1052
|
-
await initConfig()
|
|
1053
|
-
|
|
1064
|
+
if (await initConfig()) {
|
|
1065
|
+
console.log(picocolors5.green("Initialized a `./cli.json` config file."));
|
|
1066
|
+
} else {
|
|
1067
|
+
console.log(picocolors5.redBright("A config file already exists."));
|
|
1068
|
+
}
|
|
1054
1069
|
});
|
|
1055
1070
|
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) => {
|
|
1056
1071
|
const resolver = getResolverFromDir(options.dir);
|
|
@@ -1071,48 +1086,20 @@ program.command("init").description("init a new plugin to your docs").argument("
|
|
|
1071
1086
|
value: c
|
|
1072
1087
|
}))
|
|
1073
1088
|
});
|
|
1074
|
-
if (
|
|
1075
|
-
|
|
1089
|
+
if (isCancel5(value)) {
|
|
1090
|
+
outro4("Ended");
|
|
1076
1091
|
return;
|
|
1077
1092
|
}
|
|
1078
1093
|
await init(plugins[value], loadedConfig);
|
|
1079
1094
|
});
|
|
1080
1095
|
var dirShortcuts = {
|
|
1081
|
-
":dev": "https://fumadocs
|
|
1096
|
+
":dev": "https://preview.fumadocs.dev/registry",
|
|
1097
|
+
":localhost": "http://localhost:3000/registry"
|
|
1082
1098
|
};
|
|
1083
1099
|
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(
|
|
1084
1100
|
async (input, options) => {
|
|
1085
1101
|
const resolver = getResolverFromDir(options.dir);
|
|
1086
|
-
|
|
1087
|
-
if (input.length === 0) {
|
|
1088
|
-
const spin = spinner3();
|
|
1089
|
-
spin.start("fetching registry");
|
|
1090
|
-
const registry = await resolver("_registry.json");
|
|
1091
|
-
spin.stop(picocolors6.bold(picocolors6.greenBright("registry fetched")));
|
|
1092
|
-
if (!registry) {
|
|
1093
|
-
log6.error(
|
|
1094
|
-
`Failed to fetch '_registry.json' file from ${options.dir ?? "registry"}`
|
|
1095
|
-
);
|
|
1096
|
-
throw new Error(`Failed to fetch registry`);
|
|
1097
|
-
}
|
|
1098
|
-
const value = await multiselect({
|
|
1099
|
-
message: "Select components to install",
|
|
1100
|
-
options: registry.map((item) => ({
|
|
1101
|
-
label: item.name,
|
|
1102
|
-
value: item.name,
|
|
1103
|
-
hint: item.description
|
|
1104
|
-
}))
|
|
1105
|
-
});
|
|
1106
|
-
if (isCancel3(value)) {
|
|
1107
|
-
outro2("Ended");
|
|
1108
|
-
return;
|
|
1109
|
-
}
|
|
1110
|
-
target = value;
|
|
1111
|
-
}
|
|
1112
|
-
const loadedConfig = await loadConfig(options.config);
|
|
1113
|
-
for (const name of target) {
|
|
1114
|
-
await add(name, resolver, loadedConfig);
|
|
1115
|
-
}
|
|
1102
|
+
await add(input, resolver, await loadConfig(options.config));
|
|
1116
1103
|
}
|
|
1117
1104
|
);
|
|
1118
1105
|
program.command("tree").argument(
|
|
@@ -1141,7 +1128,7 @@ program.command("tree").argument(
|
|
|
1141
1128
|
}
|
|
1142
1129
|
}
|
|
1143
1130
|
);
|
|
1144
|
-
function getResolverFromDir(dir = "https://fumadocs.
|
|
1131
|
+
function getResolverFromDir(dir = "https://fumadocs.dev/registry") {
|
|
1145
1132
|
if (dir in dirShortcuts) dir = dirShortcuts[dir];
|
|
1146
1133
|
return dir.startsWith("http://") || dir.startsWith("https://") ? remoteResolver(dir) : localResolver(dir);
|
|
1147
1134
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fumadocs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "The CLI tool for Fumadocs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"NextJs",
|
|
@@ -26,19 +26,17 @@
|
|
|
26
26
|
"dist/*"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@clack/prompts": "^0.10.
|
|
30
|
-
"commander": "^
|
|
31
|
-
"
|
|
32
|
-
"package-manager-detector": "^1.1.0",
|
|
29
|
+
"@clack/prompts": "^0.10.1",
|
|
30
|
+
"commander": "^14.0.0",
|
|
31
|
+
"package-manager-detector": "^1.3.0",
|
|
33
32
|
"picocolors": "^1.1.1",
|
|
34
|
-
"
|
|
33
|
+
"tinyexec": "^1.0.1",
|
|
34
|
+
"ts-morph": "^26.0.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@types/
|
|
38
|
-
"@types/
|
|
39
|
-
"
|
|
40
|
-
"fast-glob": "^3.3.3",
|
|
41
|
-
"tsx": "^4.19.3",
|
|
37
|
+
"@types/node": "22.15.19",
|
|
38
|
+
"@types/react": "^19.1.4",
|
|
39
|
+
"tsx": "^4.19.4",
|
|
42
40
|
"eslint-config-custom": "0.0.0",
|
|
43
41
|
"tsconfig": "0.0.0"
|
|
44
42
|
},
|