@fumadocs/cli 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-BVuNjYXU.js +100 -0
- package/dist/ai-BVuNjYXU.js.map +1 -0
- package/dist/all-aUHxtoXL.js +27 -0
- package/dist/all-aUHxtoXL.js.map +1 -0
- package/dist/codemod/index.d.ts +2 -0
- package/dist/codemod/index.js +2 -0
- package/dist/codemod-auol-p4R.js +404 -0
- package/dist/codemod-auol-p4R.js.map +1 -0
- package/dist/config-CT2jj39D.d.ts +52 -0
- package/dist/config-CT2jj39D.d.ts.map +1 -0
- package/dist/config-D6uv73fs.js +94 -0
- package/dist/config-D6uv73fs.js.map +1 -0
- package/dist/config.d.ts +2 -52
- package/dist/config.js +1 -72
- package/dist/docs-DeIEl_Kt.js +738 -0
- package/dist/docs-DeIEl_Kt.js.map +1 -0
- package/dist/epub-CnQFJh09.js +52 -0
- package/dist/epub-CnQFJh09.js.map +1 -0
- package/dist/features/ai.d.ts +25 -0
- package/dist/features/ai.d.ts.map +1 -0
- package/dist/features/ai.js +2 -0
- package/dist/features/all.d.ts +5 -0
- package/dist/features/all.d.ts.map +1 -0
- package/dist/features/all.js +2 -0
- package/dist/features/docs/index.d.ts +10 -0
- package/dist/features/docs/index.d.ts.map +1 -0
- package/dist/features/docs/index.js +2 -0
- package/dist/features/epub.d.ts +5 -0
- package/dist/features/epub.d.ts.map +1 -0
- package/dist/features/epub.js +2 -0
- package/dist/features/feedback.d.ts +5 -0
- package/dist/features/feedback.d.ts.map +1 -0
- package/dist/features/feedback.js +2 -0
- package/dist/features/index.d.ts +2 -0
- package/dist/features/index.js +2 -0
- package/dist/features/lint.d.ts +7 -0
- package/dist/features/lint.d.ts.map +1 -0
- package/dist/features/lint.js +2 -0
- package/dist/features/llms.d.ts +27 -0
- package/dist/features/llms.d.ts.map +1 -0
- package/dist/features/llms.js +2 -0
- package/dist/features/mcp.d.ts +9 -0
- package/dist/features/mcp.d.ts.map +1 -0
- package/dist/features/mcp.js +2 -0
- package/dist/features/og.d.ts +8 -0
- package/dist/features/og.d.ts.map +1 -0
- package/dist/features/og.js +2 -0
- package/dist/features/search.d.ts +84 -0
- package/dist/features/search.d.ts.map +1 -0
- package/dist/features/search.js +2 -0
- package/dist/features/utils.d.ts +2 -0
- package/dist/features/utils.js +2 -0
- package/dist/features/webmcp.d.ts +12 -0
- package/dist/features/webmcp.d.ts.map +1 -0
- package/dist/features/webmcp.js +2 -0
- package/dist/features-C1kf2s8j.js +132 -0
- package/dist/features-C1kf2s8j.js.map +1 -0
- package/dist/feedback-COmWtc5C.js +37 -0
- package/dist/feedback-COmWtc5C.js.map +1 -0
- package/dist/fs-D6J9dOEx.js +14 -0
- package/dist/fs-D6J9dOEx.js.map +1 -0
- package/dist/index-BCVGQxn9.d.ts +64 -0
- package/dist/index-BCVGQxn9.d.ts.map +1 -0
- package/dist/index-CR9T40ox.d.ts +76 -0
- package/dist/index-CR9T40ox.d.ts.map +1 -0
- package/dist/index-Ck0EPdgu.d.ts +126 -0
- package/dist/index-Ck0EPdgu.d.ts.map +1 -0
- package/dist/index.js +121 -112
- package/dist/index.js.map +1 -1
- package/dist/{installer-Ci3Hf9it.js → installer-D0Obb2Md.js} +16 -6
- package/dist/installer-D0Obb2Md.js.map +1 -0
- package/dist/lint-zkXUULgO.js +137 -0
- package/dist/lint-zkXUULgO.js.map +1 -0
- package/dist/llms-CONd8Vnn.js +228 -0
- package/dist/llms-CONd8Vnn.js.map +1 -0
- package/dist/mcp-BT8BLZZ-.js +67 -0
- package/dist/mcp-BT8BLZZ-.js.map +1 -0
- package/dist/og-DRDOyQ_t.js +142 -0
- package/dist/og-DRDOyQ_t.js.map +1 -0
- package/dist/project/index.d.ts +2 -0
- package/dist/project/index.js +2 -0
- package/dist/project-DwRVTm4p.js +110 -0
- package/dist/project-DwRVTm4p.js.map +1 -0
- package/dist/registry/installer.d.ts +4 -5
- package/dist/registry/installer.d.ts.map +1 -1
- package/dist/registry/installer.js +1 -1
- package/dist/schema.json +1 -1
- package/dist/search-iphnSVVY.js +381 -0
- package/dist/search-iphnSVVY.js.map +1 -0
- package/dist/utils-B1Xy4l_a.d.ts +43 -0
- package/dist/utils-B1Xy4l_a.d.ts.map +1 -0
- package/dist/utils-DbMe7h6c.js +98 -0
- package/dist/utils-DbMe7h6c.js.map +1 -0
- package/dist/webmcp-C2PM_9md.js +135 -0
- package/dist/webmcp-C2PM_9md.js.map +1 -0
- package/package.json +27 -7
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/installer-Ci3Hf9it.js.map +0 -1
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { a as addReactRouterRoute, p as addImport } from "./codemod-auol-p4R.js";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
//#region src/features/utils.ts
|
|
5
|
+
/** the shallowest `.ts`/`.tsx` file under `dir` whose content includes `needle`, relative to `cwd` */
|
|
6
|
+
async function findSource(cwd, dir, needle, match = () => true) {
|
|
7
|
+
const entries = await fs.readdir(path.join(cwd, dir), {
|
|
8
|
+
recursive: true,
|
|
9
|
+
withFileTypes: true
|
|
10
|
+
}).catch(() => []);
|
|
11
|
+
const depth = (dir) => dir.split(path.sep).length;
|
|
12
|
+
entries.sort((a, b) => depth(a.parentPath) - depth(b.parentPath));
|
|
13
|
+
for (const entry of entries) {
|
|
14
|
+
if (!entry.isFile() || !/\.tsx?$/.test(entry.name) || entry.parentPath.includes("node_modules")) continue;
|
|
15
|
+
const file = path.join(entry.parentPath, entry.name);
|
|
16
|
+
if (!match(file)) continue;
|
|
17
|
+
if ((await fs.readFile(file, "utf-8")).includes(needle)) return path.relative(cwd, file);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function scripts(json, scripts) {
|
|
21
|
+
Object.assign(json.scripts ??= {}, scripts);
|
|
22
|
+
}
|
|
23
|
+
const reactOnly = (project) => project.framework !== "astro" || "Astro is not supported by this feature";
|
|
24
|
+
function reactFramework(project) {
|
|
25
|
+
if (project.framework === "astro") throw new Error("Astro is not supported by this feature");
|
|
26
|
+
return project.framework;
|
|
27
|
+
}
|
|
28
|
+
/** POSIX path for generated code and route configs */
|
|
29
|
+
const posix = (file) => file.split(path.sep).join("/");
|
|
30
|
+
/** add an export to a source file, unless a declaration with the same name exists */
|
|
31
|
+
async function addExport(ctx, file, name, code) {
|
|
32
|
+
const current = await fs.readFile(path.join(ctx.project.cwd, file), "utf-8").catch(() => "");
|
|
33
|
+
if (new RegExp(`export (async )?(const|function) ${name}\\b`).test(current)) return false;
|
|
34
|
+
await ctx.append(file, [code]);
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
/** add a page URL helper to `lib/shared.ts`, where the route constants live */
|
|
38
|
+
async function addPageUrl(ctx, name, code) {
|
|
39
|
+
const file = path.join(ctx.project.baseDir, "lib/shared.ts");
|
|
40
|
+
if (!await addExport(ctx, file, name, code)) return false;
|
|
41
|
+
await ctx.source(file, (source) => addImport(source, {
|
|
42
|
+
from: "fumadocs-core/source",
|
|
43
|
+
named: ["createGetUrl"]
|
|
44
|
+
}));
|
|
45
|
+
if (ctx.project.i18n) await ctx.source(file, (source) => addImport(source, {
|
|
46
|
+
from: "./i18n",
|
|
47
|
+
named: ["i18n"]
|
|
48
|
+
}));
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
/** the value of a route constant in `lib/shared.ts`, defined with `value` when missing */
|
|
52
|
+
async function sharedRoute(ctx, name, value) {
|
|
53
|
+
const file = path.join(ctx.project.baseDir, "lib/shared.ts");
|
|
54
|
+
const current = await fs.readFile(path.join(ctx.project.cwd, file), "utf-8").catch(() => "");
|
|
55
|
+
const existing = new RegExp(`export const ${name}\\s*=\\s*['"]([^'"]*)['"]`).exec(current)?.[1];
|
|
56
|
+
if (existing !== void 0) return existing;
|
|
57
|
+
const separator = current.trimEnd().endsWith(";") || current.length === 0 ? "" : "\n";
|
|
58
|
+
await ctx.append(file, [`${separator}export const ${name} = '${value}';`]);
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
/** add routes to `routes.ts`, or tell the user when the config isn't an array (e.g. file-based routes) */
|
|
62
|
+
async function registerReactRouterRoutes(ctx, routes) {
|
|
63
|
+
const { baseDir } = ctx.project;
|
|
64
|
+
const entries = routes.map((item) => typeof item === "string" ? item : {
|
|
65
|
+
path: item.path,
|
|
66
|
+
entry: item.file
|
|
67
|
+
});
|
|
68
|
+
let added = false;
|
|
69
|
+
if (!await ctx.source(path.join(baseDir, "routes.ts"), (file) => {
|
|
70
|
+
const first = entries[0];
|
|
71
|
+
if (file.code.includes(typeof first === "string" ? first : first.entry)) {
|
|
72
|
+
added = true;
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
added = addReactRouterRoute(file, entries);
|
|
76
|
+
}) || !added) {
|
|
77
|
+
const list = entries.map((item) => typeof item === "string" ? item : `route('${item.path}', '${item.entry}')`);
|
|
78
|
+
ctx.note(`Register the routes in your route config:\n ${list.join("\n ")}`);
|
|
79
|
+
}
|
|
80
|
+
return added;
|
|
81
|
+
}
|
|
82
|
+
/** features reading page Markdown need Fumadocs MDX collections, or a runtime source exposing `page.data.content` */
|
|
83
|
+
const requiresMarkdown = (project) => project.framework === "astro" ? "Astro is not supported by this feature" : project.source.collections !== null || project.source.dynamic || "requires Fumadocs MDX or a runtime content source";
|
|
84
|
+
function sourceRef(dynamic) {
|
|
85
|
+
return dynamic ? {
|
|
86
|
+
dynamic: true,
|
|
87
|
+
ref: "getSource",
|
|
88
|
+
resolved: "(await getSource())"
|
|
89
|
+
} : {
|
|
90
|
+
dynamic: false,
|
|
91
|
+
ref: "source",
|
|
92
|
+
resolved: "source"
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
96
|
+
export { reactFramework as a, requiresMarkdown as c, sourceRef as d, posix as i, scripts as l, addPageUrl as n, reactOnly as o, findSource as r, registerReactRouterRoutes as s, addExport as t, sharedRoute as u };
|
|
97
|
+
|
|
98
|
+
//# sourceMappingURL=utils-DbMe7h6c.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils-DbMe7h6c.js","names":[],"sources":["../src/features/utils.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { Project, ReactFramework } from '@/project';\nimport type { FormattedRoute } from '@/project/route';\nimport type { FeatureContext, PackageJson } from '@/features';\nimport { addImport, addReactRouterRoute } from '@/codemod';\n\n/** the shallowest `.ts`/`.tsx` file under `dir` whose content includes `needle`, relative to `cwd` */\nexport async function findSource(\n cwd: string,\n dir: string,\n needle: string,\n match: (file: string) => boolean = () => true,\n) {\n const entries = await fs\n .readdir(path.join(cwd, dir), { recursive: true, withFileTypes: true })\n .catch(() => []);\n const depth = (dir: string) => dir.split(path.sep).length;\n entries.sort((a, b) => depth(a.parentPath) - depth(b.parentPath));\n\n for (const entry of entries) {\n if (!entry.isFile() || !/\\.tsx?$/.test(entry.name) || entry.parentPath.includes('node_modules'))\n continue;\n const file = path.join(entry.parentPath, entry.name);\n if (!match(file)) continue;\n if ((await fs.readFile(file, 'utf-8')).includes(needle)) return path.relative(cwd, file);\n }\n}\n\nexport function scripts(json: PackageJson, scripts: Record<string, string>) {\n Object.assign((json.scripts ??= {}), scripts);\n}\n\nexport const reactOnly = (project: Project) =>\n project.framework !== 'astro' || 'Astro is not supported by this feature';\n\nexport function reactFramework(project: Project): ReactFramework {\n if (project.framework === 'astro') throw new Error('Astro is not supported by this feature');\n return project.framework;\n}\n\n/** POSIX path for generated code and route configs */\nexport const posix = (file: string) => file.split(path.sep).join('/');\n\n/** add an export to a source file, unless a declaration with the same name exists */\nexport async function addExport(ctx: FeatureContext, file: string, name: string, code: string) {\n const current = await fs.readFile(path.join(ctx.project.cwd, file), 'utf-8').catch(() => '');\n if (new RegExp(`export (async )?(const|function) ${name}\\\\b`).test(current)) return false;\n await ctx.append(file, [code]);\n return true;\n}\n\n/** add a page URL helper to `lib/shared.ts`, where the route constants live */\nexport async function addPageUrl(ctx: FeatureContext, name: string, code: string) {\n const file = path.join(ctx.project.baseDir, 'lib/shared.ts');\n if (!(await addExport(ctx, file, name, code))) return false;\n\n await ctx.source(file, (source) =>\n addImport(source, { from: 'fumadocs-core/source', named: ['createGetUrl'] }),\n );\n // a second pass, so the import lands after the one above instead of before it\n if (ctx.project.i18n) {\n await ctx.source(file, (source) => addImport(source, { from: './i18n', named: ['i18n'] }));\n }\n return true;\n}\n\n/** the value of a route constant in `lib/shared.ts`, defined with `value` when missing */\nexport async function sharedRoute(ctx: FeatureContext, name: string, value: string) {\n const file = path.join(ctx.project.baseDir, 'lib/shared.ts');\n const current = await fs.readFile(path.join(ctx.project.cwd, file), 'utf-8').catch(() => '');\n const existing = new RegExp(`export const ${name}\\\\s*=\\\\s*['\"]([^'\"]*)['\"]`).exec(current)?.[1];\n if (existing !== undefined) return existing;\n // keep a blank line after the declarations already in the file\n const separator = current.trimEnd().endsWith(';') || current.length === 0 ? '' : '\\n';\n await ctx.append(file, [`${separator}export const ${name} = '${value}';`]);\n return value;\n}\n\n/** add routes to `routes.ts`, or tell the user when the config isn't an array (e.g. file-based routes) */\nexport async function registerReactRouterRoutes(\n ctx: FeatureContext,\n routes: (FormattedRoute | string)[],\n): Promise<boolean> {\n const { baseDir } = ctx.project;\n const entries = routes.map((item) =>\n typeof item === 'string' ? item : { path: item.path, entry: item.file },\n );\n let added = false;\n const edited = await ctx.source(path.join(baseDir, 'routes.ts'), (file) => {\n const first = entries[0];\n if (file.code.includes(typeof first === 'string' ? first : first.entry)) {\n added = true;\n return;\n }\n added = addReactRouterRoute(file, entries);\n });\n\n if (!edited || !added) {\n const list = entries.map((item) =>\n typeof item === 'string' ? item : `route('${item.path}', '${item.entry}')`,\n );\n ctx.note(`Register the routes in your route config:\\n ${list.join('\\n ')}`);\n }\n return added;\n}\n\n/** features reading page Markdown need Fumadocs MDX collections, or a runtime source exposing `page.data.content` */\nexport const requiresMarkdown = (project: Project) =>\n project.framework === 'astro'\n ? 'Astro is not supported by this feature'\n : project.source.collections !== null ||\n project.source.dynamic ||\n 'requires Fumadocs MDX or a runtime content source';\n\n/** how generated code refers to the loader of `lib/source.ts` */\nexport interface SourceRef {\n /** resolved on demand, `lib/source.ts` exports `getSource()` instead of `source` */\n dynamic: boolean;\n /** passed to Fumadocs APIs, which accept a loader or a function resolving one */\n ref: string;\n /** reads the loader inside an async function */\n resolved: string;\n}\n\nexport function sourceRef(dynamic: boolean): SourceRef {\n return dynamic\n ? { dynamic: true, ref: 'getSource', resolved: '(await getSource())' }\n : { dynamic: false, ref: 'source', resolved: 'source' };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { b as findJsxElement, h as appendJsxChildren, p as addImport } from "./codemod-auol-p4R.js";
|
|
2
|
+
import { a as reactFramework, c as requiresMarkdown, r as findSource } from "./utils-DbMe7h6c.js";
|
|
3
|
+
import { r as llms } from "./llms-CONd8Vnn.js";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
//#region src/features/webmcp.ts
|
|
6
|
+
const component = ({ static: isStatic, i18n, tanstack }) => `'use client';
|
|
7
|
+
import { useEffect } from 'react';
|
|
8
|
+
import { useRouter } from 'fumadocs-core/framework';
|
|
9
|
+
import { ${isStatic ? "staticClient" : "fetchClient"} } from 'fumadocs-core/search/client/${isStatic ? "orama-static" : "fetch"}';
|
|
10
|
+
${i18n ? "import { useI18n } from 'fumadocs-ui/contexts/i18n';\n" : ""}import { ${tanstack ? "docsRoute" : "docsContentRoute, docsRoute"} } from '@/lib/shared';
|
|
11
|
+
|
|
12
|
+
interface ModelContextTool<Input> {
|
|
13
|
+
name: string;
|
|
14
|
+
description: string;
|
|
15
|
+
inputSchema?: object;
|
|
16
|
+
execute: (input: Input) => Promise<unknown>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
declare global {
|
|
20
|
+
interface Document {
|
|
21
|
+
/** WebMCP, see https://webmachinelearning.github.io/webmcp/ */
|
|
22
|
+
modelContext?: {
|
|
23
|
+
registerTool: <Input>(
|
|
24
|
+
tool: ModelContextTool<Input>,
|
|
25
|
+
options?: { signal?: AbortSignal },
|
|
26
|
+
) => Promise<void>;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** the Markdown URL of a page, e.g. ${tanstack ? "`/docs/page` -> `/docs/page.md`" : "`/docs/page` -> `/llms.mdx/docs/page/content.md`"} */
|
|
32
|
+
function markdownUrl(url: string) {
|
|
33
|
+
${tanstack ? " return url.endsWith(docsRoute) ? `${url}/index.md` : `${url}.md`;" : ` const start = url.indexOf(docsRoute);
|
|
34
|
+
const slugs = url.slice(start + docsRoute.length).replace(/^\\//, '');
|
|
35
|
+
return [url.slice(0, start) + docsContentRoute, slugs, 'content.md'].filter(Boolean).join('/');`}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** expose the docs to AI agents in the browser */
|
|
39
|
+
export function WebMCP() {
|
|
40
|
+
const router = useRouter();
|
|
41
|
+
${i18n ? " const { locale } = useI18n();\n" : ""}
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const context = document.modelContext;
|
|
44
|
+
if (!context) return;
|
|
45
|
+
const controller = new AbortController();
|
|
46
|
+
const options = { signal: controller.signal };
|
|
47
|
+
const client = ${isStatic ? "staticClient" : "fetchClient"}(${i18n ? "{ locale }" : ""});
|
|
48
|
+
|
|
49
|
+
void context.registerTool(
|
|
50
|
+
{
|
|
51
|
+
name: 'search_docs',
|
|
52
|
+
description: 'Search the docs, returns matching pages and headings with their URLs',
|
|
53
|
+
inputSchema: {
|
|
54
|
+
type: 'object',
|
|
55
|
+
properties: { query: { type: 'string' } },
|
|
56
|
+
required: ['query'],
|
|
57
|
+
},
|
|
58
|
+
async execute({ query }: { query: string }) {
|
|
59
|
+
return client.search(query);
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
options,
|
|
63
|
+
);
|
|
64
|
+
void context.registerTool(
|
|
65
|
+
{
|
|
66
|
+
name: 'read_page',
|
|
67
|
+
description: 'Read a docs page as Markdown by its URL',
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: { url: { type: 'string' } },
|
|
71
|
+
required: ['url'],
|
|
72
|
+
},
|
|
73
|
+
async execute({ url }: { url: string }) {
|
|
74
|
+
const res = await fetch(markdownUrl(url));
|
|
75
|
+
if (!res.ok) throw new Error(\`page not found: \${url}\`);
|
|
76
|
+
return res.text();
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
options,
|
|
80
|
+
);
|
|
81
|
+
void context.registerTool(
|
|
82
|
+
{
|
|
83
|
+
name: 'open_page',
|
|
84
|
+
description: 'Navigate to a docs page by its URL',
|
|
85
|
+
inputSchema: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
properties: { url: { type: 'string' } },
|
|
88
|
+
required: ['url'],
|
|
89
|
+
},
|
|
90
|
+
async execute({ url }: { url: string }) {
|
|
91
|
+
await router.push(url);
|
|
92
|
+
return \`opened \${url}\`;
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
options,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
return () => controller.abort();
|
|
99
|
+
}, [router${i18n ? ", locale" : ""}]);
|
|
100
|
+
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
104
|
+
const webmcp = {
|
|
105
|
+
id: "webmcp",
|
|
106
|
+
title: "WebMCP",
|
|
107
|
+
description: "expose tools to AI agents in the browser (experimental): search, read and open docs pages",
|
|
108
|
+
requires: [llms],
|
|
109
|
+
supports: requiresMarkdown,
|
|
110
|
+
async apply(ctx) {
|
|
111
|
+
const { cwd, baseDir, i18n } = ctx.project;
|
|
112
|
+
const framework = reactFramework(ctx.project);
|
|
113
|
+
await ctx.write(path.join(baseDir, "components/webmcp.tsx"), component({
|
|
114
|
+
static: ctx.project.static,
|
|
115
|
+
i18n: i18n !== null,
|
|
116
|
+
tanstack: framework === "tanstack-start"
|
|
117
|
+
}));
|
|
118
|
+
const provider = await findSource(cwd, baseDir, "<RootProvider");
|
|
119
|
+
if (!(provider !== void 0 && await ctx.source(provider, (file) => {
|
|
120
|
+
if (file.code.includes("<WebMCP")) return;
|
|
121
|
+
const element = findJsxElement(file, "RootProvider");
|
|
122
|
+
if (!element) return;
|
|
123
|
+
appendJsxChildren(file, element, "<WebMCP />");
|
|
124
|
+
addImport(file, {
|
|
125
|
+
from: "@/components/webmcp",
|
|
126
|
+
named: ["WebMCP"]
|
|
127
|
+
});
|
|
128
|
+
}))) ctx.note("Render `<WebMCP />` from `@/components/webmcp` inside `<RootProvider />`.");
|
|
129
|
+
ctx.note("WebMCP is experimental, test it with the `#enable-webmcp-testing` flag of Chrome. The `search_docs` tool uses the search API route, see https://fumadocs.dev/docs/integrations/llms#webmcp.");
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
//#endregion
|
|
133
|
+
export { webmcp as n, component as t };
|
|
134
|
+
|
|
135
|
+
//# sourceMappingURL=webmcp-C2PM_9md.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webmcp-C2PM_9md.js","names":[],"sources":["../src/features/webmcp.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Feature } from '@/features';\nimport { addImport, appendJsxChildren, findJsxElement } from '@/codemod';\nimport { llms } from './llms';\nimport { findSource, reactFramework, requiresMarkdown } from './utils';\n\ninterface Input {\n static: boolean;\n i18n: boolean;\n /** the Markdown route of TanStack Start is `/docs/*.md` */\n tanstack: boolean;\n}\n\nexport const component = ({ static: isStatic, i18n, tanstack }: Input) => `'use client';\nimport { useEffect } from 'react';\nimport { useRouter } from 'fumadocs-core/framework';\nimport { ${isStatic ? 'staticClient' : 'fetchClient'} } from 'fumadocs-core/search/client/${isStatic ? 'orama-static' : 'fetch'}';\n${i18n ? \"import { useI18n } from 'fumadocs-ui/contexts/i18n';\\n\" : ''}import { ${tanstack ? 'docsRoute' : 'docsContentRoute, docsRoute'} } from '@/lib/shared';\n\ninterface ModelContextTool<Input> {\n name: string;\n description: string;\n inputSchema?: object;\n execute: (input: Input) => Promise<unknown>;\n}\n\ndeclare global {\n interface Document {\n /** WebMCP, see https://webmachinelearning.github.io/webmcp/ */\n modelContext?: {\n registerTool: <Input>(\n tool: ModelContextTool<Input>,\n options?: { signal?: AbortSignal },\n ) => Promise<void>;\n };\n }\n}\n\n/** the Markdown URL of a page, e.g. ${tanstack ? '`/docs/page` -> `/docs/page.md`' : '`/docs/page` -> `/llms.mdx/docs/page/content.md`'} */\nfunction markdownUrl(url: string) {\n${\n tanstack\n ? ' return url.endsWith(docsRoute) ? `${url}/index.md` : `${url}.md`;'\n : ` const start = url.indexOf(docsRoute);\n const slugs = url.slice(start + docsRoute.length).replace(/^\\\\//, '');\n return [url.slice(0, start) + docsContentRoute, slugs, 'content.md'].filter(Boolean).join('/');`\n}\n}\n\n/** expose the docs to AI agents in the browser */\nexport function WebMCP() {\n const router = useRouter();\n${i18n ? ' const { locale } = useI18n();\\n' : ''}\n useEffect(() => {\n const context = document.modelContext;\n if (!context) return;\n const controller = new AbortController();\n const options = { signal: controller.signal };\n const client = ${isStatic ? 'staticClient' : 'fetchClient'}(${i18n ? '{ locale }' : ''});\n\n void context.registerTool(\n {\n name: 'search_docs',\n description: 'Search the docs, returns matching pages and headings with their URLs',\n inputSchema: {\n type: 'object',\n properties: { query: { type: 'string' } },\n required: ['query'],\n },\n async execute({ query }: { query: string }) {\n return client.search(query);\n },\n },\n options,\n );\n void context.registerTool(\n {\n name: 'read_page',\n description: 'Read a docs page as Markdown by its URL',\n inputSchema: {\n type: 'object',\n properties: { url: { type: 'string' } },\n required: ['url'],\n },\n async execute({ url }: { url: string }) {\n const res = await fetch(markdownUrl(url));\n if (!res.ok) throw new Error(\\`page not found: \\${url}\\`);\n return res.text();\n },\n },\n options,\n );\n void context.registerTool(\n {\n name: 'open_page',\n description: 'Navigate to a docs page by its URL',\n inputSchema: {\n type: 'object',\n properties: { url: { type: 'string' } },\n required: ['url'],\n },\n async execute({ url }: { url: string }) {\n await router.push(url);\n return \\`opened \\${url}\\`;\n },\n },\n options,\n );\n\n return () => controller.abort();\n }, [router${i18n ? ', locale' : ''}]);\n\n return null;\n}\n`;\n\nexport const webmcp: Feature = {\n id: 'webmcp',\n title: 'WebMCP',\n description:\n 'expose tools to AI agents in the browser (experimental): search, read and open docs pages',\n requires: [llms],\n supports: requiresMarkdown,\n async apply(ctx) {\n const { cwd, baseDir, i18n } = ctx.project;\n const framework = reactFramework(ctx.project);\n await ctx.write(\n path.join(baseDir, 'components/webmcp.tsx'),\n component({\n static: ctx.project.static,\n i18n: i18n !== null,\n tanstack: framework === 'tanstack-start',\n }),\n );\n\n const provider = await findSource(cwd, baseDir, '<RootProvider');\n const edited =\n provider !== undefined &&\n (await ctx.source(provider, (file) => {\n if (file.code.includes('<WebMCP')) return;\n const element = findJsxElement(file, 'RootProvider');\n if (!element) return;\n appendJsxChildren(file, element, '<WebMCP />');\n addImport(file, { from: '@/components/webmcp', named: ['WebMCP'] });\n }));\n if (!edited)\n ctx.note('Render `<WebMCP />` from `@/components/webmcp` inside `<RootProvider />`.');\n\n ctx.note(\n 'WebMCP is experimental, test it with the `#enable-webmcp-testing` flag of Chrome. The `search_docs` tool uses the search API route, see https://fumadocs.dev/docs/integrations/llms#webmcp.',\n );\n },\n};\n"],"mappings":";;;;;AAaA,MAAa,aAAa,EAAE,QAAQ,UAAU,MAAM,eAAsB;;;WAG/D,WAAW,iBAAiB,cAAc,uCAAuC,WAAW,iBAAiB,QAAQ;EAC9H,OAAO,2DAA2D,GAAG,WAAW,WAAW,cAAc,8BAA8B;;;;;;;;;;;;;;;;;;;;;uCAqBlG,WAAW,oCAAoC,mDAAmD;;EAGvI,WACI,wEACA;;mGAGL;;;;;;EAMC,OAAO,sCAAsC,GAAG;;;;;;qBAM7B,WAAW,iBAAiB,cAAc,GAAG,OAAO,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAoD7E,OAAO,aAAa,GAAG;;;;;AAMrC,MAAa,SAAkB;CAC7B,IAAI;CACJ,OAAO;CACP,aACE;CACF,UAAU,CAAC,IAAI;CACf,UAAU;CACV,MAAM,MAAM,KAAK;EACf,MAAM,EAAE,KAAK,SAAS,SAAS,IAAI;EACnC,MAAM,YAAY,eAAe,IAAI,OAAO;EAC5C,MAAM,IAAI,MACR,KAAK,KAAK,SAAS,uBAAuB,GAC1C,UAAU;GACR,QAAQ,IAAI,QAAQ;GACpB,MAAM,SAAS;GACf,UAAU,cAAc;EAC1B,CAAC,CACH;EAEA,MAAM,WAAW,MAAM,WAAW,KAAK,SAAS,eAAe;EAU/D,IAAI,EARF,aAAa,KAAA,KACZ,MAAM,IAAI,OAAO,WAAW,SAAS;GACpC,IAAI,KAAK,KAAK,SAAS,SAAS,GAAG;GACnC,MAAM,UAAU,eAAe,MAAM,cAAc;GACnD,IAAI,CAAC,SAAS;GACd,kBAAkB,MAAM,SAAS,YAAY;GAC7C,UAAU,MAAM;IAAE,MAAM;IAAuB,OAAO,CAAC,QAAQ;GAAE,CAAC;EACpE,CAAC,IAED,IAAI,KAAK,2EAA2E;EAEtF,IAAI,KACF,6LACF;CACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fumadocs/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "The CLI tool for Fumadocs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Docs",
|
|
@@ -21,7 +21,22 @@
|
|
|
21
21
|
],
|
|
22
22
|
"type": "module",
|
|
23
23
|
"exports": {
|
|
24
|
+
"./codemod": "./dist/codemod/index.js",
|
|
24
25
|
"./config": "./dist/config.js",
|
|
26
|
+
"./features": "./dist/features/index.js",
|
|
27
|
+
"./features/ai": "./dist/features/ai.js",
|
|
28
|
+
"./features/all": "./dist/features/all.js",
|
|
29
|
+
"./features/docs": "./dist/features/docs/index.js",
|
|
30
|
+
"./features/epub": "./dist/features/epub.js",
|
|
31
|
+
"./features/feedback": "./dist/features/feedback.js",
|
|
32
|
+
"./features/lint": "./dist/features/lint.js",
|
|
33
|
+
"./features/llms": "./dist/features/llms.js",
|
|
34
|
+
"./features/mcp": "./dist/features/mcp.js",
|
|
35
|
+
"./features/og": "./dist/features/og.js",
|
|
36
|
+
"./features/search": "./dist/features/search.js",
|
|
37
|
+
"./features/utils": "./dist/features/utils.js",
|
|
38
|
+
"./features/webmcp": "./dist/features/webmcp.js",
|
|
39
|
+
"./project": "./dist/project/index.js",
|
|
25
40
|
"./registry/installer": "./dist/registry/installer.js",
|
|
26
41
|
"./package.json": "./package.json"
|
|
27
42
|
},
|
|
@@ -29,18 +44,23 @@
|
|
|
29
44
|
"access": "public"
|
|
30
45
|
},
|
|
31
46
|
"dependencies": {
|
|
32
|
-
"@clack/prompts": "^1.
|
|
33
|
-
"
|
|
47
|
+
"@clack/prompts": "^1.8.0",
|
|
48
|
+
"cac": "^7.0.0",
|
|
34
49
|
"fuma-cli": "^0.1.1",
|
|
50
|
+
"magic-string": "^1.2.3",
|
|
51
|
+
"oxc-parser": "^0.149.0",
|
|
35
52
|
"picocolors": "^1.1.1",
|
|
36
|
-
"tinyexec": "^1.3.
|
|
37
|
-
"zod": "^4.4
|
|
53
|
+
"tinyexec": "^1.3.1",
|
|
54
|
+
"zod": "^4.5.4"
|
|
38
55
|
},
|
|
39
56
|
"devDependencies": {
|
|
40
|
-
"@types/node": "26.
|
|
41
|
-
"tsdown": "0.
|
|
57
|
+
"@types/node": "26.5.0",
|
|
58
|
+
"tsdown": "0.23.0",
|
|
42
59
|
"tsconfig": "0.0.0"
|
|
43
60
|
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=20"
|
|
63
|
+
},
|
|
44
64
|
"scripts": {
|
|
45
65
|
"build": "tsdown",
|
|
46
66
|
"clean": "rimraf dist",
|
package/dist/config.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","names":[],"sources":["../src/config.ts"],"mappings":";;cAMM;KACM,oBAAoB;iBAMV,mBAAmB,YAAmB,QAAA,EAAA;;;;;;;;;;;;;;;;;;GAyC3D,EAAA,KAAA;KAEI,eAAe,QAAQ,kBAAkB;KAElC,cAAc,EAAE,MAAM;KACtB,eAAe,EAAE,OAAO;iBAEd,mBAAmB,gBAAsB,QAAQ;;;;;;iBAejD,WAAW,gBAAsB,QAAQ;iBAezC,iBAAiB,eAAY"}
|
package/dist/config.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport { z } from 'zod';\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { detectFramework } from 'fuma-cli/detect';\n\nconst frameworks = ['next', 'astro', 'waku', 'react-router', 'tanstack-start'] as const;\nexport type Framework = (typeof frameworks)[number];\n\nfunction isSupportedFramework(v: string): v is Framework {\n return frameworks.includes(v as Framework);\n}\n\nexport async function createConfigSchema(cwd = process.cwd()) {\n const defaultAliases = {\n uiDir: './components/ui',\n componentsDir: './components',\n layoutDir: './layouts',\n cssDir: './styles',\n libDir: './lib',\n };\n\n let framework = await detectFramework(cwd);\n if (!isSupportedFramework(framework)) framework = 'next';\n\n return z.object({\n $schema: z.string().default('node_modules/@fumadocs/cli/dist/schema.json').optional(),\n aliases: z\n .object({\n uiDir: z.string().default(defaultAliases.uiDir),\n componentsDir: z.string().default(defaultAliases.uiDir),\n layoutDir: z.string().default(defaultAliases.layoutDir),\n cssDir: z.string().default(defaultAliases.componentsDir),\n libDir: z.string().default(defaultAliases.libDir),\n })\n .default(defaultAliases),\n\n baseDir: z.string().default(() => {\n if (framework === 'react-router' && existsSync(path.resolve(cwd, 'app'))) return 'app';\n if (existsSync(path.resolve(cwd, 'src'))) return 'src';\n return '';\n }),\n uiLibrary: z.enum(['radix-ui', 'base-ui']).default('base-ui'),\n framework: z.literal(frameworks).default(framework),\n\n commands: z\n .object({\n /**\n * command to format output code automatically\n */\n format: z.string().optional(),\n })\n .default({}),\n });\n}\n\ntype ConfigSchema = Awaited<ReturnType<typeof createConfigSchema>>;\n\nexport type ConfigInput = z.input<ConfigSchema>;\nexport type LoadedConfig = z.output<ConfigSchema>;\n\nexport async function createOrLoadConfig(file = './cli.json'): Promise<LoadedConfig> {\n const inited = await initConfig(file);\n if (inited) return inited;\n\n const content = await fs.readFile(file, 'utf-8');\n const configSchema = await createConfigSchema();\n\n return configSchema.parse(JSON.parse(content));\n}\n\n/**\n * Write new config, skip if a config already exists\n *\n * @returns the created config, `undefined` if not created\n */\nexport async function initConfig(file = './cli.json'): Promise<LoadedConfig | undefined> {\n if (\n await fs\n .stat(file)\n .then(() => true)\n .catch(() => false)\n ) {\n return;\n }\n\n const defaultConfig = await getDefaultConfig();\n await fs.writeFile(file, JSON.stringify(defaultConfig, null, 2));\n return defaultConfig;\n}\n\nexport async function getDefaultConfig(cwd?: string) {\n const schema = await createConfigSchema(cwd);\n return schema.parse({} satisfies ConfigInput);\n}\n"],"mappings":";;;;;;AAMA,MAAM,aAAa;CAAC;CAAQ;CAAS;CAAQ;CAAgB;AAAgB;AAG7E,SAAS,qBAAqB,GAA2B;CACvD,OAAO,WAAW,SAAS,CAAc;AAC3C;AAEA,eAAsB,mBAAmB,MAAM,QAAQ,IAAI,GAAG;CAC5D,MAAM,iBAAiB;EACrB,OAAO;EACP,eAAe;EACf,WAAW;EACX,QAAQ;EACR,QAAQ;CACV;CAEA,IAAI,YAAY,MAAM,gBAAgB,GAAG;CACzC,IAAI,CAAC,qBAAqB,SAAS,GAAG,YAAY;CAElD,OAAO,EAAE,OAAO;EACd,SAAS,EAAE,OAAO,CAAC,CAAC,QAAQ,6CAA6C,CAAC,CAAC,SAAS;EACpF,SAAS,EACN,OAAO;GACN,OAAO,EAAE,OAAO,CAAC,CAAC,QAAQ,eAAe,KAAK;GAC9C,eAAe,EAAE,OAAO,CAAC,CAAC,QAAQ,eAAe,KAAK;GACtD,WAAW,EAAE,OAAO,CAAC,CAAC,QAAQ,eAAe,SAAS;GACtD,QAAQ,EAAE,OAAO,CAAC,CAAC,QAAQ,eAAe,aAAa;GACvD,QAAQ,EAAE,OAAO,CAAC,CAAC,QAAQ,eAAe,MAAM;EAClD,CAAC,CAAC,CACD,QAAQ,cAAc;EAEzB,SAAS,EAAE,OAAO,CAAC,CAAC,cAAc;GAChC,IAAI,cAAc,kBAAkB,WAAW,KAAK,QAAQ,KAAK,KAAK,CAAC,GAAG,OAAO;GACjF,IAAI,WAAW,KAAK,QAAQ,KAAK,KAAK,CAAC,GAAG,OAAO;GACjD,OAAO;EACT,CAAC;EACD,WAAW,EAAE,KAAK,CAAC,YAAY,SAAS,CAAC,CAAC,CAAC,QAAQ,SAAS;EAC5D,WAAW,EAAE,QAAQ,UAAU,CAAC,CAAC,QAAQ,SAAS;EAElD,UAAU,EACP,OAAO;;;;AAIN,QAAQ,EAAE,OAAO,CAAC,CAAC,SAAS,EAC9B,CAAC,CAAC,CACD,QAAQ,CAAC,CAAC;CACf,CAAC;AACH;AAOA,eAAsB,mBAAmB,OAAO,cAAqC;CACnF,MAAM,SAAS,MAAM,WAAW,IAAI;CACpC,IAAI,QAAQ,OAAO;CAEnB,MAAM,UAAU,MAAM,GAAG,SAAS,MAAM,OAAO;CAG/C,QAAO,MAFoB,mBAAmB,EAAA,CAE1B,MAAM,KAAK,MAAM,OAAO,CAAC;AAC/C;;;;;;AAOA,eAAsB,WAAW,OAAO,cAAiD;CACvF,IACE,MAAM,GACH,KAAK,IAAI,CAAC,CACV,WAAW,IAAI,CAAC,CAChB,YAAY,KAAK,GAEpB;CAGF,MAAM,gBAAgB,MAAM,iBAAiB;CAC7C,MAAM,GAAG,UAAU,MAAM,KAAK,UAAU,eAAe,MAAM,CAAC,CAAC;CAC/D,OAAO;AACT;AAEA,eAAsB,iBAAiB,KAAc;CAEnD,QAAO,MADc,mBAAmB,GAAG,EAAA,CAC7B,MAAM,CAAC,CAAuB;AAC9C"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"installer-Ci3Hf9it.js","names":[],"sources":["../src/registry/plugins/preserve.ts","../src/registry/installer.ts"],"sourcesContent":["import type { InstallerPlugin } from 'fuma-cli/registry/installer';\n\n/**\n * keep references to `fumadocs-ui/layouts/*` components as original, unless the user is installing them directly.\n */\nexport function pluginPreserveLayouts(): InstallerPlugin {\n const layoutNames = [\n 'layouts/home',\n 'layouts/flux',\n 'layouts/glass',\n 'layouts/notebook',\n 'layouts/docs',\n 'layouts/shared',\n ];\n // original specifier -> new specifier\n const layoutComps: Record<string, string> = {\n 'local:<dir>/home/index.tsx': 'layouts/home',\n 'local:<dir>/shared/index.tsx': 'layouts/shared',\n 'local:<dir>/shared/client.tsx': 'layouts/shared',\n 'local:<dir>/notebook/index.tsx': 'layouts/notebook',\n 'local:<dir>/notebook/client.tsx': 'layouts/notebook',\n 'local:<dir>/notebook/page/index.tsx': 'layouts/notebook/page',\n 'local:<dir>/notebook/page/client.tsx': 'layouts/notebook/page',\n 'local:<dir>/docs/index.tsx': 'layouts/docs',\n 'local:<dir>/docs/client.tsx': 'layouts/docs',\n 'local:<dir>/docs/page/index.tsx': 'layouts/docs/page',\n 'local:<dir>/docs/page/client.tsx': 'layouts/docs/page',\n 'local:<dir>/flux/index.tsx': 'layouts/flux',\n 'local:<dir>/flux/page/index.tsx': 'layouts/flux/page',\n 'local:<dir>/flux/page/client.tsx': 'layouts/flux/page',\n 'local:<dir>/glass/index.tsx': 'layouts/glass',\n 'local:<dir>/glass/page/index.tsx': 'layouts/glass/page',\n };\n const layoutNameSet = new Set(layoutNames);\n\n return {\n beforeInstall(comp, { stack }) {\n const isDirectInstall = layoutNameSet.has(stack[0].name);\n if (isDirectInstall) return;\n\n return {\n ...comp,\n $subComponents: comp.$subComponents.filter((child) => !layoutNameSet.has(child.name)),\n };\n },\n transformImport(specifier, { stack }) {\n const isDirectInstall = layoutNameSet.has(stack[0].name);\n // skip if direct install or unrelated to layout component\n if (isDirectInstall || !(specifier in layoutComps)) return specifier;\n\n return `fumadocs-ui/${layoutComps[specifier]}`;\n },\n };\n}\n","import { ComponentInstaller } from 'fuma-cli/registry/installer';\nimport { pluginPreserveLayouts } from './plugins/preserve';\nimport { RegistryConnector } from 'fuma-cli/registry/connector';\nimport type { LoadedConfig } from '@/config';\nimport { box, confirm, isCancel, log, outro, spinner, SpinnerResult } from '@clack/prompts';\nimport picocolors from 'picocolors';\nimport { detectPackageManager } from 'fuma-cli/detect';\n\nexport class FumadocsComponentInstaller extends ComponentInstaller {\n private interactive: {\n name: string;\n spin: SpinnerResult;\n } | null = null;\n\n constructor(connector: RegistryConnector, config: LoadedConfig, cwd?: string) {\n super(connector, {\n cwd,\n framework: config.framework === 'astro' ? 'none' : config.framework,\n outDir: {\n base: config.baseDir,\n components: config.aliases.componentsDir,\n css: config.aliases.cssDir,\n layout: config.aliases.layoutDir,\n lib: config.aliases.libDir,\n ui: config.aliases.uiDir,\n },\n io: {\n onWarn: (message) => {\n this.interactive?.spin.message(message);\n },\n confirmFileOverride: async (options) => {\n if (!this.interactive) return true;\n const { name, spin } = this.interactive;\n spin.clear();\n const value = await confirm({\n message: `Do you want to override ${options.path}?`,\n initialValue: false,\n });\n if (isCancel(value)) {\n outro('Installation terminated');\n process.exit(0);\n }\n spin.start(picocolors.bold(picocolors.cyanBright(`Installing ${name}`)));\n return value;\n },\n onFileDownloaded: (options) => {\n this.interactive?.spin.message(options.path);\n },\n },\n plugins: [pluginPreserveLayouts()],\n });\n }\n\n async installInteractive(name: string, subRegistry?: string): Promise<void> {\n if (this.interactive) {\n throw new Error(`cannot install while installing another component`);\n }\n\n const spin = spinner();\n spin.start(picocolors.bold(picocolors.cyanBright(`Installing ${name}`)));\n\n try {\n this.interactive = { name, spin };\n const deps = await super.install(name, subRegistry).then((res) => res.deps());\n spin.stop(picocolors.bold(picocolors.greenBright(`${name} installed`)));\n\n if (deps.hasRequired()) {\n log.message();\n box([...deps.dependencies, ...deps.devDependencies].join('\\n'), 'New Dependencies');\n const pm = (await detectPackageManager())?.name ?? 'npm';\n const value = await confirm({\n message: `Do you want to install with ${pm}?`,\n });\n\n if (isCancel(value)) {\n outro('Installation terminated');\n process.exit(0);\n }\n\n if (value) {\n const spin = spinner({\n errorMessage: 'Failed to install dependencies',\n });\n spin.start('Installing dependencies');\n await deps.installRequired(pm);\n spin.stop('Dependencies installed');\n } else {\n await deps.writeRequired();\n }\n }\n } catch (e) {\n spin.error(e instanceof Error ? e.message : String(e));\n process.exit(-1);\n } finally {\n this.interactive = null;\n }\n }\n}\n"],"mappings":"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"}
|