@fumadocs/cli 0.2.1 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build/index.d.ts +112 -91
- package/dist/build/index.js +264 -244
- package/dist/index.js +347 -784
- package/package.json +5 -5
- package/dist/chunk-DG6SFM2G.js +0 -19
package/dist/build/index.js
CHANGED
|
@@ -1,12 +1,108 @@
|
|
|
1
|
-
import {
|
|
2
|
-
exists
|
|
3
|
-
} from "../chunk-DG6SFM2G.js";
|
|
4
|
-
|
|
5
1
|
// src/build/index.ts
|
|
6
2
|
import * as fs3 from "fs/promises";
|
|
7
3
|
import * as path4 from "path";
|
|
8
4
|
import picocolors from "picocolors";
|
|
9
5
|
|
|
6
|
+
// src/build/shadcn.ts
|
|
7
|
+
function mapDeps(deps) {
|
|
8
|
+
return Object.entries(deps).map(([k, v]) => {
|
|
9
|
+
if (v) return `${k}@${v}`;
|
|
10
|
+
return k;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
function escapeName(name) {
|
|
14
|
+
return name;
|
|
15
|
+
}
|
|
16
|
+
function toShadcnRegistry(out, baseUrl) {
|
|
17
|
+
const registry = {
|
|
18
|
+
homepage: baseUrl,
|
|
19
|
+
name: out.name,
|
|
20
|
+
items: out.components.map((comp) => componentToShadcn(comp, baseUrl))
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
registry,
|
|
24
|
+
index: {
|
|
25
|
+
...registry,
|
|
26
|
+
items: out.components.map(
|
|
27
|
+
(comp) => componentToShadcn(comp, baseUrl, true)
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function componentToShadcn(comp, baseUrl, noFile = false) {
|
|
33
|
+
const FileType = {
|
|
34
|
+
components: "registry:component",
|
|
35
|
+
lib: "registry:lib",
|
|
36
|
+
css: "registry:style",
|
|
37
|
+
route: "registry:page",
|
|
38
|
+
ui: "registry:ui",
|
|
39
|
+
block: "registry:block"
|
|
40
|
+
};
|
|
41
|
+
function onFile(file) {
|
|
42
|
+
return {
|
|
43
|
+
type: FileType[file.type],
|
|
44
|
+
content: file.content,
|
|
45
|
+
path: file.path,
|
|
46
|
+
target: file.target
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
extends: "none",
|
|
51
|
+
type: "registry:block",
|
|
52
|
+
name: escapeName(comp.name),
|
|
53
|
+
title: comp.title ?? comp.name,
|
|
54
|
+
description: comp.description,
|
|
55
|
+
dependencies: mapDeps(comp.dependencies),
|
|
56
|
+
devDependencies: mapDeps(comp.devDependencies),
|
|
57
|
+
registryDependencies: comp.subComponents.map((comp2) => {
|
|
58
|
+
if (comp2.startsWith("https://") || comp2.startsWith("http://"))
|
|
59
|
+
return comp2;
|
|
60
|
+
return new URL(`/r/${escapeName(comp2)}.json`, baseUrl).toString();
|
|
61
|
+
}),
|
|
62
|
+
files: noFile ? [] : comp.files.map(onFile)
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/build/validate.ts
|
|
67
|
+
function validateOutput(registry) {
|
|
68
|
+
function validateComponent(comp, ctx = {}) {
|
|
69
|
+
const { stack = /* @__PURE__ */ new Map() } = ctx;
|
|
70
|
+
for (const file of comp.files) {
|
|
71
|
+
const parents = stack.get(file.path);
|
|
72
|
+
if (parents) {
|
|
73
|
+
parents.add(comp.name);
|
|
74
|
+
} else {
|
|
75
|
+
stack.set(file.path, /* @__PURE__ */ new Set([comp.name]));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
for (const name of comp.subComponents) {
|
|
79
|
+
const subComp = registry.components.find((item) => item.name === name);
|
|
80
|
+
if (!subComp) {
|
|
81
|
+
console.warn(`skipped component ${name}: not found`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
validateComponent(subComp, {
|
|
85
|
+
stack
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
for (const file of comp.files) {
|
|
89
|
+
const parents = stack.get(file.path);
|
|
90
|
+
if (!parents) continue;
|
|
91
|
+
if (parents.size <= 1) continue;
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Duplicated file in same component ${Array.from(parents).join(", ")}: ${file.path}`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const compSet = /* @__PURE__ */ new Set();
|
|
98
|
+
for (const comp of registry.components) {
|
|
99
|
+
if (compSet.has(comp.name))
|
|
100
|
+
throw new Error(`duplicated component name ${comp.name}`);
|
|
101
|
+
compSet.add(comp.name);
|
|
102
|
+
validateComponent(comp);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
10
106
|
// src/build/build-registry.ts
|
|
11
107
|
import * as fs2 from "fs/promises";
|
|
12
108
|
import * as path3 from "path";
|
|
@@ -14,86 +110,51 @@ import * as path3 from "path";
|
|
|
14
110
|
// src/build/build-file.ts
|
|
15
111
|
import * as path from "path";
|
|
16
112
|
import { ts } from "ts-morph";
|
|
17
|
-
async function buildFile(
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
function process2(specifier, getSpecifiedFile) {
|
|
28
|
-
let specifiedFile = getSpecifiedFile();
|
|
29
|
-
if (!specifiedFile) return;
|
|
30
|
-
const name = specifiedFile.isInNodeModules() ? builder.resolveDep(specifier.getLiteralValue()).name : path.relative(builder.registryDir, specifiedFile.getFilePath());
|
|
31
|
-
if (name in importMap) {
|
|
32
|
-
const resolver = importMap[name];
|
|
33
|
-
if (typeof resolver === "string") {
|
|
34
|
-
specifier.setLiteralValue(resolver);
|
|
35
|
-
specifiedFile = getSpecifiedFile();
|
|
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;
|
|
47
|
-
} else {
|
|
48
|
-
const sub2 = builder.getComponentByName(
|
|
49
|
-
resolver.name,
|
|
50
|
-
resolver.registry
|
|
51
|
-
);
|
|
52
|
-
if (!sub2)
|
|
53
|
-
throw new Error(`Failed to resolve sub component ${resolver.name}`);
|
|
54
|
-
const value2 = onReference({
|
|
55
|
-
type: "sub-component",
|
|
56
|
-
resolved: sub2,
|
|
57
|
-
targetFile: resolver.file
|
|
58
|
-
});
|
|
59
|
-
if (value2) out.imports[specifier.getLiteralValue()] = value2;
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
113
|
+
async function buildFile(file, builder, comp, writeReference) {
|
|
114
|
+
const sourceFilePath = path.join(builder.registryDir, file.path);
|
|
115
|
+
const defaultResolve = (specifier, specified) => {
|
|
116
|
+
let filePath;
|
|
117
|
+
if (specified) {
|
|
118
|
+
filePath = specified.getFilePath();
|
|
119
|
+
} else if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
120
|
+
filePath = path.join(path.dirname(sourceFilePath), specifier);
|
|
121
|
+
} else {
|
|
122
|
+
throw new Error("Unknown specifier " + specifier);
|
|
62
123
|
}
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
-
const value2 = onReference({
|
|
124
|
+
if (path.relative(builder.registryDir, filePath).startsWith("../")) {
|
|
125
|
+
return {
|
|
66
126
|
type: "dependency",
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
});
|
|
71
|
-
if (value2) out.imports[specifier.getLiteralValue()] = value2;
|
|
72
|
-
return;
|
|
127
|
+
dep: builder.getDepFromSpecifier(specifier),
|
|
128
|
+
specifier
|
|
129
|
+
};
|
|
73
130
|
}
|
|
74
|
-
const sub = builder.getSubComponent(
|
|
131
|
+
const sub = builder.getSubComponent(filePath);
|
|
75
132
|
if (sub) {
|
|
76
|
-
|
|
133
|
+
return {
|
|
77
134
|
type: "sub-component",
|
|
78
135
|
resolved: {
|
|
79
136
|
type: "local",
|
|
80
|
-
component: sub
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
specifiedFile.getFilePath()
|
|
85
|
-
)
|
|
86
|
-
});
|
|
87
|
-
if (value2) out.imports[specifier.getLiteralValue()] = value2;
|
|
88
|
-
return;
|
|
137
|
+
component: sub.component,
|
|
138
|
+
file: sub.file
|
|
139
|
+
}
|
|
140
|
+
};
|
|
89
141
|
}
|
|
90
|
-
|
|
142
|
+
return {
|
|
91
143
|
type: "file",
|
|
92
|
-
file:
|
|
93
|
-
}
|
|
94
|
-
|
|
144
|
+
file: filePath
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
function process2(specifier, getSpecifiedFile) {
|
|
148
|
+
const onResolve = comp.onResolve ?? builder.registry.onResolve;
|
|
149
|
+
let resolved = defaultResolve(
|
|
150
|
+
specifier.getLiteralValue(),
|
|
151
|
+
getSpecifiedFile()
|
|
152
|
+
);
|
|
153
|
+
if (onResolve) resolved = onResolve(resolved);
|
|
154
|
+
const out = writeReference(resolved);
|
|
155
|
+
if (out) specifier.setLiteralValue(out);
|
|
95
156
|
}
|
|
96
|
-
const sourceFile = await builder.createSourceFile(
|
|
157
|
+
const sourceFile = await builder.createSourceFile(sourceFilePath);
|
|
97
158
|
for (const item of sourceFile.getImportDeclarations()) {
|
|
98
159
|
process2(
|
|
99
160
|
item.getModuleSpecifier(),
|
|
@@ -116,8 +177,12 @@ async function buildFile(inputPath, outputPath, builder, comp, onReference) {
|
|
|
116
177
|
);
|
|
117
178
|
}
|
|
118
179
|
}
|
|
119
|
-
|
|
120
|
-
|
|
180
|
+
return {
|
|
181
|
+
content: sourceFile.getFullText(),
|
|
182
|
+
type: file.type,
|
|
183
|
+
path: file.path,
|
|
184
|
+
target: file.target
|
|
185
|
+
};
|
|
121
186
|
}
|
|
122
187
|
|
|
123
188
|
// src/build/component-builder.ts
|
|
@@ -125,46 +190,47 @@ import path2 from "path";
|
|
|
125
190
|
import { Project } from "ts-morph";
|
|
126
191
|
import * as fs from "fs/promises";
|
|
127
192
|
function createComponentBuilder(registry, packageJson) {
|
|
128
|
-
const rootDir = path2.join(registry.dir, registry.rootDir);
|
|
129
193
|
const project = new Project({
|
|
130
|
-
tsConfigFilePath:
|
|
194
|
+
tsConfigFilePath: path2.join(registry.dir, registry.tsconfigPath)
|
|
131
195
|
});
|
|
132
196
|
const fileToComponent = /* @__PURE__ */ new Map();
|
|
133
197
|
for (const comp of registry.components) {
|
|
134
198
|
for (const file of comp.files) {
|
|
135
|
-
|
|
136
|
-
if (fileToComponent.has(filePath))
|
|
199
|
+
if (fileToComponent.has(file.path))
|
|
137
200
|
console.warn(
|
|
138
|
-
`the same file ${file} exists in multiple component, you should make the shared file a separate component.`
|
|
201
|
+
`the same file ${file.path} exists in multiple component, you should make the shared file a separate component.`
|
|
139
202
|
);
|
|
140
|
-
fileToComponent.set(
|
|
203
|
+
fileToComponent.set(file.path, [comp, file]);
|
|
141
204
|
}
|
|
142
205
|
}
|
|
206
|
+
const deps = {
|
|
207
|
+
...packageJson?.dependencies,
|
|
208
|
+
...registry.dependencies
|
|
209
|
+
};
|
|
210
|
+
const devDeps = {
|
|
211
|
+
...packageJson?.devDependencies,
|
|
212
|
+
...registry.devDependencies
|
|
213
|
+
};
|
|
143
214
|
return {
|
|
144
215
|
registryDir: registry.dir,
|
|
145
216
|
registry,
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
217
|
+
getDepFromSpecifier(specifier) {
|
|
218
|
+
return specifier.startsWith("@") ? specifier.split("/").slice(0, 2).join("/") : specifier.split("/")[0];
|
|
219
|
+
},
|
|
220
|
+
getDepInfo(name) {
|
|
221
|
+
if (name in deps)
|
|
149
222
|
return {
|
|
150
|
-
|
|
151
|
-
|
|
223
|
+
name,
|
|
224
|
+
type: "runtime",
|
|
225
|
+
version: deps[name]
|
|
152
226
|
};
|
|
153
|
-
if (
|
|
227
|
+
if (name in devDeps)
|
|
154
228
|
return {
|
|
229
|
+
name,
|
|
155
230
|
type: "dev",
|
|
156
|
-
version:
|
|
157
|
-
name
|
|
231
|
+
version: devDeps[name]
|
|
158
232
|
};
|
|
159
|
-
}
|
|
160
|
-
if (packageJson && name in packageJson.dependencies) {
|
|
161
|
-
return {
|
|
162
|
-
type: "runtime",
|
|
163
|
-
version: packageJson.dependencies[name],
|
|
164
|
-
name
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
return { type: "runtime", name };
|
|
233
|
+
console.warn(`dep info for ${name} cannot be found`);
|
|
168
234
|
},
|
|
169
235
|
async createSourceFile(file) {
|
|
170
236
|
const content = await fs.readFile(file);
|
|
@@ -172,64 +238,17 @@ function createComponentBuilder(registry, packageJson) {
|
|
|
172
238
|
overwrite: true
|
|
173
239
|
});
|
|
174
240
|
},
|
|
175
|
-
getComponentByName(name
|
|
176
|
-
|
|
177
|
-
const child = registry.on[registryName];
|
|
178
|
-
const comp2 = child.registry.components.find(
|
|
179
|
-
(comp3) => comp3.name === name
|
|
180
|
-
);
|
|
181
|
-
if (comp2) {
|
|
182
|
-
return {
|
|
183
|
-
type: child.type,
|
|
184
|
-
registryName,
|
|
185
|
-
component: comp2
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
const comp = registry.components.find((comp2) => comp2.name === name);
|
|
191
|
-
if (comp) {
|
|
192
|
-
return {
|
|
193
|
-
type: "local",
|
|
194
|
-
registryName,
|
|
195
|
-
component: comp
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
resolveOutputPath(file, registryName, namespace) {
|
|
200
|
-
let targetRegistry = registry;
|
|
201
|
-
if (registryName && registry.on[registryName].type === "local") {
|
|
202
|
-
targetRegistry = registry.on[registryName].registry;
|
|
203
|
-
}
|
|
204
|
-
const parsed = file.split(":", 2);
|
|
205
|
-
if (parsed.length > 1) {
|
|
206
|
-
namespace ??= parsed[0];
|
|
207
|
-
file = parsed[1];
|
|
208
|
-
}
|
|
209
|
-
if (!path2.isAbsolute(file)) {
|
|
210
|
-
file = path2.join(targetRegistry.dir, file);
|
|
211
|
-
}
|
|
212
|
-
const rootDir2 = path2.join(targetRegistry.dir, targetRegistry.rootDir);
|
|
213
|
-
if (namespace) {
|
|
214
|
-
return `${namespace}:${path2.relative(rootDir2, file)}`;
|
|
215
|
-
}
|
|
216
|
-
if (targetRegistry.namespaces)
|
|
217
|
-
for (const namespace2 in targetRegistry.namespaces) {
|
|
218
|
-
const relativePath = path2.relative(
|
|
219
|
-
path2.join(targetRegistry.dir, namespace2),
|
|
220
|
-
file
|
|
221
|
-
);
|
|
222
|
-
if (!relativePath.startsWith("../")) {
|
|
223
|
-
return `${targetRegistry.namespaces[namespace2]}:${relativePath}`;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
return path2.relative(rootDir2, file);
|
|
241
|
+
getComponentByName(name) {
|
|
242
|
+
return registry.components.find((comp) => comp.name === name);
|
|
227
243
|
},
|
|
228
244
|
getSubComponent(file) {
|
|
229
245
|
const relativeFile = path2.relative(registry.dir, file);
|
|
230
246
|
const comp = fileToComponent.get(relativeFile);
|
|
231
247
|
if (!comp) return;
|
|
232
|
-
return
|
|
248
|
+
return {
|
|
249
|
+
component: comp[0],
|
|
250
|
+
file: comp[1]
|
|
251
|
+
};
|
|
233
252
|
}
|
|
234
253
|
};
|
|
235
254
|
}
|
|
@@ -237,30 +256,16 @@ function createComponentBuilder(registry, packageJson) {
|
|
|
237
256
|
// src/build/build-registry.ts
|
|
238
257
|
async function build(registry) {
|
|
239
258
|
const output = {
|
|
259
|
+
name: registry.name,
|
|
240
260
|
index: [],
|
|
241
261
|
components: []
|
|
242
262
|
};
|
|
243
263
|
function readPackageJson() {
|
|
244
|
-
if (typeof registry.packageJson !== "string"
|
|
245
|
-
|
|
246
|
-
return fs2.readFile(
|
|
247
|
-
registry.packageJson ? path3.join(registry.dir, registry.packageJson) : path3.join(registry.dir, registry.rootDir, "package.json")
|
|
248
|
-
).then((res) => JSON.parse(res.toString())).catch(() => void 0);
|
|
264
|
+
if (typeof registry.packageJson !== "string") return registry.packageJson;
|
|
265
|
+
return fs2.readFile(path3.join(registry.dir, registry.packageJson)).then((res) => JSON.parse(res.toString())).catch(() => void 0);
|
|
249
266
|
}
|
|
250
267
|
const packageJson = await readPackageJson();
|
|
251
268
|
const builder = createComponentBuilder(registry, packageJson);
|
|
252
|
-
const buildExtendRegistries = Object.values(registry.on ?? {}).map(
|
|
253
|
-
async (schema) => {
|
|
254
|
-
if (schema.type === "remote") {
|
|
255
|
-
return schema.registry;
|
|
256
|
-
}
|
|
257
|
-
return await build(schema.registry);
|
|
258
|
-
}
|
|
259
|
-
);
|
|
260
|
-
for (const built of await Promise.all(buildExtendRegistries)) {
|
|
261
|
-
output.components.push(...built.components);
|
|
262
|
-
output.index.push(...built.index);
|
|
263
|
-
}
|
|
264
269
|
const builtComps = await Promise.all(
|
|
265
270
|
registry.components.map((component) => buildComponent(component, builder))
|
|
266
271
|
);
|
|
@@ -268,11 +273,13 @@ async function build(registry) {
|
|
|
268
273
|
if (!input.unlisted) {
|
|
269
274
|
output.index.push({
|
|
270
275
|
name: input.name,
|
|
276
|
+
title: input.title,
|
|
271
277
|
description: input.description
|
|
272
278
|
});
|
|
273
279
|
}
|
|
274
280
|
output.components.push(comp);
|
|
275
281
|
}
|
|
282
|
+
validateOutput(output);
|
|
276
283
|
return output;
|
|
277
284
|
}
|
|
278
285
|
async function buildComponent(component, builder) {
|
|
@@ -280,70 +287,48 @@ async function buildComponent(component, builder) {
|
|
|
280
287
|
const subComponents = /* @__PURE__ */ new Set();
|
|
281
288
|
const devDependencies = /* @__PURE__ */ new Map();
|
|
282
289
|
const dependencies = /* @__PURE__ */ new Map();
|
|
290
|
+
function toImportPath(file) {
|
|
291
|
+
let filePath = file.target ?? file.path;
|
|
292
|
+
if (filePath.startsWith("./")) filePath = filePath.slice(2);
|
|
293
|
+
return `@/${filePath.replaceAll(path3.sep, "/")}`;
|
|
294
|
+
}
|
|
283
295
|
async function build2(file) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (typeof file === "string") {
|
|
287
|
-
let namespace;
|
|
288
|
-
const parsed = file.split(":", 2);
|
|
289
|
-
if (parsed.length > 1) {
|
|
290
|
-
namespace = parsed[0];
|
|
291
|
-
inputPath = path3.join(builder.registryDir, parsed[1]);
|
|
292
|
-
} else {
|
|
293
|
-
inputPath = path3.join(builder.registryDir, file);
|
|
294
|
-
}
|
|
295
|
-
outputPath = builder.resolveOutputPath(file, void 0, namespace);
|
|
296
|
-
} else {
|
|
297
|
-
inputPath = path3.join(builder.registryDir, file.in);
|
|
298
|
-
outputPath = file.out;
|
|
299
|
-
}
|
|
300
|
-
if (processedFiles.has(inputPath)) return [];
|
|
301
|
-
processedFiles.add(inputPath);
|
|
296
|
+
if (processedFiles.has(file.path)) return [];
|
|
297
|
+
processedFiles.add(file.path);
|
|
302
298
|
const queue = [];
|
|
303
|
-
const result = await buildFile(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
queue.push(path3.relative(builder.registryDir, reference.file));
|
|
311
|
-
return builder.resolveOutputPath(reference.file);
|
|
299
|
+
const result = await buildFile(file, builder, component, (reference) => {
|
|
300
|
+
if (reference.type === "custom") return reference.specifier;
|
|
301
|
+
if (reference.type === "file") {
|
|
302
|
+
const refFile = builder.registry.onUnknownFile?.(reference.file);
|
|
303
|
+
if (refFile) {
|
|
304
|
+
queue.push(refFile);
|
|
305
|
+
return toImportPath(refFile);
|
|
312
306
|
}
|
|
313
|
-
|
|
314
|
-
|
|
307
|
+
throw new Error(
|
|
308
|
+
`Unknown file ${reference.file} referenced by ${file.path}`
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
if (reference.type === "sub-component") {
|
|
312
|
+
const resolved = reference.resolved;
|
|
313
|
+
if (resolved.component.name !== component.name)
|
|
315
314
|
subComponents.add(resolved.component.name);
|
|
316
|
-
|
|
317
|
-
return reference.targetFile;
|
|
318
|
-
}
|
|
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
|
-
);
|
|
325
|
-
}
|
|
326
|
-
if (typeof childFile === "object" && childFile.in === reference.targetFile) {
|
|
327
|
-
return childFile.out;
|
|
328
|
-
}
|
|
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);
|
|
338
|
-
}
|
|
315
|
+
return toImportPath(resolved.file);
|
|
339
316
|
}
|
|
340
|
-
|
|
317
|
+
const dep = builder.getDepInfo(reference.dep);
|
|
318
|
+
if (dep) {
|
|
319
|
+
const map = dep.type === "dev" ? devDependencies : dependencies;
|
|
320
|
+
map.set(dep.name, dep.version);
|
|
321
|
+
}
|
|
322
|
+
return reference.specifier;
|
|
323
|
+
});
|
|
341
324
|
return [result, ...(await Promise.all(queue.map(build2))).flat()];
|
|
342
325
|
}
|
|
343
326
|
return [
|
|
344
327
|
component,
|
|
345
328
|
{
|
|
346
329
|
name: component.name,
|
|
330
|
+
title: component.title,
|
|
331
|
+
description: component.description,
|
|
347
332
|
files: (await Promise.all(component.files.map(build2))).flat(),
|
|
348
333
|
subComponents: Array.from(subComponents),
|
|
349
334
|
dependencies: Object.fromEntries(dependencies),
|
|
@@ -353,40 +338,75 @@ async function buildComponent(component, builder) {
|
|
|
353
338
|
}
|
|
354
339
|
|
|
355
340
|
// src/build/index.ts
|
|
356
|
-
|
|
357
|
-
const
|
|
358
|
-
|
|
341
|
+
function combineRegistry(...items) {
|
|
342
|
+
const out = {
|
|
343
|
+
index: [],
|
|
344
|
+
components: [],
|
|
345
|
+
name: items[0].name
|
|
346
|
+
};
|
|
347
|
+
for (const item of items) {
|
|
348
|
+
out.components.push(...item.components);
|
|
349
|
+
out.index.push(...item.index);
|
|
350
|
+
}
|
|
351
|
+
validateOutput(out);
|
|
352
|
+
return out;
|
|
353
|
+
}
|
|
354
|
+
async function writeShadcnRegistry(out, options) {
|
|
355
|
+
const { dir, cleanDir = false, baseUrl } = options;
|
|
356
|
+
if (cleanDir) {
|
|
359
357
|
await fs3.rm(dir, {
|
|
360
|
-
recursive: true
|
|
358
|
+
recursive: true,
|
|
359
|
+
force: true
|
|
361
360
|
});
|
|
362
|
-
|
|
363
|
-
console.log(picocolors.bold(picocolors.greenBright("Cleaned directory")));
|
|
364
|
-
}
|
|
361
|
+
console.log(picocolors.bold(picocolors.greenBright("Cleaned directory")));
|
|
365
362
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
await
|
|
370
|
-
const size = (Buffer.byteLength(content) / 1024).toFixed(2);
|
|
371
|
-
console.log(
|
|
372
|
-
`${picocolors.greenBright("+")} ${path4.relative(process.cwd(), file)} ${picocolors.dim(`${size.toString()} KB`)}`
|
|
373
|
-
);
|
|
374
|
-
}
|
|
375
|
-
const write = out.components.map(async (comp) => {
|
|
376
|
-
const file = path4.join(dir, `${comp.name}.json`);
|
|
377
|
-
const json = JSON.stringify(comp, null, 2);
|
|
378
|
-
await writeFile2(file, json);
|
|
363
|
+
const { registry, index } = toShadcnRegistry(out, baseUrl);
|
|
364
|
+
const write = registry.items.map(async (item) => {
|
|
365
|
+
const file = path4.join(dir, `${item.name}.json`);
|
|
366
|
+
await writeFile2(file, JSON.stringify(item, null, 2));
|
|
379
367
|
});
|
|
368
|
+
write.push(
|
|
369
|
+
writeFile2(path4.join(dir, "registry.json"), JSON.stringify(index, null, 2))
|
|
370
|
+
);
|
|
371
|
+
await Promise.all(write);
|
|
372
|
+
}
|
|
373
|
+
async function writeFumadocsRegistry(out, options) {
|
|
374
|
+
const { dir, cleanDir = false, log = true } = options;
|
|
375
|
+
if (cleanDir) {
|
|
376
|
+
await fs3.rm(dir, {
|
|
377
|
+
recursive: true,
|
|
378
|
+
force: true
|
|
379
|
+
});
|
|
380
|
+
console.log(picocolors.bold(picocolors.greenBright("Cleaned directory")));
|
|
381
|
+
}
|
|
380
382
|
async function writeIndex() {
|
|
381
383
|
const file = path4.join(dir, "_registry.json");
|
|
382
384
|
const json = JSON.stringify(out.index, null, 2);
|
|
383
|
-
await writeFile2(file, json);
|
|
385
|
+
await writeFile2(file, json, log);
|
|
384
386
|
}
|
|
387
|
+
const write = out.components.map(async (comp) => {
|
|
388
|
+
const file = path4.join(dir, `${comp.name}.json`);
|
|
389
|
+
const json = JSON.stringify(comp, null, 2);
|
|
390
|
+
await writeFile2(file, json, log);
|
|
391
|
+
});
|
|
385
392
|
write.push(writeIndex());
|
|
386
393
|
await Promise.all(write);
|
|
387
394
|
}
|
|
395
|
+
async function writeFile2(file, content, log = true) {
|
|
396
|
+
await fs3.mkdir(path4.dirname(file), { recursive: true });
|
|
397
|
+
await fs3.writeFile(file, content);
|
|
398
|
+
if (log) {
|
|
399
|
+
const size = (Buffer.byteLength(content) / 1024).toFixed(2);
|
|
400
|
+
console.log(
|
|
401
|
+
`${picocolors.greenBright("+")} ${path4.relative(process.cwd(), file)} ${picocolors.dim(`${size} KB`)}`
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
388
405
|
export {
|
|
389
406
|
build,
|
|
407
|
+
combineRegistry,
|
|
390
408
|
createComponentBuilder,
|
|
391
|
-
|
|
409
|
+
toShadcnRegistry,
|
|
410
|
+
writeFumadocsRegistry,
|
|
411
|
+
writeShadcnRegistry
|
|
392
412
|
};
|