@fumadocs/cli 0.0.8 → 0.1.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 +33 -17
- package/dist/build/index.js +197 -139
- package/dist/index.js +135 -21
- package/package.json +5 -5
package/dist/build/index.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import * as ts_morph from 'ts-morph';
|
|
2
|
+
|
|
1
3
|
interface Component {
|
|
2
4
|
name: string;
|
|
3
5
|
description?: string;
|
|
4
|
-
files: string
|
|
6
|
+
files: (string | {
|
|
7
|
+
in: string;
|
|
8
|
+
out: string;
|
|
9
|
+
})[];
|
|
5
10
|
/**
|
|
6
11
|
* Don't list the component in registry index file
|
|
7
12
|
*/
|
|
@@ -11,9 +16,12 @@ interface Component {
|
|
|
11
16
|
*/
|
|
12
17
|
mapImportPath?: Record<string, string | {
|
|
13
18
|
type: 'component';
|
|
14
|
-
registry: string;
|
|
15
19
|
name: string;
|
|
16
20
|
file: string;
|
|
21
|
+
/**
|
|
22
|
+
* Registry of the component, refer to the current registry if not specified
|
|
23
|
+
*/
|
|
24
|
+
registry?: string;
|
|
17
25
|
}>;
|
|
18
26
|
}
|
|
19
27
|
type NamespaceType = 'components' | 'hooks' | 'lib';
|
|
@@ -23,9 +31,9 @@ interface PackageJson {
|
|
|
23
31
|
}
|
|
24
32
|
interface Registry {
|
|
25
33
|
/**
|
|
26
|
-
* The
|
|
34
|
+
* The directory of registry, needed to resolve relative paths
|
|
27
35
|
*/
|
|
28
|
-
|
|
36
|
+
dir: string;
|
|
29
37
|
/**
|
|
30
38
|
* Extend on existing registry
|
|
31
39
|
*/
|
|
@@ -78,23 +86,31 @@ interface DependencyInfo {
|
|
|
78
86
|
type: 'runtime' | 'dev';
|
|
79
87
|
version?: string;
|
|
80
88
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
type GetComponentResult = {
|
|
90
|
+
type: 'local';
|
|
91
|
+
registryName?: string;
|
|
92
|
+
component: Component;
|
|
93
|
+
} | {
|
|
94
|
+
type: 'remote';
|
|
95
|
+
registryName: string;
|
|
96
|
+
component: OutputComponent;
|
|
97
|
+
};
|
|
98
|
+
type ComponentBuilder = ReturnType<typeof createComponentBuilder>;
|
|
91
99
|
/**
|
|
92
100
|
* @param registry registry object
|
|
93
101
|
* @param packageJson parsed package json object
|
|
94
|
-
* @param registryDir directory of registry config file
|
|
95
|
-
* @param sourceDir source directory of project (e.g. `/src`), used to resolve the output paths of component files
|
|
96
102
|
*/
|
|
97
|
-
declare function createComponentBuilder(registry: Registry, packageJson: PackageJson | undefined
|
|
103
|
+
declare function createComponentBuilder(registry: Registry, packageJson: PackageJson | undefined): {
|
|
104
|
+
registryDir: string;
|
|
105
|
+
registry: Registry;
|
|
106
|
+
resolveDep(specifier: string): DependencyInfo & {
|
|
107
|
+
name: string;
|
|
108
|
+
};
|
|
109
|
+
createSourceFile(file: string): Promise<ts_morph.SourceFile>;
|
|
110
|
+
getComponentByName(name: string, registryName?: string): GetComponentResult | undefined;
|
|
111
|
+
resolveOutputPath(file: string, registryName?: string, namespace?: string): string;
|
|
112
|
+
getSubComponent(file: string): Component | undefined;
|
|
113
|
+
};
|
|
98
114
|
|
|
99
115
|
declare function writeOutput(dir: string, out: Output, options?: {
|
|
100
116
|
/**
|
package/dist/build/index.js
CHANGED
|
@@ -3,29 +3,21 @@ import {
|
|
|
3
3
|
} from "../chunk-WBCEM7RC.js";
|
|
4
4
|
|
|
5
5
|
// src/build/index.ts
|
|
6
|
-
import * as
|
|
6
|
+
import * as fs3 from "node:fs/promises";
|
|
7
7
|
import * as path4 from "node:path";
|
|
8
8
|
import picocolors from "picocolors";
|
|
9
9
|
|
|
10
10
|
// src/build/build-registry.ts
|
|
11
|
-
import * as
|
|
11
|
+
import * as fs2 from "node:fs/promises";
|
|
12
12
|
import * as path3 from "node:path";
|
|
13
|
-
import { Project } from "ts-morph";
|
|
14
13
|
|
|
15
14
|
// src/build/build-file.ts
|
|
16
15
|
import * as path from "node:path";
|
|
17
|
-
async function buildFile(
|
|
18
|
-
processedFiles.add(filePath);
|
|
16
|
+
async function buildFile(inputPath, outputPath, builder, comp, onReference) {
|
|
19
17
|
const out = {
|
|
20
18
|
imports: {},
|
|
21
19
|
content: "",
|
|
22
|
-
path:
|
|
23
|
-
};
|
|
24
|
-
const processed = {
|
|
25
|
-
files: [out],
|
|
26
|
-
dependencies: /* @__PURE__ */ new Map(),
|
|
27
|
-
devDependencies: /* @__PURE__ */ new Map(),
|
|
28
|
-
subComponents: /* @__PURE__ */ new Set()
|
|
20
|
+
path: outputPath
|
|
29
21
|
};
|
|
30
22
|
async function process2(specifier, getSpecifiedFile) {
|
|
31
23
|
let specifiedFile = getSpecifiedFile();
|
|
@@ -38,43 +30,55 @@ async function buildFile(filePath, sourceFile, builder, comp, processedFiles) {
|
|
|
38
30
|
specifiedFile = getSpecifiedFile();
|
|
39
31
|
if (!specifiedFile) return;
|
|
40
32
|
} else {
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
const sub2 = builder.getComponentByName(
|
|
34
|
+
resolver.name,
|
|
35
|
+
resolver.registry
|
|
36
|
+
);
|
|
37
|
+
if (!sub2)
|
|
38
|
+
throw new Error(`Failed to resolve sub component ${resolver.name}`);
|
|
39
|
+
const value2 = onReference({
|
|
40
|
+
type: "sub-component",
|
|
41
|
+
resolved: sub2,
|
|
42
|
+
targetFile: resolver.file
|
|
43
|
+
});
|
|
44
|
+
if (value2) out.imports[specifier.getLiteralValue()] = value2;
|
|
43
45
|
return;
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
if (specifiedFile.isInNodeModules() || specifiedFile.isDeclarationFile()) {
|
|
47
49
|
const info = builder.resolveDep(specifier.getLiteralValue());
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
const value2 = onReference({
|
|
51
|
+
type: "dependency",
|
|
52
|
+
name: info.name,
|
|
53
|
+
version: info.version ?? "",
|
|
54
|
+
isDev: info.type === "dev"
|
|
55
|
+
});
|
|
56
|
+
if (value2) out.imports[specifier.getLiteralValue()] = value2;
|
|
53
57
|
return;
|
|
54
58
|
}
|
|
55
59
|
const sub = builder.getSubComponent(specifiedFile.getFilePath());
|
|
56
60
|
if (sub) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
const value2 = onReference({
|
|
62
|
+
type: "sub-component",
|
|
63
|
+
resolved: {
|
|
64
|
+
type: "local",
|
|
65
|
+
component: sub
|
|
66
|
+
},
|
|
67
|
+
targetFile: path.relative(
|
|
68
|
+
builder.registryDir,
|
|
69
|
+
specifiedFile.getFilePath()
|
|
70
|
+
)
|
|
71
|
+
});
|
|
72
|
+
if (value2) out.imports[specifier.getLiteralValue()] = value2;
|
|
61
73
|
return;
|
|
62
74
|
}
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
referenceOutputPath,
|
|
69
|
-
specifiedFile,
|
|
70
|
-
builder,
|
|
71
|
-
comp,
|
|
72
|
-
processedFiles
|
|
73
|
-
);
|
|
74
|
-
merge(processed, outFile);
|
|
75
|
-
}
|
|
76
|
-
out.imports[specifier.getLiteralValue()] = referenceOutputPath;
|
|
75
|
+
const value = onReference({
|
|
76
|
+
type: "file",
|
|
77
|
+
file: specifiedFile.getFilePath()
|
|
78
|
+
});
|
|
79
|
+
if (value) out.imports[specifier.getLiteralValue()] = value;
|
|
77
80
|
}
|
|
81
|
+
const sourceFile = await builder.createSourceFile(inputPath);
|
|
78
82
|
for (const item of sourceFile.getImportDeclarations()) {
|
|
79
83
|
await process2(
|
|
80
84
|
item.getModuleSpecifier(),
|
|
@@ -87,34 +91,32 @@ async function buildFile(filePath, sourceFile, builder, comp, processedFiles) {
|
|
|
87
91
|
await process2(specifier, () => item.getModuleSpecifierSourceFile());
|
|
88
92
|
}
|
|
89
93
|
out.content = sourceFile.getFullText();
|
|
90
|
-
return
|
|
91
|
-
}
|
|
92
|
-
function merge(to, from) {
|
|
93
|
-
to.files.push(...from.files);
|
|
94
|
-
for (const [k, v] of from.dependencies.entries()) {
|
|
95
|
-
to.dependencies.set(k, v);
|
|
96
|
-
}
|
|
97
|
-
for (const [k, v] of from.devDependencies.entries()) {
|
|
98
|
-
to.devDependencies.set(k, v);
|
|
99
|
-
}
|
|
100
|
-
from.subComponents.forEach((item) => to.subComponents.add(item));
|
|
94
|
+
return out;
|
|
101
95
|
}
|
|
102
96
|
|
|
103
97
|
// src/build/component-builder.ts
|
|
104
98
|
import path2 from "node:path";
|
|
105
|
-
|
|
99
|
+
import { Project } from "ts-morph";
|
|
100
|
+
import * as fs from "fs/promises";
|
|
101
|
+
function createComponentBuilder(registry, packageJson) {
|
|
102
|
+
const rootDir = path2.join(registry.dir, registry.rootDir);
|
|
103
|
+
const project = new Project({
|
|
104
|
+
tsConfigFilePath: registry.tsconfigPath ? path2.join(rootDir, registry.tsconfigPath) : path2.join(rootDir, "tsconfig.json")
|
|
105
|
+
});
|
|
106
106
|
const fileToComponent = /* @__PURE__ */ new Map();
|
|
107
107
|
for (const comp of registry.components) {
|
|
108
108
|
for (const file of comp.files) {
|
|
109
|
-
|
|
109
|
+
const filePath = typeof file === "string" ? file : file.in;
|
|
110
|
+
if (fileToComponent.has(filePath))
|
|
110
111
|
console.warn(
|
|
111
112
|
`the same file ${file} exists in multiple component, you should make the shared file a separate component.`
|
|
112
113
|
);
|
|
113
|
-
fileToComponent.set(
|
|
114
|
+
fileToComponent.set(filePath, comp);
|
|
114
115
|
}
|
|
115
116
|
}
|
|
116
117
|
return {
|
|
117
|
-
registryDir,
|
|
118
|
+
registryDir: registry.dir,
|
|
119
|
+
registry,
|
|
118
120
|
resolveDep(specifier) {
|
|
119
121
|
const name = specifier.startsWith("@") ? specifier.split("/").slice(0, 2).join("/") : specifier.split("/")[0];
|
|
120
122
|
if (registry.dependencies && name in registry.dependencies)
|
|
@@ -138,64 +140,89 @@ function createComponentBuilder(registry, packageJson, registryDir, sourceDir) {
|
|
|
138
140
|
}
|
|
139
141
|
return { type: "runtime", name };
|
|
140
142
|
},
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
async createSourceFile(file) {
|
|
144
|
+
const content = await fs.readFile(file);
|
|
145
|
+
return project.createSourceFile(file, content.toString(), {
|
|
146
|
+
overwrite: true
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
getComponentByName(name, registryName) {
|
|
150
|
+
if (registryName) {
|
|
151
|
+
const child = registry.on[registryName];
|
|
152
|
+
const comp2 = child.registry.components.find(
|
|
153
|
+
(comp3) => comp3.name === name
|
|
154
|
+
);
|
|
155
|
+
if (comp2) {
|
|
156
|
+
return {
|
|
157
|
+
type: child.type,
|
|
158
|
+
registryName,
|
|
159
|
+
component: comp2
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const comp = registry.components.find((comp2) => comp2.name === name);
|
|
165
|
+
if (comp) {
|
|
166
|
+
return {
|
|
167
|
+
type: "local",
|
|
168
|
+
registryName,
|
|
169
|
+
component: comp
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
resolveOutputPath(file, registryName, namespace) {
|
|
174
|
+
let targetRegistry = registry;
|
|
175
|
+
if (registryName && registry.on[registryName].type === "local") {
|
|
176
|
+
targetRegistry = registry.on[registryName].registry;
|
|
177
|
+
}
|
|
178
|
+
const parsed = file.split(":", 2);
|
|
179
|
+
if (parsed.length > 1) {
|
|
180
|
+
namespace ??= parsed[0];
|
|
181
|
+
file = parsed[1];
|
|
145
182
|
}
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
183
|
+
if (!path2.isAbsolute(file)) {
|
|
184
|
+
file = path2.join(targetRegistry.dir, file);
|
|
185
|
+
}
|
|
186
|
+
const rootDir2 = path2.join(targetRegistry.dir, targetRegistry.rootDir);
|
|
187
|
+
if (namespace) {
|
|
188
|
+
return `${namespace}:${path2.relative(rootDir2, file)}`;
|
|
189
|
+
}
|
|
190
|
+
if (targetRegistry.namespaces)
|
|
191
|
+
for (const namespace2 in targetRegistry.namespaces) {
|
|
192
|
+
const relativePath = path2.relative(
|
|
193
|
+
path2.join(targetRegistry.dir, namespace2),
|
|
194
|
+
file
|
|
195
|
+
);
|
|
196
|
+
if (!relativePath.startsWith("../")) {
|
|
197
|
+
return `${targetRegistry.namespaces[namespace2]}:${relativePath}`;
|
|
151
198
|
}
|
|
152
199
|
}
|
|
153
|
-
return path2.relative(
|
|
200
|
+
return path2.relative(rootDir2, file);
|
|
154
201
|
},
|
|
155
202
|
getSubComponent(file) {
|
|
156
|
-
const relativeFile = path2.relative(
|
|
203
|
+
const relativeFile = path2.relative(registry.dir, file);
|
|
157
204
|
const comp = fileToComponent.get(relativeFile);
|
|
158
205
|
if (!comp) return;
|
|
159
|
-
return
|
|
160
|
-
component: comp
|
|
161
|
-
};
|
|
206
|
+
return comp;
|
|
162
207
|
}
|
|
163
208
|
};
|
|
164
209
|
}
|
|
165
210
|
|
|
166
|
-
// src/build/get-path-namespace.ts
|
|
167
|
-
function getFileNamespace(file) {
|
|
168
|
-
const parsed = file.split(":", 2);
|
|
169
|
-
if (parsed.length > 1) return { namespace: parsed[0], path: parsed[1] };
|
|
170
|
-
return { path: file };
|
|
171
|
-
}
|
|
172
|
-
|
|
173
211
|
// src/build/build-registry.ts
|
|
174
212
|
async function build(registry) {
|
|
175
|
-
const registryDir = path3.dirname(registry.path);
|
|
176
|
-
const rootDir = path3.join(registryDir, registry.rootDir);
|
|
177
|
-
const useSrc = await exists(path3.join(rootDir, "src"));
|
|
178
213
|
const output = {
|
|
179
214
|
index: [],
|
|
180
215
|
components: []
|
|
181
216
|
};
|
|
182
|
-
const project = new Project({
|
|
183
|
-
tsConfigFilePath: registry.tsconfigPath ? path3.join(registryDir, registry.tsconfigPath) : path3.join(rootDir, "tsconfig.json")
|
|
184
|
-
});
|
|
185
217
|
function readPackageJson() {
|
|
186
218
|
if (typeof registry.packageJson !== "string" && registry.packageJson)
|
|
187
219
|
return registry.packageJson;
|
|
188
|
-
return
|
|
189
|
-
registry.packageJson ? path3.join(
|
|
220
|
+
return fs2.readFile(
|
|
221
|
+
registry.packageJson ? path3.join(registry.dir, registry.packageJson) : path3.join(registry.dir, registry.rootDir, "package.json")
|
|
190
222
|
).then((res) => JSON.parse(res.toString())).catch(() => void 0);
|
|
191
223
|
}
|
|
192
224
|
const packageJson = await readPackageJson();
|
|
193
|
-
const builder = createComponentBuilder(
|
|
194
|
-
registry,
|
|
195
|
-
packageJson,
|
|
196
|
-
registryDir,
|
|
197
|
-
useSrc ? path3.join(rootDir, "src") : rootDir
|
|
198
|
-
);
|
|
225
|
+
const builder = createComponentBuilder(registry, packageJson);
|
|
199
226
|
const buildExtendRegistries = Object.values(registry.on ?? {}).map(
|
|
200
227
|
async (schema) => {
|
|
201
228
|
if (schema.type === "remote") {
|
|
@@ -208,55 +235,10 @@ async function build(registry) {
|
|
|
208
235
|
output.components.push(...built.components);
|
|
209
236
|
output.index.push(...built.index);
|
|
210
237
|
}
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
subComponents: /* @__PURE__ */ new Set(),
|
|
216
|
-
devDependencies: /* @__PURE__ */ new Map(),
|
|
217
|
-
dependencies: /* @__PURE__ */ new Map()
|
|
218
|
-
};
|
|
219
|
-
const read = component.files.map(async (sourcePath) => {
|
|
220
|
-
const parsed = getFileNamespace(sourcePath);
|
|
221
|
-
parsed.path = path3.join(registryDir, parsed.path);
|
|
222
|
-
const content = await fs.readFile(parsed.path);
|
|
223
|
-
const sourceFile = project.createSourceFile(
|
|
224
|
-
parsed.path,
|
|
225
|
-
content.toString(),
|
|
226
|
-
{
|
|
227
|
-
overwrite: true
|
|
228
|
-
}
|
|
229
|
-
);
|
|
230
|
-
const outputPath = builder.resolveOutputPath(
|
|
231
|
-
parsed.path,
|
|
232
|
-
parsed.namespace
|
|
233
|
-
);
|
|
234
|
-
if (processedFiles.has(outputPath)) return;
|
|
235
|
-
return buildFile(
|
|
236
|
-
outputPath,
|
|
237
|
-
sourceFile,
|
|
238
|
-
builder,
|
|
239
|
-
component,
|
|
240
|
-
processedFiles
|
|
241
|
-
);
|
|
242
|
-
});
|
|
243
|
-
const outFiles = await Promise.all(read);
|
|
244
|
-
for (const file of outFiles) {
|
|
245
|
-
if (!file) continue;
|
|
246
|
-
merge(collect, file);
|
|
247
|
-
}
|
|
248
|
-
return [
|
|
249
|
-
component,
|
|
250
|
-
{
|
|
251
|
-
name: component.name,
|
|
252
|
-
files: collect.files,
|
|
253
|
-
subComponents: Array.from(collect.subComponents),
|
|
254
|
-
dependencies: Object.fromEntries(collect.dependencies),
|
|
255
|
-
devDependencies: Object.fromEntries(collect.devDependencies)
|
|
256
|
-
}
|
|
257
|
-
];
|
|
258
|
-
});
|
|
259
|
-
for (const [input, comp] of await Promise.all(buildComps)) {
|
|
238
|
+
const builtComps = await Promise.all(
|
|
239
|
+
registry.components.map((component) => buildComponent(component, builder))
|
|
240
|
+
);
|
|
241
|
+
for (const [input, comp] of builtComps) {
|
|
260
242
|
if (!input.unlisted) {
|
|
261
243
|
output.index.push({
|
|
262
244
|
name: input.name,
|
|
@@ -267,12 +249,88 @@ async function build(registry) {
|
|
|
267
249
|
}
|
|
268
250
|
return output;
|
|
269
251
|
}
|
|
252
|
+
async function buildComponent(component, builder) {
|
|
253
|
+
const processedFiles = /* @__PURE__ */ new Set();
|
|
254
|
+
const subComponents = /* @__PURE__ */ new Set();
|
|
255
|
+
const devDependencies = /* @__PURE__ */ new Map();
|
|
256
|
+
const dependencies = /* @__PURE__ */ new Map();
|
|
257
|
+
async function build2(file) {
|
|
258
|
+
let inputPath;
|
|
259
|
+
let outputPath;
|
|
260
|
+
if (typeof file === "string") {
|
|
261
|
+
let namespace;
|
|
262
|
+
const parsed = file.split(":", 2);
|
|
263
|
+
if (parsed.length > 1) {
|
|
264
|
+
namespace = parsed[0];
|
|
265
|
+
inputPath = path3.join(builder.registryDir, parsed[1]);
|
|
266
|
+
} else {
|
|
267
|
+
inputPath = path3.join(builder.registryDir, file);
|
|
268
|
+
}
|
|
269
|
+
outputPath = builder.resolveOutputPath(file, void 0, namespace);
|
|
270
|
+
} else {
|
|
271
|
+
inputPath = path3.join(builder.registryDir, file.in);
|
|
272
|
+
outputPath = file.out;
|
|
273
|
+
}
|
|
274
|
+
if (processedFiles.has(inputPath)) return [];
|
|
275
|
+
processedFiles.add(inputPath);
|
|
276
|
+
const queue = [];
|
|
277
|
+
const result = await buildFile(
|
|
278
|
+
inputPath,
|
|
279
|
+
outputPath,
|
|
280
|
+
builder,
|
|
281
|
+
component,
|
|
282
|
+
(reference) => {
|
|
283
|
+
if (reference.type === "file") {
|
|
284
|
+
queue.push(path3.relative(builder.registryDir, reference.file));
|
|
285
|
+
return builder.resolveOutputPath(reference.file);
|
|
286
|
+
}
|
|
287
|
+
if (reference.type === "sub-component") {
|
|
288
|
+
const resolved = reference.resolved;
|
|
289
|
+
subComponents.add(resolved.component.name);
|
|
290
|
+
if (resolved.type === "remote") {
|
|
291
|
+
return reference.targetFile;
|
|
292
|
+
}
|
|
293
|
+
for (const childFile of resolved.component.files) {
|
|
294
|
+
if (typeof childFile === "string" && childFile === reference.targetFile) {
|
|
295
|
+
return builder.resolveOutputPath(
|
|
296
|
+
childFile,
|
|
297
|
+
reference.resolved.registryName
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
if (typeof childFile === "object" && childFile.in === reference.targetFile) {
|
|
301
|
+
return childFile.out;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
throw new Error(
|
|
305
|
+
`Failed to find sub component ${resolved.component.name}'s ${reference.targetFile} referenced by ${inputPath}`
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
if (reference.type === "dependency") {
|
|
309
|
+
if (reference.isDev)
|
|
310
|
+
devDependencies.set(reference.name, reference.version);
|
|
311
|
+
else dependencies.set(reference.name, reference.version);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
);
|
|
315
|
+
return [result, ...(await Promise.all(queue.map(build2))).flat()];
|
|
316
|
+
}
|
|
317
|
+
return [
|
|
318
|
+
component,
|
|
319
|
+
{
|
|
320
|
+
name: component.name,
|
|
321
|
+
files: (await Promise.all(component.files.map(build2))).flat(),
|
|
322
|
+
subComponents: Array.from(subComponents),
|
|
323
|
+
dependencies: Object.fromEntries(dependencies),
|
|
324
|
+
devDependencies: Object.fromEntries(devDependencies)
|
|
325
|
+
}
|
|
326
|
+
];
|
|
327
|
+
}
|
|
270
328
|
|
|
271
329
|
// src/build/index.ts
|
|
272
330
|
async function writeOutput(dir, out, options = {}) {
|
|
273
331
|
const { log = true } = options;
|
|
274
332
|
if (options.cleanDir && await exists(dir)) {
|
|
275
|
-
await
|
|
333
|
+
await fs3.rm(dir, {
|
|
276
334
|
recursive: true
|
|
277
335
|
});
|
|
278
336
|
if (log) {
|
|
@@ -281,8 +339,8 @@ async function writeOutput(dir, out, options = {}) {
|
|
|
281
339
|
}
|
|
282
340
|
async function writeFile2(file, content) {
|
|
283
341
|
if (!log) return;
|
|
284
|
-
await
|
|
285
|
-
await
|
|
342
|
+
await fs3.mkdir(path4.dirname(file), { recursive: true });
|
|
343
|
+
await fs3.writeFile(file, content);
|
|
286
344
|
const size = (Buffer.byteLength(content) / 1024).toFixed(2);
|
|
287
345
|
console.log(
|
|
288
346
|
`${picocolors.greenBright("+")} ${path4.relative(process.cwd(), file)} ${picocolors.dim(`${size.toString()} KB`)}`
|
package/dist/index.js
CHANGED
|
@@ -8,13 +8,13 @@ import {
|
|
|
8
8
|
import fs10 from "node:fs/promises";
|
|
9
9
|
import path9 from "node:path";
|
|
10
10
|
import { Command } from "commander";
|
|
11
|
-
import
|
|
11
|
+
import picocolors6 from "picocolors";
|
|
12
12
|
import {
|
|
13
13
|
isCancel as isCancel3,
|
|
14
|
-
log as
|
|
14
|
+
log as log6,
|
|
15
15
|
multiselect,
|
|
16
16
|
outro as outro2,
|
|
17
|
-
select,
|
|
17
|
+
select as select2,
|
|
18
18
|
spinner as spinner3
|
|
19
19
|
} from "@clack/prompts";
|
|
20
20
|
|
|
@@ -424,7 +424,7 @@ function localResolver(dir) {
|
|
|
424
424
|
import picocolors3 from "picocolors";
|
|
425
425
|
|
|
426
426
|
// src/generated.js
|
|
427
|
-
var generated = { "
|
|
427
|
+
var generated = { "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/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", "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
428
|
|
|
429
429
|
// src/plugins/og-image.ts
|
|
430
430
|
function isI18nEnabled(ctx) {
|
|
@@ -908,7 +908,7 @@ async function runTree(args) {
|
|
|
908
908
|
// package.json
|
|
909
909
|
var package_default = {
|
|
910
910
|
name: "@fumadocs/cli",
|
|
911
|
-
version: "0.
|
|
911
|
+
version: "0.1.1",
|
|
912
912
|
description: "The CLI tool for Fumadocs",
|
|
913
913
|
keywords: [
|
|
914
914
|
"NextJs",
|
|
@@ -945,32 +945,142 @@ var package_default = {
|
|
|
945
945
|
"@clack/prompts": "^0.10.0",
|
|
946
946
|
commander: "^13.1.0",
|
|
947
947
|
execa: "^9.5.2",
|
|
948
|
-
"package-manager-detector": "^
|
|
948
|
+
"package-manager-detector": "^1.1.0",
|
|
949
949
|
picocolors: "^1.1.1",
|
|
950
950
|
"ts-morph": "^25.0.1"
|
|
951
951
|
},
|
|
952
952
|
devDependencies: {
|
|
953
953
|
"@types/cross-spawn": "^6.0.6",
|
|
954
|
-
"@types/node": "22.
|
|
955
|
-
"@types/react": "^19.0
|
|
954
|
+
"@types/node": "22.14.0",
|
|
955
|
+
"@types/react": "^19.1.0",
|
|
956
956
|
"eslint-config-custom": "workspace:*",
|
|
957
957
|
"fast-glob": "^3.3.3",
|
|
958
958
|
tsconfig: "workspace:*",
|
|
959
|
-
tsx: "^4.19.
|
|
959
|
+
tsx: "^4.19.3"
|
|
960
960
|
},
|
|
961
961
|
publishConfig: {
|
|
962
962
|
access: "public"
|
|
963
963
|
}
|
|
964
964
|
};
|
|
965
965
|
|
|
966
|
+
// src/commands/customise.ts
|
|
967
|
+
import { cancel as cancel2, group, intro as intro3, select, confirm as confirm3, log as log5 } from "@clack/prompts";
|
|
968
|
+
import picocolors5 from "picocolors";
|
|
969
|
+
async function customise(resolver, config) {
|
|
970
|
+
intro3(picocolors5.bgBlack(picocolors5.whiteBright("Customise Fumadocs UI")));
|
|
971
|
+
const result = await group(
|
|
972
|
+
{
|
|
973
|
+
target: () => select({
|
|
974
|
+
message: "What do you want to customise?",
|
|
975
|
+
options: [
|
|
976
|
+
{
|
|
977
|
+
label: "Docs Layout",
|
|
978
|
+
value: "docs",
|
|
979
|
+
hint: "main UI of your docs"
|
|
980
|
+
},
|
|
981
|
+
{
|
|
982
|
+
label: "Home Layout",
|
|
983
|
+
value: "home",
|
|
984
|
+
hint: "the navbar for your other pages"
|
|
985
|
+
}
|
|
986
|
+
]
|
|
987
|
+
}),
|
|
988
|
+
mode: (v) => {
|
|
989
|
+
if (v.results.target !== "docs") return;
|
|
990
|
+
return select({
|
|
991
|
+
message: "Which variant do you want to start from?",
|
|
992
|
+
options: [
|
|
993
|
+
{
|
|
994
|
+
label: "Start from minimal styles",
|
|
995
|
+
value: "minimal",
|
|
996
|
+
hint: "for those who want to build their own variant from ground up."
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
label: "Start from default layout",
|
|
1000
|
+
value: "full-default",
|
|
1001
|
+
hint: "useful for adjusting small details."
|
|
1002
|
+
},
|
|
1003
|
+
{
|
|
1004
|
+
label: "Start from Notebook layout",
|
|
1005
|
+
value: "full-notebook",
|
|
1006
|
+
hint: "useful for adjusting small details."
|
|
1007
|
+
}
|
|
1008
|
+
]
|
|
1009
|
+
});
|
|
1010
|
+
},
|
|
1011
|
+
page: async (v) => {
|
|
1012
|
+
if (v.results.target !== "docs" || v.results.mode === "minimal")
|
|
1013
|
+
return false;
|
|
1014
|
+
return confirm3({
|
|
1015
|
+
message: "Do you want to customise the page component too?"
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
},
|
|
1019
|
+
{
|
|
1020
|
+
onCancel: () => {
|
|
1021
|
+
cancel2("Installation Stopped.");
|
|
1022
|
+
process.exit(0);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
);
|
|
1026
|
+
if (result.target === "docs") {
|
|
1027
|
+
let pageAdded = false;
|
|
1028
|
+
if (result.mode === "minimal") {
|
|
1029
|
+
await add("layouts/docs-min", resolver, config);
|
|
1030
|
+
pageAdded = true;
|
|
1031
|
+
} else {
|
|
1032
|
+
if (result.page) {
|
|
1033
|
+
await add("layouts/page", resolver, config);
|
|
1034
|
+
pageAdded = true;
|
|
1035
|
+
}
|
|
1036
|
+
await add(
|
|
1037
|
+
result.mode === "full-default" ? "layouts/docs" : "layouts/notebook",
|
|
1038
|
+
resolver,
|
|
1039
|
+
config
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
log5.info(
|
|
1043
|
+
[
|
|
1044
|
+
picocolors5.bold("What is Next?"),
|
|
1045
|
+
"You can check the installed components in `components/layouts`.",
|
|
1046
|
+
picocolors5.dim("---"),
|
|
1047
|
+
"Open your `layout.tsx` files, replace the imports of components:",
|
|
1048
|
+
picocolors5.greenBright(
|
|
1049
|
+
"`fumadocs-ui/layouts/docs` -> `@/components/layouts/docs`"
|
|
1050
|
+
),
|
|
1051
|
+
pageAdded ? picocolors5.greenBright(
|
|
1052
|
+
"`fumadocs-ui/page` -> `@/components/layouts/page`"
|
|
1053
|
+
) : ""
|
|
1054
|
+
].join("\n")
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
if (result.target === "home") {
|
|
1058
|
+
await add("layouts/home", resolver, config);
|
|
1059
|
+
log5.info(
|
|
1060
|
+
[
|
|
1061
|
+
picocolors5.bold("What is Next?"),
|
|
1062
|
+
"You can check the installed components in `components/layouts`.",
|
|
1063
|
+
picocolors5.dim("---"),
|
|
1064
|
+
"Open your `layout.tsx` files, replace the imports of components:",
|
|
1065
|
+
picocolors5.greenBright(
|
|
1066
|
+
"`fumadocs-ui/layouts/home` -> `@/components/layouts/home`"
|
|
1067
|
+
)
|
|
1068
|
+
].join("\n")
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
966
1073
|
// src/index.ts
|
|
967
|
-
var program = new Command();
|
|
968
|
-
program.name("fumadocs").description("CLI to setup Fumadocs").version(package_default.version)
|
|
969
|
-
program.command("config").description("init a config for Fumadocs CLI").action(async () => {
|
|
1074
|
+
var program = new Command().option("--config <string>");
|
|
1075
|
+
program.name("fumadocs").description("CLI to setup Fumadocs, init a config ").version(package_default.version).action(async () => {
|
|
970
1076
|
await initConfig();
|
|
971
|
-
console.log(
|
|
1077
|
+
console.log(picocolors6.green("Initialized a `./cli.json` config file."));
|
|
972
1078
|
});
|
|
973
|
-
program.command("
|
|
1079
|
+
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) => {
|
|
1080
|
+
const resolver = getResolverFromDir(options.dir);
|
|
1081
|
+
await customise(resolver, await loadConfig(options.config));
|
|
1082
|
+
});
|
|
1083
|
+
program.command("init").description("init a new plugin to your docs").argument("[string]", "plugin name").action(async (str, { config }) => {
|
|
974
1084
|
const loadedConfig = await loadConfig(config);
|
|
975
1085
|
if (str) {
|
|
976
1086
|
const plugin = str in plugins ? plugins[str] : void 0;
|
|
@@ -978,7 +1088,7 @@ program.command("init").description("init a new plugin to your docs").argument("
|
|
|
978
1088
|
await init(plugin, loadedConfig);
|
|
979
1089
|
return;
|
|
980
1090
|
}
|
|
981
|
-
const value = await
|
|
1091
|
+
const value = await select2({
|
|
982
1092
|
message: "Select components to install",
|
|
983
1093
|
options: Object.keys(plugins).map((c) => ({
|
|
984
1094
|
label: c,
|
|
@@ -994,19 +1104,19 @@ program.command("init").description("init a new plugin to your docs").argument("
|
|
|
994
1104
|
var dirShortcuts = {
|
|
995
1105
|
":dev": "https://fumadocs-dev.vercel.app/registry"
|
|
996
1106
|
};
|
|
997
|
-
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").
|
|
1107
|
+
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(
|
|
998
1108
|
async (input, options) => {
|
|
999
|
-
|
|
1000
|
-
if (dir in dirShortcuts) dir = dirShortcuts[dir];
|
|
1001
|
-
const resolver = dir.startsWith("http://") || dir.startsWith("https://") ? remoteResolver(dir) : localResolver(dir);
|
|
1109
|
+
const resolver = getResolverFromDir(options.dir);
|
|
1002
1110
|
let target = input;
|
|
1003
1111
|
if (input.length === 0) {
|
|
1004
1112
|
const spin = spinner3();
|
|
1005
1113
|
spin.start("fetching registry");
|
|
1006
1114
|
const registry = await resolver("_registry.json");
|
|
1007
|
-
spin.stop(
|
|
1115
|
+
spin.stop(picocolors6.bold(picocolors6.greenBright("registry fetched")));
|
|
1008
1116
|
if (!registry) {
|
|
1009
|
-
|
|
1117
|
+
log6.error(
|
|
1118
|
+
`Failed to fetch '_registry.json' file from ${options.dir ?? "registry"}`
|
|
1119
|
+
);
|
|
1010
1120
|
throw new Error(`Failed to fetch registry`);
|
|
1011
1121
|
}
|
|
1012
1122
|
const value = await multiselect({
|
|
@@ -1055,4 +1165,8 @@ program.command("tree").argument(
|
|
|
1055
1165
|
}
|
|
1056
1166
|
}
|
|
1057
1167
|
);
|
|
1168
|
+
function getResolverFromDir(dir = "https://fumadocs.vercel.app/registry") {
|
|
1169
|
+
if (dir in dirShortcuts) dir = dirShortcuts[dir];
|
|
1170
|
+
return dir.startsWith("http://") || dir.startsWith("https://") ? remoteResolver(dir) : localResolver(dir);
|
|
1171
|
+
}
|
|
1058
1172
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fumadocs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "The CLI tool for Fumadocs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"NextJs",
|
|
@@ -29,16 +29,16 @@
|
|
|
29
29
|
"@clack/prompts": "^0.10.0",
|
|
30
30
|
"commander": "^13.1.0",
|
|
31
31
|
"execa": "^9.5.2",
|
|
32
|
-
"package-manager-detector": "^
|
|
32
|
+
"package-manager-detector": "^1.1.0",
|
|
33
33
|
"picocolors": "^1.1.1",
|
|
34
34
|
"ts-morph": "^25.0.1"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/cross-spawn": "^6.0.6",
|
|
38
|
-
"@types/node": "22.
|
|
39
|
-
"@types/react": "^19.0
|
|
38
|
+
"@types/node": "22.14.0",
|
|
39
|
+
"@types/react": "^19.1.0",
|
|
40
40
|
"fast-glob": "^3.3.3",
|
|
41
|
-
"tsx": "^4.19.
|
|
41
|
+
"tsx": "^4.19.3",
|
|
42
42
|
"eslint-config-custom": "0.0.0",
|
|
43
43
|
"tsconfig": "0.0.0"
|
|
44
44
|
},
|