@fumadocs/cli 1.0.3 → 1.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 +110 -90
- package/dist/build/index.js +260 -334
- package/dist/index.js +496 -364
- package/dist/schema/default.json +1 -0
- package/dist/schema/src.json +1 -0
- package/package.json +7 -7
package/dist/build/index.js
CHANGED
|
@@ -1,118 +1,190 @@
|
|
|
1
1
|
// src/build/index.ts
|
|
2
|
-
import * as
|
|
3
|
-
import * as
|
|
2
|
+
import * as fs2 from "fs/promises";
|
|
3
|
+
import * as path2 from "path";
|
|
4
4
|
import picocolors from "picocolors";
|
|
5
5
|
|
|
6
|
-
// src/build/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
};
|
|
6
|
+
// src/build/compiler.ts
|
|
7
|
+
import * as fs from "fs/promises";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
import { Project, ts } from "ts-morph";
|
|
10
|
+
var RegistryCompiler = class {
|
|
11
|
+
constructor(registry) {
|
|
12
|
+
this.raw = registry;
|
|
13
|
+
this.project = new Project({
|
|
14
|
+
tsConfigFilePath: path.join(registry.dir, registry.tsconfigPath)
|
|
15
|
+
});
|
|
48
16
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
17
|
+
async readPackageJson() {
|
|
18
|
+
if (typeof this.raw.packageJson !== "string") return this.raw.packageJson;
|
|
19
|
+
return fs.readFile(path.join(this.raw.dir, this.raw.packageJson)).then((res) => JSON.parse(res.toString())).catch(() => void 0);
|
|
20
|
+
}
|
|
21
|
+
async createSourceFile(file) {
|
|
22
|
+
const content = await fs.readFile(file);
|
|
23
|
+
return this.project.createSourceFile(file, content.toString(), {
|
|
24
|
+
overwrite: true
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async compile() {
|
|
28
|
+
const registry = this.raw;
|
|
29
|
+
this.resolver = new RegistryResolver(this, await this.readPackageJson());
|
|
30
|
+
const output = {
|
|
31
|
+
name: registry.name,
|
|
32
|
+
info: {
|
|
33
|
+
indexes: [],
|
|
34
|
+
env: registry.env,
|
|
35
|
+
variables: registry.variables
|
|
36
|
+
},
|
|
37
|
+
components: []
|
|
38
|
+
};
|
|
39
|
+
const builtComps = await Promise.all(
|
|
40
|
+
registry.components.map(async (component) => {
|
|
41
|
+
const compiler = new ComponentCompiler(this, component);
|
|
42
|
+
return [component, await compiler.build()];
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
for (const [input, comp] of builtComps) {
|
|
46
|
+
if (!input.unlisted) {
|
|
47
|
+
output.info.indexes.push({
|
|
48
|
+
name: input.name,
|
|
49
|
+
title: input.title,
|
|
50
|
+
description: input.description
|
|
51
|
+
});
|
|
76
52
|
}
|
|
53
|
+
output.components.push(comp);
|
|
77
54
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
55
|
+
return output;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
var RegistryResolver = class {
|
|
59
|
+
constructor(compiler, packageJson = {}) {
|
|
60
|
+
this.compiler = compiler;
|
|
61
|
+
this.fileToComponent = /* @__PURE__ */ new Map();
|
|
62
|
+
const registry = compiler.raw;
|
|
63
|
+
for (const comp of registry.components) {
|
|
64
|
+
for (const file of comp.files) {
|
|
65
|
+
if (this.fileToComponent.has(file.path))
|
|
66
|
+
console.warn(
|
|
67
|
+
`the same file ${file.path} exists in multiple component, you should make the shared file a separate component.`
|
|
68
|
+
);
|
|
69
|
+
this.fileToComponent.set(file.path, [comp, file]);
|
|
83
70
|
}
|
|
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
71
|
}
|
|
72
|
+
this.deps = {
|
|
73
|
+
...packageJson?.dependencies,
|
|
74
|
+
...registry.dependencies
|
|
75
|
+
};
|
|
76
|
+
this.devDeps = {
|
|
77
|
+
...packageJson?.devDependencies,
|
|
78
|
+
...registry.devDependencies
|
|
79
|
+
};
|
|
96
80
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (compSet.has(comp.name))
|
|
100
|
-
throw new Error(`duplicated component name ${comp.name}`);
|
|
101
|
-
compSet.add(comp.name);
|
|
102
|
-
validateComponent(comp);
|
|
81
|
+
getDepFromSpecifier(specifier) {
|
|
82
|
+
return specifier.startsWith("@") ? specifier.split("/").slice(0, 2).join("/") : specifier.split("/")[0];
|
|
103
83
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
84
|
+
getDepInfo(name) {
|
|
85
|
+
if (name in this.deps)
|
|
86
|
+
return {
|
|
87
|
+
name,
|
|
88
|
+
type: "runtime",
|
|
89
|
+
version: this.deps[name]
|
|
90
|
+
};
|
|
91
|
+
if (name in this.devDeps)
|
|
92
|
+
return {
|
|
93
|
+
name,
|
|
94
|
+
type: "dev",
|
|
95
|
+
version: this.devDeps[name]
|
|
96
|
+
};
|
|
97
|
+
console.warn(`dep info for ${name} cannot be found`);
|
|
98
|
+
}
|
|
99
|
+
getComponentByName(name) {
|
|
100
|
+
return this.compiler.raw.components.find((comp) => comp.name === name);
|
|
101
|
+
}
|
|
102
|
+
getSubComponent(file) {
|
|
103
|
+
const relativeFile = path.relative(this.compiler.raw.dir, file);
|
|
104
|
+
const comp = this.fileToComponent.get(relativeFile);
|
|
105
|
+
if (!comp) return;
|
|
106
|
+
return {
|
|
107
|
+
component: comp[0],
|
|
108
|
+
file: comp[1]
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
var ComponentCompiler = class {
|
|
113
|
+
constructor(compiler, component) {
|
|
114
|
+
this.compiler = compiler;
|
|
115
|
+
this.component = component;
|
|
116
|
+
this.processedFiles = /* @__PURE__ */ new Set();
|
|
117
|
+
this.subComponents = /* @__PURE__ */ new Set();
|
|
118
|
+
this.devDependencies = /* @__PURE__ */ new Map();
|
|
119
|
+
this.dependencies = /* @__PURE__ */ new Map();
|
|
120
|
+
this.registry = compiler.raw;
|
|
121
|
+
}
|
|
122
|
+
// see https://github.com/shadcn-ui/ui/blob/396275e46a58333caa1fa0a991bd9bc5237d2ee3/packages/shadcn/src/utils/updaters/update-files.ts#L585
|
|
123
|
+
// to hit the fast-path step, we need to import `target` path first because it's detected from `fileSet`, a set of output file paths
|
|
124
|
+
toImportPath(file) {
|
|
125
|
+
let filePath = file.target ?? file.path;
|
|
126
|
+
if (filePath.startsWith("./")) filePath = filePath.slice(2);
|
|
127
|
+
return `@/${filePath.replaceAll(path.sep, "/")}`;
|
|
128
|
+
}
|
|
129
|
+
async build() {
|
|
130
|
+
return {
|
|
131
|
+
name: this.component.name,
|
|
132
|
+
title: this.component.title,
|
|
133
|
+
description: this.component.description,
|
|
134
|
+
files: (await Promise.all(
|
|
135
|
+
this.component.files.map((file) => this.buildFileAndDeps(file))
|
|
136
|
+
)).flat(),
|
|
137
|
+
subComponents: Array.from(this.subComponents),
|
|
138
|
+
dependencies: Object.fromEntries(this.dependencies),
|
|
139
|
+
devDependencies: Object.fromEntries(this.devDependencies)
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
async buildFileAndDeps(file) {
|
|
143
|
+
if (this.processedFiles.has(file.path)) return [];
|
|
144
|
+
this.processedFiles.add(file.path);
|
|
145
|
+
const resolver = this.compiler.resolver;
|
|
146
|
+
const queue = [];
|
|
147
|
+
const result = await this.buildFile(file, (reference) => {
|
|
148
|
+
if (reference.type === "custom") return reference.specifier;
|
|
149
|
+
if (reference.type === "file") {
|
|
150
|
+
const refFile = this.registry.onUnknownFile?.(reference.file);
|
|
151
|
+
if (refFile) {
|
|
152
|
+
queue.push(refFile);
|
|
153
|
+
return this.toImportPath(refFile);
|
|
154
|
+
}
|
|
155
|
+
if (refFile === false) return;
|
|
156
|
+
throw new Error(
|
|
157
|
+
`Unknown file ${reference.file} referenced by ${file.path}`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
if (reference.type === "sub-component") {
|
|
161
|
+
const resolved = reference.resolved;
|
|
162
|
+
if (resolved.component.name !== this.component.name) {
|
|
163
|
+
if (resolved.type === "remote") {
|
|
164
|
+
this.subComponents.add({
|
|
165
|
+
type: "http",
|
|
166
|
+
baseUrl: resolved.registryName,
|
|
167
|
+
component: resolved.component.name
|
|
168
|
+
});
|
|
169
|
+
} else {
|
|
170
|
+
this.subComponents.add(resolved.component.name);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return this.toImportPath(resolved.file);
|
|
174
|
+
}
|
|
175
|
+
const dep = resolver.getDepInfo(reference.dep);
|
|
176
|
+
if (dep) {
|
|
177
|
+
const map = dep.type === "dev" ? this.devDependencies : this.dependencies;
|
|
178
|
+
map.set(dep.name, dep.version);
|
|
179
|
+
}
|
|
180
|
+
return reference.specifier;
|
|
181
|
+
});
|
|
182
|
+
return [
|
|
183
|
+
result,
|
|
184
|
+
...(await Promise.all(queue.map((file2) => this.buildFileAndDeps(file2)))).flat()
|
|
185
|
+
];
|
|
186
|
+
}
|
|
187
|
+
resolveImport(sourceFilePath, specifier, specified) {
|
|
116
188
|
let filePath;
|
|
117
189
|
if (specified) {
|
|
118
190
|
filePath = specified.getFilePath();
|
|
@@ -123,14 +195,15 @@ async function buildFile(file, builder, comp, writeReference) {
|
|
|
123
195
|
console.warn(`Unknown specifier ${specifier}, skipping for now`);
|
|
124
196
|
return;
|
|
125
197
|
}
|
|
126
|
-
|
|
198
|
+
const resolver = this.compiler.resolver;
|
|
199
|
+
if (path.relative(this.registry.dir, filePath).startsWith("../")) {
|
|
127
200
|
return {
|
|
128
201
|
type: "dependency",
|
|
129
|
-
dep:
|
|
202
|
+
dep: resolver.getDepFromSpecifier(specifier),
|
|
130
203
|
specifier
|
|
131
204
|
};
|
|
132
205
|
}
|
|
133
|
-
const sub =
|
|
206
|
+
const sub = resolver.getSubComponent(filePath);
|
|
134
207
|
if (sub) {
|
|
135
208
|
return {
|
|
136
209
|
type: "sub-component",
|
|
@@ -145,269 +218,122 @@ async function buildFile(file, builder, comp, writeReference) {
|
|
|
145
218
|
type: "file",
|
|
146
219
|
file: filePath
|
|
147
220
|
};
|
|
148
|
-
};
|
|
149
|
-
function process2(specifier, specifiedFile) {
|
|
150
|
-
const onResolve = comp.onResolve ?? builder.registry.onResolve;
|
|
151
|
-
let resolved = defaultResolve(
|
|
152
|
-
specifier.getLiteralValue(),
|
|
153
|
-
specifiedFile
|
|
154
|
-
);
|
|
155
|
-
if (!resolved) return;
|
|
156
|
-
if (onResolve) resolved = onResolve(resolved);
|
|
157
|
-
const out = writeReference(resolved);
|
|
158
|
-
if (out) specifier.setLiteralValue(out);
|
|
159
|
-
}
|
|
160
|
-
const sourceFile = await builder.createSourceFile(sourceFilePath);
|
|
161
|
-
for (const item of sourceFile.getImportDeclarations()) {
|
|
162
|
-
process2(item.getModuleSpecifier(), item.getModuleSpecifierSourceFile());
|
|
163
|
-
}
|
|
164
|
-
for (const item of sourceFile.getExportDeclarations()) {
|
|
165
|
-
const specifier = item.getModuleSpecifier();
|
|
166
|
-
if (!specifier) continue;
|
|
167
|
-
process2(specifier, item.getModuleSpecifierSourceFile());
|
|
168
221
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
222
|
+
async buildFile(file, writeReference) {
|
|
223
|
+
const sourceFilePath = path.join(this.registry.dir, file.path);
|
|
224
|
+
const process2 = (specifier, specifiedFile) => {
|
|
225
|
+
const onResolve = this.component.onResolve ?? this.registry.onResolve;
|
|
226
|
+
let resolved = this.resolveImport(
|
|
227
|
+
sourceFilePath,
|
|
228
|
+
specifier.getLiteralValue(),
|
|
229
|
+
specifiedFile
|
|
177
230
|
);
|
|
231
|
+
if (!resolved) return;
|
|
232
|
+
if (onResolve) resolved = onResolve(resolved);
|
|
233
|
+
const out = writeReference(resolved);
|
|
234
|
+
if (out) specifier.setLiteralValue(out);
|
|
235
|
+
};
|
|
236
|
+
const sourceFile = await this.compiler.createSourceFile(sourceFilePath);
|
|
237
|
+
for (const item of sourceFile.getImportDeclarations()) {
|
|
238
|
+
process2(item.getModuleSpecifier(), item.getModuleSpecifierSourceFile());
|
|
178
239
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
function createComponentBuilder(registry, packageJson) {
|
|
193
|
-
const project = new Project({
|
|
194
|
-
tsConfigFilePath: path2.join(registry.dir, registry.tsconfigPath)
|
|
195
|
-
});
|
|
196
|
-
const fileToComponent = /* @__PURE__ */ new Map();
|
|
197
|
-
for (const comp of registry.components) {
|
|
198
|
-
for (const file of comp.files) {
|
|
199
|
-
if (fileToComponent.has(file.path))
|
|
200
|
-
console.warn(
|
|
201
|
-
`the same file ${file.path} exists in multiple component, you should make the shared file a separate component.`
|
|
240
|
+
for (const item of sourceFile.getExportDeclarations()) {
|
|
241
|
+
const specifier = item.getModuleSpecifier();
|
|
242
|
+
if (!specifier) continue;
|
|
243
|
+
process2(specifier, item.getModuleSpecifierSourceFile());
|
|
244
|
+
}
|
|
245
|
+
const calls = sourceFile.getDescendantsOfKind(ts.SyntaxKind.CallExpression);
|
|
246
|
+
for (const expression of calls) {
|
|
247
|
+
if (expression.getExpression().isKind(ts.SyntaxKind.ImportKeyword) && expression.getArguments().length === 1) {
|
|
248
|
+
const argument = expression.getArguments()[0];
|
|
249
|
+
if (!argument.isKind(ts.SyntaxKind.StringLiteral)) continue;
|
|
250
|
+
process2(
|
|
251
|
+
argument,
|
|
252
|
+
argument.getSymbol()?.getDeclarations()[0].getSourceFile()
|
|
202
253
|
);
|
|
203
|
-
|
|
254
|
+
}
|
|
204
255
|
}
|
|
256
|
+
return {
|
|
257
|
+
content: sourceFile.getFullText(),
|
|
258
|
+
type: file.type,
|
|
259
|
+
path: file.path,
|
|
260
|
+
target: file.target
|
|
261
|
+
};
|
|
205
262
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
...registry.devDependencies
|
|
213
|
-
};
|
|
263
|
+
};
|
|
264
|
+
function resolveFromRemote(r, component, selectFile) {
|
|
265
|
+
const comp = r.components.find((comp2) => comp2.name === component);
|
|
266
|
+
if (!comp) return;
|
|
267
|
+
const file = comp.files.find(selectFile);
|
|
268
|
+
if (!file) return;
|
|
214
269
|
return {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (name in deps)
|
|
222
|
-
return {
|
|
223
|
-
name,
|
|
224
|
-
type: "runtime",
|
|
225
|
-
version: deps[name]
|
|
226
|
-
};
|
|
227
|
-
if (name in devDeps)
|
|
228
|
-
return {
|
|
229
|
-
name,
|
|
230
|
-
type: "dev",
|
|
231
|
-
version: devDeps[name]
|
|
232
|
-
};
|
|
233
|
-
console.warn(`dep info for ${name} cannot be found`);
|
|
234
|
-
},
|
|
235
|
-
async createSourceFile(file) {
|
|
236
|
-
const content = await fs.readFile(file);
|
|
237
|
-
return project.createSourceFile(file, content.toString(), {
|
|
238
|
-
overwrite: true
|
|
239
|
-
});
|
|
240
|
-
},
|
|
241
|
-
getComponentByName(name) {
|
|
242
|
-
return registry.components.find((comp) => comp.name === name);
|
|
243
|
-
},
|
|
244
|
-
getSubComponent(file) {
|
|
245
|
-
const relativeFile = path2.relative(registry.dir, file);
|
|
246
|
-
const comp = fileToComponent.get(relativeFile);
|
|
247
|
-
if (!comp) return;
|
|
248
|
-
return {
|
|
249
|
-
component: comp[0],
|
|
250
|
-
file: comp[1]
|
|
251
|
-
};
|
|
270
|
+
type: "sub-component",
|
|
271
|
+
resolved: {
|
|
272
|
+
type: "remote",
|
|
273
|
+
registryName: r.name,
|
|
274
|
+
component: comp,
|
|
275
|
+
file
|
|
252
276
|
}
|
|
253
277
|
};
|
|
254
278
|
}
|
|
255
279
|
|
|
256
|
-
// src/build/build-registry.ts
|
|
257
|
-
async function build(registry) {
|
|
258
|
-
const output = {
|
|
259
|
-
name: registry.name,
|
|
260
|
-
index: [],
|
|
261
|
-
components: []
|
|
262
|
-
};
|
|
263
|
-
function readPackageJson() {
|
|
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);
|
|
266
|
-
}
|
|
267
|
-
const packageJson = await readPackageJson();
|
|
268
|
-
const builder = createComponentBuilder(registry, packageJson);
|
|
269
|
-
const builtComps = await Promise.all(
|
|
270
|
-
registry.components.map((component) => buildComponent(component, builder))
|
|
271
|
-
);
|
|
272
|
-
for (const [input, comp] of builtComps) {
|
|
273
|
-
if (!input.unlisted) {
|
|
274
|
-
output.index.push({
|
|
275
|
-
name: input.name,
|
|
276
|
-
title: input.title,
|
|
277
|
-
description: input.description
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
output.components.push(comp);
|
|
281
|
-
}
|
|
282
|
-
validateOutput(output);
|
|
283
|
-
return output;
|
|
284
|
-
}
|
|
285
|
-
async function buildComponent(component, builder) {
|
|
286
|
-
const processedFiles = /* @__PURE__ */ new Set();
|
|
287
|
-
const subComponents = /* @__PURE__ */ new Set();
|
|
288
|
-
const devDependencies = /* @__PURE__ */ new Map();
|
|
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
|
-
}
|
|
295
|
-
async function build2(file) {
|
|
296
|
-
if (processedFiles.has(file.path)) return [];
|
|
297
|
-
processedFiles.add(file.path);
|
|
298
|
-
const queue = [];
|
|
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);
|
|
306
|
-
}
|
|
307
|
-
if (refFile === false) return;
|
|
308
|
-
throw new Error(
|
|
309
|
-
`Unknown file ${reference.file} referenced by ${file.path}`
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
if (reference.type === "sub-component") {
|
|
313
|
-
const resolved = reference.resolved;
|
|
314
|
-
if (resolved.component.name !== component.name)
|
|
315
|
-
subComponents.add(resolved.component.name);
|
|
316
|
-
return toImportPath(resolved.file);
|
|
317
|
-
}
|
|
318
|
-
const dep = builder.getDepInfo(reference.dep);
|
|
319
|
-
if (dep) {
|
|
320
|
-
const map = dep.type === "dev" ? devDependencies : dependencies;
|
|
321
|
-
map.set(dep.name, dep.version);
|
|
322
|
-
}
|
|
323
|
-
return reference.specifier;
|
|
324
|
-
});
|
|
325
|
-
return [result, ...(await Promise.all(queue.map(build2))).flat()];
|
|
326
|
-
}
|
|
327
|
-
return [
|
|
328
|
-
component,
|
|
329
|
-
{
|
|
330
|
-
name: component.name,
|
|
331
|
-
title: component.title,
|
|
332
|
-
description: component.description,
|
|
333
|
-
files: (await Promise.all(component.files.map(build2))).flat(),
|
|
334
|
-
subComponents: Array.from(subComponents),
|
|
335
|
-
dependencies: Object.fromEntries(dependencies),
|
|
336
|
-
devDependencies: Object.fromEntries(devDependencies)
|
|
337
|
-
}
|
|
338
|
-
];
|
|
339
|
-
}
|
|
340
|
-
|
|
341
280
|
// src/build/index.ts
|
|
342
|
-
function combineRegistry(...items) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
281
|
+
function combineRegistry(root, ...items) {
|
|
282
|
+
return {
|
|
283
|
+
...root,
|
|
284
|
+
info: {
|
|
285
|
+
...root.info,
|
|
286
|
+
registries: items.map((item) => item.name)
|
|
287
|
+
},
|
|
288
|
+
registries: items
|
|
347
289
|
};
|
|
348
|
-
for (const item of items) {
|
|
349
|
-
out.components.push(...item.components);
|
|
350
|
-
out.index.push(...item.index);
|
|
351
|
-
}
|
|
352
|
-
validateOutput(out);
|
|
353
|
-
return out;
|
|
354
|
-
}
|
|
355
|
-
async function writeShadcnRegistry(out, options) {
|
|
356
|
-
const { dir, cleanDir = false, baseUrl } = options;
|
|
357
|
-
if (cleanDir) {
|
|
358
|
-
await fs3.rm(dir, {
|
|
359
|
-
recursive: true,
|
|
360
|
-
force: true
|
|
361
|
-
});
|
|
362
|
-
console.log(picocolors.bold(picocolors.greenBright("Cleaned directory")));
|
|
363
|
-
}
|
|
364
|
-
const { registry, index } = toShadcnRegistry(out, baseUrl);
|
|
365
|
-
const write = registry.items.map(async (item) => {
|
|
366
|
-
const file = path4.join(dir, `${item.name}.json`);
|
|
367
|
-
await writeFile2(file, JSON.stringify(item, null, 2));
|
|
368
|
-
});
|
|
369
|
-
write.push(
|
|
370
|
-
writeFile2(path4.join(dir, "registry.json"), JSON.stringify(index, null, 2))
|
|
371
|
-
);
|
|
372
|
-
await Promise.all(write);
|
|
373
290
|
}
|
|
374
291
|
async function writeFumadocsRegistry(out, options) {
|
|
375
292
|
const { dir, cleanDir = false, log = true } = options;
|
|
376
293
|
if (cleanDir) {
|
|
377
|
-
await
|
|
294
|
+
await fs2.rm(dir, {
|
|
378
295
|
recursive: true,
|
|
379
296
|
force: true
|
|
380
297
|
});
|
|
381
298
|
console.log(picocolors.bold(picocolors.greenBright("Cleaned directory")));
|
|
382
299
|
}
|
|
383
|
-
async function
|
|
384
|
-
const file =
|
|
385
|
-
const json = JSON.stringify(out.
|
|
300
|
+
async function writeInfo() {
|
|
301
|
+
const file = path2.join(dir, "_registry.json");
|
|
302
|
+
const json = JSON.stringify(out.info, null, 2);
|
|
386
303
|
await writeFile2(file, json, log);
|
|
387
304
|
}
|
|
388
305
|
const write = out.components.map(async (comp) => {
|
|
389
|
-
const file =
|
|
306
|
+
const file = path2.join(dir, `${comp.name}.json`);
|
|
390
307
|
const json = JSON.stringify(comp, null, 2);
|
|
391
308
|
await writeFile2(file, json, log);
|
|
392
309
|
});
|
|
393
|
-
write.push(
|
|
310
|
+
write.push(writeInfo());
|
|
311
|
+
if ("registries" in out) {
|
|
312
|
+
for (const child of out.registries) {
|
|
313
|
+
write.push(
|
|
314
|
+
writeFumadocsRegistry(child, {
|
|
315
|
+
dir: path2.join(dir, child.name),
|
|
316
|
+
log: options.log
|
|
317
|
+
})
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
394
321
|
await Promise.all(write);
|
|
395
322
|
}
|
|
396
323
|
async function writeFile2(file, content, log = true) {
|
|
397
|
-
await
|
|
398
|
-
await
|
|
324
|
+
await fs2.mkdir(path2.dirname(file), { recursive: true });
|
|
325
|
+
await fs2.writeFile(file, content);
|
|
399
326
|
if (log) {
|
|
400
327
|
const size = (Buffer.byteLength(content) / 1024).toFixed(2);
|
|
401
328
|
console.log(
|
|
402
|
-
`${picocolors.greenBright("+")} ${
|
|
329
|
+
`${picocolors.greenBright("+")} ${path2.relative(process.cwd(), file)} ${picocolors.dim(`${size} KB`)}`
|
|
403
330
|
);
|
|
404
331
|
}
|
|
405
332
|
}
|
|
406
333
|
export {
|
|
407
|
-
|
|
334
|
+
ComponentCompiler,
|
|
335
|
+
RegistryCompiler,
|
|
408
336
|
combineRegistry,
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
writeFumadocsRegistry,
|
|
412
|
-
writeShadcnRegistry
|
|
337
|
+
resolveFromRemote,
|
|
338
|
+
writeFumadocsRegistry
|
|
413
339
|
};
|