@fumadocs/cli 1.2.0 → 1.2.2

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.
@@ -1,339 +1,291 @@
1
- // src/build/index.ts
2
- import * as fs2 from "fs/promises";
3
- import * as path2 from "path";
1
+ import { n as transformSpecifiers } from "../ast-DFGM1gEn.js";
2
+ import * as fs$1 from "node:fs/promises";
3
+ import * as path$1 from "node:path";
4
4
  import picocolors from "picocolors";
5
+ import { parse } from "oxc-parser";
6
+ import MagicString from "magic-string";
7
+ import { ResolverFactory } from "oxc-resolver";
5
8
 
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";
9
+ //#region src/build/compiler.ts
10
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
- });
16
- }
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
- });
52
- }
53
- output.components.push(comp);
54
- }
55
- return output;
56
- }
11
+ constructor(registry) {
12
+ this.raw = registry;
13
+ }
14
+ async readPackageJson() {
15
+ if (typeof this.raw.packageJson !== "string") return this.raw.packageJson;
16
+ return fs$1.readFile(path$1.join(this.raw.dir, this.raw.packageJson)).then((res) => JSON.parse(res.toString())).catch(() => void 0);
17
+ }
18
+ async compile() {
19
+ const registry = this.raw;
20
+ this.resolver = new RegistryResolver(this, await this.readPackageJson());
21
+ const output = {
22
+ name: registry.name,
23
+ info: {
24
+ indexes: [],
25
+ env: registry.env,
26
+ variables: registry.variables
27
+ },
28
+ components: []
29
+ };
30
+ const builtComps = await Promise.all(registry.components.map(async (component) => {
31
+ return [component, await new ComponentCompiler(this, component).build()];
32
+ }));
33
+ for (const [input, comp] of builtComps) {
34
+ if (!input.unlisted) output.info.indexes.push({
35
+ name: input.name,
36
+ title: input.title,
37
+ description: input.description
38
+ });
39
+ output.components.push(comp);
40
+ }
41
+ return output;
42
+ }
57
43
  };
58
44
  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]);
70
- }
71
- }
72
- this.deps = {
73
- ...packageJson?.dependencies,
74
- ...registry.dependencies
75
- };
76
- this.devDeps = {
77
- ...packageJson?.devDependencies,
78
- ...registry.devDependencies
79
- };
80
- }
81
- getDepFromSpecifier(specifier) {
82
- return specifier.startsWith("@") ? specifier.split("/").slice(0, 2).join("/") : specifier.split("/")[0];
83
- }
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
- }
45
+ constructor(compiler, packageJson = {}) {
46
+ this.compiler = compiler;
47
+ this.fileToComponent = /* @__PURE__ */ new Map();
48
+ const registry = compiler.raw;
49
+ for (const comp of registry.components) for (const file of comp.files) {
50
+ if (this.fileToComponent.has(file.path)) console.warn(`the same file ${file.path} exists in multiple component, you should make the shared file a separate component.`);
51
+ this.fileToComponent.set(file.path, [comp, file]);
52
+ }
53
+ this.deps = {
54
+ ...packageJson?.dependencies,
55
+ ...registry.dependencies
56
+ };
57
+ this.devDeps = {
58
+ ...packageJson?.devDependencies,
59
+ ...registry.devDependencies
60
+ };
61
+ this.oxc = new ResolverFactory({
62
+ extensions: [
63
+ ".js",
64
+ ".jsx",
65
+ ".ts",
66
+ ".tsx",
67
+ ".node"
68
+ ],
69
+ conditionNames: [
70
+ "node",
71
+ "import",
72
+ "require",
73
+ "default",
74
+ "types"
75
+ ],
76
+ tsconfig: { configFile: path$1.join(registry.dir, registry.tsconfigPath) }
77
+ });
78
+ }
79
+ getDepFromSpecifier(specifier) {
80
+ return specifier.startsWith("@") ? specifier.split("/").slice(0, 2).join("/") : specifier.split("/")[0];
81
+ }
82
+ getDepInfo(name) {
83
+ if (name in this.deps) return {
84
+ name,
85
+ type: "runtime",
86
+ version: this.deps[name]
87
+ };
88
+ if (name in this.devDeps) return {
89
+ name,
90
+ type: "dev",
91
+ version: this.devDeps[name]
92
+ };
93
+ console.warn(`dep info for ${name} cannot be found`);
94
+ }
95
+ getComponentByName(name) {
96
+ return this.compiler.raw.components.find((comp) => comp.name === name);
97
+ }
98
+ getSubComponent(file) {
99
+ const relativeFile = path$1.relative(this.compiler.raw.dir, file);
100
+ const comp = this.fileToComponent.get(relativeFile);
101
+ if (!comp) return;
102
+ return {
103
+ component: comp[0],
104
+ file: comp[1]
105
+ };
106
+ }
111
107
  };
