@effinrich/forgekit-storybook-plugin 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,7 +21,7 @@ var CHAKRA_IMPORTS = ["@chakra-ui/react", "@redesignhealth/ui"];
21
21
  var ROUTER_IMPORTS = ["react-router-dom", "react-router"];
22
22
  var QUERY_IMPORTS = ["@tanstack/react-query", "react-query"];
23
23
  var DEFAULT_DEBOUNCE_MS = 300;
24
- var STORYBOOK_TEST_IMPORT = "@storybook/test";
24
+ var STORYBOOK_TEST_IMPORT = "storybook/test";
25
25
  var STORYBOOK_META_IMPORT = "@storybook/react-vite";
26
26
 
27
27
  // src/core/analyze-component.ts
@@ -410,4 +410,4 @@ function resolveComponentPath(componentPath) {
410
410
 
411
411
 
412
412
  exports.STORY_FILE_SUFFIX = STORY_FILE_SUFFIX; exports.COMPONENT_EXTENSIONS = COMPONENT_EXTENSIONS; exports.IGNORED_DIRS = IGNORED_DIRS; exports.DEFAULT_DEBOUNCE_MS = DEFAULT_DEBOUNCE_MS; exports.STORYBOOK_TEST_IMPORT = STORYBOOK_TEST_IMPORT; exports.STORYBOOK_META_IMPORT = STORYBOOK_META_IMPORT; exports.analyzeComponent = analyzeComponent; exports.generatePlaywrightTest = generatePlaywrightTest; exports.forgeTest = forgeTest;
413
- //# sourceMappingURL=chunk-WUKJNZOF.js.map
413
+ //# sourceMappingURL=chunk-KDZBMNMU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/richtillman/Documents/GitHub/forgekit-storybook-plugin-1/dist/chunk-KDZBMNMU.js","../src/api/forge-test.ts","../src/utils/constants.ts","../src/core/analyze-component.ts","../src/core/generate-playwright-test.ts"],"names":[],"mappings":"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","file":"/Users/richtillman/Documents/GitHub/forgekit-storybook-plugin-1/dist/chunk-KDZBMNMU.js","sourcesContent":[null,"import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { COMPONENT_EXTENSIONS, CT_FILE_SUFFIX, STORY_FILE_SUFFIX } from '../utils/constants';\nimport type { ForgeTestOptions, ComponentAnalysis } from '../utils/types';\nimport { analyzeComponent } from '../core/analyze-component';\nimport { generatePlaywrightTest } from '../core/generate-playwright-test';\n\nexport interface ForgeTestResult {\n testPath: string;\n content: string;\n analysis: ComponentAnalysis;\n written: boolean;\n}\n\n/**\n * High-level: analyze a component, generate a Playwright component test, and write it.\n */\nexport async function forgeTest(options: ForgeTestOptions): Promise<ForgeTestResult> {\n const { componentPath, overwrite = false, dryRun = false } = options;\n\n const resolvedPath = resolveComponentPath(componentPath);\n if (!resolvedPath) {\n throw new Error(`Component file not found: ${componentPath}`);\n }\n\n const testPath = resolvedPath.replace(/\\.tsx?$/, CT_FILE_SUFFIX);\n if (fs.existsSync(testPath) && !overwrite) {\n throw new Error(`Test already exists: ${testPath}. Use --overwrite to replace.`);\n }\n\n const analysis = analyzeComponent(resolvedPath);\n if (!analysis) {\n throw new Error(`Could not analyze component at ${resolvedPath}. Ensure it exports a React component.`);\n }\n\n const storyPath = resolvedPath.replace(/\\.tsx?$/, STORY_FILE_SUFFIX);\n const hasStories = fs.existsSync(storyPath);\n const importPath = `./${analysis.fileName}`;\n const storyImportPath = hasStories\n ? `./${path.basename(storyPath, '.tsx')}`\n : undefined;\n\n const content = generatePlaywrightTest({\n analysis,\n importPath,\n hasStories,\n storyImportPath,\n });\n\n let written = false;\n if (!dryRun) {\n const dir = path.dirname(testPath);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(testPath, content, 'utf-8');\n written = true;\n }\n\n return { testPath, content, analysis, written };\n}\n\nfunction resolveComponentPath(componentPath: string): string | null {\n const resolved = path.resolve(componentPath);\n\n if (fs.existsSync(resolved)) return resolved;\n\n for (const ext of COMPONENT_EXTENSIONS) {\n const withExt = resolved + ext;\n if (fs.existsSync(withExt)) return withExt;\n }\n\n return null;\n}\n","export const STORY_FILE_SUFFIX = '.stories.tsx'\nexport const CT_FILE_SUFFIX = '.ct.tsx'\nexport const COMPONENT_EXTENSIONS = ['.tsx', '.ts']\n\nexport const IGNORED_FILES = [\n '*.spec.*',\n '*.test.*',\n '*.stories.*',\n '*.styles.*',\n '*.style.*',\n 'index.ts',\n 'index.tsx'\n]\n\nexport const IGNORED_DIRS = [\n 'node_modules',\n 'dist',\n 'build',\n '.storybook',\n '.next',\n '.vite',\n 'coverage',\n '__tests__',\n '__mocks__'\n]\n\nexport const CHAKRA_IMPORTS = ['@chakra-ui/react', '@redesignhealth/ui']\nexport const ROUTER_IMPORTS = ['react-router-dom', 'react-router']\nexport const QUERY_IMPORTS = ['@tanstack/react-query', 'react-query']\nexport const ZUSTAND_IMPORTS = ['zustand']\nexport const FORMIK_IMPORTS = ['formik']\nexport const RHF_IMPORTS = ['react-hook-form']\n\nexport const DEFAULT_DEBOUNCE_MS = 300\n\nexport const STORYBOOK_TEST_IMPORT = 'storybook/test'\nexport const STORYBOOK_META_IMPORT = '@storybook/react-vite'\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport {\n CHAKRA_IMPORTS,\n QUERY_IMPORTS,\n ROUTER_IMPORTS,\n} from '../utils/constants';\nimport type {\n ComponentAnalysis,\n ImportInfo,\n PropInfo,\n} from '../utils/types';\n\n/**\n * Analyze a React component file to extract metadata for story generation.\n *\n * @param filePath - Absolute or relative path to the component file\n * @param content - Optional file content (reads from disk if omitted)\n */\nexport function analyzeComponent(\n filePath: string,\n content?: string,\n): ComponentAnalysis | null {\n if (!content) {\n try {\n content = fs.readFileSync(filePath, 'utf-8');\n } catch {\n return null;\n }\n }\n\n if (!content.trim()) return null;\n\n const name = extractComponentName(content, filePath);\n if (!name) return null;\n\n const imports = extractImports(content);\n const props = extractProps(content, name);\n\n return {\n name,\n fileName: path.basename(filePath).replace(/\\.tsx?$/, ''),\n filePath,\n props,\n hasChildren: props.some((p) => p.name === 'children'),\n imports,\n usesRouter: imports.some((i) =>\n ROUTER_IMPORTS.some((r) => i.source.includes(r))\n ),\n usesReactQuery: imports.some((i) =>\n QUERY_IMPORTS.some((r) => i.source.includes(r))\n ),\n usesChakra: imports.some((i) =>\n CHAKRA_IMPORTS.some((r) => i.source.includes(r))\n ),\n exportType: detectExportType(content, name),\n };\n}\n\nfunction extractComponentName(\n content: string,\n filePath: string,\n): string | null {\n // Match: export const ComponentName = ... or export function ComponentName(\n const namedExport = content.match(\n /export\\s+(?:const|function)\\s+([A-Z][A-Za-z0-9]*)/\n );\n if (namedExport) return namedExport[1];\n\n // Match: export default function ComponentName\n const defaultFn = content.match(\n /export\\s+default\\s+function\\s+([A-Z][A-Za-z0-9]*)/\n );\n if (defaultFn) return defaultFn[1];\n\n // Match: const ComponentName = ... followed by export default ComponentName\n const constThenDefault = content.match(\n /(?:const|function)\\s+([A-Z][A-Za-z0-9]*)\\s*=[\\s\\S]*?export\\s+default\\s+\\1/\n );\n if (constThenDefault) return constThenDefault[1];\n\n // Match: React.forwardRef / React.memo wrapping\n const forwardRef = content.match(\n /export\\s+(?:const|default)\\s+(?:const\\s+)?([A-Z][A-Za-z0-9]*)\\s*=\\s*(?:React\\.)?(?:forwardRef|memo)\\s*[<(]/\n );\n if (forwardRef) return forwardRef[1];\n\n // Fallback: derive from file name\n const fileName = filePath.split('/').pop()?.replace(/\\.tsx?$/, '');\n if (fileName && /[a-zA-Z]/.test(fileName)) {\n return fileName\n .split('-')\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n }\n\n return null;\n}\n\nfunction extractImports(content: string): ImportInfo[] {\n const imports: ImportInfo[] = [];\n const importRegex =\n /import\\s+(?:(?:(\\{[^}]+\\})|(\\w+))\\s+from\\s+)?['\"]([^'\"]+)['\"]/g;\n let match;\n\n while ((match = importRegex.exec(content)) !== null) {\n const namedImports = match[1];\n const defaultImport = match[2];\n const source = match[3];\n const specifiers: string[] = [];\n\n if (namedImports) {\n const names = namedImports\n .replace(/[{}]/g, '')\n .split(',')\n .map((s) => s.trim().split(/\\s+as\\s+/)[0].trim())\n .filter(Boolean);\n specifiers.push(...names);\n }\n if (defaultImport) {\n specifiers.push(defaultImport);\n }\n\n imports.push({ source, specifiers });\n }\n\n return imports;\n}\n\nfunction extractProps(content: string, componentName: string): PropInfo[] {\n const props: PropInfo[] = [];\n\n const propsTypeName = findPropsTypeName(content, componentName);\n if (!propsTypeName) return props;\n\n // Match interface or type body — handle intersection types with &\n const interfaceMatch = content.match(\n new RegExp(\n `(?:interface|type)\\\\s+${propsTypeName}\\\\s*(?:extends\\\\s+[^{]+)?\\\\s*(?:=\\\\s*)?(?:[^{]*&\\\\s*)?\\\\{([^}]*)\\\\}`,\n 's'\n )\n );\n if (!interfaceMatch) return props;\n\n const body = interfaceMatch[1];\n const propLines = body.split('\\n').filter((line) => {\n const trimmed = line.trim();\n return trimmed && !trimmed.startsWith('//') && !trimmed.startsWith('*');\n });\n\n for (const line of propLines) {\n const propMatch = line.match(\n /^\\s*(\\w+)(\\??):\\s*(.+?)(?:\\s*\\/\\/.*)?;?\\s*$/\n );\n if (!propMatch) continue;\n\n const [, name, optional, rawType] = propMatch;\n const type = rawType.trim().replace(/;$/, '');\n const isCallback =\n type.includes('=>') ||\n type.startsWith('(') ||\n /^on[A-Z]/.test(name);\n\n const unionValues = extractUnionValues(type);\n\n props.push({\n name,\n type,\n required: optional !== '?',\n isCallback,\n unionValues: unionValues.length > 0 ? unionValues : undefined,\n });\n }\n\n return props;\n}\n\nfunction findPropsTypeName(\n content: string,\n componentName: string,\n): string | null {\n // Pattern: ComponentName = ({ ... }: PropsType)\n const destructuredProps = content.match(\n new RegExp(\n `(?:const|function)\\\\s+${componentName}\\\\s*(?:=\\\\s*)?\\\\([^)]*:\\\\s*([A-Z]\\\\w*)`\n )\n );\n if (destructuredProps) return destructuredProps[1];\n\n // Pattern: ComponentName(props: PropsType)\n const regularProps = content.match(\n new RegExp(\n `(?:const|function)\\\\s+${componentName}\\\\s*(?:=\\\\s*)?\\\\(\\\\s*(?:props|\\\\w+)\\\\s*:\\\\s*([A-Z]\\\\w*)`\n )\n );\n if (regularProps) return regularProps[1];\n\n // Pattern: React.FC<PropsType>\n const fcProps = content.match(\n new RegExp(`${componentName}[^=]*=\\\\s*(?:React\\\\.)?FC<([A-Z]\\\\w*)>`)\n );\n if (fcProps) return fcProps[1];\n\n // Fallback: look for interface named ComponentNameProps\n const defaultPropsName = `${componentName}Props`;\n if (content.includes(`interface ${defaultPropsName}`) || content.includes(`type ${defaultPropsName}`)) {\n return defaultPropsName;\n }\n\n return null;\n}\n\nfunction extractUnionValues(type: string): string[] {\n // Match single or double-quoted string literal unions: 'a' | 'b' or \"a\" | \"b\"\n const singleQuote = type.match(/^'[^']*'(?:\\s*\\|\\s*'[^']*')+$/);\n if (singleQuote) {\n return type\n .split('|')\n .map((v) => v.trim().replace(/'/g, ''))\n .filter(Boolean);\n }\n const doubleQuote = type.match(/^\"[^\"]*\"(?:\\s*\\|\\s*\"[^\"]*\")+$/);\n if (doubleQuote) {\n return type\n .split('|')\n .map((v) => v.trim().replace(/\"/g, ''))\n .filter(Boolean);\n }\n return [];\n}\n\nfunction detectExportType(\n content: string,\n componentName: string,\n): 'default' | 'named' | 'both' {\n const hasDefault =\n content.includes(`export default ${componentName}`) ||\n content.includes(`export default function ${componentName}`);\n const hasNamed =\n content.includes(`export const ${componentName}`) ||\n content.includes(`export function ${componentName}`);\n\n if (hasDefault && hasNamed) return 'both';\n if (hasDefault) return 'default';\n return 'named';\n}\n","import type { ComponentAnalysis, PropInfo, PlaywrightTestOptions } from '../utils/types';\n\n/**\n * Generate a co-located Playwright component test (.ct.tsx) that:\n * - Mounts the component using @playwright/experimental-ct-react\n * - Tests rendering, visual regression (screenshot), interactions, and a11y\n * - Optionally imports stories for story-driven testing\n */\nexport function generatePlaywrightTest(options: PlaywrightTestOptions): string {\n const { analysis, importPath, hasStories, storyImportPath } = options;\n const { name, props, exportType, hasChildren } = analysis;\n\n const lines: string[] = [];\n\n // Imports\n lines.push(`import { test, expect } from '@playwright/experimental-ct-react';`);\n lines.push(`import AxeBuilder from '@axe-core/playwright';`);\n\n if (exportType === 'default') {\n lines.push(`import ${name} from '${importPath}';`);\n } else {\n lines.push(`import { ${name} } from '${importPath}';`);\n }\n\n if (hasStories && storyImportPath) {\n lines.push(`import * as stories from '${storyImportPath}';`);\n }\n\n lines.push('');\n\n // Test suite\n lines.push(`test.describe('${name}', () => {`);\n\n // Mount & render test\n lines.push(` test('mounts and renders without crashing', async ({ mount }) => {`);\n lines.push(` const component = await mount(`);\n lines.push(` <${name}${buildMinimalProps(props, hasChildren, name)} />`);\n lines.push(` );`);\n lines.push(` await expect(component).toBeVisible();`);\n lines.push(` });`);\n lines.push('');\n\n // Screenshot / visual regression test\n lines.push(` test('matches visual snapshot', async ({ mount }) => {`);\n lines.push(` const component = await mount(`);\n lines.push(` <${name}${buildMinimalProps(props, hasChildren, name)} />`);\n lines.push(` );`);\n lines.push(` await expect(component).toHaveScreenshot('${toKebab(name)}-default.png');`);\n lines.push(` });`);\n lines.push('');\n\n // Interaction tests\n const clickHandler = props.find(\n (p) => p.isCallback && (p.name === 'onClick' || p.name === 'onPress')\n );\n if (clickHandler) {\n lines.push(` test('handles click interaction', async ({ mount }) => {`);\n lines.push(` let clicked = false;`);\n lines.push(` const component = await mount(`);\n lines.push(` <${name}`);\n lines.push(` ${clickHandler.name}={() => { clicked = true; }}`);\n lines.push(...buildRequiredPropsLines(props, hasChildren, name, ' '));\n lines.push(` />`);\n lines.push(` );`);\n lines.push(` await component.click();`);\n lines.push(` expect(clicked).toBe(true);`);\n lines.push(` });`);\n lines.push('');\n }\n\n const onChangeHandler = props.find(\n (p) => p.isCallback && (p.name === 'onChange' || p.name === 'onValueChange')\n );\n if (onChangeHandler) {\n lines.push(` test('handles value change', async ({ mount }) => {`);\n lines.push(` let changed = false;`);\n lines.push(` const component = await mount(`);\n lines.push(` <${name}`);\n lines.push(` ${onChangeHandler.name}={() => { changed = true; }}`);\n lines.push(...buildRequiredPropsLines(props, hasChildren, name, ' '));\n lines.push(` />`);\n lines.push(` );`);\n lines.push(` // Trigger a change event appropriate to the component type`);\n lines.push(` await component.locator('input, select, textarea').first().fill('test');`);\n lines.push(` expect(changed).toBe(true);`);\n lines.push(` });`);\n lines.push('');\n }\n\n // Disabled state\n const disabledProp = props.find(\n (p) => p.name === 'disabled' || p.name === 'isDisabled'\n );\n if (disabledProp) {\n lines.push(` test('renders disabled state correctly', async ({ mount }) => {`);\n lines.push(` const component = await mount(`);\n lines.push(` <${name}`);\n lines.push(` ${disabledProp.name}={true}`);\n lines.push(...buildRequiredPropsLines(props, hasChildren, name, ' '));\n lines.push(` />`);\n lines.push(` );`);\n lines.push(` await expect(component).toHaveScreenshot('${toKebab(name)}-disabled.png');`);\n if (clickHandler) {\n lines.push(` // Verify click handler is not triggered when disabled`);\n lines.push(` let clicked = false;`);\n lines.push(` const disabledComponent = await mount(`);\n lines.push(` <${name}`);\n lines.push(` ${disabledProp.name}={true}`);\n lines.push(` ${clickHandler.name}={() => { clicked = true; }}`);\n lines.push(...buildRequiredPropsLines(props, hasChildren, name, ' '));\n lines.push(` />`);\n lines.push(` );`);\n lines.push(` await disabledComponent.click({ force: true });`);\n lines.push(` expect(clicked).toBe(false);`);\n }\n lines.push(` });`);\n lines.push('');\n }\n\n // Variant visual regression\n const variantProps = props.filter(\n (p) =>\n (p.name === 'size' || p.name === 'variant' || p.name === 'colorPalette') &&\n p.unionValues &&\n p.unionValues.length > 0\n );\n for (const vp of variantProps) {\n lines.push(` test.describe('${vp.name} variants', () => {`);\n for (const val of vp.unionValues!) {\n lines.push(` test('${vp.name}=\"${val}\" matches snapshot', async ({ mount }) => {`);\n lines.push(` const component = await mount(`);\n lines.push(` <${name}`);\n lines.push(` ${vp.name}=\"${val}\"`);\n lines.push(...buildRequiredPropsLines(props, hasChildren, name, ' '));\n lines.push(` />`);\n lines.push(` );`);\n lines.push(` await expect(component).toHaveScreenshot('${toKebab(name)}-${vp.name}-${val}.png');`);\n lines.push(` });`);\n }\n lines.push(` });`);\n lines.push('');\n }\n\n // Accessibility test\n lines.push(` test('meets accessibility standards', async ({ mount, page }) => {`);\n lines.push(` await mount(`);\n lines.push(` <${name}${buildMinimalProps(props, hasChildren, name)} />`);\n lines.push(` );`);\n lines.push(` const a11yResults = await new AxeBuilder({ page }).analyze();`);\n lines.push(` expect(a11yResults.violations).toEqual([]);`);\n lines.push(` });`);\n\n // Story-driven tests\n if (hasStories && storyImportPath) {\n lines.push('');\n lines.push(` test.describe('story-driven tests', () => {`);\n lines.push(` test('renders Default story', async ({ mount }) => {`);\n lines.push(` const args = stories.Default?.args ?? {};`);\n lines.push(` const component = await mount(<${name} {...args} />);`);\n lines.push(` await expect(component).toBeVisible();`);\n lines.push(` });`);\n lines.push(` });`);\n }\n\n lines.push(`});`);\n lines.push('');\n\n return lines.join('\\n');\n}\n\nfunction buildMinimalProps(\n props: PropInfo[],\n hasChildren: boolean,\n componentName: string,\n): string {\n const required = props.filter(\n (p) => p.required && p.name !== 'children'\n );\n\n if (required.length === 0 && !hasChildren) return '';\n\n const propStrings: string[] = [];\n for (const p of required) {\n propStrings.push(`${p.name}={${inferTestValue(p)}}`);\n }\n\n if (hasChildren) {\n return ` ${propStrings.join(' ')}>${componentName} content</${componentName}`;\n }\n\n if (propStrings.length === 0) return '';\n return ` ${propStrings.join(' ')}`;\n}\n\nfunction buildRequiredPropsLines(\n props: PropInfo[],\n hasChildren: boolean,\n componentName: string,\n indent: string,\n): string[] {\n const lines: string[] = [];\n const required = props.filter(\n (p) => p.required && p.name !== 'children' && !p.isCallback\n );\n\n for (const p of required) {\n lines.push(`${indent}${p.name}={${inferTestValue(p)}}`);\n }\n\n if (hasChildren) {\n lines.push(`${indent}children=\"${componentName} content\"`);\n }\n\n return lines;\n}\n\nfunction inferTestValue(prop: PropInfo): string {\n if (prop.isCallback) return '() => {}';\n if (prop.unionValues && prop.unionValues.length > 0)\n return `\"${prop.unionValues[0]}\"`;\n\n const type = prop.type.toLowerCase();\n if (type === 'string') return `\"Test ${prop.name}\"`;\n if (type === 'number') return '42';\n if (type === 'boolean' || type === 'bool') return 'true';\n\n return `undefined as any`;\n}\n\nfunction toKebab(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')\n .toLowerCase();\n}\n"]}
@@ -21,7 +21,7 @@ var CHAKRA_IMPORTS = ["@chakra-ui/react", "@redesignhealth/ui"];
21
21
  var ROUTER_IMPORTS = ["react-router-dom", "react-router"];
22
22
  var QUERY_IMPORTS = ["@tanstack/react-query", "react-query"];
23
23
  var DEFAULT_DEBOUNCE_MS = 300;
24
- var STORYBOOK_TEST_IMPORT = "@storybook/test";
24
+ var STORYBOOK_TEST_IMPORT = "storybook/test";
25
25
  var STORYBOOK_META_IMPORT = "@storybook/react-vite";
26
26
 
27
27
  // src/core/analyze-component.ts
@@ -410,4 +410,4 @@ export {
410
410
  generatePlaywrightTest,
411
411
  forgeTest
412
412
  };
413
- //# sourceMappingURL=chunk-D2RQPIRR.mjs.map
413
+ //# sourceMappingURL=chunk-LI7HWDT6.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/api/forge-test.ts","../src/utils/constants.ts","../src/core/analyze-component.ts","../src/core/generate-playwright-test.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { COMPONENT_EXTENSIONS, CT_FILE_SUFFIX, STORY_FILE_SUFFIX } from '../utils/constants';\nimport type { ForgeTestOptions, ComponentAnalysis } from '../utils/types';\nimport { analyzeComponent } from '../core/analyze-component';\nimport { generatePlaywrightTest } from '../core/generate-playwright-test';\n\nexport interface ForgeTestResult {\n testPath: string;\n content: string;\n analysis: ComponentAnalysis;\n written: boolean;\n}\n\n/**\n * High-level: analyze a component, generate a Playwright component test, and write it.\n */\nexport async function forgeTest(options: ForgeTestOptions): Promise<ForgeTestResult> {\n const { componentPath, overwrite = false, dryRun = false } = options;\n\n const resolvedPath = resolveComponentPath(componentPath);\n if (!resolvedPath) {\n throw new Error(`Component file not found: ${componentPath}`);\n }\n\n const testPath = resolvedPath.replace(/\\.tsx?$/, CT_FILE_SUFFIX);\n if (fs.existsSync(testPath) && !overwrite) {\n throw new Error(`Test already exists: ${testPath}. Use --overwrite to replace.`);\n }\n\n const analysis = analyzeComponent(resolvedPath);\n if (!analysis) {\n throw new Error(`Could not analyze component at ${resolvedPath}. Ensure it exports a React component.`);\n }\n\n const storyPath = resolvedPath.replace(/\\.tsx?$/, STORY_FILE_SUFFIX);\n const hasStories = fs.existsSync(storyPath);\n const importPath = `./${analysis.fileName}`;\n const storyImportPath = hasStories\n ? `./${path.basename(storyPath, '.tsx')}`\n : undefined;\n\n const content = generatePlaywrightTest({\n analysis,\n importPath,\n hasStories,\n storyImportPath,\n });\n\n let written = false;\n if (!dryRun) {\n const dir = path.dirname(testPath);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(testPath, content, 'utf-8');\n written = true;\n }\n\n return { testPath, content, analysis, written };\n}\n\nfunction resolveComponentPath(componentPath: string): string | null {\n const resolved = path.resolve(componentPath);\n\n if (fs.existsSync(resolved)) return resolved;\n\n for (const ext of COMPONENT_EXTENSIONS) {\n const withExt = resolved + ext;\n if (fs.existsSync(withExt)) return withExt;\n }\n\n return null;\n}\n","export const STORY_FILE_SUFFIX = '.stories.tsx'\nexport const CT_FILE_SUFFIX = '.ct.tsx'\nexport const COMPONENT_EXTENSIONS = ['.tsx', '.ts']\n\nexport const IGNORED_FILES = [\n '*.spec.*',\n '*.test.*',\n '*.stories.*',\n '*.styles.*',\n '*.style.*',\n 'index.ts',\n 'index.tsx'\n]\n\nexport const IGNORED_DIRS = [\n 'node_modules',\n 'dist',\n 'build',\n '.storybook',\n '.next',\n '.vite',\n 'coverage',\n '__tests__',\n '__mocks__'\n]\n\nexport const CHAKRA_IMPORTS = ['@chakra-ui/react', '@redesignhealth/ui']\nexport const ROUTER_IMPORTS = ['react-router-dom', 'react-router']\nexport const QUERY_IMPORTS = ['@tanstack/react-query', 'react-query']\nexport const ZUSTAND_IMPORTS = ['zustand']\nexport const FORMIK_IMPORTS = ['formik']\nexport const RHF_IMPORTS = ['react-hook-form']\n\nexport const DEFAULT_DEBOUNCE_MS = 300\n\nexport const STORYBOOK_TEST_IMPORT = 'storybook/test'\nexport const STORYBOOK_META_IMPORT = '@storybook/react-vite'\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport {\n CHAKRA_IMPORTS,\n QUERY_IMPORTS,\n ROUTER_IMPORTS,\n} from '../utils/constants';\nimport type {\n ComponentAnalysis,\n ImportInfo,\n PropInfo,\n} from '../utils/types';\n\n/**\n * Analyze a React component file to extract metadata for story generation.\n *\n * @param filePath - Absolute or relative path to the component file\n * @param content - Optional file content (reads from disk if omitted)\n */\nexport function analyzeComponent(\n filePath: string,\n content?: string,\n): ComponentAnalysis | null {\n if (!content) {\n try {\n content = fs.readFileSync(filePath, 'utf-8');\n } catch {\n return null;\n }\n }\n\n if (!content.trim()) return null;\n\n const name = extractComponentName(content, filePath);\n if (!name) return null;\n\n const imports = extractImports(content);\n const props = extractProps(content, name);\n\n return {\n name,\n fileName: path.basename(filePath).replace(/\\.tsx?$/, ''),\n filePath,\n props,\n hasChildren: props.some((p) => p.name === 'children'),\n imports,\n usesRouter: imports.some((i) =>\n ROUTER_IMPORTS.some((r) => i.source.includes(r))\n ),\n usesReactQuery: imports.some((i) =>\n QUERY_IMPORTS.some((r) => i.source.includes(r))\n ),\n usesChakra: imports.some((i) =>\n CHAKRA_IMPORTS.some((r) => i.source.includes(r))\n ),\n exportType: detectExportType(content, name),\n };\n}\n\nfunction extractComponentName(\n content: string,\n filePath: string,\n): string | null {\n // Match: export const ComponentName = ... or export function ComponentName(\n const namedExport = content.match(\n /export\\s+(?:const|function)\\s+([A-Z][A-Za-z0-9]*)/\n );\n if (namedExport) return namedExport[1];\n\n // Match: export default function ComponentName\n const defaultFn = content.match(\n /export\\s+default\\s+function\\s+([A-Z][A-Za-z0-9]*)/\n );\n if (defaultFn) return defaultFn[1];\n\n // Match: const ComponentName = ... followed by export default ComponentName\n const constThenDefault = content.match(\n /(?:const|function)\\s+([A-Z][A-Za-z0-9]*)\\s*=[\\s\\S]*?export\\s+default\\s+\\1/\n );\n if (constThenDefault) return constThenDefault[1];\n\n // Match: React.forwardRef / React.memo wrapping\n const forwardRef = content.match(\n /export\\s+(?:const|default)\\s+(?:const\\s+)?([A-Z][A-Za-z0-9]*)\\s*=\\s*(?:React\\.)?(?:forwardRef|memo)\\s*[<(]/\n );\n if (forwardRef) return forwardRef[1];\n\n // Fallback: derive from file name\n const fileName = filePath.split('/').pop()?.replace(/\\.tsx?$/, '');\n if (fileName && /[a-zA-Z]/.test(fileName)) {\n return fileName\n .split('-')\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n }\n\n return null;\n}\n\nfunction extractImports(content: string): ImportInfo[] {\n const imports: ImportInfo[] = [];\n const importRegex =\n /import\\s+(?:(?:(\\{[^}]+\\})|(\\w+))\\s+from\\s+)?['\"]([^'\"]+)['\"]/g;\n let match;\n\n while ((match = importRegex.exec(content)) !== null) {\n const namedImports = match[1];\n const defaultImport = match[2];\n const source = match[3];\n const specifiers: string[] = [];\n\n if (namedImports) {\n const names = namedImports\n .replace(/[{}]/g, '')\n .split(',')\n .map((s) => s.trim().split(/\\s+as\\s+/)[0].trim())\n .filter(Boolean);\n specifiers.push(...names);\n }\n if (defaultImport) {\n specifiers.push(defaultImport);\n }\n\n imports.push({ source, specifiers });\n }\n\n return imports;\n}\n\nfunction extractProps(content: string, componentName: string): PropInfo[] {\n const props: PropInfo[] = [];\n\n const propsTypeName = findPropsTypeName(content, componentName);\n if (!propsTypeName) return props;\n\n // Match interface or type body — handle intersection types with &\n const interfaceMatch = content.match(\n new RegExp(\n `(?:interface|type)\\\\s+${propsTypeName}\\\\s*(?:extends\\\\s+[^{]+)?\\\\s*(?:=\\\\s*)?(?:[^{]*&\\\\s*)?\\\\{([^}]*)\\\\}`,\n 's'\n )\n );\n if (!interfaceMatch) return props;\n\n const body = interfaceMatch[1];\n const propLines = body.split('\\n').filter((line) => {\n const trimmed = line.trim();\n return trimmed && !trimmed.startsWith('//') && !trimmed.startsWith('*');\n });\n\n for (const line of propLines) {\n const propMatch = line.match(\n /^\\s*(\\w+)(\\??):\\s*(.+?)(?:\\s*\\/\\/.*)?;?\\s*$/\n );\n if (!propMatch) continue;\n\n const [, name, optional, rawType] = propMatch;\n const type = rawType.trim().replace(/;$/, '');\n const isCallback =\n type.includes('=>') ||\n type.startsWith('(') ||\n /^on[A-Z]/.test(name);\n\n const unionValues = extractUnionValues(type);\n\n props.push({\n name,\n type,\n required: optional !== '?',\n isCallback,\n unionValues: unionValues.length > 0 ? unionValues : undefined,\n });\n }\n\n return props;\n}\n\nfunction findPropsTypeName(\n content: string,\n componentName: string,\n): string | null {\n // Pattern: ComponentName = ({ ... }: PropsType)\n const destructuredProps = content.match(\n new RegExp(\n `(?:const|function)\\\\s+${componentName}\\\\s*(?:=\\\\s*)?\\\\([^)]*:\\\\s*([A-Z]\\\\w*)`\n )\n );\n if (destructuredProps) return destructuredProps[1];\n\n // Pattern: ComponentName(props: PropsType)\n const regularProps = content.match(\n new RegExp(\n `(?:const|function)\\\\s+${componentName}\\\\s*(?:=\\\\s*)?\\\\(\\\\s*(?:props|\\\\w+)\\\\s*:\\\\s*([A-Z]\\\\w*)`\n )\n );\n if (regularProps) return regularProps[1];\n\n // Pattern: React.FC<PropsType>\n const fcProps = content.match(\n new RegExp(`${componentName}[^=]*=\\\\s*(?:React\\\\.)?FC<([A-Z]\\\\w*)>`)\n );\n if (fcProps) return fcProps[1];\n\n // Fallback: look for interface named ComponentNameProps\n const defaultPropsName = `${componentName}Props`;\n if (content.includes(`interface ${defaultPropsName}`) || content.includes(`type ${defaultPropsName}`)) {\n return defaultPropsName;\n }\n\n return null;\n}\n\nfunction extractUnionValues(type: string): string[] {\n // Match single or double-quoted string literal unions: 'a' | 'b' or \"a\" | \"b\"\n const singleQuote = type.match(/^'[^']*'(?:\\s*\\|\\s*'[^']*')+$/);\n if (singleQuote) {\n return type\n .split('|')\n .map((v) => v.trim().replace(/'/g, ''))\n .filter(Boolean);\n }\n const doubleQuote = type.match(/^\"[^\"]*\"(?:\\s*\\|\\s*\"[^\"]*\")+$/);\n if (doubleQuote) {\n return type\n .split('|')\n .map((v) => v.trim().replace(/\"/g, ''))\n .filter(Boolean);\n }\n return [];\n}\n\nfunction detectExportType(\n content: string,\n componentName: string,\n): 'default' | 'named' | 'both' {\n const hasDefault =\n content.includes(`export default ${componentName}`) ||\n content.includes(`export default function ${componentName}`);\n const hasNamed =\n content.includes(`export const ${componentName}`) ||\n content.includes(`export function ${componentName}`);\n\n if (hasDefault && hasNamed) return 'both';\n if (hasDefault) return 'default';\n return 'named';\n}\n","import type { ComponentAnalysis, PropInfo, PlaywrightTestOptions } from '../utils/types';\n\n/**\n * Generate a co-located Playwright component test (.ct.tsx) that:\n * - Mounts the component using @playwright/experimental-ct-react\n * - Tests rendering, visual regression (screenshot), interactions, and a11y\n * - Optionally imports stories for story-driven testing\n */\nexport function generatePlaywrightTest(options: PlaywrightTestOptions): string {\n const { analysis, importPath, hasStories, storyImportPath } = options;\n const { name, props, exportType, hasChildren } = analysis;\n\n const lines: string[] = [];\n\n // Imports\n lines.push(`import { test, expect } from '@playwright/experimental-ct-react';`);\n lines.push(`import AxeBuilder from '@axe-core/playwright';`);\n\n if (exportType === 'default') {\n lines.push(`import ${name} from '${importPath}';`);\n } else {\n lines.push(`import { ${name} } from '${importPath}';`);\n }\n\n if (hasStories && storyImportPath) {\n lines.push(`import * as stories from '${storyImportPath}';`);\n }\n\n lines.push('');\n\n // Test suite\n lines.push(`test.describe('${name}', () => {`);\n\n // Mount & render test\n lines.push(` test('mounts and renders without crashing', async ({ mount }) => {`);\n lines.push(` const component = await mount(`);\n lines.push(` <${name}${buildMinimalProps(props, hasChildren, name)} />`);\n lines.push(` );`);\n lines.push(` await expect(component).toBeVisible();`);\n lines.push(` });`);\n lines.push('');\n\n // Screenshot / visual regression test\n lines.push(` test('matches visual snapshot', async ({ mount }) => {`);\n lines.push(` const component = await mount(`);\n lines.push(` <${name}${buildMinimalProps(props, hasChildren, name)} />`);\n lines.push(` );`);\n lines.push(` await expect(component).toHaveScreenshot('${toKebab(name)}-default.png');`);\n lines.push(` });`);\n lines.push('');\n\n // Interaction tests\n const clickHandler = props.find(\n (p) => p.isCallback && (p.name === 'onClick' || p.name === 'onPress')\n );\n if (clickHandler) {\n lines.push(` test('handles click interaction', async ({ mount }) => {`);\n lines.push(` let clicked = false;`);\n lines.push(` const component = await mount(`);\n lines.push(` <${name}`);\n lines.push(` ${clickHandler.name}={() => { clicked = true; }}`);\n lines.push(...buildRequiredPropsLines(props, hasChildren, name, ' '));\n lines.push(` />`);\n lines.push(` );`);\n lines.push(` await component.click();`);\n lines.push(` expect(clicked).toBe(true);`);\n lines.push(` });`);\n lines.push('');\n }\n\n const onChangeHandler = props.find(\n (p) => p.isCallback && (p.name === 'onChange' || p.name === 'onValueChange')\n );\n if (onChangeHandler) {\n lines.push(` test('handles value change', async ({ mount }) => {`);\n lines.push(` let changed = false;`);\n lines.push(` const component = await mount(`);\n lines.push(` <${name}`);\n lines.push(` ${onChangeHandler.name}={() => { changed = true; }}`);\n lines.push(...buildRequiredPropsLines(props, hasChildren, name, ' '));\n lines.push(` />`);\n lines.push(` );`);\n lines.push(` // Trigger a change event appropriate to the component type`);\n lines.push(` await component.locator('input, select, textarea').first().fill('test');`);\n lines.push(` expect(changed).toBe(true);`);\n lines.push(` });`);\n lines.push('');\n }\n\n // Disabled state\n const disabledProp = props.find(\n (p) => p.name === 'disabled' || p.name === 'isDisabled'\n );\n if (disabledProp) {\n lines.push(` test('renders disabled state correctly', async ({ mount }) => {`);\n lines.push(` const component = await mount(`);\n lines.push(` <${name}`);\n lines.push(` ${disabledProp.name}={true}`);\n lines.push(...buildRequiredPropsLines(props, hasChildren, name, ' '));\n lines.push(` />`);\n lines.push(` );`);\n lines.push(` await expect(component).toHaveScreenshot('${toKebab(name)}-disabled.png');`);\n if (clickHandler) {\n lines.push(` // Verify click handler is not triggered when disabled`);\n lines.push(` let clicked = false;`);\n lines.push(` const disabledComponent = await mount(`);\n lines.push(` <${name}`);\n lines.push(` ${disabledProp.name}={true}`);\n lines.push(` ${clickHandler.name}={() => { clicked = true; }}`);\n lines.push(...buildRequiredPropsLines(props, hasChildren, name, ' '));\n lines.push(` />`);\n lines.push(` );`);\n lines.push(` await disabledComponent.click({ force: true });`);\n lines.push(` expect(clicked).toBe(false);`);\n }\n lines.push(` });`);\n lines.push('');\n }\n\n // Variant visual regression\n const variantProps = props.filter(\n (p) =>\n (p.name === 'size' || p.name === 'variant' || p.name === 'colorPalette') &&\n p.unionValues &&\n p.unionValues.length > 0\n );\n for (const vp of variantProps) {\n lines.push(` test.describe('${vp.name} variants', () => {`);\n for (const val of vp.unionValues!) {\n lines.push(` test('${vp.name}=\"${val}\" matches snapshot', async ({ mount }) => {`);\n lines.push(` const component = await mount(`);\n lines.push(` <${name}`);\n lines.push(` ${vp.name}=\"${val}\"`);\n lines.push(...buildRequiredPropsLines(props, hasChildren, name, ' '));\n lines.push(` />`);\n lines.push(` );`);\n lines.push(` await expect(component).toHaveScreenshot('${toKebab(name)}-${vp.name}-${val}.png');`);\n lines.push(` });`);\n }\n lines.push(` });`);\n lines.push('');\n }\n\n // Accessibility test\n lines.push(` test('meets accessibility standards', async ({ mount, page }) => {`);\n lines.push(` await mount(`);\n lines.push(` <${name}${buildMinimalProps(props, hasChildren, name)} />`);\n lines.push(` );`);\n lines.push(` const a11yResults = await new AxeBuilder({ page }).analyze();`);\n lines.push(` expect(a11yResults.violations).toEqual([]);`);\n lines.push(` });`);\n\n // Story-driven tests\n if (hasStories && storyImportPath) {\n lines.push('');\n lines.push(` test.describe('story-driven tests', () => {`);\n lines.push(` test('renders Default story', async ({ mount }) => {`);\n lines.push(` const args = stories.Default?.args ?? {};`);\n lines.push(` const component = await mount(<${name} {...args} />);`);\n lines.push(` await expect(component).toBeVisible();`);\n lines.push(` });`);\n lines.push(` });`);\n }\n\n lines.push(`});`);\n lines.push('');\n\n return lines.join('\\n');\n}\n\nfunction buildMinimalProps(\n props: PropInfo[],\n hasChildren: boolean,\n componentName: string,\n): string {\n const required = props.filter(\n (p) => p.required && p.name !== 'children'\n );\n\n if (required.length === 0 && !hasChildren) return '';\n\n const propStrings: string[] = [];\n for (const p of required) {\n propStrings.push(`${p.name}={${inferTestValue(p)}}`);\n }\n\n if (hasChildren) {\n return ` ${propStrings.join(' ')}>${componentName} content</${componentName}`;\n }\n\n if (propStrings.length === 0) return '';\n return ` ${propStrings.join(' ')}`;\n}\n\nfunction buildRequiredPropsLines(\n props: PropInfo[],\n hasChildren: boolean,\n componentName: string,\n indent: string,\n): string[] {\n const lines: string[] = [];\n const required = props.filter(\n (p) => p.required && p.name !== 'children' && !p.isCallback\n );\n\n for (const p of required) {\n lines.push(`${indent}${p.name}={${inferTestValue(p)}}`);\n }\n\n if (hasChildren) {\n lines.push(`${indent}children=\"${componentName} content\"`);\n }\n\n return lines;\n}\n\nfunction inferTestValue(prop: PropInfo): string {\n if (prop.isCallback) return '() => {}';\n if (prop.unionValues && prop.unionValues.length > 0)\n return `\"${prop.unionValues[0]}\"`;\n\n const type = prop.type.toLowerCase();\n if (type === 'string') return `\"Test ${prop.name}\"`;\n if (type === 'number') return '42';\n if (type === 'boolean' || type === 'bool') return 'true';\n\n return `undefined as any`;\n}\n\nfunction toKebab(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')\n .toLowerCase();\n}\n"],"mappings":"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","names":["fs","path"]}
@@ -6,7 +6,7 @@ import {
6
6
  STORYBOOK_TEST_IMPORT,
7
7
  STORY_FILE_SUFFIX,
8
8
  analyzeComponent
9
- } from "./chunk-D2RQPIRR.mjs";
9
+ } from "./chunk-LI7HWDT6.mjs";
10
10
 
