@mcp-apps-kit/ui-react-builder
Advanced tools
| export{b as buildReactUI,a as buildReactUIs}from'./chunk-3YHDRPOV.js';import'./chunk-R7RJYHEX.js';//# sourceMappingURL=build-GBFL3B7J.js.map | ||
| //# sourceMappingURL=build-GBFL3B7J.js.map |
| {"version":3,"sources":[],"names":[],"mappings":"","file":"build-GBFL3B7J.js"} |
| import {b as b$1,a}from'./chunk-R7RJYHEX.js';import*as w from'esbuild';import*as u from'fs/promises';import*as p from'path';async function C(s,t={}){let e=Date.now(),n=new Map,o=new Map,i=[],l=[],r=t.cwd??process.cwd();t.outDir&&await u.mkdir(p.resolve(r,t.outDir),{recursive:true});let m;if(t.globalCss)try{m=await u.readFile(p.resolve(r,t.globalCss),"utf-8");}catch(a){i.push(`Could not load global CSS from ${t.globalCss}: ${a instanceof Error?a.message:String(a)}`);}for(let[a,d]of Object.entries(s))try{let c=await b(a,d,{...t,cwd:r,globalCss:m});if(n.set(a,c),t.outDir){let f=p.resolve(r,t.outDir,`${a}.html`);await u.writeFile(f,c,"utf-8"),o.set(a,f);}}catch(c){let f={key:a,message:c instanceof Error?c.message:String(c),stack:c instanceof Error?c.stack:void 0};l.push(f);}return {outputs:n,files:o,duration:Date.now()-e,warnings:i,errors:l}}async function b(s,t,e){let n=t.__component,o=n.name||"Component",i=t.__defaultProps,l=t.__autoResize,r=b$1({componentPath:"__COMPONENT_PLACEHOLDER__",defaultProps:i,autoResize:l}),m=[_(n,o),y()],a$1=await w.build({stdin:{contents:r,loader:"tsx",resolveDir:e.cwd,sourcefile:`${s}-entry.tsx`},bundle:true,write:false,format:"esm",platform:"browser",target:["es2020","chrome90","firefox90","safari14"],minify:e.minify??true,sourcemap:e.sourcemap?"inline":false,jsx:"automatic",jsxImportSource:"react",logLevel:"warning",external:e.external,plugins:m,define:{"process.env.NODE_ENV":e.minify?'"production"':'"development"'}}),d=a$1.outputFiles?.[0];if(!d)throw new Error(`No output generated for UI: ${s}`);let c=d.text;return a$1.warnings,a({key:s,name:t.name??s,script:c,css:e.globalCss})}function _(s,t){return {name:"mcp-component-resolver",setup(e){e.onResolve({filter:/__COMPONENT_PLACEHOLDER__/},()=>({path:"__COMPONENT__",namespace:"mcp-component"})),e.onLoad({filter:/.*/,namespace:"mcp-component"},()=>{let n=s.toString(),o=/^\s*class\b/.test(n),i=/^\s*function\b/.test(n),l=!o&&!i,r;return l?r=` | ||
| import React from "react"; | ||
| const ${t} = ${n}; | ||
| export default ${t}; | ||
| `:r=` | ||
| import React from "react"; | ||
| ${n} | ||
| export default ${t}; | ||
| `,{contents:r,loader:"tsx"}});}}}function y(){return {name:"mcp-css-handler",setup(s){s.onLoad({filter:/\.css$/},async t=>{if(t.path.endsWith(".module.css"))return null;let e=await u.readFile(t.path,"utf-8");return {contents:` | ||
| const style = document.createElement("style"); | ||
| style.textContent = ${JSON.stringify(e)}; | ||
| document.head.appendChild(style); | ||
| `,loader:"js"}}),s.onLoad({filter:/\.module\.css$/},async t=>{let e=await u.readFile(t.path,"utf-8"),n=E(e),o=Object.fromEntries(n.map(r=>[r,`${r}_${x(t.path)}`])),i=e;for(let[r,m]of Object.entries(o))i=i.replace(new RegExp(`\\.${r}\\b`,"g"),`.${m}`);return {contents:` | ||
| const style = document.createElement("style"); | ||
| style.textContent = ${JSON.stringify(i)}; | ||
| document.head.appendChild(style); | ||
| export default ${JSON.stringify(o)}; | ||
| `,loader:"js"}});}}}function E(s){let t=/\.([a-zA-Z_][a-zA-Z0-9_-]*)/g,e=new Set,n;for(;(n=t.exec(s))!==null;)n[1]&&e.add(n[1]);return Array.from(e)}function x(s){let t=0;for(let e=0;e<s.length;e++){let n=s.charCodeAt(e);t=(t<<5)-t+n,t=t&t;}return Math.abs(t).toString(36).substring(0,5)}async function O(s,t,e={}){let n=await C({[s]:t},e),o=n.outputs.get(s);if(!o){let i=n.errors.find(l=>l.key===s);throw new Error(i?.message??`Failed to build UI: ${s}`)}return o}export{C as a,O as b};//# sourceMappingURL=chunk-3YHDRPOV.js.map | ||
| //# sourceMappingURL=chunk-3YHDRPOV.js.map |
| {"version":3,"sources":["../src/build.ts"],"names":["buildReactUIs","uis","options","startTime","outputs","files","warnings","errors","cwd","globalCss","error","key","def","html","compileComponent","outPath","buildError","component","componentName","defaultProps","autoResize","entryPoint","generateEntryPoint","plugins","createComponentPlugin","createCSSPlugin","result","outputFile","script","generateHTML","build","componentSource","isClassComponent","isFunctionComponent","isArrowFunction","contents","args","css","classNames","extractClassNames","mapping","name","hashString","transformedCss","original","hashed","classRegex","classes","match","str","hash","i","char","buildReactUI","e"],"mappings":"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","file":"chunk-3YHDRPOV.js","sourcesContent":["/**\n * React UI build system using esbuild\n *\n * Compiles React components into self-contained HTML files that can be\n * served as MCP UI resources.\n */\n\nimport * as esbuild from \"esbuild\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type { ReactUIDef, BuildOptions, BuildResult, BuildError } from \"./types\";\nimport { generateHTML, generateEntryPoint } from \"./html\";\n\n/**\n * Build multiple React UIs into self-contained HTML files.\n *\n * This function takes a record of React UI definitions and compiles each one\n * into a complete HTML file that includes React, ReactDOM, @mcp-apps-kit/ui-react,\n * and the user's component code.\n *\n * **IMPORTANT: Limitations**\n *\n * This programmatic build function serializes components using `.toString()`, which has\n * significant limitations:\n *\n * - **No external imports**: Components cannot import other modules, hooks, or utilities\n * - **No closures**: Components that capture external variables will not work\n * - **Simple components only**: Best for self-contained components without dependencies\n *\n * For production use, prefer the **Vite plugin** (`mcpReactUI`) which uses file paths\n * for proper import resolution and supports:\n * - Full import/export resolution\n * - CSS imports and CSS modules\n * - All React hooks and utilities\n * - External component dependencies\n *\n * This function is primarily intended for:\n * - **Testing**: Unit and integration tests for the build pipeline\n * - **Simple widgets**: Self-contained components with no external imports\n * - **Prototyping**: Quick experimentation before setting up Vite\n *\n * @param uis - Record of UI keys to React UI definitions\n * @param options - Build configuration options\n * @returns Build result with compiled HTML and metadata\n *\n * @example\n * ```typescript\n * // For production, use the Vite plugin instead:\n * // vite.config.ts\n * import { mcpReactUI } from \"@mcp-apps-kit/ui-react-builder/vite\";\n *\n * export default defineConfig({\n * plugins: [mcpReactUI({ serverEntry: \"./src/index.ts\" })],\n * });\n *\n * // This programmatic API is for simple/test cases:\n * import { buildReactUIs, defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n *\n * // Only works with simple, self-contained components\n * const SimpleWidget = () => <div>Hello World</div>;\n *\n * const result = await buildReactUIs({\n * \"simple\": defineReactUI({\n * component: SimpleWidget,\n * name: \"Simple Widget\",\n * }),\n * });\n * ```\n */\nexport async function buildReactUIs(\n uis: Record<string, ReactUIDef>,\n options: BuildOptions = {}\n): Promise<BuildResult> {\n const startTime = Date.now();\n const outputs = new Map<string, string>();\n const files = new Map<string, string>();\n const warnings: string[] = [];\n const errors: BuildError[] = [];\n\n const cwd = options.cwd ?? process.cwd();\n\n // Create output directory if specified\n if (options.outDir) {\n await fs.mkdir(path.resolve(cwd, options.outDir), { recursive: true });\n }\n\n // Load global CSS if specified\n let globalCss: string | undefined;\n if (options.globalCss) {\n try {\n globalCss = await fs.readFile(path.resolve(cwd, options.globalCss), \"utf-8\");\n } catch (error) {\n warnings.push(\n `Could not load global CSS from ${options.globalCss}: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n\n // Build each UI\n for (const [key, def] of Object.entries(uis)) {\n try {\n const html = await compileComponent(key, def, {\n ...options,\n cwd,\n globalCss,\n });\n\n outputs.set(key, html);\n\n // Write to file if outDir specified\n if (options.outDir) {\n const outPath = path.resolve(cwd, options.outDir, `${key}.html`);\n await fs.writeFile(outPath, html, \"utf-8\");\n files.set(key, outPath);\n }\n } catch (error) {\n const buildError: BuildError = {\n key,\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n errors.push(buildError);\n }\n }\n\n return {\n outputs,\n files,\n duration: Date.now() - startTime,\n warnings,\n errors,\n };\n}\n\n/**\n * Compile a single React component to self-contained HTML.\n *\n * @param key - Unique key for this UI\n * @param def - React UI definition\n * @param options - Build options\n * @returns Complete HTML document as a string\n */\nasync function compileComponent(\n key: string,\n def: ReactUIDef,\n options: BuildOptions & { cwd: string; globalCss?: string }\n): Promise<string> {\n // Get component from internal properties\n const component = def.__component;\n const componentName = component.name || \"Component\";\n const defaultProps = def.__defaultProps;\n const autoResize = def.__autoResize;\n\n // Create the entry point using function serialization\n // Note: The Vite plugin uses file paths for proper import resolution\n // This build function falls back to function serialization (limited - doesn't capture imports)\n const entryPoint = generateEntryPoint({\n componentPath: `__COMPONENT_PLACEHOLDER__`,\n defaultProps,\n autoResize,\n });\n\n // Configure plugins\n const plugins: esbuild.Plugin[] = [\n createComponentPlugin(component, componentName),\n createCSSPlugin(),\n ];\n\n // Use esbuild to bundle everything\n const result = await esbuild.build({\n stdin: {\n contents: entryPoint,\n loader: \"tsx\",\n resolveDir: options.cwd,\n sourcefile: `${key}-entry.tsx`,\n },\n bundle: true,\n write: false,\n format: \"esm\",\n platform: \"browser\",\n target: [\"es2020\", \"chrome90\", \"firefox90\", \"safari14\"],\n minify: options.minify ?? true,\n sourcemap: options.sourcemap ? \"inline\" : false,\n jsx: \"automatic\",\n jsxImportSource: \"react\",\n logLevel: \"warning\",\n external: options.external,\n plugins,\n define: {\n \"process.env.NODE_ENV\": options.minify ? '\"production\"' : '\"development\"',\n },\n });\n\n // Get the bundled JavaScript\n const outputFile = result.outputFiles?.[0];\n if (!outputFile) {\n throw new Error(`No output generated for UI: ${key}`);\n }\n\n const script = outputFile.text;\n\n // Collect any warnings from esbuild (currently silent, could add a logger later)\n // Warnings are typically about missing exports, unused imports, etc.\n // For now we don't surface these to users\n void result.warnings;\n\n // Generate the final HTML\n const html = generateHTML({\n key,\n name: def.name ?? key,\n script,\n css: options.globalCss,\n });\n\n return html;\n}\n\n/**\n * Create an esbuild plugin that resolves the component placeholder.\n *\n * This plugin intercepts imports of the placeholder module and replaces\n * it with the actual component code. This is necessary because we receive\n * a function reference, not a file path.\n */\nfunction createComponentPlugin(\n component: { toString: () => string },\n componentName: string\n): esbuild.Plugin {\n return {\n name: \"mcp-component-resolver\",\n setup(build) {\n // Intercept the placeholder import\n build.onResolve({ filter: /__COMPONENT_PLACEHOLDER__/ }, () => {\n return {\n path: \"__COMPONENT__\",\n namespace: \"mcp-component\",\n };\n });\n\n // Provide the component code\n build.onLoad({ filter: /.*/, namespace: \"mcp-component\" }, () => {\n // Serialize the component to a module\n // For now, we export a placeholder that requires runtime injection\n // In a real implementation, we'd need the component's source path\n const componentSource = component.toString();\n\n // Detect component type: class, function, or arrow function\n const isClassComponent = /^\\s*class\\b/.test(componentSource);\n const isFunctionComponent = /^\\s*function\\b/.test(componentSource);\n const isArrowFunction = !isClassComponent && !isFunctionComponent;\n\n let contents: string;\n if (isArrowFunction) {\n contents = `\n import React from \"react\";\n const ${componentName} = ${componentSource};\n export default ${componentName};\n `;\n } else {\n contents = `\n import React from \"react\";\n ${componentSource}\n export default ${componentName};\n `;\n }\n\n return {\n contents,\n loader: \"tsx\",\n };\n });\n },\n };\n}\n\n/**\n * Create an esbuild plugin that handles CSS imports.\n *\n * This plugin transforms CSS imports into JavaScript that injects\n * the styles into the document head at runtime.\n */\nfunction createCSSPlugin(): esbuild.Plugin {\n return {\n name: \"mcp-css-handler\",\n setup(build) {\n // Handle .css imports (excluding .module.css which has its own handler)\n build.onLoad({ filter: /\\.css$/ }, async (args) => {\n // Skip .module.css files - they're handled by the CSS modules loader\n if (args.path.endsWith(\".module.css\")) {\n return null;\n }\n const css = await fs.readFile(args.path, \"utf-8\");\n\n // Create JavaScript that injects the CSS\n const contents = `\n const style = document.createElement(\"style\");\n style.textContent = ${JSON.stringify(css)};\n document.head.appendChild(style);\n `;\n\n return {\n contents,\n loader: \"js\",\n };\n });\n\n // Handle CSS modules (.module.css)\n build.onLoad({ filter: /\\.module\\.css$/ }, async (args) => {\n const css = await fs.readFile(args.path, \"utf-8\");\n\n // Generate a simple class name mapping\n // In a real implementation, we'd use a proper CSS modules processor\n const classNames = extractClassNames(css);\n const mapping = Object.fromEntries(\n classNames.map((name) => [name, `${name}_${hashString(args.path)}`])\n );\n\n // Transform the CSS with new class names\n let transformedCss = css;\n for (const [original, hashed] of Object.entries(mapping)) {\n transformedCss = transformedCss.replace(\n new RegExp(`\\\\.${original}\\\\b`, \"g\"),\n `.${hashed}`\n );\n }\n\n const contents = `\n const style = document.createElement(\"style\");\n style.textContent = ${JSON.stringify(transformedCss)};\n document.head.appendChild(style);\n export default ${JSON.stringify(mapping)};\n `;\n\n return {\n contents,\n loader: \"js\",\n };\n });\n },\n };\n}\n\n/**\n * Extract class names from CSS content.\n */\nfunction extractClassNames(css: string): string[] {\n const classRegex = /\\.([a-zA-Z_][a-zA-Z0-9_-]*)/g;\n const classes = new Set<string>();\n let match;\n while ((match = classRegex.exec(css)) !== null) {\n if (match[1]) {\n classes.add(match[1]);\n }\n }\n return Array.from(classes);\n}\n\n/**\n * Simple string hash for generating unique class names.\n */\nfunction hashString(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return Math.abs(hash).toString(36).substring(0, 5);\n}\n\n/**\n * Build a single React UI.\n *\n * Convenience function for building just one UI.\n *\n * @param key - Unique key for this UI\n * @param def - React UI definition\n * @param options - Build options\n * @returns The compiled HTML string\n *\n * @example\n * ```typescript\n * const html = await buildReactUI(\"my-widget\", defineReactUI({\n * component: MyWidget,\n * name: \"My Widget\",\n * }));\n * ```\n */\nexport async function buildReactUI(\n key: string,\n def: ReactUIDef,\n options: BuildOptions = {}\n): Promise<string> {\n const result = await buildReactUIs({ [key]: def }, options);\n\n const html = result.outputs.get(key);\n if (!html) {\n const error = result.errors.find((e) => e.key === key);\n throw new Error(error?.message ?? `Failed to build UI: ${key}`);\n }\n\n return html;\n}\n"]} |
| var a=` | ||
| *, | ||
| *::before, | ||
| *::after { | ||
| box-sizing: border-box; | ||
| margin: 0; | ||
| padding: 0; | ||
| } | ||
| html { | ||
| font-size: 16px; | ||
| -webkit-font-smoothing: antialiased; | ||
| -moz-osx-font-smoothing: grayscale; | ||
| } | ||
| body { | ||
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, | ||
| Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; | ||
| line-height: 1.5; | ||
| color: #1a1a1a; | ||
| background-color: transparent; | ||
| } | ||
| @media (prefers-color-scheme: dark) { | ||
| body { | ||
| color: #f5f5f5; | ||
| } | ||
| } | ||
| #root { | ||
| min-height: 100%; | ||
| } | ||
| `;function d(t){let{key:o,name:e,script:r,css:n}=t,i=n?`${a} | ||
| ${n}`:a;return `<!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <meta name="mcp-ui-key" content="${c(o)}"> | ||
| <title>${c(e)}</title> | ||
| <style>${i}</style> | ||
| </head> | ||
| <body> | ||
| <div id="root"></div> | ||
| <script type="module">${r.replace(/<\/script>/gi,"</script>")}</script> | ||
| </body> | ||
| </html>`}function g(t,o){let e=typeof t=="string"?{componentPath:t,defaultProps:o}:t,{componentPath:r,componentExport:n="default",defaultProps:i,autoResize:s}=e,p=i?JSON.stringify(i):"{}",m=n==="default"?`import Component from "${r}";`:`import { ${n} as Component } from "${r}";`,l=s===void 0?"":` autoResize={${s}}`;return ` | ||
| import React from "react"; | ||
| import { createRoot } from "react-dom/client"; | ||
| import { AppsProvider } from "@mcp-apps-kit/ui-react"; | ||
| ${m} | ||
| const rootElement = document.getElementById("root"); | ||
| if (rootElement) { | ||
| const root = createRoot(rootElement); | ||
| root.render( | ||
| <React.StrictMode> | ||
| <AppsProvider${l}> | ||
| <Component {...${p}} /> | ||
| </AppsProvider> | ||
| </React.StrictMode> | ||
| ); | ||
| } | ||
| `}function c(t){let o={"&":"&","<":"<",">":">",'"':""","'":"'"};return t.replace(/[&<>"']/g,e=>o[e]??e)}export{d as a,g as b};//# sourceMappingURL=chunk-R7RJYHEX.js.map | ||
| //# sourceMappingURL=chunk-R7RJYHEX.js.map |
| {"version":3,"sources":["../src/html.ts"],"names":["DEFAULT_BASE_CSS","generateHTML","options","key","name","script","css","combinedCss","escapeHtml","generateEntryPoint","componentPathOrOptions","defaultProps","componentPath","componentExport","props","autoResize","propsJson","importStatement","providerProps","text","htmlEntities","char"],"mappings":"AAUA,IAAMA,CAAAA,CAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA,CAAA,CA0DlB,SAASC,CAAAA,CAAaC,CAAAA,CAAkC,CAC7D,GAAM,CAAE,IAAAC,CAAAA,CAAK,IAAA,CAAAC,EAAM,MAAA,CAAAC,CAAAA,CAAQ,IAAAC,CAAI,CAAA,CAAIJ,EAG7BK,CAAAA,CAAcD,CAAAA,CAAM,GAAGN,CAAgB;AAAA,EAAKM,CAAG,CAAA,CAAA,CAAKN,CAAAA,CAE1D,OAAO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mCAAA,EAK4BQ,CAAAA,CAAWL,CAAG,CAAC,CAAA;AAAA,SAAA,EACzCK,CAAAA,CAAWJ,CAAI,CAAC,CAAA;AAAA,SAAA,EAChBG,CAAW,CAAA;AAAA;AAAA;AAAA;AAAA,wBAAA,EAIIF,CAAAA,CAAO,OAAA,CAAQ,cAAA,CAAgB,WAAgB,CAAC,CAAA;AAAA;AAAA,OAAA,CAG1E,CA8DO,SAASI,CAAAA,CACdC,CAAAA,CACAC,CAAAA,CACQ,CAER,IAAMT,CAAAA,CACJ,OAAOQ,CAAAA,EAA2B,QAAA,CAC9B,CAAE,cAAeA,CAAAA,CAAwB,YAAA,CAAAC,CAAa,CAAA,CACtDD,CAAAA,CAEA,CAAE,aAAA,CAAAE,CAAAA,CAAe,eAAA,CAAAC,CAAAA,CAAkB,SAAA,CAAW,YAAA,CAAcC,CAAAA,CAAO,UAAA,CAAAC,CAAW,CAAA,CAAIb,CAAAA,CAClFc,CAAAA,CAAYF,CAAAA,CAAQ,IAAA,CAAK,SAAA,CAAUA,CAAK,CAAA,CAAI,IAAA,CAG5CG,CAAAA,CACJJ,CAAAA,GAAoB,SAAA,CAChB,CAAA,uBAAA,EAA0BD,CAAa,KACvC,CAAA,SAAA,EAAYC,CAAe,CAAA,sBAAA,EAAyBD,CAAa,CAAA,EAAA,CAAA,CAGjEM,CAAAA,CAAgBH,CAAAA,GAAe,MAAA,CAAY,EAAA,CAAK,CAAA,aAAA,EAAgBA,CAAU,CAAA,CAAA,CAAA,CAEhF,OAAO;AAAA;AAAA;AAAA;AAAA,EAIPE,CAAe;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAA,EAOIC,CAAa,CAAA;AAAA,uBAAA,EACTF,CAAS,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAMlC,CAQA,SAASR,CAAAA,CAAWW,CAAAA,CAAsB,CACxC,IAAMC,CAAAA,CAAuC,CAC3C,GAAA,CAAK,OAAA,CACL,GAAA,CAAK,MAAA,CACL,GAAA,CAAK,MAAA,CACL,GAAA,CAAK,QAAA,CACL,GAAA,CAAK,OACP,CAAA,CAEA,OAAOD,CAAAA,CAAK,OAAA,CAAQ,UAAA,CAAaE,CAAAA,EAASD,CAAAA,CAAaC,CAAI,CAAA,EAAKA,CAAI,CACtE","file":"chunk-R7RJYHEX.js","sourcesContent":["/**\n * HTML template generation for React UIs\n */\n\nimport type { TemplateOptions } from \"./types\";\n\n/**\n * Default base CSS reset for all UIs.\n * Provides a consistent starting point across platforms.\n */\nconst DEFAULT_BASE_CSS = `\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n html {\n font-size: 16px;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen,\n Ubuntu, Cantarell, \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;\n line-height: 1.5;\n color: #1a1a1a;\n background-color: transparent;\n }\n\n @media (prefers-color-scheme: dark) {\n body {\n color: #f5f5f5;\n }\n }\n\n #root {\n min-height: 100%;\n }\n`;\n\n/**\n * Generate a self-contained HTML document for a React UI.\n *\n * The generated HTML includes:\n * - DOCTYPE and valid HTML5 structure\n * - Meta tags for responsive design\n * - Inlined CSS (base reset + optional custom CSS)\n * - Inlined JavaScript bundle with React app\n *\n * @param options - Template options with key, name, script, and optional CSS\n * @returns Complete HTML document as a string\n *\n * @example\n * ```typescript\n * const html = generateHTML({\n * key: \"restaurant-list\",\n * name: \"Restaurant List Widget\",\n * script: bundledJavaScript,\n * css: customStyles,\n * });\n * ```\n *\n * @internal\n */\nexport function generateHTML(options: TemplateOptions): string {\n const { key, name, script, css } = options;\n\n // Combine base CSS with any custom CSS\n const combinedCss = css ? `${DEFAULT_BASE_CSS}\\n${css}` : DEFAULT_BASE_CSS;\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"mcp-ui-key\" content=\"${escapeHtml(key)}\">\n <title>${escapeHtml(name)}</title>\n <style>${combinedCss}</style>\n</head>\n<body>\n <div id=\"root\"></div>\n <script type=\"module\">${script.replace(/<\\/script>/gi, \"</scr\" + \"ipt>\")}</script>\n</body>\n</html>`;\n}\n\n/**\n * Options for generating the React entry point.\n *\n * @internal\n */\nexport interface EntryPointOptions {\n /**\n * Path to the component file.\n */\n componentPath: string;\n\n /**\n * Name of the export to use.\n * Use \"default\" for default exports, or the actual name for named exports.\n * @default \"default\"\n */\n componentExport?: string;\n\n /**\n * Default props to pass to the component.\n */\n defaultProps?: Record<string, unknown>;\n\n /**\n * Whether to enable automatic size change notifications.\n * When undefined, uses the default (true).\n */\n autoResize?: boolean;\n}\n\n/**\n * Generate the React entry point code that will be bundled.\n *\n * This creates a small JavaScript module that:\n * 1. Imports React and ReactDOM\n * 2. Imports the user's component\n * 3. Imports AppsProvider from @mcp-apps-kit/ui-react\n * 4. Renders the component wrapped in providers\n *\n * @param componentPath - Path to the React component file (for backward compatibility)\n * @param defaultProps - Optional default props to pass to the component\n * @returns JavaScript/TypeScript source code for the entry point\n *\n * @example\n * ```typescript\n * // Default export\n * const entryCode = generateEntryPoint(\n * \"./src/widgets/MyWidget.tsx\",\n * { theme: \"dark\" }\n * );\n *\n * // Named export\n * const entryCode = generateEntryPoint({\n * componentPath: \"./src/widgets/MyWidget.tsx\",\n * componentExport: \"MyWidget\",\n * });\n * ```\n *\n * @internal\n */\nexport function generateEntryPoint(\n componentPathOrOptions: string | EntryPointOptions,\n defaultProps?: Record<string, unknown>\n): string {\n // Handle backward compatibility with string-only signature\n const options: EntryPointOptions =\n typeof componentPathOrOptions === \"string\"\n ? { componentPath: componentPathOrOptions, defaultProps }\n : componentPathOrOptions;\n\n const { componentPath, componentExport = \"default\", defaultProps: props, autoResize } = options;\n const propsJson = props ? JSON.stringify(props) : \"{}\";\n\n // Generate appropriate import statement based on export type\n const importStatement =\n componentExport === \"default\"\n ? `import Component from \"${componentPath}\";`\n : `import { ${componentExport} as Component } from \"${componentPath}\";`;\n\n // Generate AppsProvider props\n const providerProps = autoResize === undefined ? \"\" : ` autoResize={${autoResize}}`;\n\n return `\nimport React from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { AppsProvider } from \"@mcp-apps-kit/ui-react\";\n${importStatement}\n\nconst rootElement = document.getElementById(\"root\");\nif (rootElement) {\n const root = createRoot(rootElement);\n root.render(\n <React.StrictMode>\n <AppsProvider${providerProps}>\n <Component {...${propsJson}} />\n </AppsProvider>\n </React.StrictMode>\n );\n}\n`;\n}\n\n/**\n * Escape HTML special characters to prevent XSS.\n *\n * @param text - Text to escape\n * @returns Escaped text safe for HTML insertion\n */\nfunction escapeHtml(text: string): string {\n const htmlEntities: Record<string, string> = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n '\"': \""\",\n \"'\": \"'\",\n };\n\n return text.replace(/[&<>\"']/g, (char) => htmlEntities[char] ?? char);\n}\n\n/**\n * Extract CSS from a string that may contain both JS and CSS.\n * Used when esbuild bundles CSS as JavaScript that injects styles.\n *\n * @param code - Bundled code that may contain CSS injection\n * @returns Extracted CSS string or undefined\n */\nexport function extractInlineCSS(code: string): string | undefined {\n // esbuild injects CSS as: document.head.appendChild(...).textContent = \"css content\"\n const cssMatch = code.match(/\\.textContent\\s*=\\s*[\"'`]([\\s\\S]*?)[\"'`]\\s*[;,)]/);\n if (cssMatch?.[1]) {\n // Unescape the CSS string\n return cssMatch[1].replace(/\\\\n/g, \"\\n\").replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\");\n }\n return undefined;\n}\n"]} |
+16
-16
@@ -1,3 +0,3 @@ | ||
| 'use strict';var x=require('esbuild'),f=require('fs/promises'),d=require('path');function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var x__namespace=/*#__PURE__*/_interopNamespace(x);var f__namespace=/*#__PURE__*/_interopNamespace(f);var d__namespace=/*#__PURE__*/_interopNamespace(d);var S=Object.defineProperty;var D=(e,t)=>()=>(e&&(t=e(e=0)),t);var _=(e,t)=>{for(var r in t)S(e,r,{get:t[r],enumerable:true});};function h(e){let{key:t,name:r,script:n,css:o}=e,s=o?`${b} | ||
| ${o}`:b;return `<!DOCTYPE html> | ||
| 'use strict';var x=require('esbuild'),u=require('fs/promises'),g=require('path');function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var x__namespace=/*#__PURE__*/_interopNamespace(x);var u__namespace=/*#__PURE__*/_interopNamespace(u);var g__namespace=/*#__PURE__*/_interopNamespace(g);var _=Object.defineProperty;var b=(e,t)=>()=>(e&&(t=e(e=0)),t);var S=(e,t)=>{for(var r in t)_(e,r,{get:t[r],enumerable:true});};function R(e){let{key:t,name:r,script:o,css:n}=e,s=n?`${D} | ||
| ${n}`:D;return `<!DOCTYPE html> | ||
| <html lang="en"> | ||
@@ -13,9 +13,9 @@ <head> | ||
| <div id="root"></div> | ||
| <script type="module">${n.replace(/<\/script>/gi,"</script>")}</script> | ||
| <script type="module">${o.replace(/<\/script>/gi,"</script>")}</script> | ||
| </body> | ||
| </html>`}function R(e,t){let r=typeof e=="string"?{componentPath:e,defaultProps:t}:e,{componentPath:n,componentExport:o="default",defaultProps:s}=r,c=s?JSON.stringify(s):"{}";return ` | ||
| </html>`}function h(e,t){let r=typeof e=="string"?{componentPath:e,defaultProps:t}:e,{componentPath:o,componentExport:n="default",defaultProps:s,autoResize:a}=r,i=s?JSON.stringify(s):"{}",p=n==="default"?`import Component from "${o}";`:`import { ${n} as Component } from "${o}";`,c=a===void 0?"":` autoResize={${a}}`;return ` | ||
| import React from "react"; | ||
| import { createRoot } from "react-dom/client"; | ||
| import { AppsProvider } from "@mcp-apps-kit/ui-react"; | ||
| ${o==="default"?`import Component from "${n}";`:`import { ${o} as Component } from "${n}";`} | ||
| ${p} | ||
@@ -27,4 +27,4 @@ const rootElement = document.getElementById("root"); | ||
| <React.StrictMode> | ||
| <AppsProvider> | ||
| <Component {...${c}} /> | ||
| <AppsProvider${c}> | ||
| <Component {...${i}} /> | ||
| </AppsProvider> | ||
@@ -34,3 +34,3 @@ </React.StrictMode> | ||
| } | ||
| `}function w(e){let t={"&":"&","<":"<",">":">",'"':""","'":"'"};return e.replace(/[&<>"']/g,r=>t[r]??r)}var b,y=D(()=>{b=` | ||
| `}function w(e){let t={"&":"&","<":"<",">":">",'"':""","'":"'"};return e.replace(/[&<>"']/g,r=>t[r]??r)}var D,y=b(()=>{D=` | ||
| *, | ||
@@ -67,22 +67,22 @@ *::before, | ||
| } | ||
| `;});var C={};_(C,{buildReactUI:()=>E,buildReactUIs:()=>I});async function I(e,t={}){let r=Date.now(),n=new Map,o=new Map,s=[],c=[],i=t.cwd??process.cwd();t.outDir&&await f__namespace.mkdir(d__namespace.resolve(i,t.outDir),{recursive:true});let l;if(t.globalCss)try{l=await f__namespace.readFile(d__namespace.resolve(i,t.globalCss),"utf-8");}catch(a){s.push(`Could not load global CSS from ${t.globalCss}: ${a instanceof Error?a.message:String(a)}`);}for(let[a,g]of Object.entries(e))try{let p=await v(a,g,{...t,cwd:i,globalCss:l});if(n.set(a,p),t.outDir){let m=d__namespace.resolve(i,t.outDir,`${a}.html`);await f__namespace.writeFile(m,p,"utf-8"),o.set(a,m);}}catch(p){let m={key:a,message:p instanceof Error?p.message:String(p),stack:p instanceof Error?p.stack:void 0};c.push(m);}return {outputs:n,files:o,duration:Date.now()-r,warnings:s,errors:c}}async function v(e,t,r){let n=t.__component,o=n.name||"Component",s=t.__defaultProps,c=R("__COMPONENT_PLACEHOLDER__",s),i=[T(n,o),N()],l=await x__namespace.build({stdin:{contents:c,loader:"tsx",resolveDir:r.cwd,sourcefile:`${e}-entry.tsx`},bundle:true,write:false,format:"esm",platform:"browser",target:["es2020","chrome90","firefox90","safari14"],minify:r.minify??true,sourcemap:r.sourcemap?"inline":false,jsx:"automatic",jsxImportSource:"react",logLevel:"warning",external:r.external,plugins:i,define:{"process.env.NODE_ENV":r.minify?'"production"':'"development"'}}),a=l.outputFiles?.[0];if(!a)throw new Error(`No output generated for UI: ${e}`);let g=a.text;return l.warnings,h({key:e,name:t.name??e,script:g,css:r.globalCss})}function T(e,t){return {name:"mcp-component-resolver",setup(r){r.onResolve({filter:/__COMPONENT_PLACEHOLDER__/},()=>({path:"__COMPONENT__",namespace:"mcp-component"})),r.onLoad({filter:/.*/,namespace:"mcp-component"},()=>{let n=e.toString(),o=/^\s*class\b/.test(n),s=/^\s*function\b/.test(n),c=!o&&!s,i;return c?i=` | ||
| `;});var $={};S($,{buildReactUI:()=>E,buildReactUIs:()=>I});async function I(e,t={}){let r=Date.now(),o=new Map,n=new Map,s=[],a=[],i=t.cwd??process.cwd();t.outDir&&await u__namespace.mkdir(g__namespace.resolve(i,t.outDir),{recursive:true});let p;if(t.globalCss)try{p=await u__namespace.readFile(g__namespace.resolve(i,t.globalCss),"utf-8");}catch(c){s.push(`Could not load global CSS from ${t.globalCss}: ${c instanceof Error?c.message:String(c)}`);}for(let[c,m]of Object.entries(e))try{let l=await v(c,m,{...t,cwd:i,globalCss:p});if(o.set(c,l),t.outDir){let f=g__namespace.resolve(i,t.outDir,`${c}.html`);await u__namespace.writeFile(f,l,"utf-8"),n.set(c,f);}}catch(l){let f={key:c,message:l instanceof Error?l.message:String(l),stack:l instanceof Error?l.stack:void 0};a.push(f);}return {outputs:o,files:n,duration:Date.now()-r,warnings:s,errors:a}}async function v(e,t,r){let o=t.__component,n=o.name||"Component",s=t.__defaultProps,a=t.__autoResize,i=h({componentPath:"__COMPONENT_PLACEHOLDER__",defaultProps:s,autoResize:a}),p=[T(o,n),N()],c=await x__namespace.build({stdin:{contents:i,loader:"tsx",resolveDir:r.cwd,sourcefile:`${e}-entry.tsx`},bundle:true,write:false,format:"esm",platform:"browser",target:["es2020","chrome90","firefox90","safari14"],minify:r.minify??true,sourcemap:r.sourcemap?"inline":false,jsx:"automatic",jsxImportSource:"react",logLevel:"warning",external:r.external,plugins:p,define:{"process.env.NODE_ENV":r.minify?'"production"':'"development"'}}),m=c.outputFiles?.[0];if(!m)throw new Error(`No output generated for UI: ${e}`);let l=m.text;return c.warnings,R({key:e,name:t.name??e,script:l,css:r.globalCss})}function T(e,t){return {name:"mcp-component-resolver",setup(r){r.onResolve({filter:/__COMPONENT_PLACEHOLDER__/},()=>({path:"__COMPONENT__",namespace:"mcp-component"})),r.onLoad({filter:/.*/,namespace:"mcp-component"},()=>{let o=e.toString(),n=/^\s*class\b/.test(o),s=/^\s*function\b/.test(o),a=!n&&!s,i;return a?i=` | ||
| import React from "react"; | ||
| const ${t} = ${n}; | ||
| const ${t} = ${o}; | ||
| export default ${t}; | ||
| `:i=` | ||
| import React from "react"; | ||
| ${n} | ||
| ${o} | ||
| export default ${t}; | ||
| `,{contents:i,loader:"tsx"}});}}}function N(){return {name:"mcp-css-handler",setup(e){e.onLoad({filter:/\.css$/},async t=>{if(t.path.endsWith(".module.css"))return null;let r=await f__namespace.readFile(t.path,"utf-8");return {contents:` | ||
| `,{contents:i,loader:"tsx"}});}}}function N(){return {name:"mcp-css-handler",setup(e){e.onLoad({filter:/\.css$/},async t=>{if(t.path.endsWith(".module.css"))return null;let r=await u__namespace.readFile(t.path,"utf-8");return {contents:` | ||
| const style = document.createElement("style"); | ||
| style.textContent = ${JSON.stringify(r)}; | ||
| document.head.appendChild(style); | ||
| `,loader:"js"}}),e.onLoad({filter:/\.module\.css$/},async t=>{let r=await f__namespace.readFile(t.path,"utf-8"),n=k(r),o=Object.fromEntries(n.map(i=>[i,`${i}_${A(t.path)}`])),s=r;for(let[i,l]of Object.entries(o))s=s.replace(new RegExp(`\\.${i}\\b`,"g"),`.${l}`);return {contents:` | ||
| `,loader:"js"}}),e.onLoad({filter:/\.module\.css$/},async t=>{let r=await u__namespace.readFile(t.path,"utf-8"),o=k(r),n=Object.fromEntries(o.map(i=>[i,`${i}_${A(t.path)}`])),s=r;for(let[i,p]of Object.entries(n))s=s.replace(new RegExp(`\\.${i}\\b`,"g"),`.${p}`);return {contents:` | ||
| const style = document.createElement("style"); | ||
| style.textContent = ${JSON.stringify(s)}; | ||
| document.head.appendChild(style); | ||
| export default ${JSON.stringify(o)}; | ||
| `,loader:"js"}});}}}function k(e){let t=/\.([a-zA-Z_][a-zA-Z0-9_-]*)/g,r=new Set,n;for(;(n=t.exec(e))!==null;)n[1]&&r.add(n[1]);return Array.from(r)}function A(e){let t=0;for(let r=0;r<e.length;r++){let n=e.charCodeAt(r);t=(t<<5)-t+n,t=t&t;}return Math.abs(t).toString(36).substring(0,5)}async function E(e,t,r={}){let n=await I({[e]:t},r),o=n.outputs.get(e);if(!o){let s=n.errors.find(c=>c.key===e);throw new Error(s?.message??`Failed to build UI: ${e}`)}return o}var U=D(()=>{y();});function O(e){return e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}function B(e){let{component:t,defaultProps:r,outDir:n,...o}=e,s=t.name??"component",c=O(s),l=`${n??"./src/ui/dist"}/${c}.html`;return {...o,html:l,__reactUI:true,__component:t,__defaultProps:r}}function u(e){return typeof e=="object"&&e!==null&&"__reactUI"in e&&e.__reactUI}U();function $(e,t){let r={};for(let[n,o]of Object.entries(e))if(u(o)){let s=t.outputs.get(n);if(!s){let c=t.errors.find(i=>i.key===n);throw c?new Error(`Build failed for React UI "${n}": ${c.message}`):new Error(`No build output found for React UI "${n}". Did you forget to include it in buildReactUIs()?`)}r[n]={html:s,name:o.name,description:o.description,widgetDescription:o.widgetDescription,csp:o.csp,prefersBorder:o.prefersBorder,domain:o.domain};}else r[n]=o;return r}function F(e,t){return {html:t,name:e.name,description:e.description,widgetDescription:e.widgetDescription,csp:e.csp,prefersBorder:e.prefersBorder,domain:e.domain}}function P(e){let t={},r={};for(let[n,o]of Object.entries(e))u(o)?t[n]=o:r[n]=o;return {reactUIs:t,standardUIs:r}}async function M(e,t){let{buildReactUIs:r}=await Promise.resolve().then(()=>(U(),C)),{reactUIs:n,standardUIs:o}=P(e);if(Object.keys(n).length===0)return o;let s=await r(n,t);if(s.errors.length>0){let c=s.errors.map(i=>` - ${i.key}: ${i.message}`).join(` | ||
| export default ${JSON.stringify(n)}; | ||
| `,loader:"js"}});}}}function k(e){let t=/\.([a-zA-Z_][a-zA-Z0-9_-]*)/g,r=new Set,o;for(;(o=t.exec(e))!==null;)o[1]&&r.add(o[1]);return Array.from(r)}function A(e){let t=0;for(let r=0;r<e.length;r++){let o=e.charCodeAt(r);t=(t<<5)-t+o,t=t&t;}return Math.abs(t).toString(36).substring(0,5)}async function E(e,t,r={}){let o=await I({[e]:t},r),n=o.outputs.get(e);if(!n){let s=o.errors.find(a=>a.key===e);throw new Error(s?.message??`Failed to build UI: ${e}`)}return n}var U=b(()=>{y();});function O(e){return e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}function B(e){let{component:t,defaultProps:r,outDir:o,autoResize:n,...s}=e,a=t.name??"component",i=O(a),c=`${o??"./src/ui/dist"}/${i}.html`;return {...s,html:c,__reactUI:true,__component:t,__defaultProps:r,__autoResize:n??true}}function d(e){return typeof e=="object"&&e!==null&&"__reactUI"in e&&e.__reactUI}U();function C(e,t){let r={};for(let[o,n]of Object.entries(e))if(d(n)){let s=t.outputs.get(o);if(!s){let a=t.errors.find(i=>i.key===o);throw a?new Error(`Build failed for React UI "${o}": ${a.message}`):new Error(`No build output found for React UI "${o}". Did you forget to include it in buildReactUIs()?`)}r[o]={html:s,name:n.name,description:n.description,widgetDescription:n.widgetDescription,csp:n.csp,prefersBorder:n.prefersBorder,domain:n.domain};}else r[o]=n;return r}function F(e,t){return {html:t,name:e.name,description:e.description,widgetDescription:e.widgetDescription,csp:e.csp,prefersBorder:e.prefersBorder,domain:e.domain}}function P(e){let t={},r={};for(let[o,n]of Object.entries(e))d(n)?t[o]=n:r[o]=n;return {reactUIs:t,standardUIs:r}}async function M(e,t){let{buildReactUIs:r}=await Promise.resolve().then(()=>(U(),$)),{reactUIs:o,standardUIs:n}=P(e);if(Object.keys(o).length===0)return n;let s=await r(o,t);if(s.errors.length>0){let a=s.errors.map(i=>` - ${i.key}: ${i.message}`).join(` | ||
| `);throw new Error(`Failed to build some React UIs: | ||
| ${c}`)}return {...o,...$(n,s)}}y();exports.buildAndTransform=M;exports.buildReactUI=E;exports.buildReactUIs=I;exports.defineReactUI=B;exports.extractReactUIs=P;exports.generateEntryPoint=R;exports.generateHTML=h;exports.isReactUIDef=u;exports.transformSingleToCoreDef=F;exports.transformToCoreDefs=$;//# sourceMappingURL=index.cjs.map | ||
| ${a}`)}return {...n,...C(o,s)}}y();exports.buildAndTransform=M;exports.buildReactUI=E;exports.buildReactUIs=I;exports.defineReactUI=B;exports.extractReactUIs=P;exports.generateEntryPoint=h;exports.generateHTML=R;exports.isReactUIDef=d;exports.transformSingleToCoreDef=F;exports.transformToCoreDefs=C;//# sourceMappingURL=index.cjs.map | ||
| //# sourceMappingURL=index.cjs.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/html.ts","../src/build.ts","../src/define.ts","../src/index.ts","../src/transform.ts"],"names":["generateHTML","options","key","name","script","css","combinedCss","DEFAULT_BASE_CSS","escapeHtml","generateEntryPoint","componentPathOrOptions","defaultProps","componentPath","componentExport","props","propsJson","text","htmlEntities","char","init_html","__esmMin","build_exports","__export","buildReactUI","buildReactUIs","uis","startTime","outputs","files","warnings","errors","cwd","f","d","globalCss","error","def","html","compileComponent","outPath","buildError","component","componentName","entryPoint","plugins","createComponentPlugin","createCSSPlugin","result","x","outputFile","build","componentSource","isClassComponent","isFunctionComponent","isArrowFunction","contents","args","classNames","extractClassNames","mapping","hashString","transformedCss","original","hashed","classRegex","classes","match","str","hash","i","e","init_build","toKebabCase","defineReactUI","definition","outDir","rest","htmlPath","isReactUIDef","value","transformToCoreDefs","defs","buildResult","transformSingleToCoreDef","extractReactUIs","reactUIs","standardUIs","buildAndTransform","errorMessages"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * HTML template generation for React UIs\n */\n\nimport type { TemplateOptions } from \"./types\";\n\n/**\n * Default base CSS reset for all UIs.\n * Provides a consistent starting point across platforms.\n */\nconst DEFAULT_BASE_CSS = `\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n html {\n font-size: 16px;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen,\n Ubuntu, Cantarell, \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;\n line-height: 1.5;\n color: #1a1a1a;\n background-color: transparent;\n }\n\n @media (prefers-color-scheme: dark) {\n body {\n color: #f5f5f5;\n }\n }\n\n #root {\n min-height: 100%;\n }\n`;\n\n/**\n * Generate a self-contained HTML document for a React UI.\n *\n * The generated HTML includes:\n * - DOCTYPE and valid HTML5 structure\n * - Meta tags for responsive design\n * - Inlined CSS (base reset + optional custom CSS)\n * - Inlined JavaScript bundle with React app\n *\n * @param options - Template options with key, name, script, and optional CSS\n * @returns Complete HTML document as a string\n *\n * @example\n * ```typescript\n * const html = generateHTML({\n * key: \"restaurant-list\",\n * name: \"Restaurant List Widget\",\n * script: bundledJavaScript,\n * css: customStyles,\n * });\n * ```\n *\n * @internal\n */\nexport function generateHTML(options: TemplateOptions): string {\n const { key, name, script, css } = options;\n\n // Combine base CSS with any custom CSS\n const combinedCss = css ? `${DEFAULT_BASE_CSS}\\n${css}` : DEFAULT_BASE_CSS;\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"mcp-ui-key\" content=\"${escapeHtml(key)}\">\n <title>${escapeHtml(name)}</title>\n <style>${combinedCss}</style>\n</head>\n<body>\n <div id=\"root\"></div>\n <script type=\"module\">${script.replace(/<\\/script>/gi, \"</scr\" + \"ipt>\")}</script>\n</body>\n</html>`;\n}\n\n/**\n * Options for generating the React entry point.\n *\n * @internal\n */\nexport interface EntryPointOptions {\n /**\n * Path to the component file.\n */\n componentPath: string;\n\n /**\n * Name of the export to use.\n * Use \"default\" for default exports, or the actual name for named exports.\n * @default \"default\"\n */\n componentExport?: string;\n\n /**\n * Default props to pass to the component.\n */\n defaultProps?: Record<string, unknown>;\n}\n\n/**\n * Generate the React entry point code that will be bundled.\n *\n * This creates a small JavaScript module that:\n * 1. Imports React and ReactDOM\n * 2. Imports the user's component\n * 3. Imports AppsProvider from @mcp-apps-kit/ui-react\n * 4. Renders the component wrapped in providers\n *\n * @param componentPath - Path to the React component file (for backward compatibility)\n * @param defaultProps - Optional default props to pass to the component\n * @returns JavaScript/TypeScript source code for the entry point\n *\n * @example\n * ```typescript\n * // Default export\n * const entryCode = generateEntryPoint(\n * \"./src/widgets/MyWidget.tsx\",\n * { theme: \"dark\" }\n * );\n *\n * // Named export\n * const entryCode = generateEntryPoint({\n * componentPath: \"./src/widgets/MyWidget.tsx\",\n * componentExport: \"MyWidget\",\n * });\n * ```\n *\n * @internal\n */\nexport function generateEntryPoint(\n componentPathOrOptions: string | EntryPointOptions,\n defaultProps?: Record<string, unknown>\n): string {\n // Handle backward compatibility with string-only signature\n const options: EntryPointOptions =\n typeof componentPathOrOptions === \"string\"\n ? { componentPath: componentPathOrOptions, defaultProps }\n : componentPathOrOptions;\n\n const { componentPath, componentExport = \"default\", defaultProps: props } = options;\n const propsJson = props ? JSON.stringify(props) : \"{}\";\n\n // Generate appropriate import statement based on export type\n const importStatement =\n componentExport === \"default\"\n ? `import Component from \"${componentPath}\";`\n : `import { ${componentExport} as Component } from \"${componentPath}\";`;\n\n return `\nimport React from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { AppsProvider } from \"@mcp-apps-kit/ui-react\";\n${importStatement}\n\nconst rootElement = document.getElementById(\"root\");\nif (rootElement) {\n const root = createRoot(rootElement);\n root.render(\n <React.StrictMode>\n <AppsProvider>\n <Component {...${propsJson}} />\n </AppsProvider>\n </React.StrictMode>\n );\n}\n`;\n}\n\n/**\n * Escape HTML special characters to prevent XSS.\n *\n * @param text - Text to escape\n * @returns Escaped text safe for HTML insertion\n */\nfunction escapeHtml(text: string): string {\n const htmlEntities: Record<string, string> = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n '\"': \""\",\n \"'\": \"'\",\n };\n\n return text.replace(/[&<>\"']/g, (char) => htmlEntities[char] ?? char);\n}\n\n/**\n * Extract CSS from a string that may contain both JS and CSS.\n * Used when esbuild bundles CSS as JavaScript that injects styles.\n *\n * @param code - Bundled code that may contain CSS injection\n * @returns Extracted CSS string or undefined\n */\nexport function extractInlineCSS(code: string): string | undefined {\n // esbuild injects CSS as: document.head.appendChild(...).textContent = \"css content\"\n const cssMatch = code.match(/\\.textContent\\s*=\\s*[\"'`]([\\s\\S]*?)[\"'`]\\s*[;,)]/);\n if (cssMatch?.[1]) {\n // Unescape the CSS string\n return cssMatch[1].replace(/\\\\n/g, \"\\n\").replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\");\n }\n return undefined;\n}\n","/**\n * React UI build system using esbuild\n *\n * Compiles React components into self-contained HTML files that can be\n * served as MCP UI resources.\n */\n\nimport * as esbuild from \"esbuild\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type { ReactUIDef, BuildOptions, BuildResult, BuildError } from \"./types\";\nimport { generateHTML, generateEntryPoint } from \"./html\";\n\n/**\n * Build multiple React UIs into self-contained HTML files.\n *\n * This function takes a record of React UI definitions and compiles each one\n * into a complete HTML file that includes React, ReactDOM, @mcp-apps-kit/ui-react,\n * and the user's component code.\n *\n * **IMPORTANT: Limitations**\n *\n * This programmatic build function serializes components using `.toString()`, which has\n * significant limitations:\n *\n * - **No external imports**: Components cannot import other modules, hooks, or utilities\n * - **No closures**: Components that capture external variables will not work\n * - **Simple components only**: Best for self-contained components without dependencies\n *\n * For production use, prefer the **Vite plugin** (`mcpReactUI`) which uses file paths\n * for proper import resolution and supports:\n * - Full import/export resolution\n * - CSS imports and CSS modules\n * - All React hooks and utilities\n * - External component dependencies\n *\n * This function is primarily intended for:\n * - **Testing**: Unit and integration tests for the build pipeline\n * - **Simple widgets**: Self-contained components with no external imports\n * - **Prototyping**: Quick experimentation before setting up Vite\n *\n * @param uis - Record of UI keys to React UI definitions\n * @param options - Build configuration options\n * @returns Build result with compiled HTML and metadata\n *\n * @example\n * ```typescript\n * // For production, use the Vite plugin instead:\n * // vite.config.ts\n * import { mcpReactUI } from \"@mcp-apps-kit/ui-react-builder/vite\";\n *\n * export default defineConfig({\n * plugins: [mcpReactUI({ serverEntry: \"./src/index.ts\" })],\n * });\n *\n * // This programmatic API is for simple/test cases:\n * import { buildReactUIs, defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n *\n * // Only works with simple, self-contained components\n * const SimpleWidget = () => <div>Hello World</div>;\n *\n * const result = await buildReactUIs({\n * \"simple\": defineReactUI({\n * component: SimpleWidget,\n * name: \"Simple Widget\",\n * }),\n * });\n * ```\n */\nexport async function buildReactUIs(\n uis: Record<string, ReactUIDef>,\n options: BuildOptions = {}\n): Promise<BuildResult> {\n const startTime = Date.now();\n const outputs = new Map<string, string>();\n const files = new Map<string, string>();\n const warnings: string[] = [];\n const errors: BuildError[] = [];\n\n const cwd = options.cwd ?? process.cwd();\n\n // Create output directory if specified\n if (options.outDir) {\n await fs.mkdir(path.resolve(cwd, options.outDir), { recursive: true });\n }\n\n // Load global CSS if specified\n let globalCss: string | undefined;\n if (options.globalCss) {\n try {\n globalCss = await fs.readFile(path.resolve(cwd, options.globalCss), \"utf-8\");\n } catch (error) {\n warnings.push(\n `Could not load global CSS from ${options.globalCss}: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n\n // Build each UI\n for (const [key, def] of Object.entries(uis)) {\n try {\n const html = await compileComponent(key, def, {\n ...options,\n cwd,\n globalCss,\n });\n\n outputs.set(key, html);\n\n // Write to file if outDir specified\n if (options.outDir) {\n const outPath = path.resolve(cwd, options.outDir, `${key}.html`);\n await fs.writeFile(outPath, html, \"utf-8\");\n files.set(key, outPath);\n }\n } catch (error) {\n const buildError: BuildError = {\n key,\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n errors.push(buildError);\n }\n }\n\n return {\n outputs,\n files,\n duration: Date.now() - startTime,\n warnings,\n errors,\n };\n}\n\n/**\n * Compile a single React component to self-contained HTML.\n *\n * @param key - Unique key for this UI\n * @param def - React UI definition\n * @param options - Build options\n * @returns Complete HTML document as a string\n */\nasync function compileComponent(\n key: string,\n def: ReactUIDef,\n options: BuildOptions & { cwd: string; globalCss?: string }\n): Promise<string> {\n // Get component from internal properties\n const component = def.__component;\n const componentName = component.name || \"Component\";\n const defaultProps = def.__defaultProps;\n\n // Create the entry point using function serialization\n // Note: The Vite plugin uses file paths for proper import resolution\n // This build function falls back to function serialization (limited - doesn't capture imports)\n const entryPoint = generateEntryPoint(`__COMPONENT_PLACEHOLDER__`, defaultProps);\n\n // Configure plugins\n const plugins: esbuild.Plugin[] = [\n createComponentPlugin(component, componentName),\n createCSSPlugin(),\n ];\n\n // Use esbuild to bundle everything\n const result = await esbuild.build({\n stdin: {\n contents: entryPoint,\n loader: \"tsx\",\n resolveDir: options.cwd,\n sourcefile: `${key}-entry.tsx`,\n },\n bundle: true,\n write: false,\n format: \"esm\",\n platform: \"browser\",\n target: [\"es2020\", \"chrome90\", \"firefox90\", \"safari14\"],\n minify: options.minify ?? true,\n sourcemap: options.sourcemap ? \"inline\" : false,\n jsx: \"automatic\",\n jsxImportSource: \"react\",\n logLevel: \"warning\",\n external: options.external,\n plugins,\n define: {\n \"process.env.NODE_ENV\": options.minify ? '\"production\"' : '\"development\"',\n },\n });\n\n // Get the bundled JavaScript\n const outputFile = result.outputFiles?.[0];\n if (!outputFile) {\n throw new Error(`No output generated for UI: ${key}`);\n }\n\n const script = outputFile.text;\n\n // Collect any warnings from esbuild (currently silent, could add a logger later)\n // Warnings are typically about missing exports, unused imports, etc.\n // For now we don't surface these to users\n void result.warnings;\n\n // Generate the final HTML\n const html = generateHTML({\n key,\n name: def.name ?? key,\n script,\n css: options.globalCss,\n });\n\n return html;\n}\n\n/**\n * Create an esbuild plugin that resolves the component placeholder.\n *\n * This plugin intercepts imports of the placeholder module and replaces\n * it with the actual component code. This is necessary because we receive\n * a function reference, not a file path.\n */\nfunction createComponentPlugin(\n component: { toString: () => string },\n componentName: string\n): esbuild.Plugin {\n return {\n name: \"mcp-component-resolver\",\n setup(build) {\n // Intercept the placeholder import\n build.onResolve({ filter: /__COMPONENT_PLACEHOLDER__/ }, () => {\n return {\n path: \"__COMPONENT__\",\n namespace: \"mcp-component\",\n };\n });\n\n // Provide the component code\n build.onLoad({ filter: /.*/, namespace: \"mcp-component\" }, () => {\n // Serialize the component to a module\n // For now, we export a placeholder that requires runtime injection\n // In a real implementation, we'd need the component's source path\n const componentSource = component.toString();\n\n // Detect component type: class, function, or arrow function\n const isClassComponent = /^\\s*class\\b/.test(componentSource);\n const isFunctionComponent = /^\\s*function\\b/.test(componentSource);\n const isArrowFunction = !isClassComponent && !isFunctionComponent;\n\n let contents: string;\n if (isArrowFunction) {\n contents = `\n import React from \"react\";\n const ${componentName} = ${componentSource};\n export default ${componentName};\n `;\n } else {\n contents = `\n import React from \"react\";\n ${componentSource}\n export default ${componentName};\n `;\n }\n\n return {\n contents,\n loader: \"tsx\",\n };\n });\n },\n };\n}\n\n/**\n * Create an esbuild plugin that handles CSS imports.\n *\n * This plugin transforms CSS imports into JavaScript that injects\n * the styles into the document head at runtime.\n */\nfunction createCSSPlugin(): esbuild.Plugin {\n return {\n name: \"mcp-css-handler\",\n setup(build) {\n // Handle .css imports (excluding .module.css which has its own handler)\n build.onLoad({ filter: /\\.css$/ }, async (args) => {\n // Skip .module.css files - they're handled by the CSS modules loader\n if (args.path.endsWith(\".module.css\")) {\n return null;\n }\n const css = await fs.readFile(args.path, \"utf-8\");\n\n // Create JavaScript that injects the CSS\n const contents = `\n const style = document.createElement(\"style\");\n style.textContent = ${JSON.stringify(css)};\n document.head.appendChild(style);\n `;\n\n return {\n contents,\n loader: \"js\",\n };\n });\n\n // Handle CSS modules (.module.css)\n build.onLoad({ filter: /\\.module\\.css$/ }, async (args) => {\n const css = await fs.readFile(args.path, \"utf-8\");\n\n // Generate a simple class name mapping\n // In a real implementation, we'd use a proper CSS modules processor\n const classNames = extractClassNames(css);\n const mapping = Object.fromEntries(\n classNames.map((name) => [name, `${name}_${hashString(args.path)}`])\n );\n\n // Transform the CSS with new class names\n let transformedCss = css;\n for (const [original, hashed] of Object.entries(mapping)) {\n transformedCss = transformedCss.replace(\n new RegExp(`\\\\.${original}\\\\b`, \"g\"),\n `.${hashed}`\n );\n }\n\n const contents = `\n const style = document.createElement(\"style\");\n style.textContent = ${JSON.stringify(transformedCss)};\n document.head.appendChild(style);\n export default ${JSON.stringify(mapping)};\n `;\n\n return {\n contents,\n loader: \"js\",\n };\n });\n },\n };\n}\n\n/**\n * Extract class names from CSS content.\n */\nfunction extractClassNames(css: string): string[] {\n const classRegex = /\\.([a-zA-Z_][a-zA-Z0-9_-]*)/g;\n const classes = new Set<string>();\n let match;\n while ((match = classRegex.exec(css)) !== null) {\n if (match[1]) {\n classes.add(match[1]);\n }\n }\n return Array.from(classes);\n}\n\n/**\n * Simple string hash for generating unique class names.\n */\nfunction hashString(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return Math.abs(hash).toString(36).substring(0, 5);\n}\n\n/**\n * Build a single React UI.\n *\n * Convenience function for building just one UI.\n *\n * @param key - Unique key for this UI\n * @param def - React UI definition\n * @param options - Build options\n * @returns The compiled HTML string\n *\n * @example\n * ```typescript\n * const html = await buildReactUI(\"my-widget\", defineReactUI({\n * component: MyWidget,\n * name: \"My Widget\",\n * }));\n * ```\n */\nexport async function buildReactUI(\n key: string,\n def: ReactUIDef,\n options: BuildOptions = {}\n): Promise<string> {\n const result = await buildReactUIs({ [key]: def }, options);\n\n const html = result.outputs.get(key);\n if (!html) {\n const error = result.errors.find((e) => e.key === key);\n throw new Error(error?.message ?? `Failed to build UI: ${key}`);\n }\n\n return html;\n}\n","/**\n * Helper functions for defining React-based UIs\n */\n\nimport type { ReactUIInput, ReactUIDef } from \"./types\";\n\n/**\n * Convert component name to kebab-case for filename.\n * @internal\n */\nfunction toKebabCase(name: string): string {\n return name.replace(/([a-z])([A-Z])/g, \"$1-$2\").toLowerCase();\n}\n\n/**\n * Define a UI using a React component.\n *\n * This helper creates a UIDef with an auto-generated HTML path based on\n * the component name. The Vite plugin discovers these definitions and\n * builds the React components into self-contained HTML files.\n *\n * @param definition - React UI input with component and metadata\n * @returns A UIDef with auto-generated html path and React metadata\n *\n * @example Basic usage\n * ```typescript\n * import { defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { MyWidget } from \"./widgets/MyWidget\";\n *\n * const widgetUI = defineReactUI({\n * component: MyWidget,\n * name: \"My Widget\",\n * prefersBorder: true,\n * });\n * // Returns: { html: \"./src/ui/dist/my-widget.html\", name: \"My Widget\", ... }\n * ```\n *\n * @example With tool definition\n * ```typescript\n * import { createApp, defineTool } from \"@mcp-apps-kit/core\";\n * import { defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { RestaurantList } from \"./widgets/RestaurantList\";\n *\n * const app = createApp({\n * name: \"restaurant-finder\",\n * version: \"1.0.0\",\n * tools: {\n * search: defineTool({\n * description: \"Search for restaurants\",\n * input: z.object({ query: z.string() }),\n * output: z.object({ restaurants: z.array(RestaurantSchema) }),\n * ui: defineReactUI({\n * component: RestaurantList,\n * name: \"Restaurant List\",\n * }),\n * handler: async (input) => {\n * // ... search logic\n * },\n * }),\n * },\n * });\n * ```\n *\n * @example Custom output directory\n * ```typescript\n * const widgetUI = defineReactUI({\n * component: ConfigurableWidget,\n * name: \"Configurable Widget\",\n * outDir: \"./dist/ui\",\n * });\n * // Returns: { html: \"./dist/ui/configurable-widget.html\", ... }\n * ```\n */\nexport function defineReactUI(definition: ReactUIInput): ReactUIDef {\n const { component, defaultProps, outDir, ...rest } = definition;\n\n // Generate the output path from component name\n const componentName = component.name ?? \"component\";\n const key = toKebabCase(componentName);\n const outputDir = outDir ?? \"./src/ui/dist\";\n const htmlPath = `${outputDir}/${key}.html`;\n\n return {\n ...rest,\n html: htmlPath,\n __reactUI: true,\n __component: component,\n __defaultProps: defaultProps,\n };\n}\n\n/**\n * Check if a value is a React UI definition.\n *\n * This type guard is used by the build system to identify\n * React UIs that need to be compiled to HTML.\n *\n * @param value - Value to check\n * @returns True if the value is a ReactUIDef\n *\n * @example\n * ```typescript\n * import { isReactUIDef } from \"@mcp-apps-kit/ui-react-builder\";\n *\n * const ui = someUnknownUI;\n * if (isReactUIDef(ui)) {\n * // ui.__component is available here\n * console.log(\"React UI:\", ui.__component.name);\n * }\n * ```\n *\n * @internal\n */\nexport function isReactUIDef(value: unknown): value is ReactUIDef {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"__reactUI\" in value &&\n (value as ReactUIDef).__reactUI\n );\n}\n","/**\n * @mcp-apps-kit/ui-react-builder\n *\n * Build tool for React-based MCP application UIs.\n *\n * This package allows you to define UI resources using React components\n * instead of pre-built HTML files. The framework handles bundling React,\n * ReactDOM, and @mcp-apps-kit/ui-react into self-contained HTML that works\n * with both MCP Apps (Claude Desktop) and ChatGPT.\n *\n * @example Basic usage\n * ```typescript\n * import { defineReactUI, buildReactUIs } from \"@mcp-apps-kit/ui-react-builder\";\n * import { MyWidget } from \"./widgets/MyWidget\";\n *\n * // Define your UI with a React component\n * const widgetUI = defineReactUI({\n * component: MyWidget,\n * name: \"My Widget\",\n * prefersBorder: true,\n * });\n *\n * // Build to HTML\n * const result = await buildReactUIs({\n * \"my-widget\": widgetUI,\n * }, {\n * outDir: \"./dist/ui\",\n * });\n *\n * console.log(`Built ${result.outputs.size} UIs in ${result.duration}ms`);\n * ```\n *\n * @example With tool definition\n * ```typescript\n * import { createApp, defineTool } from \"@mcp-apps-kit/core\";\n * import { defineReactUI, buildAndTransform } from \"@mcp-apps-kit/ui-react-builder\";\n * import { RestaurantList } from \"./widgets/RestaurantList\";\n * import { z } from \"zod\";\n *\n * // Build and transform in one step\n * const uis = await buildAndTransform({\n * \"restaurant-list\": defineReactUI({\n * component: RestaurantList,\n * name: \"Restaurant List\",\n * }),\n * });\n *\n * const app = createApp({\n * name: \"restaurant-finder\",\n * version: \"1.0.0\",\n * tools: {\n * search: defineTool({\n * description: \"Search for restaurants\",\n * input: z.object({ query: z.string() }),\n * output: z.object({ restaurants: z.array(z.unknown()) }),\n * ui: uis[\"restaurant-list\"],\n * handler: async (input) => {\n * // ... search logic\n * return { restaurants: [] };\n * },\n * }),\n * },\n * });\n * ```\n *\n * @packageDocumentation\n */\n\n// =============================================================================\n// TYPE EXPORTS\n// =============================================================================\n\nexport type {\n ReactUIInput,\n ReactUIDef,\n BuildOptions,\n BuildResult,\n BuildError,\n TemplateOptions,\n DevServerOptions,\n} from \"./types\";\n\n// =============================================================================\n// DEFINITION HELPERS\n// =============================================================================\n\nexport { defineReactUI, isReactUIDef } from \"./define\";\n\n// =============================================================================\n// BUILD FUNCTIONS\n// =============================================================================\n\nexport { buildReactUIs, buildReactUI } from \"./build\";\n\n// =============================================================================\n// TRANSFORM UTILITIES\n// =============================================================================\n\nexport {\n transformToCoreDefs,\n transformSingleToCoreDef,\n extractReactUIs,\n buildAndTransform,\n} from \"./transform\";\n\n// =============================================================================\n// HTML UTILITIES\n// =============================================================================\n\nexport type { EntryPointOptions } from \"./html\";\nexport { generateHTML, generateEntryPoint } from \"./html\";\n","/**\n * Transform utilities for converting ReactUIDef to standard UIDef\n *\n * After building React UIs, this module helps convert them to the\n * standard UIDef format that @mcp-apps-kit/core understands.\n */\n\nimport type { UIDef } from \"@mcp-apps-kit/core\";\nimport type { ReactUIDef, BuildResult } from \"./types\";\nimport { isReactUIDef } from \"./define\";\n\n/**\n * Transform React UI definitions to standard core UIDefs.\n *\n * This function takes a mix of React UIs and standard UIs, and converts\n * all React UIs to standard UIDefs using the build result. Standard UIDefs\n * pass through unchanged.\n *\n * Use this after calling `buildReactUIs` to get definitions that can be\n * passed directly to `createApp`.\n *\n * @param defs - Record of UI keys to ReactUIDef or UIDef\n * @param buildResult - Result from buildReactUIs containing compiled HTML\n * @returns Record of UI keys to standard UIDefs\n *\n * @example\n * ```typescript\n * import { buildReactUIs, transformToCoreDefs, defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { defineUI, createApp, defineTool } from \"@mcp-apps-kit/core\";\n * import { MyWidget } from \"./widgets/MyWidget\";\n *\n * // Define a mix of React and standard UIs\n * const uis = {\n * \"react-widget\": defineReactUI({\n * component: MyWidget,\n * name: \"My Widget\",\n * }),\n * \"html-widget\": defineUI({\n * html: \"./widget.html\",\n * name: \"HTML Widget\",\n * }),\n * };\n *\n * // Build React UIs\n * const buildResult = await buildReactUIs({\n * \"react-widget\": uis[\"react-widget\"] as ReactUIDef,\n * });\n *\n * // Transform all to core format\n * const coreDefs = transformToCoreDefs(uis, buildResult);\n *\n * // Now use with createApp\n * const app = createApp({\n * name: \"my-app\",\n * version: \"1.0.0\",\n * tools: {\n * myTool: defineTool({\n * // ... tool definition\n * ui: coreDefs[\"react-widget\"],\n * }),\n * },\n * });\n * ```\n */\nexport function transformToCoreDefs(\n defs: Record<string, ReactUIDef | UIDef>,\n buildResult: BuildResult\n): Record<string, UIDef> {\n const result: Record<string, UIDef> = {};\n\n for (const [key, def] of Object.entries(defs)) {\n if (isReactUIDef(def)) {\n // Get compiled HTML from build result\n const html = buildResult.outputs.get(key);\n if (!html) {\n // Check if there was a build error\n const error = buildResult.errors.find((e) => e.key === key);\n if (error) {\n throw new Error(`Build failed for React UI \"${key}\": ${error.message}`);\n }\n throw new Error(\n `No build output found for React UI \"${key}\". Did you forget to include it in buildReactUIs()?`\n );\n }\n\n // Transform ReactUIDef to UIDef with inline HTML\n result[key] = {\n html, // Inline the compiled HTML\n name: def.name,\n description: def.description,\n widgetDescription: def.widgetDescription,\n csp: def.csp,\n prefersBorder: def.prefersBorder,\n domain: def.domain,\n };\n } else {\n // Pass through standard UIDef unchanged\n result[key] = def;\n }\n }\n\n return result;\n}\n\n/**\n * Transform a single React UI definition to a standard UIDef.\n *\n * @param def - React UI definition\n * @param html - Compiled HTML string from build\n * @returns Standard UIDef with inline HTML\n *\n * @example\n * ```typescript\n * const html = await buildReactUI(\"my-widget\", reactDef);\n * const coreDef = transformSingleToCoreDef(reactDef, html);\n * ```\n *\n * @internal\n */\nexport function transformSingleToCoreDef(def: ReactUIDef, html: string): UIDef {\n return {\n html,\n name: def.name,\n description: def.description,\n widgetDescription: def.widgetDescription,\n csp: def.csp,\n prefersBorder: def.prefersBorder,\n domain: def.domain,\n };\n}\n\n/**\n * Extract React UI definitions from a mixed record.\n *\n * Use this to separate React UIs (that need building) from standard UIs\n * (that are already HTML).\n *\n * @param defs - Record of UI keys to ReactUIDef or UIDef\n * @returns Object with separated React and standard UIs\n *\n * @example\n * ```typescript\n * const { reactUIs, standardUIs } = extractReactUIs(allUIs);\n *\n * // Build only the React UIs\n * const buildResult = await buildReactUIs(reactUIs);\n *\n * // Combine back together\n * const allCoreDefs = {\n * ...standardUIs,\n * ...transformToCoreDefs(reactUIs, buildResult),\n * };\n * ```\n *\n * @internal\n */\nexport function extractReactUIs(defs: Record<string, ReactUIDef | UIDef>): {\n reactUIs: Record<string, ReactUIDef>;\n standardUIs: Record<string, UIDef>;\n} {\n const reactUIs: Record<string, ReactUIDef> = {};\n const standardUIs: Record<string, UIDef> = {};\n\n for (const [key, def] of Object.entries(defs)) {\n if (isReactUIDef(def)) {\n reactUIs[key] = def;\n } else {\n standardUIs[key] = def;\n }\n }\n\n return { reactUIs, standardUIs };\n}\n\n/**\n * Convenience function to build and transform React UIs in one step.\n *\n * This combines `buildReactUIs` and `transformToCoreDefs` for simpler usage.\n *\n * @param defs - Record of UI keys to ReactUIDef or UIDef\n * @param options - Build options (passed to buildReactUIs)\n * @returns Promise resolving to standard UIDefs\n *\n * @example\n * ```typescript\n * import { buildAndTransform, defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { defineUI, createApp } from \"@mcp-apps-kit/core\";\n *\n * const uis = await buildAndTransform({\n * \"react-widget\": defineReactUI({ component: MyWidget }),\n * \"html-widget\": defineUI({ html: \"./widget.html\" }),\n * });\n *\n * // All are now standard UIDefs\n * console.log(uis[\"react-widget\"].html.startsWith(\"<!DOCTYPE html>\")); // true\n * console.log(uis[\"html-widget\"].html); // \"./widget.html\"\n * ```\n */\nexport async function buildAndTransform(\n defs: Record<string, ReactUIDef | UIDef>,\n options?: Parameters<typeof import(\"./build\").buildReactUIs>[1]\n): Promise<Record<string, UIDef>> {\n // Dynamically import build to avoid circular dependencies\n const { buildReactUIs } = await import(\"./build\");\n\n // Separate React UIs from standard UIs\n const { reactUIs, standardUIs } = extractReactUIs(defs);\n\n // If no React UIs, just return standard UIs\n if (Object.keys(reactUIs).length === 0) {\n return standardUIs;\n }\n\n // Build React UIs\n const buildResult = await buildReactUIs(reactUIs, options);\n\n // Check for errors\n if (buildResult.errors.length > 0) {\n const errorMessages = buildResult.errors.map((e) => ` - ${e.key}: ${e.message}`).join(\"\\n\");\n throw new Error(`Failed to build some React UIs:\\n${errorMessages}`);\n }\n\n // Transform and combine\n return {\n ...standardUIs,\n ...transformToCoreDefs(reactUIs, buildResult),\n };\n}\n"]} | ||
| {"version":3,"sources":["../src/html.ts","../src/build.ts","../src/define.ts","../src/index.ts","../src/transform.ts"],"names":["generateHTML","options","key","name","script","css","combinedCss","DEFAULT_BASE_CSS","escapeHtml","generateEntryPoint","componentPathOrOptions","defaultProps","componentPath","componentExport","props","autoResize","propsJson","importStatement","providerProps","text","htmlEntities","char","init_html","__esmMin","build_exports","__export","buildReactUI","buildReactUIs","uis","startTime","outputs","files","warnings","errors","cwd","u","g","globalCss","error","def","html","compileComponent","outPath","buildError","component","componentName","entryPoint","plugins","createComponentPlugin","createCSSPlugin","result","x","outputFile","build","componentSource","isClassComponent","isFunctionComponent","isArrowFunction","contents","args","classNames","extractClassNames","mapping","hashString","transformedCss","original","hashed","classRegex","classes","match","str","hash","i","e","init_build","toKebabCase","defineReactUI","definition","outDir","rest","htmlPath","isReactUIDef","value","transformToCoreDefs","defs","buildResult","transformSingleToCoreDef","extractReactUIs","reactUIs","standardUIs","buildAndTransform","errorMessages"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * HTML template generation for React UIs\n */\n\nimport type { TemplateOptions } from \"./types\";\n\n/**\n * Default base CSS reset for all UIs.\n * Provides a consistent starting point across platforms.\n */\nconst DEFAULT_BASE_CSS = `\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n html {\n font-size: 16px;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen,\n Ubuntu, Cantarell, \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;\n line-height: 1.5;\n color: #1a1a1a;\n background-color: transparent;\n }\n\n @media (prefers-color-scheme: dark) {\n body {\n color: #f5f5f5;\n }\n }\n\n #root {\n min-height: 100%;\n }\n`;\n\n/**\n * Generate a self-contained HTML document for a React UI.\n *\n * The generated HTML includes:\n * - DOCTYPE and valid HTML5 structure\n * - Meta tags for responsive design\n * - Inlined CSS (base reset + optional custom CSS)\n * - Inlined JavaScript bundle with React app\n *\n * @param options - Template options with key, name, script, and optional CSS\n * @returns Complete HTML document as a string\n *\n * @example\n * ```typescript\n * const html = generateHTML({\n * key: \"restaurant-list\",\n * name: \"Restaurant List Widget\",\n * script: bundledJavaScript,\n * css: customStyles,\n * });\n * ```\n *\n * @internal\n */\nexport function generateHTML(options: TemplateOptions): string {\n const { key, name, script, css } = options;\n\n // Combine base CSS with any custom CSS\n const combinedCss = css ? `${DEFAULT_BASE_CSS}\\n${css}` : DEFAULT_BASE_CSS;\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"mcp-ui-key\" content=\"${escapeHtml(key)}\">\n <title>${escapeHtml(name)}</title>\n <style>${combinedCss}</style>\n</head>\n<body>\n <div id=\"root\"></div>\n <script type=\"module\">${script.replace(/<\\/script>/gi, \"</scr\" + \"ipt>\")}</script>\n</body>\n</html>`;\n}\n\n/**\n * Options for generating the React entry point.\n *\n * @internal\n */\nexport interface EntryPointOptions {\n /**\n * Path to the component file.\n */\n componentPath: string;\n\n /**\n * Name of the export to use.\n * Use \"default\" for default exports, or the actual name for named exports.\n * @default \"default\"\n */\n componentExport?: string;\n\n /**\n * Default props to pass to the component.\n */\n defaultProps?: Record<string, unknown>;\n\n /**\n * Whether to enable automatic size change notifications.\n * When undefined, uses the default (true).\n */\n autoResize?: boolean;\n}\n\n/**\n * Generate the React entry point code that will be bundled.\n *\n * This creates a small JavaScript module that:\n * 1. Imports React and ReactDOM\n * 2. Imports the user's component\n * 3. Imports AppsProvider from @mcp-apps-kit/ui-react\n * 4. Renders the component wrapped in providers\n *\n * @param componentPath - Path to the React component file (for backward compatibility)\n * @param defaultProps - Optional default props to pass to the component\n * @returns JavaScript/TypeScript source code for the entry point\n *\n * @example\n * ```typescript\n * // Default export\n * const entryCode = generateEntryPoint(\n * \"./src/widgets/MyWidget.tsx\",\n * { theme: \"dark\" }\n * );\n *\n * // Named export\n * const entryCode = generateEntryPoint({\n * componentPath: \"./src/widgets/MyWidget.tsx\",\n * componentExport: \"MyWidget\",\n * });\n * ```\n *\n * @internal\n */\nexport function generateEntryPoint(\n componentPathOrOptions: string | EntryPointOptions,\n defaultProps?: Record<string, unknown>\n): string {\n // Handle backward compatibility with string-only signature\n const options: EntryPointOptions =\n typeof componentPathOrOptions === \"string\"\n ? { componentPath: componentPathOrOptions, defaultProps }\n : componentPathOrOptions;\n\n const { componentPath, componentExport = \"default\", defaultProps: props, autoResize } = options;\n const propsJson = props ? JSON.stringify(props) : \"{}\";\n\n // Generate appropriate import statement based on export type\n const importStatement =\n componentExport === \"default\"\n ? `import Component from \"${componentPath}\";`\n : `import { ${componentExport} as Component } from \"${componentPath}\";`;\n\n // Generate AppsProvider props\n const providerProps = autoResize === undefined ? \"\" : ` autoResize={${autoResize}}`;\n\n return `\nimport React from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { AppsProvider } from \"@mcp-apps-kit/ui-react\";\n${importStatement}\n\nconst rootElement = document.getElementById(\"root\");\nif (rootElement) {\n const root = createRoot(rootElement);\n root.render(\n <React.StrictMode>\n <AppsProvider${providerProps}>\n <Component {...${propsJson}} />\n </AppsProvider>\n </React.StrictMode>\n );\n}\n`;\n}\n\n/**\n * Escape HTML special characters to prevent XSS.\n *\n * @param text - Text to escape\n * @returns Escaped text safe for HTML insertion\n */\nfunction escapeHtml(text: string): string {\n const htmlEntities: Record<string, string> = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n '\"': \""\",\n \"'\": \"'\",\n };\n\n return text.replace(/[&<>\"']/g, (char) => htmlEntities[char] ?? char);\n}\n\n/**\n * Extract CSS from a string that may contain both JS and CSS.\n * Used when esbuild bundles CSS as JavaScript that injects styles.\n *\n * @param code - Bundled code that may contain CSS injection\n * @returns Extracted CSS string or undefined\n */\nexport function extractInlineCSS(code: string): string | undefined {\n // esbuild injects CSS as: document.head.appendChild(...).textContent = \"css content\"\n const cssMatch = code.match(/\\.textContent\\s*=\\s*[\"'`]([\\s\\S]*?)[\"'`]\\s*[;,)]/);\n if (cssMatch?.[1]) {\n // Unescape the CSS string\n return cssMatch[1].replace(/\\\\n/g, \"\\n\").replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\");\n }\n return undefined;\n}\n","/**\n * React UI build system using esbuild\n *\n * Compiles React components into self-contained HTML files that can be\n * served as MCP UI resources.\n */\n\nimport * as esbuild from \"esbuild\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type { ReactUIDef, BuildOptions, BuildResult, BuildError } from \"./types\";\nimport { generateHTML, generateEntryPoint } from \"./html\";\n\n/**\n * Build multiple React UIs into self-contained HTML files.\n *\n * This function takes a record of React UI definitions and compiles each one\n * into a complete HTML file that includes React, ReactDOM, @mcp-apps-kit/ui-react,\n * and the user's component code.\n *\n * **IMPORTANT: Limitations**\n *\n * This programmatic build function serializes components using `.toString()`, which has\n * significant limitations:\n *\n * - **No external imports**: Components cannot import other modules, hooks, or utilities\n * - **No closures**: Components that capture external variables will not work\n * - **Simple components only**: Best for self-contained components without dependencies\n *\n * For production use, prefer the **Vite plugin** (`mcpReactUI`) which uses file paths\n * for proper import resolution and supports:\n * - Full import/export resolution\n * - CSS imports and CSS modules\n * - All React hooks and utilities\n * - External component dependencies\n *\n * This function is primarily intended for:\n * - **Testing**: Unit and integration tests for the build pipeline\n * - **Simple widgets**: Self-contained components with no external imports\n * - **Prototyping**: Quick experimentation before setting up Vite\n *\n * @param uis - Record of UI keys to React UI definitions\n * @param options - Build configuration options\n * @returns Build result with compiled HTML and metadata\n *\n * @example\n * ```typescript\n * // For production, use the Vite plugin instead:\n * // vite.config.ts\n * import { mcpReactUI } from \"@mcp-apps-kit/ui-react-builder/vite\";\n *\n * export default defineConfig({\n * plugins: [mcpReactUI({ serverEntry: \"./src/index.ts\" })],\n * });\n *\n * // This programmatic API is for simple/test cases:\n * import { buildReactUIs, defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n *\n * // Only works with simple, self-contained components\n * const SimpleWidget = () => <div>Hello World</div>;\n *\n * const result = await buildReactUIs({\n * \"simple\": defineReactUI({\n * component: SimpleWidget,\n * name: \"Simple Widget\",\n * }),\n * });\n * ```\n */\nexport async function buildReactUIs(\n uis: Record<string, ReactUIDef>,\n options: BuildOptions = {}\n): Promise<BuildResult> {\n const startTime = Date.now();\n const outputs = new Map<string, string>();\n const files = new Map<string, string>();\n const warnings: string[] = [];\n const errors: BuildError[] = [];\n\n const cwd = options.cwd ?? process.cwd();\n\n // Create output directory if specified\n if (options.outDir) {\n await fs.mkdir(path.resolve(cwd, options.outDir), { recursive: true });\n }\n\n // Load global CSS if specified\n let globalCss: string | undefined;\n if (options.globalCss) {\n try {\n globalCss = await fs.readFile(path.resolve(cwd, options.globalCss), \"utf-8\");\n } catch (error) {\n warnings.push(\n `Could not load global CSS from ${options.globalCss}: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n\n // Build each UI\n for (const [key, def] of Object.entries(uis)) {\n try {\n const html = await compileComponent(key, def, {\n ...options,\n cwd,\n globalCss,\n });\n\n outputs.set(key, html);\n\n // Write to file if outDir specified\n if (options.outDir) {\n const outPath = path.resolve(cwd, options.outDir, `${key}.html`);\n await fs.writeFile(outPath, html, \"utf-8\");\n files.set(key, outPath);\n }\n } catch (error) {\n const buildError: BuildError = {\n key,\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n errors.push(buildError);\n }\n }\n\n return {\n outputs,\n files,\n duration: Date.now() - startTime,\n warnings,\n errors,\n };\n}\n\n/**\n * Compile a single React component to self-contained HTML.\n *\n * @param key - Unique key for this UI\n * @param def - React UI definition\n * @param options - Build options\n * @returns Complete HTML document as a string\n */\nasync function compileComponent(\n key: string,\n def: ReactUIDef,\n options: BuildOptions & { cwd: string; globalCss?: string }\n): Promise<string> {\n // Get component from internal properties\n const component = def.__component;\n const componentName = component.name || \"Component\";\n const defaultProps = def.__defaultProps;\n const autoResize = def.__autoResize;\n\n // Create the entry point using function serialization\n // Note: The Vite plugin uses file paths for proper import resolution\n // This build function falls back to function serialization (limited - doesn't capture imports)\n const entryPoint = generateEntryPoint({\n componentPath: `__COMPONENT_PLACEHOLDER__`,\n defaultProps,\n autoResize,\n });\n\n // Configure plugins\n const plugins: esbuild.Plugin[] = [\n createComponentPlugin(component, componentName),\n createCSSPlugin(),\n ];\n\n // Use esbuild to bundle everything\n const result = await esbuild.build({\n stdin: {\n contents: entryPoint,\n loader: \"tsx\",\n resolveDir: options.cwd,\n sourcefile: `${key}-entry.tsx`,\n },\n bundle: true,\n write: false,\n format: \"esm\",\n platform: \"browser\",\n target: [\"es2020\", \"chrome90\", \"firefox90\", \"safari14\"],\n minify: options.minify ?? true,\n sourcemap: options.sourcemap ? \"inline\" : false,\n jsx: \"automatic\",\n jsxImportSource: \"react\",\n logLevel: \"warning\",\n external: options.external,\n plugins,\n define: {\n \"process.env.NODE_ENV\": options.minify ? '\"production\"' : '\"development\"',\n },\n });\n\n // Get the bundled JavaScript\n const outputFile = result.outputFiles?.[0];\n if (!outputFile) {\n throw new Error(`No output generated for UI: ${key}`);\n }\n\n const script = outputFile.text;\n\n // Collect any warnings from esbuild (currently silent, could add a logger later)\n // Warnings are typically about missing exports, unused imports, etc.\n // For now we don't surface these to users\n void result.warnings;\n\n // Generate the final HTML\n const html = generateHTML({\n key,\n name: def.name ?? key,\n script,\n css: options.globalCss,\n });\n\n return html;\n}\n\n/**\n * Create an esbuild plugin that resolves the component placeholder.\n *\n * This plugin intercepts imports of the placeholder module and replaces\n * it with the actual component code. This is necessary because we receive\n * a function reference, not a file path.\n */\nfunction createComponentPlugin(\n component: { toString: () => string },\n componentName: string\n): esbuild.Plugin {\n return {\n name: \"mcp-component-resolver\",\n setup(build) {\n // Intercept the placeholder import\n build.onResolve({ filter: /__COMPONENT_PLACEHOLDER__/ }, () => {\n return {\n path: \"__COMPONENT__\",\n namespace: \"mcp-component\",\n };\n });\n\n // Provide the component code\n build.onLoad({ filter: /.*/, namespace: \"mcp-component\" }, () => {\n // Serialize the component to a module\n // For now, we export a placeholder that requires runtime injection\n // In a real implementation, we'd need the component's source path\n const componentSource = component.toString();\n\n // Detect component type: class, function, or arrow function\n const isClassComponent = /^\\s*class\\b/.test(componentSource);\n const isFunctionComponent = /^\\s*function\\b/.test(componentSource);\n const isArrowFunction = !isClassComponent && !isFunctionComponent;\n\n let contents: string;\n if (isArrowFunction) {\n contents = `\n import React from \"react\";\n const ${componentName} = ${componentSource};\n export default ${componentName};\n `;\n } else {\n contents = `\n import React from \"react\";\n ${componentSource}\n export default ${componentName};\n `;\n }\n\n return {\n contents,\n loader: \"tsx\",\n };\n });\n },\n };\n}\n\n/**\n * Create an esbuild plugin that handles CSS imports.\n *\n * This plugin transforms CSS imports into JavaScript that injects\n * the styles into the document head at runtime.\n */\nfunction createCSSPlugin(): esbuild.Plugin {\n return {\n name: \"mcp-css-handler\",\n setup(build) {\n // Handle .css imports (excluding .module.css which has its own handler)\n build.onLoad({ filter: /\\.css$/ }, async (args) => {\n // Skip .module.css files - they're handled by the CSS modules loader\n if (args.path.endsWith(\".module.css\")) {\n return null;\n }\n const css = await fs.readFile(args.path, \"utf-8\");\n\n // Create JavaScript that injects the CSS\n const contents = `\n const style = document.createElement(\"style\");\n style.textContent = ${JSON.stringify(css)};\n document.head.appendChild(style);\n `;\n\n return {\n contents,\n loader: \"js\",\n };\n });\n\n // Handle CSS modules (.module.css)\n build.onLoad({ filter: /\\.module\\.css$/ }, async (args) => {\n const css = await fs.readFile(args.path, \"utf-8\");\n\n // Generate a simple class name mapping\n // In a real implementation, we'd use a proper CSS modules processor\n const classNames = extractClassNames(css);\n const mapping = Object.fromEntries(\n classNames.map((name) => [name, `${name}_${hashString(args.path)}`])\n );\n\n // Transform the CSS with new class names\n let transformedCss = css;\n for (const [original, hashed] of Object.entries(mapping)) {\n transformedCss = transformedCss.replace(\n new RegExp(`\\\\.${original}\\\\b`, \"g\"),\n `.${hashed}`\n );\n }\n\n const contents = `\n const style = document.createElement(\"style\");\n style.textContent = ${JSON.stringify(transformedCss)};\n document.head.appendChild(style);\n export default ${JSON.stringify(mapping)};\n `;\n\n return {\n contents,\n loader: \"js\",\n };\n });\n },\n };\n}\n\n/**\n * Extract class names from CSS content.\n */\nfunction extractClassNames(css: string): string[] {\n const classRegex = /\\.([a-zA-Z_][a-zA-Z0-9_-]*)/g;\n const classes = new Set<string>();\n let match;\n while ((match = classRegex.exec(css)) !== null) {\n if (match[1]) {\n classes.add(match[1]);\n }\n }\n return Array.from(classes);\n}\n\n/**\n * Simple string hash for generating unique class names.\n */\nfunction hashString(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return Math.abs(hash).toString(36).substring(0, 5);\n}\n\n/**\n * Build a single React UI.\n *\n * Convenience function for building just one UI.\n *\n * @param key - Unique key for this UI\n * @param def - React UI definition\n * @param options - Build options\n * @returns The compiled HTML string\n *\n * @example\n * ```typescript\n * const html = await buildReactUI(\"my-widget\", defineReactUI({\n * component: MyWidget,\n * name: \"My Widget\",\n * }));\n * ```\n */\nexport async function buildReactUI(\n key: string,\n def: ReactUIDef,\n options: BuildOptions = {}\n): Promise<string> {\n const result = await buildReactUIs({ [key]: def }, options);\n\n const html = result.outputs.get(key);\n if (!html) {\n const error = result.errors.find((e) => e.key === key);\n throw new Error(error?.message ?? `Failed to build UI: ${key}`);\n }\n\n return html;\n}\n","/**\n * Helper functions for defining React-based UIs\n */\n\nimport type { ReactUIInput, ReactUIDef } from \"./types\";\n\n/**\n * Convert component name to kebab-case for filename.\n * @internal\n */\nfunction toKebabCase(name: string): string {\n return name.replace(/([a-z])([A-Z])/g, \"$1-$2\").toLowerCase();\n}\n\n/**\n * Define a UI using a React component.\n *\n * This helper creates a UIDef with an auto-generated HTML path based on\n * the component name. The Vite plugin discovers these definitions and\n * builds the React components into self-contained HTML files.\n *\n * @param definition - React UI input with component and metadata\n * @returns A UIDef with auto-generated html path and React metadata\n *\n * @example Basic usage\n * ```typescript\n * import { defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { MyWidget } from \"./widgets/MyWidget\";\n *\n * const widgetUI = defineReactUI({\n * component: MyWidget,\n * name: \"My Widget\",\n * prefersBorder: true,\n * });\n * // Returns: { html: \"./src/ui/dist/my-widget.html\", name: \"My Widget\", ... }\n * ```\n *\n * @example With tool definition\n * ```typescript\n * import { createApp, defineTool } from \"@mcp-apps-kit/core\";\n * import { defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { RestaurantList } from \"./widgets/RestaurantList\";\n *\n * const app = createApp({\n * name: \"restaurant-finder\",\n * version: \"1.0.0\",\n * tools: {\n * search: defineTool({\n * description: \"Search for restaurants\",\n * input: z.object({ query: z.string() }),\n * output: z.object({ restaurants: z.array(RestaurantSchema) }),\n * ui: defineReactUI({\n * component: RestaurantList,\n * name: \"Restaurant List\",\n * }),\n * handler: async (input) => {\n * // ... search logic\n * },\n * }),\n * },\n * });\n * ```\n *\n * @example Custom output directory\n * ```typescript\n * const widgetUI = defineReactUI({\n * component: ConfigurableWidget,\n * name: \"Configurable Widget\",\n * outDir: \"./dist/ui\",\n * });\n * // Returns: { html: \"./dist/ui/configurable-widget.html\", ... }\n * ```\n */\nexport function defineReactUI(definition: ReactUIInput): ReactUIDef {\n const { component, defaultProps, outDir, autoResize, ...rest } = definition;\n\n // Generate the output path from component name\n const componentName = component.name ?? \"component\";\n const key = toKebabCase(componentName);\n const outputDir = outDir ?? \"./src/ui/dist\";\n const htmlPath = `${outputDir}/${key}.html`;\n\n return {\n ...rest,\n html: htmlPath,\n __reactUI: true,\n __component: component,\n __defaultProps: defaultProps,\n __autoResize: autoResize ?? true,\n };\n}\n\n/**\n * Check if a value is a React UI definition.\n *\n * This type guard is used by the build system to identify\n * React UIs that need to be compiled to HTML.\n *\n * @param value - Value to check\n * @returns True if the value is a ReactUIDef\n *\n * @example\n * ```typescript\n * import { isReactUIDef } from \"@mcp-apps-kit/ui-react-builder\";\n *\n * const ui = someUnknownUI;\n * if (isReactUIDef(ui)) {\n * // ui.__component is available here\n * console.log(\"React UI:\", ui.__component.name);\n * }\n * ```\n *\n * @internal\n */\nexport function isReactUIDef(value: unknown): value is ReactUIDef {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"__reactUI\" in value &&\n (value as ReactUIDef).__reactUI\n );\n}\n","/**\n * @mcp-apps-kit/ui-react-builder\n *\n * Build tool for React-based MCP application UIs.\n *\n * This package allows you to define UI resources using React components\n * instead of pre-built HTML files. The framework handles bundling React,\n * ReactDOM, and @mcp-apps-kit/ui-react into self-contained HTML that works\n * with both MCP Apps and ChatGPT.\n *\n * @example Basic usage\n * ```typescript\n * import { defineReactUI, buildReactUIs } from \"@mcp-apps-kit/ui-react-builder\";\n * import { MyWidget } from \"./widgets/MyWidget\";\n *\n * // Define your UI with a React component\n * const widgetUI = defineReactUI({\n * component: MyWidget,\n * name: \"My Widget\",\n * prefersBorder: true,\n * });\n *\n * // Build to HTML\n * const result = await buildReactUIs({\n * \"my-widget\": widgetUI,\n * }, {\n * outDir: \"./dist/ui\",\n * });\n *\n * console.log(`Built ${result.outputs.size} UIs in ${result.duration}ms`);\n * ```\n *\n * @example With tool definition\n * ```typescript\n * import { createApp, defineTool } from \"@mcp-apps-kit/core\";\n * import { defineReactUI, buildAndTransform } from \"@mcp-apps-kit/ui-react-builder\";\n * import { RestaurantList } from \"./widgets/RestaurantList\";\n * import { z } from \"zod\";\n *\n * // Build and transform in one step\n * const uis = await buildAndTransform({\n * \"restaurant-list\": defineReactUI({\n * component: RestaurantList,\n * name: \"Restaurant List\",\n * }),\n * });\n *\n * const app = createApp({\n * name: \"restaurant-finder\",\n * version: \"1.0.0\",\n * tools: {\n * search: defineTool({\n * description: \"Search for restaurants\",\n * input: z.object({ query: z.string() }),\n * output: z.object({ restaurants: z.array(z.unknown()) }),\n * ui: uis[\"restaurant-list\"],\n * handler: async (input) => {\n * // ... search logic\n * return { restaurants: [] };\n * },\n * }),\n * },\n * });\n * ```\n *\n * @packageDocumentation\n */\n\n// =============================================================================\n// TYPE EXPORTS\n// =============================================================================\n\nexport type {\n ReactUIInput,\n ReactUIDef,\n BuildOptions,\n BuildResult,\n BuildError,\n TemplateOptions,\n DevServerOptions,\n} from \"./types\";\n\n// =============================================================================\n// DEFINITION HELPERS\n// =============================================================================\n\nexport { defineReactUI, isReactUIDef } from \"./define\";\n\n// =============================================================================\n// BUILD FUNCTIONS\n// =============================================================================\n\nexport { buildReactUIs, buildReactUI } from \"./build\";\n\n// =============================================================================\n// TRANSFORM UTILITIES\n// =============================================================================\n\nexport {\n transformToCoreDefs,\n transformSingleToCoreDef,\n extractReactUIs,\n buildAndTransform,\n} from \"./transform\";\n\n// =============================================================================\n// HTML UTILITIES\n// =============================================================================\n\nexport type { EntryPointOptions } from \"./html\";\nexport { generateHTML, generateEntryPoint } from \"./html\";\n","/**\n * Transform utilities for converting ReactUIDef to standard UIDef\n *\n * After building React UIs, this module helps convert them to the\n * standard UIDef format that @mcp-apps-kit/core understands.\n */\n\nimport type { UIDef } from \"@mcp-apps-kit/core\";\nimport type { ReactUIDef, BuildResult } from \"./types\";\nimport { isReactUIDef } from \"./define\";\n\n/**\n * Transform React UI definitions to standard core UIDefs.\n *\n * This function takes a mix of React UIs and standard UIs, and converts\n * all React UIs to standard UIDefs using the build result. Standard UIDefs\n * pass through unchanged.\n *\n * Use this after calling `buildReactUIs` to get definitions that can be\n * passed directly to `createApp`.\n *\n * @param defs - Record of UI keys to ReactUIDef or UIDef\n * @param buildResult - Result from buildReactUIs containing compiled HTML\n * @returns Record of UI keys to standard UIDefs\n *\n * @example\n * ```typescript\n * import { buildReactUIs, transformToCoreDefs, defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { defineUI, createApp, defineTool } from \"@mcp-apps-kit/core\";\n * import { MyWidget } from \"./widgets/MyWidget\";\n *\n * // Define a mix of React and standard UIs\n * const uis = {\n * \"react-widget\": defineReactUI({\n * component: MyWidget,\n * name: \"My Widget\",\n * }),\n * \"html-widget\": defineUI({\n * html: \"./widget.html\",\n * name: \"HTML Widget\",\n * }),\n * };\n *\n * // Build React UIs\n * const buildResult = await buildReactUIs({\n * \"react-widget\": uis[\"react-widget\"] as ReactUIDef,\n * });\n *\n * // Transform all to core format\n * const coreDefs = transformToCoreDefs(uis, buildResult);\n *\n * // Now use with createApp\n * const app = createApp({\n * name: \"my-app\",\n * version: \"1.0.0\",\n * tools: {\n * myTool: defineTool({\n * // ... tool definition\n * ui: coreDefs[\"react-widget\"],\n * }),\n * },\n * });\n * ```\n */\nexport function transformToCoreDefs(\n defs: Record<string, ReactUIDef | UIDef>,\n buildResult: BuildResult\n): Record<string, UIDef> {\n const result: Record<string, UIDef> = {};\n\n for (const [key, def] of Object.entries(defs)) {\n if (isReactUIDef(def)) {\n // Get compiled HTML from build result\n const html = buildResult.outputs.get(key);\n if (!html) {\n // Check if there was a build error\n const error = buildResult.errors.find((e) => e.key === key);\n if (error) {\n throw new Error(`Build failed for React UI \"${key}\": ${error.message}`);\n }\n throw new Error(\n `No build output found for React UI \"${key}\". Did you forget to include it in buildReactUIs()?`\n );\n }\n\n // Transform ReactUIDef to UIDef with inline HTML\n result[key] = {\n html, // Inline the compiled HTML\n name: def.name,\n description: def.description,\n widgetDescription: def.widgetDescription,\n csp: def.csp,\n prefersBorder: def.prefersBorder,\n domain: def.domain,\n };\n } else {\n // Pass through standard UIDef unchanged\n result[key] = def;\n }\n }\n\n return result;\n}\n\n/**\n * Transform a single React UI definition to a standard UIDef.\n *\n * @param def - React UI definition\n * @param html - Compiled HTML string from build\n * @returns Standard UIDef with inline HTML\n *\n * @example\n * ```typescript\n * const html = await buildReactUI(\"my-widget\", reactDef);\n * const coreDef = transformSingleToCoreDef(reactDef, html);\n * ```\n *\n * @internal\n */\nexport function transformSingleToCoreDef(def: ReactUIDef, html: string): UIDef {\n return {\n html,\n name: def.name,\n description: def.description,\n widgetDescription: def.widgetDescription,\n csp: def.csp,\n prefersBorder: def.prefersBorder,\n domain: def.domain,\n };\n}\n\n/**\n * Extract React UI definitions from a mixed record.\n *\n * Use this to separate React UIs (that need building) from standard UIs\n * (that are already HTML).\n *\n * @param defs - Record of UI keys to ReactUIDef or UIDef\n * @returns Object with separated React and standard UIs\n *\n * @example\n * ```typescript\n * const { reactUIs, standardUIs } = extractReactUIs(allUIs);\n *\n * // Build only the React UIs\n * const buildResult = await buildReactUIs(reactUIs);\n *\n * // Combine back together\n * const allCoreDefs = {\n * ...standardUIs,\n * ...transformToCoreDefs(reactUIs, buildResult),\n * };\n * ```\n *\n * @internal\n */\nexport function extractReactUIs(defs: Record<string, ReactUIDef | UIDef>): {\n reactUIs: Record<string, ReactUIDef>;\n standardUIs: Record<string, UIDef>;\n} {\n const reactUIs: Record<string, ReactUIDef> = {};\n const standardUIs: Record<string, UIDef> = {};\n\n for (const [key, def] of Object.entries(defs)) {\n if (isReactUIDef(def)) {\n reactUIs[key] = def;\n } else {\n standardUIs[key] = def;\n }\n }\n\n return { reactUIs, standardUIs };\n}\n\n/**\n * Convenience function to build and transform React UIs in one step.\n *\n * This combines `buildReactUIs` and `transformToCoreDefs` for simpler usage.\n *\n * @param defs - Record of UI keys to ReactUIDef or UIDef\n * @param options - Build options (passed to buildReactUIs)\n * @returns Promise resolving to standard UIDefs\n *\n * @example\n * ```typescript\n * import { buildAndTransform, defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { defineUI, createApp } from \"@mcp-apps-kit/core\";\n *\n * const uis = await buildAndTransform({\n * \"react-widget\": defineReactUI({ component: MyWidget }),\n * \"html-widget\": defineUI({ html: \"./widget.html\" }),\n * });\n *\n * // All are now standard UIDefs\n * console.log(uis[\"react-widget\"].html.startsWith(\"<!DOCTYPE html>\")); // true\n * console.log(uis[\"html-widget\"].html); // \"./widget.html\"\n * ```\n */\nexport async function buildAndTransform(\n defs: Record<string, ReactUIDef | UIDef>,\n options?: Parameters<typeof import(\"./build\").buildReactUIs>[1]\n): Promise<Record<string, UIDef>> {\n // Dynamically import build to avoid circular dependencies\n const { buildReactUIs } = await import(\"./build\");\n\n // Separate React UIs from standard UIs\n const { reactUIs, standardUIs } = extractReactUIs(defs);\n\n // If no React UIs, just return standard UIs\n if (Object.keys(reactUIs).length === 0) {\n return standardUIs;\n }\n\n // Build React UIs\n const buildResult = await buildReactUIs(reactUIs, options);\n\n // Check for errors\n if (buildResult.errors.length > 0) {\n const errorMessages = buildResult.errors.map((e) => ` - ${e.key}: ${e.message}`).join(\"\\n\");\n throw new Error(`Failed to build some React UIs:\\n${errorMessages}`);\n }\n\n // Transform and combine\n return {\n ...standardUIs,\n ...transformToCoreDefs(reactUIs, buildResult),\n };\n}\n"]} |
+20
-0
@@ -56,2 +56,12 @@ import { ComponentType } from 'react'; | ||
| outDir?: string; | ||
| /** | ||
| * Enable automatic size change notifications (MCP adapter only). | ||
| * | ||
| * When enabled, the UI automatically reports its size changes to the host | ||
| * using a ResizeObserver on document.body and document.documentElement. | ||
| * The host can then resize the UI container accordingly. | ||
| * | ||
| * @default true | ||
| */ | ||
| autoResize?: boolean; | ||
| } | ||
@@ -82,2 +92,7 @@ /** | ||
| __defaultProps?: Record<string, unknown>; | ||
| /** | ||
| * Whether to enable automatic size change notifications. | ||
| * @internal | ||
| */ | ||
| __autoResize?: boolean; | ||
| } | ||
@@ -576,2 +591,7 @@ /** | ||
| defaultProps?: Record<string, unknown>; | ||
| /** | ||
| * Whether to enable automatic size change notifications. | ||
| * When undefined, uses the default (true). | ||
| */ | ||
| autoResize?: boolean; | ||
| } | ||
@@ -578,0 +598,0 @@ /** |
+20
-0
@@ -56,2 +56,12 @@ import { ComponentType } from 'react'; | ||
| outDir?: string; | ||
| /** | ||
| * Enable automatic size change notifications (MCP adapter only). | ||
| * | ||
| * When enabled, the UI automatically reports its size changes to the host | ||
| * using a ResizeObserver on document.body and document.documentElement. | ||
| * The host can then resize the UI container accordingly. | ||
| * | ||
| * @default true | ||
| */ | ||
| autoResize?: boolean; | ||
| } | ||
@@ -82,2 +92,7 @@ /** | ||
| __defaultProps?: Record<string, unknown>; | ||
| /** | ||
| * Whether to enable automatic size change notifications. | ||
| * @internal | ||
| */ | ||
| __autoResize?: boolean; | ||
| } | ||
@@ -576,2 +591,7 @@ /** | ||
| defaultProps?: Record<string, unknown>; | ||
| /** | ||
| * Whether to enable automatic size change notifications. | ||
| * When undefined, uses the default (true). | ||
| */ | ||
| autoResize?: boolean; | ||
| } | ||
@@ -578,0 +598,0 @@ /** |
+1
-1
@@ -1,4 +0,4 @@ | ||
| export{b as buildReactUI,a as buildReactUIs}from'./chunk-5WFQIJJW.js';export{b as generateEntryPoint,a as generateHTML}from'./chunk-6JS3KVIH.js';function R(e){return e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}function l(e){let{component:o,defaultProps:n,outDir:r,...t}=e,s=o.name??"component",i=R(s),d=`${r??"./src/ui/dist"}/${i}.html`;return {...t,html:d,__reactUI:true,__component:o,__defaultProps:n}}function a(e){return typeof e=="object"&&e!==null&&"__reactUI"in e&&e.__reactUI}function f(e,o){let n={};for(let[r,t]of Object.entries(e))if(a(t)){let s=o.outputs.get(r);if(!s){let i=o.errors.find(c=>c.key===r);throw i?new Error(`Build failed for React UI "${r}": ${i.message}`):new Error(`No build output found for React UI "${r}". Did you forget to include it in buildReactUIs()?`)}n[r]={html:s,name:t.name,description:t.description,widgetDescription:t.widgetDescription,csp:t.csp,prefersBorder:t.prefersBorder,domain:t.domain};}else n[r]=t;return n}function D(e,o){return {html:o,name:e.name,description:e.description,widgetDescription:e.widgetDescription,csp:e.csp,prefersBorder:e.prefersBorder,domain:e.domain}}function p(e){let o={},n={};for(let[r,t]of Object.entries(e))a(t)?o[r]=t:n[r]=t;return {reactUIs:o,standardUIs:n}}async function g(e,o){let{buildReactUIs:n}=await import('./build-ZF3MI5NJ.js'),{reactUIs:r,standardUIs:t}=p(e);if(Object.keys(r).length===0)return t;let s=await n(r,o);if(s.errors.length>0){let i=s.errors.map(c=>` - ${c.key}: ${c.message}`).join(` | ||
| export{b as buildReactUI,a as buildReactUIs}from'./chunk-3YHDRPOV.js';export{b as generateEntryPoint,a as generateHTML}from'./chunk-R7RJYHEX.js';function R(e){return e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}function l(e){let{component:o,defaultProps:n,outDir:r,autoResize:t,...s}=e,i=o.name??"component",c=R(i),d=`${r??"./src/ui/dist"}/${c}.html`;return {...s,html:d,__reactUI:true,__component:o,__defaultProps:n,__autoResize:t??true}}function a(e){return typeof e=="object"&&e!==null&&"__reactUI"in e&&e.__reactUI}function f(e,o){let n={};for(let[r,t]of Object.entries(e))if(a(t)){let s=o.outputs.get(r);if(!s){let i=o.errors.find(c=>c.key===r);throw i?new Error(`Build failed for React UI "${r}": ${i.message}`):new Error(`No build output found for React UI "${r}". Did you forget to include it in buildReactUIs()?`)}n[r]={html:s,name:t.name,description:t.description,widgetDescription:t.widgetDescription,csp:t.csp,prefersBorder:t.prefersBorder,domain:t.domain};}else n[r]=t;return n}function D(e,o){return {html:o,name:e.name,description:e.description,widgetDescription:e.widgetDescription,csp:e.csp,prefersBorder:e.prefersBorder,domain:e.domain}}function p(e){let o={},n={};for(let[r,t]of Object.entries(e))a(t)?o[r]=t:n[r]=t;return {reactUIs:o,standardUIs:n}}async function g(e,o){let{buildReactUIs:n}=await import('./build-GBFL3B7J.js'),{reactUIs:r,standardUIs:t}=p(e);if(Object.keys(r).length===0)return t;let s=await n(r,o);if(s.errors.length>0){let i=s.errors.map(c=>` - ${c.key}: ${c.message}`).join(` | ||
| `);throw new Error(`Failed to build some React UIs: | ||
| ${i}`)}return {...t,...f(r,s)}}export{g as buildAndTransform,l as defineReactUI,p as extractReactUIs,a as isReactUIDef,D as transformSingleToCoreDef,f as transformToCoreDefs};//# sourceMappingURL=index.js.map | ||
| //# sourceMappingURL=index.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/define.ts","../src/transform.ts"],"names":["toKebabCase","name","defineReactUI","definition","component","defaultProps","outDir","rest","componentName","key","htmlPath","isReactUIDef","value","transformToCoreDefs","defs","buildResult","result","def","html","error","e","transformSingleToCoreDef","extractReactUIs","reactUIs","standardUIs","buildAndTransform","options","buildReactUIs","errorMessages"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Helper functions for defining React-based UIs\n */\n\nimport type { ReactUIInput, ReactUIDef } from \"./types\";\n\n/**\n * Convert component name to kebab-case for filename.\n * @internal\n */\nfunction toKebabCase(name: string): string {\n return name.replace(/([a-z])([A-Z])/g, \"$1-$2\").toLowerCase();\n}\n\n/**\n * Define a UI using a React component.\n *\n * This helper creates a UIDef with an auto-generated HTML path based on\n * the component name. The Vite plugin discovers these definitions and\n * builds the React components into self-contained HTML files.\n *\n * @param definition - React UI input with component and metadata\n * @returns A UIDef with auto-generated html path and React metadata\n *\n * @example Basic usage\n * ```typescript\n * import { defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { MyWidget } from \"./widgets/MyWidget\";\n *\n * const widgetUI = defineReactUI({\n * component: MyWidget,\n * name: \"My Widget\",\n * prefersBorder: true,\n * });\n * // Returns: { html: \"./src/ui/dist/my-widget.html\", name: \"My Widget\", ... }\n * ```\n *\n * @example With tool definition\n * ```typescript\n * import { createApp, defineTool } from \"@mcp-apps-kit/core\";\n * import { defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { RestaurantList } from \"./widgets/RestaurantList\";\n *\n * const app = createApp({\n * name: \"restaurant-finder\",\n * version: \"1.0.0\",\n * tools: {\n * search: defineTool({\n * description: \"Search for restaurants\",\n * input: z.object({ query: z.string() }),\n * output: z.object({ restaurants: z.array(RestaurantSchema) }),\n * ui: defineReactUI({\n * component: RestaurantList,\n * name: \"Restaurant List\",\n * }),\n * handler: async (input) => {\n * // ... search logic\n * },\n * }),\n * },\n * });\n * ```\n *\n * @example Custom output directory\n * ```typescript\n * const widgetUI = defineReactUI({\n * component: ConfigurableWidget,\n * name: \"Configurable Widget\",\n * outDir: \"./dist/ui\",\n * });\n * // Returns: { html: \"./dist/ui/configurable-widget.html\", ... }\n * ```\n */\nexport function defineReactUI(definition: ReactUIInput): ReactUIDef {\n const { component, defaultProps, outDir, ...rest } = definition;\n\n // Generate the output path from component name\n const componentName = component.name ?? \"component\";\n const key = toKebabCase(componentName);\n const outputDir = outDir ?? \"./src/ui/dist\";\n const htmlPath = `${outputDir}/${key}.html`;\n\n return {\n ...rest,\n html: htmlPath,\n __reactUI: true,\n __component: component,\n __defaultProps: defaultProps,\n };\n}\n\n/**\n * Check if a value is a React UI definition.\n *\n * This type guard is used by the build system to identify\n * React UIs that need to be compiled to HTML.\n *\n * @param value - Value to check\n * @returns True if the value is a ReactUIDef\n *\n * @example\n * ```typescript\n * import { isReactUIDef } from \"@mcp-apps-kit/ui-react-builder\";\n *\n * const ui = someUnknownUI;\n * if (isReactUIDef(ui)) {\n * // ui.__component is available here\n * console.log(\"React UI:\", ui.__component.name);\n * }\n * ```\n *\n * @internal\n */\nexport function isReactUIDef(value: unknown): value is ReactUIDef {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"__reactUI\" in value &&\n (value as ReactUIDef).__reactUI\n );\n}\n","/**\n * Transform utilities for converting ReactUIDef to standard UIDef\n *\n * After building React UIs, this module helps convert them to the\n * standard UIDef format that @mcp-apps-kit/core understands.\n */\n\nimport type { UIDef } from \"@mcp-apps-kit/core\";\nimport type { ReactUIDef, BuildResult } from \"./types\";\nimport { isReactUIDef } from \"./define\";\n\n/**\n * Transform React UI definitions to standard core UIDefs.\n *\n * This function takes a mix of React UIs and standard UIs, and converts\n * all React UIs to standard UIDefs using the build result. Standard UIDefs\n * pass through unchanged.\n *\n * Use this after calling `buildReactUIs` to get definitions that can be\n * passed directly to `createApp`.\n *\n * @param defs - Record of UI keys to ReactUIDef or UIDef\n * @param buildResult - Result from buildReactUIs containing compiled HTML\n * @returns Record of UI keys to standard UIDefs\n *\n * @example\n * ```typescript\n * import { buildReactUIs, transformToCoreDefs, defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { defineUI, createApp, defineTool } from \"@mcp-apps-kit/core\";\n * import { MyWidget } from \"./widgets/MyWidget\";\n *\n * // Define a mix of React and standard UIs\n * const uis = {\n * \"react-widget\": defineReactUI({\n * component: MyWidget,\n * name: \"My Widget\",\n * }),\n * \"html-widget\": defineUI({\n * html: \"./widget.html\",\n * name: \"HTML Widget\",\n * }),\n * };\n *\n * // Build React UIs\n * const buildResult = await buildReactUIs({\n * \"react-widget\": uis[\"react-widget\"] as ReactUIDef,\n * });\n *\n * // Transform all to core format\n * const coreDefs = transformToCoreDefs(uis, buildResult);\n *\n * // Now use with createApp\n * const app = createApp({\n * name: \"my-app\",\n * version: \"1.0.0\",\n * tools: {\n * myTool: defineTool({\n * // ... tool definition\n * ui: coreDefs[\"react-widget\"],\n * }),\n * },\n * });\n * ```\n */\nexport function transformToCoreDefs(\n defs: Record<string, ReactUIDef | UIDef>,\n buildResult: BuildResult\n): Record<string, UIDef> {\n const result: Record<string, UIDef> = {};\n\n for (const [key, def] of Object.entries(defs)) {\n if (isReactUIDef(def)) {\n // Get compiled HTML from build result\n const html = buildResult.outputs.get(key);\n if (!html) {\n // Check if there was a build error\n const error = buildResult.errors.find((e) => e.key === key);\n if (error) {\n throw new Error(`Build failed for React UI \"${key}\": ${error.message}`);\n }\n throw new Error(\n `No build output found for React UI \"${key}\". Did you forget to include it in buildReactUIs()?`\n );\n }\n\n // Transform ReactUIDef to UIDef with inline HTML\n result[key] = {\n html, // Inline the compiled HTML\n name: def.name,\n description: def.description,\n widgetDescription: def.widgetDescription,\n csp: def.csp,\n prefersBorder: def.prefersBorder,\n domain: def.domain,\n };\n } else {\n // Pass through standard UIDef unchanged\n result[key] = def;\n }\n }\n\n return result;\n}\n\n/**\n * Transform a single React UI definition to a standard UIDef.\n *\n * @param def - React UI definition\n * @param html - Compiled HTML string from build\n * @returns Standard UIDef with inline HTML\n *\n * @example\n * ```typescript\n * const html = await buildReactUI(\"my-widget\", reactDef);\n * const coreDef = transformSingleToCoreDef(reactDef, html);\n * ```\n *\n * @internal\n */\nexport function transformSingleToCoreDef(def: ReactUIDef, html: string): UIDef {\n return {\n html,\n name: def.name,\n description: def.description,\n widgetDescription: def.widgetDescription,\n csp: def.csp,\n prefersBorder: def.prefersBorder,\n domain: def.domain,\n };\n}\n\n/**\n * Extract React UI definitions from a mixed record.\n *\n * Use this to separate React UIs (that need building) from standard UIs\n * (that are already HTML).\n *\n * @param defs - Record of UI keys to ReactUIDef or UIDef\n * @returns Object with separated React and standard UIs\n *\n * @example\n * ```typescript\n * const { reactUIs, standardUIs } = extractReactUIs(allUIs);\n *\n * // Build only the React UIs\n * const buildResult = await buildReactUIs(reactUIs);\n *\n * // Combine back together\n * const allCoreDefs = {\n * ...standardUIs,\n * ...transformToCoreDefs(reactUIs, buildResult),\n * };\n * ```\n *\n * @internal\n */\nexport function extractReactUIs(defs: Record<string, ReactUIDef | UIDef>): {\n reactUIs: Record<string, ReactUIDef>;\n standardUIs: Record<string, UIDef>;\n} {\n const reactUIs: Record<string, ReactUIDef> = {};\n const standardUIs: Record<string, UIDef> = {};\n\n for (const [key, def] of Object.entries(defs)) {\n if (isReactUIDef(def)) {\n reactUIs[key] = def;\n } else {\n standardUIs[key] = def;\n }\n }\n\n return { reactUIs, standardUIs };\n}\n\n/**\n * Convenience function to build and transform React UIs in one step.\n *\n * This combines `buildReactUIs` and `transformToCoreDefs` for simpler usage.\n *\n * @param defs - Record of UI keys to ReactUIDef or UIDef\n * @param options - Build options (passed to buildReactUIs)\n * @returns Promise resolving to standard UIDefs\n *\n * @example\n * ```typescript\n * import { buildAndTransform, defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { defineUI, createApp } from \"@mcp-apps-kit/core\";\n *\n * const uis = await buildAndTransform({\n * \"react-widget\": defineReactUI({ component: MyWidget }),\n * \"html-widget\": defineUI({ html: \"./widget.html\" }),\n * });\n *\n * // All are now standard UIDefs\n * console.log(uis[\"react-widget\"].html.startsWith(\"<!DOCTYPE html>\")); // true\n * console.log(uis[\"html-widget\"].html); // \"./widget.html\"\n * ```\n */\nexport async function buildAndTransform(\n defs: Record<string, ReactUIDef | UIDef>,\n options?: Parameters<typeof import(\"./build\").buildReactUIs>[1]\n): Promise<Record<string, UIDef>> {\n // Dynamically import build to avoid circular dependencies\n const { buildReactUIs } = await import(\"./build\");\n\n // Separate React UIs from standard UIs\n const { reactUIs, standardUIs } = extractReactUIs(defs);\n\n // If no React UIs, just return standard UIs\n if (Object.keys(reactUIs).length === 0) {\n return standardUIs;\n }\n\n // Build React UIs\n const buildResult = await buildReactUIs(reactUIs, options);\n\n // Check for errors\n if (buildResult.errors.length > 0) {\n const errorMessages = buildResult.errors.map((e) => ` - ${e.key}: ${e.message}`).join(\"\\n\");\n throw new Error(`Failed to build some React UIs:\\n${errorMessages}`);\n }\n\n // Transform and combine\n return {\n ...standardUIs,\n ...transformToCoreDefs(reactUIs, buildResult),\n };\n}\n"]} | ||
| {"version":3,"sources":["../src/define.ts","../src/transform.ts"],"names":["toKebabCase","name","defineReactUI","definition","component","defaultProps","outDir","autoResize","rest","componentName","key","htmlPath","isReactUIDef","value","transformToCoreDefs","defs","buildResult","result","def","html","error","e","transformSingleToCoreDef","extractReactUIs","reactUIs","standardUIs","buildAndTransform","options","buildReactUIs","errorMessages"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Helper functions for defining React-based UIs\n */\n\nimport type { ReactUIInput, ReactUIDef } from \"./types\";\n\n/**\n * Convert component name to kebab-case for filename.\n * @internal\n */\nfunction toKebabCase(name: string): string {\n return name.replace(/([a-z])([A-Z])/g, \"$1-$2\").toLowerCase();\n}\n\n/**\n * Define a UI using a React component.\n *\n * This helper creates a UIDef with an auto-generated HTML path based on\n * the component name. The Vite plugin discovers these definitions and\n * builds the React components into self-contained HTML files.\n *\n * @param definition - React UI input with component and metadata\n * @returns A UIDef with auto-generated html path and React metadata\n *\n * @example Basic usage\n * ```typescript\n * import { defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { MyWidget } from \"./widgets/MyWidget\";\n *\n * const widgetUI = defineReactUI({\n * component: MyWidget,\n * name: \"My Widget\",\n * prefersBorder: true,\n * });\n * // Returns: { html: \"./src/ui/dist/my-widget.html\", name: \"My Widget\", ... }\n * ```\n *\n * @example With tool definition\n * ```typescript\n * import { createApp, defineTool } from \"@mcp-apps-kit/core\";\n * import { defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { RestaurantList } from \"./widgets/RestaurantList\";\n *\n * const app = createApp({\n * name: \"restaurant-finder\",\n * version: \"1.0.0\",\n * tools: {\n * search: defineTool({\n * description: \"Search for restaurants\",\n * input: z.object({ query: z.string() }),\n * output: z.object({ restaurants: z.array(RestaurantSchema) }),\n * ui: defineReactUI({\n * component: RestaurantList,\n * name: \"Restaurant List\",\n * }),\n * handler: async (input) => {\n * // ... search logic\n * },\n * }),\n * },\n * });\n * ```\n *\n * @example Custom output directory\n * ```typescript\n * const widgetUI = defineReactUI({\n * component: ConfigurableWidget,\n * name: \"Configurable Widget\",\n * outDir: \"./dist/ui\",\n * });\n * // Returns: { html: \"./dist/ui/configurable-widget.html\", ... }\n * ```\n */\nexport function defineReactUI(definition: ReactUIInput): ReactUIDef {\n const { component, defaultProps, outDir, autoResize, ...rest } = definition;\n\n // Generate the output path from component name\n const componentName = component.name ?? \"component\";\n const key = toKebabCase(componentName);\n const outputDir = outDir ?? \"./src/ui/dist\";\n const htmlPath = `${outputDir}/${key}.html`;\n\n return {\n ...rest,\n html: htmlPath,\n __reactUI: true,\n __component: component,\n __defaultProps: defaultProps,\n __autoResize: autoResize ?? true,\n };\n}\n\n/**\n * Check if a value is a React UI definition.\n *\n * This type guard is used by the build system to identify\n * React UIs that need to be compiled to HTML.\n *\n * @param value - Value to check\n * @returns True if the value is a ReactUIDef\n *\n * @example\n * ```typescript\n * import { isReactUIDef } from \"@mcp-apps-kit/ui-react-builder\";\n *\n * const ui = someUnknownUI;\n * if (isReactUIDef(ui)) {\n * // ui.__component is available here\n * console.log(\"React UI:\", ui.__component.name);\n * }\n * ```\n *\n * @internal\n */\nexport function isReactUIDef(value: unknown): value is ReactUIDef {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"__reactUI\" in value &&\n (value as ReactUIDef).__reactUI\n );\n}\n","/**\n * Transform utilities for converting ReactUIDef to standard UIDef\n *\n * After building React UIs, this module helps convert them to the\n * standard UIDef format that @mcp-apps-kit/core understands.\n */\n\nimport type { UIDef } from \"@mcp-apps-kit/core\";\nimport type { ReactUIDef, BuildResult } from \"./types\";\nimport { isReactUIDef } from \"./define\";\n\n/**\n * Transform React UI definitions to standard core UIDefs.\n *\n * This function takes a mix of React UIs and standard UIs, and converts\n * all React UIs to standard UIDefs using the build result. Standard UIDefs\n * pass through unchanged.\n *\n * Use this after calling `buildReactUIs` to get definitions that can be\n * passed directly to `createApp`.\n *\n * @param defs - Record of UI keys to ReactUIDef or UIDef\n * @param buildResult - Result from buildReactUIs containing compiled HTML\n * @returns Record of UI keys to standard UIDefs\n *\n * @example\n * ```typescript\n * import { buildReactUIs, transformToCoreDefs, defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { defineUI, createApp, defineTool } from \"@mcp-apps-kit/core\";\n * import { MyWidget } from \"./widgets/MyWidget\";\n *\n * // Define a mix of React and standard UIs\n * const uis = {\n * \"react-widget\": defineReactUI({\n * component: MyWidget,\n * name: \"My Widget\",\n * }),\n * \"html-widget\": defineUI({\n * html: \"./widget.html\",\n * name: \"HTML Widget\",\n * }),\n * };\n *\n * // Build React UIs\n * const buildResult = await buildReactUIs({\n * \"react-widget\": uis[\"react-widget\"] as ReactUIDef,\n * });\n *\n * // Transform all to core format\n * const coreDefs = transformToCoreDefs(uis, buildResult);\n *\n * // Now use with createApp\n * const app = createApp({\n * name: \"my-app\",\n * version: \"1.0.0\",\n * tools: {\n * myTool: defineTool({\n * // ... tool definition\n * ui: coreDefs[\"react-widget\"],\n * }),\n * },\n * });\n * ```\n */\nexport function transformToCoreDefs(\n defs: Record<string, ReactUIDef | UIDef>,\n buildResult: BuildResult\n): Record<string, UIDef> {\n const result: Record<string, UIDef> = {};\n\n for (const [key, def] of Object.entries(defs)) {\n if (isReactUIDef(def)) {\n // Get compiled HTML from build result\n const html = buildResult.outputs.get(key);\n if (!html) {\n // Check if there was a build error\n const error = buildResult.errors.find((e) => e.key === key);\n if (error) {\n throw new Error(`Build failed for React UI \"${key}\": ${error.message}`);\n }\n throw new Error(\n `No build output found for React UI \"${key}\". Did you forget to include it in buildReactUIs()?`\n );\n }\n\n // Transform ReactUIDef to UIDef with inline HTML\n result[key] = {\n html, // Inline the compiled HTML\n name: def.name,\n description: def.description,\n widgetDescription: def.widgetDescription,\n csp: def.csp,\n prefersBorder: def.prefersBorder,\n domain: def.domain,\n };\n } else {\n // Pass through standard UIDef unchanged\n result[key] = def;\n }\n }\n\n return result;\n}\n\n/**\n * Transform a single React UI definition to a standard UIDef.\n *\n * @param def - React UI definition\n * @param html - Compiled HTML string from build\n * @returns Standard UIDef with inline HTML\n *\n * @example\n * ```typescript\n * const html = await buildReactUI(\"my-widget\", reactDef);\n * const coreDef = transformSingleToCoreDef(reactDef, html);\n * ```\n *\n * @internal\n */\nexport function transformSingleToCoreDef(def: ReactUIDef, html: string): UIDef {\n return {\n html,\n name: def.name,\n description: def.description,\n widgetDescription: def.widgetDescription,\n csp: def.csp,\n prefersBorder: def.prefersBorder,\n domain: def.domain,\n };\n}\n\n/**\n * Extract React UI definitions from a mixed record.\n *\n * Use this to separate React UIs (that need building) from standard UIs\n * (that are already HTML).\n *\n * @param defs - Record of UI keys to ReactUIDef or UIDef\n * @returns Object with separated React and standard UIs\n *\n * @example\n * ```typescript\n * const { reactUIs, standardUIs } = extractReactUIs(allUIs);\n *\n * // Build only the React UIs\n * const buildResult = await buildReactUIs(reactUIs);\n *\n * // Combine back together\n * const allCoreDefs = {\n * ...standardUIs,\n * ...transformToCoreDefs(reactUIs, buildResult),\n * };\n * ```\n *\n * @internal\n */\nexport function extractReactUIs(defs: Record<string, ReactUIDef | UIDef>): {\n reactUIs: Record<string, ReactUIDef>;\n standardUIs: Record<string, UIDef>;\n} {\n const reactUIs: Record<string, ReactUIDef> = {};\n const standardUIs: Record<string, UIDef> = {};\n\n for (const [key, def] of Object.entries(defs)) {\n if (isReactUIDef(def)) {\n reactUIs[key] = def;\n } else {\n standardUIs[key] = def;\n }\n }\n\n return { reactUIs, standardUIs };\n}\n\n/**\n * Convenience function to build and transform React UIs in one step.\n *\n * This combines `buildReactUIs` and `transformToCoreDefs` for simpler usage.\n *\n * @param defs - Record of UI keys to ReactUIDef or UIDef\n * @param options - Build options (passed to buildReactUIs)\n * @returns Promise resolving to standard UIDefs\n *\n * @example\n * ```typescript\n * import { buildAndTransform, defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { defineUI, createApp } from \"@mcp-apps-kit/core\";\n *\n * const uis = await buildAndTransform({\n * \"react-widget\": defineReactUI({ component: MyWidget }),\n * \"html-widget\": defineUI({ html: \"./widget.html\" }),\n * });\n *\n * // All are now standard UIDefs\n * console.log(uis[\"react-widget\"].html.startsWith(\"<!DOCTYPE html>\")); // true\n * console.log(uis[\"html-widget\"].html); // \"./widget.html\"\n * ```\n */\nexport async function buildAndTransform(\n defs: Record<string, ReactUIDef | UIDef>,\n options?: Parameters<typeof import(\"./build\").buildReactUIs>[1]\n): Promise<Record<string, UIDef>> {\n // Dynamically import build to avoid circular dependencies\n const { buildReactUIs } = await import(\"./build\");\n\n // Separate React UIs from standard UIs\n const { reactUIs, standardUIs } = extractReactUIs(defs);\n\n // If no React UIs, just return standard UIs\n if (Object.keys(reactUIs).length === 0) {\n return standardUIs;\n }\n\n // Build React UIs\n const buildResult = await buildReactUIs(reactUIs, options);\n\n // Check for errors\n if (buildResult.errors.length > 0) {\n const errorMessages = buildResult.errors.map((e) => ` - ${e.key}: ${e.message}`).join(\"\\n\");\n throw new Error(`Failed to build some React UIs:\\n${errorMessages}`);\n }\n\n // Transform and combine\n return {\n ...standardUIs,\n ...transformToCoreDefs(reactUIs, buildResult),\n };\n}\n"]} |
+10
-10
@@ -1,2 +0,2 @@ | ||
| 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var S=require('esbuild'),f=require('fs/promises'),a=require('path'),typescriptEstree=require('@typescript-eslint/typescript-estree');function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var S__namespace=/*#__PURE__*/_interopNamespace(S);var f__namespace=/*#__PURE__*/_interopNamespace(f);var a__namespace=/*#__PURE__*/_interopNamespace(a);var h=` | ||
| 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var b=require('esbuild'),u=require('fs/promises'),l=require('path'),typescriptEstree=require('@typescript-eslint/typescript-estree');function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var b__namespace=/*#__PURE__*/_interopNamespace(b);var u__namespace=/*#__PURE__*/_interopNamespace(u);var l__namespace=/*#__PURE__*/_interopNamespace(l);var v=` | ||
| *, | ||
@@ -33,4 +33,4 @@ *::before, | ||
| } | ||
| `;function P(t){let{key:r,name:n,script:o,css:e}=t,i=e?`${h} | ||
| ${e}`:h;return `<!DOCTYPE html> | ||
| `;function P(t){let{key:o,name:n,script:r,css:e}=t,i=e?`${v} | ||
| ${e}`:v;return `<!DOCTYPE html> | ||
| <html lang="en"> | ||
@@ -40,4 +40,4 @@ <head> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <meta name="mcp-ui-key" content="${v(r)}"> | ||
| <title>${v(n)}</title> | ||
| <meta name="mcp-ui-key" content="${h(o)}"> | ||
| <title>${h(n)}</title> | ||
| <style>${i}</style> | ||
@@ -47,9 +47,9 @@ </head> | ||
| <div id="root"></div> | ||
| <script type="module">${o.replace(/<\/script>/gi,"</script>")}</script> | ||
| <script type="module">${r.replace(/<\/script>/gi,"</script>")}</script> | ||
| </body> | ||
| </html>`}function v(t){let r={"&":"&","<":"<",">":">",'"':""","'":"'"};return t.replace(/[&<>"']/g,n=>r[n]??n)}async function b(t){let r=typescriptEstree.parse(t,{loc:true,range:true,jsx:true}),n=[],o=new Map;for(let e of r.body)if(e.type===typescriptEstree.AST_NODE_TYPES.ImportDeclaration){let i=e.source.value;for(let s of e.specifiers)if(s.type===typescriptEstree.AST_NODE_TYPES.ImportSpecifier){let c=s.local.name;o.set(c,i);}else if(s.type===typescriptEstree.AST_NODE_TYPES.ImportDefaultSpecifier){let c=s.local.name;o.set(c,i);}}return g(r,e=>{if(e.type===typescriptEstree.AST_NODE_TYPES.CallExpression&&e.callee.type===typescriptEstree.AST_NODE_TYPES.Identifier&&e.callee.name==="defineReactUI"){let i=w(e,o);i&&n.push(i);}}),n}function w(t,r){let n=t.arguments[0];if(n?.type!==typescriptEstree.AST_NODE_TYPES.ObjectExpression)return null;let o=null,e=null;for(let s of n.properties){if(s.type!==typescriptEstree.AST_NODE_TYPES.Property)continue;let c=s.key,l=c.type===typescriptEstree.AST_NODE_TYPES.Identifier?c.name:null;l&&(l==="component"?s.value.type===typescriptEstree.AST_NODE_TYPES.Identifier&&(o=s.value.name):l==="name"&&s.value.type===typescriptEstree.AST_NODE_TYPES.Literal&&typeof s.value.value=="string"&&(e=s.value.value));}if(!o||!e)return null;let i=r.get(o);return i?{componentName:o,importPath:i,name:e}:null}function g(t,r){if(Array.isArray(t)){for(let o of t)g(o,r);return}r(t);let n=t;for(let o of Object.keys(n)){let e=n[o];if(e&&typeof e=="object")if(Array.isArray(e))for(let i of e)i&&typeof i=="object"&&"type"in i&&g(i,r);else "type"in e&&g(e,r);}}var I={info:t=>{console.log(t);},warn:t=>{console.warn(t);},error:t=>{console.error(t);}},C={info:()=>{},warn:()=>{},error:()=>{}};function k(t){let r=t.replace(/\\/g,"/"),n=/^[a-zA-Z]:\//.test(r),o=r.startsWith("//");return r.startsWith(".")||r.startsWith("/")||n||o?r:`./${r}`}function $(t,r){let n=a__namespace.resolve(t),o=a__namespace.resolve(r),e=a__namespace.relative(n,o);return e===""?true:e===".."?false:!e.startsWith(`..${a__namespace.sep}`)&&!a__namespace.isAbsolute(e)}async function U(t,r,n,o,e){if(!t.startsWith("."))return null;let i=await f__namespace.realpath(n),s=a__namespace.resolve(r,t),c=a__namespace.extname(s)?[s]:[".tsx",".ts",".jsx",".js"].map(l=>s+l);for(let l of c){try{await f__namespace.access(l);}catch{continue}let p=await f__namespace.realpath(l);return $(i,p)?p:(e.warn(`[mcp-react-ui] Refusing to build UI component outside project root. component="${o}", import="${t}", resolved="${p}"`),null)}return e.warn(`[mcp-react-ui] Could not resolve component file for "${o}" from import "${t}". Tried extensions: .tsx, .ts, .jsx, .js. Skipping this component.`),null}async function T(t,r,n){let o=a__namespace.resolve(r,t),e=await f__namespace.readFile(o,"utf-8"),i=a__namespace.dirname(o),s=await b(e),c=[];for(let l of s){let p=await U(l.importPath,i,r,l.componentName,n);if(!p)continue;let u=l.componentName.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();c.push({componentName:l.componentName,componentPath:p,name:l.name,key:u});}return c}async function N(t,r,n,o,e){let i=r.minify??o,s=r.outDir??"./dist/ui",c=r.serverConfig??{},l;if(r.globalCss){let p=a__namespace.resolve(n,r.globalCss);try{l=await f__namespace.readFile(p,"utf-8");}catch(u){e.warn(`[mcp-react-ui] globalCss file not found or unreadable: ${p} - ${u instanceof Error?u.message:u}`);}}for(let p of t){let R=` | ||
| </html>`}function h(t){let o={"&":"&","<":"<",">":">",'"':""","'":"'"};return t.replace(/[&<>"']/g,n=>o[n]??n)}async function R(t){let o=typescriptEstree.parse(t,{loc:true,range:true,jsx:true}),n=[],r=new Map;for(let e of o.body)if(e.type===typescriptEstree.AST_NODE_TYPES.ImportDeclaration){let i=e.source.value;for(let c of e.specifiers)if(c.type===typescriptEstree.AST_NODE_TYPES.ImportSpecifier){let s=c.local.name;r.set(s,i);}else if(c.type===typescriptEstree.AST_NODE_TYPES.ImportDefaultSpecifier){let s=c.local.name;r.set(s,i);}}return d(o,e=>{if(e.type===typescriptEstree.AST_NODE_TYPES.CallExpression&&e.callee.type===typescriptEstree.AST_NODE_TYPES.Identifier&&e.callee.name==="defineReactUI"){let i=I(e,r);i&&n.push(i);}}),n}function I(t,o){let n=t.arguments[0];if(n?.type!==typescriptEstree.AST_NODE_TYPES.ObjectExpression)return null;let r=null,e=null,i;for(let s of n.properties){if(s.type!==typescriptEstree.AST_NODE_TYPES.Property)continue;let p=s.key,a=p.type===typescriptEstree.AST_NODE_TYPES.Identifier?p.name:null;a&&(a==="component"?s.value.type===typescriptEstree.AST_NODE_TYPES.Identifier&&(r=s.value.name):a==="name"?s.value.type===typescriptEstree.AST_NODE_TYPES.Literal&&typeof s.value.value=="string"&&(e=s.value.value):a==="autoResize"&&s.value.type===typescriptEstree.AST_NODE_TYPES.Literal&&typeof s.value.value=="boolean"&&(i=s.value.value));}if(!r||!e)return null;let c=o.get(r);return c?{componentName:r,importPath:c,name:e,autoResize:i}:null}function d(t,o){if(Array.isArray(t)){for(let r of t)d(r,o);return}o(t);let n=t;for(let r of Object.keys(n)){let e=n[r];if(e&&typeof e=="object")if(Array.isArray(e))for(let i of e)i&&typeof i=="object"&&"type"in i&&d(i,o);else "type"in e&&d(e,o);}}var C={info:t=>{console.log(t);},warn:t=>{console.warn(t);},error:t=>{console.error(t);}},k={info:()=>{},warn:()=>{},error:()=>{}};function $(t){let o=t.replace(/\\/g,"/"),n=/^[a-zA-Z]:\//.test(o),r=o.startsWith("//");return o.startsWith(".")||o.startsWith("/")||n||r?o:`./${o}`}function U(t,o){let n=l__namespace.resolve(t),r=l__namespace.resolve(o),e=l__namespace.relative(n,r);return e===""?true:e===".."?false:!e.startsWith(`..${l__namespace.sep}`)&&!l__namespace.isAbsolute(e)}async function T(t,o,n,r,e){if(!t.startsWith("."))return null;let i=await u__namespace.realpath(n),c=l__namespace.resolve(o,t),s=l__namespace.extname(c)?[c]:[".tsx",".ts",".jsx",".js"].map(p=>c+p);for(let p of s){try{await u__namespace.access(p);}catch{continue}let a=await u__namespace.realpath(p);return U(i,a)?a:(e.warn(`[mcp-react-ui] Refusing to build UI component outside project root. component="${r}", import="${t}", resolved="${a}"`),null)}return e.warn(`[mcp-react-ui] Could not resolve component file for "${r}" from import "${t}". Tried extensions: .tsx, .ts, .jsx, .js. Skipping this component.`),null}async function N(t,o,n){let r=l__namespace.resolve(o,t),e=await u__namespace.readFile(r,"utf-8"),i=l__namespace.dirname(r),c=await R(e),s=[];for(let p of c){let a=await T(p.importPath,i,o,p.componentName,n);if(!a)continue;let m=p.componentName.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();s.push({componentName:p.componentName,componentPath:a,name:p.name,key:m,autoResize:p.autoResize});}return s}async function D(t,o,n,r,e){let i=o.minify??r,c=o.outDir??"./dist/ui",s=o.serverConfig??{},p;if(o.globalCss){let a=l__namespace.resolve(n,o.globalCss);try{p=await u__namespace.readFile(a,"utf-8");}catch(m){e.warn(`[mcp-react-ui] globalCss file not found or unreadable: ${a} - ${m instanceof Error?m.message:m}`);}}for(let a of t){let m=$(a.componentPath),S=a.autoResize===void 0?"":` autoResize={${a.autoResize}}`,x=` | ||
| import React from "react"; | ||
| import { createRoot } from "react-dom/client"; | ||
| import { AppsProvider } from "@mcp-apps-kit/ui-react"; | ||
| import Component from "${k(p.componentPath)}"; | ||
| import Component from "${m}"; | ||
@@ -61,3 +61,3 @@ const rootElement = document.getElementById("root"); | ||
| <React.StrictMode> | ||
| <AppsProvider> | ||
| <AppsProvider${S}> | ||
| <Component /> | ||
@@ -68,3 +68,3 @@ </AppsProvider> | ||
| } | ||
| `,d=(await S__namespace.build({stdin:{contents:R,loader:"tsx",resolveDir:n,sourcefile:`${p.key}-entry.tsx`},bundle:true,write:false,format:"esm",platform:"browser",target:["es2020","chrome90","firefox90","safari14"],minify:i,jsx:"automatic",jsxImportSource:"react",define:{"process.env.NODE_ENV":i?'"production"':'"development"',__MCP_SERVER_CONFIG__:JSON.stringify(c)}})).outputFiles?.[0]?.text;if(!d)throw new Error(`Failed to build UI: ${p.key}`);let x=P({key:p.key,name:p.name,script:d,css:l}),y=a__namespace.resolve(n,s,`${p.key}.html`);await f__namespace.mkdir(a__namespace.dirname(y),{recursive:true}),await f__namespace.writeFile(y,x,"utf-8"),e.info(`[mcp-react-ui] Built: ${p.key}.html`);}}function D(t){let r,n=t.standalone??false,o=t.logger===false?C:t.logger??I;return {name:"mcp-react-ui",configResolved(e){r=e;},async buildStart(){let e=r.root,i=r.mode==="production",s=await T(t.serverEntry,e,o);if(s.length===0){o.info("[mcp-react-ui] No defineReactUI calls found");return}o.info(`[mcp-react-ui] Found ${s.length} React UI(s): ${s.map(c=>c.componentName).join(", ")}`),await N(s,t,e,i,o);},resolveId(e){return n&&e==="virtual:mcp-react-ui-entry"?e:null},load(e){return n&&e==="virtual:mcp-react-ui-entry"?"export default {}":null},config(){if(n)return {build:{rollupOptions:{input:"virtual:mcp-react-ui-entry"}}}},generateBundle(e,i){if(!n)return;let s=Object.keys(i);for(let c of s)delete i[c];}}}var F=D;exports.default=F;exports.isPathWithinRoot=$;exports.mcpReactUI=D;exports.toEsbuildImportSpecifier=k;//# sourceMappingURL=vite-plugin.cjs.map | ||
| `,g=(await b__namespace.build({stdin:{contents:x,loader:"tsx",resolveDir:n,sourcefile:`${a.key}-entry.tsx`},bundle:true,write:false,format:"esm",platform:"browser",target:["es2020","chrome90","firefox90","safari14"],minify:i,jsx:"automatic",jsxImportSource:"react",define:{"process.env.NODE_ENV":i?'"production"':'"development"',__MCP_SERVER_CONFIG__:JSON.stringify(s)}})).outputFiles?.[0]?.text;if(!g)throw new Error(`Failed to build UI: ${a.key}`);let E=P({key:a.key,name:a.name,script:g,css:p}),y=l__namespace.resolve(n,c,`${a.key}.html`);await u__namespace.mkdir(l__namespace.dirname(y),{recursive:true}),await u__namespace.writeFile(y,E,"utf-8"),e.info(`[mcp-react-ui] Built: ${a.key}.html`);}}function A(t){let o,n=t.standalone??false,r=t.logger===false?k:t.logger??C;return {name:"mcp-react-ui",configResolved(e){o=e;},async buildStart(){let e=o.root,i=o.mode==="production",c=await N(t.serverEntry,e,r);if(c.length===0){r.info("[mcp-react-ui] No defineReactUI calls found");return}r.info(`[mcp-react-ui] Found ${c.length} React UI(s): ${c.map(s=>s.componentName).join(", ")}`),await D(c,t,e,i,r);},resolveId(e){return n&&e==="virtual:mcp-react-ui-entry"?e:null},load(e){return n&&e==="virtual:mcp-react-ui-entry"?"export default {}":null},config(){if(n)return {build:{rollupOptions:{input:"virtual:mcp-react-ui-entry"}}}},generateBundle(e,i){if(!n)return;let c=Object.keys(i);for(let s of c)delete i[s];}}}var F=A;exports.default=F;exports.isPathWithinRoot=U;exports.mcpReactUI=A;exports.toEsbuildImportSpecifier=$;//# sourceMappingURL=vite-plugin.cjs.map | ||
| //# sourceMappingURL=vite-plugin.cjs.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/html.ts","../src/ast-parser.ts","../src/vite-plugin.ts"],"names":["DEFAULT_BASE_CSS","generateHTML","options","key","name","script","css","combinedCss","escapeHtml","text","htmlEntities","char","parseReactUIDefinitions","content","ast","parse","results","importMap","node","AST_NODE_TYPES","importPath","specifier","localName","walkAST","parsed","parseDefineReactUICall","arg","componentName","uiName","prop","keyName","visitor","child","nodeRecord","value","item","defaultLogger","message","silentLogger","toEsbuildImportSpecifier","componentPath","normalized","isWindowsDriveAbsolute","isUncAbsolute","isPathWithinRoot","rootPath","candidatePath","resolvedRoot","a","resolvedCandidate","relative","resolveComponentPath","entryDir","rootDir","logger","rootRealPath","f","basePath","candidatePaths","ext","candidateRealPath","discoverReactUIs","serverEntry","root","entryPath","discovered","ui","buildDiscoveredUIs","isProduction","minify","outDir","serverConfig","globalCss","globalCssPath","error","entryCode","S","html","outputPath","mcpReactUI","config","standalone","resolvedConfig","d","id","_","bundle","keys","vite_plugin_default"],"mappings":"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","file":"vite-plugin.cjs","sourcesContent":["/**\n * HTML template generation for React UIs\n */\n\nimport type { TemplateOptions } from \"./types\";\n\n/**\n * Default base CSS reset for all UIs.\n * Provides a consistent starting point across platforms.\n */\nconst DEFAULT_BASE_CSS = `\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n html {\n font-size: 16px;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen,\n Ubuntu, Cantarell, \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;\n line-height: 1.5;\n color: #1a1a1a;\n background-color: transparent;\n }\n\n @media (prefers-color-scheme: dark) {\n body {\n color: #f5f5f5;\n }\n }\n\n #root {\n min-height: 100%;\n }\n`;\n\n/**\n * Generate a self-contained HTML document for a React UI.\n *\n * The generated HTML includes:\n * - DOCTYPE and valid HTML5 structure\n * - Meta tags for responsive design\n * - Inlined CSS (base reset + optional custom CSS)\n * - Inlined JavaScript bundle with React app\n *\n * @param options - Template options with key, name, script, and optional CSS\n * @returns Complete HTML document as a string\n *\n * @example\n * ```typescript\n * const html = generateHTML({\n * key: \"restaurant-list\",\n * name: \"Restaurant List Widget\",\n * script: bundledJavaScript,\n * css: customStyles,\n * });\n * ```\n *\n * @internal\n */\nexport function generateHTML(options: TemplateOptions): string {\n const { key, name, script, css } = options;\n\n // Combine base CSS with any custom CSS\n const combinedCss = css ? `${DEFAULT_BASE_CSS}\\n${css}` : DEFAULT_BASE_CSS;\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"mcp-ui-key\" content=\"${escapeHtml(key)}\">\n <title>${escapeHtml(name)}</title>\n <style>${combinedCss}</style>\n</head>\n<body>\n <div id=\"root\"></div>\n <script type=\"module\">${script.replace(/<\\/script>/gi, \"</scr\" + \"ipt>\")}</script>\n</body>\n</html>`;\n}\n\n/**\n * Options for generating the React entry point.\n *\n * @internal\n */\nexport interface EntryPointOptions {\n /**\n * Path to the component file.\n */\n componentPath: string;\n\n /**\n * Name of the export to use.\n * Use \"default\" for default exports, or the actual name for named exports.\n * @default \"default\"\n */\n componentExport?: string;\n\n /**\n * Default props to pass to the component.\n */\n defaultProps?: Record<string, unknown>;\n}\n\n/**\n * Generate the React entry point code that will be bundled.\n *\n * This creates a small JavaScript module that:\n * 1. Imports React and ReactDOM\n * 2. Imports the user's component\n * 3. Imports AppsProvider from @mcp-apps-kit/ui-react\n * 4. Renders the component wrapped in providers\n *\n * @param componentPath - Path to the React component file (for backward compatibility)\n * @param defaultProps - Optional default props to pass to the component\n * @returns JavaScript/TypeScript source code for the entry point\n *\n * @example\n * ```typescript\n * // Default export\n * const entryCode = generateEntryPoint(\n * \"./src/widgets/MyWidget.tsx\",\n * { theme: \"dark\" }\n * );\n *\n * // Named export\n * const entryCode = generateEntryPoint({\n * componentPath: \"./src/widgets/MyWidget.tsx\",\n * componentExport: \"MyWidget\",\n * });\n * ```\n *\n * @internal\n */\nexport function generateEntryPoint(\n componentPathOrOptions: string | EntryPointOptions,\n defaultProps?: Record<string, unknown>\n): string {\n // Handle backward compatibility with string-only signature\n const options: EntryPointOptions =\n typeof componentPathOrOptions === \"string\"\n ? { componentPath: componentPathOrOptions, defaultProps }\n : componentPathOrOptions;\n\n const { componentPath, componentExport = \"default\", defaultProps: props } = options;\n const propsJson = props ? JSON.stringify(props) : \"{}\";\n\n // Generate appropriate import statement based on export type\n const importStatement =\n componentExport === \"default\"\n ? `import Component from \"${componentPath}\";`\n : `import { ${componentExport} as Component } from \"${componentPath}\";`;\n\n return `\nimport React from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { AppsProvider } from \"@mcp-apps-kit/ui-react\";\n${importStatement}\n\nconst rootElement = document.getElementById(\"root\");\nif (rootElement) {\n const root = createRoot(rootElement);\n root.render(\n <React.StrictMode>\n <AppsProvider>\n <Component {...${propsJson}} />\n </AppsProvider>\n </React.StrictMode>\n );\n}\n`;\n}\n\n/**\n * Escape HTML special characters to prevent XSS.\n *\n * @param text - Text to escape\n * @returns Escaped text safe for HTML insertion\n */\nfunction escapeHtml(text: string): string {\n const htmlEntities: Record<string, string> = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n '\"': \""\",\n \"'\": \"'\",\n };\n\n return text.replace(/[&<>\"']/g, (char) => htmlEntities[char] ?? char);\n}\n\n/**\n * Extract CSS from a string that may contain both JS and CSS.\n * Used when esbuild bundles CSS as JavaScript that injects styles.\n *\n * @param code - Bundled code that may contain CSS injection\n * @returns Extracted CSS string or undefined\n */\nexport function extractInlineCSS(code: string): string | undefined {\n // esbuild injects CSS as: document.head.appendChild(...).textContent = \"css content\"\n const cssMatch = code.match(/\\.textContent\\s*=\\s*[\"'`]([\\s\\S]*?)[\"'`]\\s*[;,)]/);\n if (cssMatch?.[1]) {\n // Unescape the CSS string\n return cssMatch[1].replace(/\\\\n/g, \"\\n\").replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\");\n }\n return undefined;\n}\n","/**\n * AST-based parser for discovering defineReactUI calls\n *\n * Uses @typescript-eslint/typescript-estree for reliable TypeScript/TSX parsing.\n * This replaces the regex-based approach for more accurate import resolution.\n */\n\nimport { AST_NODE_TYPES, parse, type TSESTree } from \"@typescript-eslint/typescript-estree\";\n\n/**\n * Result of parsing a defineReactUI call\n */\nexport interface ParsedReactUI {\n /** Variable name used for the component */\n componentName: string;\n /** Import path for the component */\n importPath: string;\n /** UI name from the defineReactUI call */\n name: string;\n}\n\n/**\n * Parse a TypeScript/TSX file and extract defineReactUI calls.\n *\n * @param content - File content to parse\n * @returns Array of parsed React UI definitions\n */\nexport async function parseReactUIDefinitions(content: string): Promise<ParsedReactUI[]> {\n const ast = parse(content, {\n loc: true,\n range: true,\n jsx: true,\n // Don't require a project - we just need syntax parsing\n });\n\n const results: ParsedReactUI[] = [];\n\n // Build import map: identifier -> import path\n const importMap = new Map<string, string>();\n\n for (const node of ast.body) {\n if (node.type === AST_NODE_TYPES.ImportDeclaration) {\n const importPath = node.source.value;\n\n for (const specifier of node.specifiers) {\n if (specifier.type === AST_NODE_TYPES.ImportSpecifier) {\n // Named import: import { Foo } from \"./path\" or import { Foo as Bar } from \"./path\"\n const localName = specifier.local.name;\n importMap.set(localName, importPath);\n } else if (specifier.type === AST_NODE_TYPES.ImportDefaultSpecifier) {\n // Default import: import Foo from \"./path\"\n const localName = specifier.local.name;\n importMap.set(localName, importPath);\n }\n }\n }\n }\n\n // Walk the AST to find defineReactUI calls\n walkAST(ast, (node) => {\n if (\n node.type === AST_NODE_TYPES.CallExpression &&\n node.callee.type === AST_NODE_TYPES.Identifier &&\n node.callee.name === \"defineReactUI\"\n ) {\n const parsed = parseDefineReactUICall(node, importMap);\n if (parsed) {\n results.push(parsed);\n }\n }\n });\n\n return results;\n}\n\n/**\n * Parse a defineReactUI call expression\n */\nfunction parseDefineReactUICall(\n node: TSESTree.CallExpression,\n importMap: Map<string, string>\n): ParsedReactUI | null {\n // First argument should be an object expression\n const arg = node.arguments[0];\n if (arg?.type !== AST_NODE_TYPES.ObjectExpression) {\n return null;\n }\n\n let componentName: string | null = null;\n let uiName: string | null = null;\n\n for (const prop of arg.properties) {\n if (prop.type !== AST_NODE_TYPES.Property) continue;\n\n const key = prop.key;\n const keyName = key.type === AST_NODE_TYPES.Identifier ? key.name : null;\n if (!keyName) continue;\n\n if (keyName === \"component\") {\n // Extract component identifier\n if (prop.value.type === AST_NODE_TYPES.Identifier) {\n componentName = prop.value.name;\n }\n } else if (keyName === \"name\") {\n // Extract name string\n if (prop.value.type === AST_NODE_TYPES.Literal && typeof prop.value.value === \"string\") {\n uiName = prop.value.value;\n }\n }\n }\n\n if (!componentName || !uiName) {\n return null;\n }\n\n const importPath = importMap.get(componentName);\n if (!importPath) {\n return null;\n }\n\n return {\n componentName,\n importPath,\n name: uiName,\n };\n}\n\n/**\n * Simple AST walker\n */\nfunction walkAST(\n node: TSESTree.Node | TSESTree.Node[],\n visitor: (node: TSESTree.Node) => void\n): void {\n if (Array.isArray(node)) {\n for (const child of node) {\n walkAST(child, visitor);\n }\n return;\n }\n\n visitor(node);\n\n // Walk child nodes - cast to unknown first to avoid strict type checking\n const nodeRecord = node as unknown as Record<string, unknown>;\n for (const key of Object.keys(nodeRecord)) {\n const value = nodeRecord[key];\n if (value && typeof value === \"object\") {\n if (Array.isArray(value)) {\n for (const item of value) {\n if (item && typeof item === \"object\" && \"type\" in item) {\n walkAST(item as TSESTree.Node, visitor);\n }\n }\n } else if (\"type\" in value) {\n walkAST(value as TSESTree.Node, visitor);\n }\n }\n }\n}\n","/**\n * Vite plugin for building React UI components\n *\n * This plugin automatically discovers `defineReactUI` calls in your source code,\n * resolves the component imports, and builds them into self-contained HTML files.\n *\n * Usage in vite.config.ts:\n * ```typescript\n * import { defineConfig } from \"vite\";\n * import { mcpReactUI } from \"@mcp-apps-kit/ui-react-builder/vite\";\n *\n * export default defineConfig({\n * plugins: [\n * mcpReactUI({\n * // Server entry point to scan for defineReactUI calls\n * serverEntry: \"./src/index.ts\",\n * // Output directory for built HTML files\n * outDir: \"./src/ui/dist\",\n * }),\n * ],\n * });\n * ```\n *\n * Then in your server code:\n * ```typescript\n * import { defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { GreetingWidget } from \"./ui/GreetingWidget\";\n *\n * const greetTool = defineTool({\n * ui: defineReactUI({\n * component: GreetingWidget,\n * name: \"Greeting Widget\",\n * }),\n * // ...\n * });\n * ```\n */\n\nimport type { Plugin, ResolvedConfig } from \"vite\";\nimport * as esbuild from \"esbuild\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { generateHTML } from \"./html\";\nimport { parseReactUIDefinitions } from \"./ast-parser\";\n\n/**\n * Logger interface for the MCP React UI plugin.\n */\nexport interface PluginLogger {\n info: (message: string) => void;\n warn: (message: string) => void;\n error: (message: string) => void;\n}\n\n/**\n * Default logger that uses console methods with a prefix.\n */\nconst defaultLogger: PluginLogger = {\n info: (message: string) => {\n console.log(message); // eslint-disable-line no-console\n },\n warn: (message: string) => {\n console.warn(message); // eslint-disable-line no-console\n },\n error: (message: string) => {\n console.error(message); // eslint-disable-line no-console\n },\n};\n\n/**\n * Silent logger that does nothing.\n */\nconst silentLogger: PluginLogger = {\n info: () => {\n // Intentionally empty - silent logger\n },\n warn: () => {\n // Intentionally empty - silent logger\n },\n error: () => {\n // Intentionally empty - silent logger\n },\n};\n\n/**\n * Server configuration injected into UIs at build time.\n *\n * These values are available in the UI via `getMcpServerConfig()` from @mcp-apps-kit/ui.\n */\nexport type McpServerConfig = {\n /**\n * Base URL of the MCP server.\n *\n * Used by UIs to make API calls (e.g., debug logging via HTTP).\n * Should include protocol and port (e.g., \"http://localhost:3000\").\n *\n * @example \"http://localhost:3000\"\n * @example \"https://api.myapp.com\"\n */\n baseUrl?: string;\n\n /**\n * Additional custom configuration.\n *\n * Any extra values your UI needs at runtime.\n */\n [key: string]: unknown;\n};\n\n/**\n * Options for the MCP React UI Vite plugin.\n */\nexport interface McpReactUIOptions {\n /**\n * Server entry point file to scan for defineReactUI calls.\n * The plugin will parse this file and find all defineReactUI usages,\n * then resolve the component imports to their source files.\n *\n * @example \"./src/index.ts\"\n */\n serverEntry: string;\n\n /**\n * Output directory for built HTML files.\n * @default \"./dist/ui\"\n */\n outDir?: string;\n\n /**\n * Whether to minify the output.\n * Defaults to true in production, false in development.\n */\n minify?: boolean;\n\n /**\n * Path to global CSS file to include in all UIs.\n */\n globalCss?: string;\n\n /**\n * Custom logger for plugin output.\n * Set to `false` to disable all logging, or provide a custom logger.\n * @default console\n */\n logger?: PluginLogger | false;\n\n /**\n * Standalone mode takes over the Vite build.\n *\n * - When `true`, the plugin overrides the build input and removes all Vite outputs,\n * producing only the generated UI HTML files.\n * - When `false` (default), the plugin is additive: it generates UI HTML files\n * without modifying the main Vite build inputs/outputs.\n *\n * Use `true` when your Vite config exists solely to build MCP UI HTML.\n */\n standalone?: boolean;\n\n /**\n * Server configuration to inject into UIs at build time.\n *\n * These values become available in the UI via `getMcpServerConfig()` from @mcp-apps-kit/ui.\n * Useful for injecting the server base URL, API endpoints, or other runtime config.\n *\n * @example\n * ```typescript\n * mcpReactUI({\n * serverEntry: \"./src/index.ts\",\n * serverConfig: {\n * baseUrl: \"http://localhost:3000\",\n * },\n * })\n * ```\n */\n serverConfig?: McpServerConfig;\n}\n\n/**\n * Information about a discovered React UI definition.\n */\ninterface DiscoveredUI {\n /** Variable name used for the component */\n componentName: string;\n /** Resolved file path to the component */\n componentPath: string;\n /** UI name from the defineReactUI call */\n name: string;\n /** Generated key for output file */\n key: string;\n}\n\n/**\n * Convert a filesystem path to an import specifier suitable for esbuild.\n *\n * esbuild accepts absolute paths as specifiers (e.g. \"/abs/file.tsx\", \"C:/abs/file.tsx\").\n * For relative-like paths, we prefix with \"./\".\n *\n * @internal\n */\nexport function toEsbuildImportSpecifier(componentPath: string): string {\n // Normalize path for ESM imports (Windows backslashes -> forward slashes)\n const normalized = componentPath.replace(/\\\\/g, \"/\");\n\n // Absolute path forms we must not prefix with \"./\":\n // - POSIX absolute: /...\n // - Windows drive absolute: C:/...\n // - UNC absolute: //server/share/...\n const isWindowsDriveAbsolute = /^[a-zA-Z]:\\//.test(normalized);\n const isUncAbsolute = normalized.startsWith(\"//\");\n\n if (\n normalized.startsWith(\".\") ||\n normalized.startsWith(\"/\") ||\n isWindowsDriveAbsolute ||\n isUncAbsolute\n ) {\n return normalized;\n }\n\n return `./${normalized}`;\n}\n\n/**\n * Checks whether `candidatePath` is within `rootPath`.\n *\n * Both inputs may be relative; they will be resolved before comparison.\n *\n * @internal\n */\nexport function isPathWithinRoot(rootPath: string, candidatePath: string): boolean {\n const resolvedRoot = path.resolve(rootPath);\n const resolvedCandidate = path.resolve(candidatePath);\n\n const relative = path.relative(resolvedRoot, resolvedCandidate);\n if (relative === \"\") return true;\n if (relative === \"..\") return false;\n\n return !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);\n}\n\n/**\n * Resolve import path to actual file path with extension.\n */\nasync function resolveComponentPath(\n importPath: string,\n entryDir: string,\n rootDir: string,\n componentName: string,\n logger: PluginLogger\n): Promise<string | null> {\n if (!importPath.startsWith(\".\")) {\n // Package import - skip\n return null;\n }\n\n const rootRealPath = await fs.realpath(rootDir);\n const basePath = path.resolve(entryDir, importPath);\n\n const candidatePaths = path.extname(basePath)\n ? [basePath]\n : [\".tsx\", \".ts\", \".jsx\", \".js\"].map((ext) => basePath + ext);\n\n for (const candidatePath of candidatePaths) {\n try {\n await fs.access(candidatePath);\n } catch {\n continue;\n }\n\n // Resolve symlinks before boundary check.\n const candidateRealPath = await fs.realpath(candidatePath);\n if (!isPathWithinRoot(rootRealPath, candidateRealPath)) {\n logger.warn(\n `[mcp-react-ui] Refusing to build UI component outside project root. ` +\n `component=\"${componentName}\", import=\"${importPath}\", resolved=\"${candidateRealPath}\"`\n );\n return null;\n }\n\n return candidateRealPath;\n }\n\n logger.warn(\n `[mcp-react-ui] Could not resolve component file for \"${componentName}\" from import \"${importPath}\". ` +\n `Tried extensions: .tsx, .ts, .jsx, .js. Skipping this component.`\n );\n return null;\n}\n\n/**\n * Scan source file for defineReactUI calls and extract component information.\n * Uses AST parsing for reliable detection of imports and defineReactUI calls.\n */\nasync function discoverReactUIs(\n serverEntry: string,\n root: string,\n logger: PluginLogger\n): Promise<DiscoveredUI[]> {\n const entryPath = path.resolve(root, serverEntry);\n const content = await fs.readFile(entryPath, \"utf-8\");\n const entryDir = path.dirname(entryPath);\n\n const parsed = await parseReactUIDefinitions(content);\n const discovered: DiscoveredUI[] = [];\n\n for (const ui of parsed) {\n const componentPath = await resolveComponentPath(\n ui.importPath,\n entryDir,\n root,\n ui.componentName,\n logger\n );\n if (!componentPath) continue;\n\n const key = ui.componentName.replace(/([a-z])([A-Z])/g, \"$1-$2\").toLowerCase();\n discovered.push({\n componentName: ui.componentName,\n componentPath,\n name: ui.name,\n key,\n });\n }\n\n return discovered;\n}\n\n/**\n * Build discovered React UI components.\n */\nasync function buildDiscoveredUIs(\n discovered: DiscoveredUI[],\n options: McpReactUIOptions,\n root: string,\n isProduction: boolean,\n logger: PluginLogger\n): Promise<void> {\n const minify = options.minify ?? isProduction;\n const outDir = options.outDir ?? \"./dist/ui\";\n const serverConfig = options.serverConfig ?? {};\n\n // Load global CSS if specified\n let globalCss: string | undefined;\n if (options.globalCss) {\n const globalCssPath = path.resolve(root, options.globalCss);\n try {\n globalCss = await fs.readFile(globalCssPath, \"utf-8\");\n } catch (error) {\n logger.warn(\n `[mcp-react-ui] globalCss file not found or unreadable: ${globalCssPath} - ${\n error instanceof Error ? error.message : error\n }`\n );\n }\n }\n\n // Build each discovered UI\n for (const ui of discovered) {\n const importPath = toEsbuildImportSpecifier(ui.componentPath);\n\n // Generate entry point code\n const entryCode = `\nimport React from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { AppsProvider } from \"@mcp-apps-kit/ui-react\";\nimport Component from \"${importPath}\";\n\nconst rootElement = document.getElementById(\"root\");\nif (rootElement) {\n const root = createRoot(rootElement);\n root.render(\n <React.StrictMode>\n <AppsProvider>\n <Component />\n </AppsProvider>\n </React.StrictMode>\n );\n}\n`;\n\n // Bundle with esbuild\n const result = await esbuild.build({\n stdin: {\n contents: entryCode,\n loader: \"tsx\",\n resolveDir: root,\n sourcefile: `${ui.key}-entry.tsx`,\n },\n bundle: true,\n write: false,\n format: \"esm\",\n platform: \"browser\",\n target: [\"es2020\", \"chrome90\", \"firefox90\", \"safari14\"],\n minify,\n jsx: \"automatic\",\n jsxImportSource: \"react\",\n define: {\n \"process.env.NODE_ENV\": minify ? '\"production\"' : '\"development\"',\n __MCP_SERVER_CONFIG__: JSON.stringify(serverConfig),\n },\n });\n\n const script = result.outputFiles?.[0]?.text;\n if (!script) {\n throw new Error(`Failed to build UI: ${ui.key}`);\n }\n\n // Generate HTML\n const html = generateHTML({\n key: ui.key,\n name: ui.name,\n script,\n css: globalCss,\n });\n\n // Write output file\n const outputPath = path.resolve(root, outDir, `${ui.key}.html`);\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.writeFile(outputPath, html, \"utf-8\");\n\n logger.info(`[mcp-react-ui] Built: ${ui.key}.html`);\n }\n}\n\n/**\n * Vite plugin that automatically discovers and builds React UI components.\n *\n * This plugin scans your server entry point for `defineReactUI` calls,\n * resolves the component imports, and builds them into self-contained HTML.\n *\n * @param options - Plugin configuration\n * @returns Vite plugin\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { mcpReactUI } from \"@mcp-apps-kit/ui-react-builder/vite\";\n *\n * export default defineConfig({\n * plugins: [\n * mcpReactUI({\n * serverEntry: \"./src/index.ts\",\n * outDir: \"./src/ui/dist\",\n * }),\n * ],\n * });\n * ```\n */\nexport function mcpReactUI(options: McpReactUIOptions): Plugin {\n let config: ResolvedConfig;\n\n const standalone = options.standalone ?? false;\n\n // Resolve logger: false = silent, undefined = default, custom = use provided\n const logger: PluginLogger =\n options.logger === false ? silentLogger : (options.logger ?? defaultLogger);\n\n return {\n name: \"mcp-react-ui\",\n\n configResolved(resolvedConfig) {\n config = resolvedConfig;\n },\n\n // Run build at the start of the build process\n async buildStart() {\n const root = config.root;\n const isProduction = config.mode === \"production\";\n\n // Discover React UIs from server entry\n const discovered = await discoverReactUIs(options.serverEntry, root, logger);\n\n if (discovered.length === 0) {\n logger.info(\"[mcp-react-ui] No defineReactUI calls found\");\n return;\n }\n\n logger.info(\n `[mcp-react-ui] Found ${discovered.length} React UI(s): ${discovered.map((d) => d.componentName).join(\", \")}`\n );\n\n // Build discovered UIs\n await buildDiscoveredUIs(discovered, options, root, isProduction, logger);\n },\n\n // Provide a virtual empty module so Vite doesn't complain about missing entry\n resolveId(id) {\n if (standalone && id === \"virtual:mcp-react-ui-entry\") {\n return id;\n }\n return null;\n },\n\n load(id) {\n if (standalone && id === \"virtual:mcp-react-ui-entry\") {\n return \"export default {}\";\n }\n return null;\n },\n\n // Override the config to use our virtual entry\n config() {\n if (!standalone) {\n return undefined;\n }\n\n return {\n build: {\n rollupOptions: {\n input: \"virtual:mcp-react-ui-entry\",\n },\n },\n };\n },\n\n // Standalone mode: prevent Vite from generating output files (we already wrote our HTML)\n generateBundle(_, bundle) {\n if (!standalone) {\n return;\n }\n\n // Remove all generated chunks since we don't need them\n const keys = Object.keys(bundle);\n for (const key of keys) {\n // eslint-disable-next-line @typescript-eslint/no-dynamic-delete\n delete bundle[key];\n }\n },\n };\n}\n\nexport default mcpReactUI;\n"]} | ||
| {"version":3,"sources":["../src/html.ts","../src/ast-parser.ts","../src/vite-plugin.ts"],"names":["DEFAULT_BASE_CSS","generateHTML","options","key","name","script","css","combinedCss","escapeHtml","text","htmlEntities","char","parseReactUIDefinitions","content","ast","parse","results","importMap","node","AST_NODE_TYPES","importPath","specifier","localName","walkAST","parsed","parseDefineReactUICall","arg","componentName","uiName","autoResize","prop","keyName","visitor","child","nodeRecord","value","item","defaultLogger","message","silentLogger","toEsbuildImportSpecifier","componentPath","normalized","isWindowsDriveAbsolute","isUncAbsolute","isPathWithinRoot","rootPath","candidatePath","resolvedRoot","l","resolvedCandidate","relative","resolveComponentPath","entryDir","rootDir","logger","rootRealPath","u","basePath","candidatePaths","ext","candidateRealPath","discoverReactUIs","serverEntry","root","entryPath","discovered","ui","buildDiscoveredUIs","isProduction","minify","outDir","serverConfig","globalCss","globalCssPath","error","providerProps","entryCode","b","html","outputPath","mcpReactUI","config","standalone","resolvedConfig","d","id","_","bundle","keys","vite_plugin_default"],"mappings":"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","file":"vite-plugin.cjs","sourcesContent":["/**\n * HTML template generation for React UIs\n */\n\nimport type { TemplateOptions } from \"./types\";\n\n/**\n * Default base CSS reset for all UIs.\n * Provides a consistent starting point across platforms.\n */\nconst DEFAULT_BASE_CSS = `\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n html {\n font-size: 16px;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen,\n Ubuntu, Cantarell, \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;\n line-height: 1.5;\n color: #1a1a1a;\n background-color: transparent;\n }\n\n @media (prefers-color-scheme: dark) {\n body {\n color: #f5f5f5;\n }\n }\n\n #root {\n min-height: 100%;\n }\n`;\n\n/**\n * Generate a self-contained HTML document for a React UI.\n *\n * The generated HTML includes:\n * - DOCTYPE and valid HTML5 structure\n * - Meta tags for responsive design\n * - Inlined CSS (base reset + optional custom CSS)\n * - Inlined JavaScript bundle with React app\n *\n * @param options - Template options with key, name, script, and optional CSS\n * @returns Complete HTML document as a string\n *\n * @example\n * ```typescript\n * const html = generateHTML({\n * key: \"restaurant-list\",\n * name: \"Restaurant List Widget\",\n * script: bundledJavaScript,\n * css: customStyles,\n * });\n * ```\n *\n * @internal\n */\nexport function generateHTML(options: TemplateOptions): string {\n const { key, name, script, css } = options;\n\n // Combine base CSS with any custom CSS\n const combinedCss = css ? `${DEFAULT_BASE_CSS}\\n${css}` : DEFAULT_BASE_CSS;\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"mcp-ui-key\" content=\"${escapeHtml(key)}\">\n <title>${escapeHtml(name)}</title>\n <style>${combinedCss}</style>\n</head>\n<body>\n <div id=\"root\"></div>\n <script type=\"module\">${script.replace(/<\\/script>/gi, \"</scr\" + \"ipt>\")}</script>\n</body>\n</html>`;\n}\n\n/**\n * Options for generating the React entry point.\n *\n * @internal\n */\nexport interface EntryPointOptions {\n /**\n * Path to the component file.\n */\n componentPath: string;\n\n /**\n * Name of the export to use.\n * Use \"default\" for default exports, or the actual name for named exports.\n * @default \"default\"\n */\n componentExport?: string;\n\n /**\n * Default props to pass to the component.\n */\n defaultProps?: Record<string, unknown>;\n\n /**\n * Whether to enable automatic size change notifications.\n * When undefined, uses the default (true).\n */\n autoResize?: boolean;\n}\n\n/**\n * Generate the React entry point code that will be bundled.\n *\n * This creates a small JavaScript module that:\n * 1. Imports React and ReactDOM\n * 2. Imports the user's component\n * 3. Imports AppsProvider from @mcp-apps-kit/ui-react\n * 4. Renders the component wrapped in providers\n *\n * @param componentPath - Path to the React component file (for backward compatibility)\n * @param defaultProps - Optional default props to pass to the component\n * @returns JavaScript/TypeScript source code for the entry point\n *\n * @example\n * ```typescript\n * // Default export\n * const entryCode = generateEntryPoint(\n * \"./src/widgets/MyWidget.tsx\",\n * { theme: \"dark\" }\n * );\n *\n * // Named export\n * const entryCode = generateEntryPoint({\n * componentPath: \"./src/widgets/MyWidget.tsx\",\n * componentExport: \"MyWidget\",\n * });\n * ```\n *\n * @internal\n */\nexport function generateEntryPoint(\n componentPathOrOptions: string | EntryPointOptions,\n defaultProps?: Record<string, unknown>\n): string {\n // Handle backward compatibility with string-only signature\n const options: EntryPointOptions =\n typeof componentPathOrOptions === \"string\"\n ? { componentPath: componentPathOrOptions, defaultProps }\n : componentPathOrOptions;\n\n const { componentPath, componentExport = \"default\", defaultProps: props, autoResize } = options;\n const propsJson = props ? JSON.stringify(props) : \"{}\";\n\n // Generate appropriate import statement based on export type\n const importStatement =\n componentExport === \"default\"\n ? `import Component from \"${componentPath}\";`\n : `import { ${componentExport} as Component } from \"${componentPath}\";`;\n\n // Generate AppsProvider props\n const providerProps = autoResize === undefined ? \"\" : ` autoResize={${autoResize}}`;\n\n return `\nimport React from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { AppsProvider } from \"@mcp-apps-kit/ui-react\";\n${importStatement}\n\nconst rootElement = document.getElementById(\"root\");\nif (rootElement) {\n const root = createRoot(rootElement);\n root.render(\n <React.StrictMode>\n <AppsProvider${providerProps}>\n <Component {...${propsJson}} />\n </AppsProvider>\n </React.StrictMode>\n );\n}\n`;\n}\n\n/**\n * Escape HTML special characters to prevent XSS.\n *\n * @param text - Text to escape\n * @returns Escaped text safe for HTML insertion\n */\nfunction escapeHtml(text: string): string {\n const htmlEntities: Record<string, string> = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n '\"': \""\",\n \"'\": \"'\",\n };\n\n return text.replace(/[&<>\"']/g, (char) => htmlEntities[char] ?? char);\n}\n\n/**\n * Extract CSS from a string that may contain both JS and CSS.\n * Used when esbuild bundles CSS as JavaScript that injects styles.\n *\n * @param code - Bundled code that may contain CSS injection\n * @returns Extracted CSS string or undefined\n */\nexport function extractInlineCSS(code: string): string | undefined {\n // esbuild injects CSS as: document.head.appendChild(...).textContent = \"css content\"\n const cssMatch = code.match(/\\.textContent\\s*=\\s*[\"'`]([\\s\\S]*?)[\"'`]\\s*[;,)]/);\n if (cssMatch?.[1]) {\n // Unescape the CSS string\n return cssMatch[1].replace(/\\\\n/g, \"\\n\").replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\");\n }\n return undefined;\n}\n","/**\n * AST-based parser for discovering defineReactUI calls\n *\n * Uses @typescript-eslint/typescript-estree for reliable TypeScript/TSX parsing.\n * This replaces the regex-based approach for more accurate import resolution.\n */\n\nimport { AST_NODE_TYPES, parse, type TSESTree } from \"@typescript-eslint/typescript-estree\";\n\n/**\n * Result of parsing a defineReactUI call\n */\nexport interface ParsedReactUI {\n /** Variable name used for the component */\n componentName: string;\n /** Import path for the component */\n importPath: string;\n /** UI name from the defineReactUI call */\n name: string;\n /** Whether auto-resize is enabled (undefined means default/true) */\n autoResize?: boolean;\n}\n\n/**\n * Parse a TypeScript/TSX file and extract defineReactUI calls.\n *\n * @param content - File content to parse\n * @returns Array of parsed React UI definitions\n */\nexport async function parseReactUIDefinitions(content: string): Promise<ParsedReactUI[]> {\n const ast = parse(content, {\n loc: true,\n range: true,\n jsx: true,\n // Don't require a project - we just need syntax parsing\n });\n\n const results: ParsedReactUI[] = [];\n\n // Build import map: identifier -> import path\n const importMap = new Map<string, string>();\n\n for (const node of ast.body) {\n if (node.type === AST_NODE_TYPES.ImportDeclaration) {\n const importPath = node.source.value;\n\n for (const specifier of node.specifiers) {\n if (specifier.type === AST_NODE_TYPES.ImportSpecifier) {\n // Named import: import { Foo } from \"./path\" or import { Foo as Bar } from \"./path\"\n const localName = specifier.local.name;\n importMap.set(localName, importPath);\n } else if (specifier.type === AST_NODE_TYPES.ImportDefaultSpecifier) {\n // Default import: import Foo from \"./path\"\n const localName = specifier.local.name;\n importMap.set(localName, importPath);\n }\n }\n }\n }\n\n // Walk the AST to find defineReactUI calls\n walkAST(ast, (node) => {\n if (\n node.type === AST_NODE_TYPES.CallExpression &&\n node.callee.type === AST_NODE_TYPES.Identifier &&\n node.callee.name === \"defineReactUI\"\n ) {\n const parsed = parseDefineReactUICall(node, importMap);\n if (parsed) {\n results.push(parsed);\n }\n }\n });\n\n return results;\n}\n\n/**\n * Parse a defineReactUI call expression\n */\nfunction parseDefineReactUICall(\n node: TSESTree.CallExpression,\n importMap: Map<string, string>\n): ParsedReactUI | null {\n // First argument should be an object expression\n const arg = node.arguments[0];\n if (arg?.type !== AST_NODE_TYPES.ObjectExpression) {\n return null;\n }\n\n let componentName: string | null = null;\n let uiName: string | null = null;\n let autoResize: boolean | undefined = undefined;\n\n for (const prop of arg.properties) {\n if (prop.type !== AST_NODE_TYPES.Property) continue;\n\n const key = prop.key;\n const keyName = key.type === AST_NODE_TYPES.Identifier ? key.name : null;\n if (!keyName) continue;\n\n if (keyName === \"component\") {\n // Extract component identifier\n if (prop.value.type === AST_NODE_TYPES.Identifier) {\n componentName = prop.value.name;\n }\n } else if (keyName === \"name\") {\n // Extract name string\n if (prop.value.type === AST_NODE_TYPES.Literal && typeof prop.value.value === \"string\") {\n uiName = prop.value.value;\n }\n } else if (keyName === \"autoResize\") {\n // Extract autoResize boolean\n if (prop.value.type === AST_NODE_TYPES.Literal && typeof prop.value.value === \"boolean\") {\n autoResize = prop.value.value;\n }\n }\n }\n\n if (!componentName || !uiName) {\n return null;\n }\n\n const importPath = importMap.get(componentName);\n if (!importPath) {\n return null;\n }\n\n return {\n componentName,\n importPath,\n name: uiName,\n autoResize,\n };\n}\n\n/**\n * Simple AST walker\n */\nfunction walkAST(\n node: TSESTree.Node | TSESTree.Node[],\n visitor: (node: TSESTree.Node) => void\n): void {\n if (Array.isArray(node)) {\n for (const child of node) {\n walkAST(child, visitor);\n }\n return;\n }\n\n visitor(node);\n\n // Walk child nodes - cast to unknown first to avoid strict type checking\n const nodeRecord = node as unknown as Record<string, unknown>;\n for (const key of Object.keys(nodeRecord)) {\n const value = nodeRecord[key];\n if (value && typeof value === \"object\") {\n if (Array.isArray(value)) {\n for (const item of value) {\n if (item && typeof item === \"object\" && \"type\" in item) {\n walkAST(item as TSESTree.Node, visitor);\n }\n }\n } else if (\"type\" in value) {\n walkAST(value as TSESTree.Node, visitor);\n }\n }\n }\n}\n","/**\n * Vite plugin for building React UI components\n *\n * This plugin automatically discovers `defineReactUI` calls in your source code,\n * resolves the component imports, and builds them into self-contained HTML files.\n *\n * Usage in vite.config.ts:\n * ```typescript\n * import { defineConfig } from \"vite\";\n * import { mcpReactUI } from \"@mcp-apps-kit/ui-react-builder/vite\";\n *\n * export default defineConfig({\n * plugins: [\n * mcpReactUI({\n * // Server entry point to scan for defineReactUI calls\n * serverEntry: \"./src/index.ts\",\n * // Output directory for built HTML files\n * outDir: \"./src/ui/dist\",\n * }),\n * ],\n * });\n * ```\n *\n * Then in your server code:\n * ```typescript\n * import { defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { GreetingWidget } from \"./ui/GreetingWidget\";\n *\n * const greetTool = defineTool({\n * ui: defineReactUI({\n * component: GreetingWidget,\n * name: \"Greeting Widget\",\n * }),\n * // ...\n * });\n * ```\n */\n\nimport type { Plugin, ResolvedConfig } from \"vite\";\nimport * as esbuild from \"esbuild\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { generateHTML } from \"./html\";\nimport { parseReactUIDefinitions } from \"./ast-parser\";\n\n/**\n * Logger interface for the MCP React UI plugin.\n */\nexport interface PluginLogger {\n info: (message: string) => void;\n warn: (message: string) => void;\n error: (message: string) => void;\n}\n\n/**\n * Default logger that uses console methods with a prefix.\n */\nconst defaultLogger: PluginLogger = {\n info: (message: string) => {\n console.log(message); // eslint-disable-line no-console\n },\n warn: (message: string) => {\n console.warn(message); // eslint-disable-line no-console\n },\n error: (message: string) => {\n console.error(message); // eslint-disable-line no-console\n },\n};\n\n/**\n * Silent logger that does nothing.\n */\nconst silentLogger: PluginLogger = {\n info: () => {\n // Intentionally empty - silent logger\n },\n warn: () => {\n // Intentionally empty - silent logger\n },\n error: () => {\n // Intentionally empty - silent logger\n },\n};\n\n/**\n * Server configuration injected into UIs at build time.\n *\n * These values are available in the UI via `getMcpServerConfig()` from @mcp-apps-kit/ui.\n */\nexport type McpServerConfig = {\n /**\n * Base URL of the MCP server.\n *\n * Used by UIs to make API calls (e.g., debug logging via HTTP).\n * Should include protocol and port (e.g., \"http://localhost:3000\").\n *\n * @example \"http://localhost:3000\"\n * @example \"https://api.myapp.com\"\n */\n baseUrl?: string;\n\n /**\n * Additional custom configuration.\n *\n * Any extra values your UI needs at runtime.\n */\n [key: string]: unknown;\n};\n\n/**\n * Options for the MCP React UI Vite plugin.\n */\nexport interface McpReactUIOptions {\n /**\n * Server entry point file to scan for defineReactUI calls.\n * The plugin will parse this file and find all defineReactUI usages,\n * then resolve the component imports to their source files.\n *\n * @example \"./src/index.ts\"\n */\n serverEntry: string;\n\n /**\n * Output directory for built HTML files.\n * @default \"./dist/ui\"\n */\n outDir?: string;\n\n /**\n * Whether to minify the output.\n * Defaults to true in production, false in development.\n */\n minify?: boolean;\n\n /**\n * Path to global CSS file to include in all UIs.\n */\n globalCss?: string;\n\n /**\n * Custom logger for plugin output.\n * Set to `false` to disable all logging, or provide a custom logger.\n * @default console\n */\n logger?: PluginLogger | false;\n\n /**\n * Standalone mode takes over the Vite build.\n *\n * - When `true`, the plugin overrides the build input and removes all Vite outputs,\n * producing only the generated UI HTML files.\n * - When `false` (default), the plugin is additive: it generates UI HTML files\n * without modifying the main Vite build inputs/outputs.\n *\n * Use `true` when your Vite config exists solely to build MCP UI HTML.\n */\n standalone?: boolean;\n\n /**\n * Server configuration to inject into UIs at build time.\n *\n * These values become available in the UI via `getMcpServerConfig()` from @mcp-apps-kit/ui.\n * Useful for injecting the server base URL, API endpoints, or other runtime config.\n *\n * @example\n * ```typescript\n * mcpReactUI({\n * serverEntry: \"./src/index.ts\",\n * serverConfig: {\n * baseUrl: \"http://localhost:3000\",\n * },\n * })\n * ```\n */\n serverConfig?: McpServerConfig;\n}\n\n/**\n * Information about a discovered React UI definition.\n */\ninterface DiscoveredUI {\n /** Variable name used for the component */\n componentName: string;\n /** Resolved file path to the component */\n componentPath: string;\n /** UI name from the defineReactUI call */\n name: string;\n /** Generated key for output file */\n key: string;\n /** Whether auto-resize is enabled (undefined means default/true) */\n autoResize?: boolean;\n}\n\n/**\n * Convert a filesystem path to an import specifier suitable for esbuild.\n *\n * esbuild accepts absolute paths as specifiers (e.g. \"/abs/file.tsx\", \"C:/abs/file.tsx\").\n * For relative-like paths, we prefix with \"./\".\n *\n * @internal\n */\nexport function toEsbuildImportSpecifier(componentPath: string): string {\n // Normalize path for ESM imports (Windows backslashes -> forward slashes)\n const normalized = componentPath.replace(/\\\\/g, \"/\");\n\n // Absolute path forms we must not prefix with \"./\":\n // - POSIX absolute: /...\n // - Windows drive absolute: C:/...\n // - UNC absolute: //server/share/...\n const isWindowsDriveAbsolute = /^[a-zA-Z]:\\//.test(normalized);\n const isUncAbsolute = normalized.startsWith(\"//\");\n\n if (\n normalized.startsWith(\".\") ||\n normalized.startsWith(\"/\") ||\n isWindowsDriveAbsolute ||\n isUncAbsolute\n ) {\n return normalized;\n }\n\n return `./${normalized}`;\n}\n\n/**\n * Checks whether `candidatePath` is within `rootPath`.\n *\n * Both inputs may be relative; they will be resolved before comparison.\n *\n * @internal\n */\nexport function isPathWithinRoot(rootPath: string, candidatePath: string): boolean {\n const resolvedRoot = path.resolve(rootPath);\n const resolvedCandidate = path.resolve(candidatePath);\n\n const relative = path.relative(resolvedRoot, resolvedCandidate);\n if (relative === \"\") return true;\n if (relative === \"..\") return false;\n\n return !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);\n}\n\n/**\n * Resolve import path to actual file path with extension.\n */\nasync function resolveComponentPath(\n importPath: string,\n entryDir: string,\n rootDir: string,\n componentName: string,\n logger: PluginLogger\n): Promise<string | null> {\n if (!importPath.startsWith(\".\")) {\n // Package import - skip\n return null;\n }\n\n const rootRealPath = await fs.realpath(rootDir);\n const basePath = path.resolve(entryDir, importPath);\n\n const candidatePaths = path.extname(basePath)\n ? [basePath]\n : [\".tsx\", \".ts\", \".jsx\", \".js\"].map((ext) => basePath + ext);\n\n for (const candidatePath of candidatePaths) {\n try {\n await fs.access(candidatePath);\n } catch {\n continue;\n }\n\n // Resolve symlinks before boundary check.\n const candidateRealPath = await fs.realpath(candidatePath);\n if (!isPathWithinRoot(rootRealPath, candidateRealPath)) {\n logger.warn(\n `[mcp-react-ui] Refusing to build UI component outside project root. ` +\n `component=\"${componentName}\", import=\"${importPath}\", resolved=\"${candidateRealPath}\"`\n );\n return null;\n }\n\n return candidateRealPath;\n }\n\n logger.warn(\n `[mcp-react-ui] Could not resolve component file for \"${componentName}\" from import \"${importPath}\". ` +\n `Tried extensions: .tsx, .ts, .jsx, .js. Skipping this component.`\n );\n return null;\n}\n\n/**\n * Scan source file for defineReactUI calls and extract component information.\n * Uses AST parsing for reliable detection of imports and defineReactUI calls.\n */\nasync function discoverReactUIs(\n serverEntry: string,\n root: string,\n logger: PluginLogger\n): Promise<DiscoveredUI[]> {\n const entryPath = path.resolve(root, serverEntry);\n const content = await fs.readFile(entryPath, \"utf-8\");\n const entryDir = path.dirname(entryPath);\n\n const parsed = await parseReactUIDefinitions(content);\n const discovered: DiscoveredUI[] = [];\n\n for (const ui of parsed) {\n const componentPath = await resolveComponentPath(\n ui.importPath,\n entryDir,\n root,\n ui.componentName,\n logger\n );\n if (!componentPath) continue;\n\n const key = ui.componentName.replace(/([a-z])([A-Z])/g, \"$1-$2\").toLowerCase();\n discovered.push({\n componentName: ui.componentName,\n componentPath,\n name: ui.name,\n key,\n autoResize: ui.autoResize,\n });\n }\n\n return discovered;\n}\n\n/**\n * Build discovered React UI components.\n */\nasync function buildDiscoveredUIs(\n discovered: DiscoveredUI[],\n options: McpReactUIOptions,\n root: string,\n isProduction: boolean,\n logger: PluginLogger\n): Promise<void> {\n const minify = options.minify ?? isProduction;\n const outDir = options.outDir ?? \"./dist/ui\";\n const serverConfig = options.serverConfig ?? {};\n\n // Load global CSS if specified\n let globalCss: string | undefined;\n if (options.globalCss) {\n const globalCssPath = path.resolve(root, options.globalCss);\n try {\n globalCss = await fs.readFile(globalCssPath, \"utf-8\");\n } catch (error) {\n logger.warn(\n `[mcp-react-ui] globalCss file not found or unreadable: ${globalCssPath} - ${\n error instanceof Error ? error.message : error\n }`\n );\n }\n }\n\n // Build each discovered UI\n for (const ui of discovered) {\n const importPath = toEsbuildImportSpecifier(ui.componentPath);\n\n // Generate AppsProvider props based on autoResize setting\n const providerProps = ui.autoResize === undefined ? \"\" : ` autoResize={${ui.autoResize}}`;\n\n // Generate entry point code\n const entryCode = `\nimport React from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { AppsProvider } from \"@mcp-apps-kit/ui-react\";\nimport Component from \"${importPath}\";\n\nconst rootElement = document.getElementById(\"root\");\nif (rootElement) {\n const root = createRoot(rootElement);\n root.render(\n <React.StrictMode>\n <AppsProvider${providerProps}>\n <Component />\n </AppsProvider>\n </React.StrictMode>\n );\n}\n`;\n\n // Bundle with esbuild\n const result = await esbuild.build({\n stdin: {\n contents: entryCode,\n loader: \"tsx\",\n resolveDir: root,\n sourcefile: `${ui.key}-entry.tsx`,\n },\n bundle: true,\n write: false,\n format: \"esm\",\n platform: \"browser\",\n target: [\"es2020\", \"chrome90\", \"firefox90\", \"safari14\"],\n minify,\n jsx: \"automatic\",\n jsxImportSource: \"react\",\n define: {\n \"process.env.NODE_ENV\": minify ? '\"production\"' : '\"development\"',\n __MCP_SERVER_CONFIG__: JSON.stringify(serverConfig),\n },\n });\n\n const script = result.outputFiles?.[0]?.text;\n if (!script) {\n throw new Error(`Failed to build UI: ${ui.key}`);\n }\n\n // Generate HTML\n const html = generateHTML({\n key: ui.key,\n name: ui.name,\n script,\n css: globalCss,\n });\n\n // Write output file\n const outputPath = path.resolve(root, outDir, `${ui.key}.html`);\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.writeFile(outputPath, html, \"utf-8\");\n\n logger.info(`[mcp-react-ui] Built: ${ui.key}.html`);\n }\n}\n\n/**\n * Vite plugin that automatically discovers and builds React UI components.\n *\n * This plugin scans your server entry point for `defineReactUI` calls,\n * resolves the component imports, and builds them into self-contained HTML.\n *\n * @param options - Plugin configuration\n * @returns Vite plugin\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { mcpReactUI } from \"@mcp-apps-kit/ui-react-builder/vite\";\n *\n * export default defineConfig({\n * plugins: [\n * mcpReactUI({\n * serverEntry: \"./src/index.ts\",\n * outDir: \"./src/ui/dist\",\n * }),\n * ],\n * });\n * ```\n */\nexport function mcpReactUI(options: McpReactUIOptions): Plugin {\n let config: ResolvedConfig;\n\n const standalone = options.standalone ?? false;\n\n // Resolve logger: false = silent, undefined = default, custom = use provided\n const logger: PluginLogger =\n options.logger === false ? silentLogger : (options.logger ?? defaultLogger);\n\n return {\n name: \"mcp-react-ui\",\n\n configResolved(resolvedConfig) {\n config = resolvedConfig;\n },\n\n // Run build at the start of the build process\n async buildStart() {\n const root = config.root;\n const isProduction = config.mode === \"production\";\n\n // Discover React UIs from server entry\n const discovered = await discoverReactUIs(options.serverEntry, root, logger);\n\n if (discovered.length === 0) {\n logger.info(\"[mcp-react-ui] No defineReactUI calls found\");\n return;\n }\n\n logger.info(\n `[mcp-react-ui] Found ${discovered.length} React UI(s): ${discovered.map((d) => d.componentName).join(\", \")}`\n );\n\n // Build discovered UIs\n await buildDiscoveredUIs(discovered, options, root, isProduction, logger);\n },\n\n // Provide a virtual empty module so Vite doesn't complain about missing entry\n resolveId(id) {\n if (standalone && id === \"virtual:mcp-react-ui-entry\") {\n return id;\n }\n return null;\n },\n\n load(id) {\n if (standalone && id === \"virtual:mcp-react-ui-entry\") {\n return \"export default {}\";\n }\n return null;\n },\n\n // Override the config to use our virtual entry\n config() {\n if (!standalone) {\n return undefined;\n }\n\n return {\n build: {\n rollupOptions: {\n input: \"virtual:mcp-react-ui-entry\",\n },\n },\n };\n },\n\n // Standalone mode: prevent Vite from generating output files (we already wrote our HTML)\n generateBundle(_, bundle) {\n if (!standalone) {\n return;\n }\n\n // Remove all generated chunks since we don't need them\n const keys = Object.keys(bundle);\n for (const key of keys) {\n // eslint-disable-next-line @typescript-eslint/no-dynamic-delete\n delete bundle[key];\n }\n },\n };\n}\n\nexport default mcpReactUI;\n"]} |
@@ -1,6 +0,6 @@ | ||
| import {a as a$1}from'./chunk-6JS3KVIH.js';import*as P from'esbuild';import*as f from'fs/promises';import*as a from'path';import {parse,AST_NODE_TYPES}from'@typescript-eslint/typescript-estree';async function h(t){let r=parse(t,{loc:true,range:true,jsx:true}),i=[],o=new Map;for(let e of r.body)if(e.type===AST_NODE_TYPES.ImportDeclaration){let s=e.source.value;for(let n of e.specifiers)if(n.type===AST_NODE_TYPES.ImportSpecifier){let c=n.local.name;o.set(c,s);}else if(n.type===AST_NODE_TYPES.ImportDefaultSpecifier){let c=n.local.name;o.set(c,s);}}return g(r,e=>{if(e.type===AST_NODE_TYPES.CallExpression&&e.callee.type===AST_NODE_TYPES.Identifier&&e.callee.name==="defineReactUI"){let s=w(e,o);s&&i.push(s);}}),i}function w(t,r){let i=t.arguments[0];if(i?.type!==AST_NODE_TYPES.ObjectExpression)return null;let o=null,e=null;for(let n of i.properties){if(n.type!==AST_NODE_TYPES.Property)continue;let c=n.key,l=c.type===AST_NODE_TYPES.Identifier?c.name:null;l&&(l==="component"?n.value.type===AST_NODE_TYPES.Identifier&&(o=n.value.name):l==="name"&&n.value.type===AST_NODE_TYPES.Literal&&typeof n.value.value=="string"&&(e=n.value.value));}if(!o||!e)return null;let s=r.get(o);return s?{componentName:o,importPath:s,name:e}:null}function g(t,r){if(Array.isArray(t)){for(let o of t)g(o,r);return}r(t);let i=t;for(let o of Object.keys(i)){let e=i[o];if(e&&typeof e=="object")if(Array.isArray(e))for(let s of e)s&&typeof s=="object"&&"type"in s&&g(s,r);else "type"in e&&g(e,r);}}var S={info:t=>{console.log(t);},warn:t=>{console.warn(t);},error:t=>{console.error(t);}},x={info:()=>{},warn:()=>{},error:()=>{}};function C(t){let r=t.replace(/\\/g,"/"),i=/^[a-zA-Z]:\//.test(r),o=r.startsWith("//");return r.startsWith(".")||r.startsWith("/")||i||o?r:`./${r}`}function E(t,r){let i=a.resolve(t),o=a.resolve(r),e=a.relative(i,o);return e===""?true:e===".."?false:!e.startsWith(`..${a.sep}`)&&!a.isAbsolute(e)}async function k(t,r,i,o,e){if(!t.startsWith("."))return null;let s=await f.realpath(i),n=a.resolve(r,t),c=a.extname(n)?[n]:[".tsx",".ts",".jsx",".js"].map(l=>n+l);for(let l of c){try{await f.access(l);}catch{continue}let u=await f.realpath(l);return E(s,u)?u:(e.warn(`[mcp-react-ui] Refusing to build UI component outside project root. component="${o}", import="${t}", resolved="${u}"`),null)}return e.warn(`[mcp-react-ui] Could not resolve component file for "${o}" from import "${t}". Tried extensions: .tsx, .ts, .jsx, .js. Skipping this component.`),null}async function U(t,r,i){let o=a.resolve(r,t),e=await f.readFile(o,"utf-8"),s=a.dirname(o),n=await h(e),c=[];for(let l of n){let u=await k(l.importPath,s,r,l.componentName,i);if(!u)continue;let m=l.componentName.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();c.push({componentName:l.componentName,componentPath:u,name:l.name,key:m});}return c}async function N(t,r,i,o,e){let s=r.minify??o,n=r.outDir??"./dist/ui",c=r.serverConfig??{},l;if(r.globalCss){let u=a.resolve(i,r.globalCss);try{l=await f.readFile(u,"utf-8");}catch(m){e.warn(`[mcp-react-ui] globalCss file not found or unreadable: ${u} - ${m instanceof Error?m.message:m}`);}}for(let u of t){let I=` | ||
| import {a}from'./chunk-R7RJYHEX.js';import*as P from'esbuild';import*as f from'fs/promises';import*as l from'path';import {parse,AST_NODE_TYPES}from'@typescript-eslint/typescript-estree';async function h(t){let r=parse(t,{loc:true,range:true,jsx:true}),n=[],o=new Map;for(let e of r.body)if(e.type===AST_NODE_TYPES.ImportDeclaration){let i=e.source.value;for(let c of e.specifiers)if(c.type===AST_NODE_TYPES.ImportSpecifier){let s=c.local.name;o.set(s,i);}else if(c.type===AST_NODE_TYPES.ImportDefaultSpecifier){let s=c.local.name;o.set(s,i);}}return d(r,e=>{if(e.type===AST_NODE_TYPES.CallExpression&&e.callee.type===AST_NODE_TYPES.Identifier&&e.callee.name==="defineReactUI"){let i=S(e,o);i&&n.push(i);}}),n}function S(t,r){let n=t.arguments[0];if(n?.type!==AST_NODE_TYPES.ObjectExpression)return null;let o=null,e=null,i;for(let s of n.properties){if(s.type!==AST_NODE_TYPES.Property)continue;let u=s.key,a=u.type===AST_NODE_TYPES.Identifier?u.name:null;a&&(a==="component"?s.value.type===AST_NODE_TYPES.Identifier&&(o=s.value.name):a==="name"?s.value.type===AST_NODE_TYPES.Literal&&typeof s.value.value=="string"&&(e=s.value.value):a==="autoResize"&&s.value.type===AST_NODE_TYPES.Literal&&typeof s.value.value=="boolean"&&(i=s.value.value));}if(!o||!e)return null;let c=r.get(o);return c?{componentName:o,importPath:c,name:e,autoResize:i}:null}function d(t,r){if(Array.isArray(t)){for(let o of t)d(o,r);return}r(t);let n=t;for(let o of Object.keys(n)){let e=n[o];if(e&&typeof e=="object")if(Array.isArray(e))for(let i of e)i&&typeof i=="object"&&"type"in i&&d(i,r);else "type"in e&&d(e,r);}}var x={info:t=>{console.log(t);},warn:t=>{console.warn(t);},error:t=>{console.error(t);}},C={info:()=>{},warn:()=>{},error:()=>{}};function E(t){let r=t.replace(/\\/g,"/"),n=/^[a-zA-Z]:\//.test(r),o=r.startsWith("//");return r.startsWith(".")||r.startsWith("/")||n||o?r:`./${r}`}function k(t,r){let n=l.resolve(t),o=l.resolve(r),e=l.relative(n,o);return e===""?true:e===".."?false:!e.startsWith(`..${l.sep}`)&&!l.isAbsolute(e)}async function U(t,r,n,o,e){if(!t.startsWith("."))return null;let i=await f.realpath(n),c=l.resolve(r,t),s=l.extname(c)?[c]:[".tsx",".ts",".jsx",".js"].map(u=>c+u);for(let u of s){try{await f.access(u);}catch{continue}let a=await f.realpath(u);return k(i,a)?a:(e.warn(`[mcp-react-ui] Refusing to build UI component outside project root. component="${o}", import="${t}", resolved="${a}"`),null)}return e.warn(`[mcp-react-ui] Could not resolve component file for "${o}" from import "${t}". Tried extensions: .tsx, .ts, .jsx, .js. Skipping this component.`),null}async function N(t,r,n){let o=l.resolve(r,t),e=await f.readFile(o,"utf-8"),i=l.dirname(o),c=await h(e),s=[];for(let u of c){let a=await U(u.importPath,i,r,u.componentName,n);if(!a)continue;let m=u.componentName.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();s.push({componentName:u.componentName,componentPath:a,name:u.name,key:m,autoResize:u.autoResize});}return s}async function $(t,r,n,o,e){let i=r.minify??o,c=r.outDir??"./dist/ui",s=r.serverConfig??{},u;if(r.globalCss){let a=l.resolve(n,r.globalCss);try{u=await f.readFile(a,"utf-8");}catch(m){e.warn(`[mcp-react-ui] globalCss file not found or unreadable: ${a} - ${m instanceof Error?m.message:m}`);}}for(let a$1 of t){let m=E(a$1.componentPath),R=a$1.autoResize===void 0?"":` autoResize={${a$1.autoResize}}`,b=` | ||
| import React from "react"; | ||
| import { createRoot } from "react-dom/client"; | ||
| import { AppsProvider } from "@mcp-apps-kit/ui-react"; | ||
| import Component from "${C(u.componentPath)}"; | ||
| import Component from "${m}"; | ||
@@ -12,3 +12,3 @@ const rootElement = document.getElementById("root"); | ||
| <React.StrictMode> | ||
| <AppsProvider> | ||
| <AppsProvider${R}> | ||
| <Component /> | ||
@@ -19,3 +19,3 @@ </AppsProvider> | ||
| } | ||
| `,d=(await P.build({stdin:{contents:I,loader:"tsx",resolveDir:i,sourcefile:`${u.key}-entry.tsx`},bundle:true,write:false,format:"esm",platform:"browser",target:["es2020","chrome90","firefox90","safari14"],minify:s,jsx:"automatic",jsxImportSource:"react",define:{"process.env.NODE_ENV":s?'"production"':'"development"',__MCP_SERVER_CONFIG__:JSON.stringify(c)}})).outputFiles?.[0]?.text;if(!d)throw new Error(`Failed to build UI: ${u.key}`);let b=a$1({key:u.key,name:u.name,script:d,css:l}),y=a.resolve(i,n,`${u.key}.html`);await f.mkdir(a.dirname(y),{recursive:true}),await f.writeFile(y,b,"utf-8"),e.info(`[mcp-react-ui] Built: ${u.key}.html`);}}function T(t){let r,i=t.standalone??false,o=t.logger===false?x:t.logger??S;return {name:"mcp-react-ui",configResolved(e){r=e;},async buildStart(){let e=r.root,s=r.mode==="production",n=await U(t.serverEntry,e,o);if(n.length===0){o.info("[mcp-react-ui] No defineReactUI calls found");return}o.info(`[mcp-react-ui] Found ${n.length} React UI(s): ${n.map(c=>c.componentName).join(", ")}`),await N(n,t,e,s,o);},resolveId(e){return i&&e==="virtual:mcp-react-ui-entry"?e:null},load(e){return i&&e==="virtual:mcp-react-ui-entry"?"export default {}":null},config(){if(i)return {build:{rollupOptions:{input:"virtual:mcp-react-ui-entry"}}}},generateBundle(e,s){if(!i)return;let n=Object.keys(s);for(let c of n)delete s[c];}}}var M=T;export{M as default,E as isPathWithinRoot,T as mcpReactUI,C as toEsbuildImportSpecifier};//# sourceMappingURL=vite-plugin.js.map | ||
| `,g=(await P.build({stdin:{contents:b,loader:"tsx",resolveDir:n,sourcefile:`${a$1.key}-entry.tsx`},bundle:true,write:false,format:"esm",platform:"browser",target:["es2020","chrome90","firefox90","safari14"],minify:i,jsx:"automatic",jsxImportSource:"react",define:{"process.env.NODE_ENV":i?'"production"':'"development"',__MCP_SERVER_CONFIG__:JSON.stringify(s)}})).outputFiles?.[0]?.text;if(!g)throw new Error(`Failed to build UI: ${a$1.key}`);let I=a({key:a$1.key,name:a$1.name,script:g,css:u}),y=l.resolve(n,c,`${a$1.key}.html`);await f.mkdir(l.dirname(y),{recursive:true}),await f.writeFile(y,I,"utf-8"),e.info(`[mcp-react-ui] Built: ${a$1.key}.html`);}}function T(t){let r,n=t.standalone??false,o=t.logger===false?C:t.logger??x;return {name:"mcp-react-ui",configResolved(e){r=e;},async buildStart(){let e=r.root,i=r.mode==="production",c=await N(t.serverEntry,e,o);if(c.length===0){o.info("[mcp-react-ui] No defineReactUI calls found");return}o.info(`[mcp-react-ui] Found ${c.length} React UI(s): ${c.map(s=>s.componentName).join(", ")}`),await $(c,t,e,i,o);},resolveId(e){return n&&e==="virtual:mcp-react-ui-entry"?e:null},load(e){return n&&e==="virtual:mcp-react-ui-entry"?"export default {}":null},config(){if(n)return {build:{rollupOptions:{input:"virtual:mcp-react-ui-entry"}}}},generateBundle(e,i){if(!n)return;let c=Object.keys(i);for(let s of c)delete i[s];}}}var M=T;export{M as default,k as isPathWithinRoot,T as mcpReactUI,E as toEsbuildImportSpecifier};//# sourceMappingURL=vite-plugin.js.map | ||
| //# sourceMappingURL=vite-plugin.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/ast-parser.ts","../src/vite-plugin.ts"],"names":["parseReactUIDefinitions","content","ast","parse","results","importMap","node","AST_NODE_TYPES","importPath","specifier","localName","walkAST","parsed","parseDefineReactUICall","arg","componentName","uiName","prop","key","keyName","visitor","child","nodeRecord","value","item","defaultLogger","message","silentLogger","toEsbuildImportSpecifier","componentPath","normalized","isWindowsDriveAbsolute","isUncAbsolute","isPathWithinRoot","rootPath","candidatePath","resolvedRoot","resolvedCandidate","relative","resolveComponentPath","entryDir","rootDir","logger","rootRealPath","basePath","candidatePaths","ext","candidateRealPath","discoverReactUIs","serverEntry","root","entryPath","discovered","ui","buildDiscoveredUIs","options","isProduction","minify","outDir","serverConfig","globalCss","globalCssPath","error","entryCode","script","html","generateHTML","outputPath","mcpReactUI","config","standalone","resolvedConfig","d","id","_","bundle","keys","vite_plugin_default"],"mappings":"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","file":"vite-plugin.js","sourcesContent":["/**\n * AST-based parser for discovering defineReactUI calls\n *\n * Uses @typescript-eslint/typescript-estree for reliable TypeScript/TSX parsing.\n * This replaces the regex-based approach for more accurate import resolution.\n */\n\nimport { AST_NODE_TYPES, parse, type TSESTree } from \"@typescript-eslint/typescript-estree\";\n\n/**\n * Result of parsing a defineReactUI call\n */\nexport interface ParsedReactUI {\n /** Variable name used for the component */\n componentName: string;\n /** Import path for the component */\n importPath: string;\n /** UI name from the defineReactUI call */\n name: string;\n}\n\n/**\n * Parse a TypeScript/TSX file and extract defineReactUI calls.\n *\n * @param content - File content to parse\n * @returns Array of parsed React UI definitions\n */\nexport async function parseReactUIDefinitions(content: string): Promise<ParsedReactUI[]> {\n const ast = parse(content, {\n loc: true,\n range: true,\n jsx: true,\n // Don't require a project - we just need syntax parsing\n });\n\n const results: ParsedReactUI[] = [];\n\n // Build import map: identifier -> import path\n const importMap = new Map<string, string>();\n\n for (const node of ast.body) {\n if (node.type === AST_NODE_TYPES.ImportDeclaration) {\n const importPath = node.source.value;\n\n for (const specifier of node.specifiers) {\n if (specifier.type === AST_NODE_TYPES.ImportSpecifier) {\n // Named import: import { Foo } from \"./path\" or import { Foo as Bar } from \"./path\"\n const localName = specifier.local.name;\n importMap.set(localName, importPath);\n } else if (specifier.type === AST_NODE_TYPES.ImportDefaultSpecifier) {\n // Default import: import Foo from \"./path\"\n const localName = specifier.local.name;\n importMap.set(localName, importPath);\n }\n }\n }\n }\n\n // Walk the AST to find defineReactUI calls\n walkAST(ast, (node) => {\n if (\n node.type === AST_NODE_TYPES.CallExpression &&\n node.callee.type === AST_NODE_TYPES.Identifier &&\n node.callee.name === \"defineReactUI\"\n ) {\n const parsed = parseDefineReactUICall(node, importMap);\n if (parsed) {\n results.push(parsed);\n }\n }\n });\n\n return results;\n}\n\n/**\n * Parse a defineReactUI call expression\n */\nfunction parseDefineReactUICall(\n node: TSESTree.CallExpression,\n importMap: Map<string, string>\n): ParsedReactUI | null {\n // First argument should be an object expression\n const arg = node.arguments[0];\n if (arg?.type !== AST_NODE_TYPES.ObjectExpression) {\n return null;\n }\n\n let componentName: string | null = null;\n let uiName: string | null = null;\n\n for (const prop of arg.properties) {\n if (prop.type !== AST_NODE_TYPES.Property) continue;\n\n const key = prop.key;\n const keyName = key.type === AST_NODE_TYPES.Identifier ? key.name : null;\n if (!keyName) continue;\n\n if (keyName === \"component\") {\n // Extract component identifier\n if (prop.value.type === AST_NODE_TYPES.Identifier) {\n componentName = prop.value.name;\n }\n } else if (keyName === \"name\") {\n // Extract name string\n if (prop.value.type === AST_NODE_TYPES.Literal && typeof prop.value.value === \"string\") {\n uiName = prop.value.value;\n }\n }\n }\n\n if (!componentName || !uiName) {\n return null;\n }\n\n const importPath = importMap.get(componentName);\n if (!importPath) {\n return null;\n }\n\n return {\n componentName,\n importPath,\n name: uiName,\n };\n}\n\n/**\n * Simple AST walker\n */\nfunction walkAST(\n node: TSESTree.Node | TSESTree.Node[],\n visitor: (node: TSESTree.Node) => void\n): void {\n if (Array.isArray(node)) {\n for (const child of node) {\n walkAST(child, visitor);\n }\n return;\n }\n\n visitor(node);\n\n // Walk child nodes - cast to unknown first to avoid strict type checking\n const nodeRecord = node as unknown as Record<string, unknown>;\n for (const key of Object.keys(nodeRecord)) {\n const value = nodeRecord[key];\n if (value && typeof value === \"object\") {\n if (Array.isArray(value)) {\n for (const item of value) {\n if (item && typeof item === \"object\" && \"type\" in item) {\n walkAST(item as TSESTree.Node, visitor);\n }\n }\n } else if (\"type\" in value) {\n walkAST(value as TSESTree.Node, visitor);\n }\n }\n }\n}\n","/**\n * Vite plugin for building React UI components\n *\n * This plugin automatically discovers `defineReactUI` calls in your source code,\n * resolves the component imports, and builds them into self-contained HTML files.\n *\n * Usage in vite.config.ts:\n * ```typescript\n * import { defineConfig } from \"vite\";\n * import { mcpReactUI } from \"@mcp-apps-kit/ui-react-builder/vite\";\n *\n * export default defineConfig({\n * plugins: [\n * mcpReactUI({\n * // Server entry point to scan for defineReactUI calls\n * serverEntry: \"./src/index.ts\",\n * // Output directory for built HTML files\n * outDir: \"./src/ui/dist\",\n * }),\n * ],\n * });\n * ```\n *\n * Then in your server code:\n * ```typescript\n * import { defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { GreetingWidget } from \"./ui/GreetingWidget\";\n *\n * const greetTool = defineTool({\n * ui: defineReactUI({\n * component: GreetingWidget,\n * name: \"Greeting Widget\",\n * }),\n * // ...\n * });\n * ```\n */\n\nimport type { Plugin, ResolvedConfig } from \"vite\";\nimport * as esbuild from \"esbuild\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { generateHTML } from \"./html\";\nimport { parseReactUIDefinitions } from \"./ast-parser\";\n\n/**\n * Logger interface for the MCP React UI plugin.\n */\nexport interface PluginLogger {\n info: (message: string) => void;\n warn: (message: string) => void;\n error: (message: string) => void;\n}\n\n/**\n * Default logger that uses console methods with a prefix.\n */\nconst defaultLogger: PluginLogger = {\n info: (message: string) => {\n console.log(message); // eslint-disable-line no-console\n },\n warn: (message: string) => {\n console.warn(message); // eslint-disable-line no-console\n },\n error: (message: string) => {\n console.error(message); // eslint-disable-line no-console\n },\n};\n\n/**\n * Silent logger that does nothing.\n */\nconst silentLogger: PluginLogger = {\n info: () => {\n // Intentionally empty - silent logger\n },\n warn: () => {\n // Intentionally empty - silent logger\n },\n error: () => {\n // Intentionally empty - silent logger\n },\n};\n\n/**\n * Server configuration injected into UIs at build time.\n *\n * These values are available in the UI via `getMcpServerConfig()` from @mcp-apps-kit/ui.\n */\nexport type McpServerConfig = {\n /**\n * Base URL of the MCP server.\n *\n * Used by UIs to make API calls (e.g., debug logging via HTTP).\n * Should include protocol and port (e.g., \"http://localhost:3000\").\n *\n * @example \"http://localhost:3000\"\n * @example \"https://api.myapp.com\"\n */\n baseUrl?: string;\n\n /**\n * Additional custom configuration.\n *\n * Any extra values your UI needs at runtime.\n */\n [key: string]: unknown;\n};\n\n/**\n * Options for the MCP React UI Vite plugin.\n */\nexport interface McpReactUIOptions {\n /**\n * Server entry point file to scan for defineReactUI calls.\n * The plugin will parse this file and find all defineReactUI usages,\n * then resolve the component imports to their source files.\n *\n * @example \"./src/index.ts\"\n */\n serverEntry: string;\n\n /**\n * Output directory for built HTML files.\n * @default \"./dist/ui\"\n */\n outDir?: string;\n\n /**\n * Whether to minify the output.\n * Defaults to true in production, false in development.\n */\n minify?: boolean;\n\n /**\n * Path to global CSS file to include in all UIs.\n */\n globalCss?: string;\n\n /**\n * Custom logger for plugin output.\n * Set to `false` to disable all logging, or provide a custom logger.\n * @default console\n */\n logger?: PluginLogger | false;\n\n /**\n * Standalone mode takes over the Vite build.\n *\n * - When `true`, the plugin overrides the build input and removes all Vite outputs,\n * producing only the generated UI HTML files.\n * - When `false` (default), the plugin is additive: it generates UI HTML files\n * without modifying the main Vite build inputs/outputs.\n *\n * Use `true` when your Vite config exists solely to build MCP UI HTML.\n */\n standalone?: boolean;\n\n /**\n * Server configuration to inject into UIs at build time.\n *\n * These values become available in the UI via `getMcpServerConfig()` from @mcp-apps-kit/ui.\n * Useful for injecting the server base URL, API endpoints, or other runtime config.\n *\n * @example\n * ```typescript\n * mcpReactUI({\n * serverEntry: \"./src/index.ts\",\n * serverConfig: {\n * baseUrl: \"http://localhost:3000\",\n * },\n * })\n * ```\n */\n serverConfig?: McpServerConfig;\n}\n\n/**\n * Information about a discovered React UI definition.\n */\ninterface DiscoveredUI {\n /** Variable name used for the component */\n componentName: string;\n /** Resolved file path to the component */\n componentPath: string;\n /** UI name from the defineReactUI call */\n name: string;\n /** Generated key for output file */\n key: string;\n}\n\n/**\n * Convert a filesystem path to an import specifier suitable for esbuild.\n *\n * esbuild accepts absolute paths as specifiers (e.g. \"/abs/file.tsx\", \"C:/abs/file.tsx\").\n * For relative-like paths, we prefix with \"./\".\n *\n * @internal\n */\nexport function toEsbuildImportSpecifier(componentPath: string): string {\n // Normalize path for ESM imports (Windows backslashes -> forward slashes)\n const normalized = componentPath.replace(/\\\\/g, \"/\");\n\n // Absolute path forms we must not prefix with \"./\":\n // - POSIX absolute: /...\n // - Windows drive absolute: C:/...\n // - UNC absolute: //server/share/...\n const isWindowsDriveAbsolute = /^[a-zA-Z]:\\//.test(normalized);\n const isUncAbsolute = normalized.startsWith(\"//\");\n\n if (\n normalized.startsWith(\".\") ||\n normalized.startsWith(\"/\") ||\n isWindowsDriveAbsolute ||\n isUncAbsolute\n ) {\n return normalized;\n }\n\n return `./${normalized}`;\n}\n\n/**\n * Checks whether `candidatePath` is within `rootPath`.\n *\n * Both inputs may be relative; they will be resolved before comparison.\n *\n * @internal\n */\nexport function isPathWithinRoot(rootPath: string, candidatePath: string): boolean {\n const resolvedRoot = path.resolve(rootPath);\n const resolvedCandidate = path.resolve(candidatePath);\n\n const relative = path.relative(resolvedRoot, resolvedCandidate);\n if (relative === \"\") return true;\n if (relative === \"..\") return false;\n\n return !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);\n}\n\n/**\n * Resolve import path to actual file path with extension.\n */\nasync function resolveComponentPath(\n importPath: string,\n entryDir: string,\n rootDir: string,\n componentName: string,\n logger: PluginLogger\n): Promise<string | null> {\n if (!importPath.startsWith(\".\")) {\n // Package import - skip\n return null;\n }\n\n const rootRealPath = await fs.realpath(rootDir);\n const basePath = path.resolve(entryDir, importPath);\n\n const candidatePaths = path.extname(basePath)\n ? [basePath]\n : [\".tsx\", \".ts\", \".jsx\", \".js\"].map((ext) => basePath + ext);\n\n for (const candidatePath of candidatePaths) {\n try {\n await fs.access(candidatePath);\n } catch {\n continue;\n }\n\n // Resolve symlinks before boundary check.\n const candidateRealPath = await fs.realpath(candidatePath);\n if (!isPathWithinRoot(rootRealPath, candidateRealPath)) {\n logger.warn(\n `[mcp-react-ui] Refusing to build UI component outside project root. ` +\n `component=\"${componentName}\", import=\"${importPath}\", resolved=\"${candidateRealPath}\"`\n );\n return null;\n }\n\n return candidateRealPath;\n }\n\n logger.warn(\n `[mcp-react-ui] Could not resolve component file for \"${componentName}\" from import \"${importPath}\". ` +\n `Tried extensions: .tsx, .ts, .jsx, .js. Skipping this component.`\n );\n return null;\n}\n\n/**\n * Scan source file for defineReactUI calls and extract component information.\n * Uses AST parsing for reliable detection of imports and defineReactUI calls.\n */\nasync function discoverReactUIs(\n serverEntry: string,\n root: string,\n logger: PluginLogger\n): Promise<DiscoveredUI[]> {\n const entryPath = path.resolve(root, serverEntry);\n const content = await fs.readFile(entryPath, \"utf-8\");\n const entryDir = path.dirname(entryPath);\n\n const parsed = await parseReactUIDefinitions(content);\n const discovered: DiscoveredUI[] = [];\n\n for (const ui of parsed) {\n const componentPath = await resolveComponentPath(\n ui.importPath,\n entryDir,\n root,\n ui.componentName,\n logger\n );\n if (!componentPath) continue;\n\n const key = ui.componentName.replace(/([a-z])([A-Z])/g, \"$1-$2\").toLowerCase();\n discovered.push({\n componentName: ui.componentName,\n componentPath,\n name: ui.name,\n key,\n });\n }\n\n return discovered;\n}\n\n/**\n * Build discovered React UI components.\n */\nasync function buildDiscoveredUIs(\n discovered: DiscoveredUI[],\n options: McpReactUIOptions,\n root: string,\n isProduction: boolean,\n logger: PluginLogger\n): Promise<void> {\n const minify = options.minify ?? isProduction;\n const outDir = options.outDir ?? \"./dist/ui\";\n const serverConfig = options.serverConfig ?? {};\n\n // Load global CSS if specified\n let globalCss: string | undefined;\n if (options.globalCss) {\n const globalCssPath = path.resolve(root, options.globalCss);\n try {\n globalCss = await fs.readFile(globalCssPath, \"utf-8\");\n } catch (error) {\n logger.warn(\n `[mcp-react-ui] globalCss file not found or unreadable: ${globalCssPath} - ${\n error instanceof Error ? error.message : error\n }`\n );\n }\n }\n\n // Build each discovered UI\n for (const ui of discovered) {\n const importPath = toEsbuildImportSpecifier(ui.componentPath);\n\n // Generate entry point code\n const entryCode = `\nimport React from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { AppsProvider } from \"@mcp-apps-kit/ui-react\";\nimport Component from \"${importPath}\";\n\nconst rootElement = document.getElementById(\"root\");\nif (rootElement) {\n const root = createRoot(rootElement);\n root.render(\n <React.StrictMode>\n <AppsProvider>\n <Component />\n </AppsProvider>\n </React.StrictMode>\n );\n}\n`;\n\n // Bundle with esbuild\n const result = await esbuild.build({\n stdin: {\n contents: entryCode,\n loader: \"tsx\",\n resolveDir: root,\n sourcefile: `${ui.key}-entry.tsx`,\n },\n bundle: true,\n write: false,\n format: \"esm\",\n platform: \"browser\",\n target: [\"es2020\", \"chrome90\", \"firefox90\", \"safari14\"],\n minify,\n jsx: \"automatic\",\n jsxImportSource: \"react\",\n define: {\n \"process.env.NODE_ENV\": minify ? '\"production\"' : '\"development\"',\n __MCP_SERVER_CONFIG__: JSON.stringify(serverConfig),\n },\n });\n\n const script = result.outputFiles?.[0]?.text;\n if (!script) {\n throw new Error(`Failed to build UI: ${ui.key}`);\n }\n\n // Generate HTML\n const html = generateHTML({\n key: ui.key,\n name: ui.name,\n script,\n css: globalCss,\n });\n\n // Write output file\n const outputPath = path.resolve(root, outDir, `${ui.key}.html`);\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.writeFile(outputPath, html, \"utf-8\");\n\n logger.info(`[mcp-react-ui] Built: ${ui.key}.html`);\n }\n}\n\n/**\n * Vite plugin that automatically discovers and builds React UI components.\n *\n * This plugin scans your server entry point for `defineReactUI` calls,\n * resolves the component imports, and builds them into self-contained HTML.\n *\n * @param options - Plugin configuration\n * @returns Vite plugin\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { mcpReactUI } from \"@mcp-apps-kit/ui-react-builder/vite\";\n *\n * export default defineConfig({\n * plugins: [\n * mcpReactUI({\n * serverEntry: \"./src/index.ts\",\n * outDir: \"./src/ui/dist\",\n * }),\n * ],\n * });\n * ```\n */\nexport function mcpReactUI(options: McpReactUIOptions): Plugin {\n let config: ResolvedConfig;\n\n const standalone = options.standalone ?? false;\n\n // Resolve logger: false = silent, undefined = default, custom = use provided\n const logger: PluginLogger =\n options.logger === false ? silentLogger : (options.logger ?? defaultLogger);\n\n return {\n name: \"mcp-react-ui\",\n\n configResolved(resolvedConfig) {\n config = resolvedConfig;\n },\n\n // Run build at the start of the build process\n async buildStart() {\n const root = config.root;\n const isProduction = config.mode === \"production\";\n\n // Discover React UIs from server entry\n const discovered = await discoverReactUIs(options.serverEntry, root, logger);\n\n if (discovered.length === 0) {\n logger.info(\"[mcp-react-ui] No defineReactUI calls found\");\n return;\n }\n\n logger.info(\n `[mcp-react-ui] Found ${discovered.length} React UI(s): ${discovered.map((d) => d.componentName).join(\", \")}`\n );\n\n // Build discovered UIs\n await buildDiscoveredUIs(discovered, options, root, isProduction, logger);\n },\n\n // Provide a virtual empty module so Vite doesn't complain about missing entry\n resolveId(id) {\n if (standalone && id === \"virtual:mcp-react-ui-entry\") {\n return id;\n }\n return null;\n },\n\n load(id) {\n if (standalone && id === \"virtual:mcp-react-ui-entry\") {\n return \"export default {}\";\n }\n return null;\n },\n\n // Override the config to use our virtual entry\n config() {\n if (!standalone) {\n return undefined;\n }\n\n return {\n build: {\n rollupOptions: {\n input: \"virtual:mcp-react-ui-entry\",\n },\n },\n };\n },\n\n // Standalone mode: prevent Vite from generating output files (we already wrote our HTML)\n generateBundle(_, bundle) {\n if (!standalone) {\n return;\n }\n\n // Remove all generated chunks since we don't need them\n const keys = Object.keys(bundle);\n for (const key of keys) {\n // eslint-disable-next-line @typescript-eslint/no-dynamic-delete\n delete bundle[key];\n }\n },\n };\n}\n\nexport default mcpReactUI;\n"]} | ||
| {"version":3,"sources":["../src/ast-parser.ts","../src/vite-plugin.ts"],"names":["parseReactUIDefinitions","content","ast","parse","results","importMap","node","AST_NODE_TYPES","importPath","specifier","localName","walkAST","parsed","parseDefineReactUICall","arg","componentName","uiName","autoResize","prop","key","keyName","visitor","child","nodeRecord","value","item","defaultLogger","message","silentLogger","toEsbuildImportSpecifier","componentPath","normalized","isWindowsDriveAbsolute","isUncAbsolute","isPathWithinRoot","rootPath","candidatePath","resolvedRoot","resolvedCandidate","relative","resolveComponentPath","entryDir","rootDir","logger","rootRealPath","basePath","candidatePaths","ext","candidateRealPath","discoverReactUIs","serverEntry","root","entryPath","discovered","ui","buildDiscoveredUIs","options","isProduction","minify","outDir","serverConfig","globalCss","globalCssPath","error","providerProps","entryCode","script","html","generateHTML","outputPath","mcpReactUI","config","standalone","resolvedConfig","d","id","_","bundle","keys","vite_plugin_default"],"mappings":"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","file":"vite-plugin.js","sourcesContent":["/**\n * AST-based parser for discovering defineReactUI calls\n *\n * Uses @typescript-eslint/typescript-estree for reliable TypeScript/TSX parsing.\n * This replaces the regex-based approach for more accurate import resolution.\n */\n\nimport { AST_NODE_TYPES, parse, type TSESTree } from \"@typescript-eslint/typescript-estree\";\n\n/**\n * Result of parsing a defineReactUI call\n */\nexport interface ParsedReactUI {\n /** Variable name used for the component */\n componentName: string;\n /** Import path for the component */\n importPath: string;\n /** UI name from the defineReactUI call */\n name: string;\n /** Whether auto-resize is enabled (undefined means default/true) */\n autoResize?: boolean;\n}\n\n/**\n * Parse a TypeScript/TSX file and extract defineReactUI calls.\n *\n * @param content - File content to parse\n * @returns Array of parsed React UI definitions\n */\nexport async function parseReactUIDefinitions(content: string): Promise<ParsedReactUI[]> {\n const ast = parse(content, {\n loc: true,\n range: true,\n jsx: true,\n // Don't require a project - we just need syntax parsing\n });\n\n const results: ParsedReactUI[] = [];\n\n // Build import map: identifier -> import path\n const importMap = new Map<string, string>();\n\n for (const node of ast.body) {\n if (node.type === AST_NODE_TYPES.ImportDeclaration) {\n const importPath = node.source.value;\n\n for (const specifier of node.specifiers) {\n if (specifier.type === AST_NODE_TYPES.ImportSpecifier) {\n // Named import: import { Foo } from \"./path\" or import { Foo as Bar } from \"./path\"\n const localName = specifier.local.name;\n importMap.set(localName, importPath);\n } else if (specifier.type === AST_NODE_TYPES.ImportDefaultSpecifier) {\n // Default import: import Foo from \"./path\"\n const localName = specifier.local.name;\n importMap.set(localName, importPath);\n }\n }\n }\n }\n\n // Walk the AST to find defineReactUI calls\n walkAST(ast, (node) => {\n if (\n node.type === AST_NODE_TYPES.CallExpression &&\n node.callee.type === AST_NODE_TYPES.Identifier &&\n node.callee.name === \"defineReactUI\"\n ) {\n const parsed = parseDefineReactUICall(node, importMap);\n if (parsed) {\n results.push(parsed);\n }\n }\n });\n\n return results;\n}\n\n/**\n * Parse a defineReactUI call expression\n */\nfunction parseDefineReactUICall(\n node: TSESTree.CallExpression,\n importMap: Map<string, string>\n): ParsedReactUI | null {\n // First argument should be an object expression\n const arg = node.arguments[0];\n if (arg?.type !== AST_NODE_TYPES.ObjectExpression) {\n return null;\n }\n\n let componentName: string | null = null;\n let uiName: string | null = null;\n let autoResize: boolean | undefined = undefined;\n\n for (const prop of arg.properties) {\n if (prop.type !== AST_NODE_TYPES.Property) continue;\n\n const key = prop.key;\n const keyName = key.type === AST_NODE_TYPES.Identifier ? key.name : null;\n if (!keyName) continue;\n\n if (keyName === \"component\") {\n // Extract component identifier\n if (prop.value.type === AST_NODE_TYPES.Identifier) {\n componentName = prop.value.name;\n }\n } else if (keyName === \"name\") {\n // Extract name string\n if (prop.value.type === AST_NODE_TYPES.Literal && typeof prop.value.value === \"string\") {\n uiName = prop.value.value;\n }\n } else if (keyName === \"autoResize\") {\n // Extract autoResize boolean\n if (prop.value.type === AST_NODE_TYPES.Literal && typeof prop.value.value === \"boolean\") {\n autoResize = prop.value.value;\n }\n }\n }\n\n if (!componentName || !uiName) {\n return null;\n }\n\n const importPath = importMap.get(componentName);\n if (!importPath) {\n return null;\n }\n\n return {\n componentName,\n importPath,\n name: uiName,\n autoResize,\n };\n}\n\n/**\n * Simple AST walker\n */\nfunction walkAST(\n node: TSESTree.Node | TSESTree.Node[],\n visitor: (node: TSESTree.Node) => void\n): void {\n if (Array.isArray(node)) {\n for (const child of node) {\n walkAST(child, visitor);\n }\n return;\n }\n\n visitor(node);\n\n // Walk child nodes - cast to unknown first to avoid strict type checking\n const nodeRecord = node as unknown as Record<string, unknown>;\n for (const key of Object.keys(nodeRecord)) {\n const value = nodeRecord[key];\n if (value && typeof value === \"object\") {\n if (Array.isArray(value)) {\n for (const item of value) {\n if (item && typeof item === \"object\" && \"type\" in item) {\n walkAST(item as TSESTree.Node, visitor);\n }\n }\n } else if (\"type\" in value) {\n walkAST(value as TSESTree.Node, visitor);\n }\n }\n }\n}\n","/**\n * Vite plugin for building React UI components\n *\n * This plugin automatically discovers `defineReactUI` calls in your source code,\n * resolves the component imports, and builds them into self-contained HTML files.\n *\n * Usage in vite.config.ts:\n * ```typescript\n * import { defineConfig } from \"vite\";\n * import { mcpReactUI } from \"@mcp-apps-kit/ui-react-builder/vite\";\n *\n * export default defineConfig({\n * plugins: [\n * mcpReactUI({\n * // Server entry point to scan for defineReactUI calls\n * serverEntry: \"./src/index.ts\",\n * // Output directory for built HTML files\n * outDir: \"./src/ui/dist\",\n * }),\n * ],\n * });\n * ```\n *\n * Then in your server code:\n * ```typescript\n * import { defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n * import { GreetingWidget } from \"./ui/GreetingWidget\";\n *\n * const greetTool = defineTool({\n * ui: defineReactUI({\n * component: GreetingWidget,\n * name: \"Greeting Widget\",\n * }),\n * // ...\n * });\n * ```\n */\n\nimport type { Plugin, ResolvedConfig } from \"vite\";\nimport * as esbuild from \"esbuild\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { generateHTML } from \"./html\";\nimport { parseReactUIDefinitions } from \"./ast-parser\";\n\n/**\n * Logger interface for the MCP React UI plugin.\n */\nexport interface PluginLogger {\n info: (message: string) => void;\n warn: (message: string) => void;\n error: (message: string) => void;\n}\n\n/**\n * Default logger that uses console methods with a prefix.\n */\nconst defaultLogger: PluginLogger = {\n info: (message: string) => {\n console.log(message); // eslint-disable-line no-console\n },\n warn: (message: string) => {\n console.warn(message); // eslint-disable-line no-console\n },\n error: (message: string) => {\n console.error(message); // eslint-disable-line no-console\n },\n};\n\n/**\n * Silent logger that does nothing.\n */\nconst silentLogger: PluginLogger = {\n info: () => {\n // Intentionally empty - silent logger\n },\n warn: () => {\n // Intentionally empty - silent logger\n },\n error: () => {\n // Intentionally empty - silent logger\n },\n};\n\n/**\n * Server configuration injected into UIs at build time.\n *\n * These values are available in the UI via `getMcpServerConfig()` from @mcp-apps-kit/ui.\n */\nexport type McpServerConfig = {\n /**\n * Base URL of the MCP server.\n *\n * Used by UIs to make API calls (e.g., debug logging via HTTP).\n * Should include protocol and port (e.g., \"http://localhost:3000\").\n *\n * @example \"http://localhost:3000\"\n * @example \"https://api.myapp.com\"\n */\n baseUrl?: string;\n\n /**\n * Additional custom configuration.\n *\n * Any extra values your UI needs at runtime.\n */\n [key: string]: unknown;\n};\n\n/**\n * Options for the MCP React UI Vite plugin.\n */\nexport interface McpReactUIOptions {\n /**\n * Server entry point file to scan for defineReactUI calls.\n * The plugin will parse this file and find all defineReactUI usages,\n * then resolve the component imports to their source files.\n *\n * @example \"./src/index.ts\"\n */\n serverEntry: string;\n\n /**\n * Output directory for built HTML files.\n * @default \"./dist/ui\"\n */\n outDir?: string;\n\n /**\n * Whether to minify the output.\n * Defaults to true in production, false in development.\n */\n minify?: boolean;\n\n /**\n * Path to global CSS file to include in all UIs.\n */\n globalCss?: string;\n\n /**\n * Custom logger for plugin output.\n * Set to `false` to disable all logging, or provide a custom logger.\n * @default console\n */\n logger?: PluginLogger | false;\n\n /**\n * Standalone mode takes over the Vite build.\n *\n * - When `true`, the plugin overrides the build input and removes all Vite outputs,\n * producing only the generated UI HTML files.\n * - When `false` (default), the plugin is additive: it generates UI HTML files\n * without modifying the main Vite build inputs/outputs.\n *\n * Use `true` when your Vite config exists solely to build MCP UI HTML.\n */\n standalone?: boolean;\n\n /**\n * Server configuration to inject into UIs at build time.\n *\n * These values become available in the UI via `getMcpServerConfig()` from @mcp-apps-kit/ui.\n * Useful for injecting the server base URL, API endpoints, or other runtime config.\n *\n * @example\n * ```typescript\n * mcpReactUI({\n * serverEntry: \"./src/index.ts\",\n * serverConfig: {\n * baseUrl: \"http://localhost:3000\",\n * },\n * })\n * ```\n */\n serverConfig?: McpServerConfig;\n}\n\n/**\n * Information about a discovered React UI definition.\n */\ninterface DiscoveredUI {\n /** Variable name used for the component */\n componentName: string;\n /** Resolved file path to the component */\n componentPath: string;\n /** UI name from the defineReactUI call */\n name: string;\n /** Generated key for output file */\n key: string;\n /** Whether auto-resize is enabled (undefined means default/true) */\n autoResize?: boolean;\n}\n\n/**\n * Convert a filesystem path to an import specifier suitable for esbuild.\n *\n * esbuild accepts absolute paths as specifiers (e.g. \"/abs/file.tsx\", \"C:/abs/file.tsx\").\n * For relative-like paths, we prefix with \"./\".\n *\n * @internal\n */\nexport function toEsbuildImportSpecifier(componentPath: string): string {\n // Normalize path for ESM imports (Windows backslashes -> forward slashes)\n const normalized = componentPath.replace(/\\\\/g, \"/\");\n\n // Absolute path forms we must not prefix with \"./\":\n // - POSIX absolute: /...\n // - Windows drive absolute: C:/...\n // - UNC absolute: //server/share/...\n const isWindowsDriveAbsolute = /^[a-zA-Z]:\\//.test(normalized);\n const isUncAbsolute = normalized.startsWith(\"//\");\n\n if (\n normalized.startsWith(\".\") ||\n normalized.startsWith(\"/\") ||\n isWindowsDriveAbsolute ||\n isUncAbsolute\n ) {\n return normalized;\n }\n\n return `./${normalized}`;\n}\n\n/**\n * Checks whether `candidatePath` is within `rootPath`.\n *\n * Both inputs may be relative; they will be resolved before comparison.\n *\n * @internal\n */\nexport function isPathWithinRoot(rootPath: string, candidatePath: string): boolean {\n const resolvedRoot = path.resolve(rootPath);\n const resolvedCandidate = path.resolve(candidatePath);\n\n const relative = path.relative(resolvedRoot, resolvedCandidate);\n if (relative === \"\") return true;\n if (relative === \"..\") return false;\n\n return !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);\n}\n\n/**\n * Resolve import path to actual file path with extension.\n */\nasync function resolveComponentPath(\n importPath: string,\n entryDir: string,\n rootDir: string,\n componentName: string,\n logger: PluginLogger\n): Promise<string | null> {\n if (!importPath.startsWith(\".\")) {\n // Package import - skip\n return null;\n }\n\n const rootRealPath = await fs.realpath(rootDir);\n const basePath = path.resolve(entryDir, importPath);\n\n const candidatePaths = path.extname(basePath)\n ? [basePath]\n : [\".tsx\", \".ts\", \".jsx\", \".js\"].map((ext) => basePath + ext);\n\n for (const candidatePath of candidatePaths) {\n try {\n await fs.access(candidatePath);\n } catch {\n continue;\n }\n\n // Resolve symlinks before boundary check.\n const candidateRealPath = await fs.realpath(candidatePath);\n if (!isPathWithinRoot(rootRealPath, candidateRealPath)) {\n logger.warn(\n `[mcp-react-ui] Refusing to build UI component outside project root. ` +\n `component=\"${componentName}\", import=\"${importPath}\", resolved=\"${candidateRealPath}\"`\n );\n return null;\n }\n\n return candidateRealPath;\n }\n\n logger.warn(\n `[mcp-react-ui] Could not resolve component file for \"${componentName}\" from import \"${importPath}\". ` +\n `Tried extensions: .tsx, .ts, .jsx, .js. Skipping this component.`\n );\n return null;\n}\n\n/**\n * Scan source file for defineReactUI calls and extract component information.\n * Uses AST parsing for reliable detection of imports and defineReactUI calls.\n */\nasync function discoverReactUIs(\n serverEntry: string,\n root: string,\n logger: PluginLogger\n): Promise<DiscoveredUI[]> {\n const entryPath = path.resolve(root, serverEntry);\n const content = await fs.readFile(entryPath, \"utf-8\");\n const entryDir = path.dirname(entryPath);\n\n const parsed = await parseReactUIDefinitions(content);\n const discovered: DiscoveredUI[] = [];\n\n for (const ui of parsed) {\n const componentPath = await resolveComponentPath(\n ui.importPath,\n entryDir,\n root,\n ui.componentName,\n logger\n );\n if (!componentPath) continue;\n\n const key = ui.componentName.replace(/([a-z])([A-Z])/g, \"$1-$2\").toLowerCase();\n discovered.push({\n componentName: ui.componentName,\n componentPath,\n name: ui.name,\n key,\n autoResize: ui.autoResize,\n });\n }\n\n return discovered;\n}\n\n/**\n * Build discovered React UI components.\n */\nasync function buildDiscoveredUIs(\n discovered: DiscoveredUI[],\n options: McpReactUIOptions,\n root: string,\n isProduction: boolean,\n logger: PluginLogger\n): Promise<void> {\n const minify = options.minify ?? isProduction;\n const outDir = options.outDir ?? \"./dist/ui\";\n const serverConfig = options.serverConfig ?? {};\n\n // Load global CSS if specified\n let globalCss: string | undefined;\n if (options.globalCss) {\n const globalCssPath = path.resolve(root, options.globalCss);\n try {\n globalCss = await fs.readFile(globalCssPath, \"utf-8\");\n } catch (error) {\n logger.warn(\n `[mcp-react-ui] globalCss file not found or unreadable: ${globalCssPath} - ${\n error instanceof Error ? error.message : error\n }`\n );\n }\n }\n\n // Build each discovered UI\n for (const ui of discovered) {\n const importPath = toEsbuildImportSpecifier(ui.componentPath);\n\n // Generate AppsProvider props based on autoResize setting\n const providerProps = ui.autoResize === undefined ? \"\" : ` autoResize={${ui.autoResize}}`;\n\n // Generate entry point code\n const entryCode = `\nimport React from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { AppsProvider } from \"@mcp-apps-kit/ui-react\";\nimport Component from \"${importPath}\";\n\nconst rootElement = document.getElementById(\"root\");\nif (rootElement) {\n const root = createRoot(rootElement);\n root.render(\n <React.StrictMode>\n <AppsProvider${providerProps}>\n <Component />\n </AppsProvider>\n </React.StrictMode>\n );\n}\n`;\n\n // Bundle with esbuild\n const result = await esbuild.build({\n stdin: {\n contents: entryCode,\n loader: \"tsx\",\n resolveDir: root,\n sourcefile: `${ui.key}-entry.tsx`,\n },\n bundle: true,\n write: false,\n format: \"esm\",\n platform: \"browser\",\n target: [\"es2020\", \"chrome90\", \"firefox90\", \"safari14\"],\n minify,\n jsx: \"automatic\",\n jsxImportSource: \"react\",\n define: {\n \"process.env.NODE_ENV\": minify ? '\"production\"' : '\"development\"',\n __MCP_SERVER_CONFIG__: JSON.stringify(serverConfig),\n },\n });\n\n const script = result.outputFiles?.[0]?.text;\n if (!script) {\n throw new Error(`Failed to build UI: ${ui.key}`);\n }\n\n // Generate HTML\n const html = generateHTML({\n key: ui.key,\n name: ui.name,\n script,\n css: globalCss,\n });\n\n // Write output file\n const outputPath = path.resolve(root, outDir, `${ui.key}.html`);\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.writeFile(outputPath, html, \"utf-8\");\n\n logger.info(`[mcp-react-ui] Built: ${ui.key}.html`);\n }\n}\n\n/**\n * Vite plugin that automatically discovers and builds React UI components.\n *\n * This plugin scans your server entry point for `defineReactUI` calls,\n * resolves the component imports, and builds them into self-contained HTML.\n *\n * @param options - Plugin configuration\n * @returns Vite plugin\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { mcpReactUI } from \"@mcp-apps-kit/ui-react-builder/vite\";\n *\n * export default defineConfig({\n * plugins: [\n * mcpReactUI({\n * serverEntry: \"./src/index.ts\",\n * outDir: \"./src/ui/dist\",\n * }),\n * ],\n * });\n * ```\n */\nexport function mcpReactUI(options: McpReactUIOptions): Plugin {\n let config: ResolvedConfig;\n\n const standalone = options.standalone ?? false;\n\n // Resolve logger: false = silent, undefined = default, custom = use provided\n const logger: PluginLogger =\n options.logger === false ? silentLogger : (options.logger ?? defaultLogger);\n\n return {\n name: \"mcp-react-ui\",\n\n configResolved(resolvedConfig) {\n config = resolvedConfig;\n },\n\n // Run build at the start of the build process\n async buildStart() {\n const root = config.root;\n const isProduction = config.mode === \"production\";\n\n // Discover React UIs from server entry\n const discovered = await discoverReactUIs(options.serverEntry, root, logger);\n\n if (discovered.length === 0) {\n logger.info(\"[mcp-react-ui] No defineReactUI calls found\");\n return;\n }\n\n logger.info(\n `[mcp-react-ui] Found ${discovered.length} React UI(s): ${discovered.map((d) => d.componentName).join(\", \")}`\n );\n\n // Build discovered UIs\n await buildDiscoveredUIs(discovered, options, root, isProduction, logger);\n },\n\n // Provide a virtual empty module so Vite doesn't complain about missing entry\n resolveId(id) {\n if (standalone && id === \"virtual:mcp-react-ui-entry\") {\n return id;\n }\n return null;\n },\n\n load(id) {\n if (standalone && id === \"virtual:mcp-react-ui-entry\") {\n return \"export default {}\";\n }\n return null;\n },\n\n // Override the config to use our virtual entry\n config() {\n if (!standalone) {\n return undefined;\n }\n\n return {\n build: {\n rollupOptions: {\n input: \"virtual:mcp-react-ui-entry\",\n },\n },\n };\n },\n\n // Standalone mode: prevent Vite from generating output files (we already wrote our HTML)\n generateBundle(_, bundle) {\n if (!standalone) {\n return;\n }\n\n // Remove all generated chunks since we don't need them\n const keys = Object.keys(bundle);\n for (const key of keys) {\n // eslint-disable-next-line @typescript-eslint/no-dynamic-delete\n delete bundle[key];\n }\n },\n };\n}\n\nexport default mcpReactUI;\n"]} |
+3
-3
| { | ||
| "name": "@mcp-apps-kit/ui-react-builder", | ||
| "version": "0.3.0", | ||
| "version": "0.4.0", | ||
| "description": "Build tool for React-based MCP application UIs", | ||
@@ -62,4 +62,4 @@ "type": "module", | ||
| "peerDependencies": { | ||
| "@mcp-apps-kit/core": "^0.3.0", | ||
| "@mcp-apps-kit/ui-react": "^0.3.0", | ||
| "@mcp-apps-kit/core": "^0.4.0", | ||
| "@mcp-apps-kit/ui-react": "^0.4.0", | ||
| "react": "^18.0.0 || ^19.0.0", | ||
@@ -66,0 +66,0 @@ "react-dom": "^18.0.0 || ^19.0.0", |
+14
-1
@@ -7,3 +7,3 @@ # @mcp-apps-kit/ui-react-builder | ||
| `@mcp-apps-kit/ui-react-builder` allows you to define UI resources using React components instead of pre-built HTML files. The framework handles bundling React, ReactDOM, and `@mcp-apps-kit/ui-react` into self-contained HTML that works with both MCP Apps (Claude Desktop) and ChatGPT. | ||
| `@mcp-apps-kit/ui-react-builder` allows you to define UI resources using React components instead of pre-built HTML files. The framework handles bundling React, ReactDOM, and `@mcp-apps-kit/ui-react` into self-contained HTML that works with both MCP Apps and ChatGPT. | ||
@@ -96,2 +96,4 @@ ## Table of Contents | ||
| prefersBorder: true, | ||
| // Optional: Disable automatic size notifications (default: true) | ||
| // autoResize: false, | ||
| }), | ||
@@ -194,2 +196,13 @@ handler: async ({ name }) => ({ | ||
| #### `defineReactUI` Options | ||
| | Option | Type | Default | Description | | ||
| | --------------- | --------------- | ---------- | ------------------------------------------------------------------------------------------- | | ||
| | `component` | `ComponentType` | (required) | React component to render | | ||
| | `name` | `string` | (required) | Display name for the UI | | ||
| | `description` | `string` | - | Description of the UI widget | | ||
| | `prefersBorder` | `boolean` | - | Hint to the host whether a border should be drawn | | ||
| | `autoResize` | `boolean` | `true` | Enable automatic size change notifications. Only supported in MCP Apps; ignored in ChatGPT. | | ||
| | `csp` | `CSPConfig` | - | Content Security Policy configuration (ChatGPT only) | | ||
| ### Types | ||
@@ -196,0 +209,0 @@ |
| export{b as buildReactUI,a as buildReactUIs}from'./chunk-5WFQIJJW.js';import'./chunk-6JS3KVIH.js';//# sourceMappingURL=build-ZF3MI5NJ.js.map | ||
| //# sourceMappingURL=build-ZF3MI5NJ.js.map |
| {"version":3,"sources":[],"names":[],"mappings":"","file":"build-ZF3MI5NJ.js"} |
| import {b as b$1,a}from'./chunk-6JS3KVIH.js';import*as w from'esbuild';import*as u from'fs/promises';import*as d from'path';async function C(s,t={}){let e=Date.now(),n=new Map,o=new Map,i=[],c=[],r=t.cwd??process.cwd();t.outDir&&await u.mkdir(d.resolve(r,t.outDir),{recursive:true});let f;if(t.globalCss)try{f=await u.readFile(d.resolve(r,t.globalCss),"utf-8");}catch(a){i.push(`Could not load global CSS from ${t.globalCss}: ${a instanceof Error?a.message:String(a)}`);}for(let[a,p]of Object.entries(s))try{let l=await b(a,p,{...t,cwd:r,globalCss:f});if(n.set(a,l),t.outDir){let m=d.resolve(r,t.outDir,`${a}.html`);await u.writeFile(m,l,"utf-8"),o.set(a,m);}}catch(l){let m={key:a,message:l instanceof Error?l.message:String(l),stack:l instanceof Error?l.stack:void 0};c.push(m);}return {outputs:n,files:o,duration:Date.now()-e,warnings:i,errors:c}}async function b(s,t,e){let n=t.__component,o=n.name||"Component",i=t.__defaultProps,c=b$1("__COMPONENT_PLACEHOLDER__",i),r=[y(n,o),E()],f=await w.build({stdin:{contents:c,loader:"tsx",resolveDir:e.cwd,sourcefile:`${s}-entry.tsx`},bundle:true,write:false,format:"esm",platform:"browser",target:["es2020","chrome90","firefox90","safari14"],minify:e.minify??true,sourcemap:e.sourcemap?"inline":false,jsx:"automatic",jsxImportSource:"react",logLevel:"warning",external:e.external,plugins:r,define:{"process.env.NODE_ENV":e.minify?'"production"':'"development"'}}),a$1=f.outputFiles?.[0];if(!a$1)throw new Error(`No output generated for UI: ${s}`);let p=a$1.text;return f.warnings,a({key:s,name:t.name??s,script:p,css:e.globalCss})}function y(s,t){return {name:"mcp-component-resolver",setup(e){e.onResolve({filter:/__COMPONENT_PLACEHOLDER__/},()=>({path:"__COMPONENT__",namespace:"mcp-component"})),e.onLoad({filter:/.*/,namespace:"mcp-component"},()=>{let n=s.toString(),o=/^\s*class\b/.test(n),i=/^\s*function\b/.test(n),c=!o&&!i,r;return c?r=` | ||
| import React from "react"; | ||
| const ${t} = ${n}; | ||
| export default ${t}; | ||
| `:r=` | ||
| import React from "react"; | ||
| ${n} | ||
| export default ${t}; | ||
| `,{contents:r,loader:"tsx"}});}}}function E(){return {name:"mcp-css-handler",setup(s){s.onLoad({filter:/\.css$/},async t=>{if(t.path.endsWith(".module.css"))return null;let e=await u.readFile(t.path,"utf-8");return {contents:` | ||
| const style = document.createElement("style"); | ||
| style.textContent = ${JSON.stringify(e)}; | ||
| document.head.appendChild(style); | ||
| `,loader:"js"}}),s.onLoad({filter:/\.module\.css$/},async t=>{let e=await u.readFile(t.path,"utf-8"),n=_(e),o=Object.fromEntries(n.map(r=>[r,`${r}_${x(t.path)}`])),i=e;for(let[r,f]of Object.entries(o))i=i.replace(new RegExp(`\\.${r}\\b`,"g"),`.${f}`);return {contents:` | ||
| const style = document.createElement("style"); | ||
| style.textContent = ${JSON.stringify(i)}; | ||
| document.head.appendChild(style); | ||
| export default ${JSON.stringify(o)}; | ||
| `,loader:"js"}});}}}function _(s){let t=/\.([a-zA-Z_][a-zA-Z0-9_-]*)/g,e=new Set,n;for(;(n=t.exec(s))!==null;)n[1]&&e.add(n[1]);return Array.from(e)}function x(s){let t=0;for(let e=0;e<s.length;e++){let n=s.charCodeAt(e);t=(t<<5)-t+n,t=t&t;}return Math.abs(t).toString(36).substring(0,5)}async function O(s,t,e={}){let n=await C({[s]:t},e),o=n.outputs.get(s);if(!o){let i=n.errors.find(c=>c.key===s);throw new Error(i?.message??`Failed to build UI: ${s}`)}return o}export{C as a,O as b};//# sourceMappingURL=chunk-5WFQIJJW.js.map | ||
| //# sourceMappingURL=chunk-5WFQIJJW.js.map |
| {"version":3,"sources":["../src/build.ts"],"names":["buildReactUIs","uis","options","startTime","outputs","files","warnings","errors","cwd","globalCss","error","key","def","html","compileComponent","outPath","buildError","component","componentName","defaultProps","entryPoint","generateEntryPoint","plugins","createComponentPlugin","createCSSPlugin","result","outputFile","script","generateHTML","build","componentSource","isClassComponent","isFunctionComponent","isArrowFunction","contents","args","css","classNames","extractClassNames","mapping","name","hashString","transformedCss","original","hashed","classRegex","classes","match","str","hash","i","char","buildReactUI","e"],"mappings":"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","file":"chunk-5WFQIJJW.js","sourcesContent":["/**\n * React UI build system using esbuild\n *\n * Compiles React components into self-contained HTML files that can be\n * served as MCP UI resources.\n */\n\nimport * as esbuild from \"esbuild\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport type { ReactUIDef, BuildOptions, BuildResult, BuildError } from \"./types\";\nimport { generateHTML, generateEntryPoint } from \"./html\";\n\n/**\n * Build multiple React UIs into self-contained HTML files.\n *\n * This function takes a record of React UI definitions and compiles each one\n * into a complete HTML file that includes React, ReactDOM, @mcp-apps-kit/ui-react,\n * and the user's component code.\n *\n * **IMPORTANT: Limitations**\n *\n * This programmatic build function serializes components using `.toString()`, which has\n * significant limitations:\n *\n * - **No external imports**: Components cannot import other modules, hooks, or utilities\n * - **No closures**: Components that capture external variables will not work\n * - **Simple components only**: Best for self-contained components without dependencies\n *\n * For production use, prefer the **Vite plugin** (`mcpReactUI`) which uses file paths\n * for proper import resolution and supports:\n * - Full import/export resolution\n * - CSS imports and CSS modules\n * - All React hooks and utilities\n * - External component dependencies\n *\n * This function is primarily intended for:\n * - **Testing**: Unit and integration tests for the build pipeline\n * - **Simple widgets**: Self-contained components with no external imports\n * - **Prototyping**: Quick experimentation before setting up Vite\n *\n * @param uis - Record of UI keys to React UI definitions\n * @param options - Build configuration options\n * @returns Build result with compiled HTML and metadata\n *\n * @example\n * ```typescript\n * // For production, use the Vite plugin instead:\n * // vite.config.ts\n * import { mcpReactUI } from \"@mcp-apps-kit/ui-react-builder/vite\";\n *\n * export default defineConfig({\n * plugins: [mcpReactUI({ serverEntry: \"./src/index.ts\" })],\n * });\n *\n * // This programmatic API is for simple/test cases:\n * import { buildReactUIs, defineReactUI } from \"@mcp-apps-kit/ui-react-builder\";\n *\n * // Only works with simple, self-contained components\n * const SimpleWidget = () => <div>Hello World</div>;\n *\n * const result = await buildReactUIs({\n * \"simple\": defineReactUI({\n * component: SimpleWidget,\n * name: \"Simple Widget\",\n * }),\n * });\n * ```\n */\nexport async function buildReactUIs(\n uis: Record<string, ReactUIDef>,\n options: BuildOptions = {}\n): Promise<BuildResult> {\n const startTime = Date.now();\n const outputs = new Map<string, string>();\n const files = new Map<string, string>();\n const warnings: string[] = [];\n const errors: BuildError[] = [];\n\n const cwd = options.cwd ?? process.cwd();\n\n // Create output directory if specified\n if (options.outDir) {\n await fs.mkdir(path.resolve(cwd, options.outDir), { recursive: true });\n }\n\n // Load global CSS if specified\n let globalCss: string | undefined;\n if (options.globalCss) {\n try {\n globalCss = await fs.readFile(path.resolve(cwd, options.globalCss), \"utf-8\");\n } catch (error) {\n warnings.push(\n `Could not load global CSS from ${options.globalCss}: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n\n // Build each UI\n for (const [key, def] of Object.entries(uis)) {\n try {\n const html = await compileComponent(key, def, {\n ...options,\n cwd,\n globalCss,\n });\n\n outputs.set(key, html);\n\n // Write to file if outDir specified\n if (options.outDir) {\n const outPath = path.resolve(cwd, options.outDir, `${key}.html`);\n await fs.writeFile(outPath, html, \"utf-8\");\n files.set(key, outPath);\n }\n } catch (error) {\n const buildError: BuildError = {\n key,\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n errors.push(buildError);\n }\n }\n\n return {\n outputs,\n files,\n duration: Date.now() - startTime,\n warnings,\n errors,\n };\n}\n\n/**\n * Compile a single React component to self-contained HTML.\n *\n * @param key - Unique key for this UI\n * @param def - React UI definition\n * @param options - Build options\n * @returns Complete HTML document as a string\n */\nasync function compileComponent(\n key: string,\n def: ReactUIDef,\n options: BuildOptions & { cwd: string; globalCss?: string }\n): Promise<string> {\n // Get component from internal properties\n const component = def.__component;\n const componentName = component.name || \"Component\";\n const defaultProps = def.__defaultProps;\n\n // Create the entry point using function serialization\n // Note: The Vite plugin uses file paths for proper import resolution\n // This build function falls back to function serialization (limited - doesn't capture imports)\n const entryPoint = generateEntryPoint(`__COMPONENT_PLACEHOLDER__`, defaultProps);\n\n // Configure plugins\n const plugins: esbuild.Plugin[] = [\n createComponentPlugin(component, componentName),\n createCSSPlugin(),\n ];\n\n // Use esbuild to bundle everything\n const result = await esbuild.build({\n stdin: {\n contents: entryPoint,\n loader: \"tsx\",\n resolveDir: options.cwd,\n sourcefile: `${key}-entry.tsx`,\n },\n bundle: true,\n write: false,\n format: \"esm\",\n platform: \"browser\",\n target: [\"es2020\", \"chrome90\", \"firefox90\", \"safari14\"],\n minify: options.minify ?? true,\n sourcemap: options.sourcemap ? \"inline\" : false,\n jsx: \"automatic\",\n jsxImportSource: \"react\",\n logLevel: \"warning\",\n external: options.external,\n plugins,\n define: {\n \"process.env.NODE_ENV\": options.minify ? '\"production\"' : '\"development\"',\n },\n });\n\n // Get the bundled JavaScript\n const outputFile = result.outputFiles?.[0];\n if (!outputFile) {\n throw new Error(`No output generated for UI: ${key}`);\n }\n\n const script = outputFile.text;\n\n // Collect any warnings from esbuild (currently silent, could add a logger later)\n // Warnings are typically about missing exports, unused imports, etc.\n // For now we don't surface these to users\n void result.warnings;\n\n // Generate the final HTML\n const html = generateHTML({\n key,\n name: def.name ?? key,\n script,\n css: options.globalCss,\n });\n\n return html;\n}\n\n/**\n * Create an esbuild plugin that resolves the component placeholder.\n *\n * This plugin intercepts imports of the placeholder module and replaces\n * it with the actual component code. This is necessary because we receive\n * a function reference, not a file path.\n */\nfunction createComponentPlugin(\n component: { toString: () => string },\n componentName: string\n): esbuild.Plugin {\n return {\n name: \"mcp-component-resolver\",\n setup(build) {\n // Intercept the placeholder import\n build.onResolve({ filter: /__COMPONENT_PLACEHOLDER__/ }, () => {\n return {\n path: \"__COMPONENT__\",\n namespace: \"mcp-component\",\n };\n });\n\n // Provide the component code\n build.onLoad({ filter: /.*/, namespace: \"mcp-component\" }, () => {\n // Serialize the component to a module\n // For now, we export a placeholder that requires runtime injection\n // In a real implementation, we'd need the component's source path\n const componentSource = component.toString();\n\n // Detect component type: class, function, or arrow function\n const isClassComponent = /^\\s*class\\b/.test(componentSource);\n const isFunctionComponent = /^\\s*function\\b/.test(componentSource);\n const isArrowFunction = !isClassComponent && !isFunctionComponent;\n\n let contents: string;\n if (isArrowFunction) {\n contents = `\n import React from \"react\";\n const ${componentName} = ${componentSource};\n export default ${componentName};\n `;\n } else {\n contents = `\n import React from \"react\";\n ${componentSource}\n export default ${componentName};\n `;\n }\n\n return {\n contents,\n loader: \"tsx\",\n };\n });\n },\n };\n}\n\n/**\n * Create an esbuild plugin that handles CSS imports.\n *\n * This plugin transforms CSS imports into JavaScript that injects\n * the styles into the document head at runtime.\n */\nfunction createCSSPlugin(): esbuild.Plugin {\n return {\n name: \"mcp-css-handler\",\n setup(build) {\n // Handle .css imports (excluding .module.css which has its own handler)\n build.onLoad({ filter: /\\.css$/ }, async (args) => {\n // Skip .module.css files - they're handled by the CSS modules loader\n if (args.path.endsWith(\".module.css\")) {\n return null;\n }\n const css = await fs.readFile(args.path, \"utf-8\");\n\n // Create JavaScript that injects the CSS\n const contents = `\n const style = document.createElement(\"style\");\n style.textContent = ${JSON.stringify(css)};\n document.head.appendChild(style);\n `;\n\n return {\n contents,\n loader: \"js\",\n };\n });\n\n // Handle CSS modules (.module.css)\n build.onLoad({ filter: /\\.module\\.css$/ }, async (args) => {\n const css = await fs.readFile(args.path, \"utf-8\");\n\n // Generate a simple class name mapping\n // In a real implementation, we'd use a proper CSS modules processor\n const classNames = extractClassNames(css);\n const mapping = Object.fromEntries(\n classNames.map((name) => [name, `${name}_${hashString(args.path)}`])\n );\n\n // Transform the CSS with new class names\n let transformedCss = css;\n for (const [original, hashed] of Object.entries(mapping)) {\n transformedCss = transformedCss.replace(\n new RegExp(`\\\\.${original}\\\\b`, \"g\"),\n `.${hashed}`\n );\n }\n\n const contents = `\n const style = document.createElement(\"style\");\n style.textContent = ${JSON.stringify(transformedCss)};\n document.head.appendChild(style);\n export default ${JSON.stringify(mapping)};\n `;\n\n return {\n contents,\n loader: \"js\",\n };\n });\n },\n };\n}\n\n/**\n * Extract class names from CSS content.\n */\nfunction extractClassNames(css: string): string[] {\n const classRegex = /\\.([a-zA-Z_][a-zA-Z0-9_-]*)/g;\n const classes = new Set<string>();\n let match;\n while ((match = classRegex.exec(css)) !== null) {\n if (match[1]) {\n classes.add(match[1]);\n }\n }\n return Array.from(classes);\n}\n\n/**\n * Simple string hash for generating unique class names.\n */\nfunction hashString(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32bit integer\n }\n return Math.abs(hash).toString(36).substring(0, 5);\n}\n\n/**\n * Build a single React UI.\n *\n * Convenience function for building just one UI.\n *\n * @param key - Unique key for this UI\n * @param def - React UI definition\n * @param options - Build options\n * @returns The compiled HTML string\n *\n * @example\n * ```typescript\n * const html = await buildReactUI(\"my-widget\", defineReactUI({\n * component: MyWidget,\n * name: \"My Widget\",\n * }));\n * ```\n */\nexport async function buildReactUI(\n key: string,\n def: ReactUIDef,\n options: BuildOptions = {}\n): Promise<string> {\n const result = await buildReactUIs({ [key]: def }, options);\n\n const html = result.outputs.get(key);\n if (!html) {\n const error = result.errors.find((e) => e.key === key);\n throw new Error(error?.message ?? `Failed to build UI: ${key}`);\n }\n\n return html;\n}\n"]} |
| var s=` | ||
| *, | ||
| *::before, | ||
| *::after { | ||
| box-sizing: border-box; | ||
| margin: 0; | ||
| padding: 0; | ||
| } | ||
| html { | ||
| font-size: 16px; | ||
| -webkit-font-smoothing: antialiased; | ||
| -moz-osx-font-smoothing: grayscale; | ||
| } | ||
| body { | ||
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, | ||
| Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; | ||
| line-height: 1.5; | ||
| color: #1a1a1a; | ||
| background-color: transparent; | ||
| } | ||
| @media (prefers-color-scheme: dark) { | ||
| body { | ||
| color: #f5f5f5; | ||
| } | ||
| } | ||
| #root { | ||
| min-height: 100%; | ||
| } | ||
| `;function m(t){let{key:o,name:e,script:r,css:n}=t,i=n?`${s} | ||
| ${n}`:s;return `<!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <meta name="mcp-ui-key" content="${a(o)}"> | ||
| <title>${a(e)}</title> | ||
| <style>${i}</style> | ||
| </head> | ||
| <body> | ||
| <div id="root"></div> | ||
| <script type="module">${r.replace(/<\/script>/gi,"</script>")}</script> | ||
| </body> | ||
| </html>`}function l(t,o){let e=typeof t=="string"?{componentPath:t,defaultProps:o}:t,{componentPath:r,componentExport:n="default",defaultProps:i}=e,c=i?JSON.stringify(i):"{}";return ` | ||
| import React from "react"; | ||
| import { createRoot } from "react-dom/client"; | ||
| import { AppsProvider } from "@mcp-apps-kit/ui-react"; | ||
| ${n==="default"?`import Component from "${r}";`:`import { ${n} as Component } from "${r}";`} | ||
| const rootElement = document.getElementById("root"); | ||
| if (rootElement) { | ||
| const root = createRoot(rootElement); | ||
| root.render( | ||
| <React.StrictMode> | ||
| <AppsProvider> | ||
| <Component {...${c}} /> | ||
| </AppsProvider> | ||
| </React.StrictMode> | ||
| ); | ||
| } | ||
| `}function a(t){let o={"&":"&","<":"<",">":">",'"':""","'":"'"};return t.replace(/[&<>"']/g,e=>o[e]??e)}export{m as a,l as b};//# sourceMappingURL=chunk-6JS3KVIH.js.map | ||
| //# sourceMappingURL=chunk-6JS3KVIH.js.map |
| {"version":3,"sources":["../src/html.ts"],"names":["DEFAULT_BASE_CSS","generateHTML","options","key","name","script","css","combinedCss","escapeHtml","generateEntryPoint","componentPathOrOptions","defaultProps","componentPath","componentExport","props","propsJson","text","htmlEntities","char"],"mappings":"AAUA,IAAMA,CAAAA,CAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA,CAAA,CA0DlB,SAASC,CAAAA,CAAaC,CAAAA,CAAkC,CAC7D,GAAM,CAAE,IAAAC,CAAAA,CAAK,IAAA,CAAAC,EAAM,MAAA,CAAAC,CAAAA,CAAQ,IAAAC,CAAI,CAAA,CAAIJ,EAG7BK,CAAAA,CAAcD,CAAAA,CAAM,GAAGN,CAAgB;AAAA,EAAKM,CAAG,CAAA,CAAA,CAAKN,CAAAA,CAE1D,OAAO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mCAAA,EAK4BQ,CAAAA,CAAWL,CAAG,CAAC,CAAA;AAAA,SAAA,EACzCK,CAAAA,CAAWJ,CAAI,CAAC,CAAA;AAAA,SAAA,EAChBG,CAAW,CAAA;AAAA;AAAA;AAAA;AAAA,wBAAA,EAIIF,CAAAA,CAAO,OAAA,CAAQ,cAAA,CAAgB,WAAgB,CAAC,CAAA;AAAA;AAAA,OAAA,CAG1E,CAwDO,SAASI,CAAAA,CACdC,CAAAA,CACAC,EACQ,CAER,IAAMT,CAAAA,CACJ,OAAOQ,GAA2B,QAAA,CAC9B,CAAE,aAAA,CAAeA,CAAAA,CAAwB,aAAAC,CAAa,CAAA,CACtDD,CAAAA,CAEA,CAAE,cAAAE,CAAAA,CAAe,eAAA,CAAAC,CAAAA,CAAkB,SAAA,CAAW,aAAcC,CAAM,CAAA,CAAIZ,CAAAA,CACtEa,CAAAA,CAAYD,EAAQ,IAAA,CAAK,SAAA,CAAUA,CAAK,CAAA,CAAI,KAQlD,OAAO;AAAA;AAAA;AAAA;AAAA,EAJLD,CAAAA,GAAoB,UAChB,CAAA,uBAAA,EAA0BD,CAAa,KACvC,CAAA,SAAA,EAAYC,CAAe,CAAA,sBAAA,EAAyBD,CAAa,CAAA,EAAA,CAMxD;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAA,EAQQG,CAAS,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAMlC,CAQA,SAASP,CAAAA,CAAWQ,CAAAA,CAAsB,CACxC,IAAMC,CAAAA,CAAuC,CAC3C,GAAA,CAAK,OAAA,CACL,GAAA,CAAK,MAAA,CACL,GAAA,CAAK,MAAA,CACL,GAAA,CAAK,QAAA,CACL,GAAA,CAAK,OACP,CAAA,CAEA,OAAOD,CAAAA,CAAK,OAAA,CAAQ,UAAA,CAAaE,CAAAA,EAASD,CAAAA,CAAaC,CAAI,CAAA,EAAKA,CAAI,CACtE","file":"chunk-6JS3KVIH.js","sourcesContent":["/**\n * HTML template generation for React UIs\n */\n\nimport type { TemplateOptions } from \"./types\";\n\n/**\n * Default base CSS reset for all UIs.\n * Provides a consistent starting point across platforms.\n */\nconst DEFAULT_BASE_CSS = `\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n html {\n font-size: 16px;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen,\n Ubuntu, Cantarell, \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\", sans-serif;\n line-height: 1.5;\n color: #1a1a1a;\n background-color: transparent;\n }\n\n @media (prefers-color-scheme: dark) {\n body {\n color: #f5f5f5;\n }\n }\n\n #root {\n min-height: 100%;\n }\n`;\n\n/**\n * Generate a self-contained HTML document for a React UI.\n *\n * The generated HTML includes:\n * - DOCTYPE and valid HTML5 structure\n * - Meta tags for responsive design\n * - Inlined CSS (base reset + optional custom CSS)\n * - Inlined JavaScript bundle with React app\n *\n * @param options - Template options with key, name, script, and optional CSS\n * @returns Complete HTML document as a string\n *\n * @example\n * ```typescript\n * const html = generateHTML({\n * key: \"restaurant-list\",\n * name: \"Restaurant List Widget\",\n * script: bundledJavaScript,\n * css: customStyles,\n * });\n * ```\n *\n * @internal\n */\nexport function generateHTML(options: TemplateOptions): string {\n const { key, name, script, css } = options;\n\n // Combine base CSS with any custom CSS\n const combinedCss = css ? `${DEFAULT_BASE_CSS}\\n${css}` : DEFAULT_BASE_CSS;\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"mcp-ui-key\" content=\"${escapeHtml(key)}\">\n <title>${escapeHtml(name)}</title>\n <style>${combinedCss}</style>\n</head>\n<body>\n <div id=\"root\"></div>\n <script type=\"module\">${script.replace(/<\\/script>/gi, \"</scr\" + \"ipt>\")}</script>\n</body>\n</html>`;\n}\n\n/**\n * Options for generating the React entry point.\n *\n * @internal\n */\nexport interface EntryPointOptions {\n /**\n * Path to the component file.\n */\n componentPath: string;\n\n /**\n * Name of the export to use.\n * Use \"default\" for default exports, or the actual name for named exports.\n * @default \"default\"\n */\n componentExport?: string;\n\n /**\n * Default props to pass to the component.\n */\n defaultProps?: Record<string, unknown>;\n}\n\n/**\n * Generate the React entry point code that will be bundled.\n *\n * This creates a small JavaScript module that:\n * 1. Imports React and ReactDOM\n * 2. Imports the user's component\n * 3. Imports AppsProvider from @mcp-apps-kit/ui-react\n * 4. Renders the component wrapped in providers\n *\n * @param componentPath - Path to the React component file (for backward compatibility)\n * @param defaultProps - Optional default props to pass to the component\n * @returns JavaScript/TypeScript source code for the entry point\n *\n * @example\n * ```typescript\n * // Default export\n * const entryCode = generateEntryPoint(\n * \"./src/widgets/MyWidget.tsx\",\n * { theme: \"dark\" }\n * );\n *\n * // Named export\n * const entryCode = generateEntryPoint({\n * componentPath: \"./src/widgets/MyWidget.tsx\",\n * componentExport: \"MyWidget\",\n * });\n * ```\n *\n * @internal\n */\nexport function generateEntryPoint(\n componentPathOrOptions: string | EntryPointOptions,\n defaultProps?: Record<string, unknown>\n): string {\n // Handle backward compatibility with string-only signature\n const options: EntryPointOptions =\n typeof componentPathOrOptions === \"string\"\n ? { componentPath: componentPathOrOptions, defaultProps }\n : componentPathOrOptions;\n\n const { componentPath, componentExport = \"default\", defaultProps: props } = options;\n const propsJson = props ? JSON.stringify(props) : \"{}\";\n\n // Generate appropriate import statement based on export type\n const importStatement =\n componentExport === \"default\"\n ? `import Component from \"${componentPath}\";`\n : `import { ${componentExport} as Component } from \"${componentPath}\";`;\n\n return `\nimport React from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { AppsProvider } from \"@mcp-apps-kit/ui-react\";\n${importStatement}\n\nconst rootElement = document.getElementById(\"root\");\nif (rootElement) {\n const root = createRoot(rootElement);\n root.render(\n <React.StrictMode>\n <AppsProvider>\n <Component {...${propsJson}} />\n </AppsProvider>\n </React.StrictMode>\n );\n}\n`;\n}\n\n/**\n * Escape HTML special characters to prevent XSS.\n *\n * @param text - Text to escape\n * @returns Escaped text safe for HTML insertion\n */\nfunction escapeHtml(text: string): string {\n const htmlEntities: Record<string, string> = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n '\"': \""\",\n \"'\": \"'\",\n };\n\n return text.replace(/[&<>\"']/g, (char) => htmlEntities[char] ?? char);\n}\n\n/**\n * Extract CSS from a string that may contain both JS and CSS.\n * Used when esbuild bundles CSS as JavaScript that injects styles.\n *\n * @param code - Bundled code that may contain CSS injection\n * @returns Extracted CSS string or undefined\n */\nexport function extractInlineCSS(code: string): string | undefined {\n // esbuild injects CSS as: document.head.appendChild(...).textContent = \"css content\"\n const cssMatch = code.match(/\\.textContent\\s*=\\s*[\"'`]([\\s\\S]*?)[\"'`]\\s*[;,)]/);\n if (cssMatch?.[1]) {\n // Unescape the CSS string\n return cssMatch[1].replace(/\\\\n/g, \"\\n\").replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\");\n }\n return undefined;\n}\n"]} |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 2 instances in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 3 instances in 1 package
240379
3.1%1097
2.33%6
-14.29%292
4.66%11
22.22%