@hanzo/docs-cli 1.1.1 → 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.
package/dist/index.js CHANGED
@@ -1,728 +1,644 @@
1
1
  #!/usr/bin/env node
2
-
3
- // src/index.ts
4
- import fs6 from "fs/promises";
5
- import path5 from "path";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
6
4
  import { Command } from "commander";
7
- import picocolors3 from "picocolors";
8
-
9
- // src/config.ts
10
- import fs2 from "fs/promises";
5
+ import picocolors from "picocolors";
6
+ import { z } from "zod";
7
+ import { x } from "tinyexec";
8
+ import { cancel, confirm, group, intro, isCancel, log, multiselect, outro, select, spinner } from "@clack/prompts";
9
+ import { Project } from "ts-morph";
10
+ import { detect } from "package-manager-detector";
11
11
 
12
- // src/utils/fs.ts
13
- import fs from "fs/promises";
14
- import path from "path";
12
+ //#region src/utils/fs.ts
15
13
  async function exists(pathLike) {
16
- try {
17
- await fs.access(pathLike);
18
- return true;
19
- } catch {
20
- return false;
21
- }
14
+ try {
15
+ await fs.access(pathLike);
16
+ return true;
17
+ } catch {
18
+ return false;
19
+ }
22
20
  }
23
21
 
24
- // src/utils/is-src.ts
22
+ //#endregion
23
+ //#region src/utils/is-src.ts
25
24
  async function isSrc() {
26
- return exists("./src");
25
+ return exists("./src");
27
26
  }
28
27
 