11
11
  // src/core/scan-directory.ts
12
12
  import fg from "fast-glob";
@@ -97,21 +97,25 @@ function buildClickTest(componentName, clickHandlers) {
97
97
  );
98
98
  lines.push(` await userEvent.click(submitButton);`);
99
99
  lines.push("");
100
- lines.push(` await expect(args.${handler.name}).toHaveBeenCalledTimes(1);`);
100
+ lines.push(
101
+ ` await expect(args.${handler.name}).toHaveBeenCalledTimes(1);`
102
+ );
101
103
  } else if (handler.name === "onClose") {
102
104
  lines.push(
103
105
  ` const closeButton = canvas.getByRole('button', { name: /close/i });`
104
106
  );
105
107
  lines.push(` await userEvent.click(closeButton);`);
106
108
  lines.push("");
107
- lines.push(` await expect(args.${handler.name}).toHaveBeenCalledTimes(1);`);
108
- } else {
109
109
  lines.push(
110
- ` const element = canvas.getByRole('button');`
110
+ ` await expect(args.${handler.name}).toHaveBeenCalledTimes(1);`
111
111
  );
112
+ } else {
113
+ lines.push(` const element = canvas.getByRole('button');`);
112
114
  lines.push(` await userEvent.click(element);`);
113
115
  lines.push("");
114
- lines.push(` await expect(args.${handler.name}).toHaveBeenCalledTimes(1);`);
116
+ lines.push(
117
+ ` await expect(args.${handler.name}).toHaveBeenCalledTimes(1);`
118
+ );
115
119
  }
