@fragments-sdk/cli 0.15.0 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ai-client-I6MDWNYA.js → ai-client-LSLQGOMM.js} +1 -2
- package/dist/bin.js +463 -71
- package/dist/bin.js.map +1 -1
- package/dist/chunk-5JF26E55.js +1255 -0
- package/dist/chunk-5JF26E55.js.map +1 -0
- package/dist/{chunk-XJQ5BIWI.js → chunk-6SQPP47U.js} +30 -314
- package/dist/chunk-6SQPP47U.js.map +1 -0
- package/dist/{chunk-65WSVDV5.js → chunk-HQ6A6DTV.js} +1386 -1097
- package/dist/chunk-HQ6A6DTV.js.map +1 -0
- package/dist/chunk-MHIBEEW4.js +511 -0
- package/dist/chunk-MHIBEEW4.js.map +1 -0
- package/dist/{chunk-CZD3AD4Q.js → chunk-ONUP6Z4W.js} +17 -6
- package/dist/chunk-ONUP6Z4W.js.map +1 -0
- package/dist/{codebase-scanner-VOTPXRYW.js → codebase-scanner-MQHUZC2G.js} +1 -2
- package/dist/{converter-JLINP7CJ.js → converter-7XM3Y6NJ.js} +1 -2
- package/dist/{converter-JLINP7CJ.js.map → converter-7XM3Y6NJ.js.map} +1 -1
- package/dist/core/index.js +0 -1
- package/dist/create-IH4R45GE.js +806 -0
- package/dist/create-IH4R45GE.js.map +1 -0
- package/dist/{generate-A4FP5426.js → generate-PVOLUAAC.js} +3 -4
- package/dist/{generate-A4FP5426.js.map → generate-PVOLUAAC.js.map} +1 -1
- package/dist/{govern-scan-UCBZR6D6.js → govern-scan-OYFZYOQW.js} +142 -9
- package/dist/govern-scan-OYFZYOQW.js.map +1 -0
- package/dist/index.d.ts +2 -22
- package/dist/index.js +8 -7
- package/dist/index.js.map +1 -1
- package/dist/{init-HGSM35XA.js → init-SSGUSP7Z.js} +3 -4
- package/dist/{init-HGSM35XA.js.map → init-SSGUSP7Z.js.map} +1 -1
- package/dist/{init-cloud-MQ6GRJAZ.js → init-cloud-3DNKPWFB.js} +29 -4
- package/dist/{init-cloud-MQ6GRJAZ.js.map → init-cloud-3DNKPWFB.js.map} +1 -1
- package/dist/mcp-bin.js +1 -2
- package/dist/mcp-bin.js.map +1 -1
- package/dist/node-37AUE74M.js +65 -0
- package/dist/push-contracts-WY32TFP6.js +84 -0
- package/dist/push-contracts-WY32TFP6.js.map +1 -0
- package/dist/{scan-VNNKACG2.js → scan-PKSYSTRR.js} +5 -5
- package/dist/{scan-generate-TWRHNU5M.js → scan-generate-VY27PIOX.js} +8 -9
- package/dist/scan-generate-VY27PIOX.js.map +1 -0
- package/dist/{scanner-7LAZYPWZ.js → scanner-4KZNOXAK.js} +1 -2
- package/dist/{service-FHQU7YS7.js → service-QJGWUIVL.js} +16 -9
- package/dist/{snapshot-KQEQ6XHL.js → snapshot-WIJMEIFT.js} +1 -2
- package/dist/{snapshot-KQEQ6XHL.js.map → snapshot-WIJMEIFT.js.map} +1 -1
- package/dist/{static-viewer-63PG6FWY.js → static-viewer-7QIBQZRC.js} +1 -2
- package/dist/{test-UQYUCZIS.js → test-64Z5BKBA.js} +2 -3
- package/dist/{test-UQYUCZIS.js.map → test-64Z5BKBA.js.map} +1 -1
- package/dist/token-normalizer-TEPOVBPV.js +312 -0
- package/dist/token-normalizer-TEPOVBPV.js.map +1 -0
- package/dist/token-parser-32KOIOFN.js +22 -0
- package/dist/token-parser-32KOIOFN.js.map +1 -0
- package/dist/{tokens-6GYKDV6U.js → tokens-NZWFQIAB.js} +7 -7
- package/dist/{tokens-generate-VTZV5EEW.js → tokens-generate-5JQSJ27E.js} +1 -2
- package/dist/{tokens-generate-VTZV5EEW.js.map → tokens-generate-5JQSJ27E.js.map} +1 -1
- package/dist/tokens-push-HY3KO36V.js +148 -0
- package/dist/tokens-push-HY3KO36V.js.map +1 -0
- package/package.json +5 -3
- package/src/bin.ts +90 -0
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.contract.json +1 -1
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.contract.json +1 -1
- package/src/commands/__tests__/build-freshness.test.ts +231 -0
- package/src/commands/__tests__/create.test.ts +71 -0
- package/src/commands/__tests__/drift-sync.test.ts +1 -1
- package/src/commands/__tests__/govern.test.ts +258 -0
- package/src/commands/__tests__/init.test.ts +1 -1
- package/src/commands/__tests__/scan-generate.test.ts +1 -1
- package/src/commands/build.ts +54 -1
- package/src/commands/context.ts +1 -1
- package/src/commands/create.ts +536 -0
- package/src/commands/doctor.ts +3 -2
- package/src/commands/govern-scan.ts +187 -8
- package/src/commands/govern.ts +65 -2
- package/src/commands/init-cloud.ts +32 -4
- package/src/commands/push-contracts.ts +112 -0
- package/src/commands/scan-generate.ts +1 -1
- package/src/commands/scan.ts +13 -0
- package/src/commands/sync.ts +2 -2
- package/src/commands/tokens-push.ts +199 -0
- package/src/core/__tests__/token-resolver.test.ts +1 -1
- package/src/core/component-extractor.test.ts +1 -1
- package/src/core/drift-verifier.ts +1 -1
- package/src/core/extractor-adapter.ts +1 -1
- package/src/index.ts +3 -3
- package/src/migrate/fragment-to-contract.ts +2 -2
- package/src/service/index.ts +8 -0
- package/src/service/tailwind-v4-parser.ts +314 -0
- package/src/service/token-parser.ts +56 -0
- package/src/setup.ts +10 -39
- package/src/theme/__tests__/component-contrast.test.ts +2 -2
- package/src/theme/__tests__/serializer.test.ts +1 -1
- package/src/theme/generator.ts +16 -1
- package/src/theme/schema.ts +8 -0
- package/src/theme/serializer.ts +13 -9
- package/src/theme/types.ts +8 -0
- package/src/validators.ts +1 -2
- package/dist/chunk-65WSVDV5.js.map +0 -1
- package/dist/chunk-7WHVW72L.js +0 -2664
- package/dist/chunk-7WHVW72L.js.map +0 -1
- package/dist/chunk-CZD3AD4Q.js.map +0 -1
- package/dist/chunk-MN3TJ3D5.js +0 -695
- package/dist/chunk-MN3TJ3D5.js.map +0 -1
- package/dist/chunk-XJQ5BIWI.js.map +0 -1
- package/dist/chunk-Z7EY4VHE.js +0 -50
- package/dist/govern-scan-UCBZR6D6.js.map +0 -1
- package/dist/sass.node-4XJK6YBF.js +0 -130708
- package/dist/sass.node-4XJK6YBF.js.map +0 -1
- package/dist/scan-generate-TWRHNU5M.js.map +0 -1
- package/src/build.ts +0 -736
- package/src/core/auto-props.ts +0 -464
- package/src/core/component-extractor.ts +0 -1121
- package/src/core/token-resolver.ts +0 -155
- package/src/viewer/preview-adapter.ts +0 -116
- /package/dist/{ai-client-I6MDWNYA.js.map → ai-client-LSLQGOMM.js.map} +0 -0
- /package/dist/{chunk-Z7EY4VHE.js.map → codebase-scanner-MQHUZC2G.js.map} +0 -0
- /package/dist/{codebase-scanner-VOTPXRYW.js.map → node-37AUE74M.js.map} +0 -0
- /package/dist/{scan-VNNKACG2.js.map → scan-PKSYSTRR.js.map} +0 -0
- /package/dist/{scanner-7LAZYPWZ.js.map → scanner-4KZNOXAK.js.map} +0 -0
- /package/dist/{service-FHQU7YS7.js.map → service-QJGWUIVL.js.map} +0 -0
- /package/dist/{static-viewer-63PG6FWY.js.map → static-viewer-7QIBQZRC.js.map} +0 -0
- /package/dist/{tokens-6GYKDV6U.js.map → tokens-NZWFQIAB.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/create.ts","../src/theme/serializer.ts","../src/theme/schema.ts","../src/theme/generator.ts"],"sourcesContent":["/**\n * fragments create - Scaffold a new project with Fragments UI and a custom theme\n *\n * Mirrors the shadcn model: configure on the web (usefragments.com/create),\n * then `npx @fragments-sdk/cli create` bootstraps a project with that config.\n */\n\nimport { execSync } from 'node:child_process';\nimport { existsSync, mkdirSync, writeFileSync, readFileSync, unlinkSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport pc from 'picocolors';\nimport { BRAND } from '../core/index.js';\nimport { decompressTheme } from '../theme/serializer.js';\nimport { generateCssTokens, generateScssTokens } from '../theme/generator.js';\nimport type { ThemeConfig } from '../theme/types.js';\n\n// ============================================\n// Types\n// ============================================\n\nexport interface CreateOptions {\n name?: string;\n template?: 'nextjs' | 'vite';\n packageManager?: 'npm' | 'pnpm' | 'yarn' | 'bun';\n theme?: string;\n preset?: string;\n brand?: string;\n scss?: boolean;\n mcp?: boolean;\n yes?: boolean;\n noGit?: boolean;\n}\n\nexport interface CreateResult {\n success: boolean;\n projectDir?: string;\n error?: string;\n}\n\n// ============================================\n// Package Manager Detection\n// ============================================\n\nfunction detectPackageManager(): 'npm' | 'pnpm' | 'yarn' | 'bun' {\n const agent = process.env.npm_config_user_agent || '';\n if (agent.startsWith('pnpm')) return 'pnpm';\n if (agent.startsWith('yarn')) return 'yarn';\n if (agent.startsWith('bun')) return 'bun';\n return 'npm';\n}\n\nfunction getInstallCommand(pm: string): string {\n return pm === 'yarn' ? 'yarn add' : `${pm} add`;\n}\n\nfunction getDevInstallCommand(pm: string): string {\n return pm === 'yarn' ? 'yarn add -D' : `${pm} add -D`;\n}\n\nfunction getRunCommand(pm: string): string {\n return pm === 'npm' ? 'npm run' : pm;\n}\n\n// ============================================\n// Validation\n// ============================================\n\nfunction isValidProjectName(name: string): boolean {\n return /^[a-z0-9][a-z0-9._-]*$/.test(name);\n}\n\n// ============================================\n// Theme Resolution\n// ============================================\n\nconst PRESET_API_URL = 'https://canny-otter-874.convex.site/api/theme-presets';\n\nasync function fetchPreset(presetId: string): Promise<ThemeConfig | null> {\n try {\n const res = await fetch(`${PRESET_API_URL}?id=${encodeURIComponent(presetId)}`);\n if (!res.ok) return null;\n const theme = await res.json();\n if (!theme || !theme.name) return null;\n return theme as ThemeConfig;\n } catch {\n return null;\n }\n}\n\nasync function resolveTheme(options: CreateOptions): Promise<ThemeConfig | null> {\n if (options.preset) {\n console.log(pc.dim(` Fetching theme preset ${options.preset}...`));\n const theme = await fetchPreset(options.preset);\n if (!theme) {\n console.error(pc.red(`Error: Could not fetch preset \"${options.preset}\". It may have expired or the ID is invalid.`));\n return null;\n }\n return theme;\n }\n\n if (options.theme) {\n const decoded = decompressTheme(options.theme);\n if (!decoded) {\n console.error(pc.red('Error: Could not decode theme string. Make sure you copied it from usefragments.com/create'));\n return null;\n }\n return decoded;\n }\n\n if (options.brand) {\n return {\n name: 'custom',\n colors: {\n accent: options.brand,\n },\n };\n }\n\n return {\n name: 'default',\n colors: {\n accent: '#6366f1',\n },\n };\n}\n\n// ============================================\n// Templates\n// ============================================\n\nexport function generateNextjsLayout(themePath: string): string {\n return `import type { Metadata } from 'next';\nimport '@fragments-sdk/ui/styles';\nimport '${themePath}';\nimport { Providers } from './providers';\n\nexport const metadata: Metadata = {\n title: 'My App',\n description: 'Built with Fragments UI',\n};\n\nexport default function RootLayout({\n children,\n}: {\n children: React.ReactNode;\n}) {\n return (\n <html lang=\"en\" suppressHydrationWarning>\n <body>\n <Providers>{children}</Providers>\n </body>\n </html>\n );\n}\n`;\n}\n\nexport function generateNextjsProviders(): string {\n return `'use client';\n\nimport type { ReactNode } from 'react';\nimport { ThemeProvider, TooltipProvider, ToastProvider } from '@fragments-sdk/ui';\n\nexport function Providers({ children }: { children: ReactNode }) {\n return (\n <ThemeProvider defaultMode=\"system\">\n <TooltipProvider>\n <ToastProvider>{children}</ToastProvider>\n </TooltipProvider>\n </ThemeProvider>\n );\n}\n`;\n}\n\nexport function generateNextjsPage(): string {\n return `'use client';\n\nimport { Button, Card, Stack, Text, Input } from '@fragments-sdk/ui';\n\nexport default function Home() {\n return (\n <main style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>\n <Card style={{ maxWidth: 480, width: '100%' }}>\n <Card.Header>\n <Card.Title>Welcome to Fragments</Card.Title>\n <Card.Description>Your app is ready. Start building something great.</Card.Description>\n </Card.Header>\n <Card.Body>\n <Stack gap={3}>\n <Input placeholder=\"Enter something...\" />\n <Stack direction=\"row\" gap={2}>\n <Button>Get Started</Button>\n <Button variant=\"secondary\">Learn More</Button>\n </Stack>\n </Stack>\n </Card.Body>\n </Card>\n </main>\n );\n}\n`;\n}\n\nfunction generateViteMain(themePath: string): string {\n return `import { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport { ThemeProvider, TooltipProvider, ToastProvider } from '@fragments-sdk/ui';\nimport '@fragments-sdk/ui/styles';\nimport '${themePath}';\nimport App from './App';\n\ncreateRoot(document.getElementById('root')!).render(\n <StrictMode>\n <ThemeProvider defaultMode=\"system\">\n <TooltipProvider>\n <ToastProvider>\n <App />\n </ToastProvider>\n </TooltipProvider>\n </ThemeProvider>\n </StrictMode>,\n);\n`;\n}\n\nfunction generateViteApp(): string {\n return `import { Button, Card, Stack, Text, Input } from '@fragments-sdk/ui';\n\nfunction App() {\n return (\n <main style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>\n <Card style={{ maxWidth: 480, width: '100%' }}>\n <Card.Header>\n <Card.Title>Welcome to Fragments</Card.Title>\n <Card.Description>Your app is ready. Start building something great.</Card.Description>\n </Card.Header>\n <Card.Body>\n <Stack gap={3}>\n <Input placeholder=\"Enter something...\" />\n <Stack direction=\"row\" gap={2}>\n <Button>Get Started</Button>\n <Button variant=\"secondary\">Learn More</Button>\n </Stack>\n </Stack>\n </Card.Body>\n </Card>\n </main>\n );\n}\n\nexport default App;\n`;\n}\n\n// ============================================\n// Scaffolding\n// ============================================\n\nasync function promptIfMissing(options: CreateOptions): Promise<CreateOptions> {\n const resolved = { ...options };\n\n if (!resolved.name) {\n if (resolved.yes) {\n resolved.name = 'my-app';\n } else {\n try {\n const { input } = await import('@inquirer/prompts');\n resolved.name = await input({\n message: 'Project name:',\n default: 'my-app',\n validate: (val) => isValidProjectName(val) || 'Use lowercase letters, numbers, hyphens, dots, underscores',\n });\n } catch {\n resolved.name = 'my-app';\n }\n }\n }\n\n if (!resolved.template && !resolved.yes) {\n try {\n const { select } = await import('@inquirer/prompts');\n resolved.template = await select({\n message: 'Framework:',\n choices: [\n { name: 'Next.js (App Router)', value: 'nextjs' as const },\n { name: 'Vite + React', value: 'vite' as const },\n ],\n default: 'nextjs',\n });\n } catch {\n resolved.template = 'nextjs';\n }\n }\n\n resolved.template = resolved.template || 'nextjs';\n resolved.packageManager = resolved.packageManager || detectPackageManager();\n\n return resolved;\n}\n\nfunction scaffoldFramework(name: string, template: string, pm: string): void {\n console.log(pc.cyan(`\\nScaffolding ${template === 'nextjs' ? 'Next.js' : 'Vite + React'} project...\\n`));\n\n if (template === 'nextjs') {\n const cmd = `npx create-next-app@latest ${name} --ts --app --src-dir --no-tailwind --no-eslint --import-alias \"@/*\" --yes`;\n execSync(cmd, { stdio: 'inherit' });\n } else {\n // Vite\n if (pm === 'bun') {\n execSync(`bun create vite ${name} --template react-ts`, { stdio: 'inherit' });\n } else {\n execSync(`npm create vite@latest ${name} -- --template react-ts`, { stdio: 'inherit' });\n }\n }\n}\n\nfunction installDeps(projectDir: string, pm: string, scss: boolean): void {\n console.log(pc.cyan('\\nInstalling Fragments UI...\\n'));\n\n const install = getInstallCommand(pm);\n execSync(`${install} @fragments-sdk/ui`, { cwd: projectDir, stdio: 'inherit' });\n\n if (scss) {\n const devInstall = getDevInstallCommand(pm);\n execSync(`${devInstall} sass`, { cwd: projectDir, stdio: 'inherit' });\n }\n}\n\nfunction writeThemeFile(projectDir: string, theme: ThemeConfig, scss: boolean, template: string): string {\n const stylesDir = join(projectDir, 'src', 'styles');\n mkdirSync(stylesDir, { recursive: true });\n\n const ext = scss ? 'scss' : 'css';\n const content = scss ? generateScssTokens(theme) : generateCssTokens(theme);\n const filePath = join(stylesDir, `theme.${ext}`);\n writeFileSync(filePath, content, 'utf-8');\n\n // Next.js layout is at src/app/layout.tsx → relative path is ../styles/theme.ext\n // Vite main is at src/main.tsx → relative path is ./styles/theme.ext\n if (template === 'nextjs') {\n return `../styles/theme.${ext}`;\n }\n return `./styles/theme.${ext}`;\n}\n\nexport function addNextTranspilePackages(projectDir: string): void {\n const configCandidates = ['next.config.ts', 'next.config.mjs', 'next.config.js'];\n\n for (const configFile of configCandidates) {\n const fullPath = join(projectDir, configFile);\n if (!existsSync(fullPath)) continue;\n\n const content = readFileSync(fullPath, 'utf-8');\n\n if (content.includes('transpilePackages') && content.includes('@fragments-sdk/ui')) {\n return;\n }\n\n if (content.includes('transpilePackages')) {\n return;\n }\n\n const patterns = [\n {\n search: /const\\s+\\w+\\s*(?::\\s*\\w+)?\\s*=\\s*\\{/,\n replacement: (match: string) => `${match}\\n transpilePackages: ['@fragments-sdk/ui'],`,\n },\n {\n search: /module\\.exports\\s*=\\s*\\{/,\n replacement: (match: string) => `${match}\\n transpilePackages: ['@fragments-sdk/ui'],`,\n },\n {\n search: /export\\s+default\\s*\\{/,\n replacement: (match: string) => `${match}\\n transpilePackages: ['@fragments-sdk/ui'],`,\n },\n ];\n\n for (const pattern of patterns) {\n if (!pattern.search.test(content)) continue;\n\n writeFileSync(\n fullPath,\n content.replace(pattern.search, pattern.replacement),\n 'utf-8',\n );\n return;\n }\n\n return;\n }\n}\n\nfunction rewriteAppFiles(projectDir: string, template: string, themePath: string): void {\n if (template === 'nextjs') {\n // Rewrite layout.tsx\n const layoutPath = join(projectDir, 'src', 'app', 'layout.tsx');\n writeFileSync(layoutPath, generateNextjsLayout(themePath), 'utf-8');\n\n const providersPath = join(projectDir, 'src', 'app', 'providers.tsx');\n writeFileSync(providersPath, generateNextjsProviders(), 'utf-8');\n\n // Rewrite page.tsx\n const pagePath = join(projectDir, 'src', 'app', 'page.tsx');\n writeFileSync(pagePath, generateNextjsPage(), 'utf-8');\n\n // Remove default page.module.css if it exists\n const moduleCssPath = join(projectDir, 'src', 'app', 'page.module.css');\n try {\n unlinkSync(moduleCssPath);\n } catch { /* doesn't exist, that's fine */ }\n\n // Remove globals.css if it exists (we use our own theme)\n const globalsCssPath = join(projectDir, 'src', 'app', 'globals.css');\n try {\n unlinkSync(globalsCssPath);\n } catch { /* doesn't exist */ }\n\n addNextTranspilePackages(projectDir);\n } else {\n // Vite: rewrite main.tsx and App.tsx\n const mainPath = join(projectDir, 'src', 'main.tsx');\n writeFileSync(mainPath, generateViteMain(themePath), 'utf-8');\n\n const appPath = join(projectDir, 'src', 'App.tsx');\n writeFileSync(appPath, generateViteApp(), 'utf-8');\n\n // Clean up default Vite files\n for (const file of ['src/App.css', 'src/index.css']) {\n try {\n unlinkSync(join(projectDir, file));\n } catch { /* doesn't exist */ }\n }\n }\n}\n\nfunction initGit(projectDir: string): void {\n try {\n // create-next-app already inits git, so check first\n if (!existsSync(join(projectDir, '.git'))) {\n execSync('git init', { cwd: projectDir, stdio: 'ignore' });\n execSync('git add -A', { cwd: projectDir, stdio: 'ignore' });\n execSync('git commit -m \"Initial commit with Fragments UI\"', { cwd: projectDir, stdio: 'ignore' });\n }\n } catch {\n // Git not available — skip silently\n }\n}\n\nfunction configureMcp(projectDir: string): void {\n const mcpConfig = {\n mcpServers: {\n fragments: {\n command: 'npx',\n args: ['-y', '@fragments-sdk/cli', 'mcp'],\n },\n },\n };\n\n const mcpDir = join(projectDir, '.mcp');\n mkdirSync(mcpDir, { recursive: true });\n writeFileSync(join(mcpDir, 'config.json'), JSON.stringify(mcpConfig, null, 2), 'utf-8');\n}\n\n// ============================================\n// Main\n// ============================================\n\nexport async function create(options: CreateOptions): Promise<CreateResult> {\n console.log(pc.cyan(`\\n${BRAND.name} Create\\n`));\n\n // 1. Gather inputs\n const resolved = await promptIfMissing(options);\n const name = resolved.name!;\n const template = resolved.template!;\n const pm = resolved.packageManager!;\n\n // 2. Validate\n if (!isValidProjectName(name)) {\n return { success: false, error: `Invalid project name: ${name}. Use lowercase letters, numbers, hyphens, dots, underscores.` };\n }\n\n const projectDir = resolve(process.cwd(), name);\n if (existsSync(projectDir)) {\n return { success: false, error: `Directory \"${name}\" already exists.` };\n }\n\n // 3. Resolve theme\n const theme = await resolveTheme(resolved);\n if (!theme) {\n return { success: false, error: 'Invalid theme configuration.' };\n }\n\n // 4. Scaffold framework\n scaffoldFramework(name, template, pm);\n\n if (!existsSync(projectDir)) {\n return { success: false, error: 'Framework scaffolding failed — project directory was not created.' };\n }\n\n // 5. Install deps\n installDeps(projectDir, pm, !!resolved.scss);\n\n // 6. Write theme tokens\n const themePath = writeThemeFile(projectDir, theme, !!resolved.scss, template);\n\n // 7. Rewrite app files with providers + theme import\n rewriteAppFiles(projectDir, template, themePath);\n\n // 8. MCP config\n if (resolved.mcp) {\n configureMcp(projectDir);\n console.log(pc.dim(' Configured MCP server for AI tooling'));\n }\n\n // 9. Git init\n if (!resolved.noGit) {\n initGit(projectDir);\n }\n\n // 10. Success message\n const run = getRunCommand(pm);\n console.log('');\n console.log(pc.green(' Project created successfully!'));\n console.log('');\n console.log(` ${pc.cyan('cd')} ${name}`);\n console.log(` ${pc.cyan(run)} dev`);\n console.log('');\n\n if (theme.name !== 'default') {\n console.log(pc.dim(` Theme \"${theme.name}\" applied with full token set.`));\n console.log(pc.dim(` Edit src/styles/theme.${resolved.scss ? 'scss' : 'css'} to customize.\\n`));\n }\n\n return { success: true, projectDir };\n}\n","/**\n * Theme Serialization\n *\n * Encodes/decodes theme configurations to/from URL-safe strings\n * using zlib compression and base64url encoding.\n */\n\nimport { deflateSync, inflateSync } from \"node:zlib\";\nimport { validateThemeConfig } from \"./schema.js\";\nimport type { ThemeConfig } from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://usefragments.com/create\";\n\n/**\n * Convert a Buffer to base64url encoding (URL-safe base64)\n */\nfunction toBase64Url(buffer: Buffer): string {\n return buffer\n .toString(\"base64\")\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=/g, \"\");\n}\n\n/**\n * Convert a base64url string to Buffer\n */\nfunction fromBase64Url(str: string): Buffer {\n // Restore standard base64 characters\n let base64 = str.replace(/-/g, \"+\").replace(/_/g, \"/\");\n\n // Add padding if needed\n const pad = base64.length % 4;\n if (pad) {\n base64 += \"=\".repeat(4 - pad);\n }\n\n return Buffer.from(base64, \"base64\");\n}\n\n/**\n * Compress a theme configuration to a URL-safe string\n *\n * Process: JSON → deflate → base64url\n *\n * @param config - Theme configuration to compress\n * @returns URL-safe encoded string\n */\nexport function compressTheme(config: ThemeConfig): string {\n const json = JSON.stringify(config);\n const compressed = deflateSync(Buffer.from(json, \"utf-8\"), { level: 9 });\n return toBase64Url(compressed);\n}\n\n/**\n * Decompress a URL-safe string back to theme configuration\n *\n * Process: base64url → inflate → JSON\n *\n * @param encoded - URL-safe encoded string\n * @returns Theme configuration or null if invalid\n */\nexport function decompressTheme(encoded: string): ThemeConfig | null {\n // Try zlib-compressed first (CLI native encoding)\n try {\n const buffer = fromBase64Url(encoded);\n const decompressed = inflateSync(buffer);\n const json = decompressed.toString(\"utf-8\");\n const parsed = JSON.parse(json);\n const result = validateThemeConfig(parsed);\n if (result.success) return result.data;\n } catch { /* not zlib-compressed */ }\n\n // Fall back to plain base64url JSON (browser/docs encoding)\n try {\n const buffer = fromBase64Url(encoded);\n const json = buffer.toString(\"utf-8\");\n const parsed = JSON.parse(json);\n const result = validateThemeConfig(parsed);\n if (result.success) return result.data;\n } catch { /* invalid */ }\n\n return null;\n}\n\n/**\n * Encode a theme configuration to a full URL with ?preset= parameter\n *\n * @param config - Theme configuration to encode\n * @param baseUrl - Base URL (default: https://fragments.dev/init)\n * @returns Full URL with encoded theme as preset parameter\n */\nexport function encodeThemeToUrl(\n config: ThemeConfig,\n baseUrl: string = DEFAULT_BASE_URL\n): string {\n const encoded = compressTheme(config);\n const url = new URL(baseUrl);\n url.searchParams.set(\"preset\", encoded);\n return url.toString();\n}\n\n/**\n * Decode a theme from a URL or raw encoded string\n *\n * Handles:\n * - Full URLs with ?preset= parameter\n * - Raw encoded strings (compressed theme data)\n *\n * @param input - URL or encoded string\n * @returns Theme configuration or null if invalid\n */\nexport function decodeThemeFromUrl(input: string): ThemeConfig | null {\n // Try to parse as URL first\n try {\n const url = new URL(input);\n const preset = url.searchParams.get(\"preset\");\n if (preset) {\n return decompressTheme(preset);\n }\n // URL parsed but no preset param\n return null;\n } catch {\n // Not a valid URL, try as raw encoded string\n return decompressTheme(input);\n }\n}\n","/**\n * Theme Configuration Zod Schemas\n *\n * Provides validation for theme configuration objects\n */\n\nimport { z } from \"zod\";\nimport type { ThemeConfig } from \"./types.js\";\n\n/**\n * Regex patterns for color validation\n */\nconst HEX_COLOR_REGEX = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;\nconst RGB_COLOR_REGEX = /^rgba?\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*(,\\s*(0|1|0?\\.\\d+))?\\s*\\)$/;\nconst HSL_COLOR_REGEX = /^hsla?\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}%\\s*,\\s*\\d{1,3}%\\s*(,\\s*(0|1|0?\\.\\d+))?\\s*\\)$/;\n\n/**\n * CSS color string validator\n */\nconst colorSchema = z.string().refine(\n (val) => {\n return (\n HEX_COLOR_REGEX.test(val) ||\n RGB_COLOR_REGEX.test(val) ||\n HSL_COLOR_REGEX.test(val)\n );\n },\n { message: \"Invalid color format. Use hex (#rrggbb), rgb(), rgba(), hsl(), or hsla()\" }\n);\n\n/**\n * CSS size value validator (px, rem, em, etc.)\n */\nconst sizeSchema = z.string().refine(\n (val) => {\n // Match number followed by unit, or just a number (for unitless values like 9999px)\n return /^-?\\d*\\.?\\d+(px|rem|em|%|vw|vh)?$/.test(val);\n },\n { message: \"Invalid size format. Use px, rem, em, %, vw, or vh\" }\n);\n\n/**\n * Shadow value validator (more permissive for complex shadow syntax)\n */\nconst shadowSchema = z.string();\n\n/**\n * Font stack validator\n */\nconst fontStackSchema = z.string();\n\n/**\n * Font weight validator (100-900 in increments of 100)\n */\nconst fontWeightSchema = z.number().int().min(100).max(900);\n\n/**\n * Theme colors schema\n */\nexport const themeColorsSchema = z.object({\n accent: colorSchema.optional(),\n accentHover: colorSchema.optional(),\n accentActive: colorSchema.optional(),\n danger: colorSchema.optional(),\n dangerHover: colorSchema.optional(),\n success: colorSchema.optional(),\n warning: colorSchema.optional(),\n info: colorSchema.optional(),\n dangerBg: colorSchema.optional(),\n successBg: colorSchema.optional(),\n warningBg: colorSchema.optional(),\n infoBg: colorSchema.optional(),\n}).strict();\n\n/**\n * Theme surfaces schema\n */\nexport const themeSurfacesSchema = z.object({\n bgPrimary: colorSchema.optional(),\n bgSecondary: colorSchema.optional(),\n bgTertiary: colorSchema.optional(),\n bgElevated: colorSchema.optional(),\n bgHover: colorSchema.optional(),\n bgActive: colorSchema.optional(),\n}).strict();\n\n/**\n * Theme text schema\n */\nexport const themeTextSchema = z.object({\n primary: colorSchema.optional(),\n secondary: colorSchema.optional(),\n tertiary: colorSchema.optional(),\n inverse: colorSchema.optional(),\n}).strict();\n\n/**\n * Theme borders schema\n */\nexport const themeBordersSchema = z.object({\n default: colorSchema.optional(),\n strong: colorSchema.optional(),\n}).strict();\n\n/**\n * Theme typography schema\n */\nexport const themeTypographySchema = z.object({\n fontSans: fontStackSchema.optional(),\n fontMono: fontStackSchema.optional(),\n fontWeightNormal: fontWeightSchema.optional(),\n fontWeightMedium: fontWeightSchema.optional(),\n fontWeightSemibold: fontWeightSchema.optional(),\n}).strict();\n\n/**\n * Theme radius schema\n */\nexport const themeRadiusSchema = z.object({\n sm: sizeSchema.optional(),\n md: sizeSchema.optional(),\n lg: sizeSchema.optional(),\n full: sizeSchema.optional(),\n}).strict();\n\n/**\n * Theme shadows schema\n */\nexport const themeShadowsSchema = z.object({\n sm: shadowSchema.optional(),\n md: shadowSchema.optional(),\n}).strict();\n\n/**\n * Theme dark mode schema\n */\nexport const themeDarkModeSchema = z.object({\n surfaces: themeSurfacesSchema.optional(),\n text: themeTextSchema.optional(),\n borders: themeBordersSchema.optional(),\n shadows: themeShadowsSchema.optional(),\n accent: colorSchema.optional(),\n accentHover: colorSchema.optional(),\n accentActive: colorSchema.optional(),\n dangerBg: colorSchema.optional(),\n successBg: colorSchema.optional(),\n warningBg: colorSchema.optional(),\n infoBg: colorSchema.optional(),\n backdrop: colorSchema.optional(),\n dangerText: colorSchema.optional(),\n successText: colorSchema.optional(),\n warningText: colorSchema.optional(),\n infoText: colorSchema.optional(),\n}).strict();\n\n/**\n * Complete theme configuration schema\n */\nexport const themeConfigSchema = z.object({\n name: z.string().min(1, \"Theme name is required\"),\n version: z.string().optional(),\n extends: z.string().optional(),\n colors: themeColorsSchema.optional(),\n surfaces: themeSurfacesSchema.optional(),\n text: themeTextSchema.optional(),\n borders: themeBordersSchema.optional(),\n typography: themeTypographySchema.optional(),\n radius: themeRadiusSchema.optional(),\n shadows: themeShadowsSchema.optional(),\n dark: themeDarkModeSchema.optional(),\n density: z.enum(['compact', 'default', 'relaxed']).optional(),\n}).strict();\n\n/**\n * Type for the inferred schema\n */\nexport type ThemeConfigInput = z.input<typeof themeConfigSchema>;\nexport type ThemeConfigOutput = z.output<typeof themeConfigSchema>;\n\n/**\n * Validation result type\n */\nexport type ThemeValidationResult =\n | { success: true; data: ThemeConfig }\n | { success: false; error: z.ZodError };\n\n/**\n * Validate a theme configuration object\n *\n * @param config - The theme configuration to validate\n * @returns Validation result with either the parsed data or error details\n */\nexport function validateThemeConfig(config: unknown): ThemeValidationResult {\n const result = themeConfigSchema.safeParse(config);\n\n if (result.success) {\n return { success: true, data: result.data as ThemeConfig };\n }\n\n return { success: false, error: result.error };\n}\n","/**\n * Theme Token Generator\n *\n * Generates SCSS and CSS token files from theme configurations\n */\n\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type {\n ThemeConfig,\n TokenGeneratorOptions,\n TokenGeneratorResult,\n ContrastPair,\n ContrastWarning,\n ContrastValidationResult,\n} from \"./types.js\";\nimport {\n parseColor,\n contrastRatio,\n meetsAA as checkAA,\n meetsAAA as checkAAA,\n} from \"./contrast.js\";\n\nconst DEFAULT_FILE_PREFIX = \"_theme-tokens\";\n\n/**\n * Token mapping from ThemeConfig structure to SCSS variable names\n */\nconst TOKEN_MAPPINGS = {\n colors: {\n accent: \"fui-color-accent\",\n accentHover: \"fui-color-accent-hover\",\n accentActive: \"fui-color-accent-active\",\n danger: \"fui-color-danger\",\n dangerHover: \"fui-color-danger-hover\",\n success: \"fui-color-success\",\n warning: \"fui-color-warning\",\n info: \"fui-color-info\",\n dangerBg: \"fui-color-danger-bg\",\n successBg: \"fui-color-success-bg\",\n warningBg: \"fui-color-warning-bg\",\n infoBg: \"fui-color-info-bg\",\n dangerText: \"fui-color-danger-text\",\n successText: \"fui-color-success-text\",\n warningText: \"fui-color-warning-text\",\n infoText: \"fui-color-info-text\",\n },\n surfaces: {\n bgPrimary: \"fui-bg-primary\",\n bgSecondary: \"fui-bg-secondary\",\n bgTertiary: \"fui-bg-tertiary\",\n bgElevated: \"fui-bg-elevated\",\n bgHover: \"fui-bg-hover\",\n bgActive: \"fui-bg-active\",\n },\n text: {\n primary: \"fui-text-primary\",\n secondary: \"fui-text-secondary\",\n tertiary: \"fui-text-tertiary\",\n inverse: \"fui-text-inverse\",\n },\n borders: {\n default: \"fui-border\",\n strong: \"fui-border-strong\",\n },\n typography: {\n fontSans: \"fui-font-sans\",\n fontMono: \"fui-font-mono\",\n fontWeightNormal: \"fui-font-weight-normal\",\n fontWeightMedium: \"fui-font-weight-medium\",\n fontWeightSemibold: \"fui-font-weight-semibold\",\n },\n radius: {\n sm: \"fui-radius-sm\",\n md: \"fui-radius-md\",\n lg: \"fui-radius-lg\",\n full: \"fui-radius-full\",\n },\n shadows: {\n sm: \"fui-shadow-sm\",\n md: \"fui-shadow-md\",\n },\n} as const;\n\n/**\n * Dark mode token mappings\n */\nconst DARK_TOKEN_MAPPINGS = {\n surfaces: {\n bgPrimary: \"fui-dark-bg-primary\",\n bgSecondary: \"fui-dark-bg-secondary\",\n bgTertiary: \"fui-dark-bg-tertiary\",\n bgElevated: \"fui-dark-bg-elevated\",\n bgHover: \"fui-dark-bg-hover\",\n bgActive: \"fui-dark-bg-active\",\n },\n text: {\n primary: \"fui-dark-text-primary\",\n secondary: \"fui-dark-text-secondary\",\n tertiary: \"fui-dark-text-tertiary\",\n inverse: \"fui-dark-text-inverse\",\n },\n borders: {\n default: \"fui-dark-border\",\n strong: \"fui-dark-border-strong\",\n },\n shadows: {\n sm: \"fui-dark-shadow-sm\",\n md: \"fui-dark-shadow-md\",\n },\n // Direct dark mode properties\n accent: \"fui-dark-color-accent\",\n accentHover: \"fui-dark-color-accent-hover\",\n accentActive: \"fui-dark-color-accent-active\",\n dangerBg: \"fui-dark-color-danger-bg\",\n successBg: \"fui-dark-color-success-bg\",\n warningBg: \"fui-dark-color-warning-bg\",\n infoBg: \"fui-dark-color-info-bg\",\n dangerText: \"fui-dark-color-danger-text\",\n successText: \"fui-dark-color-success-text\",\n warningText: \"fui-dark-color-warning-text\",\n infoText: \"fui-dark-color-info-text\",\n backdrop: \"fui-dark-backdrop\",\n} as const;\n\n/**\n * Generate tokens for a category\n */\nfunction generateCategoryTokens(\n config: ThemeConfig,\n categoryKey: keyof typeof TOKEN_MAPPINGS,\n format: \"scss\" | \"css\"\n): string[] {\n const category = config[categoryKey];\n if (!category) return [];\n\n const mappings = TOKEN_MAPPINGS[categoryKey];\n const tokens: string[] = [];\n\n for (const [key, varName] of Object.entries(mappings)) {\n const value = (category as Record<string, unknown>)[key];\n if (value !== undefined) {\n if (format === \"scss\") {\n tokens.push(`$${varName}: ${value} !default;`);\n } else {\n tokens.push(` --${varName}: ${value};`);\n }\n }\n }\n\n return tokens;\n}\n\n/**\n * Generate dark mode tokens\n */\nfunction generateDarkTokens(\n config: ThemeConfig,\n format: \"scss\" | \"css\"\n): string[] {\n if (!config.dark) return [];\n\n const tokens: string[] = [];\n\n // Handle nested categories\n const nestedCategories = [\"surfaces\", \"text\", \"borders\", \"shadows\"] as const;\n for (const category of nestedCategories) {\n const categoryData = config.dark[category];\n if (!categoryData) continue;\n\n const mappings = DARK_TOKEN_MAPPINGS[category];\n for (const [key, varName] of Object.entries(mappings)) {\n const value = (categoryData as Record<string, unknown>)[key];\n if (value !== undefined) {\n if (format === \"scss\") {\n tokens.push(`$${varName}: ${value} !default;`);\n } else {\n tokens.push(` --${varName.replace(\"fui-dark-\", \"fui-\")}: ${value};`);\n }\n }\n }\n }\n\n // Handle direct dark mode properties\n const directProps = [\"accent\", \"accentHover\", \"accentActive\", \"dangerBg\", \"successBg\", \"warningBg\", \"infoBg\", \"dangerText\", \"successText\", \"warningText\", \"infoText\", \"backdrop\"] as const;\n for (const prop of directProps) {\n const value = config.dark[prop];\n if (value !== undefined) {\n const varName = DARK_TOKEN_MAPPINGS[prop];\n if (format === \"scss\") {\n tokens.push(`$${varName}: ${value} !default;`);\n } else {\n // For CSS, use the light mode variable name in dark context\n const cssVarName = varName.replace(\"fui-dark-color-\", \"fui-color-\").replace(\"fui-dark-\", \"fui-\");\n tokens.push(` --${cssVarName}: ${value};`);\n }\n }\n }\n\n return tokens;\n}\n\n/**\n * Validate contrast across all text/surface token pairs in a theme.\n *\n * Returns `ContrastValidationResult` with every evaluated pair plus\n * errors (fails AA) and warnings (passes AA, fails AAA).\n */\nexport function validateContrast(config: ThemeConfig): ContrastValidationResult {\n const pairs: ContrastPair[] = [];\n const warnings: ContrastWarning[] = [];\n const errors: ContrastWarning[] = [];\n\n const textTokens = [\n { key: 'primary', token: 'text.primary' },\n { key: 'secondary', token: 'text.secondary' },\n { key: 'tertiary', token: 'text.tertiary' },\n ] as const;\n\n const surfaceTokens = [\n { key: 'bgPrimary', token: 'surfaces.bgPrimary' },\n { key: 'bgSecondary', token: 'surfaces.bgSecondary' },\n { key: 'bgTertiary', token: 'surfaces.bgTertiary' },\n { key: 'bgElevated', token: 'surfaces.bgElevated' },\n ] as const;\n\n // -- Light mode matrix --\n for (const text of textTokens) {\n const textColor = config.text?.[text.key];\n if (!textColor) continue;\n\n for (const surface of surfaceTokens) {\n const surfaceColor = config.surfaces?.[surface.key];\n if (!surfaceColor) continue;\n\n try {\n const fg = parseColor(textColor);\n const bg = parseColor(surfaceColor);\n const ratio = contrastRatio(fg, bg);\n const aa = checkAA(ratio);\n const aaa = checkAAA(ratio);\n\n const pair: ContrastPair = {\n textToken: text.token,\n surfaceToken: surface.token,\n textColor,\n surfaceColor,\n ratio: Math.round(ratio * 100) / 100,\n meetsAA: aa,\n meetsAAA: aaa,\n mode: 'light',\n };\n pairs.push(pair);\n\n if (!aa) {\n errors.push({\n message: `${text.token} (${textColor}) on ${surface.token} (${surfaceColor}) — ${pair.ratio}:1 — fails AA (need 4.5:1)`,\n pair,\n severity: 'error',\n });\n } else if (!aaa) {\n warnings.push({\n message: `${text.token} (${textColor}) on ${surface.token} (${surfaceColor}) — ${pair.ratio}:1 — passes AA but fails AAA (need 7:1)`,\n pair,\n severity: 'warning',\n });\n }\n } catch {\n // Skip unparseable colors (e.g. rgba with alpha)\n }\n }\n }\n\n // inverse text on accent\n if (config.text?.inverse && config.colors?.accent) {\n try {\n const fg = parseColor(config.text.inverse);\n const bg = parseColor(config.colors.accent);\n const ratio = contrastRatio(fg, bg);\n const aa = checkAA(ratio);\n const aaa = checkAAA(ratio);\n\n const pair: ContrastPair = {\n textToken: 'text.inverse',\n surfaceToken: 'colors.accent',\n textColor: config.text.inverse,\n surfaceColor: config.colors.accent,\n ratio: Math.round(ratio * 100) / 100,\n meetsAA: aa,\n meetsAAA: aaa,\n mode: 'light',\n };\n pairs.push(pair);\n\n if (!aa) {\n errors.push({\n message: `text.inverse (${config.text.inverse}) on colors.accent (${config.colors.accent}) — ${pair.ratio}:1 — fails AA`,\n pair,\n severity: 'error',\n });\n } else if (!aaa) {\n warnings.push({\n message: `text.inverse (${config.text.inverse}) on colors.accent (${config.colors.accent}) — ${pair.ratio}:1 — passes AA but fails AAA`,\n pair,\n severity: 'warning',\n });\n }\n } catch {\n // skip\n }\n }\n\n // -- Dark mode matrix --\n if (config.dark) {\n const darkTextTokens = [\n { key: 'primary' as const, token: 'dark.text.primary' },\n { key: 'secondary' as const, token: 'dark.text.secondary' },\n { key: 'tertiary' as const, token: 'dark.text.tertiary' },\n ];\n\n const darkSurfaceTokens = [\n { key: 'bgPrimary' as const, token: 'dark.surfaces.bgPrimary' },\n { key: 'bgSecondary' as const, token: 'dark.surfaces.bgSecondary' },\n { key: 'bgTertiary' as const, token: 'dark.surfaces.bgTertiary' },\n { key: 'bgElevated' as const, token: 'dark.surfaces.bgElevated' },\n ];\n\n for (const text of darkTextTokens) {\n const textColor = config.dark.text?.[text.key] ?? config.text?.[text.key];\n if (!textColor) continue;\n\n for (const surface of darkSurfaceTokens) {\n const surfaceColor = config.dark.surfaces?.[surface.key] ?? config.surfaces?.[surface.key];\n if (!surfaceColor) continue;\n\n try {\n const fg = parseColor(textColor);\n const bg = parseColor(surfaceColor);\n const ratio = contrastRatio(fg, bg);\n const aa = checkAA(ratio);\n const aaa = checkAAA(ratio);\n\n const pair: ContrastPair = {\n textToken: text.token,\n surfaceToken: surface.token,\n textColor,\n surfaceColor,\n ratio: Math.round(ratio * 100) / 100,\n meetsAA: aa,\n meetsAAA: aaa,\n mode: 'dark',\n };\n pairs.push(pair);\n\n if (!aa) {\n errors.push({\n message: `${text.token} (${textColor}) on ${surface.token} (${surfaceColor}) — ${pair.ratio}:1 — fails AA`,\n pair,\n severity: 'error',\n });\n } else if (!aaa) {\n warnings.push({\n message: `${text.token} (${textColor}) on ${surface.token} (${surfaceColor}) — ${pair.ratio}:1 — passes AA but fails AAA`,\n pair,\n severity: 'warning',\n });\n }\n } catch {\n // skip\n }\n }\n }\n }\n\n return {\n pairs,\n warnings,\n errors,\n passed: errors.length === 0,\n };\n}\n\n/**\n * Generate SCSS tokens from a theme configuration\n *\n * @param config - Theme configuration\n * @returns SCSS content string\n */\nexport function generateScssTokens(config: ThemeConfig): string {\n const lines: string[] = [];\n\n // Header\n lines.push(\"// Auto-generated by @fragments-sdk/cli\");\n lines.push(`// Theme: ${config.name}`);\n if (config.version) {\n lines.push(`// Version: ${config.version}`);\n }\n lines.push(\"\");\n\n // Colors\n const colorTokens = generateCategoryTokens(config, \"colors\", \"scss\");\n if (colorTokens.length > 0) {\n lines.push(\"// Colors\");\n lines.push(...colorTokens);\n lines.push(\"\");\n }\n\n // Surfaces\n const surfaceTokens = generateCategoryTokens(config, \"surfaces\", \"scss\");\n if (surfaceTokens.length > 0) {\n lines.push(\"// Surfaces\");\n lines.push(...surfaceTokens);\n lines.push(\"\");\n }\n\n // Text\n const textTokens = generateCategoryTokens(config, \"text\", \"scss\");\n if (textTokens.length > 0) {\n lines.push(\"// Text\");\n lines.push(...textTokens);\n lines.push(\"\");\n }\n\n // Borders\n const borderTokens = generateCategoryTokens(config, \"borders\", \"scss\");\n if (borderTokens.length > 0) {\n lines.push(\"// Borders\");\n lines.push(...borderTokens);\n lines.push(\"\");\n }\n\n // Typography\n const typographyTokens = generateCategoryTokens(config, \"typography\", \"scss\");\n if (typographyTokens.length > 0) {\n lines.push(\"// Typography\");\n lines.push(...typographyTokens);\n lines.push(\"\");\n }\n\n // Border Radius\n const radiusTokens = generateCategoryTokens(config, \"radius\", \"scss\");\n if (radiusTokens.length > 0) {\n lines.push(\"// Border Radius\");\n lines.push(...radiusTokens);\n lines.push(\"\");\n }\n\n // Shadows\n const shadowTokens = generateCategoryTokens(config, \"shadows\", \"scss\");\n if (shadowTokens.length > 0) {\n lines.push(\"// Shadows\");\n lines.push(...shadowTokens);\n lines.push(\"\");\n }\n\n // Density\n if (config.density) {\n lines.push(\"// Density\");\n lines.push(`$fui-density: \"${config.density}\" !default;`);\n lines.push(\"\");\n }\n\n // Dark mode\n const darkTokens = generateDarkTokens(config, \"scss\");\n if (darkTokens.length > 0) {\n lines.push(\"// Dark Mode\");\n lines.push(...darkTokens);\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Generate CSS custom properties from a theme configuration\n *\n * @param config - Theme configuration\n * @returns CSS content string\n */\nexport function generateCssTokens(config: ThemeConfig): string {\n const lines: string[] = [];\n\n // Header\n lines.push(\"/* Auto-generated by @fragments-sdk/cli */\");\n lines.push(`/* Theme: ${config.name} */`);\n if (config.version) {\n lines.push(`/* Version: ${config.version} */`);\n }\n lines.push(\"\");\n\n // Collect all light mode tokens\n const lightTokens: string[] = [];\n\n const categories = [\"colors\", \"surfaces\", \"text\", \"borders\", \"typography\", \"radius\", \"shadows\"] as const;\n for (const category of categories) {\n const tokens = generateCategoryTokens(config, category, \"css\");\n lightTokens.push(...tokens);\n }\n\n // Density\n if (config.density) {\n lightTokens.push(` --fui-density: ${config.density};`);\n }\n\n if (lightTokens.length > 0) {\n lines.push(\":root {\");\n lines.push(...lightTokens);\n lines.push(\"}\");\n lines.push(\"\");\n }\n\n // Dark mode\n const darkTokens = generateDarkTokens(config, \"css\");\n if (darkTokens.length > 0) {\n lines.push(':root.dark,');\n lines.push(':root[data-theme=\"dark\"] {');\n lines.push(...darkTokens);\n lines.push(\"}\");\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Generate token files from a theme configuration\n *\n * @param config - Theme configuration\n * @param options - Generator options\n * @returns Result with file paths or error\n */\nexport async function generateTokenFiles(\n config: ThemeConfig,\n options: TokenGeneratorOptions\n): Promise<TokenGeneratorResult> {\n const { format, outputDir, filePrefix = DEFAULT_FILE_PREFIX } = options;\n\n try {\n // Ensure output directory exists\n await mkdir(outputDir, { recursive: true });\n\n const result: TokenGeneratorResult = { success: true };\n\n // Run contrast validation (non-blocking)\n const contrastResult = validateContrast(config);\n result.contrastValidation = contrastResult;\n\n if (contrastResult.errors.length > 0) {\n for (const err of contrastResult.errors) {\n console.warn(`[contrast] ERROR: ${err.message}`);\n }\n }\n if (contrastResult.warnings.length > 0) {\n for (const warn of contrastResult.warnings) {\n console.warn(`[contrast] WARNING: ${warn.message}`);\n }\n }\n\n // Generate SCSS if requested\n if (format === \"scss\" || format === \"both\") {\n const scssContent = generateScssTokens(config);\n const scssPath = join(outputDir, `${filePrefix}.scss`);\n await writeFile(scssPath, scssContent, \"utf-8\");\n result.scssPath = scssPath;\n }\n\n // Generate CSS if requested\n if (format === \"css\" || format === \"both\") {\n const cssContent = generateCssTokens(config);\n const cssPath = join(outputDir, `${filePrefix}.css`);\n await writeFile(cssPath, cssContent, \"utf-8\");\n result.cssPath = cssPath;\n }\n\n return result;\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n}\n"],"mappings":"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","names":["join","join"]}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
|
|
2
|
+
import "./chunk-D2CDBRNU.js";
|
|
2
3
|
import {
|
|
3
4
|
extractPropsFromFile
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-D2CDBRNU.js";
|
|
5
|
+
} from "./chunk-HQ6A6DTV.js";
|
|
6
6
|
import {
|
|
7
7
|
BRAND
|
|
8
8
|
} from "./chunk-32LIWN2P.js";
|
|
9
|
-
import "./chunk-Z7EY4VHE.js";
|
|
10
9
|
|
|
11
10
|
// src/commands/generate.ts
|
|
12
11
|
import { readFile, writeFile, access } from "fs/promises";
|
|
@@ -455,4 +454,4 @@ function inferStatus(filePath) {
|
|
|
455
454
|
export {
|
|
456
455
|
generate
|
|
457
456
|
};
|
|
458
|
-
//# sourceMappingURL=generate-
|
|
457
|
+
//# sourceMappingURL=generate-PVOLUAAC.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/generate.ts"],"sourcesContent":["/**\n * fragments generate - Generate fragment files from component source code\n *\n * Analyzes component source code and generates proper defineFragment() TSX files\n * colocated next to the component source. These files are parseable by the\n * build command and renderable by the dev viewer.\n */\n\nimport { readFile, writeFile, access } from \"node:fs/promises\";\nimport { resolve, basename, dirname, relative, join } from \"node:path\";\nimport pc from \"picocolors\";\nimport fg from \"fast-glob\";\nimport { BRAND } from \"../core/index.js\";\nimport { extractPropsFromFile } from \"../core/node.js\";\n\nexport interface GenerateOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Specific component name to generate (optional) */\n component?: string;\n /** Overwrite existing fragment files */\n force?: boolean;\n /** Pattern for component files */\n componentPattern?: string;\n}\n\nexport interface GenerateResult {\n success: boolean;\n generated: Array<{ name: string; path: string }>;\n skipped: Array<{ name: string; reason: string }>;\n errors: Array<{ name: string; error: string }>;\n}\n\n/**\n * Generate fragment files from component source code\n */\nexport async function generate(options: GenerateOptions = {}): Promise<GenerateResult> {\n const projectRoot = resolve(options.projectRoot || process.cwd());\n const generated: Array<{ name: string; path: string }> = [];\n const skipped: Array<{ name: string; reason: string }> = [];\n const errors: Array<{ name: string; error: string }> = [];\n\n console.log(pc.cyan(`\\n${BRAND.name} Generate\\n`));\n\n // Find component files\n const componentPattern =\n options.componentPattern ||\n \"src/components/**/*.tsx\";\n\n const componentFiles = await fg(componentPattern, {\n cwd: projectRoot,\n ignore: [\n \"**/node_modules/**\",\n \"**/*.stories.*\",\n \"**/*.fragment.*\",\n \"**/*.test.*\",\n \"**/*.spec.*\",\n \"**/*.d.ts\",\n \"**/index.tsx\", // Often just re-exports\n ],\n absolute: true,\n });\n\n // Find story files for pattern inference\n const storyFiles = await fg([\"src/**/*.stories.tsx\", \"src/**/*.stories.ts\"], {\n cwd: projectRoot,\n ignore: [\"**/node_modules/**\"],\n absolute: true,\n });\n\n // Build story map for pattern inference (names + args)\n const storyMap = new Map<string, StoryVariant[]>();\n for (const storyFile of storyFiles) {\n try {\n const content = await readFile(storyFile, \"utf-8\");\n const componentName = extractComponentNameFromStory(content, storyFile);\n if (componentName) {\n const variants = extractStoryVariants(content);\n storyMap.set(componentName, variants);\n }\n } catch {\n // Ignore parsing errors\n }\n }\n\n console.log(pc.dim(`Found ${componentFiles.length} component files\\n`));\n\n // Process each component file\n for (const filePath of componentFiles) {\n try {\n const extracted = extractPropsFromFile(filePath);\n\n if (!extracted || !extracted.componentName) {\n continue;\n }\n\n const componentName = extracted.componentName;\n\n // Filter by component name if specified\n if (options.component && componentName !== options.component) {\n continue;\n }\n\n // Write fragment file colocated next to the component source\n const componentDir = dirname(filePath);\n const componentBaseName = basename(filePath, \".tsx\");\n const fragmentPath = join(componentDir, `${componentBaseName}${BRAND.fileExtension}`);\n\n let fragmentExists = false;\n try {\n await access(fragmentPath);\n fragmentExists = true;\n } catch {\n // Fragment doesn't exist\n }\n\n if (fragmentExists && !options.force) {\n skipped.push({ name: componentName, reason: \"Fragment already exists\" });\n console.log(pc.dim(` Skipping ${componentName} (fragment exists)`));\n continue;\n }\n\n // Generate proper defineFragment() TSX content\n const storyVariants = storyMap.get(componentName) || [];\n const fragmentContent = generateFragmentTsx(\n componentName,\n componentBaseName,\n extracted,\n filePath,\n storyVariants\n );\n\n // Write fragment file\n await writeFile(fragmentPath, fragmentContent, \"utf-8\");\n generated.push({ name: componentName, path: relative(projectRoot, fragmentPath) });\n console.log(pc.green(` ✓ Generated ${componentName}${BRAND.fileExtension}`));\n } catch (e) {\n const fileName = basename(filePath);\n errors.push({\n name: fileName,\n error: e instanceof Error ? e.message : String(e),\n });\n console.log(pc.red(` ✗ Failed: ${fileName}`));\n }\n }\n\n // Summary\n console.log();\n if (generated.length > 0) {\n console.log(pc.green(`✓ Generated ${generated.length} fragment(s)`));\n }\n if (skipped.length > 0) {\n console.log(pc.dim(` Skipped ${skipped.length} (use --force to overwrite)`));\n }\n if (errors.length > 0) {\n console.log(pc.yellow(` ${errors.length} error(s)`));\n }\n console.log();\n\n return {\n success: errors.length === 0,\n generated,\n skipped,\n errors,\n };\n}\n\n/**\n * Generate proper defineFragment() TSX file content\n */\nfunction generateFragmentTsx(\n componentName: string,\n componentBaseName: string,\n extracted: ReturnType<typeof extractPropsFromFile> & { componentName: string },\n filePath: string,\n storyVariants: StoryVariant[]\n): string {\n const storyNames = storyVariants.map((v) => v.name);\n const whenToUse = inferUsageFromStories(storyNames);\n const description = generateDescription(componentName, extracted.props);\n const status = inferStatus(filePath);\n const accessibility = inferAccessibility(extracted.props);\n\n // Build props object for defineFragment\n const propsEntries = Object.entries(extracted.props || {});\n\n // Format when array\n const whenLines = whenToUse.length > 0\n ? whenToUse.map((w) => ` '${escapeQuotes(w)}',`).join(\"\\n\")\n : ` 'TODO: describe when to use ${componentName}',`;\n\n // Format props\n let propsBlock = \"{}\";\n if (propsEntries.length > 0) {\n const propLines = propsEntries.map(([name, info]) => {\n const propInfo = info as Record<string, unknown>;\n const rawType = propInfo.type ? String(propInfo.type) : \"\";\n const classified = classifyPropType(rawType);\n const desc = propInfo.description ? String(propInfo.description).replace(/\\n/g, \" \") : \"\";\n const required = propInfo.required ? \"true\" : \"false\";\n const parts = [` type: '${classified.type}'`];\n if (desc) parts.push(` description: '${escapeQuotes(desc)}'`);\n parts.push(` required: ${required}`);\n if (propInfo.default !== undefined) {\n parts.push(` default: ${JSON.stringify(propInfo.default)}`);\n }\n // Use classified values (from string literal unions) or existing values\n const values = classified.values || (propInfo.values && Array.isArray(propInfo.values) ? propInfo.values : null);\n if (values && values.length > 0) {\n parts.push(` values: ${JSON.stringify(values)}`);\n }\n return ` ${name}: {\\n${parts.join(\",\\n\")},\\n }`;\n });\n propsBlock = `{\\n${propLines.join(\",\\n\")},\\n }`;\n }\n\n // Build accessibility section\n let accessibilityBlock = \"\";\n if (accessibility.role || (accessibility.requirements && accessibility.requirements.length > 0)) {\n const parts: string[] = [];\n if (accessibility.role) {\n parts.push(` role: '${accessibility.role}'`);\n }\n if (accessibility.requirements && accessibility.requirements.length > 0) {\n const reqs = accessibility.requirements.map((r) => `'${escapeQuotes(r)}'`).join(\", \");\n parts.push(` requirements: [${reqs}]`);\n }\n accessibilityBlock = `\n\n accessibility: {\n${parts.join(\",\\n\")},\n },`;\n }\n\n // Build variants from story args or fallback to bare render\n const variants = buildVariants(componentName, storyVariants);\n\n // Use default import when the source component uses export default\n const componentImportStatement = extracted.isDefaultExport\n ? `import ${componentName} from './${componentBaseName}';`\n : `import { ${componentName} } from './${componentBaseName}';`;\n\n return `import React from 'react';\nimport { defineFragment } from '@fragments-sdk/cli/core';\n${componentImportStatement}\n\nexport default defineFragment({\n component: ${componentName},\n\n meta: {\n name: '${escapeQuotes(componentName)}',\n description: '${escapeQuotes(description)}',\n category: '${inferCategory(componentName, extracted.props)}',\n status: '${status}',\n },\n\n usage: {\n when: [\n${whenLines}\n ],\n whenNot: [],\n },\n\n props: ${propsBlock},${accessibilityBlock}\n\n variants: [\n${variants}\n ],\n});\n`;\n}\n\n/**\n * Escape single quotes in strings\n */\nfunction escapeQuotes(str: string): string {\n return str.replace(/'/g, \"\\\\'\");\n}\n\n/**\n * Classify a raw TypeScript type string into a valid fragment prop type enum value.\n * Returns the classified type and optionally extracted enum values.\n */\nfunction classifyPropType(rawType: string): { type: string; values?: string[] } {\n const t = rawType.replace(/\\s+/g, \" \").trim();\n const lower = t.toLowerCase();\n\n // Direct primitive matches\n if (lower === \"string\") return { type: \"string\" };\n if (lower === \"number\") return { type: \"number\" };\n if (lower === \"boolean\" || lower === \"bool\") return { type: \"boolean\" };\n\n // String/number literal unions → enum with values\n // e.g., '\"primary\" | \"secondary\"' or \"'default' | 'modal'\"\n if (/^[\"'][^\"']*[\"'](\\s*\\|\\s*[\"'][^\"']*[\"'])*$/.test(t)) {\n const values = [...t.matchAll(/[\"']([^\"']+)[\"']/g)].map((m) => m[1]);\n if (values.length > 0) return { type: \"enum\", values };\n }\n\n // Function types — arrow functions, callbacks, handlers, dispatchers\n if (\n lower.includes(\"=>\") ||\n lower.includes(\"handler\") ||\n lower.includes(\"callback\") ||\n lower.includes(\"dispatch\") ||\n lower.includes(\"listener\") ||\n /^\\(/.test(t)\n ) {\n return { type: \"function\" };\n }\n\n // React node types\n if (\n lower.includes(\"reactnode\") ||\n lower.includes(\"react.reactnode\") ||\n lower === \"node\"\n ) {\n return { type: \"node\" };\n }\n\n // React element types\n if (\n lower.includes(\"reactelement\") ||\n lower.includes(\"react.reactelement\") ||\n lower.includes(\"jsx.element\")\n ) {\n return { type: \"element\" };\n }\n\n // Array types\n if (lower.includes(\"[]\") || lower.startsWith(\"array<\") || lower.startsWith(\"readonly \")) {\n return { type: \"array\" };\n }\n\n // Object types (must come after array check since objects can contain [])\n if (lower.startsWith(\"{\") || lower.includes(\"record<\") || lower === \"object\") {\n return { type: \"object\" };\n }\n\n // Union types (mixed types with |)\n if (lower.includes(\"|\")) {\n // Check if it's a union of just string literals (enum)\n const parts = t.split(\"|\").map((p) => p.trim());\n const allStringLiterals = parts.every((p) => /^[\"'].*[\"']$/.test(p));\n if (allStringLiterals) {\n const values = parts.map((p) => p.replace(/^[\"']|[\"']$/g, \"\"));\n return { type: \"enum\", values };\n }\n return { type: \"union\" };\n }\n\n // Anything else\n return { type: \"custom\" };\n}\n\n/**\n * Infer category from component name and props\n */\nfunction inferCategory(componentName: string, props: Record<string, unknown>): string {\n const lower = componentName.toLowerCase();\n\n const categoryPatterns: Record<string, string[]> = {\n \"Actions\": [\"button\", \"action\", \"cta\", \"fab\", \"floatingaction\"],\n \"Forms\": [\"form\", \"input\", \"select\", \"checkbox\", \"radio\", \"textarea\", \"field\", \"textfield\", \"datepicker\", \"switch\", \"slider\", \"segmented\"],\n \"Layout\": [\"layout\", \"container\", \"grid\", \"flex\", \"stack\", \"box\", \"divider\", \"spacer\", \"sidebar\"],\n \"Navigation\": [\"nav\", \"menu\", \"breadcrumb\", \"tab\", \"link\", \"pagination\", \"stepper\", \"topbar\"],\n \"Feedback\": [\"alert\", \"toast\", \"notification\", \"message\", \"badge\", \"indicator\", \"progress\", \"spinner\", \"loading\", \"loader\", \"lozenge\", \"chip\"],\n \"Data Display\": [\"table\", \"list\", \"card\", \"avatar\", \"stat\", \"timeline\", \"tree\", \"datalist\", \"datacard\"],\n \"Overlays\": [\"modal\", \"dialog\", \"drawer\", \"popover\", \"tooltip\", \"dropdown\", \"slidepanel\"],\n \"Typography\": [\"text\", \"heading\", \"title\", \"label\", \"paragraph\"],\n \"Media\": [\"image\", \"video\", \"icon\", \"carousel\"],\n };\n\n for (const [category, patterns] of Object.entries(categoryPatterns)) {\n for (const pattern of patterns) {\n if (lower.includes(pattern)) {\n return category;\n }\n }\n }\n\n if (\"onClick\" in props || \"onPress\" in props) return \"Actions\";\n if (\"value\" in props || \"defaultValue\" in props) return \"Forms\";\n if (\"children\" in props) return \"Layout\";\n\n return \"Components\";\n}\n\n/**\n * Extract component name from story file\n */\nfunction extractComponentNameFromStory(content: string, filePath: string): string | null {\n const titleMatch = content.match(/title:\\s*['\"](?:[^'\"]+\\/)?([^'\"]+)['\"]/);\n if (titleMatch) {\n return titleMatch[1];\n }\n\n const fileName = basename(filePath);\n const componentName = fileName.replace(/\\.stories\\.(tsx?|jsx?)$/, \"\");\n if (/^[A-Z]/.test(componentName)) {\n return componentName;\n }\n\n return null;\n}\n\ninterface StoryVariant {\n name: string;\n args: Record<string, unknown>;\n}\n\n/**\n * Extract story names and their args from story file content (CSF3 format).\n * Parses patterns like:\n * export const Primary: Story = { args: { variant: 'primary', children: 'Click me' } }\n */\nfunction extractStoryVariants(content: string): StoryVariant[] {\n const variants: StoryVariant[] = [];\n\n const exportMatches = content.matchAll(\n /export\\s+const\\s+([A-Z][a-zA-Z0-9]*)\\s*[=:]/g\n );\n\n for (const match of exportMatches) {\n const name = match[1];\n if (name === \"default\" || name.endsWith(\"Args\") || name.endsWith(\"Meta\")) {\n continue;\n }\n\n const args = extractStoryArgs(content, name);\n variants.push({ name, args });\n }\n\n return variants;\n}\n\n/**\n * Extract the `args` object from a named story export.\n * Uses balanced-brace matching to handle nested objects.\n */\nfunction extractStoryArgs(content: string, storyName: string): Record<string, unknown> {\n // Find the story export and look for an args block\n const storyPattern = new RegExp(\n `export\\\\s+const\\\\s+${storyName}[^=]*=\\\\s*\\\\{([\\\\s\\\\S]*?)\\\\n\\\\};`,\n );\n const storyMatch = content.match(storyPattern);\n if (!storyMatch) return {};\n\n const storyBody = storyMatch[1];\n\n // Find the args block within the story body\n const argsStart = storyBody.indexOf('args:');\n if (argsStart === -1) return {};\n\n // Find the opening brace after \"args:\"\n const braceStart = storyBody.indexOf('{', argsStart);\n if (braceStart === -1) return {};\n\n // Balanced brace matching to find the full args object\n let depth = 0;\n let braceEnd = -1;\n for (let i = braceStart; i < storyBody.length; i++) {\n if (storyBody[i] === '{') depth++;\n else if (storyBody[i] === '}') {\n depth--;\n if (depth === 0) {\n braceEnd = i;\n break;\n }\n }\n }\n if (braceEnd === -1) return {};\n\n const argsBlock = storyBody.slice(braceStart + 1, braceEnd).trim();\n return parseArgsBlock(argsBlock);\n}\n\n/**\n * Parse a simplified args block into key-value pairs.\n * Handles string literals, numbers, booleans, and simple expressions.\n */\nfunction parseArgsBlock(argsBlock: string): Record<string, unknown> {\n const args: Record<string, unknown> = {};\n\n // Match key: value pairs (handles string, number, boolean values)\n const pairPattern = /(\\w+)\\s*:\\s*(?:['\"]([^'\"]*?)['\"]|(true|false)|(\\d+(?:\\.\\d+)?))/g;\n let pairMatch: RegExpExecArray | null;\n\n while ((pairMatch = pairPattern.exec(argsBlock)) !== null) {\n const key = pairMatch[1];\n if (pairMatch[2] !== undefined) {\n // String value\n args[key] = pairMatch[2];\n } else if (pairMatch[3] !== undefined) {\n // Boolean\n args[key] = pairMatch[3] === 'true';\n } else if (pairMatch[4] !== undefined) {\n // Number\n args[key] = Number(pairMatch[4]);\n }\n }\n\n return args;\n}\n\n/**\n * Build variant entries from story args.\n * If stories have args, generates JSX with those props.\n * Falls back to a bare `<Component />` for the Default variant.\n */\nfunction buildVariants(componentName: string, storyVariants: StoryVariant[]): string {\n // Filter to variants with args, plus always include a Default\n const hasDefault = storyVariants.some((v) => v.name === 'Default');\n const entries: string[] = [];\n\n if (!hasDefault) {\n entries.push(formatVariantEntry(componentName, 'Default', `Default ${componentName}`, {}));\n }\n\n for (const variant of storyVariants) {\n const description = variant.name\n .replace(/([A-Z])/g, ' $1')\n .trim();\n entries.push(formatVariantEntry(componentName, variant.name, `${description} ${componentName}`, variant.args));\n }\n\n // Deduplicate: if stories provided a Default, don't double-add\n return entries.join('\\n');\n}\n\n/**\n * Format a single variant entry with JSX code and render function.\n * Handles `children` as JSX children, all other args as props.\n */\nfunction formatVariantEntry(\n componentName: string,\n name: string,\n description: string,\n args: Record<string, unknown>\n): string {\n const jsxCode = buildJsxString(componentName, args);\n return ` {\n name: '${escapeQuotes(name)}',\n description: '${escapeQuotes(description)}',\n code: \\`${jsxCode}\\`,\n render: () => ${jsxCode},\n },`;\n}\n\n/**\n * Build a JSX string from component name and args.\n * `children` string args become JSX children, others become props.\n */\nfunction buildJsxString(componentName: string, args: Record<string, unknown>): string {\n const { children, ...restArgs } = args;\n const propParts: string[] = [];\n\n for (const [key, value] of Object.entries(restArgs)) {\n if (typeof value === 'string') {\n propParts.push(`${key}=\"${escapeQuotes(value)}\"`);\n } else if (typeof value === 'boolean') {\n propParts.push(value ? key : `${key}={false}`);\n } else if (typeof value === 'number') {\n propParts.push(`${key}={${value}}`);\n }\n }\n\n const propsStr = propParts.length > 0 ? ' ' + propParts.join(' ') : '';\n\n if (typeof children === 'string') {\n return `<${componentName}${propsStr}>${children}</${componentName}>`;\n }\n\n return `<${componentName}${propsStr} />`;\n}\n\n/**\n * Infer usage scenarios from story names\n */\nfunction inferUsageFromStories(storyNames: string[]): string[] {\n const usage: string[] = [];\n\n for (const name of storyNames) {\n const sentence = name\n .replace(/([A-Z])/g, \" $1\")\n .trim()\n .toLowerCase();\n\n if (\n [\"default\", \"primary\", \"basic\", \"example\", \"playground\"].includes(\n sentence.toLowerCase()\n )\n ) {\n continue;\n }\n\n if (sentence.includes(\"loading\")) {\n usage.push(\"Showing loading states\");\n } else if (sentence.includes(\"disabled\")) {\n usage.push(\"Preventing user interaction\");\n } else if (sentence.includes(\"error\")) {\n usage.push(\"Displaying error states\");\n } else if (sentence.includes(\"success\")) {\n usage.push(\"Showing success feedback\");\n } else if (sentence.includes(\"empty\")) {\n usage.push(\"Handling empty states\");\n } else if (sentence.includes(\"with\")) {\n const withPart = sentence.replace(\"with \", \"\");\n usage.push(`Displaying with ${withPart}`);\n }\n }\n\n return usage;\n}\n\n/**\n * Generate description from component name and props\n */\nfunction generateDescription(\n componentName: string,\n props: Record<string, unknown>\n): string {\n const words = componentName\n .replace(/([A-Z])/g, \" $1\")\n .trim()\n .toLowerCase();\n\n const hasOnClick = \"onClick\" in props || \"onPress\" in props;\n const hasValue = \"value\" in props || \"defaultValue\" in props;\n const hasChildren = \"children\" in props;\n\n if (hasOnClick && !hasValue) {\n return `Interactive ${words} element for triggering actions`;\n }\n\n if (hasValue) {\n return `Form ${words} for user input`;\n }\n\n if (hasChildren) {\n return `Container ${words} for grouping content`;\n }\n\n return `${words.charAt(0).toUpperCase() + words.slice(1)} component`;\n}\n\n/**\n * Infer accessibility from props\n */\nfunction inferAccessibility(props: Record<string, unknown>): {\n role?: string;\n requirements?: string[];\n} {\n const accessibility: { role?: string; requirements?: string[] } = {};\n\n const hasOnClick = \"onClick\" in props || \"onPress\" in props;\n const hasAriaLabel = \"ariaLabel\" in props || \"aria-label\" in props;\n const hasDisabled = \"disabled\" in props;\n const hasHref = \"href\" in props;\n\n if (hasOnClick && !hasHref) {\n accessibility.role = \"button\";\n } else if (hasHref) {\n accessibility.role = \"link\";\n }\n\n const requirements: string[] = [];\n\n if (hasOnClick && !hasAriaLabel) {\n requirements.push(\"Should have visible text or aria-label\");\n }\n\n if (hasDisabled) {\n requirements.push(\"Disabled state should be conveyed to assistive technology\");\n }\n\n if (requirements.length > 0) {\n accessibility.requirements = requirements;\n }\n\n return accessibility;\n}\n\n/**\n * Infer status from file path\n */\nfunction inferStatus(\n filePath: string\n): \"draft\" | \"experimental\" | \"beta\" | \"stable\" | \"deprecated\" {\n const lowerPath = filePath.toLowerCase();\n\n if (lowerPath.includes(\"/experimental/\") || lowerPath.includes(\"/labs/\")) {\n return \"experimental\";\n }\n if (lowerPath.includes(\"/beta/\")) {\n return \"beta\";\n }\n if (lowerPath.includes(\"/deprecated/\") || lowerPath.includes(\"/legacy/\")) {\n return \"deprecated\";\n }\n if (lowerPath.includes(\"/draft/\") || lowerPath.includes(\"/wip/\")) {\n return \"draft\";\n }\n\n return \"stable\";\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/commands/generate.ts"],"sourcesContent":["/**\n * fragments generate - Generate fragment files from component source code\n *\n * Analyzes component source code and generates proper defineFragment() TSX files\n * colocated next to the component source. These files are parseable by the\n * build command and renderable by the dev viewer.\n */\n\nimport { readFile, writeFile, access } from \"node:fs/promises\";\nimport { resolve, basename, dirname, relative, join } from \"node:path\";\nimport pc from \"picocolors\";\nimport fg from \"fast-glob\";\nimport { BRAND } from \"../core/index.js\";\nimport { extractPropsFromFile } from \"../core/node.js\";\n\nexport interface GenerateOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Specific component name to generate (optional) */\n component?: string;\n /** Overwrite existing fragment files */\n force?: boolean;\n /** Pattern for component files */\n componentPattern?: string;\n}\n\nexport interface GenerateResult {\n success: boolean;\n generated: Array<{ name: string; path: string }>;\n skipped: Array<{ name: string; reason: string }>;\n errors: Array<{ name: string; error: string }>;\n}\n\n/**\n * Generate fragment files from component source code\n */\nexport async function generate(options: GenerateOptions = {}): Promise<GenerateResult> {\n const projectRoot = resolve(options.projectRoot || process.cwd());\n const generated: Array<{ name: string; path: string }> = [];\n const skipped: Array<{ name: string; reason: string }> = [];\n const errors: Array<{ name: string; error: string }> = [];\n\n console.log(pc.cyan(`\\n${BRAND.name} Generate\\n`));\n\n // Find component files\n const componentPattern =\n options.componentPattern ||\n \"src/components/**/*.tsx\";\n\n const componentFiles = await fg(componentPattern, {\n cwd: projectRoot,\n ignore: [\n \"**/node_modules/**\",\n \"**/*.stories.*\",\n \"**/*.fragment.*\",\n \"**/*.test.*\",\n \"**/*.spec.*\",\n \"**/*.d.ts\",\n \"**/index.tsx\", // Often just re-exports\n ],\n absolute: true,\n });\n\n // Find story files for pattern inference\n const storyFiles = await fg([\"src/**/*.stories.tsx\", \"src/**/*.stories.ts\"], {\n cwd: projectRoot,\n ignore: [\"**/node_modules/**\"],\n absolute: true,\n });\n\n // Build story map for pattern inference (names + args)\n const storyMap = new Map<string, StoryVariant[]>();\n for (const storyFile of storyFiles) {\n try {\n const content = await readFile(storyFile, \"utf-8\");\n const componentName = extractComponentNameFromStory(content, storyFile);\n if (componentName) {\n const variants = extractStoryVariants(content);\n storyMap.set(componentName, variants);\n }\n } catch {\n // Ignore parsing errors\n }\n }\n\n console.log(pc.dim(`Found ${componentFiles.length} component files\\n`));\n\n // Process each component file\n for (const filePath of componentFiles) {\n try {\n const extracted = extractPropsFromFile(filePath);\n\n if (!extracted || !extracted.componentName) {\n continue;\n }\n\n const componentName = extracted.componentName;\n\n // Filter by component name if specified\n if (options.component && componentName !== options.component) {\n continue;\n }\n\n // Write fragment file colocated next to the component source\n const componentDir = dirname(filePath);\n const componentBaseName = basename(filePath, \".tsx\");\n const fragmentPath = join(componentDir, `${componentBaseName}${BRAND.fileExtension}`);\n\n let fragmentExists = false;\n try {\n await access(fragmentPath);\n fragmentExists = true;\n } catch {\n // Fragment doesn't exist\n }\n\n if (fragmentExists && !options.force) {\n skipped.push({ name: componentName, reason: \"Fragment already exists\" });\n console.log(pc.dim(` Skipping ${componentName} (fragment exists)`));\n continue;\n }\n\n // Generate proper defineFragment() TSX content\n const storyVariants = storyMap.get(componentName) || [];\n const fragmentContent = generateFragmentTsx(\n componentName,\n componentBaseName,\n extracted,\n filePath,\n storyVariants\n );\n\n // Write fragment file\n await writeFile(fragmentPath, fragmentContent, \"utf-8\");\n generated.push({ name: componentName, path: relative(projectRoot, fragmentPath) });\n console.log(pc.green(` ✓ Generated ${componentName}${BRAND.fileExtension}`));\n } catch (e) {\n const fileName = basename(filePath);\n errors.push({\n name: fileName,\n error: e instanceof Error ? e.message : String(e),\n });\n console.log(pc.red(` ✗ Failed: ${fileName}`));\n }\n }\n\n // Summary\n console.log();\n if (generated.length > 0) {\n console.log(pc.green(`✓ Generated ${generated.length} fragment(s)`));\n }\n if (skipped.length > 0) {\n console.log(pc.dim(` Skipped ${skipped.length} (use --force to overwrite)`));\n }\n if (errors.length > 0) {\n console.log(pc.yellow(` ${errors.length} error(s)`));\n }\n console.log();\n\n return {\n success: errors.length === 0,\n generated,\n skipped,\n errors,\n };\n}\n\n/**\n * Generate proper defineFragment() TSX file content\n */\nfunction generateFragmentTsx(\n componentName: string,\n componentBaseName: string,\n extracted: ReturnType<typeof extractPropsFromFile> & { componentName: string },\n filePath: string,\n storyVariants: StoryVariant[]\n): string {\n const storyNames = storyVariants.map((v) => v.name);\n const whenToUse = inferUsageFromStories(storyNames);\n const description = generateDescription(componentName, extracted.props);\n const status = inferStatus(filePath);\n const accessibility = inferAccessibility(extracted.props);\n\n // Build props object for defineFragment\n const propsEntries = Object.entries(extracted.props || {});\n\n // Format when array\n const whenLines = whenToUse.length > 0\n ? whenToUse.map((w) => ` '${escapeQuotes(w)}',`).join(\"\\n\")\n : ` 'TODO: describe when to use ${componentName}',`;\n\n // Format props\n let propsBlock = \"{}\";\n if (propsEntries.length > 0) {\n const propLines = propsEntries.map(([name, info]) => {\n const propInfo = info as Record<string, unknown>;\n const rawType = propInfo.type ? String(propInfo.type) : \"\";\n const classified = classifyPropType(rawType);\n const desc = propInfo.description ? String(propInfo.description).replace(/\\n/g, \" \") : \"\";\n const required = propInfo.required ? \"true\" : \"false\";\n const parts = [` type: '${classified.type}'`];\n if (desc) parts.push(` description: '${escapeQuotes(desc)}'`);\n parts.push(` required: ${required}`);\n if (propInfo.default !== undefined) {\n parts.push(` default: ${JSON.stringify(propInfo.default)}`);\n }\n // Use classified values (from string literal unions) or existing values\n const values = classified.values || (propInfo.values && Array.isArray(propInfo.values) ? propInfo.values : null);\n if (values && values.length > 0) {\n parts.push(` values: ${JSON.stringify(values)}`);\n }\n return ` ${name}: {\\n${parts.join(\",\\n\")},\\n }`;\n });\n propsBlock = `{\\n${propLines.join(\",\\n\")},\\n }`;\n }\n\n // Build accessibility section\n let accessibilityBlock = \"\";\n if (accessibility.role || (accessibility.requirements && accessibility.requirements.length > 0)) {\n const parts: string[] = [];\n if (accessibility.role) {\n parts.push(` role: '${accessibility.role}'`);\n }\n if (accessibility.requirements && accessibility.requirements.length > 0) {\n const reqs = accessibility.requirements.map((r) => `'${escapeQuotes(r)}'`).join(\", \");\n parts.push(` requirements: [${reqs}]`);\n }\n accessibilityBlock = `\n\n accessibility: {\n${parts.join(\",\\n\")},\n },`;\n }\n\n // Build variants from story args or fallback to bare render\n const variants = buildVariants(componentName, storyVariants);\n\n // Use default import when the source component uses export default\n const componentImportStatement = extracted.isDefaultExport\n ? `import ${componentName} from './${componentBaseName}';`\n : `import { ${componentName} } from './${componentBaseName}';`;\n\n return `import React from 'react';\nimport { defineFragment } from '@fragments-sdk/cli/core';\n${componentImportStatement}\n\nexport default defineFragment({\n component: ${componentName},\n\n meta: {\n name: '${escapeQuotes(componentName)}',\n description: '${escapeQuotes(description)}',\n category: '${inferCategory(componentName, extracted.props)}',\n status: '${status}',\n },\n\n usage: {\n when: [\n${whenLines}\n ],\n whenNot: [],\n },\n\n props: ${propsBlock},${accessibilityBlock}\n\n variants: [\n${variants}\n ],\n});\n`;\n}\n\n/**\n * Escape single quotes in strings\n */\nfunction escapeQuotes(str: string): string {\n return str.replace(/'/g, \"\\\\'\");\n}\n\n/**\n * Classify a raw TypeScript type string into a valid fragment prop type enum value.\n * Returns the classified type and optionally extracted enum values.\n */\nfunction classifyPropType(rawType: string): { type: string; values?: string[] } {\n const t = rawType.replace(/\\s+/g, \" \").trim();\n const lower = t.toLowerCase();\n\n // Direct primitive matches\n if (lower === \"string\") return { type: \"string\" };\n if (lower === \"number\") return { type: \"number\" };\n if (lower === \"boolean\" || lower === \"bool\") return { type: \"boolean\" };\n\n // String/number literal unions → enum with values\n // e.g., '\"primary\" | \"secondary\"' or \"'default' | 'modal'\"\n if (/^[\"'][^\"']*[\"'](\\s*\\|\\s*[\"'][^\"']*[\"'])*$/.test(t)) {\n const values = [...t.matchAll(/[\"']([^\"']+)[\"']/g)].map((m) => m[1]);\n if (values.length > 0) return { type: \"enum\", values };\n }\n\n // Function types — arrow functions, callbacks, handlers, dispatchers\n if (\n lower.includes(\"=>\") ||\n lower.includes(\"handler\") ||\n lower.includes(\"callback\") ||\n lower.includes(\"dispatch\") ||\n lower.includes(\"listener\") ||\n /^\\(/.test(t)\n ) {\n return { type: \"function\" };\n }\n\n // React node types\n if (\n lower.includes(\"reactnode\") ||\n lower.includes(\"react.reactnode\") ||\n lower === \"node\"\n ) {\n return { type: \"node\" };\n }\n\n // React element types\n if (\n lower.includes(\"reactelement\") ||\n lower.includes(\"react.reactelement\") ||\n lower.includes(\"jsx.element\")\n ) {\n return { type: \"element\" };\n }\n\n // Array types\n if (lower.includes(\"[]\") || lower.startsWith(\"array<\") || lower.startsWith(\"readonly \")) {\n return { type: \"array\" };\n }\n\n // Object types (must come after array check since objects can contain [])\n if (lower.startsWith(\"{\") || lower.includes(\"record<\") || lower === \"object\") {\n return { type: \"object\" };\n }\n\n // Union types (mixed types with |)\n if (lower.includes(\"|\")) {\n // Check if it's a union of just string literals (enum)\n const parts = t.split(\"|\").map((p) => p.trim());\n const allStringLiterals = parts.every((p) => /^[\"'].*[\"']$/.test(p));\n if (allStringLiterals) {\n const values = parts.map((p) => p.replace(/^[\"']|[\"']$/g, \"\"));\n return { type: \"enum\", values };\n }\n return { type: \"union\" };\n }\n\n // Anything else\n return { type: \"custom\" };\n}\n\n/**\n * Infer category from component name and props\n */\nfunction inferCategory(componentName: string, props: Record<string, unknown>): string {\n const lower = componentName.toLowerCase();\n\n const categoryPatterns: Record<string, string[]> = {\n \"Actions\": [\"button\", \"action\", \"cta\", \"fab\", \"floatingaction\"],\n \"Forms\": [\"form\", \"input\", \"select\", \"checkbox\", \"radio\", \"textarea\", \"field\", \"textfield\", \"datepicker\", \"switch\", \"slider\", \"segmented\"],\n \"Layout\": [\"layout\", \"container\", \"grid\", \"flex\", \"stack\", \"box\", \"divider\", \"spacer\", \"sidebar\"],\n \"Navigation\": [\"nav\", \"menu\", \"breadcrumb\", \"tab\", \"link\", \"pagination\", \"stepper\", \"topbar\"],\n \"Feedback\": [\"alert\", \"toast\", \"notification\", \"message\", \"badge\", \"indicator\", \"progress\", \"spinner\", \"loading\", \"loader\", \"lozenge\", \"chip\"],\n \"Data Display\": [\"table\", \"list\", \"card\", \"avatar\", \"stat\", \"timeline\", \"tree\", \"datalist\", \"datacard\"],\n \"Overlays\": [\"modal\", \"dialog\", \"drawer\", \"popover\", \"tooltip\", \"dropdown\", \"slidepanel\"],\n \"Typography\": [\"text\", \"heading\", \"title\", \"label\", \"paragraph\"],\n \"Media\": [\"image\", \"video\", \"icon\", \"carousel\"],\n };\n\n for (const [category, patterns] of Object.entries(categoryPatterns)) {\n for (const pattern of patterns) {\n if (lower.includes(pattern)) {\n return category;\n }\n }\n }\n\n if (\"onClick\" in props || \"onPress\" in props) return \"Actions\";\n if (\"value\" in props || \"defaultValue\" in props) return \"Forms\";\n if (\"children\" in props) return \"Layout\";\n\n return \"Components\";\n}\n\n/**\n * Extract component name from story file\n */\nfunction extractComponentNameFromStory(content: string, filePath: string): string | null {\n const titleMatch = content.match(/title:\\s*['\"](?:[^'\"]+\\/)?([^'\"]+)['\"]/);\n if (titleMatch) {\n return titleMatch[1];\n }\n\n const fileName = basename(filePath);\n const componentName = fileName.replace(/\\.stories\\.(tsx?|jsx?)$/, \"\");\n if (/^[A-Z]/.test(componentName)) {\n return componentName;\n }\n\n return null;\n}\n\ninterface StoryVariant {\n name: string;\n args: Record<string, unknown>;\n}\n\n/**\n * Extract story names and their args from story file content (CSF3 format).\n * Parses patterns like:\n * export const Primary: Story = { args: { variant: 'primary', children: 'Click me' } }\n */\nfunction extractStoryVariants(content: string): StoryVariant[] {\n const variants: StoryVariant[] = [];\n\n const exportMatches = content.matchAll(\n /export\\s+const\\s+([A-Z][a-zA-Z0-9]*)\\s*[=:]/g\n );\n\n for (const match of exportMatches) {\n const name = match[1];\n if (name === \"default\" || name.endsWith(\"Args\") || name.endsWith(\"Meta\")) {\n continue;\n }\n\n const args = extractStoryArgs(content, name);\n variants.push({ name, args });\n }\n\n return variants;\n}\n\n/**\n * Extract the `args` object from a named story export.\n * Uses balanced-brace matching to handle nested objects.\n */\nfunction extractStoryArgs(content: string, storyName: string): Record<string, unknown> {\n // Find the story export and look for an args block\n const storyPattern = new RegExp(\n `export\\\\s+const\\\\s+${storyName}[^=]*=\\\\s*\\\\{([\\\\s\\\\S]*?)\\\\n\\\\};`,\n );\n const storyMatch = content.match(storyPattern);\n if (!storyMatch) return {};\n\n const storyBody = storyMatch[1];\n\n // Find the args block within the story body\n const argsStart = storyBody.indexOf('args:');\n if (argsStart === -1) return {};\n\n // Find the opening brace after \"args:\"\n const braceStart = storyBody.indexOf('{', argsStart);\n if (braceStart === -1) return {};\n\n // Balanced brace matching to find the full args object\n let depth = 0;\n let braceEnd = -1;\n for (let i = braceStart; i < storyBody.length; i++) {\n if (storyBody[i] === '{') depth++;\n else if (storyBody[i] === '}') {\n depth--;\n if (depth === 0) {\n braceEnd = i;\n break;\n }\n }\n }\n if (braceEnd === -1) return {};\n\n const argsBlock = storyBody.slice(braceStart + 1, braceEnd).trim();\n return parseArgsBlock(argsBlock);\n}\n\n/**\n * Parse a simplified args block into key-value pairs.\n * Handles string literals, numbers, booleans, and simple expressions.\n */\nfunction parseArgsBlock(argsBlock: string): Record<string, unknown> {\n const args: Record<string, unknown> = {};\n\n // Match key: value pairs (handles string, number, boolean values)\n const pairPattern = /(\\w+)\\s*:\\s*(?:['\"]([^'\"]*?)['\"]|(true|false)|(\\d+(?:\\.\\d+)?))/g;\n let pairMatch: RegExpExecArray | null;\n\n while ((pairMatch = pairPattern.exec(argsBlock)) !== null) {\n const key = pairMatch[1];\n if (pairMatch[2] !== undefined) {\n // String value\n args[key] = pairMatch[2];\n } else if (pairMatch[3] !== undefined) {\n // Boolean\n args[key] = pairMatch[3] === 'true';\n } else if (pairMatch[4] !== undefined) {\n // Number\n args[key] = Number(pairMatch[4]);\n }\n }\n\n return args;\n}\n\n/**\n * Build variant entries from story args.\n * If stories have args, generates JSX with those props.\n * Falls back to a bare `<Component />` for the Default variant.\n */\nfunction buildVariants(componentName: string, storyVariants: StoryVariant[]): string {\n // Filter to variants with args, plus always include a Default\n const hasDefault = storyVariants.some((v) => v.name === 'Default');\n const entries: string[] = [];\n\n if (!hasDefault) {\n entries.push(formatVariantEntry(componentName, 'Default', `Default ${componentName}`, {}));\n }\n\n for (const variant of storyVariants) {\n const description = variant.name\n .replace(/([A-Z])/g, ' $1')\n .trim();\n entries.push(formatVariantEntry(componentName, variant.name, `${description} ${componentName}`, variant.args));\n }\n\n // Deduplicate: if stories provided a Default, don't double-add\n return entries.join('\\n');\n}\n\n/**\n * Format a single variant entry with JSX code and render function.\n * Handles `children` as JSX children, all other args as props.\n */\nfunction formatVariantEntry(\n componentName: string,\n name: string,\n description: string,\n args: Record<string, unknown>\n): string {\n const jsxCode = buildJsxString(componentName, args);\n return ` {\n name: '${escapeQuotes(name)}',\n description: '${escapeQuotes(description)}',\n code: \\`${jsxCode}\\`,\n render: () => ${jsxCode},\n },`;\n}\n\n/**\n * Build a JSX string from component name and args.\n * `children` string args become JSX children, others become props.\n */\nfunction buildJsxString(componentName: string, args: Record<string, unknown>): string {\n const { children, ...restArgs } = args;\n const propParts: string[] = [];\n\n for (const [key, value] of Object.entries(restArgs)) {\n if (typeof value === 'string') {\n propParts.push(`${key}=\"${escapeQuotes(value)}\"`);\n } else if (typeof value === 'boolean') {\n propParts.push(value ? key : `${key}={false}`);\n } else if (typeof value === 'number') {\n propParts.push(`${key}={${value}}`);\n }\n }\n\n const propsStr = propParts.length > 0 ? ' ' + propParts.join(' ') : '';\n\n if (typeof children === 'string') {\n return `<${componentName}${propsStr}>${children}</${componentName}>`;\n }\n\n return `<${componentName}${propsStr} />`;\n}\n\n/**\n * Infer usage scenarios from story names\n */\nfunction inferUsageFromStories(storyNames: string[]): string[] {\n const usage: string[] = [];\n\n for (const name of storyNames) {\n const sentence = name\n .replace(/([A-Z])/g, \" $1\")\n .trim()\n .toLowerCase();\n\n if (\n [\"default\", \"primary\", \"basic\", \"example\", \"playground\"].includes(\n sentence.toLowerCase()\n )\n ) {\n continue;\n }\n\n if (sentence.includes(\"loading\")) {\n usage.push(\"Showing loading states\");\n } else if (sentence.includes(\"disabled\")) {\n usage.push(\"Preventing user interaction\");\n } else if (sentence.includes(\"error\")) {\n usage.push(\"Displaying error states\");\n } else if (sentence.includes(\"success\")) {\n usage.push(\"Showing success feedback\");\n } else if (sentence.includes(\"empty\")) {\n usage.push(\"Handling empty states\");\n } else if (sentence.includes(\"with\")) {\n const withPart = sentence.replace(\"with \", \"\");\n usage.push(`Displaying with ${withPart}`);\n }\n }\n\n return usage;\n}\n\n/**\n * Generate description from component name and props\n */\nfunction generateDescription(\n componentName: string,\n props: Record<string, unknown>\n): string {\n const words = componentName\n .replace(/([A-Z])/g, \" $1\")\n .trim()\n .toLowerCase();\n\n const hasOnClick = \"onClick\" in props || \"onPress\" in props;\n const hasValue = \"value\" in props || \"defaultValue\" in props;\n const hasChildren = \"children\" in props;\n\n if (hasOnClick && !hasValue) {\n return `Interactive ${words} element for triggering actions`;\n }\n\n if (hasValue) {\n return `Form ${words} for user input`;\n }\n\n if (hasChildren) {\n return `Container ${words} for grouping content`;\n }\n\n return `${words.charAt(0).toUpperCase() + words.slice(1)} component`;\n}\n\n/**\n * Infer accessibility from props\n */\nfunction inferAccessibility(props: Record<string, unknown>): {\n role?: string;\n requirements?: string[];\n} {\n const accessibility: { role?: string; requirements?: string[] } = {};\n\n const hasOnClick = \"onClick\" in props || \"onPress\" in props;\n const hasAriaLabel = \"ariaLabel\" in props || \"aria-label\" in props;\n const hasDisabled = \"disabled\" in props;\n const hasHref = \"href\" in props;\n\n if (hasOnClick && !hasHref) {\n accessibility.role = \"button\";\n } else if (hasHref) {\n accessibility.role = \"link\";\n }\n\n const requirements: string[] = [];\n\n if (hasOnClick && !hasAriaLabel) {\n requirements.push(\"Should have visible text or aria-label\");\n }\n\n if (hasDisabled) {\n requirements.push(\"Disabled state should be conveyed to assistive technology\");\n }\n\n if (requirements.length > 0) {\n accessibility.requirements = requirements;\n }\n\n return accessibility;\n}\n\n/**\n * Infer status from file path\n */\nfunction inferStatus(\n filePath: string\n): \"draft\" | \"experimental\" | \"beta\" | \"stable\" | \"deprecated\" {\n const lowerPath = filePath.toLowerCase();\n\n if (lowerPath.includes(\"/experimental/\") || lowerPath.includes(\"/labs/\")) {\n return \"experimental\";\n }\n if (lowerPath.includes(\"/beta/\")) {\n return \"beta\";\n }\n if (lowerPath.includes(\"/deprecated/\") || lowerPath.includes(\"/legacy/\")) {\n return \"deprecated\";\n }\n if (lowerPath.includes(\"/draft/\") || lowerPath.includes(\"/wip/\")) {\n return \"draft\";\n }\n\n return \"stable\";\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,7 +3,6 @@ import "./chunk-D2CDBRNU.js";
|
|
|
3
3
|
import {
|
|
4
4
|
BRAND
|
|
5
5
|
} from "./chunk-32LIWN2P.js";
|
|
6
|
-
import "./chunk-Z7EY4VHE.js";
|
|
7
6
|
|
|
8
7
|
// src/commands/govern-scan.ts
|
|
9
8
|
import pc from "picocolors";
|
|
@@ -43,10 +42,11 @@ async function governScan(options = {}) {
|
|
|
43
42
|
createEngine,
|
|
44
43
|
buildAdaptersFromConfig,
|
|
45
44
|
createCloudAdapter,
|
|
46
|
-
formatVerdict
|
|
45
|
+
formatVerdict,
|
|
46
|
+
computeComponentHealth
|
|
47
47
|
} = await import("@fragments-sdk/govern");
|
|
48
|
-
const { scanCodebase } = await import("./codebase-scanner-
|
|
49
|
-
const { usagesToSpec } = await import("./converter-
|
|
48
|
+
const { scanCodebase } = await import("./codebase-scanner-MQHUZC2G.js");
|
|
49
|
+
const { usagesToSpec } = await import("./converter-7XM3Y6NJ.js");
|
|
50
50
|
const format = options.format ?? "summary";
|
|
51
51
|
const quiet = options.quiet ?? false;
|
|
52
52
|
if (!quiet) {
|
|
@@ -67,15 +67,52 @@ ${BRAND.name} Governance Scan
|
|
|
67
67
|
console.log(pc.dim(" No config found \u2014 using scan defaults (safety + tokens)\n"));
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
+
let codeTokens;
|
|
71
|
+
if (process.env.FRAGMENTS_API_KEY) {
|
|
72
|
+
codeTokens = await extractCodeTokens(rootDir, options.config, quiet);
|
|
73
|
+
}
|
|
74
|
+
let contractRegistry;
|
|
75
|
+
let registryMap;
|
|
76
|
+
{
|
|
77
|
+
const { readFileSync, existsSync: existsSync2 } = await import("fs");
|
|
78
|
+
const fragmentsJsonPath = resolve(rootDir, "fragments.json");
|
|
79
|
+
if (existsSync2(fragmentsJsonPath)) {
|
|
80
|
+
try {
|
|
81
|
+
const raw = readFileSync(fragmentsJsonPath, "utf-8");
|
|
82
|
+
const parsed = JSON.parse(raw);
|
|
83
|
+
if (parsed.fragments && Array.isArray(parsed.fragments)) {
|
|
84
|
+
const map = {};
|
|
85
|
+
for (const f of parsed.fragments) {
|
|
86
|
+
if (f.meta?.name) {
|
|
87
|
+
map[f.meta.name] = f;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
registryMap = map;
|
|
91
|
+
if (process.env.FRAGMENTS_API_KEY) {
|
|
92
|
+
contractRegistry = JSON.stringify({ fragments: parsed.fragments });
|
|
93
|
+
}
|
|
94
|
+
if (!quiet) {
|
|
95
|
+
console.log(pc.dim(` Contract registry loaded (${parsed.fragments.length} components)
|
|
96
|
+
`));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
70
103
|
const adapters = buildAdaptersFromConfig(policy.audit);
|
|
71
104
|
const hasCloudAdapter = adapters.length > 0 && policy.audit?.cloud;
|
|
72
105
|
if (!hasCloudAdapter && process.env.FRAGMENTS_API_KEY) {
|
|
73
|
-
adapters.push(createCloudAdapter());
|
|
106
|
+
adapters.push(createCloudAdapter({ codeTokens, contractRegistry }));
|
|
74
107
|
if (!quiet) {
|
|
75
108
|
console.log(pc.dim(" Cloud audit enabled (FRAGMENTS_API_KEY detected)\n"));
|
|
76
109
|
}
|
|
77
110
|
}
|
|
78
|
-
const engine = createEngine(
|
|
111
|
+
const engine = createEngine(
|
|
112
|
+
policy,
|
|
113
|
+
adapters,
|
|
114
|
+
registryMap ? { registry: { fragments: registryMap } } : void 0
|
|
115
|
+
);
|
|
79
116
|
if (!quiet) {
|
|
80
117
|
console.log(pc.dim(" Scanning files...\n"));
|
|
81
118
|
}
|
|
@@ -112,6 +149,8 @@ ${BRAND.name} Governance Scan
|
|
|
112
149
|
let passedFiles = 0;
|
|
113
150
|
let totalViolations = 0;
|
|
114
151
|
const violationCounts = /* @__PURE__ */ new Map();
|
|
152
|
+
const allVerdicts = [];
|
|
153
|
+
const usageSnapshot = [];
|
|
115
154
|
for (const [filePath, usages] of grouped) {
|
|
116
155
|
const spec = usagesToSpec(usages, filePath, rootDir);
|
|
117
156
|
const relPath = relative(rootDir, filePath);
|
|
@@ -119,6 +158,18 @@ ${BRAND.name} Governance Scan
|
|
|
119
158
|
runner: "cli",
|
|
120
159
|
input: relPath
|
|
121
160
|
});
|
|
161
|
+
allVerdicts.push(verdict);
|
|
162
|
+
usageSnapshot.push({
|
|
163
|
+
file: relPath,
|
|
164
|
+
components: usages.map((u) => ({
|
|
165
|
+
name: u.componentName,
|
|
166
|
+
line: u.line,
|
|
167
|
+
props: {
|
|
168
|
+
static: u.props.static,
|
|
169
|
+
dynamic: u.props.dynamic
|
|
170
|
+
}
|
|
171
|
+
}))
|
|
172
|
+
});
|
|
122
173
|
totalFiles++;
|
|
123
174
|
if (verdict.passed) {
|
|
124
175
|
passedFiles++;
|
|
@@ -150,6 +201,7 @@ ${BRAND.name} Governance Scan
|
|
|
150
201
|
console.log(output);
|
|
151
202
|
}
|
|
152
203
|
}
|
|
204
|
+
const health = computeComponentHealth(allVerdicts, registryMap ?? {});
|
|
153
205
|
if (!quiet && format === "summary") {
|
|
154
206
|
console.log(pc.dim("\n \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
|
|
155
207
|
console.log(` Files checked: ${totalFiles}`);
|
|
@@ -162,6 +214,12 @@ ${BRAND.name} Governance Scan
|
|
|
162
214
|
console.log(pc.dim(` ${count}\xD7 `) + pc.yellow(rule));
|
|
163
215
|
}
|
|
164
216
|
}
|
|
217
|
+
console.log(pc.dim("\n Component Health:"));
|
|
218
|
+
console.log(` Contract coverage: ${health.contractCoverage}% (${health.contractedComponents}/${health.totalComponents})`);
|
|
219
|
+
console.log(` Compliance rate: ${health.overallCompliance}%`);
|
|
220
|
+
if (health.uncontracted.length > 0) {
|
|
221
|
+
console.log(pc.dim(` Uncontracted: ${health.uncontracted.slice(0, 5).join(", ")}${health.uncontracted.length > 5 ? ` (+${health.uncontracted.length - 5} more)` : ""}`));
|
|
222
|
+
}
|
|
165
223
|
console.log();
|
|
166
224
|
if (passedFiles === totalFiles) {
|
|
167
225
|
console.log(pc.green(` \u2713 All files passed governance checks
|
|
@@ -173,8 +231,83 @@ ${BRAND.name} Governance Scan
|
|
|
173
231
|
);
|
|
174
232
|
}
|
|
175
233
|
}
|
|
234
|
+
if (process.env.FRAGMENTS_API_KEY && usageSnapshot.length > 0) {
|
|
235
|
+
try {
|
|
236
|
+
const apiKey = process.env.FRAGMENTS_API_KEY;
|
|
237
|
+
const url = process.env.FRAGMENTS_URL ?? "https://app.usefragments.com";
|
|
238
|
+
await fetch(`${url}/api/ingest`, {
|
|
239
|
+
method: "POST",
|
|
240
|
+
headers: {
|
|
241
|
+
"Content-Type": "application/json",
|
|
242
|
+
Authorization: `Bearer ${apiKey}`
|
|
243
|
+
},
|
|
244
|
+
body: JSON.stringify({
|
|
245
|
+
componentUsage: JSON.stringify(usageSnapshot),
|
|
246
|
+
componentHealth: JSON.stringify(health)
|
|
247
|
+
})
|
|
248
|
+
});
|
|
249
|
+
} catch {
|
|
250
|
+
}
|
|
251
|
+
}
|
|
176
252
|
return { exitCode: passedFiles === totalFiles ? 0 : 1 };
|
|
177
253
|
}
|
|
254
|
+
async function extractCodeTokens(rootDir, configPath, quiet) {
|
|
255
|
+
try {
|
|
256
|
+
try {
|
|
257
|
+
const { loadConfig } = await import("./node-37AUE74M.js");
|
|
258
|
+
const { parseTokenFiles } = await import("./service-QJGWUIVL.js");
|
|
259
|
+
const { config, configDir } = await loadConfig(configPath);
|
|
260
|
+
if (config.tokens?.include?.length) {
|
|
261
|
+
const result = await parseTokenFiles(config.tokens, configDir);
|
|
262
|
+
if (result.tokens.length > 0) {
|
|
263
|
+
const flat = {};
|
|
264
|
+
for (const token of result.tokens) {
|
|
265
|
+
flat[token.name] = token.resolvedValue;
|
|
266
|
+
}
|
|
267
|
+
if (!quiet) {
|
|
268
|
+
console.log(
|
|
269
|
+
pc.dim(` Extracted ${result.tokens.length} code tokens from config
|
|
270
|
+
`)
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
return JSON.stringify(flat);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
} catch {
|
|
277
|
+
}
|
|
278
|
+
const {
|
|
279
|
+
findTailwindConfig,
|
|
280
|
+
loadTailwindConfig
|
|
281
|
+
} = await import("./token-normalizer-TEPOVBPV.js");
|
|
282
|
+
const tailwindPath = findTailwindConfig(rootDir);
|
|
283
|
+
if (tailwindPath) {
|
|
284
|
+
const tokens = await loadTailwindConfig(tailwindPath);
|
|
285
|
+
if (tokens.length > 0) {
|
|
286
|
+
const flat = {};
|
|
287
|
+
for (const token of tokens) {
|
|
288
|
+
flat[token.name] = token.value;
|
|
289
|
+
}
|
|
290
|
+
if (!quiet) {
|
|
291
|
+
console.log(
|
|
292
|
+
pc.dim(` Extracted ${tokens.length} tokens from Tailwind config
|
|
293
|
+
`)
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
return JSON.stringify(flat);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
} catch (error) {
|
|
300
|
+
if (!quiet) {
|
|
301
|
+
console.log(
|
|
302
|
+
pc.dim(
|
|
303
|
+
` Token extraction skipped: ${error instanceof Error ? error.message : "unknown error"}
|
|
304
|
+
`
|
|
305
|
+
)
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return void 0;
|
|
310
|
+
}
|
|
178
311
|
async function governWatch(options = {}) {
|
|
179
312
|
const {
|
|
180
313
|
loadPolicy,
|
|
@@ -183,8 +316,8 @@ async function governWatch(options = {}) {
|
|
|
183
316
|
createCloudAdapter,
|
|
184
317
|
formatVerdict
|
|
185
318
|
} = await import("@fragments-sdk/govern");
|
|
186
|
-
const { scanFile } = await import("./scanner-
|
|
187
|
-
const { usagesToSpec } = await import("./converter-
|
|
319
|
+
const { scanFile } = await import("./scanner-4KZNOXAK.js");
|
|
320
|
+
const { usagesToSpec } = await import("./converter-7XM3Y6NJ.js");
|
|
188
321
|
const quiet = options.quiet ?? false;
|
|
189
322
|
const debounceMs = options.debounce ?? 300;
|
|
190
323
|
const format = options.format ?? "summary";
|
|
@@ -277,4 +410,4 @@ export {
|
|
|
277
410
|
governScan,
|
|
278
411
|
governWatch
|
|
279
412
|
};
|
|
280
|
-
//# sourceMappingURL=govern-scan-
|
|
413
|
+
//# sourceMappingURL=govern-scan-OYFZYOQW.js.map
|