29
- // src/config.ts
30
- import { z } from "zod";
31
- function createConfigSchema(isSrc2) {
32
- const defaultAliases = {
33
- uiDir: "./components/ui",
34
- componentsDir: "./components",
35
- blockDir: "./components",
36
- cssDir: "./styles",
37
- libDir: "./lib"
38
- };
39
- return z.object({
40
- aliases: z.object({
41
- uiDir: z.string().default(defaultAliases.uiDir),
42
- componentsDir: z.string().default(defaultAliases.uiDir),
43
- blockDir: z.string().default(defaultAliases.blockDir),
44
- cssDir: z.string().default(defaultAliases.componentsDir),
45
- libDir: z.string().default(defaultAliases.libDir)
46
- }).default(defaultAliases),
47
- baseDir: z.string().default(isSrc2 ? "src" : ""),
48
- commands: z.object({
49
- /**
50
- * command to format output code automatically
51
- */
52
- format: z.string().optional()
53
- }).default({})
54
- });
28
+ //#endregion
29
+ //#region src/config.ts
30
+ function createConfigSchema(isSrc$1) {
31
+ const defaultAliases = {
32
+ uiDir: "./components/ui",
33
+ componentsDir: "./components",
34
+ blockDir: "./components",
35
+ cssDir: "./styles",
36
+ libDir: "./lib"
37
+ };
38
+ return z.object({
39
+ $schema: z.string().default(isSrc$1 ? "node_modules/@hanzo/docs-cli/dist/schema/src.json" : "node_modules/@hanzo/docs-cli/dist/schema/default.json").optional(),
40
+ aliases: z.object({
41
+ uiDir: z.string().default(defaultAliases.uiDir),
42
+ componentsDir: z.string().default(defaultAliases.uiDir),
43
+ blockDir: z.string().default(defaultAliases.blockDir),
44
+ cssDir: z.string().default(defaultAliases.componentsDir),
45
+ libDir: z.string().default(defaultAliases.libDir)
46
+ }).default(defaultAliases),
47
+ baseDir: z.string().default(isSrc$1 ? "src" : ""),
48
+ uiLibrary: z.enum(["radix-ui", "base-ui"]).default("radix-ui"),
49
+ commands: z.object({ format: z.string().optional() }).default({})
50
+ });
55
51
  }
56
52
  async function createOrLoadConfig(file = "./cli.json") {
57
- const inited = await initConfig(file);
58
- if (inited) return inited;
59
- const content = (await fs2.readFile(file)).toString();
60
- const src = await isSrc();
61
- const configSchema = createConfigSchema(src);
62
- return configSchema.parse(JSON.parse(content));
53
+ const inited = await initConfig(file);
54
+ if (inited) return inited;
55
+ const content = (await fs.readFile(file)).toString();
56
+ return createConfigSchema(await isSrc()).parse(JSON.parse(content));
63
57
  }
64
- async function initConfig(file = "./cli.json") {
65
- if (await fs2.stat(file).then(() => true).catch(() => false)) {
66
- return;
67
- }
68
- const src = await isSrc();
69
- const defaultConfig = createConfigSchema(src).parse({});
70
- await fs2.writeFile(file, JSON.stringify(defaultConfig, null, 2));
71
- return defaultConfig;
58
+ /**
59
+ * Write new config, skip if a config already exists
60
+ *
61
+ * @returns the created config, `undefined` if not created
62
+ */
63
+ async function initConfig(file = "./cli.json", src) {
64
+ if (await fs.stat(file).then(() => true).catch(() => false)) return;
65
+ const defaultConfig = createConfigSchema(src ?? await isSrc()).parse({});
66
+ await fs.writeFile(file, JSON.stringify(defaultConfig, null, 2));
67
+ return defaultConfig;
72
68
  }
73
69
 
74
- // src/commands/file-tree.ts
75
- var scanned = ["file", "directory", "link"];
70
+ //#endregion
71
+ //#region src/commands/file-tree.ts
72
+ const scanned = [
73
+ "file",
74
+ "directory",
75
+ "link"
76
+ ];
76
77
  function treeToMdx(input, noRoot = false) {
77
- function toNode(item) {
78
- if (item.type === "file" || item.type === "link") {
79
- return `<File name=${JSON.stringify(item.name)} />`;
80
- }
81
- if (item.type === "directory") {
82
- if (item.contents.length === 1 && "name" in item.contents[0]) {
83
- const child = item.contents[0];
84
- return toNode({
85
- ...child,
86
- name: `${item.name}/${child.name}`
87
- });
88
- }
89
- return `<Folder name=${JSON.stringify(item.name)}>
78
+ function toNode(item) {
79
+ if (item.type === "file" || item.type === "link") return `<File name=${JSON.stringify(item.name)} />`;
80
+ if (item.type === "directory") {
81
+ if (item.contents.length === 1 && "name" in item.contents[0]) {
82
+ const child = item.contents[0];
83
+ return toNode({
84
+ ...child,
85
+ name: `${item.name}/${child.name}`
86
+ });
87
+ }
88
+ return `<Folder name=${JSON.stringify(item.name)}>
90
89
  ${item.contents.map(toNode).filter(Boolean).join("\n")}
91
90
  </Folder>`;
92
- }
93
- return "";
94
- }
95
- let children = input.filter((v) => scanned.includes(v.type));
96
- if (noRoot && children.length === 1 && input[0].type === "directory") {
97
- children = input[0].contents;
98
- }
99
- return `<Files>
91
+ }
92
+ return "";
93
+ }
94
+ let children = input.filter((v) => scanned.includes(v.type));
95
+ if (noRoot && children.length === 1 && input[0].type === "directory") children = input[0].contents;
96
+ return `<Files>
100
97
  ${children.map(toNode).filter(Boolean).join("\n")}
101
98
  </Files>`;
102
99
  }
103
100
  function treeToJavaScript(input, noRoot, importName = "@hanzo/docs-ui/components/files") {
104
- return `import { File, Files, Folder } from ${JSON.stringify(importName)}
101
+ return `import { File, Files, Folder } from ${JSON.stringify(importName)}
105
102
 
106
103
  export default (${treeToMdx(input, noRoot)})`;
107
104
  }
108
105
 
109
- // src/utils/file-tree/run-tree.ts
110
- import { x } from "tinyexec";
106
+ //#endregion
107
+ //#region src/utils/file-tree/run-tree.ts
111
108
  async function runTree(args) {
112
- const out = await x("tree", [args, "--gitignore", "--prune", "-J"]);
113
- try {
114
- return JSON.parse(out.stdout);
115
- } catch (e) {
116
- throw new Error("failed to run `tree` command", {
117
- cause: e
118
- });
119
- }
109
+ const out = await x("tree", [
110
+ args,
111
+ "--gitignore",
112
+ "--prune",
113
+ "-J"
114
+ ]);
115
+ try {
116
+ return JSON.parse(out.stdout);
117
+ } catch (e) {
118
+ throw new Error("failed to run `tree` command", { cause: e });
119
+ }
120
120
  }
121
121
 
122
- // package.json
123
- var package_default = {
124
- name: "@hanzo/docs-cli",
125
- version: "1.1.0",
126
- description: "The CLI tool for Hanzo Docs",
127
- keywords: [
128
- "Hanzo",
129
- "Docs",
130
- "CLI"
131
- ],
132
- homepage: "https://hanzo.ai/docs",
133
- repository: "github:hanzoai/docs",
134
- license: "MIT",
135
- author: "Fuma Nama",
136
- type: "module",
137
- exports: {
138
- "./build": {
139
- import: "./dist/build/index.js",
140
- types: "./dist/build/index.d.ts"
141
- }
142
- },
143
- main: "./dist/index.js",
144
- bin: {
145
- "hanzo-docs": "./dist/index.js"
146
- },
147
- files: [
148
- "dist/*"
149
- ],
150
- scripts: {
151
- build: "tsup",
152
- clean: "rimraf dist",
153
- dev: "tsup --watch",
154
- lint: "eslint .",
155
- "types:check": "tsc --noEmit"
156
- },
157
- dependencies: {
158
- "@clack/prompts": "^0.11.0",
159
- commander: "^14.0.2",
160
- "package-manager-detector": "^1.6.0",
161
- picocolors: "^1.1.1",
162
- tinyexec: "^1.0.2",
163
- "ts-morph": "^27.0.2",
164
- zod: "^4.1.13"
165
- },
166
- devDependencies: {
167
- "@types/node": "24.10.2",
168
- "eslint-config-custom": "workspace:*",
169
- shadcn: "3.6.0",
170
- tsconfig: "workspace:*"
171
- },
172
- publishConfig: {
173
- access: "public"
174
- }
175
- };
176
-
177
- // src/commands/customise.ts
178
- import { cancel, group, intro as intro2, log as log4, outro as outro3, select } from "@clack/prompts";
179
- import picocolors2 from "picocolors";
180
-
181
- // src/commands/add.ts
182
- import {
183
- intro,
184
- isCancel as isCancel3,
185
- log as log3,
186
- multiselect,
187
- outro as outro2,
188
- spinner as spinner2
189
- } from "@clack/prompts";
190
- import picocolors from "picocolors";
191
-
192
- // src/registry/installer/index.ts
193
- import path3 from "path";
194
- import fs4 from "fs/promises";
195
- import { confirm as confirm2, isCancel as isCancel2, log, outro } from "@clack/prompts";
122
+ //#endregion
123
+ //#region package.json
124
+ var version = "1.2.2";
196
125
 
197
- // src/utils/typescript.ts
198
- import { Project } from "ts-morph";
126
+ //#endregion
127
+ //#region src/utils/typescript.ts
199
128
  function createEmptyProject() {
200
- return new Project({
201
- compilerOptions: {}
202
- });
129
+ return new Project({ compilerOptions: {} });
203
130
  }
204
131
 
205
- // src/constants.ts
206
- var typescriptExtensions = [".ts", ".tsx", ".js", ".jsx"];
132
+ //#endregion
133
+ //#region src/constants.ts
134
+ const typescriptExtensions = [
135
+ ".ts",
136
+ ".tsx",
137
+ ".js",
138
+ ".jsx"
139
+ ];
207
140
 
208
- // src/utils/ast.ts
209
- import path2 from "path";
141
+ //#endregion
142
+ //#region src/utils/ast.ts
143
+ /**
144
+ * Return the import modifier for `sourceFile` to import `referenceFile`
145
+ *
146
+ * @example
147
+ * ```ts
148
+ * toReferencePath('index.ts', 'dir/hello.ts')
149
+ * // should output './dir/hello'
150
+ * ```
151
+ */
210
152
  function toImportSpecifier(sourceFile, referenceFile) {
211
- const extname = path2.extname(referenceFile);
212
- const removeExt = typescriptExtensions.includes(extname);
213
- const importPath = path2.relative(
214
- path2.dirname(sourceFile),
215
- removeExt ? referenceFile.substring(0, referenceFile.length - extname.length) : referenceFile
216
- ).replaceAll(path2.sep, "/");
217
- return importPath.startsWith("../") ? importPath : `./${importPath}`;
153
+ const extname = path.extname(referenceFile);
154
+ const removeExt = typescriptExtensions.includes(extname);
155
+ let importPath = path.relative(path.dirname(sourceFile), removeExt ? referenceFile.substring(0, referenceFile.length - extname.length) : referenceFile).replaceAll(path.sep, "/");
156
+ if (removeExt && importPath.endsWith("/index")) importPath = importPath.slice(0, -6);
157
+ return importPath.startsWith("../") ? importPath : `./${importPath}`;
218
158
  }
219
159
 
220
- // src/registry/installer/index.ts
221
- import { x as x3 } from "tinyexec";
160
+ //#endregion
161
+ //#region src/registry/schema.ts
162
+ const namespaces = [
163
+ "components",
164
+ "lib",
165
+ "css",
166
+ "route",
167
+ "ui",
168
+ "block"
169
+ ];
170
+ const indexSchema = z.object({
171
+ name: z.string(),
172
+ title: z.string().optional(),
173
+ description: z.string().optional()
174
+ });
175
+ const fileSchema = z.object({
176
+ type: z.literal(namespaces),
177
+ path: z.string(),
178
+ target: z.string().optional(),
179
+ content: z.string()
180
+ });
181
+ const httpSubComponent = z.object({
182
+ type: z.literal("http"),
183
+ baseUrl: z.string(),
184
+ component: z.string()
185
+ });
186
+ const componentSchema = z.object({
187
+ name: z.string(),
188
+ title: z.string().optional(),
189
+ description: z.string().optional(),
190
+ files: z.array(fileSchema),
191
+ dependencies: z.record(z.string(), z.string().or(z.null())),
192
+ devDependencies: z.record(z.string(), z.string().or(z.null())),
193
+ subComponents: z.array(z.string().or(httpSubComponent)).default([])
194
+ });
195
+ const registryInfoSchema = z.object({
196
+ variables: z.record(z.string(), z.object({
197
+ description: z.string().optional(),
198
+ default: z.unknown().optional()
199
+ })).optional(),
200
+ env: z.record(z.string(), z.unknown()).optional(),
201
+ indexes: z.array(indexSchema).default([]),
202
+ registries: z.array(z.string()).optional()
203
+ });
204
+
205
+ //#endregion
206
+ //#region src/utils/cache.ts
207
+ var AsyncCache = class {
208
+ constructor() {
209
+ this.store = /* @__PURE__ */ new Map();
210
+ }
211
+ cached(key, fn) {
212
+ let cached = this.store.get(key);
213
+ if (cached !== void 0) return cached;
214
+ cached = fn();
215
+ this.store.set(key, cached);
216
+ return cached;
217
+ }
218
+ };
222
219
 
223
- // src/registry/installer/dep-manager.ts
224
- import fs3 from "fs/promises";
220
+ //#endregion
221
+ //#region src/registry/client.ts
222
+ const fetchCache = new AsyncCache();
223
+ var HttpRegistryClient = class HttpRegistryClient {
224
+ constructor(baseUrl, config) {
225
+ this.baseUrl = baseUrl;
226
+ this.config = config;
227
+ this.registryId = baseUrl;
228
+ }
229
+ async fetchRegistryInfo(baseUrl = this.baseUrl) {
230
+ const url = new URL("_registry.json", `${baseUrl}/`);
231
+ return fetchCache.cached(url.href, async () => {
232
+ const res = await fetch(url);
233
+ if (!res.ok) throw new Error(`failed to fetch ${url.href}: ${res.statusText}`);
234
+ return registryInfoSchema.parse(await res.json());
235
+ });
236
+ }
237
+ async fetchComponent(name) {
238
+ const url = new URL(`${name}.json`, `${this.baseUrl}/`);
239
+ return fetchCache.cached(url.href, async () => {
240
+ const res = await fetch(`${this.baseUrl}/${name}.json`);
241
+ if (!res.ok) {
242
+ log.error(`component ${name} not found at ${url.href}`);
243
+ throw new Error(await res.text());
244
+ }
245
+ return componentSchema.parse(await res.json());
246
+ });
247
+ }
248
+ async hasComponent(name) {
249
+ const url = new URL(`${name}.json`, `${this.baseUrl}/`);
250
+ return (await fetch(url, { method: "HEAD" })).ok;
251
+ }
252
+ createLinkedRegistryClient(name) {
253
+ return new HttpRegistryClient(`${this.baseUrl}/${name}`, this.config);
254
+ }
255
+ };
256
+ var LocalRegistryClient = class LocalRegistryClient {
257
+ constructor(dir, config) {
258
+ this.dir = dir;
259
+ this.config = config;
260
+ this.registryId = dir;
261
+ }
262
+ async fetchRegistryInfo(dir = this.dir) {
263
+ if (this.registryInfo) return this.registryInfo;
264
+ const filePath = path.join(dir, "_registry.json");
265
+ const out = await fs.readFile(filePath).then((res) => JSON.parse(res.toString())).catch((e) => {
266
+ throw new Error(`failed to resolve local file "${filePath}"`, { cause: e });
267
+ });
268
+ return this.registryInfo = registryInfoSchema.parse(out);
269
+ }
270
+ async fetchComponent(name) {
271
+ const filePath = path.join(this.dir, `${name}.json`);
272
+ const out = await fs.readFile(filePath).then((res) => JSON.parse(res.toString())).catch((e) => {
273
+ log.error(`component ${name} not found at ${filePath}`);
274
+ throw e;
275
+ });
276
+ return componentSchema.parse(out);
277
+ }
278
+ async hasComponent(name) {
279
+ const filePath = path.join(this.dir, `${name}.json`);
280
+ try {
281
+ await fs.stat(filePath);
282
+ return true;
283
+ } catch {
284
+ return false;
285
+ }
286
+ }
287
+ createLinkedRegistryClient(name) {
288
+ return new LocalRegistryClient(path.join(this.dir, name), this.config);
289
+ }
290
+ };
225
291
 
226
- // src/utils/get-package-manager.ts
227
- import { detect } from "package-manager-detector";
292
+ //#endregion
293
+ //#region src/utils/get-package-manager.ts
228
294
  async function getPackageManager() {
229
- const result = await detect();
230
- return result?.name ?? "npm";
295
+ return (await detect())?.name ?? "npm";
231
296
  }
232
297
 
233
- // src/registry/installer/dep-manager.ts
234
- import { confirm, isCancel, spinner } from "@clack/prompts";
235
- import { x as x2 } from "tinyexec";
298
+ //#endregion
299
+ //#region src/registry/installer/dep-manager.ts
236
300
  var DependencyManager = class {
237
- /**
238
- * Get dependencies from `package.json`
239
- */
240
- async getDeps() {
241
- if (this.cachedInstalledDeps) return this.cachedInstalledDeps;
242
- const dependencies = /* @__PURE__ */ new Map();
243
- if (!await exists("package.json")) return dependencies;
244
- const content = await fs3.readFile("package.json");
245
- const parsed = JSON.parse(content.toString());
246
- if ("dependencies" in parsed && typeof parsed.dependencies === "object") {
247
- const records = parsed.dependencies;
248
- for (const [k, v] of Object.entries(records)) {
249
- dependencies.set(k, v);
250
- }
251
- }
252
- if ("devDependencies" in parsed && typeof parsed.devDependencies === "object") {
253
- const records = parsed.devDependencies;
254
- for (const [k, v] of Object.entries(records)) {
255
- dependencies.set(k, v);
256
- }
257
- }
258
- return this.cachedInstalledDeps = dependencies;
259
- }
260
- async resolveInstallDependencies(deps) {
261
- const cachedInstalledDeps = await this.getDeps();
262
- return Object.entries(deps).filter(([k]) => !cachedInstalledDeps.has(k)).map(([k, v]) => v === null || v.length === 0 ? k : `${k}@${v}`);
263
- }
264
- async installDeps(deps, devDeps) {
265
- const items = await this.resolveInstallDependencies(deps);
266
- const devItems = await this.resolveInstallDependencies(devDeps);
267
- if (items.length === 0 && devItems.length === 0) return;
268
- const manager = await getPackageManager();
269
- const value = await confirm({
270
- message: `Do you want to install with ${manager}?
271
- ${[...items, ...devItems].map((v) => `- ${v}`).join("\n")}`
272
- });
273
- if (isCancel(value) || !value) {
274
- return;
275
- }
276
- const spin = spinner();
277
- spin.start("Installing dependencies...");
278
- if (items.length > 0) await x2(manager, ["install", ...items]);
279
- if (devItems.length > 0) await x2(manager, ["install", ...devItems, "-D"]);
280
- spin.stop("Dependencies installed.");
281
- }
301
+ /**
302
+ * Get dependencies from `package.json`
303
+ */
304
+ async getDeps() {
305
+ if (this.cachedInstalledDeps) return this.cachedInstalledDeps;
306
+ const dependencies = /* @__PURE__ */ new Map();
307
+ if (!await exists("package.json")) return dependencies;
308
+ const content = await fs.readFile("package.json");
309
+ const parsed = JSON.parse(content.toString());
310
+ if ("dependencies" in parsed && typeof parsed.dependencies === "object") {
311
+ const records = parsed.dependencies;
312
+ for (const [k, v] of Object.entries(records)) dependencies.set(k, v);
313
+ }
314
+ if ("devDependencies" in parsed && typeof parsed.devDependencies === "object") {
315
+ const records = parsed.devDependencies;
316
+ for (const [k, v] of Object.entries(records)) dependencies.set(k, v);
317
+ }
318
+ return this.cachedInstalledDeps = dependencies;
319
+ }
320
+ async resolveInstallDependencies(deps) {
321
+ const cachedInstalledDeps = await this.getDeps();
322
+ return Object.entries(deps).filter(([k]) => !cachedInstalledDeps.has(k)).map(([k, v]) => v === null || v.length === 0 ? k : `${k}@${v}`);
323
+ }
324
+ async installDeps(deps, devDeps) {
325
+ const items = await this.resolveInstallDependencies(deps);
326
+ const devItems = await this.resolveInstallDependencies(devDeps);
327
+ if (items.length === 0 && devItems.length === 0) return;
328
+ const manager = await getPackageManager();
329
+ const value = await confirm({ message: `Do you want to install with ${manager}?
330
+ ${[...items, ...devItems].map((v) => `- ${v}`).join("\n")}` });
331
+ if (isCancel(value) || !value) return;
332
+ const spin = spinner();
333
+ spin.start("Installing dependencies...");
334
+ if (items.length > 0) await x(manager, ["install", ...items]);
335
+ if (devItems.length > 0) await x(manager, [
336
+ "install",
337
+ ...devItems,
338
+ "-D"
339
+ ]);
340
+ spin.stop("Dependencies installed.");
341
+ }
282
342
  };
283
343
 
284
- // src/utils/cache.ts
285
- var AsyncCache = class {
286
- constructor() {
287
- this.store = /* @__PURE__ */ new Map();
288
- }
289
- cached(key, fn) {
290
- let cached = this.store.get(key);
291
- if (cached !== void 0) return cached;
292
- cached = fn();
293
- this.store.set(key, cached);
294
- return cached;
295
- }
296
- };
297
-
298
- // src/registry/installer/index.ts
344
+ //#endregion
345
+ //#region src/registry/installer/index.ts
299
346
  var ComponentInstaller = class {
300
- constructor(client) {
301
- this.project = createEmptyProject();
302
- this.installedFiles = /* @__PURE__ */ new Set();
303
- this.downloadCache = new AsyncCache();
304
- this.dependencies = {};
305
- this.devDependencies = {};
306
- this.client = client;
307
- }
308
- async install(name) {
309
- const downloaded = await this.download(name);
310
- for (const item of downloaded) {
311
- Object.assign(this.dependencies, item.dependencies);
312
- Object.assign(this.devDependencies, item.devDependencies);
313
- }
314
- const fileList = this.buildFileList(downloaded);
315
- for (const file of fileList) {
316
- const filePath = file.target ?? file.path;
317
- if (this.installedFiles.has(filePath)) continue;
318
- const outPath = this.resolveOutputPath(file);
319
- const output = typescriptExtensions.includes(path3.extname(filePath)) ? await this.transform(outPath, file, fileList) : file.content;
320
- const status = await fs4.readFile(outPath).then((res) => {
321
- if (res.toString() === output) return "ignore";
322
- return "need-update";
323
- }).catch(() => "write");
324
- this.installedFiles.add(filePath);
325
- if (status === "ignore") continue;
326
- if (status === "need-update") {
327
- const override = await confirm2({
328
- message: `Do you want to override ${outPath}?`,
329
- initialValue: false
330
- });
331
- if (isCancel2(override)) {
332
- outro("Ended");
333
- process.exit(0);
334
- }
335
- if (!override) continue;
336
- }
337
- await fs4.mkdir(path3.dirname(outPath), { recursive: true });
338
- await fs4.writeFile(outPath, output);
339
- log.step(`downloaded ${outPath}`);
340
- }
341
- }
342
- async installDeps() {
343
- await new DependencyManager().installDeps(
344
- this.dependencies,
345
- this.devDependencies
346
- );
347
- }
348
- async onEnd() {
349
- const config = this.client.config;
350
- if (config.commands.format) {
351
- await x3(config.commands.format);
352
- }
353
- }
354
- buildFileList(downloaded) {
355
- const map = /* @__PURE__ */ new Map();
356
- for (const item of downloaded) {
357
- for (const file of item.files) {
358
- const filePath = file.target ?? file.path;
359
- if (map.has(filePath)) {
360
- console.warn(
361
- `noticed duplicated output file for ${filePath}, ignoring for now.`
362
- );
363
- continue;
364
- }
365
- map.set(filePath, file);
366
- }
367
- }
368
- return Array.from(map.values());
369
- }
370
- /**
371
- * return a list of components, merged with child components.
372
- */
373
- download(name) {
374
- return this.downloadCache.cached(name, async () => {
375
- const comp = await this.client.fetchComponent(name);
376
- const result = [comp];
377
- this.downloadCache.store.set(name, result);
378
- const child = await Promise.all(
379
- comp.subComponents.map((sub) => this.download(sub))
380
- );
381
- for (const sub of child) result.push(...sub);
382
- return result;
383
- });
384
- }
385
- async transform(filePath, file, fileList) {
386
- const sourceFile = this.project.createSourceFile(filePath, file.content, {
387
- overwrite: true
388
- });
389
- const prefix = "@/";
390
- for (const specifier of sourceFile.getImportStringLiterals()) {
391
- if (specifier.getLiteralValue().startsWith(prefix)) {
392
- const lookup = specifier.getLiteralValue().substring(prefix.length);
393
- const target = fileList.find((item) => {
394
- const filePath2 = item.target ?? item.path;
395
- return filePath2 === lookup;
396
- });
397
- if (target) {
398
- specifier.setLiteralValue(
399
- toImportSpecifier(filePath, this.resolveOutputPath(target))
400
- );
401
- } else {
402
- console.warn(`cannot find the referenced file of ${specifier}`);
403
- }
404
- }
405
- }
406
- for (const statement of sourceFile.getImportDeclarations()) {
407
- const info = await this.client.fetchRegistryInfo();
408
- const specifier = statement.getModuleSpecifier().getLiteralValue();
409
- if (info.switchables && specifier in info.switchables) {
410
- const switchable = info.switchables[specifier];
411
- statement.setModuleSpecifier(switchable.specifier);
412
- for (const member of statement.getNamedImports()) {
413
- const name = member.getName();
414
- if (name in switchable.members) {
415
- member.setName(switchable.members[name]);
416
- }
417
- }
418
- }
419
- }
420
- return sourceFile.getFullText();
421
- }
422
- resolveOutputPath(ref) {
423
- const config = this.client.config;
424
- if (ref.target) {
425
- return path3.join(config.baseDir, ref.target);
426
- }
427
- const base = path3.basename(ref.path);
428
- const dir = {
429
- components: config.aliases.componentsDir,
430
- block: config.aliases.blockDir,
431
- ui: config.aliases.uiDir,
432
- css: config.aliases.cssDir,
433
- lib: config.aliases.libDir,
434
- route: "./"
435
- }[ref.type];
436
- return path3.join(config.baseDir, dir, base);
437
- }
347
+ constructor(rootClient, plugins = []) {
348
+ this.rootClient = rootClient;
349
+ this.plugins = plugins;
350
+ this.project = createEmptyProject();
351
+ this.installedFiles = /* @__PURE__ */ new Set();
352
+ this.downloadCache = new AsyncCache();
353
+ this.dependencies = {};
354
+ this.devDependencies = {};
355
+ this.pathToFileCache = new AsyncCache();
356
+ }
357
+ async install(name) {
358
+ let downloaded;
359
+ const info = await this.rootClient.fetchRegistryInfo();
360
+ for (const registry of info.registries ?? []) if (name.startsWith(`${registry}/`)) {
361
+ downloaded = await this.download(name.slice(registry.length + 1), this.rootClient.createLinkedRegistryClient(registry));
362
+ break;
363
+ }
364
+ downloaded ??= await this.download(name, this.rootClient);
365
+ for (const item of downloaded) {
366
+ Object.assign(this.dependencies, item.dependencies);
367
+ Object.assign(this.devDependencies, item.devDependencies);
368
+ }
369
+ for (const comp of downloaded) for (const file of comp.files) {
370
+ const outPath = this.resolveOutputPath(file);
371
+ if (this.installedFiles.has(outPath)) continue;
372
+ this.installedFiles.add(outPath);
373
+ const output = typescriptExtensions.includes(path.extname(outPath)) ? await this.transform(name, file, comp, downloaded) : file.content;
374
+ const status = await fs.readFile(outPath).then((res) => {
375
+ if (res.toString() === output) return "ignore";
376
+ return "need-update";
377
+ }).catch(() => "write");
378
+ if (status === "ignore") continue;
379
+ if (status === "need-update") {
380
+ const override = await confirm({
381
+ message: `Do you want to override ${outPath}?`,
382
+ initialValue: false
383
+ });
384
+ if (isCancel(override)) {
385
+ outro("Ended");
386
+ process.exit(0);
387
+ }
388
+ if (!override) continue;
389
+ }
390
+ await fs.mkdir(path.dirname(outPath), { recursive: true });
391
+ await fs.writeFile(outPath, output);
392
+ log.step(`downloaded ${outPath}`);
393
+ }
394
+ }
395
+ async installDeps() {
396
+ await new DependencyManager().installDeps(this.dependencies, this.devDependencies);
397
+ }
398
+ async onEnd() {
399
+ const config = this.rootClient.config;
400
+ if (config.commands.format) await x(config.commands.format);
401
+ }
402
+ /**
403
+ * return a list of components, merged with child components & variables.
404
+ */
405
+ async download(name, client, contextVariables) {
406
+ const hash = `${client.registryId} ${name}`;
407
+ const info = await client.fetchRegistryInfo();
408
+ const variables = {
409
+ ...contextVariables,
410
+ ...info.env
411
+ };
412
+ for (const [k, v] of Object.entries(info.variables ?? {})) variables[k] ??= v.default;
413
+ return (await this.downloadCache.cached(hash, async () => {
414
+ const comp = await client.fetchComponent(name);
415
+ const result = [comp];
416
+ this.downloadCache.store.set(hash, result);
417
+ const child = await Promise.all(comp.subComponents.map((sub) => {
418
+ if (typeof sub === "string") return this.download(sub, client);
419
+ const baseUrl = this.rootClient instanceof HttpRegistryClient ? new URL(sub.baseUrl, `${this.rootClient.baseUrl}/`).href : sub.baseUrl;
420
+ return this.download(sub.component, new HttpRegistryClient(baseUrl, client.config), variables);
421
+ }));
422
+ for (const sub of child) result.push(...sub);
423
+ return result;
424
+ })).map((file) => ({
425
+ ...file,
426
+ variables
427
+ }));
428
+ }
429
+ async transform(taskId, file, component, allComponents) {
430
+ const filePath = this.resolveOutputPath(file);
431
+ const sourceFile = this.project.createSourceFile(filePath, file.content, { overwrite: true });
432
+ const prefix = "@/";
433
+ const variables = Object.entries(component.variables ?? {});
434
+ const pathToFile = await this.pathToFileCache.cached(taskId, () => {
435
+ const map = /* @__PURE__ */ new Map();
436
+ for (const comp of allComponents) for (const file$1 of comp.files) map.set(file$1.target ?? file$1.path, file$1);
437
+ return map;
438
+ });
439
+ for (const specifier of sourceFile.getImportStringLiterals()) {
440
+ for (const [k, v] of variables) specifier.setLiteralValue(specifier.getLiteralValue().replaceAll(`<${k}>`, v));
441
+ if (specifier.getLiteralValue().startsWith(prefix)) {
442
+ const lookup = specifier.getLiteralValue().substring(2);
443
+ const target = pathToFile.get(lookup);
444
+ if (target) specifier.setLiteralValue(toImportSpecifier(filePath, this.resolveOutputPath(target)));
445
+ else console.warn(`cannot find the referenced file of ${specifier}`);
446
+ }
447
+ }
448
+ for (const plugin of this.plugins) await plugin.transform?.({
449
+ file: sourceFile,
450
+ componentFile: file,
451
+ component
452
+ });
453
+ return sourceFile.getFullText();
454
+ }
455
+ resolveOutputPath(file) {
456
+ const config = this.rootClient.config;
457
+ const dir = {
458
+ components: config.aliases.componentsDir,
459
+ block: config.aliases.blockDir,
460
+ ui: config.aliases.uiDir,
461
+ css: config.aliases.cssDir,
462
+ lib: config.aliases.libDir,
463
+ route: "./"
464
+ }[file.type];
465
+ if (file.target) return path.join(config.baseDir, file.target.replace("<dir>", dir));
466
+ return path.join(config.baseDir, dir, path.basename(file.path));
467
+ }
438
468
  };
439
469
 
440
- // src/registry/schema.ts
441
- import { z as z2 } from "zod";
442
- var namespaces = [
443
- "components",
444
- "lib",
445
- "css",
446
- "route",
447
- "ui",
448
- "block"
449
- ];
450
- var indexSchema = z2.object({
451
- name: z2.string(),
452
- title: z2.string().optional(),
453
- description: z2.string().optional()
454
- });
455
- var fileSchema = z2.object({
456
- type: z2.literal(namespaces),
457
- path: z2.string(),
458
- target: z2.string().optional(),
459
- content: z2.string()
460
- });
461
- var componentSchema = z2.object({
462
- name: z2.string(),
463
- title: z2.string().optional(),
464
- description: z2.string().optional(),
465
- files: z2.array(fileSchema),
466
- dependencies: z2.record(z2.string(), z2.string().or(z2.null())),
467
- devDependencies: z2.record(z2.string(), z2.string().or(z2.null())),
468
- subComponents: z2.array(z2.string()).default([])
469
- });
470
- var switchableEntitySchema = z2.object({
471
- /**
472
- * map specifier string
473
- */
474
- specifier: z2.string(),
475
- /**
476
- * map names of exported members
477
- */
478
- members: z2.record(z2.string(), z2.string())
479
- });
480
- var registryInfoSchema = z2.object({
481
- switchables: z2.record(z2.string(), switchableEntitySchema).optional(),
482
- indexes: z2.array(indexSchema)
483
- });
484
-
485
- // src/registry/client.ts
486
- import path4 from "path";
487
- import fs5 from "fs/promises";
488
- import { log as log2 } from "@clack/prompts";
489
- function remoteResolver(url) {
490
- return async (file) => {
491
- const res = await fetch(`${url}/${file}`);
492
- if (!res.ok) {
493
- throw new Error(`failed to fetch ${url}/${file}: ${res.statusText}`);
494
- }
495
- return await res.json();
496
- };
497
- }
498
- function localResolver(dir) {
499
- return async (file) => {
500
- const filePath = path4.join(dir, file);
501
- return await fs5.readFile(filePath).then((res) => JSON.parse(res.toString())).catch((e) => {
502
- throw new Error(`failed to resolve local file "${filePath}"`, {
503
- cause: e
504
- });
505
- });
506
- };
507
- }
508
- var RegistryClient = class {
509
- constructor(config, resolver) {
510
- this.config = config;
511
- this.resolver = resolver;
512
- }
513
- async fetchRegistryInfo() {
514
- this.registryInfo ??= registryInfoSchema.parse(
515
- await this.resolver("_registry.json").catch((e) => {
516
- log2.error(String(e));
517
- process.exit(1);
518
- })
519
- );
520
- return this.registryInfo;
521
- }
522
- async fetchComponent(name) {
523
- return componentSchema.parse(
524
- await this.resolver(`${name}.json`).catch((e) => {
525
- log2.error(`component ${name} not found:`);
526
- log2.error(String(e));
527
- process.exit(1);
528
- })
529
- );
530
- }
470
+ //#endregion
471
+ //#region src/commands/shared.ts
472
+ const UIRegistries = {
473
+ "base-ui": "hanzo-docs/base-ui",
474
+ "radix-ui": "hanzo-docs/radix-ui"
531
475
  };
532
476
 
533
- // src/commands/add.ts
534
- async function add(input, resolver, config) {
535
- const client = new RegistryClient(config, resolver);
536
- const installer = new ComponentInstaller(client);
537
- let target = input;
538
- if (input.length === 0) {
539
- const spin = spinner2();
540
- spin.start("fetching registry");
541
- const info = await client.fetchRegistryInfo();
542
- spin.stop(picocolors.bold(picocolors.greenBright("registry fetched")));
543
- const value = await multiselect({
544
- message: "Select components to install",
545
- options: info.indexes.map((item) => ({
546
- label: item.title,
547
- value: item.name,
548
- hint: item.description
549
- }))
550
- });
551
- if (isCancel3(value)) {
552
- outro2("Ended");
553
- return;
554
- }
555
- target = value;
556
- }
557
- await install(target, installer);
477
+ //#endregion
478
+ //#region src/commands/add.ts
479
+ async function add(input, client) {
480
+ const config = client.config;
481
+ let target;
482
+ const installer = new ComponentInstaller(client);
483
+ const registry = UIRegistries[config.uiLibrary];
484
+ if (input.length === 0) {
485
+ const spin = spinner();
486
+ spin.start("fetching registry");
487
+ const info = await client.fetchRegistryInfo();
488
+ const options = [];
489
+ for (const item of info.indexes) options.push({
490
+ label: item.title ?? item.name,
491
+ value: item.name,
492
+ hint: item.description
493
+ });
494
+ const { indexes } = await client.createLinkedRegistryClient(registry).fetchRegistryInfo();
495
+ for (const item of indexes) options.push({
496
+ label: item.title ?? item.name,
497
+ value: `${registry}/${item.name}`,
498
+ hint: item.description
499
+ });
500
+ spin.stop(picocolors.bold(picocolors.greenBright("registry fetched")));
501
+ const value = await multiselect({
502
+ message: "Select components to install",
503
+ options
504
+ });
505
+ if (isCancel(value)) {
506
+ outro("Ended");
507
+ return;
508
+ }
509
+ target = value;
510
+ } else target = await Promise.all(input.map(async (item) => await client.hasComponent(item) ? item : `${registry}/${item}`));
511
+ await install(target, installer);
558
512
  }
559
513
  async function install(target, installer) {
560
- for (const name of target) {
561
- intro(
562
- picocolors.bold(
563
- picocolors.inverse(picocolors.cyanBright(`Add Component: ${name}`))
564
- )
565
- );
566
- try {
567
- await installer.install(name);
568
- outro2(picocolors.bold(picocolors.greenBright(`${name} installed`)));
569
- } catch (e) {
570
- log3.error(String(e));
571
- throw e;
572
- }
573
- }
574
- intro(picocolors.bold("New Dependencies"));
575
- await installer.installDeps();
576
- await installer.onEnd();
577
- outro2(picocolors.bold(picocolors.greenBright("Successful")));
514
+ for (const name of target) {
515
+ intro(picocolors.bold(picocolors.inverse(picocolors.cyanBright(`Add Component: ${name}`))));
516
+ try {
517
+ await installer.install(name);
518
+ outro(picocolors.bold(picocolors.greenBright(`${name} installed`)));
519
+ } catch (e) {
520
+ log.error(String(e));
521
+ throw e;
522
+ }
523
+ }
524
+ intro(picocolors.bold("New Dependencies"));
525
+ await installer.installDeps();
526
+ await installer.onEnd();
527
+ outro(picocolors.bold(picocolors.greenBright("Successful")));
578
528
  }
579
529
 
580
- // src/commands/customise.ts
581
- async function customise(resolver, config) {
582
- const client = new RegistryClient(config, resolver);
583
- intro2(picocolors2.bgBlack(picocolors2.whiteBright("Customise Hanzo Docs UI")));
584
- const installer = new ComponentInstaller(client);
585
- const result = await group(
586
- {
587
- target: () => select({
588
- message: "What do you want to customise?",
589
- options: [
590
- {
591
- label: "Docs Layout",
592
- value: "docs",
593
- hint: "main UI of your docs"
594
- },
595
- {
596
- label: "Home Layout",
597
- value: "home",
598
- hint: "the navbar for your other pages"
599
- }
600
- ]
601
- }),
602
- mode: (v) => {
603
- if (v.results.target !== "docs") return;
604
- return select({
605
- message: "Which variant do you want to start from?",
606
- options: [
607
- {
608
- label: "Start from minimal styles",
609
- value: "minimal",
610
- hint: "for those who want to build their own variant from ground up."
611
- },
612
- {
613
- label: "Start from default layout",
614
- value: "full-default",
615
- hint: "useful for adjusting small details."
616
- },
617
- {
618
- label: "Start from Notebook layout",
619
- value: "full-notebook",
620
- hint: "useful for adjusting small details."
621
- }
622
- ]
623
- });
624
- }
625
- },
626
- {
627
- onCancel: () => {
628
- cancel("Installation Stopped.");
629
- process.exit(0);
630
- }
631
- }
632
- );
633
- if (result.target === "docs") {
634
- const targets = [];
635
- if (result.mode === "minimal") {
636
- targets.push("layouts/docs-min");
637
- } else {
638
- targets.push(
639
- result.mode === "full-default" ? "layouts/docs" : "layouts/notebook"
640
- );
641
- }
642
- await install(targets, installer);
643
- const maps = result.mode === "full-notebook" ? [
644
- ["@hanzo/docs-ui/layouts/notebook", "@/components/layout/notebook"],
645
- [
646
- "@hanzo/docs-ui/layouts/notebook/page",
647
- "@/components/layout/notebook/page"
648
- ]
649
- ] : [
650
- ["@hanzo/docs-ui/layouts/docs", "@/components/layout/docs"],
651
- ["@hanzo/docs-ui/layouts/docs/page", "@/components/layout/docs/page"]
652
- ];
653
- printNext(...maps);
654
- }
655
- if (result.target === "home") {
656
- await install(["layouts/home"], installer);
657
- printNext(["@hanzo/docs-ui/layouts/home", `@/components/layout/home`]);
658
- }
659
- outro3(picocolors2.bold("Have fun!"));
530
+ //#endregion
531
+ //#region src/commands/customise.ts
532
+ async function customise(client) {
533
+ intro(picocolors.bgBlack(picocolors.whiteBright("Customise Fumadocs UI")));
534
+ const config = client.config;
535
+ const installer = new ComponentInstaller(client);
536
+ const result = await group({
537
+ target: () => select({
538
+ message: "What do you want to customise?",
539
+ options: [{
540
+ label: "Docs Layout",
541
+ value: "docs",
542
+ hint: "main UI of your docs"
543
+ }, {
544
+ label: "Home Layout",
545
+ value: "home",
546
+ hint: "the navbar for your other pages"
547
+ }]
548
+ }),
549
+ mode: (v) => {
550
+ if (v.results.target !== "docs") return;
551
+ return select({
552
+ message: "Which variant do you want to start from?",
553
+ options: [
554
+ {
555
+ label: "Start from minimal styles",
556
+ value: "minimal",
557
+ hint: "for those who want to build their own variant from ground up."
558
+ },
559
+ {
560
+ label: "Start from default layout",
561
+ value: "full-default",
562
+ hint: "useful for adjusting small details."
563
+ },
564
+ {
565
+ label: "Start from Notebook layout",
566
+ value: "full-notebook",
567
+ hint: "useful for adjusting small details."
568
+ }
569
+ ]
570
+ });
571
+ }
572
+ }, { onCancel: () => {
573
+ cancel("Installation Stopped.");
574
+ process.exit(0);
575
+ } });
576
+ const registry = UIRegistries[config.uiLibrary];
577
+ if (result.target === "docs") {
578
+ const targets = [];
579
+ if (result.mode === "minimal") targets.push("hanzo-docs/ui/layouts/docs-min");
580
+ else targets.push(result.mode === "full-default" ? `${registry}/layouts/docs` : `${registry}/layouts/notebook`);
581
+ await install(targets, installer);
582
+ printNext(...result.mode === "full-notebook" ? [["@hanzo/docs-ui/layouts/notebook", "@/components/layout/notebook"], ["@hanzo/docs-ui/layouts/notebook/page", "@/components/layout/notebook/page"]] : [["@hanzo/docs-ui/layouts/docs", "@/components/layout/docs"], ["@hanzo/docs-ui/layouts/docs/page", "@/components/layout/docs/page"]]);
583
+ }
584
+ if (result.target === "home") {
585
+ await install([`${registry}/layouts/home`], installer);
586
+ printNext(["@hanzo/docs-ui/layouts/home", `@/components/layout/home`]);
587
+ }
588
+ outro(picocolors.bold("Have fun!"));
660
589
  }
661
590
  function printNext(...maps) {
662
- intro2(picocolors2.bold("What is Next?"));
663
- log4.info(
664
- [
665
- "You can check the installed components in `components`.",
666
- picocolors2.dim("---"),
667
- "Open your `layout.tsx` files, replace the imports of components:",
668
- ...maps.map(
669
- ([from, to]) => picocolors2.greenBright(`"${from}" -> "${to}"`)
670
- )
671
- ].join("\n")
672
- );
591
+ intro(picocolors.bold("What is Next?"));
592
+ log.info([
593
+ "You can check the installed components in `components`.",
594
+ picocolors.dim("---"),
595
+ "Open your `layout.tsx` files, replace the imports of components:",
596
+ ...maps.map(([from, to]) => picocolors.greenBright(`"${from}" -> "${to}"`))
597
+ ].join("\n"));
673
598
  }
674
599
 
675
- // src/index.ts
676
- var program = new Command().option("--config <string>");
677
- program.name("hanzo-docs").description("CLI to setup Hanzo Docs, init a config").version(package_default.version).action(async () => {
678
- if (await initConfig()) {
679
- console.log(picocolors3.green("Initialized a `./cli.json` config file."));
680
- } else {
681
- console.log(picocolors3.redBright("A config file already exists."));
682
- }
600
+ //#endregion
601
+ //#region src/index.ts
602
+ const program = new Command().option("--config <string>");
603
+ program.name("hanzo-docs").description("CLI to setup Hanzo Docs, init a config").version(version).action(async () => {
604
+ if (await initConfig()) console.log(picocolors.green("Initialized a `./cli.json` config file."));
605
+ else console.log(picocolors.redBright("A config file already exists."));
683
606
  });
684
607
  program.command("customise").alias("customize").description("simple way to customise layouts with Hanzo Docs UI").option("--dir <string>", "the root url or directory to resolve registry").action(async (options) => {
685
- const resolver = getResolverFromDir(options.dir);
686
- await customise(resolver, await createOrLoadConfig(options.config));
608
+ await customise(createClientFromDir(options.dir, await createOrLoadConfig(options.config)));
687
609
  });
688
- var dirShortcuts = {
689
- ":dev": "https://preview.hanzo.ai/registry",
690
- ":localhost": "http://localhost:3000/registry"
610
+ const dirShortcuts = {
611
+ ":preview": "https://preview.hanzo.ai/docs/registry",
612
+ ":dev": "http://localhost:3000/registry"
691
613
  };
692
- 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(
693
- async (input, options) => {
694
- const resolver = getResolverFromDir(options.dir);
695
- await add(input, resolver, await createOrLoadConfig(options.config));
696
- }
697
- );
698
- program.command("tree").argument(
699
- "[json_or_args]",
700
- "JSON output of `tree` command or arguments for the `tree` command"
701
- ).argument("[output]", "output path of file").option("--js", "output as JavaScript file").option("--no-root", "remove the root node").option("--import-name <name>", "where to import components (JS only)").action(
702
- async (str, output, {
703
- js,
704
- root,
705
- importName
706
- }) => {
707
- const jsExtensions = [".js", ".tsx", ".jsx"];
708
- const noRoot = !root;
709
- let nodes;
710
- try {
711
- nodes = JSON.parse(str ?? "");
712
- } catch {
713
- nodes = await runTree(str ?? "./");
714
- }
715
- const out = js || output && jsExtensions.includes(path5.extname(output)) ? treeToJavaScript(nodes, noRoot, importName) : treeToMdx(nodes, noRoot);
716
- if (output) {
717
- await fs6.mkdir(path5.dirname(output), { recursive: true });
718
- await fs6.writeFile(output, out);
719
- } else {
720
- console.log(out);
721
- }
722
- }
723
- );
724
- function getResolverFromDir(dir = "https://hanzo.ai/registry") {
725
- if (dir in dirShortcuts) dir = dirShortcuts[dir];
726
- return dir.startsWith("http://") || dir.startsWith("https://") ? remoteResolver(dir) : localResolver(dir);
614
+ 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(async (input, options) => {
615
+ await add(input, createClientFromDir(options.dir, await createOrLoadConfig(options.config)));
616
+ });
617
+ program.command("tree").argument("[json_or_args]", "JSON output of `tree` command or arguments for the `tree` command").argument("[output]", "output path of file").option("--js", "output as JavaScript file").option("--no-root", "remove the root node").option("--import-name <name>", "where to import components (JS only)").action(async (str, output, { js, root, importName }) => {
618
+ const jsExtensions = [
619
+ ".js",
620
+ ".tsx",
621
+ ".jsx"
622
+ ];
623
+ const noRoot = !root;
624
+ let nodes;
625
+ try {
626
+ nodes = JSON.parse(str ?? "");
627
+ } catch {
628
+ nodes = await runTree(str ?? "./");
629
+ }
630
+ const out = js || output && jsExtensions.includes(path.extname(output)) ? treeToJavaScript(nodes, noRoot, importName) : treeToMdx(nodes, noRoot);
631
+ if (output) {
632
+ await fs.mkdir(path.dirname(output), { recursive: true });
633
+ await fs.writeFile(output, out);
634
+ } else console.log(out);
635
+ });
636
+ function createClientFromDir(dir = "https://hanzo.ai/docs/registry", config) {
637
+ if (dir in dirShortcuts) dir = dirShortcuts[dir];
638
+ return dir.startsWith("http://") || dir.startsWith("https://") ? new HttpRegistryClient(dir, config) : new LocalRegistryClient(dir, config);
727
639
  }
728
640
  program.parse();
641
+
642
+ //#endregion
643
+ export { };
644
+ //# sourceMappingURL=index.js.map