112
108
  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) {
188
- let filePath;
189
- if (specified) {
190
- filePath = specified.getFilePath();
191
- } else if (specifier.startsWith("./") || specifier.startsWith("../")) {
192
- filePath = path.join(path.dirname(sourceFilePath), specifier);
193
- } else {
194
- if (!specifier.startsWith("node:"))
195
- console.warn(`Unknown specifier ${specifier}, skipping for now`);
196
- return;
197
- }
198
- const resolver = this.compiler.resolver;
199
- if (path.relative(this.registry.dir, filePath).startsWith("../")) {
200
- return {
201
- type: "dependency",
202
- dep: resolver.getDepFromSpecifier(specifier),
203
- specifier
204
- };
205
- }
206
- const sub = resolver.getSubComponent(filePath);
207
- if (sub) {
208
- return {
209
- type: "sub-component",
210
- resolved: {
211
- type: "local",
212
- component: sub.component,
213
- file: sub.file
214
- }
215
- };
216
- }
217
- return {
218
- type: "file",
219
- file: filePath
220
- };
221
- }
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
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());
239
- }
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()
253
- );
254
- }
255
- }
256
- return {
257
- content: sourceFile.getFullText(),
258
- type: file.type,
259
- path: file.path,
260
- target: file.target
261
- };
262
- }
109
+ constructor(compiler, component) {
110
+ this.compiler = compiler;
111
+ this.component = component;
112
+ this.processedFiles = /* @__PURE__ */ new Set();
113
+ this.subComponents = /* @__PURE__ */ new Map();
114
+ this.devDependencies = /* @__PURE__ */ new Map();
115
+ this.dependencies = /* @__PURE__ */ new Map();
116
+ this.registry = compiler.raw;
117
+ }
118
+ toImportPath(file) {
119
+ let filePath = file.target ?? file.path;
120
+ if (filePath.startsWith("./")) filePath = filePath.slice(2);
121
+ return `@/${filePath.replaceAll(path$1.sep, "/")}`;
122
+ }
123
+ async build() {
124
+ return {
125
+ name: this.component.name,
126
+ title: this.component.title,
127
+ description: this.component.description,
128
+ files: (await Promise.all(this.component.files.map((file) => this.onBuildFile(file)))).flat(),
129
+ subComponents: Array.from(this.subComponents.values()),
130
+ dependencies: Object.fromEntries(this.dependencies),
131
+ devDependencies: Object.fromEntries(this.devDependencies)
132
+ };
133
+ }
134
+ async onBuildFile(file) {
135
+ if (this.processedFiles.has(file.path)) return [];
136
+ this.processedFiles.add(file.path);
137
+ const resolver = this.compiler.resolver;
138
+ const queue = [];
139
+ return [await this.buildFile(file, (reference) => {
140
+ if (reference.type === "unknown-specifier") {
141
+ if (!reference.specifier.startsWith("node:")) console.warn(`Unknown specifier ${reference.specifier}, skipping for now`);
142
+ return reference.specifier;
143
+ }
144
+ if (reference.type === "custom") return reference.specifier;
145
+ if (reference.type === "file") {
146
+ const refFile = this.registry.onUnknownFile?.(reference.file);
147
+ if (refFile) {
148
+ queue.push(refFile);
149
+ return this.toImportPath(refFile);
150
+ }
151
+ if (refFile === false) return;
152
+ throw new Error(`Unknown file ${reference.file} referenced by ${file.path}`);
153
+ }
154
+ if (reference.type === "sub-component") {
155
+ const resolved = reference.resolved;
156
+ if (resolved.component.name === this.component.name) return this.toImportPath(resolved.file);
157
+ if (resolved.type === "remote") this.subComponents.set(`${resolved.registryName}:${resolved.component.name}`, {
158
+ type: "http",
159
+ baseUrl: resolved.registryName,
160
+ component: resolved.component.name
161
+ });
162
+ else this.subComponents.set(resolved.component.name, resolved.component.name);
163
+ return this.toImportPath(resolved.file);
164
+ }
165
+ const dep = resolver.getDepInfo(reference.dep);
166
+ if (dep) (dep.type === "dev" ? this.devDependencies : this.dependencies).set(dep.name, dep.version);
167
+ return reference.specifier;
168
+ }), ...(await Promise.all(queue.map((file$1) => this.onBuildFile(file$1)))).flat()];
169
+ }
170
+ async buildFile(file, writeReference) {
171
+ const sourceFilePath = path$1.join(this.registry.dir, file.path);
172
+ const astType = {
173
+ ".ts": "ts",
174
+ ".tsx": "ts",
175
+ ".js": "js",
176
+ ".jsx": "js"
177
+ }[path$1.extname(file.path)];
178
+ const content = (await fs$1.readFile(sourceFilePath)).toString();
179
+ if (!astType) return {
180
+ content,
181
+ path: file.path,
182
+ type: file.type,
183
+ target: file.target
184
+ };
185
+ const resolver = this.compiler.resolver;
186
+ const ast = await parse(sourceFilePath, content, { astType });
187
+ if (ast.errors.length > 0) throw new Error(`failed to parse file ${sourceFilePath}: \n${ast.errors.join("\n")}`);
188
+ const s = new MagicString(content);
189
+ /**
190
+ * Process import paths
191
+ */
192
+ transformSpecifiers(ast.program, s, (specifier) => {
193
+ let resolved = {
194
+ type: "unknown-specifier",
195
+ specifier
196
+ };
197
+ const onResolve = this.component.onResolve ?? this.registry.onResolve;
198
+ const resolvedSpecifier = resolver.oxc.resolveFileSync(sourceFilePath, specifier);
199
+ if (resolvedSpecifier.error || !resolvedSpecifier.path) return writeReference(onResolve ? onResolve(resolved) : resolved);
200
+ resolved = {
201
+ type: "file",
202
+ file: resolvedSpecifier.path
203
+ };
204
+ if (path$1.relative(this.registry.dir, resolvedSpecifier.path).startsWith("../")) resolved = {
205
+ type: "dependency",
206
+ dep: resolver.getDepFromSpecifier(specifier),
207
+ specifier
208
+ };
209
+ else {
210
+ const sub = resolver.getSubComponent(resolvedSpecifier.path);
211
+ if (sub) resolved = {
212
+ type: "sub-component",
213
+ resolved: {
214
+ type: "local",
215
+ component: sub.component,
216
+ file: sub.file
217
+ }
218
+ };
219
+ }
220
+ return writeReference(onResolve ? onResolve(resolved) : resolved);
221
+ });
222
+ return {
223
+ content: s.toString(),
224
+ type: file.type,
225
+ path: file.path,
226
+ target: file.target
227
+ };
228
+ }
263
229
  };