116
120
  lines.push(` },`);
117
121
  lines.push(`};`);
@@ -134,7 +138,9 @@ function buildRenderTest(componentName, hasChildren) {
134
138
  lines.push(` await expect(element).toBeInTheDocument();`);
135
139
  } else {
136
140
  lines.push(` // Verify the component renders without crashing`);
137
- lines.push(` await expect(canvasElement.firstChild).toBeInTheDocument();`);
141
+ lines.push(
142
+ ` await expect(canvas.getByRole('generic')).toBeInTheDocument();`
143
+ );
138
144
  }
139
145
  lines.push(` },`);
140
146
  lines.push(`};`);
@@ -160,13 +166,17 @@ function buildA11yTest(componentName, hasChildren) {
160
166
  lines.push(` await expect(element).toBeInTheDocument();`);
161
167
  } else {
162
168
  lines.push(
163
- ` await expect(canvasElement.firstChild).toBeInTheDocument();`
169
+ ` await expect(canvas.getByRole('generic')).toBeInTheDocument();`
164
170
  );
165
171
  }
166
172
  lines.push("");
167
173
  lines.push(` // Verify no implicit ARIA role violations`);
168
- lines.push(` // The @storybook/addon-a11y will run axe-core checks on this story`);
169
- lines.push(` // Configure rules in .storybook/preview.tsx via the a11y addon parameter`);
174
+ lines.push(
175
+ ` // The @storybook/addon-a11y will run axe-core checks on this story`
176
+ );
177
+ lines.push(
178
+ ` // Configure rules in .storybook/preview.tsx via the a11y addon parameter`
179
+ );
170
180
  lines.push(` },`);
