@fumadocs/cli 0.0.7 → 0.1.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 +33 -17
- package/dist/build/index.js +201 -139
- package/dist/index.js +147 -51
- package/package.json +8 -8
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,92 @@ 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
|
+
const files = [];
|
|
258
|
+
async function build2(file) {
|
|
259
|
+
let inputPath;
|
|
260
|
+
let outputPath;
|
|
261
|
+
if (typeof file === "string") {
|
|
262
|
+
let namespace;
|
|
263
|
+
const parsed = file.split(":", 2);
|
|
264
|
+
if (parsed.length > 1) {
|
|
265
|
+
namespace = parsed[0];
|
|
266
|
+
inputPath = path3.join(builder.registryDir, parsed[1]);
|
|
267
|
+
} else {
|
|
268
|
+
inputPath = path3.join(builder.registryDir, file);
|
|
269
|
+
}
|
|
270
|
+
outputPath = builder.resolveOutputPath(file, void 0, namespace);
|
|
271
|
+
} else {
|
|
272
|
+
inputPath = path3.join(builder.registryDir, file.in);
|
|
273
|
+
outputPath = file.out;
|
|
274
|
+
}
|
|
275
|
+
if (processedFiles.has(inputPath)) return;
|
|
276
|
+
processedFiles.add(inputPath);
|
|
277
|
+
const queue = [];
|
|
278
|
+
files.push(
|
|
279
|
+
await buildFile(
|
|
280
|
+
inputPath,
|
|
281
|
+
outputPath,
|
|
282
|
+
builder,
|
|
283
|
+
component,
|
|
284
|
+
(reference) => {
|
|
285
|
+
if (reference.type === "file") {
|
|
286
|
+
queue.push(path3.relative(builder.registryDir, reference.file));
|
|
287
|
+
return builder.resolveOutputPath(reference.file);
|
|
288
|
+
}
|
|
289
|
+
if (reference.type === "sub-component") {
|
|
290
|
+
const resolved = reference.resolved;
|
|
291
|
+
subComponents.add(resolved.component.name);
|
|
292
|
+
if (resolved.type === "remote") {
|
|
293
|
+
return reference.targetFile;
|
|
294
|
+
}
|
|
295
|
+
for (const childFile of resolved.component.files) {
|
|
296
|
+
if (typeof childFile === "string" && childFile === reference.targetFile) {
|
|
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
|
+
}
|
|
305
|
+
}
|
|
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
|
+
}
|
|
315
|
+
}
|
|
316
|
+
)
|
|
317
|
+
);
|
|
318
|
+
await Promise.all(queue.map(build2));
|
|
319
|
+
}
|
|
320
|
+
await Promise.all(component.files.map(build2));
|
|
321
|
+
return [
|
|
322
|
+
component,
|
|
323
|
+
{
|
|
324
|
+
name: component.name,
|
|
325
|
+
files,
|
|
326
|
+
subComponents: Array.from(subComponents),
|
|
327
|
+
dependencies: Object.fromEntries(dependencies),
|
|
328
|
+
devDependencies: Object.fromEntries(devDependencies)
|
|
329
|
+
}
|
|
330
|
+
];
|
|
331
|
+
}
|
|
270
332
|
|
|
271
333
|
// src/build/index.ts
|
|
272
334
|
async function writeOutput(dir, out, options = {}) {
|
|
273
335
|
const { log = true } = options;
|
|
274
336
|
if (options.cleanDir && await exists(dir)) {
|
|
275
|
-
await
|
|
337
|
+
await fs3.rm(dir, {
|
|
276
338
|
recursive: true
|
|
277
339
|
});
|
|
278
340
|
if (log) {
|
|
@@ -281,8 +343,8 @@ async function writeOutput(dir, out, options = {}) {
|
|
|
281
343
|
}
|
|
282
344
|
async function writeFile2(file, content) {
|
|
283
345
|
if (!log) return;
|
|
284
|
-
await
|
|
285
|
-
await
|
|
346
|
+
await fs3.mkdir(path4.dirname(file), { recursive: true });
|
|
347
|
+
await fs3.writeFile(file, content);
|
|
286
348
|
const size = (Buffer.byteLength(content) / 1024).toFixed(2);
|
|
287
349
|
console.log(
|
|
288
350
|
`${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
|
|
|
@@ -238,7 +238,7 @@ async function init(plugin, config = {}) {
|
|
|
238
238
|
if (value) {
|
|
239
239
|
await plugin.transform(ctx);
|
|
240
240
|
note(
|
|
241
|
-
`You can format the output with Prettier or other code
|
|
241
|
+
`You can format the output with Prettier or other code formatting tools
|
|
242
242
|
prettier . --write`,
|
|
243
243
|
picocolors.bold(picocolors.green("Changes Applied"))
|
|
244
244
|
);
|
|
@@ -510,6 +510,10 @@ import fs5 from "node:fs/promises";
|
|
|
510
510
|
import path5 from "node:path";
|
|
511
511
|
var transformExtensions = [".js", ".ts", ".tsx", ".jsx"];
|
|
512
512
|
async function moveFiles(from, to, filter, project, src2, originalDir = from) {
|
|
513
|
+
function isIncluded(file) {
|
|
514
|
+
if (!transformExtensions.includes(path5.extname(file))) return false;
|
|
515
|
+
return filter(path5.resolve(file));
|
|
516
|
+
}
|
|
513
517
|
const stats = await fs5.lstat(from).catch(() => void 0);
|
|
514
518
|
if (!stats) return;
|
|
515
519
|
if (stats.isDirectory()) {
|
|
@@ -517,8 +521,8 @@ async function moveFiles(from, to, filter, project, src2, originalDir = from) {
|
|
|
517
521
|
await Promise.all(
|
|
518
522
|
items.map(async (item) => {
|
|
519
523
|
await moveFiles(
|
|
520
|
-
path5.
|
|
521
|
-
path5.
|
|
524
|
+
path5.join(from, item),
|
|
525
|
+
path5.join(to, item),
|
|
522
526
|
filter,
|
|
523
527
|
project,
|
|
524
528
|
src2,
|
|
@@ -529,31 +533,31 @@ async function moveFiles(from, to, filter, project, src2, originalDir = from) {
|
|
|
529
533
|
await fs5.rmdir(from).catch(() => {
|
|
530
534
|
});
|
|
531
535
|
}
|
|
532
|
-
if (!stats.isFile()) return;
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
alias: {
|
|
544
|
-
type: "append",
|
|
545
|
-
dir: src2 ? "src" : ""
|
|
546
|
-
},
|
|
547
|
-
relativeTo: path5.dirname(from)
|
|
536
|
+
if (!stats.isFile() || !await isIncluded(from)) return;
|
|
537
|
+
const content = await fs5.readFile(from);
|
|
538
|
+
const sourceFile = project.createSourceFile(from, content.toString(), {
|
|
539
|
+
overwrite: true
|
|
540
|
+
});
|
|
541
|
+
await transformReferences(
|
|
542
|
+
sourceFile,
|
|
543
|
+
{
|
|
544
|
+
alias: {
|
|
545
|
+
type: "append",
|
|
546
|
+
dir: src2 ? "src" : ""
|
|
548
547
|
},
|
|
549
|
-
(
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
548
|
+
relativeTo: path5.dirname(from)
|
|
549
|
+
},
|
|
550
|
+
async (resolved) => {
|
|
551
|
+
if (resolved.type !== "file") return;
|
|
552
|
+
if (
|
|
553
|
+
// ignore if the file is also moved
|
|
554
|
+
isRelative(originalDir, from) && await isIncluded(resolved.path)
|
|
555
|
+
)
|
|
556
|
+
return;
|
|
557
|
+
return toReferencePath(to, resolved.path);
|
|
558
|
+
}
|
|
559
|
+
);
|
|
560
|
+
await sourceFile.save();
|
|
557
561
|
await fs5.mkdir(path5.dirname(to), { recursive: true });
|
|
558
562
|
await fs5.rename(from, to);
|
|
559
563
|
}
|
|
@@ -707,7 +711,9 @@ export default async function Page({
|
|
|
707
711
|
resolveAppPath("./app", ctx.src),
|
|
708
712
|
resolveAppPath("./app/[lang]", ctx.src),
|
|
709
713
|
(v) => {
|
|
710
|
-
|
|
714
|
+
const parsed = path7.parse(v);
|
|
715
|
+
if (parsed.ext === ".css") return false;
|
|
716
|
+
return parsed.name !== "layout.config" && !isRelative("./app/api", v);
|
|
711
717
|
},
|
|
712
718
|
project,
|
|
713
719
|
ctx.src
|
|
@@ -902,7 +908,7 @@ async function runTree(args) {
|
|
|
902
908
|
// package.json
|
|
903
909
|
var package_default = {
|
|
904
910
|
name: "@fumadocs/cli",
|
|
905
|
-
version: "0.0
|
|
911
|
+
version: "0.1.0",
|
|
906
912
|
description: "The CLI tool for Fumadocs",
|
|
907
913
|
keywords: [
|
|
908
914
|
"NextJs",
|
|
@@ -936,35 +942,121 @@ var package_default = {
|
|
|
936
942
|
"types:check": "tsc --noEmit"
|
|
937
943
|
},
|
|
938
944
|
dependencies: {
|
|
939
|
-
"@clack/prompts": "^0.
|
|
940
|
-
commander: "^13.
|
|
945
|
+
"@clack/prompts": "^0.10.0",
|
|
946
|
+
commander: "^13.1.0",
|
|
941
947
|
execa: "^9.5.2",
|
|
942
|
-
"package-manager-detector": "^
|
|
948
|
+
"package-manager-detector": "^1.1.0",
|
|
943
949
|
picocolors: "^1.1.1",
|
|
944
|
-
"ts-morph": "^25.0.
|
|
950
|
+
"ts-morph": "^25.0.1"
|
|
945
951
|
},
|
|
946
952
|
devDependencies: {
|
|
947
953
|
"@types/cross-spawn": "^6.0.6",
|
|
948
|
-
"@types/node": "22.
|
|
949
|
-
"@types/react": "^19.0.
|
|
954
|
+
"@types/node": "22.13.16",
|
|
955
|
+
"@types/react": "^19.0.12",
|
|
950
956
|
"eslint-config-custom": "workspace:*",
|
|
951
957
|
"fast-glob": "^3.3.3",
|
|
952
958
|
tsconfig: "workspace:*",
|
|
953
|
-
tsx: "^4.19.
|
|
959
|
+
tsx: "^4.19.3"
|
|
954
960
|
},
|
|
955
961
|
publishConfig: {
|
|
956
962
|
access: "public"
|
|
957
963
|
}
|
|
958
964
|
};
|
|
959
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
|
+
}),
|
|
983
|
+
mode: (v) => {
|
|
984
|
+
if (v.results.target !== "docs") return;
|
|
985
|
+
return select({
|
|
986
|
+
message: "Which variant do you want to start from?",
|
|
987
|
+
options: [
|
|
988
|
+
{
|
|
989
|
+
label: "Start from minimal styles",
|
|
990
|
+
value: "minimal",
|
|
991
|
+
hint: "for those who want to build their own variant from ground up."
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
label: "Start from default layout",
|
|
995
|
+
value: "full-default",
|
|
996
|
+
hint: "useful for adjusting small details."
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
label: "Start from Notebook layout",
|
|
1000
|
+
value: "full-notebook",
|
|
1001
|
+
hint: "useful for adjusting small details."
|
|
1002
|
+
}
|
|
1003
|
+
]
|
|
1004
|
+
});
|
|
1005
|
+
},
|
|
1006
|
+
page: async (v) => {
|
|
1007
|
+
if (v.results.target !== "docs" || v.results.mode === "minimal")
|
|
1008
|
+
return false;
|
|
1009
|
+
return confirm3({
|
|
1010
|
+
message: "Do you want to customise the page component too?"
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
},
|
|
1014
|
+
{
|
|
1015
|
+
onCancel: () => {
|
|
1016
|
+
cancel2("Installation Stopped.");
|
|
1017
|
+
process.exit(0);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
);
|
|
1021
|
+
if (result.target === "docs" && result.mode) {
|
|
1022
|
+
if (result.page) await add("layouts/page", resolver, config);
|
|
1023
|
+
if (result.mode === "minimal") {
|
|
1024
|
+
await add("layouts/docs-min", resolver, config);
|
|
1025
|
+
} else {
|
|
1026
|
+
await add(
|
|
1027
|
+
result.mode === "full-default" ? "layouts/docs" : "layouts/notebook",
|
|
1028
|
+
resolver,
|
|
1029
|
+
config
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
log5.info(
|
|
1033
|
+
[
|
|
1034
|
+
picocolors5.bold("What is Next?"),
|
|
1035
|
+
"You can check the installed components in `components/layouts`.",
|
|
1036
|
+
picocolors5.dim("---"),
|
|
1037
|
+
"Open your `layout.tsx` files, replace the imports of components:",
|
|
1038
|
+
picocolors5.greenBright(
|
|
1039
|
+
"`fumadocs-ui/layouts/docs` -> `@/components/layouts/docs`"
|
|
1040
|
+
),
|
|
1041
|
+
result.page || result.mode === "minimal" ? picocolors5.greenBright(
|
|
1042
|
+
"`fumadocs-ui/page` -> `@/components/layouts/page`"
|
|
1043
|
+
) : ""
|
|
1044
|
+
].join("\n")
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
960
1049
|
// src/index.ts
|
|
961
|
-
var program = new Command();
|
|
962
|
-
program.name("fumadocs").description("CLI to setup Fumadocs").version(package_default.version)
|
|
963
|
-
program.command("config").description("init a config for Fumadocs CLI").action(async () => {
|
|
1050
|
+
var program = new Command().option("--config <string>");
|
|
1051
|
+
program.name("fumadocs").description("CLI to setup Fumadocs, init a config ").version(package_default.version).action(async () => {
|
|
964
1052
|
await initConfig();
|
|
965
|
-
console.log(
|
|
1053
|
+
console.log(picocolors6.green("Initialized a `./cli.json` config file."));
|
|
966
1054
|
});
|
|
967
|
-
program.command("
|
|
1055
|
+
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
|
+
const resolver = getResolverFromDir(options.dir);
|
|
1057
|
+
await customise(resolver, await loadConfig(options.config));
|
|
1058
|
+
});
|
|
1059
|
+
program.command("init").description("init a new plugin to your docs").argument("[string]", "plugin name").action(async (str, { config }) => {
|
|
968
1060
|
const loadedConfig = await loadConfig(config);
|
|
969
1061
|
if (str) {
|
|
970
1062
|
const plugin = str in plugins ? plugins[str] : void 0;
|
|
@@ -972,7 +1064,7 @@ program.command("init").description("init a new plugin to your docs").argument("
|
|
|
972
1064
|
await init(plugin, loadedConfig);
|
|
973
1065
|
return;
|
|
974
1066
|
}
|
|
975
|
-
const value = await
|
|
1067
|
+
const value = await select2({
|
|
976
1068
|
message: "Select components to install",
|
|
977
1069
|
options: Object.keys(plugins).map((c) => ({
|
|
978
1070
|
label: c,
|
|
@@ -988,19 +1080,19 @@ program.command("init").description("init a new plugin to your docs").argument("
|
|
|
988
1080
|
var dirShortcuts = {
|
|
989
1081
|
":dev": "https://fumadocs-dev.vercel.app/registry"
|
|
990
1082
|
};
|
|
991
|
-
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").
|
|
1083
|
+
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(
|
|
992
1084
|
async (input, options) => {
|
|
993
|
-
|
|
994
|
-
if (dir in dirShortcuts) dir = dirShortcuts[dir];
|
|
995
|
-
const resolver = dir.startsWith("http://") || dir.startsWith("https://") ? remoteResolver(dir) : localResolver(dir);
|
|
1085
|
+
const resolver = getResolverFromDir(options.dir);
|
|
996
1086
|
let target = input;
|
|
997
1087
|
if (input.length === 0) {
|
|
998
1088
|
const spin = spinner3();
|
|
999
1089
|
spin.start("fetching registry");
|
|
1000
1090
|
const registry = await resolver("_registry.json");
|
|
1001
|
-
spin.stop(
|
|
1091
|
+
spin.stop(picocolors6.bold(picocolors6.greenBright("registry fetched")));
|
|
1002
1092
|
if (!registry) {
|
|
1003
|
-
|
|
1093
|
+
log6.error(
|
|
1094
|
+
`Failed to fetch '_registry.json' file from ${options.dir ?? "registry"}`
|
|
1095
|
+
);
|
|
1004
1096
|
throw new Error(`Failed to fetch registry`);
|
|
1005
1097
|
}
|
|
1006
1098
|
const value = await multiselect({
|
|
@@ -1049,4 +1141,8 @@ program.command("tree").argument(
|
|
|
1049
1141
|
}
|
|
1050
1142
|
}
|
|
1051
1143
|
);
|
|
1144
|
+
function getResolverFromDir(dir = "https://fumadocs.vercel.app/registry") {
|
|
1145
|
+
if (dir in dirShortcuts) dir = dirShortcuts[dir];
|
|
1146
|
+
return dir.startsWith("http://") || dir.startsWith("https://") ? remoteResolver(dir) : localResolver(dir);
|
|
1147
|
+
}
|
|
1052
1148
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fumadocs/cli",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "The CLI tool for Fumadocs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"NextJs",
|
|
@@ -26,19 +26,19 @@
|
|
|
26
26
|
"dist/*"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@clack/prompts": "^0.
|
|
30
|
-
"commander": "^13.
|
|
29
|
+
"@clack/prompts": "^0.10.0",
|
|
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
|
-
"ts-morph": "^25.0.
|
|
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.13.16",
|
|
39
|
+
"@types/react": "^19.0.12",
|
|
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
|
},
|