@fumadocs/cli 0.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/LICENSE +21 -0
- package/dist/build/index.d.ts +104 -0
- package/dist/build/index.js +284 -0
- package/dist/chunk-WBCEM7RC.js +19 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1044 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Fuma
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
interface Component {
|
|
2
|
+
name: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
files: string[];
|
|
5
|
+
/**
|
|
6
|
+
* Don't list the component in registry index file
|
|
7
|
+
*/
|
|
8
|
+
unlisted?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Map imported file paths
|
|
11
|
+
*/
|
|
12
|
+
mapImportPath?: Record<string, string | {
|
|
13
|
+
type: 'component';
|
|
14
|
+
registry: string;
|
|
15
|
+
name: string;
|
|
16
|
+
file: string;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
type NamespaceType = 'components' | 'hooks' | 'lib';
|
|
20
|
+
interface PackageJson {
|
|
21
|
+
dependencies: Record<string, string>;
|
|
22
|
+
devDependencies: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
interface Registry {
|
|
25
|
+
/**
|
|
26
|
+
* The path of registry, needed to resolve relative paths
|
|
27
|
+
*/
|
|
28
|
+
path: string;
|
|
29
|
+
/**
|
|
30
|
+
* Extend on existing registry
|
|
31
|
+
*/
|
|
32
|
+
on?: Record<string, {
|
|
33
|
+
type: 'remote';
|
|
34
|
+
registry: Output;
|
|
35
|
+
} | {
|
|
36
|
+
type: 'local';
|
|
37
|
+
registry: Registry;
|
|
38
|
+
}>;
|
|
39
|
+
/**
|
|
40
|
+
* The root directory project, used to resolve config paths
|
|
41
|
+
*/
|
|
42
|
+
rootDir: string;
|
|
43
|
+
namespaces?: Record<string, NamespaceType>;
|
|
44
|
+
tsconfigPath?: string;
|
|
45
|
+
packageJson?: string | PackageJson;
|
|
46
|
+
components: Component[];
|
|
47
|
+
dependencies?: Record<string, {
|
|
48
|
+
type: 'runtime' | 'dev';
|
|
49
|
+
version?: string;
|
|
50
|
+
}>;
|
|
51
|
+
}
|
|
52
|
+
interface Output {
|
|
53
|
+
index: OutputIndex[];
|
|
54
|
+
components: OutputComponent[];
|
|
55
|
+
}
|
|
56
|
+
interface OutputIndex {
|
|
57
|
+
name: string;
|
|
58
|
+
description?: string;
|
|
59
|
+
}
|
|
60
|
+
interface OutputFile {
|
|
61
|
+
path: string;
|
|
62
|
+
content: string;
|
|
63
|
+
/**
|
|
64
|
+
* Import reference path - path in `files`
|
|
65
|
+
*/
|
|
66
|
+
imports: Record<string, string>;
|
|
67
|
+
}
|
|
68
|
+
interface OutputComponent {
|
|
69
|
+
name: string;
|
|
70
|
+
files: OutputFile[];
|
|
71
|
+
dependencies: Record<string, string>;
|
|
72
|
+
devDependencies: Record<string, string>;
|
|
73
|
+
subComponents: string[];
|
|
74
|
+
}
|
|
75
|
+
declare function build(registry: Registry): Promise<Output>;
|
|
76
|
+
|
|
77
|
+
interface DependencyInfo {
|
|
78
|
+
type: 'runtime' | 'dev';
|
|
79
|
+
version?: string;
|
|
80
|
+
}
|
|
81
|
+
interface ComponentBuilder {
|
|
82
|
+
rootDir: string;
|
|
83
|
+
registryDir: string;
|
|
84
|
+
resolveDep: (specifier: string) => DependencyInfo & {
|
|
85
|
+
name: string;
|
|
86
|
+
};
|
|
87
|
+
resolveOutputPath: (path: string) => string;
|
|
88
|
+
getSubComponent: (path: string) => {
|
|
89
|
+
component: Component;
|
|
90
|
+
} | undefined;
|
|
91
|
+
}
|
|
92
|
+
declare function createComponentBuilder(registry: Registry, packageJson: PackageJson | undefined, registryDir: string, rootDir: string): ComponentBuilder;
|
|
93
|
+
|
|
94
|
+
declare function writeOutput(dir: string, out: Output, options?: {
|
|
95
|
+
/**
|
|
96
|
+
* Remove all content from the `dir` directory
|
|
97
|
+
*
|
|
98
|
+
* @defaultValue false
|
|
99
|
+
*/
|
|
100
|
+
cleanDir?: boolean;
|
|
101
|
+
log?: boolean;
|
|
102
|
+
}): Promise<void>;
|
|
103
|
+
|
|
104
|
+
export { type Component, type ComponentBuilder, type DependencyInfo, type NamespaceType, type Output, type OutputComponent, type OutputFile, type OutputIndex, type PackageJson, type Registry, build, createComponentBuilder, writeOutput };
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import {
|
|
2
|
+
exists
|
|
3
|
+
} from "../chunk-WBCEM7RC.js";
|
|
4
|
+
|
|
5
|
+
// src/build/index.ts
|
|
6
|
+
import * as fs2 from "node:fs/promises";
|
|
7
|
+
import * as path4 from "node:path";
|
|
8
|
+
import picocolors from "picocolors";
|
|
9
|
+
|
|
10
|
+
// src/build/build-registry.ts
|
|
11
|
+
import * as fs from "node:fs/promises";
|
|
12
|
+
import * as path3 from "node:path";
|
|
13
|
+
import { Project } from "ts-morph";
|
|
14
|
+
|
|
15
|
+
// src/build/build-file.ts
|
|
16
|
+
import * as path from "node:path";
|
|
17
|
+
async function buildFile(filePath, sourceFile, builder, comp, processedFiles) {
|
|
18
|
+
processedFiles.add(filePath);
|
|
19
|
+
const out = {
|
|
20
|
+
imports: {},
|
|
21
|
+
content: "",
|
|
22
|
+
path: filePath
|
|
23
|
+
};
|
|
24
|
+
const processed = {
|
|
25
|
+
files: [out],
|
|
26
|
+
dependencies: /* @__PURE__ */ new Map(),
|
|
27
|
+
devDependencies: /* @__PURE__ */ new Map(),
|
|
28
|
+
subComponents: /* @__PURE__ */ new Set()
|
|
29
|
+
};
|
|
30
|
+
async function process2(specifier, getSpecifiedFile) {
|
|
31
|
+
let specifiedFile = getSpecifiedFile();
|
|
32
|
+
if (!specifiedFile) return;
|
|
33
|
+
const name = specifiedFile.isInNodeModules() ? builder.resolveDep(specifier.getLiteralValue()).name : path.relative(builder.registryDir, specifiedFile.getFilePath());
|
|
34
|
+
if (comp.mapImportPath && name in comp.mapImportPath) {
|
|
35
|
+
const resolver = comp.mapImportPath[name];
|
|
36
|
+
if (typeof resolver === "string") {
|
|
37
|
+
specifier.setLiteralValue(resolver);
|
|
38
|
+
specifiedFile = getSpecifiedFile();
|
|
39
|
+
if (!specifiedFile) return;
|
|
40
|
+
} else {
|
|
41
|
+
processed.subComponents.add(resolver.name);
|
|
42
|
+
out.imports[specifier.getLiteralValue()] = resolver.file;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (specifiedFile.isInNodeModules() || specifiedFile.isDeclarationFile()) {
|
|
47
|
+
const info = builder.resolveDep(specifier.getLiteralValue());
|
|
48
|
+
if (info.type === "dev") {
|
|
49
|
+
processed.devDependencies.set(info.name, info.version ?? "");
|
|
50
|
+
} else {
|
|
51
|
+
processed.dependencies.set(info.name, info.version ?? "");
|
|
52
|
+
}
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const sub = builder.getSubComponent(specifiedFile.getFilePath());
|
|
56
|
+
if (sub) {
|
|
57
|
+
processed.subComponents.add(sub.component.name);
|
|
58
|
+
out.imports[specifier.getLiteralValue()] = builder.resolveOutputPath(
|
|
59
|
+
specifiedFile.getFilePath()
|
|
60
|
+
);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const referenceOutputPath = builder.resolveOutputPath(
|
|
64
|
+
specifiedFile.getFilePath()
|
|
65
|
+
);
|
|
66
|
+
if (!processedFiles.has(referenceOutputPath)) {
|
|
67
|
+
const outFile = await buildFile(
|
|
68
|
+
referenceOutputPath,
|
|
69
|
+
specifiedFile,
|
|
70
|
+
builder,
|
|
71
|
+
comp,
|
|
72
|
+
processedFiles
|
|
73
|
+
);
|
|
74
|
+
merge(processed, outFile);
|
|
75
|
+
}
|
|
76
|
+
out.imports[specifier.getLiteralValue()] = referenceOutputPath;
|
|
77
|
+
}
|
|
78
|
+
for (const item of sourceFile.getImportDeclarations()) {
|
|
79
|
+
await process2(
|
|
80
|
+
item.getModuleSpecifier(),
|
|
81
|
+
() => item.getModuleSpecifierSourceFile()
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
for (const item of sourceFile.getExportDeclarations()) {
|
|
85
|
+
const specifier = item.getModuleSpecifier();
|
|
86
|
+
if (!specifier) continue;
|
|
87
|
+
await process2(specifier, () => item.getModuleSpecifierSourceFile());
|
|
88
|
+
}
|
|
89
|
+
out.content = sourceFile.getFullText();
|
|
90
|
+
return processed;
|
|
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));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/build/component-builder.ts
|
|
104
|
+
import path2 from "node:path";
|
|
105
|
+
function createComponentBuilder(registry, packageJson, registryDir, rootDir) {
|
|
106
|
+
const fileToComponent = /* @__PURE__ */ new Map();
|
|
107
|
+
for (const comp of registry.components) {
|
|
108
|
+
for (const file of comp.files) {
|
|
109
|
+
if (fileToComponent.has(file))
|
|
110
|
+
console.warn(
|
|
111
|
+
`the same file ${file} exists in multiple component, you should make the shared file a separate component.`
|
|
112
|
+
);
|
|
113
|
+
fileToComponent.set(file, comp);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
registryDir,
|
|
118
|
+
rootDir,
|
|
119
|
+
resolveDep(specifier) {
|
|
120
|
+
const name = specifier.startsWith("@") ? specifier.split("/").slice(0, 2).join("/") : specifier.split("/")[0];
|
|
121
|
+
if (registry.dependencies && name in registry.dependencies)
|
|
122
|
+
return {
|
|
123
|
+
...registry.dependencies[name],
|
|
124
|
+
name
|
|
125
|
+
};
|
|
126
|
+
if (packageJson && name in packageJson.devDependencies) {
|
|
127
|
+
return {
|
|
128
|
+
type: "dev",
|
|
129
|
+
version: packageJson.devDependencies[name],
|
|
130
|
+
name
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (packageJson && name in packageJson.dependencies) {
|
|
134
|
+
return {
|
|
135
|
+
type: "runtime",
|
|
136
|
+
version: packageJson.dependencies[name],
|
|
137
|
+
name
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
return { type: "runtime", name };
|
|
141
|
+
},
|
|
142
|
+
resolveOutputPath(file) {
|
|
143
|
+
const relativeFile = path2.relative(registryDir, file);
|
|
144
|
+
if (registry.namespaces)
|
|
145
|
+
for (const namespace of Object.keys(registry.namespaces)) {
|
|
146
|
+
const relativePath = path2.relative(namespace, relativeFile);
|
|
147
|
+
if (relativePath.startsWith("../") || path2.isAbsolute(relativePath))
|
|
148
|
+
continue;
|
|
149
|
+
return `${registry.namespaces[namespace]}:${relativePath}`;
|
|
150
|
+
}
|
|
151
|
+
return path2.relative(rootDir, file);
|
|
152
|
+
},
|
|
153
|
+
getSubComponent(file) {
|
|
154
|
+
const relativeFile = path2.relative(registryDir, file);
|
|
155
|
+
const comp = fileToComponent.get(relativeFile);
|
|
156
|
+
if (!comp) return;
|
|
157
|
+
return {
|
|
158
|
+
component: comp
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/build/build-registry.ts
|
|
165
|
+
async function build(registry) {
|
|
166
|
+
const registryDir = path3.dirname(registry.path);
|
|
167
|
+
const rootDir = path3.join(registryDir, registry.rootDir);
|
|
168
|
+
const output = {
|
|
169
|
+
index: [],
|
|
170
|
+
components: []
|
|
171
|
+
};
|
|
172
|
+
const project = new Project({
|
|
173
|
+
tsConfigFilePath: registry.tsconfigPath ? path3.join(registryDir, registry.tsconfigPath) : path3.join(rootDir, "tsconfig.json")
|
|
174
|
+
});
|
|
175
|
+
const packageJson = typeof registry.packageJson !== "string" && registry.packageJson ? registry.packageJson : await fs.readFile(
|
|
176
|
+
registry.packageJson ? path3.join(registryDir, registry.packageJson) : path3.join(rootDir, "package.json")
|
|
177
|
+
).then((res) => JSON.parse(res.toString())).catch(() => void 0);
|
|
178
|
+
const builder = createComponentBuilder(
|
|
179
|
+
registry,
|
|
180
|
+
packageJson,
|
|
181
|
+
registryDir,
|
|
182
|
+
rootDir
|
|
183
|
+
);
|
|
184
|
+
const buildExtendRegistries = Object.values(registry.on ?? {}).map(
|
|
185
|
+
async (schema) => {
|
|
186
|
+
if (schema.type === "remote") {
|
|
187
|
+
return schema.registry;
|
|
188
|
+
}
|
|
189
|
+
return await build(schema.registry);
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
for (const built of await Promise.all(buildExtendRegistries)) {
|
|
193
|
+
output.components.push(...built.components);
|
|
194
|
+
output.index.push(...built.index);
|
|
195
|
+
}
|
|
196
|
+
const buildComps = registry.components.map(async (component) => {
|
|
197
|
+
const processedFiles = /* @__PURE__ */ new Set();
|
|
198
|
+
const collect = {
|
|
199
|
+
files: [],
|
|
200
|
+
subComponents: /* @__PURE__ */ new Set(),
|
|
201
|
+
devDependencies: /* @__PURE__ */ new Map(),
|
|
202
|
+
dependencies: /* @__PURE__ */ new Map()
|
|
203
|
+
};
|
|
204
|
+
const read = component.files.map((file) => path3.join(registryDir, file)).map(async (file) => {
|
|
205
|
+
const content = await fs.readFile(file);
|
|
206
|
+
const sourceFile = project.createSourceFile(file, content.toString(), {
|
|
207
|
+
overwrite: true
|
|
208
|
+
});
|
|
209
|
+
const outputPath = builder.resolveOutputPath(sourceFile.getFilePath());
|
|
210
|
+
if (processedFiles.has(outputPath)) return;
|
|
211
|
+
return buildFile(
|
|
212
|
+
outputPath,
|
|
213
|
+
sourceFile,
|
|
214
|
+
builder,
|
|
215
|
+
component,
|
|
216
|
+
processedFiles
|
|
217
|
+
);
|
|
218
|
+
});
|
|
219
|
+
const outFiles = await Promise.all(read);
|
|
220
|
+
for (const file of outFiles) {
|
|
221
|
+
if (!file) continue;
|
|
222
|
+
merge(collect, file);
|
|
223
|
+
}
|
|
224
|
+
return [
|
|
225
|
+
component,
|
|
226
|
+
{
|
|
227
|
+
name: component.name,
|
|
228
|
+
files: collect.files,
|
|
229
|
+
subComponents: Array.from(collect.subComponents),
|
|
230
|
+
dependencies: Object.fromEntries(collect.dependencies),
|
|
231
|
+
devDependencies: Object.fromEntries(collect.devDependencies)
|
|
232
|
+
}
|
|
233
|
+
];
|
|
234
|
+
});
|
|
235
|
+
for (const [input, comp] of await Promise.all(buildComps)) {
|
|
236
|
+
if (!input.unlisted) {
|
|
237
|
+
output.index.push({
|
|
238
|
+
name: input.name,
|
|
239
|
+
description: input.description
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
output.components.push(comp);
|
|
243
|
+
}
|
|
244
|
+
return output;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// src/build/index.ts
|
|
248
|
+
async function writeOutput(dir, out, options = {}) {
|
|
249
|
+
const { log = true } = options;
|
|
250
|
+
if (options.cleanDir && await exists(dir)) {
|
|
251
|
+
await fs2.rm(dir, {
|
|
252
|
+
recursive: true
|
|
253
|
+
});
|
|
254
|
+
if (log) {
|
|
255
|
+
console.log(picocolors.bold(picocolors.greenBright("Cleaned directory")));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async function writeFile2(file, content) {
|
|
259
|
+
if (!log) return;
|
|
260
|
+
await fs2.mkdir(path4.dirname(file), { recursive: true });
|
|
261
|
+
await fs2.writeFile(file, content);
|
|
262
|
+
const size = (Buffer.byteLength(content) / 1024).toFixed(2);
|
|
263
|
+
console.log(
|
|
264
|
+
`${picocolors.greenBright("+")} ${path4.relative(process.cwd(), file)} ${picocolors.dim(`${size.toString()} KB`)}`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
const write = out.components.map(async (comp) => {
|
|
268
|
+
const file = path4.join(dir, `${comp.name}.json`);
|
|
269
|
+
const json = JSON.stringify(comp, null, 2);
|
|
270
|
+
await writeFile2(file, json);
|
|
271
|
+
});
|
|
272
|
+
async function writeIndex() {
|
|
273
|
+
const file = path4.join(dir, "_registry.json");
|
|
274
|
+
const json = JSON.stringify(out.index, null, 2);
|
|
275
|
+
await writeFile2(file, json);
|
|
276
|
+
}
|
|
277
|
+
write.push(writeIndex());
|
|
278
|
+
await Promise.all(write);
|
|
279
|
+
}
|
|
280
|
+
export {
|
|
281
|
+
build,
|
|
282
|
+
createComponentBuilder,
|
|
283
|
+
writeOutput
|
|
284
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/utils/fs.ts
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
async function exists(pathLike) {
|
|
5
|
+
try {
|
|
6
|
+
await fs.access(pathLike);
|
|
7
|
+
return true;
|
|
8
|
+
} catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function isRelative(from, to) {
|
|
13
|
+
return !path.relative(from, to).startsWith(`..${path.sep}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
exists,
|
|
18
|
+
isRelative
|
|
19
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|