171
181
  lines.push(`};`);
172
182
  return lines;
@@ -210,6 +220,11 @@ function generateStoryContent(options) {
210
220
  lines.push("");
211
221
  lines.push(...variantStories);
212
222
  }
223
+ const propStories = buildPropStories(analysis);
224
+ if (propStories.length > 0) {
225
+ lines.push("");
226
+ lines.push(...propStories);
227
+ }
213
228
  if (!skipInteractionTests) {
214
229
  const interactionStories = generateInteractionTests(analysis);
215
230
  if (interactionStories.length > 0) {
@@ -226,19 +241,15 @@ function buildImports(analysis, importPath, skipInteractionTests) {
226
241
  lines.push(
227
242
  `import type { ${metaImports.join(", ")} } from '${STORYBOOK_META_IMPORT}';`
228
243
  );
229
- if (!skipInteractionTests) {
230
- const testImports = buildTestImports(analysis);
231
- if (testImports.length > 0) {
232
- lines.push(
233
- `import { ${testImports.join(", ")} } from '${STORYBOOK_TEST_IMPORT}';`
234
- );
235
- }
236
- }
237
- if (analysis.usesRouter) {
244
+ const testImports = buildTestImports(analysis, skipInteractionTests);
245
+ if (testImports.length > 0) {
238
246
  lines.push(
239
- `import { withRouter } from 'storybook-addon-react-router-v6';`
247
+ `import { ${testImports.join(", ")} } from '${STORYBOOK_TEST_IMPORT}';`
240
248
  );
241
249
  }
250
+ if (analysis.usesRouter) {
251
+ lines.push(`import { withRouter } from 'storybook-addon-react-router-v6';`);
252
+ }
242
253
  lines.push(`import React from 'react';`);
243
254
  if (analysis.exportType === "default") {
244
255
  lines.push(`import ${analysis.name} from '${importPath}';`);
@@ -247,23 +258,22 @@ function buildImports(analysis, importPath, skipInteractionTests) {
247
258
  }
248
259
  return lines;
249
260
  }
250
- function buildTestImports(analysis) {
261
+ function buildTestImports(analysis, skipInteractionTests) {
251
262
  const imports = /* @__PURE__ */ new Set();
252
263
  const { props } = analysis;
253
264
  const hasCallbacks = props.some((p) => p.isCallback);
254
- const hasInteractiveProps = props.some(
255
- (p) => ["string", "number"].includes(inferControlType(p) ?? "")
256
- );
257
265
  const hasClickable = props.some(
258
266
  (p) => p.name === "onClick" || p.name === "onPress"
259
267
  );
260
- if (hasCallbacks) imports.add("fn");
261
- if (hasClickable || hasInteractiveProps) {
268
+ const hasDisabledProp = props.some(
269
+ (p) => p.name === "disabled" || p.name === "isDisabled"
270
+ );
271
+ if (!skipInteractionTests) {
262
272
  imports.add("expect");
263
273
  imports.add("within");
274
+ if (hasClickable) imports.add("userEvent");
264
275
  }
265
- if (hasClickable) imports.add("userEvent");
266
- if (hasInteractiveProps) imports.add("userEvent");
276
+ if (hasCallbacks) imports.add("fn");
267
277
  return Array.from(imports);
268
278
  }
269
279
  function buildMeta(analysis, storyTitle) {
@@ -403,6 +413,118 @@ function buildVariantStories(analysis) {
403
413
  }
404
414
  return lines;
405
415
  }
416
+ function buildPropStories(analysis) {
417
+ const lines = [];
418
+ const { props, name, hasChildren } = analysis;
419
+ const unionProps = props.filter(
420
+ (p) => p.unionValues && p.unionValues.length > 0 && (p.name === "size" || p.name === "variant" || p.name === "colorPalette")
421
+ );
422
+ for (const prop of unionProps) {
423
+ for (const value of prop.unionValues) {
424
+ const storyName = toPascalCase(`${prop.name}_${value}`);
425
+ lines.push(`export const ${storyName}: Story = {`);
426
+ lines.push(` args: {`);
427
+ lines.push(` ${prop.name}: '${value}',`);
428
+ if (hasChildren) {
429
+ lines.push(` children: '${name} ${value}',`);
430
+ }
431
+ lines.push(` },`);
432
+ lines.push(`};`);
433
+ lines.push("");
434
+ }
435
+ }
436
+ const booleanProps = props.filter(
437
+ (p) => !p.isCallback && (p.type.toLowerCase() === "boolean" || p.type.toLowerCase() === "bool") && p.name !== "disabled" && p.name !== "isDisabled"
438
+ );
439
+ for (const boolProp of booleanProps) {
440
+ const storyName = toPascalCase(boolProp.name);
441
+ lines.push(`export const ${storyName}: Story = {`);
442
+ lines.push(` args: {`);
443
+ lines.push(` ${boolProp.name}: true,`);
444
+ if (hasChildren) {
445
+ lines.push(` children: '${name} with ${boolProp.name}',`);
446
+ }
447
+ lines.push(` },`);
448
+ lines.push(`};`);
449
+ lines.push("");
450
+ }
451
+ if (hasChildren) {
452
+ lines.push(`export const WithChildren: Story = {`);
453
+ lines.push(` args: {`);
454
+ lines.push(` children: 'Custom child content',`);
455
+ lines.push(` },`);
456
+ lines.push(`};`);
457
+ lines.push("");
458
+ }
459
+ const sizeProp = props.find((p) => p.name === "size" && p.unionValues?.length);
460
+ const variantProp = props.find(
461
+ (p) => p.name === "variant" && p.unionValues?.length
462
+ );
463
+ if (sizeProp && variantProp) {
464
+ lines.push(`export const AllCombinations: Story = {`);
465
+ lines.push(` render: (args) => (`);
466
+ lines.push(
467
+ ` <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>`
468
+ );
469
+ for (const variant of variantProp.unionValues) {
470
+ lines.push(
471
+ ` <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>`
472
+ );
473
+ for (const size of sizeProp.unionValues) {
474
+ lines.push(
475
+ ` <${name} {...args} variant="${variant}" size="${size}">${hasChildren ? `${variant} ${size}` : ""}</${name}>`
476
+ );
477
+ }
478
+ lines.push(` </div>`);
479
+ }
480
+ lines.push(` </div>`);
481
+ lines.push(` ),`);
482
+ lines.push(`};`);
483
+ lines.push("");
484
+ }
485
+ const disabledProp = props.find(
486
+ (p) => p.name === "disabled" || p.name === "isDisabled"
487
+ );
488
+ const clickHandler = props.find(
489
+ (p) => p.isCallback && (p.name === "onClick" || p.name === "onPress")
490
+ );
491
+ if (disabledProp && clickHandler) {
492
+ lines.push(`export const DisabledInteractive: Story = {`);
493
+ lines.push(` args: {`);
494
+ lines.push(` ${disabledProp.name}: true,`);
495
+ lines.push(` ${clickHandler.name}: fn(),`);
496
+ if (hasChildren) {
497
+ lines.push(` children: 'Disabled ${name}',`);
498
+ }
499
+ lines.push(` },`);
500
+ lines.push(`};`);
501
+ lines.push("");
502
+ }
503
+ const textProp = props.find(
504
+ (p) => !p.isCallback && p.type.toLowerCase() === "string" && p.name !== "className" && p.name !== "id"
505
+ );
506
+ if (textProp || hasChildren) {
507
+ lines.push(`export const LongText: Story = {`);
508
+ lines.push(` args: {`);
509
+ if (textProp) {
510
+ lines.push(
511
+ ` ${textProp.name}: 'This is a much longer piece of text to test how the component handles overflow and wrapping behavior',`
512
+ );
513
+ }
514
+ if (hasChildren) {
515
+ lines.push(
516
+ ` children: 'This is a much longer piece of text to test how the component handles overflow and wrapping behavior',`
517
+ );
518
+ }
519
+ lines.push(` },`);
520
+ lines.push(`};`);
521
+ lines.push("");
522
+ }
523
+ return lines;
524
+ }
525
+ function toPascalCase(str) {
526
+ return str.split(/[_\-\s]+/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
527
+ }
406
528
  function inferControlType(prop) {
407
529
  const type = prop.type.toLowerCase();
408
530
  if (type === "boolean" || type === "bool") return "boolean";
@@ -450,15 +572,15 @@ function inferStoryTitle(filePath, baseDir) {
450
572
  const fileName = path2.basename(relative2, path2.extname(relative2));
451
573
  const dirPart = path2.dirname(relative2);
452
574
  if (dirPart === ".") {
453
- return `Components / ${toPascalCase(fileName)}`;
575
+ return `Components / ${toPascalCase2(fileName)}`;
454
576
  }
455
577
  let segments = dirPart.split(path2.sep);
456
578
  const stripPrefixes = ["src", "lib", "source"];
457
579
  while (segments.length > 0 && stripPrefixes.includes(segments[0].toLowerCase())) {
458
580
  segments.shift();
459
581
  }
460
- const parts = segments.map((seg) => toPascalCase(seg));
461
- const componentName = toPascalCase(fileName);
582
+ const parts = segments.map((seg) => toPascalCase2(seg));
583
+ const componentName = toPascalCase2(fileName);
462
584
  parts.push(componentName);
463
585
  const deduped = [];
464
586
  for (const part of parts) {
@@ -471,7 +593,7 @@ function inferStoryTitle(filePath, baseDir) {
471
593
  }
472
594
  return deduped.join(" / ");
473
595
  }
474
- function toPascalCase(str) {
596
+ function toPascalCase2(str) {
475
597
  return str.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
476
598
  }
477
599
 
@@ -641,7 +763,7 @@ async function forgeStories(options) {
641
763
  dryRun = false,
642
764
  includeComponentTests = false
643
765
  } = options;
644
- const { forgeTest } = includeComponentTests ? await import("./forge-test-EGP3AGFI.mjs") : { forgeTest: null };
766
+ const { forgeTest } = includeComponentTests ? await import("./forge-test-DN3KUSDK.mjs") : { forgeTest: null };
645
767
  const absoluteDir = path5.resolve(dir);
646
768
  const scan = await scanDirectory(absoluteDir);
647
769
  let generated = 0;
@@ -701,4 +823,4 @@ export {
701
823
  watchDirectory,
702
824
  forgeStories
703
825
  };
704
- //# sourceMappingURL=chunk-C2HX5UGS.mjs.map
826
+ //# sourceMappingURL=chunk-NGZBCOK5.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/scan-directory.ts","../src/core/generate-interaction-tests.ts","../src/core/generate-story-content.ts","../src/core/score-coverage.ts","../src/core/infer-title.ts","../src/api/forge-story.ts","../src/core/watch.ts","../src/api/forge-stories.ts"],"sourcesContent":["import fg from 'fast-glob';\nimport * as path from 'node:path';\nimport * as fs from 'node:fs';\n\nimport {\n COMPONENT_EXTENSIONS,\n IGNORED_DIRS,\n STORY_FILE_SUFFIX,\n} from '../utils/constants';\nimport type { ScanResult, ScannedComponent } from '../utils/types';\nimport { analyzeComponent } from './analyze-component';\n\n/**\n * Scan a directory for React components and classify them by story coverage.\n */\nexport async function scanDirectory(dir: string): Promise<ScanResult> {\n const absoluteDir = path.resolve(dir);\n\n if (!fs.existsSync(absoluteDir)) {\n throw new Error(`Directory not found: ${absoluteDir}`);\n }\n\n const extensions = COMPONENT_EXTENSIONS.map((e) => e.replace('.', '')).join(',');\n const ignorePatterns = [\n ...IGNORED_DIRS.map((d) => `**/${d}/**`),\n '**/*.spec.*',\n '**/*.test.*',\n '**/*.stories.*',\n '**/*.styles.*',\n '**/*.style.*',\n '**/index.ts',\n '**/index.tsx',\n ];\n\n const files = await fg(`**/*.{${extensions}}`, {\n cwd: absoluteDir,\n ignore: ignorePatterns,\n absolute: true,\n });\n\n const components: ScannedComponent[] = [];\n const withStories: string[] = [];\n const withoutStories: string[] = [];\n const notAnalyzable: string[] = [];\n\n for (const filePath of files) {\n const analysis = analyzeComponent(filePath);\n const storyPath = filePath.replace(/\\.tsx?$/, STORY_FILE_SUFFIX);\n const hasStory = fs.existsSync(storyPath);\n\n components.push({ filePath, analysis, hasStory });\n\n if (!analysis) {\n notAnalyzable.push(filePath);\n } else if (hasStory) {\n withStories.push(filePath);\n } else {\n withoutStories.push(filePath);\n }\n }\n\n return {\n components,\n withStories,\n withoutStories,\n notAnalyzable,\n total: files.length,\n };\n}\n","import type { ComponentAnalysis, PropInfo } from '../utils/types'\n\n/**\n * Generate interaction test stories (play functions) based on component analysis.\n * Returns an array of lines to append to the story file.\n */\nexport function generateInteractionTests(\n analysis: ComponentAnalysis\n): string[] {\n const lines: string[] = []\n const { props, name, hasChildren } = analysis\n\n const clickHandlers = props.filter(\n p =>\n p.isCallback &&\n (p.name === 'onClick' ||\n p.name === 'onPress' ||\n p.name === 'onSubmit' ||\n p.name === 'onClose')\n )\n\n const toggleProps = props.filter(\n p =>\n !p.isCallback &&\n (p.type.toLowerCase() === 'boolean' || p.type.toLowerCase() === 'bool') &&\n (p.name === 'checked' ||\n p.name === 'isChecked' ||\n p.name === 'selected' ||\n p.name === 'open' ||\n p.name === 'isOpen')\n )\n\n // Click interaction test\n if (clickHandlers.length > 0) {\n lines.push(...buildClickTest(name, clickHandlers))\n }\n\n // Render test (always generate)\n lines.push(...buildRenderTest(name, hasChildren))\n\n // Keyboard navigation test\n if (clickHandlers.length > 0 || toggleProps.length > 0) {\n lines.push('')\n lines.push(...buildKeyboardTest(name, clickHandlers))\n }\n\n // Accessibility test (always generate)\n lines.push('')\n lines.push(...buildA11yTest(name, hasChildren))\n\n return lines\n}\n\nfunction buildClickTest(\n componentName: string,\n clickHandlers: PropInfo[]\n): string[] {\n const lines: string[] = []\n const handler = clickHandlers[0]\n\n lines.push(`export const ClickInteraction: Story = {`)\n lines.push(` args: {`)\n lines.push(` ${handler.name}: fn(),`)\n lines.push(` },`)\n lines.push(` play: async ({ canvasElement, args }) => {`)\n lines.push(` const canvas = within(canvasElement);`)\n lines.push('')\n\n if (handler.name === 'onSubmit') {\n lines.push(\n ` const submitButton = canvas.getByRole('button', { name: /submit/i });`\n )\n lines.push(` await userEvent.click(submitButton);`)\n lines.push('')\n lines.push(\n ` await expect(args.${handler.name}).toHaveBeenCalledTimes(1);`\n )\n } else if (handler.name === 'onClose') {\n lines.push(\n ` const closeButton = canvas.getByRole('button', { name: /close/i });`\n )\n lines.push(` await userEvent.click(closeButton);`)\n lines.push('')\n lines.push(\n ` await expect(args.${handler.name}).toHaveBeenCalledTimes(1);`\n )\n } else {\n lines.push(` const element = canvas.getByRole('button');`)\n lines.push(` await userEvent.click(element);`)\n lines.push('')\n lines.push(\n ` await expect(args.${handler.name}).toHaveBeenCalledTimes(1);`\n )\n }\n\n lines.push(` },`)\n lines.push(`};`)\n\n return lines\n}\n\nfunction buildRenderTest(\n componentName: string,\n hasChildren: boolean\n): string[] {\n const lines: string[] = []\n\n lines.push('')\n lines.push(`export const RendersCorrectly: Story = {`)\n\n if (hasChildren) {\n lines.push(` args: {`)\n lines.push(` children: 'Test content',`)\n lines.push(` },`)\n }\n\n lines.push(` play: async ({ canvasElement }) => {`)\n lines.push(` const canvas = within(canvasElement);`)\n lines.push('')\n\n if (hasChildren) {\n lines.push(` const element = canvas.getByText('Test content');`)\n lines.push(` await expect(element).toBeInTheDocument();`)\n } else {\n lines.push(` // Verify the component renders without crashing`)\n lines.push(\n ` await expect(canvas.getByRole('generic')).toBeInTheDocument();`\n )\n }\n\n lines.push(` },`)\n lines.push(`};`)\n\n return lines\n}\n\nfunction buildA11yTest(componentName: string, hasChildren: boolean): string[] {\n const lines: string[] = []\n\n lines.push(`export const AccessibilityAudit: Story = {`)\n lines.push(` tags: ['a11y'],`)\n\n if (hasChildren) {\n lines.push(` args: {`)\n lines.push(` children: '${componentName} content',`)\n lines.push(` },`)\n }\n\n lines.push(` play: async ({ canvasElement }) => {`)\n lines.push(` const canvas = within(canvasElement);`)\n lines.push('')\n lines.push(` // Verify component is rendered and visible`)\n\n if (hasChildren) {\n lines.push(\n ` const element = canvas.getByText('${componentName} content');`\n )\n lines.push(` await expect(element).toBeInTheDocument();`)\n } else {\n lines.push(\n ` await expect(canvas.getByRole('generic')).toBeInTheDocument();`\n )\n }\n\n lines.push('')\n lines.push(` // Verify no implicit ARIA role violations`)\n lines.push(\n ` // The @storybook/addon-a11y will run axe-core checks on this story`\n )\n lines.push(\n ` // Configure rules in .storybook/preview.tsx via the a11y addon parameter`\n )\n lines.push(` },`)\n lines.push(`};`)\n\n return lines\n}\n\nfunction buildKeyboardTest(\n _componentName: string,\n clickHandlers: PropInfo[]\n): string[] {\n const lines: string[] = []\n const handler = clickHandlers[0]\n\n if (!handler) return lines\n\n lines.push(`export const KeyboardNavigation: Story = {`)\n lines.push(` args: {`)\n lines.push(` ${handler.name}: fn(),`)\n lines.push(` },`)\n lines.push(` play: async ({ canvasElement, args }) => {`)\n lines.push(` const canvas = within(canvasElement);`)\n lines.push('')\n lines.push(` const element = canvas.getByRole('button');`)\n lines.push(` await userEvent.tab();`)\n lines.push(` await expect(element).toHaveFocus();`)\n lines.push('')\n lines.push(` await userEvent.keyboard('{Enter}');`)\n lines.push(` await expect(args.${handler.name}).toHaveBeenCalled();`)\n lines.push(` },`)\n lines.push(`};`)\n\n return lines\n}\n","import type {\n ComponentAnalysis,\n PropInfo,\n StoryContentOptions\n} from '../utils/types'\nimport {\n STORYBOOK_META_IMPORT,\n STORYBOOK_TEST_IMPORT\n} from '../utils/constants'\nimport { generateInteractionTests } from './generate-interaction-tests'\n\n/**\n * Generate the full content of a .stories.tsx file from component analysis.\n */\nexport function generateStoryContent(options: StoryContentOptions): string {\n const { analysis, storyTitle, importPath, skipInteractionTests } = options\n const { name } = analysis\n\n const lines: string[] = []\n\n lines.push(...buildImports(analysis, importPath, skipInteractionTests))\n lines.push('')\n\n lines.push(...buildMeta(analysis, storyTitle))\n lines.push('')\n\n lines.push(`type Story = StoryObj<typeof ${name}>;`)\n lines.push('')\n\n lines.push(...buildDefaultStory(analysis))\n\n const variantStories = buildVariantStories(analysis)\n if (variantStories.length > 0) {\n lines.push('')\n lines.push(...variantStories)\n }\n\n const propStories = buildPropStories(analysis)\n if (propStories.length > 0) {\n lines.push('')\n lines.push(...propStories)\n }\n\n if (!skipInteractionTests) {\n const interactionStories = generateInteractionTests(analysis)\n if (interactionStories.length > 0) {\n lines.push('')\n lines.push(...interactionStories)\n }\n }\n\n lines.push('')\n return lines.join('\\n')\n}\n\nfunction buildImports(\n analysis: ComponentAnalysis,\n importPath: string,\n skipInteractionTests: boolean\n): string[] {\n const lines: string[] = []\n const metaImports = ['Meta', 'StoryObj']\n\n lines.push(\n `import type { ${metaImports.join(', ')} } from '${STORYBOOK_META_IMPORT}';`\n )\n\n // Build test imports: needed for interaction tests AND prop stories (DisabledInteractive uses fn)\n const testImports = buildTestImports(analysis, skipInteractionTests)\n if (testImports.length > 0) {\n lines.push(\n `import { ${testImports.join(', ')} } from '${STORYBOOK_TEST_IMPORT}';`\n )\n }\n\n if (analysis.usesRouter) {\n lines.push(`import { withRouter } from 'storybook-addon-react-router-v6';`)\n }\n\n lines.push(`import React from 'react';`)\n\n if (analysis.exportType === 'default') {\n lines.push(`import ${analysis.name} from '${importPath}';`)\n } else {\n lines.push(`import { ${analysis.name} } from '${importPath}';`)\n }\n\n return lines\n}\n\nfunction buildTestImports(\n analysis: ComponentAnalysis,\n skipInteractionTests: boolean\n): string[] {\n const imports = new Set<string>()\n const { props } = analysis\n\n const hasCallbacks = props.some(p => p.isCallback)\n const hasClickable = props.some(\n p => p.name === 'onClick' || p.name === 'onPress'\n )\n const hasDisabledProp = props.some(\n p => p.name === 'disabled' || p.name === 'isDisabled'\n )\n\n if (!skipInteractionTests) {\n // RendersCorrectly and AccessibilityAudit always use expect + within\n imports.add('expect')\n imports.add('within')\n if (hasClickable) imports.add('userEvent')\n }\n\n // fn() is needed for callback args in meta AND DisabledInteractive story\n if (hasCallbacks) imports.add('fn')\n\n // DisabledInteractive uses fn() even when interaction tests are skipped\n // No additional imports needed beyond fn for that case\n\n return Array.from(imports)\n}\n\nfunction buildMeta(analysis: ComponentAnalysis, storyTitle: string): string[] {\n const lines: string[] = []\n const { name, props, usesRouter } = analysis\n\n lines.push(`const meta: Meta<typeof ${name}> = {`)\n lines.push(` component: ${name},`)\n lines.push(` title: '${storyTitle}',`)\n lines.push(` tags: ['autodocs'],`)\n\n if (usesRouter) {\n lines.push(` decorators: [withRouter],`)\n }\n\n const argTypes = buildArgTypes(props)\n if (argTypes.length > 0) {\n lines.push(` argTypes: {`)\n lines.push(...argTypes)\n lines.push(` },`)\n }\n\n const defaultArgs = buildDefaultArgs(props)\n if (defaultArgs.length > 0) {\n lines.push(` args: {`)\n lines.push(...defaultArgs)\n lines.push(` },`)\n }\n\n lines.push(`};`)\n lines.push('')\n lines.push(`export default meta;`)\n\n return lines\n}\n\nfunction buildArgTypes(props: PropInfo[]): string[] {\n const lines: string[] = []\n\n for (const prop of props) {\n if (prop.name === 'children') continue\n\n if (prop.isCallback) {\n lines.push(` ${prop.name}: { action: '${prop.name}' },`)\n continue\n }\n\n if (prop.unionValues && prop.unionValues.length > 0) {\n const options = prop.unionValues.map(v => `'${v}'`).join(', ')\n lines.push(` ${prop.name}: {`)\n lines.push(` options: [${options}],`)\n lines.push(` control: { type: 'select' },`)\n lines.push(` },`)\n continue\n }\n\n const controlType = inferControlType(prop)\n if (controlType) {\n lines.push(` ${prop.name}: { control: { type: '${controlType}' } },`)\n }\n }\n\n return lines\n}\n\nfunction buildDefaultArgs(props: PropInfo[]): string[] {\n const lines: string[] = []\n\n for (const prop of props) {\n if (!prop.required) continue\n\n if (prop.isCallback) {\n lines.push(` ${prop.name}: fn(),`)\n continue\n }\n\n const defaultValue = inferDefaultValue(prop)\n if (defaultValue !== undefined) {\n lines.push(` ${prop.name}: ${defaultValue},`)\n }\n }\n\n return lines\n}\n\nfunction buildDefaultStory(analysis: ComponentAnalysis): string[] {\n const lines: string[] = []\n const { hasChildren } = analysis\n\n lines.push(`export const Default: Story = {`)\n\n if (hasChildren) {\n lines.push(` args: {`)\n lines.push(` children: '${analysis.name} content',`)\n lines.push(` },`)\n }\n\n lines.push(`};`)\n return lines\n}\n\nfunction buildVariantStories(analysis: ComponentAnalysis): string[] {\n const lines: string[] = []\n const { props, name } = analysis\n\n const sizeProp = props.find(p => p.name === 'size')\n if (sizeProp?.unionValues && sizeProp.unionValues.length > 0) {\n lines.push(`export const Sizes: Story = {`)\n lines.push(` render: (args) => (`)\n lines.push(` <>`)\n for (const size of sizeProp.unionValues) {\n lines.push(\n ` <${name} {...args} size=\"${size}\">${analysis.hasChildren ? `Size ${size}` : ''}</${name}>`\n )\n }\n lines.push(` </>`)\n lines.push(` ),`)\n lines.push(`};`)\n lines.push('')\n }\n\n const variantProp = props.find(p => p.name === 'variant')\n if (variantProp?.unionValues && variantProp.unionValues.length > 0) {\n lines.push(`export const Variants: Story = {`)\n lines.push(` render: (args) => (`)\n lines.push(` <>`)\n for (const variant of variantProp.unionValues) {\n lines.push(\n ` <${name} {...args} variant=\"${variant}\">${analysis.hasChildren ? `Variant ${variant}` : ''}</${name}>`\n )\n }\n lines.push(` </>`)\n lines.push(` ),`)\n lines.push(`};`)\n lines.push('')\n }\n\n const colorPaletteProp = props.find(p => p.name === 'colorPalette')\n if (\n colorPaletteProp?.unionValues &&\n colorPaletteProp.unionValues.length > 0\n ) {\n lines.push(`export const ColorPalettes: Story = {`)\n lines.push(` render: (args) => (`)\n lines.push(` <>`)\n for (const palette of colorPaletteProp.unionValues) {\n lines.push(\n ` <${name} {...args} colorPalette=\"${palette}\">${analysis.hasChildren ? palette : ''}</${name}>`\n )\n }\n lines.push(` </>`)\n lines.push(` ),`)\n lines.push(`};`)\n lines.push('')\n }\n\n const disabledProp = props.find(\n p => p.name === 'disabled' || p.name === 'isDisabled'\n )\n if (disabledProp) {\n lines.push(`export const Disabled: Story = {`)\n lines.push(` args: {`)\n lines.push(` ${disabledProp.name}: true,`)\n lines.push(` },`)\n lines.push(`};`)\n }\n\n return lines\n}\n\nfunction buildPropStories(analysis: ComponentAnalysis): string[] {\n const lines: string[] = []\n const { props, name, hasChildren } = analysis\n\n // Individual stories for each union value on key props (size, variant, colorPalette)\n const unionProps = props.filter(\n p =>\n p.unionValues &&\n p.unionValues.length > 0 &&\n (p.name === 'size' || p.name === 'variant' || p.name === 'colorPalette')\n )\n\n for (const prop of unionProps) {\n for (const value of prop.unionValues!) {\n const storyName = toPascalCase(`${prop.name}_${value}`)\n lines.push(`export const ${storyName}: Story = {`)\n lines.push(` args: {`)\n lines.push(` ${prop.name}: '${value}',`)\n if (hasChildren) {\n lines.push(` children: '${name} ${value}',`)\n }\n lines.push(` },`)\n lines.push(`};`)\n lines.push('')\n }\n }\n\n // Boolean prop stories (e.g. loading, fullWidth, readOnly)\n const booleanProps = props.filter(\n p =>\n !p.isCallback &&\n (p.type.toLowerCase() === 'boolean' || p.type.toLowerCase() === 'bool') &&\n p.name !== 'disabled' &&\n p.name !== 'isDisabled'\n )\n\n for (const boolProp of booleanProps) {\n const storyName = toPascalCase(boolProp.name)\n lines.push(`export const ${storyName}: Story = {`)\n lines.push(` args: {`)\n lines.push(` ${boolProp.name}: true,`)\n if (hasChildren) {\n lines.push(` children: '${name} with ${boolProp.name}',`)\n }\n lines.push(` },`)\n lines.push(`};`)\n lines.push('')\n }\n\n // WithChildren story if the component accepts children\n if (hasChildren) {\n lines.push(`export const WithChildren: Story = {`)\n lines.push(` args: {`)\n lines.push(` children: 'Custom child content',`)\n lines.push(` },`)\n lines.push(`};`)\n lines.push('')\n }\n\n // Combined variant stories (e.g. size + variant combos) when both exist\n const sizeProp = props.find(p => p.name === 'size' && p.unionValues?.length)\n const variantProp = props.find(\n p => p.name === 'variant' && p.unionValues?.length\n )\n if (sizeProp && variantProp) {\n lines.push(`export const AllCombinations: Story = {`)\n lines.push(` render: (args) => (`)\n lines.push(\n ` <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>`\n )\n for (const variant of variantProp.unionValues!) {\n lines.push(\n ` <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>`\n )\n for (const size of sizeProp.unionValues!) {\n lines.push(\n ` <${name} {...args} variant=\"${variant}\" size=\"${size}\">${hasChildren ? `${variant} ${size}` : ''}</${name}>`\n )\n }\n lines.push(` </div>`)\n }\n lines.push(` </div>`)\n lines.push(` ),`)\n lines.push(`};`)\n lines.push('')\n }\n\n // DisabledInteractive: disabled + click handler to show it's inert\n const disabledProp = props.find(\n p => p.name === 'disabled' || p.name === 'isDisabled'\n )\n const clickHandler = props.find(\n p => p.isCallback && (p.name === 'onClick' || p.name === 'onPress')\n )\n if (disabledProp && clickHandler) {\n lines.push(`export const DisabledInteractive: Story = {`)\n lines.push(` args: {`)\n lines.push(` ${disabledProp.name}: true,`)\n lines.push(` ${clickHandler.name}: fn(),`)\n if (hasChildren) {\n lines.push(` children: 'Disabled ${name}',`)\n }\n lines.push(` },`)\n lines.push(`};`)\n lines.push('')\n }\n\n // LongText story for components with string props or children\n const textProp = props.find(\n p =>\n !p.isCallback &&\n p.type.toLowerCase() === 'string' &&\n p.name !== 'className' &&\n p.name !== 'id'\n )\n if (textProp || hasChildren) {\n lines.push(`export const LongText: Story = {`)\n lines.push(` args: {`)\n if (textProp) {\n lines.push(\n ` ${textProp.name}: 'This is a much longer piece of text to test how the component handles overflow and wrapping behavior',`\n )\n }\n if (hasChildren) {\n lines.push(\n ` children: 'This is a much longer piece of text to test how the component handles overflow and wrapping behavior',`\n )\n }\n lines.push(` },`)\n lines.push(`};`)\n lines.push('')\n }\n\n return lines\n}\n\nfunction toPascalCase(str: string): string {\n return str\n .split(/[_\\-\\s]+/)\n .map(part => part.charAt(0).toUpperCase() + part.slice(1))\n .join('')\n}\n\nfunction inferControlType(prop: PropInfo): string | null {\n const type = prop.type.toLowerCase()\n\n if (type === 'boolean' || type === 'bool') return 'boolean'\n if (type === 'string') return 'text'\n if (type === 'number') return 'number'\n if (type.includes('react.reactnode') || type.includes('reactnode'))\n return null\n if (prop.isCallback) return null\n\n return null\n}\n\nfunction inferDefaultValue(prop: PropInfo): string | undefined {\n if (prop.defaultValue) return prop.defaultValue\n\n const type = prop.type.toLowerCase()\n\n if (type === 'string') return `'Example ${prop.name}'`\n if (type === 'number') return '0'\n if (type === 'boolean' || type === 'bool') return 'false'\n\n if (prop.unionValues && prop.unionValues.length > 0) {\n return `'${prop.unionValues[0]}'`\n }\n\n if (prop.isCallback) return 'fn()'\n\n return undefined\n}\n","import type { CoverageReport } from '../utils/types';\n\n/**\n * Calculate a coverage score and letter grade from covered/total counts.\n */\nexport function scoreCoverage(covered: number, total: number): CoverageReport {\n if (total === 0) {\n return { covered: 0, total: 0, percentage: 0, grade: 'F' };\n }\n\n const percentage = Math.round((covered / total) * 100);\n\n let grade: CoverageReport['grade'];\n if (percentage >= 90) grade = 'A';\n else if (percentage >= 75) grade = 'B';\n else if (percentage >= 50) grade = 'C';\n else if (percentage >= 25) grade = 'D';\n else grade = 'F';\n\n return { covered, total, percentage, grade };\n}\n","import * as path from 'node:path';\n\n/**\n * Infer a Storybook title from a component's file path.\n *\n * Strategy:\n * 1. Compute relative path from the target directory\n * 2. Strip common prefixes: src/, lib/, components/\n * 3. Convert directory segments to PascalCase\n * 4. Deduplicate consecutive identical segments\n *\n * Examples:\n * src/components/Button/Button.tsx → \"Components / Button\"\n * src/components/forms/TextInput.tsx → \"Components / Forms / TextInput\"\n * src/lib/ui/Modal/Modal.tsx → \"UI / Modal\"\n * libs/shared/ui/src/lib/button/button.tsx → \"Button\"\n */\nexport function inferStoryTitle(filePath: string, baseDir?: string): string {\n const resolvedBase = baseDir ? path.resolve(baseDir) : path.dirname(filePath);\n const resolvedFile = path.resolve(filePath);\n\n let relative = path.relative(resolvedBase, resolvedFile);\n\n // Strip the filename — we only want directory segments\n const fileName = path.basename(relative, path.extname(relative));\n const dirPart = path.dirname(relative);\n\n if (dirPart === '.') {\n return `Components / ${toPascalCase(fileName)}`;\n }\n\n // Split into segments and clean up\n let segments = dirPart.split(path.sep);\n\n // Strip common prefixes\n const stripPrefixes = ['src', 'lib', 'source'];\n while (segments.length > 0 && stripPrefixes.includes(segments[0].toLowerCase())) {\n segments.shift();\n }\n\n // Convert to PascalCase\n const parts = segments.map((seg) => toPascalCase(seg));\n\n // Add the component name\n const componentName = toPascalCase(fileName);\n parts.push(componentName);\n\n // Deduplicate consecutive identical segments\n const deduped: string[] = [];\n for (const part of parts) {\n if (part !== deduped[deduped.length - 1]) {\n deduped.push(part);\n }\n }\n\n if (deduped.length === 0) {\n return `Components / ${componentName}`;\n }\n\n return deduped.join(' / ');\n}\n\nfunction toPascalCase(str: string): string {\n return str\n .split(/[-_]/)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { COMPONENT_EXTENSIONS, STORY_FILE_SUFFIX } from '../utils/constants';\nimport type { ForgeStoryOptions, ComponentAnalysis } from '../utils/types';\nimport { analyzeComponent } from '../core/analyze-component';\nimport { generateStoryContent } from '../core/generate-story-content';\nimport { inferStoryTitle } from '../core/infer-title';\n\nexport interface ForgeStoryResult {\n storyPath: string;\n content: string;\n analysis: ComponentAnalysis;\n storiesGenerated: string[];\n written: boolean;\n}\n\n/**\n * High-level: analyze a component, generate a story, and write it to disk.\n */\nexport async function forgeStory(options: ForgeStoryOptions): Promise<ForgeStoryResult> {\n const { componentPath, storyTitle, skipInteractionTests = false, overwrite = false, dryRun = false } = options;\n\n // Resolve component file\n const resolvedPath = resolveComponentPath(componentPath);\n if (!resolvedPath) {\n throw new Error(`Component file not found: ${componentPath}`);\n }\n\n // Check for existing story\n const storyPath = resolvedPath.replace(/\\.tsx?$/, STORY_FILE_SUFFIX);\n if (fs.existsSync(storyPath) && !overwrite) {\n throw new Error(`Story already exists: ${storyPath}. Use --overwrite to replace.`);\n }\n\n // Analyze\n const analysis = analyzeComponent(resolvedPath);\n if (!analysis) {\n throw new Error(`Could not analyze component at ${resolvedPath}. Ensure it exports a React component.`);\n }\n\n // Infer title\n const title = storyTitle ?? inferStoryTitle(resolvedPath);\n const importPath = `./${analysis.fileName}`;\n\n // Generate\n const content = generateStoryContent({\n analysis,\n storyTitle: title,\n importPath,\n skipInteractionTests,\n });\n\n // Determine which stories were generated\n const storiesGenerated = collectStoriesGenerated(analysis, skipInteractionTests);\n\n // Write\n let written = false;\n if (!dryRun) {\n const dir = path.dirname(storyPath);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(storyPath, content, 'utf-8');\n written = true;\n }\n\n return { storyPath, content, analysis, storiesGenerated, written };\n}\n\nfunction resolveComponentPath(componentPath: string): string | null {\n const resolved = path.resolve(componentPath);\n\n if (fs.existsSync(resolved)) return resolved;\n\n for (const ext of COMPONENT_EXTENSIONS) {\n const withExt = resolved + ext;\n if (fs.existsSync(withExt)) return withExt;\n }\n\n return null;\n}\n\nfunction collectStoriesGenerated(analysis: ComponentAnalysis, skipInteractionTests: boolean): string[] {\n const stories = ['Default'];\n\n if (analysis.props.some((p) => p.name === 'size' && p.unionValues?.length)) {\n stories.push('Sizes');\n }\n if (analysis.props.some((p) => p.name === 'variant' && p.unionValues?.length)) {\n stories.push('Variants');\n }\n if (analysis.props.some((p) => p.name === 'colorPalette' && p.unionValues?.length)) {\n stories.push('ColorPalettes');\n }\n if (analysis.props.some((p) => p.name === 'disabled' || p.name === 'isDisabled')) {\n stories.push('Disabled');\n }\n\n if (!skipInteractionTests) {\n stories.push('RendersCorrectly', 'AccessibilityAudit');\n if (analysis.props.some(\n (p) => p.isCallback && ['onClick', 'onPress', 'onSubmit', 'onClose'].includes(p.name)\n )) {\n stories.push('ClickInteraction', 'KeyboardNavigation');\n }\n }\n\n return stories;\n}\n","import { watch as chokidarWatch } from 'chokidar';\nimport * as path from 'node:path';\nimport * as fs from 'node:fs';\n\nimport {\n COMPONENT_EXTENSIONS,\n IGNORED_DIRS,\n STORY_FILE_SUFFIX,\n DEFAULT_DEBOUNCE_MS,\n} from '../utils/constants';\nimport type { WatchOptions } from '../utils/types';\nimport { forgeStory } from '../api/forge-story';\n\nexport interface WatchHandle {\n close(): Promise<void>;\n}\n\nexport type WatchEvent =\n | { type: 'ready' }\n | { type: 'generate'; file: string; storyPath: string }\n | { type: 'update'; file: string; storyPath: string }\n | { type: 'error'; file: string; error: Error };\n\nexport type WatchCallback = (event: WatchEvent) => void;\n\n/**\n * Watch a directory for component changes and auto-generate stories.\n * Returns a handle with .close() for cleanup.\n */\nexport function watchDirectory(\n options: WatchOptions,\n callback?: WatchCallback,\n): WatchHandle {\n const {\n dir,\n ignore = [],\n debounceMs = DEFAULT_DEBOUNCE_MS,\n skipInteractionTests = false,\n } = options;\n\n const absoluteDir = path.resolve(dir);\n\n if (!fs.existsSync(absoluteDir)) {\n throw new Error(`Watch directory not found: ${absoluteDir}`);\n }\n\n const extensions = COMPONENT_EXTENSIONS.map((e) => e.replace('.', ''));\n const globPattern = `**/*.{${extensions.join(',')}}`;\n\n const ignored = [\n ...IGNORED_DIRS.map((d) => `**/${d}/**`),\n '**/*.spec.*',\n '**/*.test.*',\n '**/*.stories.*',\n '**/*.styles.*',\n '**/*.style.*',\n '**/index.{ts,tsx}',\n ...ignore,\n ];\n\n const pending = new Map<string, ReturnType<typeof setTimeout>>();\n\n const watcher = chokidarWatch(globPattern, {\n cwd: absoluteDir,\n ignored,\n ignoreInitial: true,\n awaitWriteFinish: {\n stabilityThreshold: 100,\n pollInterval: 50,\n },\n });\n\n const processChange = async (relativePath: string) => {\n const filePath = path.join(absoluteDir, relativePath);\n const storyPath = filePath.replace(/\\.tsx?$/, STORY_FILE_SUFFIX);\n const isUpdate = fs.existsSync(storyPath);\n\n try {\n await forgeStory({\n componentPath: filePath,\n skipInteractionTests,\n overwrite: true,\n quiet: true,\n });\n\n callback?.({\n type: isUpdate ? 'update' : 'generate',\n file: filePath,\n storyPath,\n });\n } catch (err) {\n callback?.({\n type: 'error',\n file: filePath,\n error: err as Error,\n });\n }\n };\n\n const debouncedProcess = (relativePath: string) => {\n if (pending.has(relativePath)) {\n clearTimeout(pending.get(relativePath)!);\n }\n\n pending.set(\n relativePath,\n setTimeout(() => {\n pending.delete(relativePath);\n processChange(relativePath);\n }, debounceMs),\n );\n };\n\n watcher.on('change', debouncedProcess);\n watcher.on('add', debouncedProcess);\n watcher.on('ready', () => callback?.({ type: 'ready' }));\n\n return {\n async close() {\n for (const timeout of pending.values()) {\n clearTimeout(timeout);\n }\n pending.clear();\n await watcher.close();\n },\n };\n}\n","import * as path from 'node:path';\n\nimport type { ForgeStoriesOptions, CoverageReport } from '../utils/types';\nimport { scanDirectory } from '../core/scan-directory';\nimport { scoreCoverage } from '../core/score-coverage';\nimport { forgeStory } from './forge-story';\n\nexport interface ForgeStoriesResult {\n generated: number;\n failed: number;\n alreadyCovered: number;\n notAnalyzable: number;\n total: number;\n coverage: CoverageReport;\n errors: Array<{ file: string; error: string }>;\n}\n\n/**\n * High-level: scan a directory, generate stories for all uncovered components.\n */\nexport async function forgeStories(options: ForgeStoriesOptions): Promise<ForgeStoriesResult> {\n const {\n dir,\n skipInteractionTests = false,\n overwrite = false,\n dryRun = false,\n includeComponentTests = false,\n } = options;\n\n // Import forgeTest lazily to avoid circular dependency at startup\n const { forgeTest } = includeComponentTests\n ? await import('./forge-test')\n : { forgeTest: null };\n\n const absoluteDir = path.resolve(dir);\n const scan = await scanDirectory(absoluteDir);\n\n let generated = 0;\n let failed = 0;\n const errors: Array<{ file: string; error: string }> = [];\n\n const filesToProcess = overwrite\n ? [...scan.withoutStories, ...scan.withStories]\n : scan.withoutStories;\n\n for (const filePath of filesToProcess) {\n try {\n await forgeStory({\n componentPath: filePath,\n skipInteractionTests,\n overwrite: true,\n dryRun,\n quiet: true,\n });\n generated++;\n\n // Generate Playwright component tests if requested\n if (forgeTest) {\n try {\n await forgeTest({\n componentPath: filePath,\n overwrite: true,\n dryRun,\n quiet: true,\n });\n } catch {\n // Non-fatal — story is the primary artifact\n }\n }\n } catch (err) {\n failed++;\n errors.push({\n file: filePath,\n error: (err as Error).message,\n });\n }\n }\n\n const totalAnalyzable = scan.total - scan.notAnalyzable.length;\n // When overwriting, generated count includes previously-covered files\n const totalCovered = overwrite\n ? generated\n : scan.withStories.length + generated;\n const coverage = scoreCoverage(totalCovered, totalAnalyzable);\n\n return {\n generated,\n failed,\n alreadyCovered: scan.withStories.length,\n notAnalyzable: scan.notAnalyzable.length,\n total: totalAnalyzable,\n coverage,\n errors,\n };\n}\n"],"mappings":"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","names":["path","relative","toPascalCase","fs","path","path","fs","path"]}