264
230
  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;
269
- return {
270
- type: "sub-component",
271
- resolved: {
272
- type: "remote",
273
- registryName: r.name,
274
- component: comp,
275
- file
276
- }
277
- };
231
+ const comp = r.components.find((comp$1) => comp$1.name === component);
232
+ if (!comp) return;
233
+ const file = comp.files.find(selectFile);
234
+ if (!file) return;
235
+ return {
236
+ type: "sub-component",
237
+ resolved: {
238
+ type: "remote",
239
+ registryName: r.name,
240
+ component: comp,
241
+ file
242
+ }
243
+ };
278
244
  }
279
245
 
280
- // src/build/index.ts
246
+ //#endregion
247
+ //#region src/build/index.ts
281
248
  function combineRegistry(root, ...items) {
282
- return {
283
- ...root,
284
- info: {
285
- ...root.info,
286
- registries: items.map((item) => item.name)
287
- },
288
- registries: items
289
- };
249
+ return {
250
+ ...root,
251
+ info: {
252
+ ...root.info,
253
+ registries: items.map((item) => item.name)
254
+ },
255
+ registries: items
256
+ };
290
257
  }
291
258
  async function writeFumadocsRegistry(out, options) {
292
- const { dir, cleanDir = false, log = true } = options;
293
- if (cleanDir) {
294
- await fs2.rm(dir, {
295
- recursive: true,
296
- force: true
297
- });
298
- console.log(picocolors.bold(picocolors.greenBright("Cleaned directory")));
299
- }
300
- async function writeInfo() {
301
- const file = path2.join(dir, "_registry.json");
302
- const json = JSON.stringify(out.info, null, 2);
303
- await writeFile2(file, json, log);
304
- }
305
- const write = out.components.map(async (comp) => {
306
- const file = path2.join(dir, `${comp.name}.json`);
307
- const json = JSON.stringify(comp, null, 2);
308
- await writeFile2(file, json, log);
309
- });
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
- }
321
- await Promise.all(write);
259
+ const { dir, cleanDir = false, log = true } = options;
260
+ if (cleanDir) {
261
+ await fs$1.rm(dir, {
262
+ recursive: true,
263
+ force: true
264
+ });
265
+ console.log(picocolors.bold(picocolors.greenBright("Cleaned directory")));
266
+ }
267
+ async function writeInfo() {
268
+ await writeFile(path$1.join(dir, "_registry.json"), JSON.stringify(out.info, null, 2), log);
269
+ }
270
+ const write = out.components.map(async (comp) => {
271
+ await writeFile(path$1.join(dir, `${comp.name}.json`), JSON.stringify(comp, null, 2), log);
272
+ });
273
+ write.push(writeInfo());
274
+ if ("registries" in out) for (const child of out.registries) write.push(writeFumadocsRegistry(child, {
275
+ dir: path$1.join(dir, child.name),
276
+ log: options.log
277
+ }));
278
+ await Promise.all(write);
322
279
  }
