@fumadocs/cli 0.1.1 → 0.2.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.
@@ -1,5 +1,17 @@
1
1
  import * as ts_morph from 'ts-morph';
2
2
 
3
+ type ImportPathMap = Record<string, string | {
4
+ type: 'component';
5
+ name: string;
6
+ file: string;
7
+ /**
8
+ * Registry of the component, refer to the current registry if not specified
9
+ */
10
+ registry?: string;
11
+ } | {
12
+ type: 'dependency';
13
+ name: string;
14
+ }>;
3
15
  interface Component {
4
16
  name: string;
5
17
  description?: string;
@@ -12,17 +24,9 @@ interface Component {
12
24
  */
13
25
  unlisted?: boolean;
14
26
  /**
15
- * Map imported file paths
27
+ * Map imported file paths, extended from registry `mapImportPath` if defined.
16
28
  */
17
- mapImportPath?: Record<string, string | {
18
- type: 'component';
19
- name: string;
20
- file: string;
21
- /**
22
- * Registry of the component, refer to the current registry if not specified
23
- */
24
- registry?: string;
25
- }>;
29
+ mapImportPath?: ImportPathMap;
26
30
  }
27
31
  type NamespaceType = 'components' | 'hooks' | 'lib';
28
32
  interface PackageJson {
@@ -52,6 +56,10 @@ interface Registry {
52
56
  tsconfigPath?: string;
53
57
  packageJson?: string | PackageJson;
54
58
  components: Component[];
59
+ /**
60
+ * Map import paths of components
61
+ */
62
+ mapImportPath?: ImportPathMap;
55
63
  dependencies?: Record<string, {
56
64
  type: 'runtime' | 'dev';
57
65
  version?: string;
@@ -122,4 +130,4 @@ declare function writeOutput(dir: string, out: Output, options?: {
122
130
  log?: boolean;
123
131
  }): Promise<void>;
124
132
 
125
- export { type Component, type ComponentBuilder, type DependencyInfo, type NamespaceType, type Output, type OutputComponent, type OutputFile, type OutputIndex, type PackageJson, type Registry, build, createComponentBuilder, writeOutput };
133
+ export { type Component, type ComponentBuilder, type DependencyInfo, type ImportPathMap, type NamespaceType, type Output, type OutputComponent, type OutputFile, type OutputIndex, type PackageJson, type Registry, build, createComponentBuilder, writeOutput };
@@ -1,34 +1,49 @@
1
1
  import {
2
2
  exists
3
- } from "../chunk-WBCEM7RC.js";
3
+ } from "../chunk-DG6SFM2G.js";
4
4
 
5
5
  // src/build/index.ts
6
- import * as fs3 from "node:fs/promises";
7
- import * as path4 from "node:path";
6
+ import * as fs3 from "fs/promises";
7
+ import * as path4 from "path";
8
8
  import picocolors from "picocolors";
9
9
 
10
10
  // src/build/build-registry.ts
11
- import * as fs2 from "node:fs/promises";
12
- import * as path3 from "node:path";
11
+ import * as fs2 from "fs/promises";
12
+ import * as path3 from "path";
13
13
 
14
14
  // src/build/build-file.ts
15
- import * as path from "node:path";
15
+ import * as path from "path";
16
+ import { ts } from "ts-morph";
16
17
  async function buildFile(inputPath, outputPath, builder, comp, onReference) {
17
18
  const out = {
18
19
  imports: {},
19
20
  content: "",
20
21
  path: outputPath
21
22
  };
22
- async function process2(specifier, getSpecifiedFile) {
23
+ const importMap = {
24
+ ...builder.registry.mapImportPath,
25
+ ...comp.mapImportPath
26
+ };
27
+ function process2(specifier, getSpecifiedFile) {
23
28
  let specifiedFile = getSpecifiedFile();
24
29
  if (!specifiedFile) return;
25
30
  const name = specifiedFile.isInNodeModules() ? builder.resolveDep(specifier.getLiteralValue()).name : path.relative(builder.registryDir, specifiedFile.getFilePath());
26
- if (comp.mapImportPath && name in comp.mapImportPath) {
27
- const resolver = comp.mapImportPath[name];
31
+ if (name in importMap) {
32
+ const resolver = importMap[name];
28
33
  if (typeof resolver === "string") {
29
34
  specifier.setLiteralValue(resolver);
30
35
  specifiedFile = getSpecifiedFile();
31
36
  if (!specifiedFile) return;
37
+ } else if (resolver.type === "dependency") {
38
+ const info = builder.resolveDep(resolver.name);
39
+ const value2 = onReference({
40
+ type: "dependency",
41
+ name: info.name,
42
+ version: info.version ?? "",
43
+ isDev: info.type === "dev"
44
+ });
45
+ if (value2) out.imports[specifier.getLiteralValue()] = value2;
46
+ return;
32
47
  } else {
33
48
  const sub2 = builder.getComponentByName(
34
49
  resolver.name,
@@ -80,7 +95,7 @@ async function buildFile(inputPath, outputPath, builder, comp, onReference) {
80
95
  }
81
96
  const sourceFile = await builder.createSourceFile(inputPath);
82
97
  for (const item of sourceFile.getImportDeclarations()) {
83
- await process2(
98
+ process2(
84
99
  item.getModuleSpecifier(),
85
100
  () => item.getModuleSpecifierSourceFile()
86
101
  );
@@ -88,14 +103,25 @@ async function buildFile(inputPath, outputPath, builder, comp, onReference) {
88
103
  for (const item of sourceFile.getExportDeclarations()) {
89
104
  const specifier = item.getModuleSpecifier();
90
105
  if (!specifier) continue;
91
- await process2(specifier, () => item.getModuleSpecifierSourceFile());
106
+ process2(specifier, () => item.getModuleSpecifierSourceFile());
107
+ }
108
+ const calls = sourceFile.getDescendantsOfKind(ts.SyntaxKind.CallExpression);
109
+ for (const expression of calls) {
110
+ if (expression.getExpression().isKind(ts.SyntaxKind.ImportKeyword) && expression.getArguments().length === 1) {
111
+ const argument = expression.getArguments()[0];
112
+ if (!argument.isKind(ts.SyntaxKind.StringLiteral)) continue;
113
+ process2(
114
+ argument,
115
+ () => argument.getSymbol()?.getDeclarations()[0].getSourceFile()
116
+ );
117
+ }
92
118
  }
93
119
  out.content = sourceFile.getFullText();
94
120
  return out;
95
121
  }
96
122
 
97
123
  // src/build/component-builder.ts
98
- import path2 from "node:path";
124
+ import path2 from "path";
99
125
  import { Project } from "ts-morph";
100
126
  import * as fs from "fs/promises";
101
127
  function createComponentBuilder(registry, packageJson) {
@@ -1,6 +1,6 @@
1
1
  // src/utils/fs.ts
2
- import fs from "node:fs/promises";
3
- import path from "node:path";
2
+ import fs from "fs/promises";
3
+ import path from "path";
4
4
  async function exists(pathLike) {
5
5
  try {
6
6
  await fs.access(pathLike);
package/dist/index.js CHANGED
@@ -2,36 +2,29 @@
2
2
  import {
3
3
  exists,
4
4
  isRelative
5
- } from "./chunk-WBCEM7RC.js";
5
+ } from "./chunk-DG6SFM2G.js";
6
6
 
7
7
  // src/index.ts
8
- import fs10 from "node:fs/promises";
9
- import path9 from "node:path";
8
+ import fs10 from "fs/promises";
9
+ import path9 from "path";
10
10
  import { Command } from "commander";
11
- import picocolors6 from "picocolors";
12
- import {
13
- isCancel as isCancel3,
14
- log as log6,
15
- multiselect,
16
- outro as outro2,
17
- select as select2,
18
- spinner as spinner3
19
- } from "@clack/prompts";
11
+ import picocolors5 from "picocolors";
12
+ import { isCancel as isCancel5, outro as outro4, select as select2 } from "@clack/prompts";
20
13
 
21
14
  // src/commands/init.ts
22
- import * as process2 from "node:process";
23
- import path3 from "node:path";
15
+ import * as process2 from "process";
16
+ import path3 from "path";
24
17
  import {
25
- intro,
18
+ cancel,
26
19
  confirm,
20
+ intro,
27
21
  isCancel,
28
- cancel,
29
- spinner,
30
22
  log,
31
- note
23
+ note,
24
+ spinner
32
25
  } from "@clack/prompts";
33
26
  import picocolors from "picocolors";
34
- import { execa } from "execa";
27
+ import { x } from "tinyexec";
35
28
 
36
29
  // src/utils/get-package-manager.ts
37
30
  import { detect } from "package-manager-detector";
@@ -41,7 +34,7 @@ async function getPackageManager() {
41
34
  }
42
35
 
43
36
  // src/utils/is-src.ts
44
- import path from "node:path";
37
+ import path from "path";
45
38
  async function isSrc() {
46
39
  return exists("./src");
47
40
  }
@@ -49,11 +42,8 @@ function resolveAppPath(filePath, src2) {
49
42
  return src2 ? path.join("./src", filePath) : filePath;
50
43
  }
51
44
 
52
- // src/utils/transform-references.ts
53
- import path2 from "node:path";
54
-
55
45
  // src/config.ts
56
- import fs from "node:fs/promises";
46
+ import fs from "fs/promises";
57
47
  var src = await isSrc();
58
48
  var defaultConfig = {
59
49
  aliases: {
@@ -72,44 +62,23 @@ async function loadConfig(file = "./cli.json") {
72
62
  }
73
63
  }
74
64
  async function initConfig(file = "./cli.json") {
65
+ if (await fs.stat(file).then(() => true).catch(() => false)) {
66
+ return false;
67
+ }
75
68
  await fs.writeFile(file, JSON.stringify(defaultConfig, null, 2));
69
+ return true;
76
70
  }
77
71
 
72
+ // src/utils/transform-references.ts
73
+ import path2 from "path";
74
+
78
75
  // src/constants.ts
79
76
  var typescriptExtensions = [".ts", ".tsx", ".js", ".jsx"];
80
77
 
81
78
  // src/utils/transform-references.ts
82
- function getOutputPath(ref, config) {
83
- if (path2.isAbsolute(ref)) throw new Error(`path cannot be absolute: ${ref}`);
84
- if (ref === "utils/cn" || ref === "utils/cn.ts") {
85
- return config.aliases?.cn ?? defaultConfig.aliases.cn;
86
- }
87
- if (ref.startsWith("components")) {
88
- return path2.join(
89
- config.aliases?.componentsDir ?? defaultConfig.aliases.componentsDir,
90
- path2.relative("components", ref)
91
- );
92
- }
93
- if (ref.startsWith("lib") || ref.startsWith("utils")) {
94
- return path2.join(
95
- config.aliases?.libDir ?? defaultConfig.aliases.libDir,
96
- path2.relative("lib", ref)
97
- );
98
- }
99
- return ref;
100
- }
101
- async function transformReferences(file, resolver, transform) {
102
- for (const item of file.getImportDeclarations()) {
103
- const ref = item.getModuleSpecifier().getLiteralValue();
104
- const result = await transform(resolveReference(ref, resolver), ref);
105
- if (!result) continue;
106
- item.getModuleSpecifier().setLiteralValue(result);
107
- }
108
- for (const item of file.getExportDeclarations()) {
109
- const specifier = item.getModuleSpecifier();
110
- if (!specifier) continue;
111
- const ref = specifier.getLiteralValue();
112
- const result = await transform(resolveReference(ref, resolver), ref);
79
+ function transformReferences(file, transform) {
80
+ for (const specifier of file.getImportStringLiterals()) {
81
+ const result = transform(specifier.getLiteralValue());
113
82
  if (!result) continue;
114
83
  specifier.setLiteralValue(result);
115
84
  }
@@ -195,20 +164,17 @@ async function init(plugin, config = {}) {
195
164
  const sourceFile = project.createSourceFile(file, content, {
196
165
  overwrite: true
197
166
  });
198
- await transformReferences(
199
- sourceFile,
200
- {
167
+ transformReferences(sourceFile, (specifier) => {
168
+ const resolved = resolveReference(specifier, {
201
169
  alias: {
202
170
  type: "append",
203
171
  dir: ctx.src ? "src" : ""
204
172
  },
205
173
  relativeTo: path3.dirname(file)
206
- },
207
- (resolved) => {
208
- if (resolved.type !== "file") return;
209
- return toReferencePath(file, getOutputPath(resolved.path, ctx));
210
- }
211
- );
174
+ });
175
+ if (resolved.type !== "file") return;
176
+ return toReferencePath(file, getOutputPath(resolved.path, ctx));
177
+ });
212
178
  await sourceFile.save();
213
179
  }
214
180
  if (plugin.dependencies.length > 0) {
@@ -223,7 +189,7 @@ async function init(plugin, config = {}) {
223
189
  if (value) {
224
190
  const spin = spinner();
225
191
  spin.start("Installing dependencies");
226
- await execa(manager, ["install", ...plugin.dependencies]);
192
+ await x(manager, ["install", ...plugin.dependencies]);
227
193
  spin.stop("Successfully installed.");
228
194
  }
229
195
  }
@@ -261,76 +227,38 @@ prettier . --write`,
261
227
  }
262
228
  }
263
229
  }
264
-
265
- // src/commands/add.ts
266
- import path4 from "node:path";
267
- import fs3 from "node:fs/promises";
268
- import { log as log2, confirm as confirm2, isCancel as isCancel2, outro, spinner as spinner2, intro as intro2 } from "@clack/prompts";
269
- import picocolors2 from "picocolors";
270
- import { execa as execa2 } from "execa";
271
-
272
- // src/utils/add/get-dependencies.ts
273
- import fs2 from "node:fs/promises";
274
- async function getDependencies() {
275
- const dependencies = /* @__PURE__ */ new Map();
276
- if (!await exists("package.json")) return dependencies;
277
- const content = await fs2.readFile("package.json");
278
- const parsed = JSON.parse(content.toString());
279
- if ("dependencies" in parsed && typeof parsed.dependencies === "object") {
280
- const records = parsed.dependencies;
281
- Object.entries(records).forEach(([k, v]) => {
282
- dependencies.set(k, v);
283
- });
230
+ function getOutputPath(ref, config) {
231
+ if (path3.isAbsolute(ref)) throw new Error(`path cannot be absolute: ${ref}`);
232
+ if (ref === "utils/cn" || ref === "utils/cn.ts") {
233
+ return config.aliases?.cn ?? defaultConfig.aliases.cn;
284
234
  }
285
- if ("devDependencies" in parsed && typeof parsed.devDependencies === "object") {
286
- const records = parsed.devDependencies;
287
- Object.entries(records).forEach(([k, v]) => {
288
- dependencies.set(k, v);
289
- });
235
+ if (ref.startsWith("components")) {
236
+ return path3.join(
237
+ config.aliases?.componentsDir ?? defaultConfig.aliases.componentsDir,
238
+ path3.relative("components", ref)
239
+ );
290
240
  }
291
- return dependencies;
241
+ if (ref.startsWith("lib") || ref.startsWith("utils")) {
242
+ return path3.join(
243
+ config.aliases?.libDir ?? defaultConfig.aliases.libDir,
244
+ path3.relative("lib", ref)
245
+ );
246
+ }
247
+ return ref;
292
248
  }
293
249
 
294
- // src/commands/add.ts
250
+ // src/utils/add/install-component.ts
251
+ import path4 from "path";
252
+ import fs2 from "fs/promises";
253
+ import { confirm as confirm2, isCancel as isCancel2, log as log2, outro } from "@clack/prompts";
295
254
  var downloadedFiles = /* @__PURE__ */ new Set();
296
- async function add(name, resolver, config = {}) {
297
- intro2(
298
- picocolors2.bold(
299
- picocolors2.inverse(picocolors2.cyanBright(`Add Component: ${name}`))
300
- )
301
- );
255
+ async function installComponent(name, resolver, config = {}) {
302
256
  const project = createEmptyProject();
303
- const result = await downloadComponent(name, {
257
+ return downloadComponent(name, {
304
258
  project,
305
259
  config,
306
260
  resolver
307
261
  });
308
- if (!result) {
309
- log2.error(`Component: ${name} not found`);
310
- process.exit(0);
311
- }
312
- const installed = await getDependencies();
313
- const deps = Object.entries(result.dependencies).filter(([k]) => !installed.has(k)).map(([k, v]) => v.length === 0 ? k : `${k}@${v}`);
314
- const devDeps = Object.entries(result.devDependencies).filter(([k]) => !installed.has(k)).map(([k, v]) => v.length === 0 ? k : `${k}@${v}`);
315
- if (deps.length > 0 || devDeps.length > 0) {
316
- const manager = await getPackageManager();
317
- const value = await confirm2({
318
- message: `This component requires dependencies (${[...deps, ...devDeps].join(" ")}), install them with ${manager}?`
319
- });
320
- if (isCancel2(value)) {
321
- outro(picocolors2.bold(picocolors2.greenBright("Component downloaded")));
322
- process.exit(0);
323
- }
324
- if (value) {
325
- const spin = spinner2();
326
- spin.start("Installing dependencies...");
327
- if (deps.length > 0) await execa2(manager, ["install", ...deps]);
328
- if (devDeps.length > 0)
329
- await execa2(manager, ["install", ...devDeps, "-D"]);
330
- spin.stop("Dependencies installed.");
331
- }
332
- }
333
- outro(picocolors2.bold(picocolors2.greenBright("Component installed")));
334
262
  }
335
263
  var downloadedComps = /* @__PURE__ */ new Map();
336
264
  async function downloadComponent(name, ctx) {
@@ -344,7 +272,7 @@ async function downloadComponent(name, ctx) {
344
272
  const outPath = resolveOutputPath(file.path, ctx.config);
345
273
  const output = typescriptExtensions.includes(path4.extname(file.path)) ? transformTypeScript(outPath, file, ctx) : file.content;
346
274
  let canWrite = true;
347
- const requireOverride = await fs3.readFile(outPath).then((res) => res.toString() !== output).catch(() => false);
275
+ const requireOverride = await fs2.readFile(outPath).then((res) => res.toString() !== output).catch(() => false);
348
276
  if (requireOverride) {
349
277
  const value = await confirm2({
350
278
  message: `Do you want to override ${outPath}?`
@@ -356,8 +284,8 @@ async function downloadComponent(name, ctx) {
356
284
  canWrite = value;
357
285
  }
358
286
  if (canWrite) {
359
- await fs3.mkdir(path4.dirname(outPath), { recursive: true });
360
- await fs3.writeFile(outPath, output);
287
+ await fs2.mkdir(path4.dirname(outPath), { recursive: true });
288
+ await fs2.writeFile(outPath, output);
361
289
  log2.step(`downloaded ${outPath}`);
362
290
  }
363
291
  downloadedFiles.add(file.path);
@@ -389,22 +317,12 @@ function transformTypeScript(filePath, file, ctx) {
389
317
  const sourceFile = ctx.project.createSourceFile(filePath, file.content, {
390
318
  overwrite: true
391
319
  });
392
- for (const item of sourceFile.getImportDeclarations()) {
393
- const ref = item.getModuleSpecifier().getLiteralValue();
394
- if (ref in file.imports) {
395
- const outputPath = resolveOutputPath(file.imports[ref], ctx.config);
396
- item.getModuleSpecifier().setLiteralValue(toReferencePath(filePath, outputPath));
397
- }
398
- }
399
- for (const item of sourceFile.getExportDeclarations()) {
400
- const specifier = item.getModuleSpecifier();
401
- if (!specifier) continue;
402
- const ref = specifier.getLiteralValue();
403
- if (ref in file.imports) {
404
- const outputPath = resolveOutputPath(file.imports[ref], ctx.config);
405
- specifier.setLiteralValue(toReferencePath(filePath, outputPath));
320
+ transformReferences(sourceFile, (specifier) => {
321
+ if (specifier in file.imports) {
322
+ const outputPath = resolveOutputPath(file.imports[specifier], ctx.config);
323
+ return toReferencePath(filePath, outputPath);
406
324
  }
407
- }
325
+ });
408
326
  return sourceFile.getFullText();
409
327
  }
410
328
  function remoteResolver(url) {
@@ -416,75 +334,24 @@ function remoteResolver(url) {
416
334
  }
417
335
  function localResolver(dir) {
418
336
  return async (file) => {
419
- return await fs3.readFile(path4.join(dir, file)).then((res) => JSON.parse(res.toString())).catch(() => void 0);
337
+ return await fs2.readFile(path4.join(dir, file)).then((res) => JSON.parse(res.toString())).catch(() => void 0);
420
338
  };
421
339
  }
422
340
 
423
- // src/plugins/og-image.ts
424
- import picocolors3 from "picocolors";
425
-
426
- // src/generated.js
427
- var generated = { "app/docs-og/[...slug]/route": "import { generateOGImage } from 'fumadocs-ui/og';\nimport { metadataImage } from '@/lib/metadata';\n\nexport const GET = metadataImage.createAPI((page) => {\n return generateOGImage({\n title: page.data.title,\n description: page.data.description,\n site: 'My App',\n });\n});\n\nexport function generateStaticParams() {\n return metadataImage.generateParams();\n}\n", "lib/metadata": "import { createMetadataImage } from 'fumadocs-core/server';\nimport { source } from '@/lib/source';\n\nexport const metadataImage = createMetadataImage({\n imageRoute: '/docs-og',\n source,\n});\n", "lib/i18n": "import type { I18nConfig } from 'fumadocs-core/i18n';\n\nexport const i18n: I18nConfig = {\n defaultLanguage: 'en',\n languages: ['en', 'cn'],\n};\n", "middleware": "import { createI18nMiddleware } from 'fumadocs-core/i18n';\nimport { i18n } from '@/lib/i18n';\n\nexport default createI18nMiddleware(i18n);\n\nexport const config = {\n // Matcher ignoring `/_next/` and `/api/`\n matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],\n};\n", "scripts/generate-docs": "import * as OpenAPI from 'fumadocs-openapi';\nimport { rimrafSync } from 'rimraf';\n\nconst out = './content/docs/(api)';\n\n// clean generated files\nrimrafSync(out, {\n filter(v) {\n return !v.endsWith('index.mdx') && !v.endsWith('meta.json');\n },\n});\n\nvoid OpenAPI.generateFiles({\n // input files\n input: ['./openapi.json'],\n output: out,\n groupBy: 'tag',\n});\n" };
428
-
429
- // src/plugins/og-image.ts
430
- function isI18nEnabled(ctx) {
431
- return exists(getOutputPath("lib/i18n.ts", ctx));
432
- }
433
- var ogImagePlugin = {
434
- files: async (ctx) => {
435
- const route = await isI18nEnabled(ctx) ? "app/[lang]/docs-og/[...slug]/route.tsx" : "app/docs-og/[...slug]/route.tsx";
436
- return {
437
- "lib/metadata.ts": generated["lib/metadata"],
438
- [ctx.src ? `src/${route}` : route]: generated["app/docs-og/[...slug]/route"]
439
- };
440
- },
441
- dependencies: [],
442
- instructions: (ctx) => [
443
- {
444
- type: "text",
445
- text: picocolors3.cyanBright(picocolors3.bold("Import the utils like:"))
446
- },
447
- {
448
- type: "code",
449
- title: "ts",
450
- code: 'import { metadataImage } from "@/lib/metadata";'
451
- },
452
- {
453
- type: "text",
454
- text: picocolors3.cyanBright(
455
- picocolors3.bold("Add the images to your metadata:")
456
- )
457
- },
458
- {
459
- type: "code",
460
- title: resolveAppPath("app/docs/[[...slug]]/page.tsx", ctx.src),
461
- code: `
462
- export function generateMetadata({ params }: { params: { slug?: string[] } }) {
463
- const page = source.getPage(params.slug);
464
-
465
- if (!page) notFound();
466
-
467
- ${picocolors3.bold(picocolors3.underline("return metadataImage.withImage(page.slugs, {"))}
468
- title: page.data.title,
469
- description: page.data.description,
470
- });
471
- }
472
- `.trim()
473
- }
474
- ]
475
- };
476
-
477
341
  // src/plugins/i18n.ts
478
- import path7 from "node:path";
342
+ import path7 from "path";
479
343
  import { log as log3 } from "@clack/prompts";
480
344
 
345
+ // src/generated.js
346
+ var generated = { "lib/i18n": "import type { I18nConfig } from 'fumadocs-core/i18n';\n\nexport const i18n: I18nConfig = {\n defaultLanguage: 'en',\n languages: ['en', 'cn'],\n};\n", "middleware": "import { createI18nMiddleware } from 'fumadocs-core/i18n';\nimport { i18n } from '@/lib/i18n';\n\nexport default createI18nMiddleware(i18n);\n\nexport const config = {\n // Matcher ignoring `/_next/` and `/api/`\n matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],\n};\n", "scripts/generate-docs": "import * as OpenAPI from 'fumadocs-openapi';\nimport { rimraf } from 'rimraf';\n\nconst out = './content/docs/(api)';\n\nasync function generate() {\n // clean generated files\n await rimraf(out, {\n filter(v) {\n return !v.endsWith('index.mdx') && !v.endsWith('meta.json');\n },\n });\n\n await OpenAPI.generateFiles({\n // input files\n input: ['./openapi.json'],\n output: out,\n });\n}\n\nvoid generate();\n" };
347
+
481
348
  // src/utils/i18n/transform-layout-config.ts
482
- import fs4 from "node:fs/promises";
349
+ import fs3 from "fs/promises";
483
350
  import { SyntaxKind } from "ts-morph";
484
351
  async function transformLayoutConfig(project, filePath) {
485
352
  let content;
486
353
  try {
487
- content = await fs4.readFile(filePath).then((res) => res.toString());
354
+ content = await fs3.readFile(filePath).then((res) => res.toString());
488
355
  } catch {
489
356
  return;
490
357
  }
@@ -506,18 +373,18 @@ async function transformLayoutConfig(project, filePath) {
506
373
  }
507
374
 
508
375
  // src/utils/move-files.ts
509
- import fs5 from "node:fs/promises";
510
- import path5 from "node:path";
376
+ import fs4 from "fs/promises";
377
+ import path5 from "path";
511
378
  var transformExtensions = [".js", ".ts", ".tsx", ".jsx"];
512
379
  async function moveFiles(from, to, filter, project, src2, originalDir = from) {
513
380
  function isIncluded(file) {
514
381
  if (!transformExtensions.includes(path5.extname(file))) return false;
515
382
  return filter(path5.resolve(file));
516
383
  }
517
- const stats = await fs5.lstat(from).catch(() => void 0);
384
+ const stats = await fs4.lstat(from).catch(() => void 0);
518
385
  if (!stats) return;
519
386
  if (stats.isDirectory()) {
520
- const items = await fs5.readdir(from);
387
+ const items = await fs4.readdir(from);
521
388
  await Promise.all(
522
389
  items.map(async (item) => {
523
390
  await moveFiles(
@@ -530,46 +397,43 @@ async function moveFiles(from, to, filter, project, src2, originalDir = from) {
530
397
  );
531
398
  })
532
399
  );
533
- await fs5.rmdir(from).catch(() => {
400
+ await fs4.rmdir(from).catch(() => {
534
401
  });
535
402
  }
536
- if (!stats.isFile() || !await isIncluded(from)) return;
537
- const content = await fs5.readFile(from);
403
+ if (!stats.isFile() || !isIncluded(from)) return;
404
+ const content = await fs4.readFile(from);
538
405
  const sourceFile = project.createSourceFile(from, content.toString(), {
539
406
  overwrite: true
540
407
  });
541
- await transformReferences(
542
- sourceFile,
543
- {
408
+ transformReferences(sourceFile, (specifier) => {
409
+ const resolved = resolveReference(specifier, {
544
410
  alias: {
545
411
  type: "append",
546
412
  dir: src2 ? "src" : ""
547
413
  },
548
414
  relativeTo: path5.dirname(from)
549
- },
550
- async (resolved) => {
551
- if (resolved.type !== "file") return;
552
- if (
553
- // ignore if the file is also moved
554
- isRelative(originalDir, from) && await isIncluded(resolved.path)
555
- )
556
- return;
557
- return toReferencePath(to, resolved.path);
558
- }
559
- );
415
+ });
416
+ if (resolved.type !== "file") return;
417
+ if (
418
+ // ignore if the file is also moved
419
+ isRelative(originalDir, from) && isIncluded(resolved.path)
420
+ )
421
+ return;
422
+ return toReferencePath(to, resolved.path);
423
+ });
560
424
  await sourceFile.save();
561
- await fs5.mkdir(path5.dirname(to), { recursive: true });
562
- await fs5.rename(from, to);
425
+ await fs4.mkdir(path5.dirname(to), { recursive: true });
426
+ await fs4.rename(from, to);
563
427
  }
564
428
 
565
429
  // src/utils/i18n/transform-source-i18n.ts
566
- import * as fs6 from "node:fs/promises";
567
- import path6 from "node:path";
430
+ import * as fs5 from "fs/promises";
431
+ import path6 from "path";
568
432
  import { StructureKind, SyntaxKind as SyntaxKind2 } from "ts-morph";
569
433
  async function transformSourceI18n(project, filePath, config) {
570
434
  let content;
571
435
  try {
572
- content = await fs6.readFile(filePath).then((res) => res.toString());
436
+ content = await fs5.readFile(filePath).then((res) => res.toString());
573
437
  } catch {
574
438
  return;
575
439
  }
@@ -598,14 +462,14 @@ async function transformSourceI18n(project, filePath, config) {
598
462
  }
599
463
 
600
464
  // src/utils/i18n/transform-root-layout.ts
601
- import fs7 from "node:fs/promises";
465
+ import fs6 from "fs/promises";
602
466
  import { StructureKind as StructureKind2, ts } from "ts-morph";
603
467
  var ScriptKind = ts.ScriptKind;
604
468
  var SyntaxKind3 = ts.SyntaxKind;
605
469
  async function transformRootLayout(project, filePath) {
606
470
  let content;
607
471
  try {
608
- content = await fs7.readFile(filePath).then((res) => res.toString());
472
+ content = await fs6.readFile(filePath).then((res) => res.toString());
609
473
  } catch {
610
474
  return;
611
475
  }
@@ -647,7 +511,7 @@ function runTransform(sourceFile) {
647
511
  }
648
512
 
649
513
  // src/plugins/i18n.ts
650
- import picocolors4 from "picocolors";
514
+ import picocolors2 from "picocolors";
651
515
  var i18nPlugin = {
652
516
  files: ({ src: src2 }) => ({
653
517
  "lib/i18n.ts": generated["lib/i18n"],
@@ -657,7 +521,7 @@ var i18nPlugin = {
657
521
  instructions: () => [
658
522
  {
659
523
  type: "title",
660
- text: `1. Update the params of ${picocolors4.bold("page.tsx")} and ${picocolors4.bold("layout.tsx")}, and make them async if necessary.`
524
+ text: `1. Update the params of ${picocolors2.bold("page.tsx")} and ${picocolors2.bold("layout.tsx")}, and make them async if necessary.`
661
525
  },
662
526
  {
663
527
  type: "code",
@@ -666,7 +530,7 @@ var i18nPlugin = {
666
530
  export default async function Layout({
667
531
  params,
668
532
  }: {
669
- ${picocolors4.underline(picocolors4.bold("params: Promise<{ lang: string }>"))}
533
+ ${picocolors2.underline(picocolors2.bold("params: Promise<{ lang: string }>"))}
670
534
  })
671
535
  `.trim()
672
536
  },
@@ -677,7 +541,7 @@ export default async function Layout({
677
541
  export default async function Page({
678
542
  params,
679
543
  }: {
680
- ${picocolors4.underline(picocolors4.bold("params: Promise<{ lang: string; slug?: string[] }>"))}
544
+ ${picocolors2.underline(picocolors2.bold("params: Promise<{ lang: string; slug?: string[] }>"))}
681
545
  })
682
546
  `.trim()
683
547
  },
@@ -731,12 +595,12 @@ See https://nextjs.org/docs/app/building-your-application/routing/internationali
731
595
  };
732
596
 
733
597
  // src/plugins/openapi.ts
734
- import fs9 from "node:fs/promises";
735
- import path8 from "node:path";
598
+ import fs8 from "fs/promises";
599
+ import path8 from "path";
736
600
  import { StructureKind as StructureKind3 } from "ts-morph";
737
601
 
738
602
  // src/utils/transform-tailwind.ts
739
- import fs8 from "node:fs/promises";
603
+ import fs7 from "fs/promises";
740
604
  import { SyntaxKind as SyntaxKind4 } from "ts-morph";
741
605
  import { log as log4 } from "@clack/prompts";
742
606
  var tailwindConfigPaths = [
@@ -762,7 +626,7 @@ async function transformTailwind(project, options) {
762
626
  }
763
627
  const configFile = project.createSourceFile(
764
628
  file,
765
- await fs8.readFile(file).then((res) => res.toString()),
629
+ await fs7.readFile(file).then((res) => res.toString()),
766
630
  { overwrite: true }
767
631
  );
768
632
  const exports = configFile.getExportAssignments();
@@ -823,21 +687,21 @@ import { openapi } from '@/lib/source';
823
687
  ]
824
688
  };
825
689
  async function addScript() {
826
- const content = await fs9.readFile("package.json");
690
+ const content = await fs8.readFile("package.json");
827
691
  const parsed = JSON.parse(content.toString());
828
692
  if (typeof parsed.scripts !== "object") return;
829
693
  parsed.scripts ??= {};
830
694
  Object.assign(parsed.scripts ?? {}, {
831
695
  "build:docs": "node ./scripts/generate-docs.mjs"
832
696
  });
833
- await fs9.writeFile("package.json", JSON.stringify(parsed, null, 2));
697
+ await fs8.writeFile("package.json", JSON.stringify(parsed, null, 2));
834
698
  }
835
699
  async function transformSource(project, config) {
836
700
  const source = path8.join(
837
701
  config.aliases?.libDir ?? defaultConfig.aliases.libDir,
838
702
  "source.ts"
839
703
  );
840
- const content = await fs9.readFile(source).catch(() => "");
704
+ const content = await fs8.readFile(source).catch(() => "");
841
705
  const file = project.createSourceFile(source, content.toString(), {
842
706
  overwrite: true
843
707
  });
@@ -852,7 +716,6 @@ async function transformSource(project, config) {
852
716
 
853
717
  // src/plugins/index.ts
854
718
  var plugins = {
855
- "og-image": ogImagePlugin,
856
719
  i18n: i18nPlugin,
857
720
  openapi: openapiPlugin
858
721
  };
@@ -893,9 +756,9 @@ export default (${treeToMdx(input, noRoot)})`;
893
756
  }
894
757
 
895
758
  // src/utils/file-tree/run-tree.ts
896
- import { execa as execa3 } from "execa";
759
+ import { x as x2 } from "tinyexec";
897
760
  async function runTree(args) {
898
- const out = await execa3("tree", [args, "--gitignore", "--prune", "-J"]);
761
+ const out = await x2("tree", [args, "--gitignore", "--prune", "-J"]);
899
762
  try {
900
763
  return JSON.parse(out.stdout);
901
764
  } catch (e) {
@@ -908,7 +771,7 @@ async function runTree(args) {
908
771
  // package.json
909
772
  var package_default = {
910
773
  name: "@fumadocs/cli",
911
- version: "0.1.1",
774
+ version: "0.2.1",
912
775
  description: "The CLI tool for Fumadocs",
913
776
  keywords: [
914
777
  "NextJs",
@@ -942,21 +805,18 @@ var package_default = {
942
805
  "types:check": "tsc --noEmit"
943
806
  },
944
807
  dependencies: {
945
- "@clack/prompts": "^0.10.0",
946
- commander: "^13.1.0",
947
- execa: "^9.5.2",
948
- "package-manager-detector": "^1.1.0",
808
+ "@clack/prompts": "^0.11.0",
809
+ commander: "^14.0.0",
810
+ "package-manager-detector": "^1.3.0",
949
811
  picocolors: "^1.1.1",
950
- "ts-morph": "^25.0.1"
812
+ tinyexec: "^1.0.1",
813
+ "ts-morph": "^26.0.0"
951
814
  },
952
815
  devDependencies: {
953
- "@types/cross-spawn": "^6.0.6",
954
- "@types/node": "22.14.0",
955
- "@types/react": "^19.1.0",
816
+ "@types/node": "24.0.1",
956
817
  "eslint-config-custom": "workspace:*",
957
- "fast-glob": "^3.3.3",
958
818
  tsconfig: "workspace:*",
959
- tsx: "^4.19.3"
819
+ tsx: "^4.20.2"
960
820
  },
961
821
  publishConfig: {
962
822
  access: "public"
@@ -964,10 +824,136 @@ var package_default = {
964
824
  };
965
825
 
966
826
  // src/commands/customise.ts
967
- import { cancel as cancel2, group, intro as intro3, select, confirm as confirm3, log as log5 } from "@clack/prompts";
968
- import picocolors5 from "picocolors";
827
+ import {
828
+ cancel as cancel2,
829
+ confirm as confirm4,
830
+ group,
831
+ intro as intro3,
832
+ log as log6,
833
+ outro as outro3,
834
+ select
835
+ } from "@clack/prompts";
836
+ import picocolors4 from "picocolors";
837
+
838
+ // src/commands/add.ts
839
+ import {
840
+ intro as intro2,
841
+ isCancel as isCancel4,
842
+ log as log5,
843
+ multiselect,
844
+ outro as outro2,
845
+ spinner as spinner3
846
+ } from "@clack/prompts";
847
+ import picocolors3 from "picocolors";
848
+
849
+ // src/utils/add/install-deps.ts
850
+ import { confirm as confirm3, isCancel as isCancel3, spinner as spinner2 } from "@clack/prompts";
851
+ import { x as x3 } from "tinyexec";
852
+
853
+ // src/utils/add/get-deps.ts
854
+ import fs9 from "fs/promises";
855
+ async function getDeps() {
856
+ const dependencies = /* @__PURE__ */ new Map();
857
+ if (!await exists("package.json")) return dependencies;
858
+ const content = await fs9.readFile("package.json");
859
+ const parsed = JSON.parse(content.toString());
860
+ if ("dependencies" in parsed && typeof parsed.dependencies === "object") {
861
+ const records = parsed.dependencies;
862
+ Object.entries(records).forEach(([k, v]) => {
863
+ dependencies.set(k, v);
864
+ });
865
+ }
866
+ if ("devDependencies" in parsed && typeof parsed.devDependencies === "object") {
867
+ const records = parsed.devDependencies;
868
+ Object.entries(records).forEach(([k, v]) => {
869
+ dependencies.set(k, v);
870
+ });
871
+ }
872
+ return dependencies;
873
+ }
874
+
875
+ // src/utils/add/install-deps.ts
876
+ async function installDeps(results) {
877
+ const installed = await getDeps();
878
+ const deps = {};
879
+ const devDeps = {};
880
+ for (const result of results) {
881
+ Object.assign(deps, result.dependencies);
882
+ Object.assign(devDeps, result.devDependencies);
883
+ }
884
+ const items = Object.entries(deps).filter(([k]) => !installed.has(k)).map(([k, v]) => v.length === 0 ? k : `${k}@${v}`);
885
+ const devItems = Object.entries(devDeps).filter(([k]) => !installed.has(k)).map(([k, v]) => v.length === 0 ? k : `${k}@${v}`);
886
+ if (items.length > 0 || devItems.length > 0) {
887
+ const manager = await getPackageManager();
888
+ const value = await confirm3({
889
+ message: `Do you want to install with ${manager}?
890
+ ${[...items, ...devItems].map((v) => `- ${v}`).join("\n")}`
891
+ });
892
+ if (isCancel3(value)) {
893
+ return;
894
+ }
895
+ if (value) {
896
+ const spin = spinner2();
897
+ spin.start("Installing dependencies...");
898
+ if (items.length > 0) await x3(manager, ["install", ...items]);
899
+ if (devItems.length > 0) await x3(manager, ["install", ...devItems, "-D"]);
900
+ spin.stop("Dependencies installed.");
901
+ }
902
+ }
903
+ }
904
+
905
+ // src/commands/add.ts
906
+ async function add(input, resolver, config) {
907
+ let target = input;
908
+ if (input.length === 0) {
909
+ const spin = spinner3();
910
+ spin.start("fetching registry");
911
+ const registry = await resolver("_registry.json");
912
+ spin.stop(picocolors3.bold(picocolors3.greenBright("registry fetched")));
913
+ if (!registry) {
914
+ log5.error(`Failed to fetch '_registry.json' file from registry`);
915
+ throw new Error(`Failed to fetch registry`);
916
+ }
917
+ const value = await multiselect({
918
+ message: "Select components to install",
919
+ options: registry.map((item) => ({
920
+ label: item.name,
921
+ value: item.name,
922
+ hint: item.description
923
+ }))
924
+ });
925
+ if (isCancel4(value)) {
926
+ outro2("Ended");
927
+ return;
928
+ }
929
+ target = value;
930
+ }
931
+ await install(target, resolver, config);
932
+ }
933
+ async function install(target, resolver, config) {
934
+ const outputs = [];
935
+ for (const name of target) {
936
+ intro2(
937
+ picocolors3.bold(
938
+ picocolors3.inverse(picocolors3.cyanBright(`Add Component: ${name}`))
939
+ )
940
+ );
941
+ const output = await installComponent(name, resolver, config);
942
+ if (!output) {
943
+ log5.error(`Failed to install ${name}: not found`);
944
+ continue;
945
+ }
946
+ outro2(picocolors3.bold(picocolors3.greenBright(`${name} installed`)));
947
+ outputs.push(output);
948
+ }
949
+ intro2(picocolors3.bold("New Dependencies"));
950
+ await installDeps(outputs);
951
+ outro2(picocolors3.bold(picocolors3.greenBright("Successful")));
952
+ }
953
+
954
+ // src/commands/customise.ts
969
955
  async function customise(resolver, config) {
970
- intro3(picocolors5.bgBlack(picocolors5.whiteBright("Customise Fumadocs UI")));
956
+ intro3(picocolors4.bgBlack(picocolors4.whiteBright("Customise Fumadocs UI")));
971
957
  const result = await group(
972
958
  {
973
959
  target: () => select({
@@ -1011,7 +997,7 @@ async function customise(resolver, config) {
1011
997
  page: async (v) => {
1012
998
  if (v.results.target !== "docs" || v.results.mode === "minimal")
1013
999
  return false;
1014
- return confirm3({
1000
+ return confirm4({
1015
1001
  message: "Do you want to customise the page component too?"
1016
1002
  });
1017
1003
  }
@@ -1024,57 +1010,61 @@ async function customise(resolver, config) {
1024
1010
  }
1025
1011
  );
1026
1012
  if (result.target === "docs") {
1013
+ const targets = [];
1027
1014
  let pageAdded = false;
1028
1015
  if (result.mode === "minimal") {
1029
- await add("layouts/docs-min", resolver, config);
1016
+ targets.push("layouts/docs-min");
1030
1017
  pageAdded = true;
1031
1018
  } else {
1032
1019
  if (result.page) {
1033
- await add("layouts/page", resolver, config);
1020
+ targets.push("layouts/page");
1034
1021
  pageAdded = true;
1035
1022
  }
1036
- await add(
1037
- result.mode === "full-default" ? "layouts/docs" : "layouts/notebook",
1038
- resolver,
1039
- config
1023
+ targets.push(
1024
+ result.mode === "full-default" ? "layouts/docs" : "layouts/notebook"
1040
1025
  );
1041
1026
  }
1042
- log5.info(
1027
+ await install(targets, resolver, config);
1028
+ intro3(picocolors4.bold("What is Next?"));
1029
+ log6.info(
1043
1030
  [
1044
- picocolors5.bold("What is Next?"),
1045
1031
  "You can check the installed components in `components/layouts`.",
1046
- picocolors5.dim("---"),
1032
+ picocolors4.dim("---"),
1047
1033
  "Open your `layout.tsx` files, replace the imports of components:",
1048
- picocolors5.greenBright(
1034
+ picocolors4.greenBright(
1049
1035
  "`fumadocs-ui/layouts/docs` -> `@/components/layouts/docs`"
1050
1036
  ),
1051
- pageAdded ? picocolors5.greenBright(
1037
+ pageAdded ? picocolors4.greenBright(
1052
1038
  "`fumadocs-ui/page` -> `@/components/layouts/page`"
1053
1039
  ) : ""
1054
1040
  ].join("\n")
1055
1041
  );
1056
1042
  }
1057
1043
  if (result.target === "home") {
1058
- await add("layouts/home", resolver, config);
1059
- log5.info(
1044
+ await install(["layouts/home"], resolver, config);
1045
+ intro3(picocolors4.bold("What is Next?"));
1046
+ log6.info(
1060
1047
  [
1061
- picocolors5.bold("What is Next?"),
1062
1048
  "You can check the installed components in `components/layouts`.",
1063
- picocolors5.dim("---"),
1049
+ picocolors4.dim("---"),
1064
1050
  "Open your `layout.tsx` files, replace the imports of components:",
1065
- picocolors5.greenBright(
1051
+ picocolors4.greenBright(
1066
1052
  "`fumadocs-ui/layouts/home` -> `@/components/layouts/home`"
1067
1053
  )
1068
1054
  ].join("\n")
1069
1055
  );
1070
1056
  }
1057
+ outro3(picocolors4.bold("Have fun!"));
1071
1058
  }
1072
1059
 
1073
1060
  // src/index.ts
1074
1061
  var program = new Command().option("--config <string>");
1075
1062
  program.name("fumadocs").description("CLI to setup Fumadocs, init a config ").version(package_default.version).action(async () => {
1076
- await initConfig();
1077
- console.log(picocolors6.green("Initialized a `./cli.json` config file."));
1063
+ if (await initConfig()) {
1064
+ console.log(picocolors5.green("Initialized a `./cli.json` config file."));
1065
+ } else {
1066
+ console.log(picocolors5.redBright("A config file already exists."));
1067
+ }
1078
1068
  });
1079
1069
  program.command("customise").alias("customize").description("simple way to customise layouts with Fumadocs UI").option("--dir <string>", "the root url or directory to resolve registry").action(async (options) => {
1080
1070
  const resolver = getResolverFromDir(options.dir);
@@ -1095,48 +1085,20 @@ program.command("init").description("init a new plugin to your docs").argument("
1095
1085
  value: c
1096
1086
  }))
1097
1087
  });
1098
- if (isCancel3(value)) {
1099
- outro2("Ended");
1088
+ if (isCancel5(value)) {
1089
+ outro4("Ended");
1100
1090
  return;
1101
1091
  }
1102
1092
  await init(plugins[value], loadedConfig);
1103
1093
  });
1104
1094
  var dirShortcuts = {
1105
- ":dev": "https://fumadocs-dev.vercel.app/registry"
1095
+ ":dev": "https://preview.fumadocs.dev/registry",
1096
+ ":localhost": "http://localhost:3000/registry"
1106
1097
  };
1107
1098
  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(
1108
1099
  async (input, options) => {
1109
1100
  const resolver = getResolverFromDir(options.dir);
1110
- let target = input;
1111
- if (input.length === 0) {
1112
- const spin = spinner3();
1113
- spin.start("fetching registry");
1114
- const registry = await resolver("_registry.json");
1115
- spin.stop(picocolors6.bold(picocolors6.greenBright("registry fetched")));
1116
- if (!registry) {
1117
- log6.error(
1118
- `Failed to fetch '_registry.json' file from ${options.dir ?? "registry"}`
1119
- );
1120
- throw new Error(`Failed to fetch registry`);
1121
- }
1122
- const value = await multiselect({
1123
- message: "Select components to install",
1124
- options: registry.map((item) => ({
1125
- label: item.name,
1126
- value: item.name,
1127
- hint: item.description
1128
- }))
1129
- });
1130
- if (isCancel3(value)) {
1131
- outro2("Ended");
1132
- return;
1133
- }
1134
- target = value;
1135
- }
1136
- const loadedConfig = await loadConfig(options.config);
1137
- for (const name of target) {
1138
- await add(name, resolver, loadedConfig);
1139
- }
1101
+ await add(input, resolver, await loadConfig(options.config));
1140
1102
  }
1141
1103
  );
1142
1104
  program.command("tree").argument(
@@ -1165,7 +1127,7 @@ program.command("tree").argument(
1165
1127
  }
1166
1128
  }
1167
1129
  );
1168
- function getResolverFromDir(dir = "https://fumadocs.vercel.app/registry") {
1130
+ function getResolverFromDir(dir = "https://fumadocs.dev/registry") {
1169
1131
  if (dir in dirShortcuts) dir = dirShortcuts[dir];
1170
1132
  return dir.startsWith("http://") || dir.startsWith("https://") ? remoteResolver(dir) : localResolver(dir);
1171
1133
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fumadocs/cli",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "The CLI tool for Fumadocs",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -26,19 +26,16 @@
26
26
  "dist/*"
27
27
  ],
28
28
  "dependencies": {
29
- "@clack/prompts": "^0.10.0",
30
- "commander": "^13.1.0",
31
- "execa": "^9.5.2",
32
- "package-manager-detector": "^1.1.0",
29
+ "@clack/prompts": "^0.11.0",
30
+ "commander": "^14.0.0",
31
+ "package-manager-detector": "^1.3.0",
33
32
  "picocolors": "^1.1.1",
34
- "ts-morph": "^25.0.1"
33
+ "tinyexec": "^1.0.1",
34
+ "ts-morph": "^26.0.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@types/cross-spawn": "^6.0.6",
38
- "@types/node": "22.14.0",
39
- "@types/react": "^19.1.0",
40
- "fast-glob": "^3.3.3",
41
- "tsx": "^4.19.3",
37
+ "@types/node": "24.0.1",
38
+ "tsx": "^4.20.2",
42
39
  "eslint-config-custom": "0.0.0",
43
40
  "tsconfig": "0.0.0"
44
41
  },