323
- async function writeFile2(file, content, log = true) {
324
- await fs2.mkdir(path2.dirname(file), { recursive: true });
325
- await fs2.writeFile(file, content);
326
- if (log) {
327
- const size = (Buffer.byteLength(content) / 1024).toFixed(2);
328
- console.log(
329
- `${picocolors.greenBright("+")} ${path2.relative(process.cwd(), file)} ${picocolors.dim(`${size} KB`)}`
330
- );
331
- }
280
+ async function writeFile(file, content, log = true) {
281
+ await fs$1.mkdir(path$1.dirname(file), { recursive: true });
282
+ await fs$1.writeFile(file, content);
283
+ if (log) {
284
+ const size = (Buffer.byteLength(content) / 1024).toFixed(2);
285
+ console.log(`${picocolors.greenBright("+")} ${path$1.relative(process.cwd(), file)} ${picocolors.dim(`${size} KB`)}`);
286
+ }
332
287
  }
333
- export {
334
- ComponentCompiler,
335
- RegistryCompiler,
336
- combineRegistry,
337
- resolveFromRemote,
338
- writeFumadocsRegistry
339
- };
288
+
289
+ //#endregion
290
+ export { ComponentCompiler, RegistryCompiler, combineRegistry, resolveFromRemote, writeFumadocsRegistry };
291
+ //# sourceMappingURL=index.js.map