uilint-eslint 0.2.76 → 0.2.77
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/index.d.ts +7 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/rules/consistent-dark-mode.js.map +1 -1
- package/dist/rules/enforce-absolute-imports.js.map +1 -1
- package/dist/rules/no-any-in-props.js.map +1 -1
- package/dist/rules/no-direct-store-import.js.map +1 -1
- package/dist/rules/no-mixed-component-libraries.js.map +1 -1
- package/dist/rules/no-prop-drilling-depth.js.map +1 -1
- package/dist/rules/no-secrets-in-code.js.map +1 -1
- package/dist/rules/no-semantic-duplicates.js.map +1 -1
- package/dist/rules/prefer-tailwind.js.map +1 -1
- package/dist/rules/prefer-zustand-state-management.js.map +1 -1
- package/dist/rules/require-input-validation.js.map +1 -1
- package/dist/rules/require-test-coverage.js.map +1 -1
- package/dist/rules/semantic-vision.js.map +1 -1
- package/dist/rules/semantic.js +1 -0
- package/dist/rules/semantic.js.map +1 -1
- package/dist/rules/zustand-use-selectors.js.map +1 -1
- package/package.json +2 -2
- package/src/rules/semantic/index.ts +1 -0
- package/src/utils/create-rule.ts +8 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/create-rule.ts","../../src/rules/require-test-coverage/index.ts","../../src/rules/require-test-coverage/lib/file-categorizer.ts","../../src/rules/require-test-coverage/lib/dependency-graph.ts","../../src/rules/require-test-coverage/lib/export-resolver.ts","../../src/rules/require-test-coverage/lib/coverage-aggregator.ts","../../src/rules/require-test-coverage/lib/jsx-coverage-analyzer.ts","../../src/rules/require-test-coverage/lib/chunk-analyzer.ts"],"sourcesContent":["/**\n * Rule creation helper using @typescript-eslint/utils\n */\n\nimport { ESLintUtils } from \"@typescript-eslint/utils\";\n\nexport const createRule = ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/peter-suggate/uilint/blob/main/packages/uilint-eslint/docs/rules/${name}.md`\n);\n\n/**\n * Schema for prompting user to configure a rule option in the CLI\n */\nexport interface OptionFieldSchema {\n /** Field name in the options object */\n key: string;\n /** Display label for the prompt */\n label: string;\n /** Prompt type */\n type: \"text\" | \"number\" | \"boolean\" | \"select\" | \"multiselect\";\n /** Default value */\n defaultValue: unknown;\n /** Placeholder text (for text/number inputs) */\n placeholder?: string;\n /** Options for select/multiselect */\n options?: Array<{ value: string | number; label: string }>;\n /** Description/hint for the field */\n description?: string;\n}\n\n/**\n * Schema describing how to prompt for rule options during installation\n */\nexport interface RuleOptionSchema {\n /** Fields that can be configured for this rule */\n fields: OptionFieldSchema[];\n}\n\n/**\n * External requirement that a rule needs to function\n */\nexport interface RuleRequirement {\n /** Requirement type for programmatic checks */\n type: \"ollama\" | \"git\" | \"coverage\" | \"semantic-index\" | \"styleguide\";\n /** Human-readable description */\n description: string;\n /** Optional: how to satisfy the requirement */\n setupHint?: string;\n}\n\n/**\n * Rule migration definition for updating rule options between versions\n */\nexport interface RuleMigration {\n /** Source version (semver) */\n from: string;\n /** Target version (semver) */\n to: string;\n /** Human-readable description of what changed */\n description: string;\n /** Function to migrate options from old format to new format */\n migrate: (oldOptions: unknown[]) => unknown[];\n /** Whether this migration contains breaking changes */\n breaking?: boolean;\n}\n\n/**\n * Colocated rule metadata - exported alongside each rule\n *\n * This structure keeps all rule metadata in the same file as the rule implementation,\n * making it easy to maintain and extend as new rules are added.\n */\nexport interface RuleMeta {\n /** Rule identifier (e.g., \"consistent-dark-mode\") - must match filename */\n id: string;\n\n /** Semantic version of the rule (e.g., \"1.0.0\") */\n version: string;\n\n /** Display name for CLI (e.g., \"No Arbitrary Tailwind\") */\n name: string;\n\n /** Short description for CLI selection prompts (one line) */\n description: string;\n\n /** Default severity level */\n defaultSeverity: \"error\" | \"warn\" | \"off\";\n\n /** Category for grouping in CLI */\n category: \"static\" | \"semantic\";\n\n /** Icon for display in CLI/UI (emoji or icon name) */\n icon?: string;\n\n /** Short hint about the rule type/requirements */\n hint?: string;\n\n /** Whether rule is enabled by default during install */\n defaultEnabled?: boolean;\n\n /** External requirements the rule needs */\n requirements?: RuleRequirement[];\n\n /** Instructions to show after installation */\n postInstallInstructions?: string;\n\n /** Framework compatibility */\n frameworks?: (\"next\" | \"vite\" | \"cra\" | \"remix\")[];\n\n /** Whether this rule requires a styleguide file */\n requiresStyleguide?: boolean;\n\n /** Default options for the rule (passed as second element in ESLint config) */\n defaultOptions?: unknown[];\n\n /** Schema for prompting user to configure options during install */\n optionSchema?: RuleOptionSchema;\n\n /**\n * Detailed documentation in markdown format.\n * Should include:\n * - What the rule does\n * - Why it's useful\n * - Examples of incorrect and correct code\n * - Configuration options explained\n */\n docs: string;\n\n /**\n * Internal utility dependencies that this rule requires.\n * When the rule is copied to a target project, these utilities\n * will be transformed to import from \"uilint-eslint\" instead\n * of relative paths.\n *\n * Example: [\"coverage-aggregator\", \"dependency-graph\"]\n */\n internalDependencies?: string[];\n\n /**\n * Whether this rule is directory-based (has lib/ folder with utilities).\n * Directory-based rules are installed as folders with index.ts and lib/ subdirectory.\n * Single-file rules are installed as single .ts files.\n *\n * When true, ESLint config imports will use:\n * ./.uilint/rules/rule-id/index.js\n * When false (default):\n * ./.uilint/rules/rule-id.js\n */\n isDirectoryBased?: boolean;\n\n /**\n * Migrations for updating rule options between versions.\n * Migrations are applied in order to transform options from older versions.\n */\n migrations?: RuleMigration[];\n\n /**\n * Which UI plugin should handle this rule.\n * Defaults based on category:\n * - \"static\" category → \"eslint\" plugin\n * - \"semantic\" category → \"semantic\" plugin\n *\n * Special cases:\n * - \"vision\" for semantic-vision rule\n */\n plugin?: \"eslint\" | \"vision\" | \"semantic\";\n\n /**\n * Custom inspector panel ID to use for this rule's issues.\n * If not specified, uses the plugin's default issue inspector.\n *\n * Examples:\n * - \"vision-issue\" for VisionIssueInspector\n * - \"duplicates\" for DuplicatesInspector\n * - \"semantic-issue\" for SemanticIssueInspector\n */\n customInspector?: string;\n\n /**\n * Custom heatmap color for this rule's issues.\n * CSS color value (hex, rgb, hsl, or named color).\n * If not specified, uses severity-based coloring.\n */\n heatmapColor?: string;\n}\n\n/**\n * Helper to define rule metadata with type safety\n */\nexport function defineRuleMeta(meta: RuleMeta): RuleMeta {\n return meta;\n}\n","/**\n * Rule: require-test-coverage\n *\n * Enforces that source files have test coverage above a configurable threshold.\n * Checks for:\n * - Existence of test files\n * - Coverage data in Istanbul JSON format\n * - Statement coverage percentage\n */\n\nimport { createRule, defineRuleMeta } from \"../../utils/create-rule.js\";\nimport { existsSync, readFileSync, statSync } from \"fs\";\nimport { dirname, join, basename, relative } from \"path\";\nimport { execSync } from \"child_process\";\nimport {\n aggregateCoverage,\n type IstanbulCoverage as AggregatorIstanbulCoverage,\n} from \"./lib/coverage-aggregator.js\";\nimport {\n analyzeJSXElementCoverage,\n type IstanbulCoverage as JSXAnalyzerIstanbulCoverage,\n} from \"./lib/jsx-coverage-analyzer.js\";\nimport { analyzeChunks, getChunkThreshold } from \"./lib/chunk-analyzer.js\";\nimport type { TSESTree } from \"@typescript-eslint/utils\";\n\n/**\n * Simple glob pattern matching function\n * Supports: *, **, ?\n */\nfunction simpleGlobMatch(pattern: string, path: string): boolean {\n // Normalize path separators\n const normalizedPath = path.replace(/\\\\/g, \"/\");\n const normalizedPattern = pattern.replace(/\\\\/g, \"/\");\n\n // Escape regex special chars except our glob patterns\n let regexStr = normalizedPattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\") // Escape regex special chars\n .replace(/\\*\\*/g, \"{{GLOBSTAR}}\") // Placeholder for **\n .replace(/\\*/g, \"[^/]*\") // * matches anything except /\n .replace(/\\?/g, \"[^/]\") // ? matches single char except /\n .replace(/{{GLOBSTAR}}/g, \".*\"); // ** matches anything including /\n\n // Add anchors\n const regex = new RegExp(`^${regexStr}$`);\n return regex.test(normalizedPath);\n}\n\ntype MessageIds =\n | \"noCoverage\"\n | \"belowThreshold\"\n | \"noCoverageData\"\n | \"belowAggregateThreshold\"\n | \"jsxBelowThreshold\"\n | \"chunkBelowThreshold\"\n | \"untestedFunction\";\n\ntype SeverityLevel = \"error\" | \"warn\" | \"off\";\n\ntype Options = [\n {\n /** Path to coverage JSON file. Default: \"coverage/coverage-final.json\" */\n coveragePath?: string;\n /** Coverage threshold percentage. Default: 80 */\n threshold?: number;\n /** Pattern-specific thresholds */\n thresholdsByPattern?: Array<{ pattern: string; threshold: number }>;\n /** Severity levels for different issue types */\n severity?: {\n noCoverage?: SeverityLevel;\n belowThreshold?: SeverityLevel;\n };\n /** Patterns to detect test files. Default: [\".test.ts\", \".test.tsx\", \".spec.ts\", \".spec.tsx\", \"__tests__/\"] */\n testPatterns?: string[];\n /** Glob patterns for files to ignore. Default: [\"**\\/*.d.ts\", \"**\\/index.ts\"] */\n ignorePatterns?: string[];\n /** Mode: \"all\" checks all code, \"changed\" only checks git-changed lines. Default: \"all\" */\n mode?: \"all\" | \"changed\";\n /** Base branch for \"changed\" mode. Default: \"main\" */\n baseBranch?: string;\n /** Aggregate coverage threshold for components. Default: 70 */\n aggregateThreshold?: number;\n /** Severity for aggregate coverage check. Default: \"warn\" */\n aggregateSeverity?: SeverityLevel;\n /** JSX element coverage threshold percentage. Default: 50 */\n jsxThreshold?: number;\n /** Severity for JSX element coverage check. Default: \"warn\" */\n jsxSeverity?: SeverityLevel;\n /** Enable chunk-level coverage reporting (replaces file-level). Default: false */\n chunkCoverage?: boolean;\n /** Threshold for strict categories (utility/hook/store). Default: 80 */\n chunkThreshold?: number;\n /** Focus on non-React code with relaxed thresholds for components. Default: false */\n focusNonReact?: boolean;\n /** Threshold for relaxed categories (component/handler). Default: 50 */\n relaxedThreshold?: number;\n /** Severity for chunk coverage. Default: \"warn\" */\n chunkSeverity?: SeverityLevel;\n /** Minimum statements in file to require coverage. Default: 5 */\n minStatements?: number;\n }\n];\n\n/**\n * Istanbul coverage JSON format\n */\ninterface IstanbulCoverage {\n [filePath: string]: {\n path: string;\n statementMap: {\n [key: string]: {\n start: { line: number; column: number };\n end: { line: number; column: number };\n };\n };\n fnMap: {\n [key: string]: {\n name: string;\n decl: {\n start: { line: number; column: number };\n end: { line: number; column: number };\n };\n loc: {\n start: { line: number; column: number };\n end: { line: number; column: number };\n };\n };\n };\n branchMap: {\n [key: string]: {\n loc: {\n start: { line: number; column: number };\n end: { line: number; column: number };\n };\n type: string;\n locations: Array<{\n start: { line: number; column: number };\n end: { line: number; column: number };\n }>;\n };\n };\n s: { [key: string]: number }; // Statement hit counts\n f: { [key: string]: number }; // Function hit counts\n b: { [key: string]: number[] }; // Branch hit counts\n };\n}\n\n/**\n * Rule metadata\n */\nexport const meta = defineRuleMeta({\n id: \"require-test-coverage\",\n version: \"1.0.0\",\n name: \"Require Test Coverage\",\n description: \"Enforce that source files have adequate test coverage\",\n defaultSeverity: \"warn\",\n category: \"static\",\n icon: \"🧪\",\n hint: \"Ensures code has tests\",\n defaultEnabled: true,\n isDirectoryBased: true,\n requirements: [\n {\n type: \"coverage\",\n description: \"Requires test coverage data\",\n setupHint: \"Run tests with coverage: npm test -- --coverage\",\n },\n ],\n defaultOptions: [\n {\n coveragePath: \"coverage/coverage-final.json\",\n threshold: 80,\n thresholdsByPattern: [],\n severity: {\n noCoverage: \"error\",\n belowThreshold: \"warn\",\n },\n testPatterns: [\n \".test.ts\",\n \".test.tsx\",\n \".spec.ts\",\n \".spec.tsx\",\n \"__tests__/\",\n ],\n ignorePatterns: [\"**/*.d.ts\", \"**/index.ts\"],\n mode: \"all\",\n baseBranch: \"main\",\n },\n ],\n optionSchema: {\n fields: [\n {\n key: \"threshold\",\n label: \"Coverage threshold\",\n type: \"number\",\n defaultValue: 80,\n description: \"Minimum coverage percentage required (0-100)\",\n },\n {\n key: \"coveragePath\",\n label: \"Coverage file path\",\n type: \"text\",\n defaultValue: \"coverage/coverage-final.json\",\n description: \"Path to Istanbul coverage JSON file\",\n },\n {\n key: \"mode\",\n label: \"Mode\",\n type: \"select\",\n defaultValue: \"all\",\n options: [\n { value: \"all\", label: \"Check all code\" },\n { value: \"changed\", label: \"Only check changed lines\" },\n ],\n description: \"Whether to check all code or only git-changed lines\",\n },\n {\n key: \"chunkCoverage\",\n label: \"Enable chunk-level coverage\",\n type: \"boolean\",\n defaultValue: true,\n description:\n \"Report coverage for individual functions instead of file level\",\n },\n {\n key: \"focusNonReact\",\n label: \"Focus on non-React code\",\n type: \"boolean\",\n defaultValue: false,\n description:\n \"Apply strict thresholds to utilities/stores/hooks, relaxed to components\",\n },\n {\n key: \"chunkThreshold\",\n label: \"Chunk coverage threshold\",\n type: \"number\",\n defaultValue: 80,\n description:\n \"Minimum coverage for utility/hook/store chunks (0-100)\",\n },\n {\n key: \"relaxedThreshold\",\n label: \"Relaxed threshold for React code\",\n type: \"number\",\n defaultValue: 50,\n description:\n \"Threshold for components/handlers when focusNonReact is enabled\",\n },\n {\n key: \"minStatements\",\n label: \"Minimum statements for coverage\",\n type: \"number\",\n defaultValue: 5,\n description:\n \"Files with fewer statements are exempt from coverage requirements\",\n },\n ],\n },\n docs: `\n## What it does\n\nEnforces that source files have test coverage above a configurable threshold.\nIt checks for:\n- Existence of corresponding test files\n- Coverage data in Istanbul JSON format\n- Statement coverage percentage meeting the threshold\n\n## Why it's useful\n\n- **Quality Assurance**: Ensures critical code is tested\n- **Catch Regressions**: Prevents merging untested changes\n- **Configurable**: Different thresholds for different file patterns\n- **Git Integration**: Can focus only on changed lines\n\n## Configuration\n\n\\`\\`\\`js\n// eslint.config.js\n\"uilint/require-test-coverage\": [\"warn\", {\n coveragePath: \"coverage/coverage-final.json\",\n threshold: 80,\n thresholdsByPattern: [\n { pattern: \"**/utils/*.ts\", threshold: 90 },\n { pattern: \"**/generated/**\", threshold: 0 },\n ],\n severity: {\n noCoverage: \"error\",\n belowThreshold: \"warn\",\n },\n testPatterns: [\".test.ts\", \".test.tsx\", \".spec.ts\", \".spec.tsx\", \"__tests__/\"],\n ignorePatterns: [\"**/*.d.ts\", \"**/index.ts\"],\n mode: \"all\", // or \"changed\"\n baseBranch: \"main\" // for \"changed\" mode\n}]\n\\`\\`\\`\n\n## Examples\n\n### Below threshold:\n\\`\\`\\`ts\n// src/api.ts - 40% coverage (threshold: 80%)\nexport function fetchData() { ... } // Warning: Coverage below threshold\n\\`\\`\\`\n`,\n});\n\n// Cache for loaded coverage data\nlet coverageCache: {\n projectRoot: string;\n coveragePath: string;\n mtime: number;\n data: IstanbulCoverage;\n} | null = null;\n\n/**\n * Clear the coverage cache (useful for testing)\n */\nexport function clearCoverageCache(): void {\n coverageCache = null;\n}\n\n/**\n * Find project root by looking for package.json\n */\nfunction findProjectRoot(startPath: string): string {\n let current = startPath;\n let lastPackageJson: string | null = null;\n\n while (current !== dirname(current)) {\n if (existsSync(join(current, \"package.json\"))) {\n lastPackageJson = current;\n }\n // If we find a coverage directory, use this as project root\n if (existsSync(join(current, \"coverage\"))) {\n return current;\n }\n current = dirname(current);\n }\n\n return lastPackageJson || startPath;\n}\n\n/**\n * Load coverage data from JSON file\n */\nfunction loadCoverage(\n projectRoot: string,\n coveragePath: string\n): IstanbulCoverage | null {\n const fullPath = join(projectRoot, coveragePath);\n\n if (!existsSync(fullPath)) {\n return null;\n }\n\n try {\n const stat = statSync(fullPath);\n const mtime = stat.mtimeMs;\n\n // Check cache\n if (\n coverageCache &&\n coverageCache.projectRoot === projectRoot &&\n coverageCache.coveragePath === coveragePath &&\n coverageCache.mtime === mtime\n ) {\n return coverageCache.data;\n }\n\n const content = readFileSync(fullPath, \"utf-8\");\n const data = JSON.parse(content) as IstanbulCoverage;\n\n // Update cache\n coverageCache = {\n projectRoot,\n coveragePath,\n mtime,\n data,\n };\n\n return data;\n } catch {\n return null;\n }\n}\n\n/**\n * Calculate statement coverage percentage for a file\n */\nfunction calculateCoverage(fileCoverage: IstanbulCoverage[string]): number {\n const statements = fileCoverage.s;\n const keys = Object.keys(statements);\n\n if (keys.length === 0) {\n return 100; // No statements = 100% covered\n }\n\n const covered = keys.filter((key) => statements[key] > 0).length;\n return Math.round((covered / keys.length) * 100);\n}\n\n/**\n * Check if a file matches any of the ignore patterns\n */\nfunction shouldIgnore(filePath: string, ignorePatterns: string[]): boolean {\n for (const pattern of ignorePatterns) {\n if (simpleGlobMatch(pattern, filePath)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Get threshold for a file, checking pattern-specific thresholds first\n */\nfunction getThreshold(\n filePath: string,\n globalThreshold: number,\n thresholdsByPattern: Array<{ pattern: string; threshold: number }>\n): number {\n for (const { pattern, threshold } of thresholdsByPattern) {\n if (simpleGlobMatch(pattern, filePath)) {\n return threshold;\n }\n }\n return globalThreshold;\n}\n\n/**\n * Get changed line numbers from git diff\n */\nfunction getChangedLines(\n projectRoot: string,\n filePath: string,\n baseBranch: string\n): Set<number> | null {\n try {\n const relPath = relative(projectRoot, filePath);\n const diff = execSync(`git diff ${baseBranch}...HEAD -- \"${relPath}\"`, {\n cwd: projectRoot,\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n const changedLines = new Set<number>();\n const lines = diff.split(\"\\n\");\n\n let currentLine = 0;\n for (const line of lines) {\n // Parse hunk header: @@ -start,count +start,count @@\n const hunkMatch = line.match(/^@@ -\\d+(?:,\\d+)? \\+(\\d+)(?:,\\d+)? @@/);\n if (hunkMatch) {\n currentLine = parseInt(hunkMatch[1], 10);\n continue;\n }\n\n if (line.startsWith(\"+\") && !line.startsWith(\"+++\")) {\n changedLines.add(currentLine);\n currentLine++;\n } else if (line.startsWith(\"-\") && !line.startsWith(\"---\")) {\n // Deleted line, don't increment\n } else if (!line.startsWith(\"\\\\\")) {\n currentLine++;\n }\n }\n\n return changedLines;\n } catch {\n // Git command failed, return null to fall back to \"all\" mode\n return null;\n }\n}\n\n/**\n * Calculate coverage for only changed lines\n */\nfunction calculateChangedLinesCoverage(\n fileCoverage: IstanbulCoverage[string],\n changedLines: Set<number>\n): number {\n const statementMap = fileCoverage.statementMap;\n const statements = fileCoverage.s;\n\n let relevantStatements = 0;\n let coveredStatements = 0;\n\n for (const [key, location] of Object.entries(statementMap)) {\n // Check if any line of this statement was changed\n let isRelevant = false;\n for (let line = location.start.line; line <= location.end.line; line++) {\n if (changedLines.has(line)) {\n isRelevant = true;\n break;\n }\n }\n\n if (isRelevant) {\n relevantStatements++;\n if (statements[key] > 0) {\n coveredStatements++;\n }\n }\n }\n\n if (relevantStatements === 0) {\n return 100; // No changed statements = 100% covered\n }\n\n return Math.round((coveredStatements / relevantStatements) * 100);\n}\n\n/**\n * Normalize file path for coverage lookup\n * Coverage JSON may store paths in different formats\n */\nfunction findCoverageForFile(\n coverage: IstanbulCoverage,\n filePath: string,\n projectRoot: string\n): IstanbulCoverage[string] | null {\n // Try exact match first\n if (coverage[filePath]) {\n return coverage[filePath];\n }\n\n // Try relative path from project root\n const relPath = relative(projectRoot, filePath);\n if (coverage[relPath]) {\n return coverage[relPath];\n }\n\n // Try with leading slash (common in Istanbul output)\n const withSlash = \"/\" + relPath;\n if (coverage[withSlash]) {\n return coverage[withSlash];\n }\n\n // Try just the src-relative path\n const srcMatch = relPath.match(/src\\/.+$/);\n if (srcMatch) {\n const srcPath = \"/\" + srcMatch[0];\n if (coverage[srcPath]) {\n return coverage[srcPath];\n }\n }\n\n return null;\n}\n\nexport default createRule<Options, MessageIds>({\n name: \"require-test-coverage\",\n meta: {\n type: \"suggestion\",\n docs: {\n description: \"Enforce that source files have adequate test coverage\",\n },\n messages: {\n noCoverage:\n \"No coverage data found for '{{fileName}}' in coverage report\",\n belowThreshold:\n \"Coverage for '{{fileName}}' is {{coverage}}%, below threshold of {{threshold}}%\",\n noCoverageData:\n \"Coverage data not found at '{{coveragePath}}'. Run tests with coverage first.\",\n belowAggregateThreshold:\n \"Aggregate coverage ({{coverage}}%) is below threshold ({{threshold}}%). \" +\n \"Includes {{fileCount}} files. Lowest: {{lowestFile}} ({{lowestCoverage}}%)\",\n jsxBelowThreshold:\n \"<{{tagName}}> element coverage is {{coverage}}%, below threshold of {{threshold}}%\",\n chunkBelowThreshold:\n \"{{category}} '{{name}}' has {{coverage}}% coverage, below {{threshold}}% threshold\",\n untestedFunction:\n \"Function '{{name}}' ({{category}}) is not covered by tests\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n coveragePath: {\n type: \"string\",\n description: \"Path to coverage JSON file\",\n },\n threshold: {\n type: \"number\",\n minimum: 0,\n maximum: 100,\n description: \"Coverage threshold percentage\",\n },\n thresholdsByPattern: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n pattern: { type: \"string\" },\n threshold: { type: \"number\", minimum: 0, maximum: 100 },\n },\n required: [\"pattern\", \"threshold\"],\n additionalProperties: false,\n },\n description: \"Pattern-specific thresholds\",\n },\n severity: {\n type: \"object\",\n properties: {\n noCoverage: { type: \"string\", enum: [\"error\", \"warn\", \"off\"] },\n belowThreshold: {\n type: \"string\",\n enum: [\"error\", \"warn\", \"off\"],\n },\n },\n additionalProperties: false,\n },\n testPatterns: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Patterns to detect test files\",\n },\n ignorePatterns: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Glob patterns for files to ignore\",\n },\n mode: {\n type: \"string\",\n enum: [\"all\", \"changed\"],\n description: \"Check all code or only changed lines\",\n },\n baseBranch: {\n type: \"string\",\n description: \"Base branch for changed mode\",\n },\n aggregateThreshold: {\n type: \"number\",\n minimum: 0,\n maximum: 100,\n description:\n \"Aggregate coverage threshold for components (includes dependencies)\",\n },\n aggregateSeverity: {\n type: \"string\",\n enum: [\"error\", \"warn\", \"off\"],\n description: \"Severity for aggregate coverage check\",\n },\n jsxThreshold: {\n type: \"number\",\n minimum: 0,\n maximum: 100,\n description:\n \"JSX element coverage threshold percentage (includes event handlers)\",\n },\n jsxSeverity: {\n type: \"string\",\n enum: [\"error\", \"warn\", \"off\"],\n description: \"Severity for JSX element coverage check\",\n },\n chunkCoverage: {\n type: \"boolean\",\n description:\n \"Enable chunk-level coverage reporting (replaces file-level)\",\n },\n chunkThreshold: {\n type: \"number\",\n minimum: 0,\n maximum: 100,\n description:\n \"Threshold for strict categories (utility/hook/store)\",\n },\n focusNonReact: {\n type: \"boolean\",\n description:\n \"Focus on non-React code with relaxed thresholds for components\",\n },\n relaxedThreshold: {\n type: \"number\",\n minimum: 0,\n maximum: 100,\n description: \"Threshold for relaxed categories (component/handler)\",\n },\n chunkSeverity: {\n type: \"string\",\n enum: [\"error\", \"warn\", \"off\"],\n description: \"Severity for chunk coverage check\",\n },\n },\n additionalProperties: false,\n },\n ],\n },\n defaultOptions: [\n {\n coveragePath: \"coverage/coverage-final.json\",\n threshold: 80,\n thresholdsByPattern: [],\n severity: {\n noCoverage: \"error\",\n belowThreshold: \"warn\",\n },\n testPatterns: [\n \".test.ts\",\n \".test.tsx\",\n \".spec.ts\",\n \".spec.tsx\",\n \"__tests__/\",\n ],\n ignorePatterns: [\"**/*.d.ts\", \"**/index.ts\"],\n mode: \"all\",\n baseBranch: \"main\",\n aggregateThreshold: 70,\n aggregateSeverity: \"warn\",\n jsxThreshold: 50,\n jsxSeverity: \"warn\",\n chunkCoverage: true,\n chunkThreshold: 80,\n focusNonReact: false,\n relaxedThreshold: 50,\n chunkSeverity: \"warn\",\n minStatements: 5,\n },\n ],\n create(context) {\n const options = context.options[0] || {};\n const coveragePath = options.coveragePath ?? \"coverage/coverage-final.json\";\n const threshold = options.threshold ?? 80;\n const thresholdsByPattern = options.thresholdsByPattern ?? [];\n const severity = {\n noCoverage: options.severity?.noCoverage ?? \"error\",\n belowThreshold: options.severity?.belowThreshold ?? \"warn\",\n };\n const aggregateThreshold = options.aggregateThreshold ?? 70;\n const aggregateSeverity = options.aggregateSeverity ?? \"warn\";\n const testPatterns = options.testPatterns ?? [\n \".test.ts\",\n \".test.tsx\",\n \".spec.ts\",\n \".spec.tsx\",\n \"__tests__/\",\n ];\n const ignorePatterns = options.ignorePatterns ?? [\n \"**/*.d.ts\",\n \"**/index.ts\",\n ];\n const mode = options.mode ?? \"all\";\n const baseBranch = options.baseBranch ?? \"main\";\n const jsxThreshold = options.jsxThreshold ?? 50;\n const jsxSeverity = options.jsxSeverity ?? \"warn\";\n const chunkCoverage = options.chunkCoverage ?? true;\n const chunkThreshold = options.chunkThreshold ?? 80;\n const focusNonReact = options.focusNonReact ?? false;\n const relaxedThreshold = options.relaxedThreshold ?? 50;\n const chunkSeverity = options.chunkSeverity ?? \"warn\";\n const minStatements = options.minStatements ?? 5;\n\n const filename = context.filename || context.getFilename();\n const projectRoot = findProjectRoot(dirname(filename));\n\n // Check if file should be ignored\n const relPath = relative(projectRoot, filename);\n if (shouldIgnore(relPath, ignorePatterns)) {\n return {};\n }\n\n // Skip test files themselves\n if (\n testPatterns.some((p) =>\n filename.includes(p.replace(\"__tests__/\", \"__tests__\"))\n )\n ) {\n return {};\n }\n\n // Track if we've already reported for this file\n let reported = false;\n\n // Collect JSX elements with their ancestors for element-level coverage\n const jsxElements: Array<{\n node: TSESTree.JSXElement;\n ancestors: TSESTree.Node[];\n }> = [];\n\n return {\n // Collect JSX elements for element-level coverage analysis\n JSXElement(node: TSESTree.JSXElement) {\n // Get ancestors using context.sourceCode (ESLint v9) or context.getAncestors (legacy)\n const ancestors = context.sourceCode?.getAncestors?.(node) ?? [];\n jsxElements.push({ node, ancestors });\n },\n\n \"Program:exit\"(node: TSESTree.Program) {\n if (reported) return;\n reported = true;\n\n // Load coverage data\n const coverage = loadCoverage(projectRoot, coveragePath);\n\n // Check if coverage data exists\n if (!coverage) {\n if (severity.noCoverage !== \"off\") {\n context.report({\n node,\n messageId: \"noCoverageData\",\n data: {\n coveragePath,\n },\n });\n }\n return;\n }\n\n // Find coverage for this file\n const fileCoverage = findCoverageForFile(\n coverage,\n filename,\n projectRoot\n );\n\n if (!fileCoverage) {\n // File is not in coverage report - this is OK if coverage data exists\n // but this specific file wasn't covered (different from no coverage data at all)\n return;\n }\n\n // Skip small files (fewer statements than minStatements threshold)\n const statementCount = Object.keys(fileCoverage.s).length;\n if (statementCount < minStatements) {\n return;\n }\n\n // Calculate coverage\n let coveragePercent: number;\n\n if (mode === \"changed\") {\n const changedLines = getChangedLines(\n projectRoot,\n filename,\n baseBranch\n );\n if (changedLines && changedLines.size > 0) {\n coveragePercent = calculateChangedLinesCoverage(\n fileCoverage,\n changedLines\n );\n } else {\n // No changed lines or git failed - use full coverage\n coveragePercent = calculateCoverage(fileCoverage);\n }\n } else {\n coveragePercent = calculateCoverage(fileCoverage);\n }\n\n // Get threshold for this file\n const fileThreshold = getThreshold(\n relPath,\n threshold,\n thresholdsByPattern\n );\n\n // Check if below threshold (file-level) - skipped when chunkCoverage is enabled\n if (\n !chunkCoverage &&\n severity.belowThreshold !== \"off\" &&\n coveragePercent < fileThreshold\n ) {\n context.report({\n node,\n messageId: \"belowThreshold\",\n data: {\n fileName: basename(filename),\n coverage: String(coveragePercent),\n threshold: String(fileThreshold),\n },\n });\n }\n\n // Check chunk-level coverage (replaces file-level when enabled)\n if (chunkCoverage && chunkSeverity !== \"off\" && fileCoverage) {\n const chunks = analyzeChunks(\n context.sourceCode.ast,\n filename,\n fileCoverage\n );\n\n // Report one message per chunk below threshold, highlighting just the declaration\n for (const chunk of chunks) {\n const chunkThresholdValue = getChunkThreshold(chunk, {\n focusNonReact,\n chunkThreshold,\n relaxedThreshold,\n });\n\n if (chunk.coverage.percentage < chunkThresholdValue) {\n const messageId =\n chunk.coverage.functionCalled\n ? \"chunkBelowThreshold\"\n : \"untestedFunction\";\n\n context.report({\n loc: chunk.declarationLoc,\n messageId,\n data: {\n name: chunk.name,\n category: chunk.category,\n coverage: String(chunk.coverage.percentage),\n threshold: String(chunkThresholdValue),\n },\n });\n }\n }\n }\n\n // Check aggregate coverage for component files (JSX)\n if (\n aggregateSeverity !== \"off\" &&\n (filename.endsWith(\".tsx\") || filename.endsWith(\".jsx\"))\n ) {\n // Check if file actually contains JSX by looking at the AST\n const hasJSX = checkForJSX(context.sourceCode.ast);\n\n if (hasJSX) {\n const aggregateResult = aggregateCoverage(\n filename,\n projectRoot,\n coverage as AggregatorIstanbulCoverage\n );\n\n if (aggregateResult.aggregateCoverage < aggregateThreshold) {\n const lowestFile = aggregateResult.lowestCoverageFile;\n context.report({\n node,\n messageId: \"belowAggregateThreshold\",\n data: {\n coverage: String(\n Math.round(aggregateResult.aggregateCoverage)\n ),\n threshold: String(aggregateThreshold),\n fileCount: String(aggregateResult.totalFiles),\n lowestFile: lowestFile\n ? basename(lowestFile.path)\n : \"N/A\",\n lowestCoverage: lowestFile\n ? String(Math.round(lowestFile.percentage))\n : \"N/A\",\n },\n });\n }\n }\n }\n\n // Check JSX element-level coverage\n if (\n jsxSeverity !== \"off\" &&\n jsxElements.length > 0 &&\n coverage\n ) {\n // Compute relative path for dataLoc (consistent with overlay matching)\n const fileRelPath = relPath.startsWith(\"/\") ? relPath : `/${relPath}`;\n\n for (const { node: jsxNode, ancestors } of jsxElements) {\n // Only check elements with event handlers (interactive elements)\n const hasEventHandlers = jsxNode.openingElement.attributes.some(\n (attr) =>\n attr.type === \"JSXAttribute\" &&\n attr.name.type === \"JSXIdentifier\" &&\n /^on[A-Z]/.test(attr.name.name)\n );\n\n // Skip non-interactive elements to reduce noise\n if (!hasEventHandlers) {\n continue;\n }\n\n const result = analyzeJSXElementCoverage(\n jsxNode,\n fileRelPath,\n coverage as JSXAnalyzerIstanbulCoverage,\n ancestors,\n projectRoot\n );\n\n if (result.coverage.percentage < jsxThreshold) {\n // Get the tag name for the error message\n const openingElement = jsxNode.openingElement;\n let tagName = \"unknown\";\n if (openingElement.name.type === \"JSXIdentifier\") {\n tagName = openingElement.name.name;\n } else if (openingElement.name.type === \"JSXMemberExpression\") {\n // For Foo.Bar, use \"Foo.Bar\"\n let current: TSESTree.JSXTagNameExpression = openingElement.name;\n const parts: string[] = [];\n while (current.type === \"JSXMemberExpression\") {\n if (current.property.type === \"JSXIdentifier\") {\n parts.unshift(current.property.name);\n }\n current = current.object;\n }\n if (current.type === \"JSXIdentifier\") {\n parts.unshift(current.name);\n }\n tagName = parts.join(\".\");\n }\n\n context.report({\n node: jsxNode,\n messageId: \"jsxBelowThreshold\",\n data: {\n tagName,\n coverage: String(result.coverage.percentage),\n threshold: String(jsxThreshold),\n dataLoc: result.dataLoc,\n },\n });\n }\n }\n }\n },\n };\n },\n});\n\n/**\n * Check if an AST contains JSX elements\n * Uses a visited set to avoid infinite recursion from circular references (e.g., parent pointers)\n */\nfunction checkForJSX(ast: unknown, visited: WeakSet<object> = new WeakSet()): boolean {\n if (!ast || typeof ast !== \"object\") return false;\n\n // Avoid circular references\n if (visited.has(ast as object)) return false;\n visited.add(ast as object);\n\n const node = ast as Record<string, unknown>;\n\n // Check if this node is JSX\n if (\n node.type === \"JSXElement\" ||\n node.type === \"JSXFragment\" ||\n node.type === \"JSXText\"\n ) {\n return true;\n }\n\n // Only traverse known AST child properties to avoid parent/token references\n const childKeys = [\"body\", \"declarations\", \"declaration\", \"expression\", \"expressions\",\n \"argument\", \"arguments\", \"callee\", \"elements\", \"properties\", \"value\", \"init\",\n \"consequent\", \"alternate\", \"test\", \"left\", \"right\", \"object\", \"property\",\n \"children\", \"openingElement\", \"closingElement\", \"attributes\"];\n\n for (const key of childKeys) {\n const child = node[key];\n if (child && typeof child === \"object\") {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (checkForJSX(item, visited)) return true;\n }\n } else {\n if (checkForJSX(child, visited)) return true;\n }\n }\n }\n\n return false;\n}\n","/**\n * File Categorizer\n *\n * Categorizes TypeScript/React files by their role in the codebase.\n * Used for smart weighting in coverage aggregation.\n *\n * Categories:\n * - core (1.0): hooks, components, services, stores - critical logic\n * - utility (0.5): formatters, validators, helpers - supporting code\n * - constant (0.25): config, constants, enums - static data\n * - type (0): .d.ts files, type-only exports - no runtime impact\n */\n\nimport { existsSync, readFileSync } from \"fs\";\nimport { basename } from \"path\";\nimport { parse } from \"@typescript-eslint/typescript-estree\";\nimport type { TSESTree } from \"@typescript-eslint/utils\";\n\nexport type FileCategory = \"core\" | \"utility\" | \"constant\" | \"type\";\n\nexport interface FileCategoryResult {\n category: FileCategory;\n weight: number;\n reason: string;\n}\n\nconst CATEGORY_WEIGHTS: Record<FileCategory, number> = {\n core: 1.0,\n utility: 0.5,\n constant: 0.25,\n type: 0,\n};\n\n/**\n * Categorize a TypeScript/React file by its role\n */\nexport function categorizeFile(\n filePath: string,\n _projectRoot: string\n): FileCategoryResult {\n const fileName = basename(filePath);\n\n // 1. Type definition files are always \"type\"\n if (filePath.endsWith(\".d.ts\")) {\n return {\n category: \"type\",\n weight: 0,\n reason: \"TypeScript declaration file (.d.ts)\",\n };\n }\n\n // 2. Check file name patterns for core files\n // Hooks: use*.ts or use*.tsx\n if (/^use[A-Z]/.test(fileName)) {\n return {\n category: \"core\",\n weight: 1.0,\n reason: \"React hook (use* pattern)\",\n };\n }\n\n // Services: *.service.ts\n if (/\\.service\\.(ts|tsx)$/.test(fileName)) {\n return {\n category: \"core\",\n weight: 1.0,\n reason: \"Service file (*.service.ts pattern)\",\n };\n }\n\n // Stores: *.store.ts\n if (/\\.store\\.(ts|tsx)$/.test(fileName)) {\n return {\n category: \"core\",\n weight: 1.0,\n reason: \"Store file (*.store.ts pattern)\",\n };\n }\n\n // API files: *.api.ts\n if (/\\.api\\.(ts|tsx)$/.test(fileName)) {\n return {\n category: \"core\",\n weight: 1.0,\n reason: \"API file (*.api.ts pattern)\",\n };\n }\n\n // 3. Parse AST to analyze exports\n if (!existsSync(filePath)) {\n return {\n category: \"utility\",\n weight: 0.5,\n reason: \"File not found, defaulting to utility\",\n };\n }\n\n let ast: TSESTree.Program;\n try {\n const content = readFileSync(filePath, \"utf-8\");\n ast = parse(content, {\n jsx: true,\n loc: true,\n range: true,\n });\n } catch {\n return {\n category: \"utility\",\n weight: 0.5,\n reason: \"Failed to parse file, defaulting to utility\",\n };\n }\n\n // Analyze the file's exports\n const analysis = analyzeExports(ast);\n\n // 4. Type-only files (only type/interface exports, no runtime code)\n if (analysis.hasOnlyTypeExports) {\n return {\n category: \"type\",\n weight: 0,\n reason: \"File contains only type/interface exports\",\n };\n }\n\n // 5. Check for JSX (React component)\n if (analysis.hasJSX) {\n return {\n category: \"core\",\n weight: 1.0,\n reason: \"React component (contains JSX)\",\n };\n }\n\n // 6. Constant files (only const/enum exports, no functions)\n if (analysis.hasOnlyConstantExports) {\n return {\n category: \"constant\",\n weight: 0.25,\n reason: \"File contains only constant/enum exports\",\n };\n }\n\n // 7. Default to utility\n return {\n category: \"utility\",\n weight: 0.5,\n reason: \"General utility file with function exports\",\n };\n}\n\ninterface ExportAnalysis {\n hasOnlyTypeExports: boolean;\n hasOnlyConstantExports: boolean;\n hasJSX: boolean;\n hasFunctionExports: boolean;\n hasConstExports: boolean;\n hasTypeExports: boolean;\n}\n\n/**\n * Analyze exports in an AST to determine file category\n */\nfunction analyzeExports(ast: TSESTree.Program): ExportAnalysis {\n let hasFunctionExports = false;\n let hasConstExports = false;\n let hasTypeExports = false;\n let hasJSX = false;\n\n // Walk the AST to find exports and JSX\n function visit(node: TSESTree.Node): void {\n // Check for JSX\n if (\n node.type === \"JSXElement\" ||\n node.type === \"JSXFragment\" ||\n node.type === \"JSXText\"\n ) {\n hasJSX = true;\n }\n\n // Export named declaration\n if (node.type === \"ExportNamedDeclaration\") {\n const decl = node.declaration;\n\n // Type/interface exports\n if (\n node.exportKind === \"type\" ||\n decl?.type === \"TSTypeAliasDeclaration\" ||\n decl?.type === \"TSInterfaceDeclaration\"\n ) {\n hasTypeExports = true;\n }\n // Function exports\n else if (\n decl?.type === \"FunctionDeclaration\" ||\n (decl?.type === \"VariableDeclaration\" &&\n decl.declarations.some(\n (d) =>\n d.init?.type === \"ArrowFunctionExpression\" ||\n d.init?.type === \"FunctionExpression\"\n ))\n ) {\n hasFunctionExports = true;\n }\n // Const/enum exports\n else if (\n decl?.type === \"VariableDeclaration\" ||\n decl?.type === \"TSEnumDeclaration\"\n ) {\n // Check if it's a const with non-function value\n if (decl.type === \"VariableDeclaration\") {\n const hasNonFunctionInit = decl.declarations.some(\n (d) =>\n d.init &&\n d.init.type !== \"ArrowFunctionExpression\" &&\n d.init.type !== \"FunctionExpression\"\n );\n if (hasNonFunctionInit) {\n hasConstExports = true;\n }\n } else {\n hasConstExports = true;\n }\n }\n // Re-exports without declaration - check specifiers\n else if (!decl && node.specifiers.length > 0) {\n // For re-exports, we can't easily determine the type without resolving\n // Treat as potential function exports to be safe\n // Note: if exportKind was \"type\", we would have matched the first branch\n hasFunctionExports = true;\n }\n }\n\n // Export default\n if (node.type === \"ExportDefaultDeclaration\") {\n const decl = node.declaration;\n if (\n decl.type === \"FunctionDeclaration\" ||\n decl.type === \"ArrowFunctionExpression\" ||\n decl.type === \"FunctionExpression\"\n ) {\n hasFunctionExports = true;\n } else if (decl.type === \"ClassDeclaration\") {\n // Classes are typically core logic\n hasFunctionExports = true;\n } else {\n hasConstExports = true;\n }\n }\n\n // Recurse into children\n for (const key of Object.keys(node)) {\n const child = (node as unknown as Record<string, unknown>)[key];\n if (child && typeof child === \"object\") {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (item && typeof item === \"object\" && \"type\" in item) {\n visit(item as TSESTree.Node);\n }\n }\n } else if (\"type\" in child) {\n visit(child as TSESTree.Node);\n }\n }\n }\n }\n\n for (const node of ast.body) {\n visit(node);\n }\n\n // Determine derived properties\n const hasOnlyTypeExports =\n hasTypeExports && !hasFunctionExports && !hasConstExports;\n const hasOnlyConstantExports =\n hasConstExports && !hasFunctionExports && !hasTypeExports;\n\n return {\n hasOnlyTypeExports,\n hasOnlyConstantExports,\n hasJSX,\n hasFunctionExports,\n hasConstExports,\n hasTypeExports,\n };\n}\n\n/**\n * Get the weight for a category\n */\nexport function getCategoryWeight(category: FileCategory): number {\n return CATEGORY_WEIGHTS[category];\n}\n","/**\n * Dependency Graph Builder\n *\n * Builds a dependency graph by tracing all imports from an entry file.\n * Used for calculating aggregate test coverage across a component and its dependencies.\n *\n * Key behaviors:\n * - Traces transitive dependencies (full depth)\n * - Excludes node_modules (external packages)\n * - Handles circular dependencies via visited set\n * - Follows re-exports to actual source files\n * - Caches results for performance\n */\n\nimport { existsSync, readFileSync, statSync } from \"fs\";\nimport { dirname, resolve } from \"path\";\nimport { parse } from \"@typescript-eslint/typescript-estree\";\nimport type { TSESTree } from \"@typescript-eslint/utils\";\nimport { resolveImportPath, parseFile } from \"./export-resolver.js\";\n\nexport interface DependencyGraph {\n /** The entry file that was analyzed */\n root: string;\n /** All transitive dependencies (absolute paths, project files only) */\n allDependencies: Set<string>;\n}\n\ninterface CacheEntry {\n graph: DependencyGraph;\n mtime: number;\n}\n\n/**\n * Cache for dependency graphs (per entry file)\n */\nconst dependencyCache = new Map<string, CacheEntry>();\n\n/**\n * Build a dependency graph starting from an entry file\n *\n * @param entryFile - Absolute path to the entry file\n * @param projectRoot - Project root directory (used for determining project boundaries)\n * @returns DependencyGraph with all transitive dependencies\n */\nexport function buildDependencyGraph(\n entryFile: string,\n projectRoot: string\n): DependencyGraph {\n // Check cache\n const cached = dependencyCache.get(entryFile);\n if (cached) {\n // Validate cache by checking entry file mtime\n try {\n const currentMtime = statSync(entryFile).mtimeMs;\n if (currentMtime === cached.mtime) {\n return cached.graph;\n }\n } catch {\n // File doesn't exist or can't be read, invalidate cache\n }\n }\n\n const allDependencies = new Set<string>();\n const visited = new Set<string>();\n\n // Recursively collect dependencies\n collectDependencies(entryFile, projectRoot, allDependencies, visited);\n\n const graph: DependencyGraph = {\n root: entryFile,\n allDependencies,\n };\n\n // Cache the result\n try {\n const mtime = statSync(entryFile).mtimeMs;\n dependencyCache.set(entryFile, { graph, mtime });\n } catch {\n // If we can't get mtime, don't cache\n }\n\n return graph;\n}\n\n/**\n * Recursively collect dependencies from a file\n */\nfunction collectDependencies(\n filePath: string,\n projectRoot: string,\n allDependencies: Set<string>,\n visited: Set<string>\n): void {\n // Prevent infinite loops from circular dependencies\n if (visited.has(filePath)) {\n return;\n }\n visited.add(filePath);\n\n // Parse the file to extract imports\n const imports = extractImports(filePath);\n\n for (const importSource of imports) {\n // Resolve the import to an absolute path\n const resolvedPath = resolveImportPath(importSource, filePath);\n\n if (!resolvedPath) {\n // Could not resolve (external package or unresolvable)\n continue;\n }\n\n // Skip node_modules\n if (resolvedPath.includes(\"node_modules\")) {\n continue;\n }\n\n // Skip files outside the project\n if (!resolvedPath.startsWith(projectRoot)) {\n continue;\n }\n\n // Skip already visited files (handles circular dependencies)\n // This prevents adding the root file back as a dependency\n if (visited.has(resolvedPath)) {\n continue;\n }\n\n // Add to dependencies\n allDependencies.add(resolvedPath);\n\n // Recurse into this dependency\n collectDependencies(resolvedPath, projectRoot, allDependencies, visited);\n }\n}\n\n/**\n * Extract all import sources from a file\n */\nfunction extractImports(filePath: string): string[] {\n if (!existsSync(filePath)) {\n return [];\n }\n\n const ast = parseFile(filePath);\n if (!ast) {\n return [];\n }\n\n const imports: string[] = [];\n\n for (const node of ast.body) {\n // import ... from \"source\"\n if (node.type === \"ImportDeclaration\" && node.source.value) {\n imports.push(node.source.value as string);\n }\n\n // export ... from \"source\" (re-exports)\n if (\n node.type === \"ExportNamedDeclaration\" &&\n node.source?.value\n ) {\n imports.push(node.source.value as string);\n }\n\n // export * from \"source\"\n if (node.type === \"ExportAllDeclaration\" && node.source.value) {\n imports.push(node.source.value as string);\n }\n }\n\n // Also check for dynamic imports in the AST\n const dynamicImports = extractDynamicImports(ast);\n imports.push(...dynamicImports);\n\n return imports;\n}\n\n/**\n * Extract dynamic import() calls from the AST\n */\nfunction extractDynamicImports(ast: TSESTree.Program): string[] {\n const imports: string[] = [];\n\n function visit(node: TSESTree.Node): void {\n // import(\"source\")\n if (\n node.type === \"ImportExpression\" &&\n node.source.type === \"Literal\" &&\n typeof node.source.value === \"string\"\n ) {\n imports.push(node.source.value);\n }\n\n // Recurse into children\n for (const key of Object.keys(node)) {\n const child = (node as unknown as Record<string, unknown>)[key];\n if (child && typeof child === \"object\") {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (item && typeof item === \"object\" && \"type\" in item) {\n visit(item as TSESTree.Node);\n }\n }\n } else if (\"type\" in child) {\n visit(child as TSESTree.Node);\n }\n }\n }\n }\n\n for (const node of ast.body) {\n visit(node);\n }\n\n return imports;\n}\n\n/**\n * Invalidate the cache for a specific file\n *\n * Call this when a file changes to ensure fresh data\n */\nexport function invalidateDependencyCache(filePath: string): void {\n dependencyCache.delete(filePath);\n\n // Also invalidate any graphs that include this file as a dependency\n for (const [entryFile, entry] of dependencyCache) {\n if (entry.graph.allDependencies.has(filePath)) {\n dependencyCache.delete(entryFile);\n }\n }\n}\n\n/**\n * Clear the entire dependency cache\n */\nexport function clearDependencyCache(): void {\n dependencyCache.clear();\n}\n\n/**\n * Get cache statistics (for debugging/monitoring)\n */\nexport function getDependencyCacheStats(): {\n size: number;\n entries: string[];\n} {\n return {\n size: dependencyCache.size,\n entries: Array.from(dependencyCache.keys()),\n };\n}\n","/**\n * Export Resolver\n *\n * Resolves import paths and finds export definitions, following re-exports\n * to their original source files.\n */\n\nimport { ResolverFactory } from \"oxc-resolver\";\nimport { parse } from \"@typescript-eslint/typescript-estree\";\nimport { readFileSync, existsSync } from \"fs\";\nimport { dirname, join, extname } from \"path\";\nimport type { TSESTree } from \"@typescript-eslint/utils\";\n\n// Module-level resolver instance (reused across calls)\nlet resolverInstance: ReturnType<typeof ResolverFactory.prototype.sync> | null =\n null;\nlet resolverFactory: ResolverFactory | null = null;\n\n/**\n * Information about a resolved export\n */\nexport interface ResolvedExport {\n /** The name of the export (e.g., \"Button\") */\n name: string;\n /** Absolute path to the file containing the actual definition */\n filePath: string;\n /** The local name in the source file (may differ from export name) */\n localName: string;\n /** Whether this is a re-export (export { X } from './other') */\n isReexport: boolean;\n}\n\n/**\n * Cache for file exports to avoid re-parsing\n */\nconst exportCache = new Map<\n string,\n Map<string, { localName: string; reexportSource?: string }>\n>();\n\n/**\n * Cache for parsed ASTs\n */\nconst astCache = new Map<string, TSESTree.Program>();\n\n/**\n * Cache for resolved paths\n */\nconst resolvedPathCache = new Map<string, string | null>();\n\n/**\n * Get or create the resolver factory\n */\nfunction getResolverFactory(): ResolverFactory {\n if (!resolverFactory) {\n resolverFactory = new ResolverFactory({\n extensions: [\".tsx\", \".ts\", \".jsx\", \".js\"],\n mainFields: [\"module\", \"main\"],\n conditionNames: [\"import\", \"require\", \"node\", \"default\"],\n // Enable TypeScript path resolution\n tsconfig: {\n configFile: \"tsconfig.json\",\n references: \"auto\",\n },\n });\n }\n return resolverFactory;\n}\n\n/**\n * Resolve an import path to an absolute file path\n */\nexport function resolveImportPath(\n importSource: string,\n fromFile: string\n): string | null {\n const cacheKey = `${fromFile}::${importSource}`;\n\n if (resolvedPathCache.has(cacheKey)) {\n return resolvedPathCache.get(cacheKey) ?? null;\n }\n\n // Skip node_modules\n if (\n importSource.startsWith(\"react\") ||\n importSource.startsWith(\"next\") ||\n (!importSource.startsWith(\".\") &&\n !importSource.startsWith(\"@/\") &&\n !importSource.startsWith(\"~/\"))\n ) {\n // Check if it's a known external package\n if (\n importSource.includes(\"@mui/\") ||\n importSource.includes(\"@chakra-ui/\") ||\n importSource.includes(\"antd\") ||\n importSource.includes(\"@radix-ui/\")\n ) {\n // Return a marker for external packages - we don't resolve them but track them\n resolvedPathCache.set(cacheKey, null);\n return null;\n }\n resolvedPathCache.set(cacheKey, null);\n return null;\n }\n\n try {\n const factory = getResolverFactory();\n const fromDir = dirname(fromFile);\n const result = factory.sync(fromDir, importSource);\n\n if (result.path) {\n resolvedPathCache.set(cacheKey, result.path);\n return result.path;\n }\n } catch {\n // Fallback: try manual resolution for common patterns\n const resolved = manualResolve(importSource, fromFile);\n resolvedPathCache.set(cacheKey, resolved);\n return resolved;\n }\n\n resolvedPathCache.set(cacheKey, null);\n return null;\n}\n\n/**\n * Manual fallback resolution for common patterns\n */\nfunction manualResolve(importSource: string, fromFile: string): string | null {\n const fromDir = dirname(fromFile);\n const extensions = [\".tsx\", \".ts\", \".jsx\", \".js\"];\n\n // Handle @/ alias - find tsconfig and resolve\n if (importSource.startsWith(\"@/\")) {\n const projectRoot = findProjectRoot(fromFile);\n if (projectRoot) {\n const relativePath = importSource.slice(2); // Remove @/\n for (const ext of extensions) {\n const candidate = join(projectRoot, relativePath + ext);\n if (existsSync(candidate)) {\n return candidate;\n }\n // Try index file\n const indexCandidate = join(projectRoot, relativePath, `index${ext}`);\n if (existsSync(indexCandidate)) {\n return indexCandidate;\n }\n }\n }\n }\n\n // Handle relative imports\n if (importSource.startsWith(\".\")) {\n for (const ext of extensions) {\n const candidate = join(fromDir, importSource + ext);\n if (existsSync(candidate)) {\n return candidate;\n }\n // Try index file\n const indexCandidate = join(fromDir, importSource, `index${ext}`);\n if (existsSync(indexCandidate)) {\n return indexCandidate;\n }\n }\n }\n\n return null;\n}\n\n/**\n * Find the project root by looking for tsconfig.json or package.json\n */\nfunction findProjectRoot(fromFile: string): string | null {\n let dir = dirname(fromFile);\n const root = \"/\";\n\n while (dir !== root) {\n if (existsSync(join(dir, \"tsconfig.json\"))) {\n return dir;\n }\n if (existsSync(join(dir, \"package.json\"))) {\n return dir;\n }\n dir = dirname(dir);\n }\n\n return null;\n}\n\n/**\n * Parse a file and cache the AST\n */\nexport function parseFile(filePath: string): TSESTree.Program | null {\n if (astCache.has(filePath)) {\n return astCache.get(filePath)!;\n }\n\n try {\n const content = readFileSync(filePath, \"utf-8\");\n const ast = parse(content, {\n jsx: true,\n loc: true,\n range: true,\n });\n astCache.set(filePath, ast);\n return ast;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract export information from a file\n */\nfunction extractExports(\n filePath: string\n): Map<string, { localName: string; reexportSource?: string }> {\n if (exportCache.has(filePath)) {\n return exportCache.get(filePath)!;\n }\n\n const exports = new Map<\n string,\n { localName: string; reexportSource?: string }\n >();\n const ast = parseFile(filePath);\n\n if (!ast) {\n exportCache.set(filePath, exports);\n return exports;\n }\n\n for (const node of ast.body) {\n // Handle: export function Button() {}\n if (\n node.type === \"ExportNamedDeclaration\" &&\n node.declaration?.type === \"FunctionDeclaration\" &&\n node.declaration.id\n ) {\n exports.set(node.declaration.id.name, {\n localName: node.declaration.id.name,\n });\n }\n\n // Handle: export const Button = () => {}\n if (\n node.type === \"ExportNamedDeclaration\" &&\n node.declaration?.type === \"VariableDeclaration\"\n ) {\n for (const decl of node.declaration.declarations) {\n if (decl.id.type === \"Identifier\") {\n exports.set(decl.id.name, { localName: decl.id.name });\n }\n }\n }\n\n // Handle: export { Button } or export { Button as Btn }\n if (node.type === \"ExportNamedDeclaration\" && node.specifiers.length > 0) {\n const source = node.source?.value as string | undefined;\n for (const spec of node.specifiers) {\n if (spec.type === \"ExportSpecifier\") {\n const exportedName =\n spec.exported.type === \"Identifier\"\n ? spec.exported.name\n : spec.exported.value;\n const localName =\n spec.local.type === \"Identifier\"\n ? spec.local.name\n : spec.local.value;\n\n exports.set(exportedName, {\n localName,\n reexportSource: source,\n });\n }\n }\n }\n\n // Handle: export default function Button() {}\n if (\n node.type === \"ExportDefaultDeclaration\" &&\n node.declaration.type === \"FunctionDeclaration\" &&\n node.declaration.id\n ) {\n exports.set(\"default\", { localName: node.declaration.id.name });\n }\n\n // Handle: export default Button\n if (\n node.type === \"ExportDefaultDeclaration\" &&\n node.declaration.type === \"Identifier\"\n ) {\n exports.set(\"default\", { localName: node.declaration.name });\n }\n }\n\n exportCache.set(filePath, exports);\n return exports;\n}\n\n/**\n * Resolve an export to its original definition, following re-exports\n */\nexport function resolveExport(\n exportName: string,\n filePath: string,\n visited = new Set<string>()\n): ResolvedExport | null {\n // Cycle detection\n const key = `${filePath}::${exportName}`;\n if (visited.has(key)) {\n return null;\n }\n visited.add(key);\n\n const exports = extractExports(filePath);\n const exportInfo = exports.get(exportName);\n\n if (!exportInfo) {\n return null;\n }\n\n // If it's a re-export, follow the chain\n if (exportInfo.reexportSource) {\n const resolvedPath = resolveImportPath(exportInfo.reexportSource, filePath);\n if (resolvedPath) {\n return resolveExport(exportInfo.localName, resolvedPath, visited);\n }\n return null;\n }\n\n // This is the actual definition\n return {\n name: exportName,\n filePath,\n localName: exportInfo.localName,\n isReexport: false,\n };\n}\n\n/**\n * Clear all caches (useful for testing or watch mode)\n */\nexport function clearResolverCaches(): void {\n exportCache.clear();\n astCache.clear();\n resolvedPathCache.clear();\n}\n","/**\n * Coverage Aggregator\n *\n * Calculates weighted aggregate coverage for a component by tracing its\n * dependencies and combining their coverage using smart weighting.\n *\n * Weighting strategy:\n * - core (1.0): hooks, components, services - critical logic\n * - utility (0.5): formatters, validators - supporting code\n * - constant (0.25): config, constants - static data\n * - type (0): .d.ts, type-only - no runtime impact\n */\n\nimport { categorizeFile, type FileCategory } from \"./file-categorizer.js\";\nimport { buildDependencyGraph } from \"./dependency-graph.js\";\n\n/**\n * Istanbul coverage JSON format (matching require-test-coverage.ts)\n */\nexport interface IstanbulCoverage {\n [filePath: string]: {\n path: string;\n statementMap: {\n [key: string]: {\n start: { line: number; column: number };\n end: { line: number; column: number };\n };\n };\n fnMap: {\n [key: string]: {\n name: string;\n decl: {\n start: { line: number; column: number };\n end: { line: number; column: number };\n };\n loc: {\n start: { line: number; column: number };\n end: { line: number; column: number };\n };\n };\n };\n branchMap: {\n [key: string]: {\n loc: {\n start: { line: number; column: number };\n end: { line: number; column: number };\n };\n type: string;\n locations: Array<{\n start: { line: number; column: number };\n end: { line: number; column: number };\n }>;\n };\n };\n s: { [key: string]: number }; // Statement hit counts\n f: { [key: string]: number }; // Function hit counts\n b: { [key: string]: number[] }; // Branch hit counts\n };\n}\n\n/**\n * Coverage information for a single file\n */\nexport interface FileCoverageInfo {\n filePath: string;\n category: FileCategory;\n weight: number;\n statements: { covered: number; total: number };\n percentage: number;\n}\n\n/**\n * Aggregated coverage result for a component\n */\nexport interface AggregatedCoverage {\n /** The component file that was analyzed */\n componentFile: string;\n /** Coverage percentage for just the component file */\n componentCoverage: number;\n /** Weighted aggregate coverage across component + all dependencies */\n aggregateCoverage: number;\n /** Total number of files analyzed (component + dependencies) */\n totalFiles: number;\n /** Detailed coverage info for each file */\n filesAnalyzed: FileCoverageInfo[];\n /** Files with 0% coverage */\n uncoveredFiles: string[];\n /** The file with lowest coverage (excluding 0% files) */\n lowestCoverageFile: { path: string; percentage: number } | null;\n}\n\n/**\n * Calculate aggregate coverage for a component and its dependencies\n *\n * @param componentFile - Absolute path to the component file\n * @param projectRoot - Project root directory\n * @param coverageData - Istanbul coverage data\n * @returns Aggregated coverage information\n */\nexport function aggregateCoverage(\n componentFile: string,\n projectRoot: string,\n coverageData: IstanbulCoverage\n): AggregatedCoverage {\n // Build dependency graph\n const graph = buildDependencyGraph(componentFile, projectRoot);\n\n // Collect all files to analyze (component + dependencies)\n const allFiles = new Set<string>([componentFile, ...graph.allDependencies]);\n\n // Analyze each file\n const filesAnalyzed: FileCoverageInfo[] = [];\n const uncoveredFiles: string[] = [];\n let lowestCoverageFile: { path: string; percentage: number } | null = null;\n let componentCoverageInfo: FileCoverageInfo | null = null;\n\n for (const filePath of allFiles) {\n const coverageInfo = getFileCoverage(filePath, projectRoot, coverageData);\n filesAnalyzed.push(coverageInfo);\n\n // Track component coverage separately\n if (filePath === componentFile) {\n componentCoverageInfo = coverageInfo;\n }\n\n // Track uncovered files (0%)\n if (coverageInfo.percentage === 0 && coverageInfo.statements.total > 0) {\n uncoveredFiles.push(filePath);\n }\n\n // Track lowest coverage (excluding 0% and type files with weight 0)\n if (\n coverageInfo.percentage > 0 &&\n coverageInfo.weight > 0 &&\n coverageInfo.statements.total > 0\n ) {\n if (\n !lowestCoverageFile ||\n coverageInfo.percentage < lowestCoverageFile.percentage\n ) {\n lowestCoverageFile = {\n path: filePath,\n percentage: coverageInfo.percentage,\n };\n }\n }\n }\n\n // Calculate weighted aggregate coverage\n const aggregateCoverageValue = calculateWeightedCoverage(filesAnalyzed);\n\n return {\n componentFile,\n componentCoverage: componentCoverageInfo?.percentage ?? 0,\n aggregateCoverage: aggregateCoverageValue,\n totalFiles: filesAnalyzed.length,\n filesAnalyzed,\n uncoveredFiles,\n lowestCoverageFile,\n };\n}\n\n/**\n * Get coverage information for a single file\n */\nfunction getFileCoverage(\n filePath: string,\n projectRoot: string,\n coverageData: IstanbulCoverage\n): FileCoverageInfo {\n // Categorize the file\n const categoryResult = categorizeFile(filePath, projectRoot);\n\n // Find coverage data for this file\n const fileCoverage = findCoverageForFile(filePath, coverageData, projectRoot);\n\n if (!fileCoverage) {\n // No coverage data - could mean not tested or file not found\n return {\n filePath,\n category: categoryResult.category,\n weight: categoryResult.weight,\n statements: { covered: 0, total: 0 },\n percentage: 0,\n };\n }\n\n // Calculate statement coverage\n const statementHits = fileCoverage.s;\n const totalStatements = Object.keys(statementHits).length;\n const coveredStatements = Object.values(statementHits).filter(\n (hits) => hits > 0\n ).length;\n\n const percentage =\n totalStatements > 0 ? (coveredStatements / totalStatements) * 100 : 0;\n\n return {\n filePath,\n category: categoryResult.category,\n weight: categoryResult.weight,\n statements: { covered: coveredStatements, total: totalStatements },\n percentage: Math.round(percentage * 100) / 100, // Round to 2 decimal places\n };\n}\n\n/**\n * Find coverage data for a file, handling path normalization\n */\nfunction findCoverageForFile(\n filePath: string,\n coverageData: IstanbulCoverage,\n projectRoot: string\n): IstanbulCoverage[string] | null {\n // Try exact match first\n if (coverageData[filePath]) {\n return coverageData[filePath];\n }\n\n // Try relative path from project root\n const relativePath = filePath.startsWith(projectRoot)\n ? filePath.slice(projectRoot.length)\n : filePath;\n\n // Try with and without leading slash\n const pathVariants = [\n relativePath,\n relativePath.startsWith(\"/\") ? relativePath.slice(1) : `/${relativePath}`,\n relativePath.startsWith(\"/\") ? relativePath : `/${relativePath}`,\n ];\n\n for (const variant of pathVariants) {\n if (coverageData[variant]) {\n return coverageData[variant];\n }\n }\n\n // Try matching by checking if coverage path ends with relative path\n for (const [coveragePath, coverage] of Object.entries(coverageData)) {\n if (\n coveragePath.endsWith(relativePath) ||\n coveragePath.endsWith(relativePath.slice(1))\n ) {\n return coverage;\n }\n }\n\n return null;\n}\n\n/**\n * Calculate weighted average coverage across files\n *\n * Uses statement count * weight for each file to determine contribution.\n * Files with weight 0 (type files) are excluded from calculation.\n */\nfunction calculateWeightedCoverage(files: FileCoverageInfo[]): number {\n let totalWeightedStatements = 0;\n let totalWeightedCovered = 0;\n\n for (const file of files) {\n // Skip files with weight 0 (type files)\n if (file.weight === 0) {\n continue;\n }\n\n // Skip files with no statements\n if (file.statements.total === 0) {\n continue;\n }\n\n const weightedTotal = file.statements.total * file.weight;\n const weightedCovered = file.statements.covered * file.weight;\n\n totalWeightedStatements += weightedTotal;\n totalWeightedCovered += weightedCovered;\n }\n\n if (totalWeightedStatements === 0) {\n return 0;\n }\n\n const percentage = (totalWeightedCovered / totalWeightedStatements) * 100;\n return Math.round(percentage * 100) / 100; // Round to 2 decimal places\n}\n","/**\n * JSX Coverage Analyzer\n *\n * Analyzes JSX elements to determine test coverage for interactive elements\n * like event handlers. Uses Istanbul coverage data to check if the code\n * associated with JSX elements has been executed during tests.\n *\n * Phase 1: Core functions for statement-level coverage analysis\n * Phase 2: Event handler extraction and analysis\n * Phase 3: Conditional parent analysis + Component-level aggregation (partial TODO)\n * Phase 4: Import dependency coverage + ESLint rule reporting (partial TODO)\n */\n\nimport type { TSESTree } from \"@typescript-eslint/utils\";\nimport type { IstanbulCoverage } from \"./coverage-aggregator.js\";\n\n/**\n * Re-export IstanbulCoverage for consumers of this module\n */\nexport type { IstanbulCoverage } from \"./coverage-aggregator.js\";\n\n/**\n * Istanbul coverage data for a single file\n */\nexport type IstanbulFileCoverage = IstanbulCoverage[string];\n\n/**\n * Source location with start and end positions\n */\nexport interface SourceLocation {\n start: { line: number; column: number };\n end: { line: number; column: number };\n}\n\n/**\n * Coverage statistics for a code region\n */\nexport interface CoverageStats {\n /** Number of statements that were executed at least once */\n covered: number;\n /** Total number of statements in the region */\n total: number;\n /** Coverage percentage (0-100) */\n percentage: number;\n}\n\n/**\n * Coverage result for a single JSX element\n */\nexport interface JSXCoverageResult {\n /** The data-loc attribute value for this element */\n dataLoc: string;\n /** Whether this element has any event handlers */\n hasEventHandlers: boolean;\n /** Names of event handlers found (e.g., [\"onClick\", \"onSubmit\"]) */\n eventHandlerNames: string[];\n /** Coverage statistics for statements within this element */\n coverage: CoverageStats;\n /** Whether the element is considered \"covered\" (percentage > 0) */\n isCovered: boolean;\n}\n\n// =============================================================================\n// Phase 1: Core Functions\n// =============================================================================\n\n/**\n * Creates a \"file:line:column\" format string for data-loc attribute\n *\n * @param filePath - Absolute or relative path to the file\n * @param loc - Source location with start position\n * @returns Formatted string like \"src/Button.tsx:15:4\"\n */\nexport function buildDataLoc(filePath: string, loc: SourceLocation): string {\n return `${filePath}:${loc.start.line}:${loc.start.column}`;\n}\n\n/**\n * Find statement IDs that overlap with the given source location\n *\n * A statement overlaps if its line range intersects with the location's\n * line range. Column-level precision is not used for overlap detection.\n *\n * @param loc - The source location to check\n * @param fileCoverage - Istanbul coverage data for the file\n * @returns Set of statement IDs (keys from statementMap) that overlap\n */\nexport function findStatementsInRange(\n loc: SourceLocation,\n fileCoverage: IstanbulFileCoverage\n): Set<string> {\n const overlappingStatements = new Set<string>();\n\n for (const [statementId, statementLoc] of Object.entries(\n fileCoverage.statementMap\n )) {\n // Check if statement's line range overlaps with location's line range\n const statementStart = statementLoc.start.line;\n const statementEnd = statementLoc.end.line;\n const locStart = loc.start.line;\n const locEnd = loc.end.line;\n\n // Two ranges overlap if: start1 <= end2 AND start2 <= end1\n if (statementStart <= locEnd && locStart <= statementEnd) {\n overlappingStatements.add(statementId);\n }\n }\n\n return overlappingStatements;\n}\n\n/**\n * Calculate coverage statistics from a set of statement IDs\n *\n * @param statementIds - Set of statement IDs to check\n * @param fileCoverage - Istanbul coverage data for the file\n * @returns Coverage statistics with covered count, total, and percentage\n */\nexport function calculateCoverageFromStatements(\n statementIds: Set<string>,\n fileCoverage: IstanbulFileCoverage\n): CoverageStats {\n if (statementIds.size === 0) {\n return { covered: 0, total: 0, percentage: 0 };\n }\n\n let covered = 0;\n const total = statementIds.size;\n\n for (const statementId of statementIds) {\n const hitCount = fileCoverage.s[statementId];\n if (hitCount !== undefined && hitCount > 0) {\n covered++;\n }\n }\n\n const percentage = total > 0 ? Math.round((covered / total) * 100) : 0;\n\n return { covered, total, percentage };\n}\n\n/**\n * Find coverage data for a file with path normalization\n *\n * Handles various path formats:\n * - Absolute paths\n * - Relative paths with or without leading slash\n * - Paths that may differ in their base directory\n *\n * @param coverage - Full Istanbul coverage data\n * @param filePath - The file path to find coverage for\n * @returns File coverage data if found, undefined otherwise\n */\nexport function findCoverageForFile(\n coverage: IstanbulCoverage,\n filePath: string\n): IstanbulFileCoverage | undefined {\n // Try exact match first\n if (coverage[filePath]) {\n return coverage[filePath];\n }\n\n // Normalize the path for comparison (remove leading slashes, standardize)\n const normalizedPath = filePath.replace(/^\\/+/, \"\");\n\n // Try with and without leading slash\n const pathVariants = [\n normalizedPath,\n `/${normalizedPath}`,\n filePath,\n ];\n\n for (const variant of pathVariants) {\n if (coverage[variant]) {\n return coverage[variant];\n }\n }\n\n // Try matching by checking if coverage path ends with our path\n for (const [coveragePath, fileCoverage] of Object.entries(coverage)) {\n const normalizedCoveragePath = coveragePath.replace(/^\\/+/, \"\");\n\n if (\n normalizedCoveragePath.endsWith(normalizedPath) ||\n normalizedPath.endsWith(normalizedCoveragePath)\n ) {\n return fileCoverage;\n }\n }\n\n return undefined;\n}\n\n/**\n * Check if a JSX attribute is an event handler (starts with \"on\" followed by uppercase)\n *\n * Event handlers follow the pattern: onClick, onSubmit, onChange, etc.\n * This excludes spread attributes and non-event props like \"only\" or \"once\".\n *\n * @param attr - JSX attribute or spread attribute\n * @returns true if the attribute is an event handler\n */\nexport function isEventHandlerAttribute(\n attr: TSESTree.JSXAttribute | TSESTree.JSXSpreadAttribute\n): boolean {\n // Spread attributes are not event handlers by themselves\n if (attr.type === \"JSXSpreadAttribute\") {\n return false;\n }\n\n // Only handle JSXIdentifier names (not namespaced like xml:lang)\n if (attr.name.type !== \"JSXIdentifier\") {\n return false;\n }\n\n const name = attr.name.name;\n\n // Match pattern: on[A-Z]...\n return /^on[A-Z]/.test(name);\n}\n\n/**\n * Analyze a JSX element for test coverage\n *\n * Main entry point that combines all the above functions to produce\n * a complete coverage analysis for a single JSX element.\n *\n * @param jsxNode - The JSX element node from the AST\n * @param filePath - Path to the file containing this element\n * @param coverage - Istanbul coverage data\n * @param ancestors - Optional ancestor nodes for resolving handler references\n * @param projectRoot - Optional project root for resolving import paths\n * @returns Coverage result for the JSX element\n */\nexport function analyzeJSXElementCoverage(\n jsxNode: TSESTree.JSXElement,\n filePath: string,\n coverage: IstanbulCoverage,\n ancestors: TSESTree.Node[] = [],\n projectRoot?: string\n): JSXCoverageResult {\n // Build the data-loc identifier\n const loc = jsxNode.loc;\n const dataLoc = buildDataLoc(filePath, loc);\n\n // Find event handlers on this element\n const eventHandlerNames: string[] = [];\n for (const attr of jsxNode.openingElement.attributes) {\n if (isEventHandlerAttribute(attr) && attr.type === \"JSXAttribute\") {\n if (attr.name.type === \"JSXIdentifier\") {\n eventHandlerNames.push(attr.name.name);\n }\n }\n }\n const hasEventHandlers = eventHandlerNames.length > 0;\n\n // Find coverage data for this file\n const fileCoverage = findCoverageForFile(coverage, filePath);\n\n if (!fileCoverage) {\n // No coverage data available for this file\n return {\n dataLoc,\n hasEventHandlers,\n eventHandlerNames,\n coverage: { covered: 0, total: 0, percentage: 0 },\n isCovered: false,\n };\n }\n\n // Find statements that overlap with this JSX element\n const statementIds = findStatementsInRange(loc, fileCoverage);\n\n // Also include statements from event handler bodies (Phase 2)\n if (hasEventHandlers) {\n const handlerStatementIds = getHandlerStatements(\n jsxNode,\n fileCoverage,\n ancestors\n );\n for (const stmtId of handlerStatementIds) {\n statementIds.add(stmtId);\n }\n }\n\n // Include statements from conditional ancestors (Phase 3)\n // This ensures conditionally rendered elements include the condition's coverage\n const conditionalAncestor = findConditionalAncestor(jsxNode, ancestors);\n if (conditionalAncestor) {\n const conditionalStatementIds = getConditionalStatements(\n conditionalAncestor,\n fileCoverage\n );\n for (const stmtId of conditionalStatementIds) {\n statementIds.add(stmtId);\n }\n }\n\n // Calculate local coverage statistics (statements in this file)\n const localCoverage = calculateCoverageFromStatements(\n statementIds,\n fileCoverage\n );\n\n // Phase 4: Include import dependency coverage\n // Find imports used in this JSX element and aggregate their coverage\n let importCoverage = { covered: 0, total: 0 };\n if (projectRoot && ancestors.length > 0) {\n const importPaths = findImportsUsedInJSX(jsxNode, ancestors);\n if (importPaths.size > 0) {\n importCoverage = aggregateImportCoverage(\n importPaths,\n coverage,\n projectRoot,\n filePath\n );\n }\n }\n\n // Combine local and import coverage (weighted by statement count)\n const totalCovered = localCoverage.covered + importCoverage.covered;\n const totalStatements = localCoverage.total + importCoverage.total;\n const combinedPercentage =\n totalStatements > 0 ? Math.round((totalCovered / totalStatements) * 100) : 0;\n\n const coverageStats: CoverageStats = {\n covered: totalCovered,\n total: totalStatements,\n percentage: combinedPercentage,\n };\n\n return {\n dataLoc,\n hasEventHandlers,\n eventHandlerNames,\n coverage: coverageStats,\n isCovered: coverageStats.percentage > 0,\n };\n}\n\n// =============================================================================\n// Phase 2: Event Handler Analysis\n// =============================================================================\n\n/**\n * Extract the expression from an event handler attribute value\n *\n * Handles various forms of event handler values:\n * - {handleClick} - identifier reference\n * - {() => doSomething()} - inline arrow function\n * - {fn.bind(this)} - call expression (like bind)\n * - {obj.method} - member expression\n *\n * @param attr - The JSX attribute to extract from\n * @returns The expression if found, null for string literals or empty values\n */\nexport function extractEventHandlerExpression(\n attr: TSESTree.JSXAttribute\n): TSESTree.Expression | null {\n // No value means no expression (e.g., <input disabled />)\n if (!attr.value) {\n return null;\n }\n\n // String literals are not expressions we can analyze\n // e.g., onClick=\"someGlobalFunction()\"\n if (attr.value.type === \"Literal\") {\n return null;\n }\n\n // JSX expression container: {expression}\n if (attr.value.type === \"JSXExpressionContainer\") {\n const expression = attr.value.expression;\n\n // Empty expression container {} or JSXEmptyExpression\n if (expression.type === \"JSXEmptyExpression\") {\n return null;\n }\n\n // Return the actual expression (Identifier, ArrowFunctionExpression, CallExpression, etc.)\n return expression;\n }\n\n // JSXElement as value (rare, but possible)\n // e.g., onClick={<SomeComponent />} - not a valid handler expression\n return null;\n}\n\n/**\n * Find the function declaration for an identifier used as an event handler\n *\n * Searches through the ancestor chain to find where an identifier is declared.\n * Handles:\n * - Variable declarations with function expressions\n * - Variable declarations with arrow functions\n * - Function declarations\n * - Function parameters (destructured or direct)\n *\n * @param identifier - The identifier node (e.g., the \"handleClick\" in onClick={handleClick})\n * @param ancestors - The ancestor nodes from the AST traversal (innermost first or any order)\n * @returns The function body node if found, null otherwise\n */\nexport function findHandlerFunctionDeclaration(\n identifier: TSESTree.Identifier,\n ancestors: TSESTree.Node[]\n): TSESTree.Node | null {\n const targetName = identifier.name;\n\n for (const ancestor of ancestors) {\n // Check FunctionDeclaration\n if (\n ancestor.type === \"FunctionDeclaration\" &&\n ancestor.id?.name === targetName\n ) {\n return ancestor.body;\n }\n\n // Check VariableDeclaration\n if (ancestor.type === \"VariableDeclaration\") {\n for (const declarator of ancestor.declarations) {\n if (\n declarator.id.type === \"Identifier\" &&\n declarator.id.name === targetName &&\n declarator.init\n ) {\n // Check if the init is a function\n if (\n declarator.init.type === \"ArrowFunctionExpression\" ||\n declarator.init.type === \"FunctionExpression\"\n ) {\n return declarator.init.body;\n }\n }\n }\n }\n\n // Check BlockStatement and Program for contained declarations\n if (ancestor.type === \"BlockStatement\" || ancestor.type === \"Program\") {\n const body =\n ancestor.type === \"Program\" ? ancestor.body : ancestor.body;\n\n for (const statement of body) {\n // Function declarations in block\n if (\n statement.type === \"FunctionDeclaration\" &&\n statement.id?.name === targetName\n ) {\n return statement.body;\n }\n\n // Variable declarations in block\n if (statement.type === \"VariableDeclaration\") {\n for (const declarator of statement.declarations) {\n if (\n declarator.id.type === \"Identifier\" &&\n declarator.id.name === targetName &&\n declarator.init\n ) {\n if (\n declarator.init.type === \"ArrowFunctionExpression\" ||\n declarator.init.type === \"FunctionExpression\"\n ) {\n return declarator.init.body;\n }\n }\n }\n }\n\n // Export declarations\n if (statement.type === \"ExportNamedDeclaration\" && statement.declaration) {\n if (\n statement.declaration.type === \"FunctionDeclaration\" &&\n statement.declaration.id?.name === targetName\n ) {\n return statement.declaration.body;\n }\n\n if (statement.declaration.type === \"VariableDeclaration\") {\n for (const declarator of statement.declaration.declarations) {\n if (\n declarator.id.type === \"Identifier\" &&\n declarator.id.name === targetName &&\n declarator.init\n ) {\n if (\n declarator.init.type === \"ArrowFunctionExpression\" ||\n declarator.init.type === \"FunctionExpression\"\n ) {\n return declarator.init.body;\n }\n }\n }\n }\n }\n }\n }\n\n // Check function body for nested declarations (component functions)\n if (\n ancestor.type === \"ArrowFunctionExpression\" ||\n ancestor.type === \"FunctionExpression\" ||\n ancestor.type === \"FunctionDeclaration\"\n ) {\n const funcBody = ancestor.body;\n\n // Only check BlockStatement bodies (not expression bodies)\n if (funcBody.type === \"BlockStatement\") {\n for (const statement of funcBody.body) {\n if (\n statement.type === \"FunctionDeclaration\" &&\n statement.id?.name === targetName\n ) {\n return statement.body;\n }\n\n if (statement.type === \"VariableDeclaration\") {\n for (const declarator of statement.declarations) {\n if (\n declarator.id.type === \"Identifier\" &&\n declarator.id.name === targetName &&\n declarator.init\n ) {\n if (\n declarator.init.type === \"ArrowFunctionExpression\" ||\n declarator.init.type === \"FunctionExpression\"\n ) {\n return declarator.init.body;\n }\n }\n }\n }\n }\n }\n }\n }\n\n return null;\n}\n\n/**\n * Get statement IDs for all event handlers on a JSX element\n *\n * For each event handler attribute on the element:\n * - Extract the handler expression\n * - For inline arrows: find statements within the arrow body range\n * - For identifier references: find the handler declaration and its body range\n *\n * @param jsxNode - The JSX element to analyze\n * @param fileCoverage - Istanbul coverage data for the file\n * @param ancestors - Optional ancestor nodes for identifier resolution\n * @returns Set of statement IDs that are part of event handler bodies\n */\nexport function getHandlerStatements(\n jsxNode: TSESTree.JSXElement,\n fileCoverage: IstanbulFileCoverage,\n ancestors: TSESTree.Node[] = []\n): Set<string> {\n const handlerStatements = new Set<string>();\n\n for (const attr of jsxNode.openingElement.attributes) {\n // Skip non-event-handler attributes\n if (!isEventHandlerAttribute(attr) || attr.type !== \"JSXAttribute\") {\n continue;\n }\n\n const expression = extractEventHandlerExpression(attr);\n if (!expression) {\n continue;\n }\n\n // Handle inline arrow functions and function expressions\n if (\n expression.type === \"ArrowFunctionExpression\" ||\n expression.type === \"FunctionExpression\"\n ) {\n const body = expression.body;\n if (body.loc) {\n const bodyStatements = findStatementsInRange(body.loc, fileCoverage);\n for (const stmtId of bodyStatements) {\n handlerStatements.add(stmtId);\n }\n }\n continue;\n }\n\n // Handle identifier references (e.g., onClick={handleClick})\n if (expression.type === \"Identifier\") {\n const functionBody = findHandlerFunctionDeclaration(expression, ancestors);\n if (functionBody && functionBody.loc) {\n const bodyStatements = findStatementsInRange(functionBody.loc, fileCoverage);\n for (const stmtId of bodyStatements) {\n handlerStatements.add(stmtId);\n }\n }\n continue;\n }\n\n // Handle call expressions (e.g., onClick={fn.bind(this)})\n // We analyze the entire call expression range\n if (expression.type === \"CallExpression\" && expression.loc) {\n const callStatements = findStatementsInRange(expression.loc, fileCoverage);\n for (const stmtId of callStatements) {\n handlerStatements.add(stmtId);\n }\n continue;\n }\n\n // Handle member expressions (e.g., onClick={obj.method})\n // These typically point to methods that we can't easily resolve\n // without more complex analysis, so we just note the expression location\n if (expression.type === \"MemberExpression\" && expression.loc) {\n const memberStatements = findStatementsInRange(expression.loc, fileCoverage);\n for (const stmtId of memberStatements) {\n handlerStatements.add(stmtId);\n }\n }\n }\n\n return handlerStatements;\n}\n\n// =============================================================================\n// Phase 3: Conditional Parent Analysis\n// =============================================================================\n\n/**\n * Find a conditional ancestor that controls this element's rendering\n *\n * Walks up the ancestor chain to find the first conditional expression\n * that determines whether this JSX element is rendered:\n * - LogicalExpression with `&&`: {condition && <Element />}\n * - ConditionalExpression (ternary): {condition ? <A /> : <B />}\n *\n * @param node - The current JSX element node\n * @param ancestors - The ancestor nodes from the AST traversal\n * @returns The conditional expression if found, null otherwise\n */\nexport function findConditionalAncestor(\n node: TSESTree.Node,\n ancestors: TSESTree.Node[]\n): TSESTree.LogicalExpression | TSESTree.ConditionalExpression | null {\n for (const ancestor of ancestors) {\n // Check for logical expression with && operator\n // Pattern: {condition && <Element />}\n if (\n ancestor.type === \"LogicalExpression\" &&\n ancestor.operator === \"&&\"\n ) {\n return ancestor;\n }\n\n // Check for conditional (ternary) expression\n // Pattern: {condition ? <A /> : <B />}\n if (ancestor.type === \"ConditionalExpression\") {\n return ancestor;\n }\n\n // Stop searching when we hit a JSX element boundary\n // (we don't want to cross into parent JSX elements)\n if (ancestor.type === \"JSXElement\" && ancestor !== node) {\n break;\n }\n\n // Stop at function boundaries\n if (\n ancestor.type === \"ArrowFunctionExpression\" ||\n ancestor.type === \"FunctionExpression\" ||\n ancestor.type === \"FunctionDeclaration\"\n ) {\n break;\n }\n }\n\n return null;\n}\n\n/**\n * Get statement IDs for the condition/test part of a conditional expression\n *\n * For LogicalExpression (&&): gets statements in the left operand (the condition)\n * For ConditionalExpression (ternary): gets statements in the test expression\n *\n * @param conditional - The conditional expression node\n * @param fileCoverage - Istanbul coverage data for the file\n * @returns Set of statement IDs that are part of the condition\n */\nexport function getConditionalStatements(\n conditional: TSESTree.LogicalExpression | TSESTree.ConditionalExpression,\n fileCoverage: IstanbulFileCoverage\n): Set<string> {\n const conditionStatements = new Set<string>();\n\n if (conditional.type === \"LogicalExpression\") {\n // For &&, the left side is the condition\n const condition = conditional.left;\n if (condition.loc) {\n const statements = findStatementsInRange(condition.loc, fileCoverage);\n for (const stmtId of statements) {\n conditionStatements.add(stmtId);\n }\n }\n } else if (conditional.type === \"ConditionalExpression\") {\n // For ternary, the test is the condition\n const condition = conditional.test;\n if (condition.loc) {\n const statements = findStatementsInRange(condition.loc, fileCoverage);\n for (const stmtId of statements) {\n conditionStatements.add(stmtId);\n }\n }\n }\n\n return conditionStatements;\n}\n\n// =============================================================================\n// Phase 4: Import Dependency Coverage\n// =============================================================================\n\n/**\n * Recursively collect all identifiers used within a node\n *\n * @param node - AST node to traverse\n * @param identifiers - Set to accumulate identifier names\n */\nfunction collectIdentifiersFromNode(\n node: TSESTree.Node,\n identifiers: Set<string>\n): void {\n switch (node.type) {\n case \"Identifier\":\n identifiers.add(node.name);\n break;\n\n case \"JSXIdentifier\":\n // JSX element names (e.g., <Component />) - these are components from imports\n identifiers.add(node.name);\n break;\n\n case \"JSXExpressionContainer\":\n if (node.expression.type !== \"JSXEmptyExpression\") {\n collectIdentifiersFromNode(node.expression, identifiers);\n }\n break;\n\n case \"JSXElement\":\n // Collect from opening element (tag name and attributes)\n collectIdentifiersFromNode(node.openingElement, identifiers);\n // Collect from children\n for (const child of node.children) {\n collectIdentifiersFromNode(child, identifiers);\n }\n break;\n\n case \"JSXOpeningElement\":\n // Collect from element name\n collectIdentifiersFromNode(node.name, identifiers);\n // Collect from attributes\n for (const attr of node.attributes) {\n collectIdentifiersFromNode(attr, identifiers);\n }\n break;\n\n case \"JSXAttribute\":\n // Collect from attribute value if it exists\n if (node.value) {\n collectIdentifiersFromNode(node.value, identifiers);\n }\n break;\n\n case \"JSXSpreadAttribute\":\n collectIdentifiersFromNode(node.argument, identifiers);\n break;\n\n case \"JSXMemberExpression\":\n // e.g., <Foo.Bar /> - collect the object\n collectIdentifiersFromNode(node.object, identifiers);\n break;\n\n case \"CallExpression\":\n collectIdentifiersFromNode(node.callee, identifiers);\n for (const arg of node.arguments) {\n collectIdentifiersFromNode(arg, identifiers);\n }\n break;\n\n case \"MemberExpression\":\n collectIdentifiersFromNode(node.object, identifiers);\n break;\n\n case \"ArrowFunctionExpression\":\n case \"FunctionExpression\":\n collectIdentifiersFromNode(node.body, identifiers);\n break;\n\n case \"BlockStatement\":\n for (const statement of node.body) {\n collectIdentifiersFromNode(statement, identifiers);\n }\n break;\n\n case \"ExpressionStatement\":\n collectIdentifiersFromNode(node.expression, identifiers);\n break;\n\n case \"ReturnStatement\":\n if (node.argument) {\n collectIdentifiersFromNode(node.argument, identifiers);\n }\n break;\n\n case \"BinaryExpression\":\n case \"LogicalExpression\":\n collectIdentifiersFromNode(node.left, identifiers);\n collectIdentifiersFromNode(node.right, identifiers);\n break;\n\n case \"ConditionalExpression\":\n collectIdentifiersFromNode(node.test, identifiers);\n collectIdentifiersFromNode(node.consequent, identifiers);\n collectIdentifiersFromNode(node.alternate, identifiers);\n break;\n\n case \"UnaryExpression\":\n collectIdentifiersFromNode(node.argument, identifiers);\n break;\n\n case \"TemplateLiteral\":\n for (const expr of node.expressions) {\n collectIdentifiersFromNode(expr, identifiers);\n }\n break;\n\n case \"ArrayExpression\":\n for (const element of node.elements) {\n if (element) {\n collectIdentifiersFromNode(element, identifiers);\n }\n }\n break;\n\n case \"ObjectExpression\":\n for (const prop of node.properties) {\n collectIdentifiersFromNode(prop, identifiers);\n }\n break;\n\n case \"Property\":\n collectIdentifiersFromNode(node.value, identifiers);\n break;\n\n case \"SpreadElement\":\n collectIdentifiersFromNode(node.argument, identifiers);\n break;\n\n case \"JSXText\":\n case \"JSXFragment\":\n case \"Literal\":\n // No identifiers in these\n break;\n\n default:\n // For other node types, we don't recurse to avoid complexity\n break;\n }\n}\n\n/**\n * Find imports used within a JSX element\n *\n * Identifies which imported modules are used within the JSX element by:\n * 1. Collecting all identifiers used in the JSX (props, children expressions, etc.)\n * 2. Walking up to the Program node to find ImportDeclaration nodes\n * 3. Matching used identifiers to their import sources\n *\n * @param jsxNode - The JSX element to analyze\n * @param ancestors - Ancestor nodes from AST traversal (should include Program)\n * @returns Set of import module specifiers (the 'from' paths) used in this JSX\n */\nexport function findImportsUsedInJSX(\n jsxNode: TSESTree.JSXElement,\n ancestors: TSESTree.Node[]\n): Set<string> {\n const importPaths = new Set<string>();\n\n // Step 1: Collect all identifiers used in this JSX element\n const usedIdentifiers = new Set<string>();\n collectIdentifiersFromNode(jsxNode, usedIdentifiers);\n\n // Step 2: Find the Program node in ancestors to access imports\n let programNode: TSESTree.Program | null = null;\n for (const ancestor of ancestors) {\n if (ancestor.type === \"Program\") {\n programNode = ancestor;\n break;\n }\n }\n\n if (!programNode) {\n return importPaths;\n }\n\n // Step 3: Build a map of imported identifiers to their module sources\n const importedIdentifiers = new Map<string, string>();\n\n for (const statement of programNode.body) {\n if (statement.type === \"ImportDeclaration\") {\n const source = statement.source.value;\n if (typeof source !== \"string\") {\n continue;\n }\n\n for (const specifier of statement.specifiers) {\n switch (specifier.type) {\n case \"ImportDefaultSpecifier\":\n // import Foo from 'module' -> Foo maps to 'module'\n importedIdentifiers.set(specifier.local.name, source);\n break;\n\n case \"ImportSpecifier\":\n // import { Foo } from 'module' -> Foo maps to 'module'\n // import { Foo as Bar } from 'module' -> Bar maps to 'module'\n importedIdentifiers.set(specifier.local.name, source);\n break;\n\n case \"ImportNamespaceSpecifier\":\n // import * as Foo from 'module' -> Foo maps to 'module'\n importedIdentifiers.set(specifier.local.name, source);\n break;\n }\n }\n }\n }\n\n // Step 4: Match used identifiers to their import sources\n for (const identifier of usedIdentifiers) {\n const importSource = importedIdentifiers.get(identifier);\n if (importSource) {\n importPaths.add(importSource);\n }\n }\n\n return importPaths;\n}\n\n/**\n * Resolve an import path to a file path\n *\n * Handles relative imports by resolving them against the current file's directory.\n * For non-relative imports (node_modules), returns null as we don't analyze those.\n *\n * @param importPath - The import specifier (e.g., './utils' or 'react')\n * @param currentFilePath - Path of the file containing the import\n * @param projectRoot - Project root directory\n * @returns Resolved file path or null if can't be resolved\n */\nfunction resolveImportPath(\n importPath: string,\n currentFilePath: string,\n projectRoot: string\n): string | null {\n // Skip non-relative imports (node_modules packages)\n if (!importPath.startsWith(\".\") && !importPath.startsWith(\"/\")) {\n return null;\n }\n\n // Get the directory of the current file\n const lastSlashIndex = currentFilePath.lastIndexOf(\"/\");\n const currentDir =\n lastSlashIndex >= 0 ? currentFilePath.slice(0, lastSlashIndex) : projectRoot;\n\n // Resolve the relative path\n let resolvedPath: string;\n if (importPath.startsWith(\"/\")) {\n // Absolute from project root\n resolvedPath = projectRoot + importPath;\n } else {\n // Relative path - need to resolve . and ..\n const parts = currentDir.split(\"/\").filter(Boolean);\n const importParts = importPath.split(\"/\");\n\n for (const part of importParts) {\n if (part === \".\") {\n continue;\n } else if (part === \"..\") {\n parts.pop();\n } else {\n parts.push(part);\n }\n }\n\n resolvedPath = \"/\" + parts.join(\"/\");\n }\n\n // Common extensions to try\n const extensions = [\"\", \".ts\", \".tsx\", \".js\", \".jsx\", \"/index.ts\", \"/index.tsx\", \"/index.js\", \"/index.jsx\"];\n\n for (const ext of extensions) {\n const fullPath = resolvedPath + ext;\n // We just return the resolved path - the caller will check if coverage exists\n // This is a simplified resolution that doesn't check file existence\n if (ext === \"\" && (resolvedPath.endsWith(\".ts\") || resolvedPath.endsWith(\".tsx\") ||\n resolvedPath.endsWith(\".js\") || resolvedPath.endsWith(\".jsx\"))) {\n return resolvedPath;\n }\n if (ext !== \"\") {\n return fullPath;\n }\n }\n\n return resolvedPath;\n}\n\n/**\n * Aggregate coverage from imported files\n *\n * For each import path, attempts to find coverage data for that file\n * and aggregates the statement coverage across all imported files.\n *\n * Note: When an import is used, the entire imported file's coverage is included\n * in the calculation (full file coverage when any part of a dependency is used).\n *\n * @param importPaths - Set of import module specifiers\n * @param coverage - Istanbul coverage data\n * @param projectRoot - Project root directory\n * @param currentFilePath - Path of the file containing the imports (for resolving relative paths)\n * @returns Aggregated coverage stats: { covered: number, total: number }\n */\nexport function aggregateImportCoverage(\n importPaths: Set<string>,\n coverage: IstanbulCoverage,\n projectRoot: string,\n currentFilePath: string = \"\"\n): { covered: number; total: number } {\n let totalCovered = 0;\n let totalStatements = 0;\n\n for (const importPath of importPaths) {\n // Try to resolve the import path to a file path\n const resolvedPath = resolveImportPath(importPath, currentFilePath, projectRoot);\n\n if (!resolvedPath) {\n // Non-relative import (node_modules) - skip\n continue;\n }\n\n // Try to find coverage for this file\n const fileCoverage = findCoverageForFile(coverage, resolvedPath);\n\n if (!fileCoverage) {\n // Also try with the raw import path in case coverage uses that format\n const rawCoverage = findCoverageForFile(coverage, importPath);\n if (!rawCoverage) {\n continue;\n }\n // Use the raw coverage\n const statementCount = Object.keys(rawCoverage.s).length;\n const coveredCount = Object.values(rawCoverage.s).filter((hits) => hits > 0).length;\n totalStatements += statementCount;\n totalCovered += coveredCount;\n continue;\n }\n\n // Aggregate full file coverage (all statements in the imported file)\n const statementCount = Object.keys(fileCoverage.s).length;\n const coveredCount = Object.values(fileCoverage.s).filter((hits) => hits > 0).length;\n\n totalStatements += statementCount;\n totalCovered += coveredCount;\n }\n\n return { covered: totalCovered, total: totalStatements };\n}\n","/**\n * Chunk Analyzer\n *\n * Analyzes individual \"chunks\" (exported functions, classes, hooks, stores)\n * for test coverage. Used for granular coverage reporting at the function level\n * instead of file level.\n *\n * Categories:\n * - utility: formatters, validators, helpers (strict threshold)\n * - hook: React hooks (use* pattern) (strict threshold)\n * - store: Zustand/Redux stores (strict threshold)\n * - handler: event handler functions (relaxed threshold)\n * - component: JSX-returning functions (relaxed threshold)\n */\n\nimport type { TSESTree } from \"@typescript-eslint/utils\";\nimport {\n findStatementsInRange,\n calculateCoverageFromStatements,\n type IstanbulFileCoverage,\n type SourceLocation,\n type CoverageStats,\n} from \"./jsx-coverage-analyzer.js\";\n\nexport type ChunkCategory =\n | \"utility\"\n | \"hook\"\n | \"store\"\n | \"handler\"\n | \"component\";\n\nexport interface ChunkInfo {\n /** Function/export name */\n name: string;\n /** Category of this chunk */\n category: ChunkCategory;\n /** Whether this is React-related code */\n isReactRelated: boolean;\n /** Location in source (full function body) */\n loc: SourceLocation;\n /** Location of just the declaration line (for error highlighting) */\n declarationLoc: SourceLocation;\n /** Function ID in fnMap (if found) */\n fnId: string | null;\n /** Is this an export? */\n isExport: boolean;\n}\n\nexport interface ChunkCoverageResult extends ChunkInfo {\n /** Coverage stats for this chunk */\n coverage: {\n /** Was the function ever called during tests? */\n functionCalled: boolean;\n /** Number of statements covered */\n statementsCovered: number;\n /** Total statements in the function */\n statementsTotal: number;\n /** Coverage percentage (0-100) */\n percentage: number;\n };\n}\n\n/**\n * Check if an AST node contains JSX elements\n */\nfunction containsJSX(\n node: TSESTree.Node,\n visited: WeakSet<object> = new WeakSet()\n): boolean {\n if (!node || typeof node !== \"object\") return false;\n if (visited.has(node)) return false;\n visited.add(node);\n\n if (\n node.type === \"JSXElement\" ||\n node.type === \"JSXFragment\" ||\n node.type === \"JSXText\"\n ) {\n return true;\n }\n\n // Only traverse known child properties to avoid parent references\n const childKeys = [\n \"body\",\n \"declarations\",\n \"declaration\",\n \"expression\",\n \"expressions\",\n \"argument\",\n \"arguments\",\n \"callee\",\n \"elements\",\n \"properties\",\n \"value\",\n \"init\",\n \"consequent\",\n \"alternate\",\n \"test\",\n \"left\",\n \"right\",\n \"object\",\n \"property\",\n \"children\",\n \"openingElement\",\n \"closingElement\",\n ];\n\n for (const key of childKeys) {\n const child = (node as unknown as Record<string, unknown>)[key];\n if (child && typeof child === \"object\") {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (item && typeof item === \"object\" && \"type\" in item) {\n if (containsJSX(item as TSESTree.Node, visited)) return true;\n }\n }\n } else if (\"type\" in child) {\n if (containsJSX(child as TSESTree.Node, visited)) return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Categorize a function based on its name and content\n */\nexport function categorizeChunk(\n name: string,\n functionBody: TSESTree.Node | null,\n isInStoreFile: boolean\n): { category: ChunkCategory; isReactRelated: boolean } {\n // Hooks: starts with \"use\" followed by uppercase\n if (/^use[A-Z]/.test(name)) {\n return { category: \"hook\", isReactRelated: true };\n }\n\n // Store: in a .store.ts file or name ends with Store\n if (isInStoreFile || /Store$/.test(name)) {\n return { category: \"store\", isReactRelated: false };\n }\n\n // Handler: starts with \"handle\" or \"on\" followed by uppercase\n if (/^handle[A-Z]/.test(name) || /^on[A-Z]/.test(name)) {\n return { category: \"handler\", isReactRelated: true };\n }\n\n // Component: contains JSX (check the function body)\n if (functionBody && containsJSX(functionBody)) {\n return { category: \"component\", isReactRelated: true };\n }\n\n // Default: utility\n return { category: \"utility\", isReactRelated: false };\n}\n\n/**\n * Extract function name from various AST patterns\n */\nfunction getFunctionName(\n node:\n | TSESTree.FunctionDeclaration\n | TSESTree.ArrowFunctionExpression\n | TSESTree.FunctionExpression,\n parent: TSESTree.Node | undefined\n): string | null {\n // Named function declaration\n if (node.type === \"FunctionDeclaration\" && node.id) {\n return node.id.name;\n }\n\n // Variable declarator: const foo = () => {}\n if (parent?.type === \"VariableDeclarator\" && parent.id.type === \"Identifier\") {\n return parent.id.name;\n }\n\n // Property: { foo: () => {} }\n if (parent?.type === \"Property\" && parent.key.type === \"Identifier\") {\n return parent.key.name;\n }\n\n return null;\n}\n\n/**\n * Find function ID in fnMap by matching name and location\n */\nfunction findFnId(\n name: string,\n loc: SourceLocation,\n fileCoverage: IstanbulFileCoverage | null\n): string | null {\n if (!fileCoverage) return null;\n\n for (const [fnId, fnInfo] of Object.entries(fileCoverage.fnMap)) {\n // Match by name first\n if (fnInfo.name === name) {\n return fnId;\n }\n\n // If name doesn't match (anonymous or different), match by location\n // Function declaration location should be close to our AST location\n if (\n fnInfo.decl.start.line === loc.start.line ||\n fnInfo.loc.start.line === loc.start.line\n ) {\n return fnId;\n }\n }\n\n return null;\n}\n\n/**\n * Calculate coverage for a specific function\n */\nfunction calculateChunkCoverage(\n fnId: string | null,\n loc: SourceLocation,\n fileCoverage: IstanbulFileCoverage | null\n): ChunkCoverageResult[\"coverage\"] {\n if (!fileCoverage) {\n return {\n functionCalled: false,\n statementsCovered: 0,\n statementsTotal: 0,\n percentage: 0,\n };\n }\n\n // Check if function was called\n const functionCalled = fnId !== null && (fileCoverage.f[fnId] ?? 0) > 0;\n\n // Find statements within function body\n const statementIds = findStatementsInRange(loc, fileCoverage);\n const stats = calculateCoverageFromStatements(statementIds, fileCoverage);\n\n return {\n functionCalled,\n statementsCovered: stats.covered,\n statementsTotal: stats.total,\n percentage: stats.percentage,\n };\n}\n\n/**\n * Collect all exported functions/classes from an AST\n */\ninterface ExportedFunction {\n name: string;\n node:\n | TSESTree.FunctionDeclaration\n | TSESTree.ArrowFunctionExpression\n | TSESTree.FunctionExpression;\n loc: SourceLocation;\n /** Location of just the declaration line (for highlighting) */\n declarationLoc: SourceLocation;\n body: TSESTree.Node | null;\n}\n\n/**\n * Get declaration location - just the first line of the function\n * This is used for error highlighting to avoid highlighting the entire function body\n */\nfunction getDeclarationLoc(loc: SourceLocation): SourceLocation {\n return {\n start: loc.start,\n end: { line: loc.start.line, column: 999 },\n };\n}\n\nfunction collectExportedFunctions(ast: TSESTree.Program): ExportedFunction[] {\n const exports: ExportedFunction[] = [];\n\n for (const node of ast.body) {\n // export function foo() {}\n if (\n node.type === \"ExportNamedDeclaration\" &&\n node.declaration?.type === \"FunctionDeclaration\" &&\n node.declaration.id\n ) {\n const loc = node.declaration.loc;\n exports.push({\n name: node.declaration.id.name,\n node: node.declaration,\n loc,\n declarationLoc: getDeclarationLoc(loc),\n body: node.declaration.body,\n });\n }\n\n // export const foo = () => {}\n if (\n node.type === \"ExportNamedDeclaration\" &&\n node.declaration?.type === \"VariableDeclaration\"\n ) {\n for (const decl of node.declaration.declarations) {\n if (\n decl.id.type === \"Identifier\" &&\n decl.init &&\n (decl.init.type === \"ArrowFunctionExpression\" ||\n decl.init.type === \"FunctionExpression\")\n ) {\n // For arrow functions, use the variable declaration line, not the function body\n const loc = decl.init.loc;\n const declarationLoc = getDeclarationLoc(decl.loc);\n exports.push({\n name: decl.id.name,\n node: decl.init,\n loc,\n declarationLoc,\n body: decl.init.body,\n });\n }\n }\n }\n\n // export default function foo() {}\n if (\n node.type === \"ExportDefaultDeclaration\" &&\n node.declaration.type === \"FunctionDeclaration\"\n ) {\n const name = node.declaration.id?.name ?? \"default\";\n const loc = node.declaration.loc;\n exports.push({\n name,\n node: node.declaration,\n loc,\n declarationLoc: getDeclarationLoc(loc),\n body: node.declaration.body,\n });\n }\n\n // export default () => {}\n if (\n node.type === \"ExportDefaultDeclaration\" &&\n (node.declaration.type === \"ArrowFunctionExpression\" ||\n node.declaration.type === \"FunctionExpression\")\n ) {\n const loc = node.declaration.loc;\n exports.push({\n name: \"default\",\n node: node.declaration,\n loc,\n declarationLoc: getDeclarationLoc(loc),\n body: node.declaration.body,\n });\n }\n }\n\n return exports;\n}\n\n/**\n * Analyze all exported chunks in a file for coverage\n *\n * @param ast - The parsed AST of the file\n * @param filePath - Path to the file (used for store detection)\n * @param fileCoverage - Istanbul coverage data for the file (or null)\n * @returns Array of chunks with their coverage information\n */\nexport function analyzeChunks(\n ast: TSESTree.Program,\n filePath: string,\n fileCoverage: IstanbulFileCoverage | null\n): ChunkCoverageResult[] {\n const isInStoreFile = /\\.store\\.(ts|tsx)$/.test(filePath);\n const exportedFunctions = collectExportedFunctions(ast);\n const results: ChunkCoverageResult[] = [];\n\n for (const exported of exportedFunctions) {\n const { category, isReactRelated } = categorizeChunk(\n exported.name,\n exported.body,\n isInStoreFile\n );\n\n const fnId = findFnId(exported.name, exported.loc, fileCoverage);\n const coverage = calculateChunkCoverage(fnId, exported.loc, fileCoverage);\n\n results.push({\n name: exported.name,\n category,\n isReactRelated,\n loc: exported.loc,\n declarationLoc: exported.declarationLoc,\n fnId,\n isExport: true,\n coverage,\n });\n }\n\n return results;\n}\n\n/**\n * Get the appropriate threshold for a chunk based on options\n */\nexport function getChunkThreshold(\n chunk: ChunkCoverageResult,\n options: {\n focusNonReact?: boolean;\n chunkThreshold?: number;\n relaxedThreshold?: number;\n }\n): number {\n const strictThreshold = options.chunkThreshold ?? 80;\n const relaxedThreshold = options.relaxedThreshold ?? 50;\n\n if (!options.focusNonReact) {\n // Uniform threshold for all chunks\n return strictThreshold;\n }\n\n // focusNonReact mode: relaxed threshold for React-related code\n if (chunk.category === \"component\" || chunk.category === \"handler\") {\n return relaxedThreshold;\n }\n\n // Strict threshold for utility, hook, store\n return strictThreshold;\n}\n"],"mappings":"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","names":["meta","existsSync","readFileSync","statSync","dirname","join","basename","existsSync","parse","readFileSync","existsSync","existsSync","readFileSync","parse","existsSync","findCoverageForFile","resolveImportPath","statementCount","coveredCount","findProjectRoot","dirname","existsSync","join","statSync","readFileSync","findCoverageForFile","basename"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/create-rule.ts","../../src/rules/require-test-coverage/index.ts","../../src/rules/require-test-coverage/lib/file-categorizer.ts","../../src/rules/require-test-coverage/lib/dependency-graph.ts","../../src/rules/require-test-coverage/lib/export-resolver.ts","../../src/rules/require-test-coverage/lib/coverage-aggregator.ts","../../src/rules/require-test-coverage/lib/jsx-coverage-analyzer.ts","../../src/rules/require-test-coverage/lib/chunk-analyzer.ts"],"sourcesContent":["/**\n * Rule creation helper using @typescript-eslint/utils\n */\n\nimport { ESLintUtils } from \"@typescript-eslint/utils\";\n\nexport const createRule = ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/peter-suggate/uilint/blob/main/packages/uilint-eslint/docs/rules/${name}.md`\n);\n\n/**\n * Schema for prompting user to configure a rule option in the CLI\n */\nexport interface OptionFieldSchema {\n /** Field name in the options object */\n key: string;\n /** Display label for the prompt */\n label: string;\n /** Prompt type */\n type: \"text\" | \"number\" | \"boolean\" | \"select\" | \"multiselect\";\n /** Default value */\n defaultValue: unknown;\n /** Placeholder text (for text/number inputs) */\n placeholder?: string;\n /** Options for select/multiselect */\n options?: Array<{ value: string | number; label: string }>;\n /** Description/hint for the field */\n description?: string;\n}\n\n/**\n * Schema describing how to prompt for rule options during installation\n */\nexport interface RuleOptionSchema {\n /** Fields that can be configured for this rule */\n fields: OptionFieldSchema[];\n}\n\n/**\n * External requirement that a rule needs to function\n */\nexport interface RuleRequirement {\n /** Requirement type for programmatic checks */\n type: \"ollama\" | \"git\" | \"coverage\" | \"semantic-index\" | \"styleguide\";\n /** Human-readable description */\n description: string;\n /** Optional: how to satisfy the requirement */\n setupHint?: string;\n}\n\n/**\n * Rule migration definition for updating rule options between versions\n */\nexport interface RuleMigration {\n /** Source version (semver) */\n from: string;\n /** Target version (semver) */\n to: string;\n /** Human-readable description of what changed */\n description: string;\n /** Function to migrate options from old format to new format */\n migrate: (oldOptions: unknown[]) => unknown[];\n /** Whether this migration contains breaking changes */\n breaking?: boolean;\n}\n\n/**\n * Colocated rule metadata - exported alongside each rule\n *\n * This structure keeps all rule metadata in the same file as the rule implementation,\n * making it easy to maintain and extend as new rules are added.\n */\nexport interface RuleMeta {\n /** Rule identifier (e.g., \"consistent-dark-mode\") - must match filename */\n id: string;\n\n /** Semantic version of the rule (e.g., \"1.0.0\") */\n version: string;\n\n /** Display name for CLI (e.g., \"No Arbitrary Tailwind\") */\n name: string;\n\n /** Short description for CLI selection prompts (one line) */\n description: string;\n\n /** Default severity level */\n defaultSeverity: \"error\" | \"warn\" | \"off\";\n\n /** Category for grouping in CLI */\n category: \"static\" | \"semantic\";\n\n /** Icon for display in CLI/UI (emoji or icon name) */\n icon?: string;\n\n /** Short hint about the rule type/requirements */\n hint?: string;\n\n /** Whether rule is enabled by default during install */\n defaultEnabled?: boolean;\n\n /** External requirements the rule needs */\n requirements?: RuleRequirement[];\n\n /**\n * NPM packages that must be installed for this rule to work.\n * These will be added to the target project's dependencies during installation.\n *\n * Example: [\"xxhash-wasm\"] for rules using the xxhash library\n */\n npmDependencies?: string[];\n\n /** Instructions to show after installation */\n postInstallInstructions?: string;\n\n /** Framework compatibility */\n frameworks?: (\"next\" | \"vite\" | \"cra\" | \"remix\")[];\n\n /** Whether this rule requires a styleguide file */\n requiresStyleguide?: boolean;\n\n /** Default options for the rule (passed as second element in ESLint config) */\n defaultOptions?: unknown[];\n\n /** Schema for prompting user to configure options during install */\n optionSchema?: RuleOptionSchema;\n\n /**\n * Detailed documentation in markdown format.\n * Should include:\n * - What the rule does\n * - Why it's useful\n * - Examples of incorrect and correct code\n * - Configuration options explained\n */\n docs: string;\n\n /**\n * Internal utility dependencies that this rule requires.\n * When the rule is copied to a target project, these utilities\n * will be transformed to import from \"uilint-eslint\" instead\n * of relative paths.\n *\n * Example: [\"coverage-aggregator\", \"dependency-graph\"]\n */\n internalDependencies?: string[];\n\n /**\n * Whether this rule is directory-based (has lib/ folder with utilities).\n * Directory-based rules are installed as folders with index.ts and lib/ subdirectory.\n * Single-file rules are installed as single .ts files.\n *\n * When true, ESLint config imports will use:\n * ./.uilint/rules/rule-id/index.js\n * When false (default):\n * ./.uilint/rules/rule-id.js\n */\n isDirectoryBased?: boolean;\n\n /**\n * Migrations for updating rule options between versions.\n * Migrations are applied in order to transform options from older versions.\n */\n migrations?: RuleMigration[];\n\n /**\n * Which UI plugin should handle this rule.\n * Defaults based on category:\n * - \"static\" category → \"eslint\" plugin\n * - \"semantic\" category → \"semantic\" plugin\n *\n * Special cases:\n * - \"vision\" for semantic-vision rule\n */\n plugin?: \"eslint\" | \"vision\" | \"semantic\";\n\n /**\n * Custom inspector panel ID to use for this rule's issues.\n * If not specified, uses the plugin's default issue inspector.\n *\n * Examples:\n * - \"vision-issue\" for VisionIssueInspector\n * - \"duplicates\" for DuplicatesInspector\n * - \"semantic-issue\" for SemanticIssueInspector\n */\n customInspector?: string;\n\n /**\n * Custom heatmap color for this rule's issues.\n * CSS color value (hex, rgb, hsl, or named color).\n * If not specified, uses severity-based coloring.\n */\n heatmapColor?: string;\n}\n\n/**\n * Helper to define rule metadata with type safety\n */\nexport function defineRuleMeta(meta: RuleMeta): RuleMeta {\n return meta;\n}\n","/**\n * Rule: require-test-coverage\n *\n * Enforces that source files have test coverage above a configurable threshold.\n * Checks for:\n * - Existence of test files\n * - Coverage data in Istanbul JSON format\n * - Statement coverage percentage\n */\n\nimport { createRule, defineRuleMeta } from \"../../utils/create-rule.js\";\nimport { existsSync, readFileSync, statSync } from \"fs\";\nimport { dirname, join, basename, relative } from \"path\";\nimport { execSync } from \"child_process\";\nimport {\n aggregateCoverage,\n type IstanbulCoverage as AggregatorIstanbulCoverage,\n} from \"./lib/coverage-aggregator.js\";\nimport {\n analyzeJSXElementCoverage,\n type IstanbulCoverage as JSXAnalyzerIstanbulCoverage,\n} from \"./lib/jsx-coverage-analyzer.js\";\nimport { analyzeChunks, getChunkThreshold } from \"./lib/chunk-analyzer.js\";\nimport type { TSESTree } from \"@typescript-eslint/utils\";\n\n/**\n * Simple glob pattern matching function\n * Supports: *, **, ?\n */\nfunction simpleGlobMatch(pattern: string, path: string): boolean {\n // Normalize path separators\n const normalizedPath = path.replace(/\\\\/g, \"/\");\n const normalizedPattern = pattern.replace(/\\\\/g, \"/\");\n\n // Escape regex special chars except our glob patterns\n let regexStr = normalizedPattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\") // Escape regex special chars\n .replace(/\\*\\*/g, \"{{GLOBSTAR}}\") // Placeholder for **\n .replace(/\\*/g, \"[^/]*\") // * matches anything except /\n .replace(/\\?/g, \"[^/]\") // ? matches single char except /\n .replace(/{{GLOBSTAR}}/g, \".*\"); // ** matches anything including /\n\n // Add anchors\n const regex = new RegExp(`^${regexStr}$`);\n return regex.test(normalizedPath);\n}\n\ntype MessageIds =\n | \"noCoverage\"\n | \"belowThreshold\"\n | \"noCoverageData\"\n | \"belowAggregateThreshold\"\n | \"jsxBelowThreshold\"\n | \"chunkBelowThreshold\"\n | \"untestedFunction\";\n\ntype SeverityLevel = \"error\" | \"warn\" | \"off\";\n\ntype Options = [\n {\n /** Path to coverage JSON file. Default: \"coverage/coverage-final.json\" */\n coveragePath?: string;\n /** Coverage threshold percentage. Default: 80 */\n threshold?: number;\n /** Pattern-specific thresholds */\n thresholdsByPattern?: Array<{ pattern: string; threshold: number }>;\n /** Severity levels for different issue types */\n severity?: {\n noCoverage?: SeverityLevel;\n belowThreshold?: SeverityLevel;\n };\n /** Patterns to detect test files. Default: [\".test.ts\", \".test.tsx\", \".spec.ts\", \".spec.tsx\", \"__tests__/\"] */\n testPatterns?: string[];\n /** Glob patterns for files to ignore. Default: [\"**\\/*.d.ts\", \"**\\/index.ts\"] */\n ignorePatterns?: string[];\n /** Mode: \"all\" checks all code, \"changed\" only checks git-changed lines. Default: \"all\" */\n mode?: \"all\" | \"changed\";\n /** Base branch for \"changed\" mode. Default: \"main\" */\n baseBranch?: string;\n /** Aggregate coverage threshold for components. Default: 70 */\n aggregateThreshold?: number;\n /** Severity for aggregate coverage check. Default: \"warn\" */\n aggregateSeverity?: SeverityLevel;\n /** JSX element coverage threshold percentage. Default: 50 */\n jsxThreshold?: number;\n /** Severity for JSX element coverage check. Default: \"warn\" */\n jsxSeverity?: SeverityLevel;\n /** Enable chunk-level coverage reporting (replaces file-level). Default: false */\n chunkCoverage?: boolean;\n /** Threshold for strict categories (utility/hook/store). Default: 80 */\n chunkThreshold?: number;\n /** Focus on non-React code with relaxed thresholds for components. Default: false */\n focusNonReact?: boolean;\n /** Threshold for relaxed categories (component/handler). Default: 50 */\n relaxedThreshold?: number;\n /** Severity for chunk coverage. Default: \"warn\" */\n chunkSeverity?: SeverityLevel;\n /** Minimum statements in file to require coverage. Default: 5 */\n minStatements?: number;\n }\n];\n\n/**\n * Istanbul coverage JSON format\n */\ninterface IstanbulCoverage {\n [filePath: string]: {\n path: string;\n statementMap: {\n [key: string]: {\n start: { line: number; column: number };\n end: { line: number; column: number };\n };\n };\n fnMap: {\n [key: string]: {\n name: string;\n decl: {\n start: { line: number; column: number };\n end: { line: number; column: number };\n };\n loc: {\n start: { line: number; column: number };\n end: { line: number; column: number };\n };\n };\n };\n branchMap: {\n [key: string]: {\n loc: {\n start: { line: number; column: number };\n end: { line: number; column: number };\n };\n type: string;\n locations: Array<{\n start: { line: number; column: number };\n end: { line: number; column: number };\n }>;\n };\n };\n s: { [key: string]: number }; // Statement hit counts\n f: { [key: string]: number }; // Function hit counts\n b: { [key: string]: number[] }; // Branch hit counts\n };\n}\n\n/**\n * Rule metadata\n */\nexport const meta = defineRuleMeta({\n id: \"require-test-coverage\",\n version: \"1.0.0\",\n name: \"Require Test Coverage\",\n description: \"Enforce that source files have adequate test coverage\",\n defaultSeverity: \"warn\",\n category: \"static\",\n icon: \"🧪\",\n hint: \"Ensures code has tests\",\n defaultEnabled: true,\n isDirectoryBased: true,\n requirements: [\n {\n type: \"coverage\",\n description: \"Requires test coverage data\",\n setupHint: \"Run tests with coverage: npm test -- --coverage\",\n },\n ],\n defaultOptions: [\n {\n coveragePath: \"coverage/coverage-final.json\",\n threshold: 80,\n thresholdsByPattern: [],\n severity: {\n noCoverage: \"error\",\n belowThreshold: \"warn\",\n },\n testPatterns: [\n \".test.ts\",\n \".test.tsx\",\n \".spec.ts\",\n \".spec.tsx\",\n \"__tests__/\",\n ],\n ignorePatterns: [\"**/*.d.ts\", \"**/index.ts\"],\n mode: \"all\",\n baseBranch: \"main\",\n },\n ],\n optionSchema: {\n fields: [\n {\n key: \"threshold\",\n label: \"Coverage threshold\",\n type: \"number\",\n defaultValue: 80,\n description: \"Minimum coverage percentage required (0-100)\",\n },\n {\n key: \"coveragePath\",\n label: \"Coverage file path\",\n type: \"text\",\n defaultValue: \"coverage/coverage-final.json\",\n description: \"Path to Istanbul coverage JSON file\",\n },\n {\n key: \"mode\",\n label: \"Mode\",\n type: \"select\",\n defaultValue: \"all\",\n options: [\n { value: \"all\", label: \"Check all code\" },\n { value: \"changed\", label: \"Only check changed lines\" },\n ],\n description: \"Whether to check all code or only git-changed lines\",\n },\n {\n key: \"chunkCoverage\",\n label: \"Enable chunk-level coverage\",\n type: \"boolean\",\n defaultValue: true,\n description:\n \"Report coverage for individual functions instead of file level\",\n },\n {\n key: \"focusNonReact\",\n label: \"Focus on non-React code\",\n type: \"boolean\",\n defaultValue: false,\n description:\n \"Apply strict thresholds to utilities/stores/hooks, relaxed to components\",\n },\n {\n key: \"chunkThreshold\",\n label: \"Chunk coverage threshold\",\n type: \"number\",\n defaultValue: 80,\n description:\n \"Minimum coverage for utility/hook/store chunks (0-100)\",\n },\n {\n key: \"relaxedThreshold\",\n label: \"Relaxed threshold for React code\",\n type: \"number\",\n defaultValue: 50,\n description:\n \"Threshold for components/handlers when focusNonReact is enabled\",\n },\n {\n key: \"minStatements\",\n label: \"Minimum statements for coverage\",\n type: \"number\",\n defaultValue: 5,\n description:\n \"Files with fewer statements are exempt from coverage requirements\",\n },\n ],\n },\n docs: `\n## What it does\n\nEnforces that source files have test coverage above a configurable threshold.\nIt checks for:\n- Existence of corresponding test files\n- Coverage data in Istanbul JSON format\n- Statement coverage percentage meeting the threshold\n\n## Why it's useful\n\n- **Quality Assurance**: Ensures critical code is tested\n- **Catch Regressions**: Prevents merging untested changes\n- **Configurable**: Different thresholds for different file patterns\n- **Git Integration**: Can focus only on changed lines\n\n## Configuration\n\n\\`\\`\\`js\n// eslint.config.js\n\"uilint/require-test-coverage\": [\"warn\", {\n coveragePath: \"coverage/coverage-final.json\",\n threshold: 80,\n thresholdsByPattern: [\n { pattern: \"**/utils/*.ts\", threshold: 90 },\n { pattern: \"**/generated/**\", threshold: 0 },\n ],\n severity: {\n noCoverage: \"error\",\n belowThreshold: \"warn\",\n },\n testPatterns: [\".test.ts\", \".test.tsx\", \".spec.ts\", \".spec.tsx\", \"__tests__/\"],\n ignorePatterns: [\"**/*.d.ts\", \"**/index.ts\"],\n mode: \"all\", // or \"changed\"\n baseBranch: \"main\" // for \"changed\" mode\n}]\n\\`\\`\\`\n\n## Examples\n\n### Below threshold:\n\\`\\`\\`ts\n// src/api.ts - 40% coverage (threshold: 80%)\nexport function fetchData() { ... } // Warning: Coverage below threshold\n\\`\\`\\`\n`,\n});\n\n// Cache for loaded coverage data\nlet coverageCache: {\n projectRoot: string;\n coveragePath: string;\n mtime: number;\n data: IstanbulCoverage;\n} | null = null;\n\n/**\n * Clear the coverage cache (useful for testing)\n */\nexport function clearCoverageCache(): void {\n coverageCache = null;\n}\n\n/**\n * Find project root by looking for package.json\n */\nfunction findProjectRoot(startPath: string): string {\n let current = startPath;\n let lastPackageJson: string | null = null;\n\n while (current !== dirname(current)) {\n if (existsSync(join(current, \"package.json\"))) {\n lastPackageJson = current;\n }\n // If we find a coverage directory, use this as project root\n if (existsSync(join(current, \"coverage\"))) {\n return current;\n }\n current = dirname(current);\n }\n\n return lastPackageJson || startPath;\n}\n\n/**\n * Load coverage data from JSON file\n */\nfunction loadCoverage(\n projectRoot: string,\n coveragePath: string\n): IstanbulCoverage | null {\n const fullPath = join(projectRoot, coveragePath);\n\n if (!existsSync(fullPath)) {\n return null;\n }\n\n try {\n const stat = statSync(fullPath);\n const mtime = stat.mtimeMs;\n\n // Check cache\n if (\n coverageCache &&\n coverageCache.projectRoot === projectRoot &&\n coverageCache.coveragePath === coveragePath &&\n coverageCache.mtime === mtime\n ) {\n return coverageCache.data;\n }\n\n const content = readFileSync(fullPath, \"utf-8\");\n const data = JSON.parse(content) as IstanbulCoverage;\n\n // Update cache\n coverageCache = {\n projectRoot,\n coveragePath,\n mtime,\n data,\n };\n\n return data;\n } catch {\n return null;\n }\n}\n\n/**\n * Calculate statement coverage percentage for a file\n */\nfunction calculateCoverage(fileCoverage: IstanbulCoverage[string]): number {\n const statements = fileCoverage.s;\n const keys = Object.keys(statements);\n\n if (keys.length === 0) {\n return 100; // No statements = 100% covered\n }\n\n const covered = keys.filter((key) => statements[key] > 0).length;\n return Math.round((covered / keys.length) * 100);\n}\n\n/**\n * Check if a file matches any of the ignore patterns\n */\nfunction shouldIgnore(filePath: string, ignorePatterns: string[]): boolean {\n for (const pattern of ignorePatterns) {\n if (simpleGlobMatch(pattern, filePath)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Get threshold for a file, checking pattern-specific thresholds first\n */\nfunction getThreshold(\n filePath: string,\n globalThreshold: number,\n thresholdsByPattern: Array<{ pattern: string; threshold: number }>\n): number {\n for (const { pattern, threshold } of thresholdsByPattern) {\n if (simpleGlobMatch(pattern, filePath)) {\n return threshold;\n }\n }\n return globalThreshold;\n}\n\n/**\n * Get changed line numbers from git diff\n */\nfunction getChangedLines(\n projectRoot: string,\n filePath: string,\n baseBranch: string\n): Set<number> | null {\n try {\n const relPath = relative(projectRoot, filePath);\n const diff = execSync(`git diff ${baseBranch}...HEAD -- \"${relPath}\"`, {\n cwd: projectRoot,\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n const changedLines = new Set<number>();\n const lines = diff.split(\"\\n\");\n\n let currentLine = 0;\n for (const line of lines) {\n // Parse hunk header: @@ -start,count +start,count @@\n const hunkMatch = line.match(/^@@ -\\d+(?:,\\d+)? \\+(\\d+)(?:,\\d+)? @@/);\n if (hunkMatch) {\n currentLine = parseInt(hunkMatch[1], 10);\n continue;\n }\n\n if (line.startsWith(\"+\") && !line.startsWith(\"+++\")) {\n changedLines.add(currentLine);\n currentLine++;\n } else if (line.startsWith(\"-\") && !line.startsWith(\"---\")) {\n // Deleted line, don't increment\n } else if (!line.startsWith(\"\\\\\")) {\n currentLine++;\n }\n }\n\n return changedLines;\n } catch {\n // Git command failed, return null to fall back to \"all\" mode\n return null;\n }\n}\n\n/**\n * Calculate coverage for only changed lines\n */\nfunction calculateChangedLinesCoverage(\n fileCoverage: IstanbulCoverage[string],\n changedLines: Set<number>\n): number {\n const statementMap = fileCoverage.statementMap;\n const statements = fileCoverage.s;\n\n let relevantStatements = 0;\n let coveredStatements = 0;\n\n for (const [key, location] of Object.entries(statementMap)) {\n // Check if any line of this statement was changed\n let isRelevant = false;\n for (let line = location.start.line; line <= location.end.line; line++) {\n if (changedLines.has(line)) {\n isRelevant = true;\n break;\n }\n }\n\n if (isRelevant) {\n relevantStatements++;\n if (statements[key] > 0) {\n coveredStatements++;\n }\n }\n }\n\n if (relevantStatements === 0) {\n return 100; // No changed statements = 100% covered\n }\n\n return Math.round((coveredStatements / relevantStatements) * 100);\n}\n\n/**\n * Normalize file path for coverage lookup\n * Coverage JSON may store paths in different formats\n */\nfunction findCoverageForFile(\n coverage: IstanbulCoverage,\n filePath: string,\n projectRoot: string\n): IstanbulCoverage[string] | null {\n // Try exact match first\n if (coverage[filePath]) {\n return coverage[filePath];\n }\n\n // Try relative path from project root\n const relPath = relative(projectRoot, filePath);\n if (coverage[relPath]) {\n return coverage[relPath];\n }\n\n // Try with leading slash (common in Istanbul output)\n const withSlash = \"/\" + relPath;\n if (coverage[withSlash]) {\n return coverage[withSlash];\n }\n\n // Try just the src-relative path\n const srcMatch = relPath.match(/src\\/.+$/);\n if (srcMatch) {\n const srcPath = \"/\" + srcMatch[0];\n if (coverage[srcPath]) {\n return coverage[srcPath];\n }\n }\n\n return null;\n}\n\nexport default createRule<Options, MessageIds>({\n name: \"require-test-coverage\",\n meta: {\n type: \"suggestion\",\n docs: {\n description: \"Enforce that source files have adequate test coverage\",\n },\n messages: {\n noCoverage:\n \"No coverage data found for '{{fileName}}' in coverage report\",\n belowThreshold:\n \"Coverage for '{{fileName}}' is {{coverage}}%, below threshold of {{threshold}}%\",\n noCoverageData:\n \"Coverage data not found at '{{coveragePath}}'. Run tests with coverage first.\",\n belowAggregateThreshold:\n \"Aggregate coverage ({{coverage}}%) is below threshold ({{threshold}}%). \" +\n \"Includes {{fileCount}} files. Lowest: {{lowestFile}} ({{lowestCoverage}}%)\",\n jsxBelowThreshold:\n \"<{{tagName}}> element coverage is {{coverage}}%, below threshold of {{threshold}}%\",\n chunkBelowThreshold:\n \"{{category}} '{{name}}' has {{coverage}}% coverage, below {{threshold}}% threshold\",\n untestedFunction:\n \"Function '{{name}}' ({{category}}) is not covered by tests\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n coveragePath: {\n type: \"string\",\n description: \"Path to coverage JSON file\",\n },\n threshold: {\n type: \"number\",\n minimum: 0,\n maximum: 100,\n description: \"Coverage threshold percentage\",\n },\n thresholdsByPattern: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n pattern: { type: \"string\" },\n threshold: { type: \"number\", minimum: 0, maximum: 100 },\n },\n required: [\"pattern\", \"threshold\"],\n additionalProperties: false,\n },\n description: \"Pattern-specific thresholds\",\n },\n severity: {\n type: \"object\",\n properties: {\n noCoverage: { type: \"string\", enum: [\"error\", \"warn\", \"off\"] },\n belowThreshold: {\n type: \"string\",\n enum: [\"error\", \"warn\", \"off\"],\n },\n },\n additionalProperties: false,\n },\n testPatterns: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Patterns to detect test files\",\n },\n ignorePatterns: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Glob patterns for files to ignore\",\n },\n mode: {\n type: \"string\",\n enum: [\"all\", \"changed\"],\n description: \"Check all code or only changed lines\",\n },\n baseBranch: {\n type: \"string\",\n description: \"Base branch for changed mode\",\n },\n aggregateThreshold: {\n type: \"number\",\n minimum: 0,\n maximum: 100,\n description:\n \"Aggregate coverage threshold for components (includes dependencies)\",\n },\n aggregateSeverity: {\n type: \"string\",\n enum: [\"error\", \"warn\", \"off\"],\n description: \"Severity for aggregate coverage check\",\n },\n jsxThreshold: {\n type: \"number\",\n minimum: 0,\n maximum: 100,\n description:\n \"JSX element coverage threshold percentage (includes event handlers)\",\n },\n jsxSeverity: {\n type: \"string\",\n enum: [\"error\", \"warn\", \"off\"],\n description: \"Severity for JSX element coverage check\",\n },\n chunkCoverage: {\n type: \"boolean\",\n description:\n \"Enable chunk-level coverage reporting (replaces file-level)\",\n },\n chunkThreshold: {\n type: \"number\",\n minimum: 0,\n maximum: 100,\n description:\n \"Threshold for strict categories (utility/hook/store)\",\n },\n focusNonReact: {\n type: \"boolean\",\n description:\n \"Focus on non-React code with relaxed thresholds for components\",\n },\n relaxedThreshold: {\n type: \"number\",\n minimum: 0,\n maximum: 100,\n description: \"Threshold for relaxed categories (component/handler)\",\n },\n chunkSeverity: {\n type: \"string\",\n enum: [\"error\", \"warn\", \"off\"],\n description: \"Severity for chunk coverage check\",\n },\n },\n additionalProperties: false,\n },\n ],\n },\n defaultOptions: [\n {\n coveragePath: \"coverage/coverage-final.json\",\n threshold: 80,\n thresholdsByPattern: [],\n severity: {\n noCoverage: \"error\",\n belowThreshold: \"warn\",\n },\n testPatterns: [\n \".test.ts\",\n \".test.tsx\",\n \".spec.ts\",\n \".spec.tsx\",\n \"__tests__/\",\n ],\n ignorePatterns: [\"**/*.d.ts\", \"**/index.ts\"],\n mode: \"all\",\n baseBranch: \"main\",\n aggregateThreshold: 70,\n aggregateSeverity: \"warn\",\n jsxThreshold: 50,\n jsxSeverity: \"warn\",\n chunkCoverage: true,\n chunkThreshold: 80,\n focusNonReact: false,\n relaxedThreshold: 50,\n chunkSeverity: \"warn\",\n minStatements: 5,\n },\n ],\n create(context) {\n const options = context.options[0] || {};\n const coveragePath = options.coveragePath ?? \"coverage/coverage-final.json\";\n const threshold = options.threshold ?? 80;\n const thresholdsByPattern = options.thresholdsByPattern ?? [];\n const severity = {\n noCoverage: options.severity?.noCoverage ?? \"error\",\n belowThreshold: options.severity?.belowThreshold ?? \"warn\",\n };\n const aggregateThreshold = options.aggregateThreshold ?? 70;\n const aggregateSeverity = options.aggregateSeverity ?? \"warn\";\n const testPatterns = options.testPatterns ?? [\n \".test.ts\",\n \".test.tsx\",\n \".spec.ts\",\n \".spec.tsx\",\n \"__tests__/\",\n ];\n const ignorePatterns = options.ignorePatterns ?? [\n \"**/*.d.ts\",\n \"**/index.ts\",\n ];\n const mode = options.mode ?? \"all\";\n const baseBranch = options.baseBranch ?? \"main\";\n const jsxThreshold = options.jsxThreshold ?? 50;\n const jsxSeverity = options.jsxSeverity ?? \"warn\";\n const chunkCoverage = options.chunkCoverage ?? true;\n const chunkThreshold = options.chunkThreshold ?? 80;\n const focusNonReact = options.focusNonReact ?? false;\n const relaxedThreshold = options.relaxedThreshold ?? 50;\n const chunkSeverity = options.chunkSeverity ?? \"warn\";\n const minStatements = options.minStatements ?? 5;\n\n const filename = context.filename || context.getFilename();\n const projectRoot = findProjectRoot(dirname(filename));\n\n // Check if file should be ignored\n const relPath = relative(projectRoot, filename);\n if (shouldIgnore(relPath, ignorePatterns)) {\n return {};\n }\n\n // Skip test files themselves\n if (\n testPatterns.some((p) =>\n filename.includes(p.replace(\"__tests__/\", \"__tests__\"))\n )\n ) {\n return {};\n }\n\n // Track if we've already reported for this file\n let reported = false;\n\n // Collect JSX elements with their ancestors for element-level coverage\n const jsxElements: Array<{\n node: TSESTree.JSXElement;\n ancestors: TSESTree.Node[];\n }> = [];\n\n return {\n // Collect JSX elements for element-level coverage analysis\n JSXElement(node: TSESTree.JSXElement) {\n // Get ancestors using context.sourceCode (ESLint v9) or context.getAncestors (legacy)\n const ancestors = context.sourceCode?.getAncestors?.(node) ?? [];\n jsxElements.push({ node, ancestors });\n },\n\n \"Program:exit\"(node: TSESTree.Program) {\n if (reported) return;\n reported = true;\n\n // Load coverage data\n const coverage = loadCoverage(projectRoot, coveragePath);\n\n // Check if coverage data exists\n if (!coverage) {\n if (severity.noCoverage !== \"off\") {\n context.report({\n node,\n messageId: \"noCoverageData\",\n data: {\n coveragePath,\n },\n });\n }\n return;\n }\n\n // Find coverage for this file\n const fileCoverage = findCoverageForFile(\n coverage,\n filename,\n projectRoot\n );\n\n if (!fileCoverage) {\n // File is not in coverage report - this is OK if coverage data exists\n // but this specific file wasn't covered (different from no coverage data at all)\n return;\n }\n\n // Skip small files (fewer statements than minStatements threshold)\n const statementCount = Object.keys(fileCoverage.s).length;\n if (statementCount < minStatements) {\n return;\n }\n\n // Calculate coverage\n let coveragePercent: number;\n\n if (mode === \"changed\") {\n const changedLines = getChangedLines(\n projectRoot,\n filename,\n baseBranch\n );\n if (changedLines && changedLines.size > 0) {\n coveragePercent = calculateChangedLinesCoverage(\n fileCoverage,\n changedLines\n );\n } else {\n // No changed lines or git failed - use full coverage\n coveragePercent = calculateCoverage(fileCoverage);\n }\n } else {\n coveragePercent = calculateCoverage(fileCoverage);\n }\n\n // Get threshold for this file\n const fileThreshold = getThreshold(\n relPath,\n threshold,\n thresholdsByPattern\n );\n\n // Check if below threshold (file-level) - skipped when chunkCoverage is enabled\n if (\n !chunkCoverage &&\n severity.belowThreshold !== \"off\" &&\n coveragePercent < fileThreshold\n ) {\n context.report({\n node,\n messageId: \"belowThreshold\",\n data: {\n fileName: basename(filename),\n coverage: String(coveragePercent),\n threshold: String(fileThreshold),\n },\n });\n }\n\n // Check chunk-level coverage (replaces file-level when enabled)\n if (chunkCoverage && chunkSeverity !== \"off\" && fileCoverage) {\n const chunks = analyzeChunks(\n context.sourceCode.ast,\n filename,\n fileCoverage\n );\n\n // Report one message per chunk below threshold, highlighting just the declaration\n for (const chunk of chunks) {\n const chunkThresholdValue = getChunkThreshold(chunk, {\n focusNonReact,\n chunkThreshold,\n relaxedThreshold,\n });\n\n if (chunk.coverage.percentage < chunkThresholdValue) {\n const messageId =\n chunk.coverage.functionCalled\n ? \"chunkBelowThreshold\"\n : \"untestedFunction\";\n\n context.report({\n loc: chunk.declarationLoc,\n messageId,\n data: {\n name: chunk.name,\n category: chunk.category,\n coverage: String(chunk.coverage.percentage),\n threshold: String(chunkThresholdValue),\n },\n });\n }\n }\n }\n\n // Check aggregate coverage for component files (JSX)\n if (\n aggregateSeverity !== \"off\" &&\n (filename.endsWith(\".tsx\") || filename.endsWith(\".jsx\"))\n ) {\n // Check if file actually contains JSX by looking at the AST\n const hasJSX = checkForJSX(context.sourceCode.ast);\n\n if (hasJSX) {\n const aggregateResult = aggregateCoverage(\n filename,\n projectRoot,\n coverage as AggregatorIstanbulCoverage\n );\n\n if (aggregateResult.aggregateCoverage < aggregateThreshold) {\n const lowestFile = aggregateResult.lowestCoverageFile;\n context.report({\n node,\n messageId: \"belowAggregateThreshold\",\n data: {\n coverage: String(\n Math.round(aggregateResult.aggregateCoverage)\n ),\n threshold: String(aggregateThreshold),\n fileCount: String(aggregateResult.totalFiles),\n lowestFile: lowestFile\n ? basename(lowestFile.path)\n : \"N/A\",\n lowestCoverage: lowestFile\n ? String(Math.round(lowestFile.percentage))\n : \"N/A\",\n },\n });\n }\n }\n }\n\n // Check JSX element-level coverage\n if (\n jsxSeverity !== \"off\" &&\n jsxElements.length > 0 &&\n coverage\n ) {\n // Compute relative path for dataLoc (consistent with overlay matching)\n const fileRelPath = relPath.startsWith(\"/\") ? relPath : `/${relPath}`;\n\n for (const { node: jsxNode, ancestors } of jsxElements) {\n // Only check elements with event handlers (interactive elements)\n const hasEventHandlers = jsxNode.openingElement.attributes.some(\n (attr) =>\n attr.type === \"JSXAttribute\" &&\n attr.name.type === \"JSXIdentifier\" &&\n /^on[A-Z]/.test(attr.name.name)\n );\n\n // Skip non-interactive elements to reduce noise\n if (!hasEventHandlers) {\n continue;\n }\n\n const result = analyzeJSXElementCoverage(\n jsxNode,\n fileRelPath,\n coverage as JSXAnalyzerIstanbulCoverage,\n ancestors,\n projectRoot\n );\n\n if (result.coverage.percentage < jsxThreshold) {\n // Get the tag name for the error message\n const openingElement = jsxNode.openingElement;\n let tagName = \"unknown\";\n if (openingElement.name.type === \"JSXIdentifier\") {\n tagName = openingElement.name.name;\n } else if (openingElement.name.type === \"JSXMemberExpression\") {\n // For Foo.Bar, use \"Foo.Bar\"\n let current: TSESTree.JSXTagNameExpression = openingElement.name;\n const parts: string[] = [];\n while (current.type === \"JSXMemberExpression\") {\n if (current.property.type === \"JSXIdentifier\") {\n parts.unshift(current.property.name);\n }\n current = current.object;\n }\n if (current.type === \"JSXIdentifier\") {\n parts.unshift(current.name);\n }\n tagName = parts.join(\".\");\n }\n\n context.report({\n node: jsxNode,\n messageId: \"jsxBelowThreshold\",\n data: {\n tagName,\n coverage: String(result.coverage.percentage),\n threshold: String(jsxThreshold),\n dataLoc: result.dataLoc,\n },\n });\n }\n }\n }\n },\n };\n },\n});\n\n/**\n * Check if an AST contains JSX elements\n * Uses a visited set to avoid infinite recursion from circular references (e.g., parent pointers)\n */\nfunction checkForJSX(ast: unknown, visited: WeakSet<object> = new WeakSet()): boolean {\n if (!ast || typeof ast !== \"object\") return false;\n\n // Avoid circular references\n if (visited.has(ast as object)) return false;\n visited.add(ast as object);\n\n const node = ast as Record<string, unknown>;\n\n // Check if this node is JSX\n if (\n node.type === \"JSXElement\" ||\n node.type === \"JSXFragment\" ||\n node.type === \"JSXText\"\n ) {\n return true;\n }\n\n // Only traverse known AST child properties to avoid parent/token references\n const childKeys = [\"body\", \"declarations\", \"declaration\", \"expression\", \"expressions\",\n \"argument\", \"arguments\", \"callee\", \"elements\", \"properties\", \"value\", \"init\",\n \"consequent\", \"alternate\", \"test\", \"left\", \"right\", \"object\", \"property\",\n \"children\", \"openingElement\", \"closingElement\", \"attributes\"];\n\n for (const key of childKeys) {\n const child = node[key];\n if (child && typeof child === \"object\") {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (checkForJSX(item, visited)) return true;\n }\n } else {\n if (checkForJSX(child, visited)) return true;\n }\n }\n }\n\n return false;\n}\n","/**\n * File Categorizer\n *\n * Categorizes TypeScript/React files by their role in the codebase.\n * Used for smart weighting in coverage aggregation.\n *\n * Categories:\n * - core (1.0): hooks, components, services, stores - critical logic\n * - utility (0.5): formatters, validators, helpers - supporting code\n * - constant (0.25): config, constants, enums - static data\n * - type (0): .d.ts files, type-only exports - no runtime impact\n */\n\nimport { existsSync, readFileSync } from \"fs\";\nimport { basename } from \"path\";\nimport { parse } from \"@typescript-eslint/typescript-estree\";\nimport type { TSESTree } from \"@typescript-eslint/utils\";\n\nexport type FileCategory = \"core\" | \"utility\" | \"constant\" | \"type\";\n\nexport interface FileCategoryResult {\n category: FileCategory;\n weight: number;\n reason: string;\n}\n\nconst CATEGORY_WEIGHTS: Record<FileCategory, number> = {\n core: 1.0,\n utility: 0.5,\n constant: 0.25,\n type: 0,\n};\n\n/**\n * Categorize a TypeScript/React file by its role\n */\nexport function categorizeFile(\n filePath: string,\n _projectRoot: string\n): FileCategoryResult {\n const fileName = basename(filePath);\n\n // 1. Type definition files are always \"type\"\n if (filePath.endsWith(\".d.ts\")) {\n return {\n category: \"type\",\n weight: 0,\n reason: \"TypeScript declaration file (.d.ts)\",\n };\n }\n\n // 2. Check file name patterns for core files\n // Hooks: use*.ts or use*.tsx\n if (/^use[A-Z]/.test(fileName)) {\n return {\n category: \"core\",\n weight: 1.0,\n reason: \"React hook (use* pattern)\",\n };\n }\n\n // Services: *.service.ts\n if (/\\.service\\.(ts|tsx)$/.test(fileName)) {\n return {\n category: \"core\",\n weight: 1.0,\n reason: \"Service file (*.service.ts pattern)\",\n };\n }\n\n // Stores: *.store.ts\n if (/\\.store\\.(ts|tsx)$/.test(fileName)) {\n return {\n category: \"core\",\n weight: 1.0,\n reason: \"Store file (*.store.ts pattern)\",\n };\n }\n\n // API files: *.api.ts\n if (/\\.api\\.(ts|tsx)$/.test(fileName)) {\n return {\n category: \"core\",\n weight: 1.0,\n reason: \"API file (*.api.ts pattern)\",\n };\n }\n\n // 3. Parse AST to analyze exports\n if (!existsSync(filePath)) {\n return {\n category: \"utility\",\n weight: 0.5,\n reason: \"File not found, defaulting to utility\",\n };\n }\n\n let ast: TSESTree.Program;\n try {\n const content = readFileSync(filePath, \"utf-8\");\n ast = parse(content, {\n jsx: true,\n loc: true,\n range: true,\n });\n } catch {\n return {\n category: \"utility\",\n weight: 0.5,\n reason: \"Failed to parse file, defaulting to utility\",\n };\n }\n\n // Analyze the file's exports\n const analysis = analyzeExports(ast);\n\n // 4. Type-only files (only type/interface exports, no runtime code)\n if (analysis.hasOnlyTypeExports) {\n return {\n category: \"type\",\n weight: 0,\n reason: \"File contains only type/interface exports\",\n };\n }\n\n // 5. Check for JSX (React component)\n if (analysis.hasJSX) {\n return {\n category: \"core\",\n weight: 1.0,\n reason: \"React component (contains JSX)\",\n };\n }\n\n // 6. Constant files (only const/enum exports, no functions)\n if (analysis.hasOnlyConstantExports) {\n return {\n category: \"constant\",\n weight: 0.25,\n reason: \"File contains only constant/enum exports\",\n };\n }\n\n // 7. Default to utility\n return {\n category: \"utility\",\n weight: 0.5,\n reason: \"General utility file with function exports\",\n };\n}\n\ninterface ExportAnalysis {\n hasOnlyTypeExports: boolean;\n hasOnlyConstantExports: boolean;\n hasJSX: boolean;\n hasFunctionExports: boolean;\n hasConstExports: boolean;\n hasTypeExports: boolean;\n}\n\n/**\n * Analyze exports in an AST to determine file category\n */\nfunction analyzeExports(ast: TSESTree.Program): ExportAnalysis {\n let hasFunctionExports = false;\n let hasConstExports = false;\n let hasTypeExports = false;\n let hasJSX = false;\n\n // Walk the AST to find exports and JSX\n function visit(node: TSESTree.Node): void {\n // Check for JSX\n if (\n node.type === \"JSXElement\" ||\n node.type === \"JSXFragment\" ||\n node.type === \"JSXText\"\n ) {\n hasJSX = true;\n }\n\n // Export named declaration\n if (node.type === \"ExportNamedDeclaration\") {\n const decl = node.declaration;\n\n // Type/interface exports\n if (\n node.exportKind === \"type\" ||\n decl?.type === \"TSTypeAliasDeclaration\" ||\n decl?.type === \"TSInterfaceDeclaration\"\n ) {\n hasTypeExports = true;\n }\n // Function exports\n else if (\n decl?.type === \"FunctionDeclaration\" ||\n (decl?.type === \"VariableDeclaration\" &&\n decl.declarations.some(\n (d) =>\n d.init?.type === \"ArrowFunctionExpression\" ||\n d.init?.type === \"FunctionExpression\"\n ))\n ) {\n hasFunctionExports = true;\n }\n // Const/enum exports\n else if (\n decl?.type === \"VariableDeclaration\" ||\n decl?.type === \"TSEnumDeclaration\"\n ) {\n // Check if it's a const with non-function value\n if (decl.type === \"VariableDeclaration\") {\n const hasNonFunctionInit = decl.declarations.some(\n (d) =>\n d.init &&\n d.init.type !== \"ArrowFunctionExpression\" &&\n d.init.type !== \"FunctionExpression\"\n );\n if (hasNonFunctionInit) {\n hasConstExports = true;\n }\n } else {\n hasConstExports = true;\n }\n }\n // Re-exports without declaration - check specifiers\n else if (!decl && node.specifiers.length > 0) {\n // For re-exports, we can't easily determine the type without resolving\n // Treat as potential function exports to be safe\n // Note: if exportKind was \"type\", we would have matched the first branch\n hasFunctionExports = true;\n }\n }\n\n // Export default\n if (node.type === \"ExportDefaultDeclaration\") {\n const decl = node.declaration;\n if (\n decl.type === \"FunctionDeclaration\" ||\n decl.type === \"ArrowFunctionExpression\" ||\n decl.type === \"FunctionExpression\"\n ) {\n hasFunctionExports = true;\n } else if (decl.type === \"ClassDeclaration\") {\n // Classes are typically core logic\n hasFunctionExports = true;\n } else {\n hasConstExports = true;\n }\n }\n\n // Recurse into children\n for (const key of Object.keys(node)) {\n const child = (node as unknown as Record<string, unknown>)[key];\n if (child && typeof child === \"object\") {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (item && typeof item === \"object\" && \"type\" in item) {\n visit(item as TSESTree.Node);\n }\n }\n } else if (\"type\" in child) {\n visit(child as TSESTree.Node);\n }\n }\n }\n }\n\n for (const node of ast.body) {\n visit(node);\n }\n\n // Determine derived properties\n const hasOnlyTypeExports =\n hasTypeExports && !hasFunctionExports && !hasConstExports;\n const hasOnlyConstantExports =\n hasConstExports && !hasFunctionExports && !hasTypeExports;\n\n return {\n hasOnlyTypeExports,\n hasOnlyConstantExports,\n hasJSX,\n hasFunctionExports,\n hasConstExports,\n hasTypeExports,\n };\n}\n\n/**\n * Get the weight for a category\n */\nexport function getCategoryWeight(category: FileCategory): number {\n return CATEGORY_WEIGHTS[category];\n}\n","/**\n * Dependency Graph Builder\n *\n * Builds a dependency graph by tracing all imports from an entry file.\n * Used for calculating aggregate test coverage across a component and its dependencies.\n *\n * Key behaviors:\n * - Traces transitive dependencies (full depth)\n * - Excludes node_modules (external packages)\n * - Handles circular dependencies via visited set\n * - Follows re-exports to actual source files\n * - Caches results for performance\n */\n\nimport { existsSync, readFileSync, statSync } from \"fs\";\nimport { dirname, resolve } from \"path\";\nimport { parse } from \"@typescript-eslint/typescript-estree\";\nimport type { TSESTree } from \"@typescript-eslint/utils\";\nimport { resolveImportPath, parseFile } from \"./export-resolver.js\";\n\nexport interface DependencyGraph {\n /** The entry file that was analyzed */\n root: string;\n /** All transitive dependencies (absolute paths, project files only) */\n allDependencies: Set<string>;\n}\n\ninterface CacheEntry {\n graph: DependencyGraph;\n mtime: number;\n}\n\n/**\n * Cache for dependency graphs (per entry file)\n */\nconst dependencyCache = new Map<string, CacheEntry>();\n\n/**\n * Build a dependency graph starting from an entry file\n *\n * @param entryFile - Absolute path to the entry file\n * @param projectRoot - Project root directory (used for determining project boundaries)\n * @returns DependencyGraph with all transitive dependencies\n */\nexport function buildDependencyGraph(\n entryFile: string,\n projectRoot: string\n): DependencyGraph {\n // Check cache\n const cached = dependencyCache.get(entryFile);\n if (cached) {\n // Validate cache by checking entry file mtime\n try {\n const currentMtime = statSync(entryFile).mtimeMs;\n if (currentMtime === cached.mtime) {\n return cached.graph;\n }\n } catch {\n // File doesn't exist or can't be read, invalidate cache\n }\n }\n\n const allDependencies = new Set<string>();\n const visited = new Set<string>();\n\n // Recursively collect dependencies\n collectDependencies(entryFile, projectRoot, allDependencies, visited);\n\n const graph: DependencyGraph = {\n root: entryFile,\n allDependencies,\n };\n\n // Cache the result\n try {\n const mtime = statSync(entryFile).mtimeMs;\n dependencyCache.set(entryFile, { graph, mtime });\n } catch {\n // If we can't get mtime, don't cache\n }\n\n return graph;\n}\n\n/**\n * Recursively collect dependencies from a file\n */\nfunction collectDependencies(\n filePath: string,\n projectRoot: string,\n allDependencies: Set<string>,\n visited: Set<string>\n): void {\n // Prevent infinite loops from circular dependencies\n if (visited.has(filePath)) {\n return;\n }\n visited.add(filePath);\n\n // Parse the file to extract imports\n const imports = extractImports(filePath);\n\n for (const importSource of imports) {\n // Resolve the import to an absolute path\n const resolvedPath = resolveImportPath(importSource, filePath);\n\n if (!resolvedPath) {\n // Could not resolve (external package or unresolvable)\n continue;\n }\n\n // Skip node_modules\n if (resolvedPath.includes(\"node_modules\")) {\n continue;\n }\n\n // Skip files outside the project\n if (!resolvedPath.startsWith(projectRoot)) {\n continue;\n }\n\n // Skip already visited files (handles circular dependencies)\n // This prevents adding the root file back as a dependency\n if (visited.has(resolvedPath)) {\n continue;\n }\n\n // Add to dependencies\n allDependencies.add(resolvedPath);\n\n // Recurse into this dependency\n collectDependencies(resolvedPath, projectRoot, allDependencies, visited);\n }\n}\n\n/**\n * Extract all import sources from a file\n */\nfunction extractImports(filePath: string): string[] {\n if (!existsSync(filePath)) {\n return [];\n }\n\n const ast = parseFile(filePath);\n if (!ast) {\n return [];\n }\n\n const imports: string[] = [];\n\n for (const node of ast.body) {\n // import ... from \"source\"\n if (node.type === \"ImportDeclaration\" && node.source.value) {\n imports.push(node.source.value as string);\n }\n\n // export ... from \"source\" (re-exports)\n if (\n node.type === \"ExportNamedDeclaration\" &&\n node.source?.value\n ) {\n imports.push(node.source.value as string);\n }\n\n // export * from \"source\"\n if (node.type === \"ExportAllDeclaration\" && node.source.value) {\n imports.push(node.source.value as string);\n }\n }\n\n // Also check for dynamic imports in the AST\n const dynamicImports = extractDynamicImports(ast);\n imports.push(...dynamicImports);\n\n return imports;\n}\n\n/**\n * Extract dynamic import() calls from the AST\n */\nfunction extractDynamicImports(ast: TSESTree.Program): string[] {\n const imports: string[] = [];\n\n function visit(node: TSESTree.Node): void {\n // import(\"source\")\n if (\n node.type === \"ImportExpression\" &&\n node.source.type === \"Literal\" &&\n typeof node.source.value === \"string\"\n ) {\n imports.push(node.source.value);\n }\n\n // Recurse into children\n for (const key of Object.keys(node)) {\n const child = (node as unknown as Record<string, unknown>)[key];\n if (child && typeof child === \"object\") {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (item && typeof item === \"object\" && \"type\" in item) {\n visit(item as TSESTree.Node);\n }\n }\n } else if (\"type\" in child) {\n visit(child as TSESTree.Node);\n }\n }\n }\n }\n\n for (const node of ast.body) {\n visit(node);\n }\n\n return imports;\n}\n\n/**\n * Invalidate the cache for a specific file\n *\n * Call this when a file changes to ensure fresh data\n */\nexport function invalidateDependencyCache(filePath: string): void {\n dependencyCache.delete(filePath);\n\n // Also invalidate any graphs that include this file as a dependency\n for (const [entryFile, entry] of dependencyCache) {\n if (entry.graph.allDependencies.has(filePath)) {\n dependencyCache.delete(entryFile);\n }\n }\n}\n\n/**\n * Clear the entire dependency cache\n */\nexport function clearDependencyCache(): void {\n dependencyCache.clear();\n}\n\n/**\n * Get cache statistics (for debugging/monitoring)\n */\nexport function getDependencyCacheStats(): {\n size: number;\n entries: string[];\n} {\n return {\n size: dependencyCache.size,\n entries: Array.from(dependencyCache.keys()),\n };\n}\n","/**\n * Export Resolver\n *\n * Resolves import paths and finds export definitions, following re-exports\n * to their original source files.\n */\n\nimport { ResolverFactory } from \"oxc-resolver\";\nimport { parse } from \"@typescript-eslint/typescript-estree\";\nimport { readFileSync, existsSync } from \"fs\";\nimport { dirname, join, extname } from \"path\";\nimport type { TSESTree } from \"@typescript-eslint/utils\";\n\n// Module-level resolver instance (reused across calls)\nlet resolverInstance: ReturnType<typeof ResolverFactory.prototype.sync> | null =\n null;\nlet resolverFactory: ResolverFactory | null = null;\n\n/**\n * Information about a resolved export\n */\nexport interface ResolvedExport {\n /** The name of the export (e.g., \"Button\") */\n name: string;\n /** Absolute path to the file containing the actual definition */\n filePath: string;\n /** The local name in the source file (may differ from export name) */\n localName: string;\n /** Whether this is a re-export (export { X } from './other') */\n isReexport: boolean;\n}\n\n/**\n * Cache for file exports to avoid re-parsing\n */\nconst exportCache = new Map<\n string,\n Map<string, { localName: string; reexportSource?: string }>\n>();\n\n/**\n * Cache for parsed ASTs\n */\nconst astCache = new Map<string, TSESTree.Program>();\n\n/**\n * Cache for resolved paths\n */\nconst resolvedPathCache = new Map<string, string | null>();\n\n/**\n * Get or create the resolver factory\n */\nfunction getResolverFactory(): ResolverFactory {\n if (!resolverFactory) {\n resolverFactory = new ResolverFactory({\n extensions: [\".tsx\", \".ts\", \".jsx\", \".js\"],\n mainFields: [\"module\", \"main\"],\n conditionNames: [\"import\", \"require\", \"node\", \"default\"],\n // Enable TypeScript path resolution\n tsconfig: {\n configFile: \"tsconfig.json\",\n references: \"auto\",\n },\n });\n }\n return resolverFactory;\n}\n\n/**\n * Resolve an import path to an absolute file path\n */\nexport function resolveImportPath(\n importSource: string,\n fromFile: string\n): string | null {\n const cacheKey = `${fromFile}::${importSource}`;\n\n if (resolvedPathCache.has(cacheKey)) {\n return resolvedPathCache.get(cacheKey) ?? null;\n }\n\n // Skip node_modules\n if (\n importSource.startsWith(\"react\") ||\n importSource.startsWith(\"next\") ||\n (!importSource.startsWith(\".\") &&\n !importSource.startsWith(\"@/\") &&\n !importSource.startsWith(\"~/\"))\n ) {\n // Check if it's a known external package\n if (\n importSource.includes(\"@mui/\") ||\n importSource.includes(\"@chakra-ui/\") ||\n importSource.includes(\"antd\") ||\n importSource.includes(\"@radix-ui/\")\n ) {\n // Return a marker for external packages - we don't resolve them but track them\n resolvedPathCache.set(cacheKey, null);\n return null;\n }\n resolvedPathCache.set(cacheKey, null);\n return null;\n }\n\n try {\n const factory = getResolverFactory();\n const fromDir = dirname(fromFile);\n const result = factory.sync(fromDir, importSource);\n\n if (result.path) {\n resolvedPathCache.set(cacheKey, result.path);\n return result.path;\n }\n } catch {\n // Fallback: try manual resolution for common patterns\n const resolved = manualResolve(importSource, fromFile);\n resolvedPathCache.set(cacheKey, resolved);\n return resolved;\n }\n\n resolvedPathCache.set(cacheKey, null);\n return null;\n}\n\n/**\n * Manual fallback resolution for common patterns\n */\nfunction manualResolve(importSource: string, fromFile: string): string | null {\n const fromDir = dirname(fromFile);\n const extensions = [\".tsx\", \".ts\", \".jsx\", \".js\"];\n\n // Handle @/ alias - find tsconfig and resolve\n if (importSource.startsWith(\"@/\")) {\n const projectRoot = findProjectRoot(fromFile);\n if (projectRoot) {\n const relativePath = importSource.slice(2); // Remove @/\n for (const ext of extensions) {\n const candidate = join(projectRoot, relativePath + ext);\n if (existsSync(candidate)) {\n return candidate;\n }\n // Try index file\n const indexCandidate = join(projectRoot, relativePath, `index${ext}`);\n if (existsSync(indexCandidate)) {\n return indexCandidate;\n }\n }\n }\n }\n\n // Handle relative imports\n if (importSource.startsWith(\".\")) {\n for (const ext of extensions) {\n const candidate = join(fromDir, importSource + ext);\n if (existsSync(candidate)) {\n return candidate;\n }\n // Try index file\n const indexCandidate = join(fromDir, importSource, `index${ext}`);\n if (existsSync(indexCandidate)) {\n return indexCandidate;\n }\n }\n }\n\n return null;\n}\n\n/**\n * Find the project root by looking for tsconfig.json or package.json\n */\nfunction findProjectRoot(fromFile: string): string | null {\n let dir = dirname(fromFile);\n const root = \"/\";\n\n while (dir !== root) {\n if (existsSync(join(dir, \"tsconfig.json\"))) {\n return dir;\n }\n if (existsSync(join(dir, \"package.json\"))) {\n return dir;\n }\n dir = dirname(dir);\n }\n\n return null;\n}\n\n/**\n * Parse a file and cache the AST\n */\nexport function parseFile(filePath: string): TSESTree.Program | null {\n if (astCache.has(filePath)) {\n return astCache.get(filePath)!;\n }\n\n try {\n const content = readFileSync(filePath, \"utf-8\");\n const ast = parse(content, {\n jsx: true,\n loc: true,\n range: true,\n });\n astCache.set(filePath, ast);\n return ast;\n } catch {\n return null;\n }\n}\n\n/**\n * Extract export information from a file\n */\nfunction extractExports(\n filePath: string\n): Map<string, { localName: string; reexportSource?: string }> {\n if (exportCache.has(filePath)) {\n return exportCache.get(filePath)!;\n }\n\n const exports = new Map<\n string,\n { localName: string; reexportSource?: string }\n >();\n const ast = parseFile(filePath);\n\n if (!ast) {\n exportCache.set(filePath, exports);\n return exports;\n }\n\n for (const node of ast.body) {\n // Handle: export function Button() {}\n if (\n node.type === \"ExportNamedDeclaration\" &&\n node.declaration?.type === \"FunctionDeclaration\" &&\n node.declaration.id\n ) {\n exports.set(node.declaration.id.name, {\n localName: node.declaration.id.name,\n });\n }\n\n // Handle: export const Button = () => {}\n if (\n node.type === \"ExportNamedDeclaration\" &&\n node.declaration?.type === \"VariableDeclaration\"\n ) {\n for (const decl of node.declaration.declarations) {\n if (decl.id.type === \"Identifier\") {\n exports.set(decl.id.name, { localName: decl.id.name });\n }\n }\n }\n\n // Handle: export { Button } or export { Button as Btn }\n if (node.type === \"ExportNamedDeclaration\" && node.specifiers.length > 0) {\n const source = node.source?.value as string | undefined;\n for (const spec of node.specifiers) {\n if (spec.type === \"ExportSpecifier\") {\n const exportedName =\n spec.exported.type === \"Identifier\"\n ? spec.exported.name\n : spec.exported.value;\n const localName =\n spec.local.type === \"Identifier\"\n ? spec.local.name\n : spec.local.value;\n\n exports.set(exportedName, {\n localName,\n reexportSource: source,\n });\n }\n }\n }\n\n // Handle: export default function Button() {}\n if (\n node.type === \"ExportDefaultDeclaration\" &&\n node.declaration.type === \"FunctionDeclaration\" &&\n node.declaration.id\n ) {\n exports.set(\"default\", { localName: node.declaration.id.name });\n }\n\n // Handle: export default Button\n if (\n node.type === \"ExportDefaultDeclaration\" &&\n node.declaration.type === \"Identifier\"\n ) {\n exports.set(\"default\", { localName: node.declaration.name });\n }\n }\n\n exportCache.set(filePath, exports);\n return exports;\n}\n\n/**\n * Resolve an export to its original definition, following re-exports\n */\nexport function resolveExport(\n exportName: string,\n filePath: string,\n visited = new Set<string>()\n): ResolvedExport | null {\n // Cycle detection\n const key = `${filePath}::${exportName}`;\n if (visited.has(key)) {\n return null;\n }\n visited.add(key);\n\n const exports = extractExports(filePath);\n const exportInfo = exports.get(exportName);\n\n if (!exportInfo) {\n return null;\n }\n\n // If it's a re-export, follow the chain\n if (exportInfo.reexportSource) {\n const resolvedPath = resolveImportPath(exportInfo.reexportSource, filePath);\n if (resolvedPath) {\n return resolveExport(exportInfo.localName, resolvedPath, visited);\n }\n return null;\n }\n\n // This is the actual definition\n return {\n name: exportName,\n filePath,\n localName: exportInfo.localName,\n isReexport: false,\n };\n}\n\n/**\n * Clear all caches (useful for testing or watch mode)\n */\nexport function clearResolverCaches(): void {\n exportCache.clear();\n astCache.clear();\n resolvedPathCache.clear();\n}\n","/**\n * Coverage Aggregator\n *\n * Calculates weighted aggregate coverage for a component by tracing its\n * dependencies and combining their coverage using smart weighting.\n *\n * Weighting strategy:\n * - core (1.0): hooks, components, services - critical logic\n * - utility (0.5): formatters, validators - supporting code\n * - constant (0.25): config, constants - static data\n * - type (0): .d.ts, type-only - no runtime impact\n */\n\nimport { categorizeFile, type FileCategory } from \"./file-categorizer.js\";\nimport { buildDependencyGraph } from \"./dependency-graph.js\";\n\n/**\n * Istanbul coverage JSON format (matching require-test-coverage.ts)\n */\nexport interface IstanbulCoverage {\n [filePath: string]: {\n path: string;\n statementMap: {\n [key: string]: {\n start: { line: number; column: number };\n end: { line: number; column: number };\n };\n };\n fnMap: {\n [key: string]: {\n name: string;\n decl: {\n start: { line: number; column: number };\n end: { line: number; column: number };\n };\n loc: {\n start: { line: number; column: number };\n end: { line: number; column: number };\n };\n };\n };\n branchMap: {\n [key: string]: {\n loc: {\n start: { line: number; column: number };\n end: { line: number; column: number };\n };\n type: string;\n locations: Array<{\n start: { line: number; column: number };\n end: { line: number; column: number };\n }>;\n };\n };\n s: { [key: string]: number }; // Statement hit counts\n f: { [key: string]: number }; // Function hit counts\n b: { [key: string]: number[] }; // Branch hit counts\n };\n}\n\n/**\n * Coverage information for a single file\n */\nexport interface FileCoverageInfo {\n filePath: string;\n category: FileCategory;\n weight: number;\n statements: { covered: number; total: number };\n percentage: number;\n}\n\n/**\n * Aggregated coverage result for a component\n */\nexport interface AggregatedCoverage {\n /** The component file that was analyzed */\n componentFile: string;\n /** Coverage percentage for just the component file */\n componentCoverage: number;\n /** Weighted aggregate coverage across component + all dependencies */\n aggregateCoverage: number;\n /** Total number of files analyzed (component + dependencies) */\n totalFiles: number;\n /** Detailed coverage info for each file */\n filesAnalyzed: FileCoverageInfo[];\n /** Files with 0% coverage */\n uncoveredFiles: string[];\n /** The file with lowest coverage (excluding 0% files) */\n lowestCoverageFile: { path: string; percentage: number } | null;\n}\n\n/**\n * Calculate aggregate coverage for a component and its dependencies\n *\n * @param componentFile - Absolute path to the component file\n * @param projectRoot - Project root directory\n * @param coverageData - Istanbul coverage data\n * @returns Aggregated coverage information\n */\nexport function aggregateCoverage(\n componentFile: string,\n projectRoot: string,\n coverageData: IstanbulCoverage\n): AggregatedCoverage {\n // Build dependency graph\n const graph = buildDependencyGraph(componentFile, projectRoot);\n\n // Collect all files to analyze (component + dependencies)\n const allFiles = new Set<string>([componentFile, ...graph.allDependencies]);\n\n // Analyze each file\n const filesAnalyzed: FileCoverageInfo[] = [];\n const uncoveredFiles: string[] = [];\n let lowestCoverageFile: { path: string; percentage: number } | null = null;\n let componentCoverageInfo: FileCoverageInfo | null = null;\n\n for (const filePath of allFiles) {\n const coverageInfo = getFileCoverage(filePath, projectRoot, coverageData);\n filesAnalyzed.push(coverageInfo);\n\n // Track component coverage separately\n if (filePath === componentFile) {\n componentCoverageInfo = coverageInfo;\n }\n\n // Track uncovered files (0%)\n if (coverageInfo.percentage === 0 && coverageInfo.statements.total > 0) {\n uncoveredFiles.push(filePath);\n }\n\n // Track lowest coverage (excluding 0% and type files with weight 0)\n if (\n coverageInfo.percentage > 0 &&\n coverageInfo.weight > 0 &&\n coverageInfo.statements.total > 0\n ) {\n if (\n !lowestCoverageFile ||\n coverageInfo.percentage < lowestCoverageFile.percentage\n ) {\n lowestCoverageFile = {\n path: filePath,\n percentage: coverageInfo.percentage,\n };\n }\n }\n }\n\n // Calculate weighted aggregate coverage\n const aggregateCoverageValue = calculateWeightedCoverage(filesAnalyzed);\n\n return {\n componentFile,\n componentCoverage: componentCoverageInfo?.percentage ?? 0,\n aggregateCoverage: aggregateCoverageValue,\n totalFiles: filesAnalyzed.length,\n filesAnalyzed,\n uncoveredFiles,\n lowestCoverageFile,\n };\n}\n\n/**\n * Get coverage information for a single file\n */\nfunction getFileCoverage(\n filePath: string,\n projectRoot: string,\n coverageData: IstanbulCoverage\n): FileCoverageInfo {\n // Categorize the file\n const categoryResult = categorizeFile(filePath, projectRoot);\n\n // Find coverage data for this file\n const fileCoverage = findCoverageForFile(filePath, coverageData, projectRoot);\n\n if (!fileCoverage) {\n // No coverage data - could mean not tested or file not found\n return {\n filePath,\n category: categoryResult.category,\n weight: categoryResult.weight,\n statements: { covered: 0, total: 0 },\n percentage: 0,\n };\n }\n\n // Calculate statement coverage\n const statementHits = fileCoverage.s;\n const totalStatements = Object.keys(statementHits).length;\n const coveredStatements = Object.values(statementHits).filter(\n (hits) => hits > 0\n ).length;\n\n const percentage =\n totalStatements > 0 ? (coveredStatements / totalStatements) * 100 : 0;\n\n return {\n filePath,\n category: categoryResult.category,\n weight: categoryResult.weight,\n statements: { covered: coveredStatements, total: totalStatements },\n percentage: Math.round(percentage * 100) / 100, // Round to 2 decimal places\n };\n}\n\n/**\n * Find coverage data for a file, handling path normalization\n */\nfunction findCoverageForFile(\n filePath: string,\n coverageData: IstanbulCoverage,\n projectRoot: string\n): IstanbulCoverage[string] | null {\n // Try exact match first\n if (coverageData[filePath]) {\n return coverageData[filePath];\n }\n\n // Try relative path from project root\n const relativePath = filePath.startsWith(projectRoot)\n ? filePath.slice(projectRoot.length)\n : filePath;\n\n // Try with and without leading slash\n const pathVariants = [\n relativePath,\n relativePath.startsWith(\"/\") ? relativePath.slice(1) : `/${relativePath}`,\n relativePath.startsWith(\"/\") ? relativePath : `/${relativePath}`,\n ];\n\n for (const variant of pathVariants) {\n if (coverageData[variant]) {\n return coverageData[variant];\n }\n }\n\n // Try matching by checking if coverage path ends with relative path\n for (const [coveragePath, coverage] of Object.entries(coverageData)) {\n if (\n coveragePath.endsWith(relativePath) ||\n coveragePath.endsWith(relativePath.slice(1))\n ) {\n return coverage;\n }\n }\n\n return null;\n}\n\n/**\n * Calculate weighted average coverage across files\n *\n * Uses statement count * weight for each file to determine contribution.\n * Files with weight 0 (type files) are excluded from calculation.\n */\nfunction calculateWeightedCoverage(files: FileCoverageInfo[]): number {\n let totalWeightedStatements = 0;\n let totalWeightedCovered = 0;\n\n for (const file of files) {\n // Skip files with weight 0 (type files)\n if (file.weight === 0) {\n continue;\n }\n\n // Skip files with no statements\n if (file.statements.total === 0) {\n continue;\n }\n\n const weightedTotal = file.statements.total * file.weight;\n const weightedCovered = file.statements.covered * file.weight;\n\n totalWeightedStatements += weightedTotal;\n totalWeightedCovered += weightedCovered;\n }\n\n if (totalWeightedStatements === 0) {\n return 0;\n }\n\n const percentage = (totalWeightedCovered / totalWeightedStatements) * 100;\n return Math.round(percentage * 100) / 100; // Round to 2 decimal places\n}\n","/**\n * JSX Coverage Analyzer\n *\n * Analyzes JSX elements to determine test coverage for interactive elements\n * like event handlers. Uses Istanbul coverage data to check if the code\n * associated with JSX elements has been executed during tests.\n *\n * Phase 1: Core functions for statement-level coverage analysis\n * Phase 2: Event handler extraction and analysis\n * Phase 3: Conditional parent analysis + Component-level aggregation (partial TODO)\n * Phase 4: Import dependency coverage + ESLint rule reporting (partial TODO)\n */\n\nimport type { TSESTree } from \"@typescript-eslint/utils\";\nimport type { IstanbulCoverage } from \"./coverage-aggregator.js\";\n\n/**\n * Re-export IstanbulCoverage for consumers of this module\n */\nexport type { IstanbulCoverage } from \"./coverage-aggregator.js\";\n\n/**\n * Istanbul coverage data for a single file\n */\nexport type IstanbulFileCoverage = IstanbulCoverage[string];\n\n/**\n * Source location with start and end positions\n */\nexport interface SourceLocation {\n start: { line: number; column: number };\n end: { line: number; column: number };\n}\n\n/**\n * Coverage statistics for a code region\n */\nexport interface CoverageStats {\n /** Number of statements that were executed at least once */\n covered: number;\n /** Total number of statements in the region */\n total: number;\n /** Coverage percentage (0-100) */\n percentage: number;\n}\n\n/**\n * Coverage result for a single JSX element\n */\nexport interface JSXCoverageResult {\n /** The data-loc attribute value for this element */\n dataLoc: string;\n /** Whether this element has any event handlers */\n hasEventHandlers: boolean;\n /** Names of event handlers found (e.g., [\"onClick\", \"onSubmit\"]) */\n eventHandlerNames: string[];\n /** Coverage statistics for statements within this element */\n coverage: CoverageStats;\n /** Whether the element is considered \"covered\" (percentage > 0) */\n isCovered: boolean;\n}\n\n// =============================================================================\n// Phase 1: Core Functions\n// =============================================================================\n\n/**\n * Creates a \"file:line:column\" format string for data-loc attribute\n *\n * @param filePath - Absolute or relative path to the file\n * @param loc - Source location with start position\n * @returns Formatted string like \"src/Button.tsx:15:4\"\n */\nexport function buildDataLoc(filePath: string, loc: SourceLocation): string {\n return `${filePath}:${loc.start.line}:${loc.start.column}`;\n}\n\n/**\n * Find statement IDs that overlap with the given source location\n *\n * A statement overlaps if its line range intersects with the location's\n * line range. Column-level precision is not used for overlap detection.\n *\n * @param loc - The source location to check\n * @param fileCoverage - Istanbul coverage data for the file\n * @returns Set of statement IDs (keys from statementMap) that overlap\n */\nexport function findStatementsInRange(\n loc: SourceLocation,\n fileCoverage: IstanbulFileCoverage\n): Set<string> {\n const overlappingStatements = new Set<string>();\n\n for (const [statementId, statementLoc] of Object.entries(\n fileCoverage.statementMap\n )) {\n // Check if statement's line range overlaps with location's line range\n const statementStart = statementLoc.start.line;\n const statementEnd = statementLoc.end.line;\n const locStart = loc.start.line;\n const locEnd = loc.end.line;\n\n // Two ranges overlap if: start1 <= end2 AND start2 <= end1\n if (statementStart <= locEnd && locStart <= statementEnd) {\n overlappingStatements.add(statementId);\n }\n }\n\n return overlappingStatements;\n}\n\n/**\n * Calculate coverage statistics from a set of statement IDs\n *\n * @param statementIds - Set of statement IDs to check\n * @param fileCoverage - Istanbul coverage data for the file\n * @returns Coverage statistics with covered count, total, and percentage\n */\nexport function calculateCoverageFromStatements(\n statementIds: Set<string>,\n fileCoverage: IstanbulFileCoverage\n): CoverageStats {\n if (statementIds.size === 0) {\n return { covered: 0, total: 0, percentage: 0 };\n }\n\n let covered = 0;\n const total = statementIds.size;\n\n for (const statementId of statementIds) {\n const hitCount = fileCoverage.s[statementId];\n if (hitCount !== undefined && hitCount > 0) {\n covered++;\n }\n }\n\n const percentage = total > 0 ? Math.round((covered / total) * 100) : 0;\n\n return { covered, total, percentage };\n}\n\n/**\n * Find coverage data for a file with path normalization\n *\n * Handles various path formats:\n * - Absolute paths\n * - Relative paths with or without leading slash\n * - Paths that may differ in their base directory\n *\n * @param coverage - Full Istanbul coverage data\n * @param filePath - The file path to find coverage for\n * @returns File coverage data if found, undefined otherwise\n */\nexport function findCoverageForFile(\n coverage: IstanbulCoverage,\n filePath: string\n): IstanbulFileCoverage | undefined {\n // Try exact match first\n if (coverage[filePath]) {\n return coverage[filePath];\n }\n\n // Normalize the path for comparison (remove leading slashes, standardize)\n const normalizedPath = filePath.replace(/^\\/+/, \"\");\n\n // Try with and without leading slash\n const pathVariants = [\n normalizedPath,\n `/${normalizedPath}`,\n filePath,\n ];\n\n for (const variant of pathVariants) {\n if (coverage[variant]) {\n return coverage[variant];\n }\n }\n\n // Try matching by checking if coverage path ends with our path\n for (const [coveragePath, fileCoverage] of Object.entries(coverage)) {\n const normalizedCoveragePath = coveragePath.replace(/^\\/+/, \"\");\n\n if (\n normalizedCoveragePath.endsWith(normalizedPath) ||\n normalizedPath.endsWith(normalizedCoveragePath)\n ) {\n return fileCoverage;\n }\n }\n\n return undefined;\n}\n\n/**\n * Check if a JSX attribute is an event handler (starts with \"on\" followed by uppercase)\n *\n * Event handlers follow the pattern: onClick, onSubmit, onChange, etc.\n * This excludes spread attributes and non-event props like \"only\" or \"once\".\n *\n * @param attr - JSX attribute or spread attribute\n * @returns true if the attribute is an event handler\n */\nexport function isEventHandlerAttribute(\n attr: TSESTree.JSXAttribute | TSESTree.JSXSpreadAttribute\n): boolean {\n // Spread attributes are not event handlers by themselves\n if (attr.type === \"JSXSpreadAttribute\") {\n return false;\n }\n\n // Only handle JSXIdentifier names (not namespaced like xml:lang)\n if (attr.name.type !== \"JSXIdentifier\") {\n return false;\n }\n\n const name = attr.name.name;\n\n // Match pattern: on[A-Z]...\n return /^on[A-Z]/.test(name);\n}\n\n/**\n * Analyze a JSX element for test coverage\n *\n * Main entry point that combines all the above functions to produce\n * a complete coverage analysis for a single JSX element.\n *\n * @param jsxNode - The JSX element node from the AST\n * @param filePath - Path to the file containing this element\n * @param coverage - Istanbul coverage data\n * @param ancestors - Optional ancestor nodes for resolving handler references\n * @param projectRoot - Optional project root for resolving import paths\n * @returns Coverage result for the JSX element\n */\nexport function analyzeJSXElementCoverage(\n jsxNode: TSESTree.JSXElement,\n filePath: string,\n coverage: IstanbulCoverage,\n ancestors: TSESTree.Node[] = [],\n projectRoot?: string\n): JSXCoverageResult {\n // Build the data-loc identifier\n const loc = jsxNode.loc;\n const dataLoc = buildDataLoc(filePath, loc);\n\n // Find event handlers on this element\n const eventHandlerNames: string[] = [];\n for (const attr of jsxNode.openingElement.attributes) {\n if (isEventHandlerAttribute(attr) && attr.type === \"JSXAttribute\") {\n if (attr.name.type === \"JSXIdentifier\") {\n eventHandlerNames.push(attr.name.name);\n }\n }\n }\n const hasEventHandlers = eventHandlerNames.length > 0;\n\n // Find coverage data for this file\n const fileCoverage = findCoverageForFile(coverage, filePath);\n\n if (!fileCoverage) {\n // No coverage data available for this file\n return {\n dataLoc,\n hasEventHandlers,\n eventHandlerNames,\n coverage: { covered: 0, total: 0, percentage: 0 },\n isCovered: false,\n };\n }\n\n // Find statements that overlap with this JSX element\n const statementIds = findStatementsInRange(loc, fileCoverage);\n\n // Also include statements from event handler bodies (Phase 2)\n if (hasEventHandlers) {\n const handlerStatementIds = getHandlerStatements(\n jsxNode,\n fileCoverage,\n ancestors\n );\n for (const stmtId of handlerStatementIds) {\n statementIds.add(stmtId);\n }\n }\n\n // Include statements from conditional ancestors (Phase 3)\n // This ensures conditionally rendered elements include the condition's coverage\n const conditionalAncestor = findConditionalAncestor(jsxNode, ancestors);\n if (conditionalAncestor) {\n const conditionalStatementIds = getConditionalStatements(\n conditionalAncestor,\n fileCoverage\n );\n for (const stmtId of conditionalStatementIds) {\n statementIds.add(stmtId);\n }\n }\n\n // Calculate local coverage statistics (statements in this file)\n const localCoverage = calculateCoverageFromStatements(\n statementIds,\n fileCoverage\n );\n\n // Phase 4: Include import dependency coverage\n // Find imports used in this JSX element and aggregate their coverage\n let importCoverage = { covered: 0, total: 0 };\n if (projectRoot && ancestors.length > 0) {\n const importPaths = findImportsUsedInJSX(jsxNode, ancestors);\n if (importPaths.size > 0) {\n importCoverage = aggregateImportCoverage(\n importPaths,\n coverage,\n projectRoot,\n filePath\n );\n }\n }\n\n // Combine local and import coverage (weighted by statement count)\n const totalCovered = localCoverage.covered + importCoverage.covered;\n const totalStatements = localCoverage.total + importCoverage.total;\n const combinedPercentage =\n totalStatements > 0 ? Math.round((totalCovered / totalStatements) * 100) : 0;\n\n const coverageStats: CoverageStats = {\n covered: totalCovered,\n total: totalStatements,\n percentage: combinedPercentage,\n };\n\n return {\n dataLoc,\n hasEventHandlers,\n eventHandlerNames,\n coverage: coverageStats,\n isCovered: coverageStats.percentage > 0,\n };\n}\n\n// =============================================================================\n// Phase 2: Event Handler Analysis\n// =============================================================================\n\n/**\n * Extract the expression from an event handler attribute value\n *\n * Handles various forms of event handler values:\n * - {handleClick} - identifier reference\n * - {() => doSomething()} - inline arrow function\n * - {fn.bind(this)} - call expression (like bind)\n * - {obj.method} - member expression\n *\n * @param attr - The JSX attribute to extract from\n * @returns The expression if found, null for string literals or empty values\n */\nexport function extractEventHandlerExpression(\n attr: TSESTree.JSXAttribute\n): TSESTree.Expression | null {\n // No value means no expression (e.g., <input disabled />)\n if (!attr.value) {\n return null;\n }\n\n // String literals are not expressions we can analyze\n // e.g., onClick=\"someGlobalFunction()\"\n if (attr.value.type === \"Literal\") {\n return null;\n }\n\n // JSX expression container: {expression}\n if (attr.value.type === \"JSXExpressionContainer\") {\n const expression = attr.value.expression;\n\n // Empty expression container {} or JSXEmptyExpression\n if (expression.type === \"JSXEmptyExpression\") {\n return null;\n }\n\n // Return the actual expression (Identifier, ArrowFunctionExpression, CallExpression, etc.)\n return expression;\n }\n\n // JSXElement as value (rare, but possible)\n // e.g., onClick={<SomeComponent />} - not a valid handler expression\n return null;\n}\n\n/**\n * Find the function declaration for an identifier used as an event handler\n *\n * Searches through the ancestor chain to find where an identifier is declared.\n * Handles:\n * - Variable declarations with function expressions\n * - Variable declarations with arrow functions\n * - Function declarations\n * - Function parameters (destructured or direct)\n *\n * @param identifier - The identifier node (e.g., the \"handleClick\" in onClick={handleClick})\n * @param ancestors - The ancestor nodes from the AST traversal (innermost first or any order)\n * @returns The function body node if found, null otherwise\n */\nexport function findHandlerFunctionDeclaration(\n identifier: TSESTree.Identifier,\n ancestors: TSESTree.Node[]\n): TSESTree.Node | null {\n const targetName = identifier.name;\n\n for (const ancestor of ancestors) {\n // Check FunctionDeclaration\n if (\n ancestor.type === \"FunctionDeclaration\" &&\n ancestor.id?.name === targetName\n ) {\n return ancestor.body;\n }\n\n // Check VariableDeclaration\n if (ancestor.type === \"VariableDeclaration\") {\n for (const declarator of ancestor.declarations) {\n if (\n declarator.id.type === \"Identifier\" &&\n declarator.id.name === targetName &&\n declarator.init\n ) {\n // Check if the init is a function\n if (\n declarator.init.type === \"ArrowFunctionExpression\" ||\n declarator.init.type === \"FunctionExpression\"\n ) {\n return declarator.init.body;\n }\n }\n }\n }\n\n // Check BlockStatement and Program for contained declarations\n if (ancestor.type === \"BlockStatement\" || ancestor.type === \"Program\") {\n const body =\n ancestor.type === \"Program\" ? ancestor.body : ancestor.body;\n\n for (const statement of body) {\n // Function declarations in block\n if (\n statement.type === \"FunctionDeclaration\" &&\n statement.id?.name === targetName\n ) {\n return statement.body;\n }\n\n // Variable declarations in block\n if (statement.type === \"VariableDeclaration\") {\n for (const declarator of statement.declarations) {\n if (\n declarator.id.type === \"Identifier\" &&\n declarator.id.name === targetName &&\n declarator.init\n ) {\n if (\n declarator.init.type === \"ArrowFunctionExpression\" ||\n declarator.init.type === \"FunctionExpression\"\n ) {\n return declarator.init.body;\n }\n }\n }\n }\n\n // Export declarations\n if (statement.type === \"ExportNamedDeclaration\" && statement.declaration) {\n if (\n statement.declaration.type === \"FunctionDeclaration\" &&\n statement.declaration.id?.name === targetName\n ) {\n return statement.declaration.body;\n }\n\n if (statement.declaration.type === \"VariableDeclaration\") {\n for (const declarator of statement.declaration.declarations) {\n if (\n declarator.id.type === \"Identifier\" &&\n declarator.id.name === targetName &&\n declarator.init\n ) {\n if (\n declarator.init.type === \"ArrowFunctionExpression\" ||\n declarator.init.type === \"FunctionExpression\"\n ) {\n return declarator.init.body;\n }\n }\n }\n }\n }\n }\n }\n\n // Check function body for nested declarations (component functions)\n if (\n ancestor.type === \"ArrowFunctionExpression\" ||\n ancestor.type === \"FunctionExpression\" ||\n ancestor.type === \"FunctionDeclaration\"\n ) {\n const funcBody = ancestor.body;\n\n // Only check BlockStatement bodies (not expression bodies)\n if (funcBody.type === \"BlockStatement\") {\n for (const statement of funcBody.body) {\n if (\n statement.type === \"FunctionDeclaration\" &&\n statement.id?.name === targetName\n ) {\n return statement.body;\n }\n\n if (statement.type === \"VariableDeclaration\") {\n for (const declarator of statement.declarations) {\n if (\n declarator.id.type === \"Identifier\" &&\n declarator.id.name === targetName &&\n declarator.init\n ) {\n if (\n declarator.init.type === \"ArrowFunctionExpression\" ||\n declarator.init.type === \"FunctionExpression\"\n ) {\n return declarator.init.body;\n }\n }\n }\n }\n }\n }\n }\n }\n\n return null;\n}\n\n/**\n * Get statement IDs for all event handlers on a JSX element\n *\n * For each event handler attribute on the element:\n * - Extract the handler expression\n * - For inline arrows: find statements within the arrow body range\n * - For identifier references: find the handler declaration and its body range\n *\n * @param jsxNode - The JSX element to analyze\n * @param fileCoverage - Istanbul coverage data for the file\n * @param ancestors - Optional ancestor nodes for identifier resolution\n * @returns Set of statement IDs that are part of event handler bodies\n */\nexport function getHandlerStatements(\n jsxNode: TSESTree.JSXElement,\n fileCoverage: IstanbulFileCoverage,\n ancestors: TSESTree.Node[] = []\n): Set<string> {\n const handlerStatements = new Set<string>();\n\n for (const attr of jsxNode.openingElement.attributes) {\n // Skip non-event-handler attributes\n if (!isEventHandlerAttribute(attr) || attr.type !== \"JSXAttribute\") {\n continue;\n }\n\n const expression = extractEventHandlerExpression(attr);\n if (!expression) {\n continue;\n }\n\n // Handle inline arrow functions and function expressions\n if (\n expression.type === \"ArrowFunctionExpression\" ||\n expression.type === \"FunctionExpression\"\n ) {\n const body = expression.body;\n if (body.loc) {\n const bodyStatements = findStatementsInRange(body.loc, fileCoverage);\n for (const stmtId of bodyStatements) {\n handlerStatements.add(stmtId);\n }\n }\n continue;\n }\n\n // Handle identifier references (e.g., onClick={handleClick})\n if (expression.type === \"Identifier\") {\n const functionBody = findHandlerFunctionDeclaration(expression, ancestors);\n if (functionBody && functionBody.loc) {\n const bodyStatements = findStatementsInRange(functionBody.loc, fileCoverage);\n for (const stmtId of bodyStatements) {\n handlerStatements.add(stmtId);\n }\n }\n continue;\n }\n\n // Handle call expressions (e.g., onClick={fn.bind(this)})\n // We analyze the entire call expression range\n if (expression.type === \"CallExpression\" && expression.loc) {\n const callStatements = findStatementsInRange(expression.loc, fileCoverage);\n for (const stmtId of callStatements) {\n handlerStatements.add(stmtId);\n }\n continue;\n }\n\n // Handle member expressions (e.g., onClick={obj.method})\n // These typically point to methods that we can't easily resolve\n // without more complex analysis, so we just note the expression location\n if (expression.type === \"MemberExpression\" && expression.loc) {\n const memberStatements = findStatementsInRange(expression.loc, fileCoverage);\n for (const stmtId of memberStatements) {\n handlerStatements.add(stmtId);\n }\n }\n }\n\n return handlerStatements;\n}\n\n// =============================================================================\n// Phase 3: Conditional Parent Analysis\n// =============================================================================\n\n/**\n * Find a conditional ancestor that controls this element's rendering\n *\n * Walks up the ancestor chain to find the first conditional expression\n * that determines whether this JSX element is rendered:\n * - LogicalExpression with `&&`: {condition && <Element />}\n * - ConditionalExpression (ternary): {condition ? <A /> : <B />}\n *\n * @param node - The current JSX element node\n * @param ancestors - The ancestor nodes from the AST traversal\n * @returns The conditional expression if found, null otherwise\n */\nexport function findConditionalAncestor(\n node: TSESTree.Node,\n ancestors: TSESTree.Node[]\n): TSESTree.LogicalExpression | TSESTree.ConditionalExpression | null {\n for (const ancestor of ancestors) {\n // Check for logical expression with && operator\n // Pattern: {condition && <Element />}\n if (\n ancestor.type === \"LogicalExpression\" &&\n ancestor.operator === \"&&\"\n ) {\n return ancestor;\n }\n\n // Check for conditional (ternary) expression\n // Pattern: {condition ? <A /> : <B />}\n if (ancestor.type === \"ConditionalExpression\") {\n return ancestor;\n }\n\n // Stop searching when we hit a JSX element boundary\n // (we don't want to cross into parent JSX elements)\n if (ancestor.type === \"JSXElement\" && ancestor !== node) {\n break;\n }\n\n // Stop at function boundaries\n if (\n ancestor.type === \"ArrowFunctionExpression\" ||\n ancestor.type === \"FunctionExpression\" ||\n ancestor.type === \"FunctionDeclaration\"\n ) {\n break;\n }\n }\n\n return null;\n}\n\n/**\n * Get statement IDs for the condition/test part of a conditional expression\n *\n * For LogicalExpression (&&): gets statements in the left operand (the condition)\n * For ConditionalExpression (ternary): gets statements in the test expression\n *\n * @param conditional - The conditional expression node\n * @param fileCoverage - Istanbul coverage data for the file\n * @returns Set of statement IDs that are part of the condition\n */\nexport function getConditionalStatements(\n conditional: TSESTree.LogicalExpression | TSESTree.ConditionalExpression,\n fileCoverage: IstanbulFileCoverage\n): Set<string> {\n const conditionStatements = new Set<string>();\n\n if (conditional.type === \"LogicalExpression\") {\n // For &&, the left side is the condition\n const condition = conditional.left;\n if (condition.loc) {\n const statements = findStatementsInRange(condition.loc, fileCoverage);\n for (const stmtId of statements) {\n conditionStatements.add(stmtId);\n }\n }\n } else if (conditional.type === \"ConditionalExpression\") {\n // For ternary, the test is the condition\n const condition = conditional.test;\n if (condition.loc) {\n const statements = findStatementsInRange(condition.loc, fileCoverage);\n for (const stmtId of statements) {\n conditionStatements.add(stmtId);\n }\n }\n }\n\n return conditionStatements;\n}\n\n// =============================================================================\n// Phase 4: Import Dependency Coverage\n// =============================================================================\n\n/**\n * Recursively collect all identifiers used within a node\n *\n * @param node - AST node to traverse\n * @param identifiers - Set to accumulate identifier names\n */\nfunction collectIdentifiersFromNode(\n node: TSESTree.Node,\n identifiers: Set<string>\n): void {\n switch (node.type) {\n case \"Identifier\":\n identifiers.add(node.name);\n break;\n\n case \"JSXIdentifier\":\n // JSX element names (e.g., <Component />) - these are components from imports\n identifiers.add(node.name);\n break;\n\n case \"JSXExpressionContainer\":\n if (node.expression.type !== \"JSXEmptyExpression\") {\n collectIdentifiersFromNode(node.expression, identifiers);\n }\n break;\n\n case \"JSXElement\":\n // Collect from opening element (tag name and attributes)\n collectIdentifiersFromNode(node.openingElement, identifiers);\n // Collect from children\n for (const child of node.children) {\n collectIdentifiersFromNode(child, identifiers);\n }\n break;\n\n case \"JSXOpeningElement\":\n // Collect from element name\n collectIdentifiersFromNode(node.name, identifiers);\n // Collect from attributes\n for (const attr of node.attributes) {\n collectIdentifiersFromNode(attr, identifiers);\n }\n break;\n\n case \"JSXAttribute\":\n // Collect from attribute value if it exists\n if (node.value) {\n collectIdentifiersFromNode(node.value, identifiers);\n }\n break;\n\n case \"JSXSpreadAttribute\":\n collectIdentifiersFromNode(node.argument, identifiers);\n break;\n\n case \"JSXMemberExpression\":\n // e.g., <Foo.Bar /> - collect the object\n collectIdentifiersFromNode(node.object, identifiers);\n break;\n\n case \"CallExpression\":\n collectIdentifiersFromNode(node.callee, identifiers);\n for (const arg of node.arguments) {\n collectIdentifiersFromNode(arg, identifiers);\n }\n break;\n\n case \"MemberExpression\":\n collectIdentifiersFromNode(node.object, identifiers);\n break;\n\n case \"ArrowFunctionExpression\":\n case \"FunctionExpression\":\n collectIdentifiersFromNode(node.body, identifiers);\n break;\n\n case \"BlockStatement\":\n for (const statement of node.body) {\n collectIdentifiersFromNode(statement, identifiers);\n }\n break;\n\n case \"ExpressionStatement\":\n collectIdentifiersFromNode(node.expression, identifiers);\n break;\n\n case \"ReturnStatement\":\n if (node.argument) {\n collectIdentifiersFromNode(node.argument, identifiers);\n }\n break;\n\n case \"BinaryExpression\":\n case \"LogicalExpression\":\n collectIdentifiersFromNode(node.left, identifiers);\n collectIdentifiersFromNode(node.right, identifiers);\n break;\n\n case \"ConditionalExpression\":\n collectIdentifiersFromNode(node.test, identifiers);\n collectIdentifiersFromNode(node.consequent, identifiers);\n collectIdentifiersFromNode(node.alternate, identifiers);\n break;\n\n case \"UnaryExpression\":\n collectIdentifiersFromNode(node.argument, identifiers);\n break;\n\n case \"TemplateLiteral\":\n for (const expr of node.expressions) {\n collectIdentifiersFromNode(expr, identifiers);\n }\n break;\n\n case \"ArrayExpression\":\n for (const element of node.elements) {\n if (element) {\n collectIdentifiersFromNode(element, identifiers);\n }\n }\n break;\n\n case \"ObjectExpression\":\n for (const prop of node.properties) {\n collectIdentifiersFromNode(prop, identifiers);\n }\n break;\n\n case \"Property\":\n collectIdentifiersFromNode(node.value, identifiers);\n break;\n\n case \"SpreadElement\":\n collectIdentifiersFromNode(node.argument, identifiers);\n break;\n\n case \"JSXText\":\n case \"JSXFragment\":\n case \"Literal\":\n // No identifiers in these\n break;\n\n default:\n // For other node types, we don't recurse to avoid complexity\n break;\n }\n}\n\n/**\n * Find imports used within a JSX element\n *\n * Identifies which imported modules are used within the JSX element by:\n * 1. Collecting all identifiers used in the JSX (props, children expressions, etc.)\n * 2. Walking up to the Program node to find ImportDeclaration nodes\n * 3. Matching used identifiers to their import sources\n *\n * @param jsxNode - The JSX element to analyze\n * @param ancestors - Ancestor nodes from AST traversal (should include Program)\n * @returns Set of import module specifiers (the 'from' paths) used in this JSX\n */\nexport function findImportsUsedInJSX(\n jsxNode: TSESTree.JSXElement,\n ancestors: TSESTree.Node[]\n): Set<string> {\n const importPaths = new Set<string>();\n\n // Step 1: Collect all identifiers used in this JSX element\n const usedIdentifiers = new Set<string>();\n collectIdentifiersFromNode(jsxNode, usedIdentifiers);\n\n // Step 2: Find the Program node in ancestors to access imports\n let programNode: TSESTree.Program | null = null;\n for (const ancestor of ancestors) {\n if (ancestor.type === \"Program\") {\n programNode = ancestor;\n break;\n }\n }\n\n if (!programNode) {\n return importPaths;\n }\n\n // Step 3: Build a map of imported identifiers to their module sources\n const importedIdentifiers = new Map<string, string>();\n\n for (const statement of programNode.body) {\n if (statement.type === \"ImportDeclaration\") {\n const source = statement.source.value;\n if (typeof source !== \"string\") {\n continue;\n }\n\n for (const specifier of statement.specifiers) {\n switch (specifier.type) {\n case \"ImportDefaultSpecifier\":\n // import Foo from 'module' -> Foo maps to 'module'\n importedIdentifiers.set(specifier.local.name, source);\n break;\n\n case \"ImportSpecifier\":\n // import { Foo } from 'module' -> Foo maps to 'module'\n // import { Foo as Bar } from 'module' -> Bar maps to 'module'\n importedIdentifiers.set(specifier.local.name, source);\n break;\n\n case \"ImportNamespaceSpecifier\":\n // import * as Foo from 'module' -> Foo maps to 'module'\n importedIdentifiers.set(specifier.local.name, source);\n break;\n }\n }\n }\n }\n\n // Step 4: Match used identifiers to their import sources\n for (const identifier of usedIdentifiers) {\n const importSource = importedIdentifiers.get(identifier);\n if (importSource) {\n importPaths.add(importSource);\n }\n }\n\n return importPaths;\n}\n\n/**\n * Resolve an import path to a file path\n *\n * Handles relative imports by resolving them against the current file's directory.\n * For non-relative imports (node_modules), returns null as we don't analyze those.\n *\n * @param importPath - The import specifier (e.g., './utils' or 'react')\n * @param currentFilePath - Path of the file containing the import\n * @param projectRoot - Project root directory\n * @returns Resolved file path or null if can't be resolved\n */\nfunction resolveImportPath(\n importPath: string,\n currentFilePath: string,\n projectRoot: string\n): string | null {\n // Skip non-relative imports (node_modules packages)\n if (!importPath.startsWith(\".\") && !importPath.startsWith(\"/\")) {\n return null;\n }\n\n // Get the directory of the current file\n const lastSlashIndex = currentFilePath.lastIndexOf(\"/\");\n const currentDir =\n lastSlashIndex >= 0 ? currentFilePath.slice(0, lastSlashIndex) : projectRoot;\n\n // Resolve the relative path\n let resolvedPath: string;\n if (importPath.startsWith(\"/\")) {\n // Absolute from project root\n resolvedPath = projectRoot + importPath;\n } else {\n // Relative path - need to resolve . and ..\n const parts = currentDir.split(\"/\").filter(Boolean);\n const importParts = importPath.split(\"/\");\n\n for (const part of importParts) {\n if (part === \".\") {\n continue;\n } else if (part === \"..\") {\n parts.pop();\n } else {\n parts.push(part);\n }\n }\n\n resolvedPath = \"/\" + parts.join(\"/\");\n }\n\n // Common extensions to try\n const extensions = [\"\", \".ts\", \".tsx\", \".js\", \".jsx\", \"/index.ts\", \"/index.tsx\", \"/index.js\", \"/index.jsx\"];\n\n for (const ext of extensions) {\n const fullPath = resolvedPath + ext;\n // We just return the resolved path - the caller will check if coverage exists\n // This is a simplified resolution that doesn't check file existence\n if (ext === \"\" && (resolvedPath.endsWith(\".ts\") || resolvedPath.endsWith(\".tsx\") ||\n resolvedPath.endsWith(\".js\") || resolvedPath.endsWith(\".jsx\"))) {\n return resolvedPath;\n }\n if (ext !== \"\") {\n return fullPath;\n }\n }\n\n return resolvedPath;\n}\n\n/**\n * Aggregate coverage from imported files\n *\n * For each import path, attempts to find coverage data for that file\n * and aggregates the statement coverage across all imported files.\n *\n * Note: When an import is used, the entire imported file's coverage is included\n * in the calculation (full file coverage when any part of a dependency is used).\n *\n * @param importPaths - Set of import module specifiers\n * @param coverage - Istanbul coverage data\n * @param projectRoot - Project root directory\n * @param currentFilePath - Path of the file containing the imports (for resolving relative paths)\n * @returns Aggregated coverage stats: { covered: number, total: number }\n */\nexport function aggregateImportCoverage(\n importPaths: Set<string>,\n coverage: IstanbulCoverage,\n projectRoot: string,\n currentFilePath: string = \"\"\n): { covered: number; total: number } {\n let totalCovered = 0;\n let totalStatements = 0;\n\n for (const importPath of importPaths) {\n // Try to resolve the import path to a file path\n const resolvedPath = resolveImportPath(importPath, currentFilePath, projectRoot);\n\n if (!resolvedPath) {\n // Non-relative import (node_modules) - skip\n continue;\n }\n\n // Try to find coverage for this file\n const fileCoverage = findCoverageForFile(coverage, resolvedPath);\n\n if (!fileCoverage) {\n // Also try with the raw import path in case coverage uses that format\n const rawCoverage = findCoverageForFile(coverage, importPath);\n if (!rawCoverage) {\n continue;\n }\n // Use the raw coverage\n const statementCount = Object.keys(rawCoverage.s).length;\n const coveredCount = Object.values(rawCoverage.s).filter((hits) => hits > 0).length;\n totalStatements += statementCount;\n totalCovered += coveredCount;\n continue;\n }\n\n // Aggregate full file coverage (all statements in the imported file)\n const statementCount = Object.keys(fileCoverage.s).length;\n const coveredCount = Object.values(fileCoverage.s).filter((hits) => hits > 0).length;\n\n totalStatements += statementCount;\n totalCovered += coveredCount;\n }\n\n return { covered: totalCovered, total: totalStatements };\n}\n","/**\n * Chunk Analyzer\n *\n * Analyzes individual \"chunks\" (exported functions, classes, hooks, stores)\n * for test coverage. Used for granular coverage reporting at the function level\n * instead of file level.\n *\n * Categories:\n * - utility: formatters, validators, helpers (strict threshold)\n * - hook: React hooks (use* pattern) (strict threshold)\n * - store: Zustand/Redux stores (strict threshold)\n * - handler: event handler functions (relaxed threshold)\n * - component: JSX-returning functions (relaxed threshold)\n */\n\nimport type { TSESTree } from \"@typescript-eslint/utils\";\nimport {\n findStatementsInRange,\n calculateCoverageFromStatements,\n type IstanbulFileCoverage,\n type SourceLocation,\n type CoverageStats,\n} from \"./jsx-coverage-analyzer.js\";\n\nexport type ChunkCategory =\n | \"utility\"\n | \"hook\"\n | \"store\"\n | \"handler\"\n | \"component\";\n\nexport interface ChunkInfo {\n /** Function/export name */\n name: string;\n /** Category of this chunk */\n category: ChunkCategory;\n /** Whether this is React-related code */\n isReactRelated: boolean;\n /** Location in source (full function body) */\n loc: SourceLocation;\n /** Location of just the declaration line (for error highlighting) */\n declarationLoc: SourceLocation;\n /** Function ID in fnMap (if found) */\n fnId: string | null;\n /** Is this an export? */\n isExport: boolean;\n}\n\nexport interface ChunkCoverageResult extends ChunkInfo {\n /** Coverage stats for this chunk */\n coverage: {\n /** Was the function ever called during tests? */\n functionCalled: boolean;\n /** Number of statements covered */\n statementsCovered: number;\n /** Total statements in the function */\n statementsTotal: number;\n /** Coverage percentage (0-100) */\n percentage: number;\n };\n}\n\n/**\n * Check if an AST node contains JSX elements\n */\nfunction containsJSX(\n node: TSESTree.Node,\n visited: WeakSet<object> = new WeakSet()\n): boolean {\n if (!node || typeof node !== \"object\") return false;\n if (visited.has(node)) return false;\n visited.add(node);\n\n if (\n node.type === \"JSXElement\" ||\n node.type === \"JSXFragment\" ||\n node.type === \"JSXText\"\n ) {\n return true;\n }\n\n // Only traverse known child properties to avoid parent references\n const childKeys = [\n \"body\",\n \"declarations\",\n \"declaration\",\n \"expression\",\n \"expressions\",\n \"argument\",\n \"arguments\",\n \"callee\",\n \"elements\",\n \"properties\",\n \"value\",\n \"init\",\n \"consequent\",\n \"alternate\",\n \"test\",\n \"left\",\n \"right\",\n \"object\",\n \"property\",\n \"children\",\n \"openingElement\",\n \"closingElement\",\n ];\n\n for (const key of childKeys) {\n const child = (node as unknown as Record<string, unknown>)[key];\n if (child && typeof child === \"object\") {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (item && typeof item === \"object\" && \"type\" in item) {\n if (containsJSX(item as TSESTree.Node, visited)) return true;\n }\n }\n } else if (\"type\" in child) {\n if (containsJSX(child as TSESTree.Node, visited)) return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Categorize a function based on its name and content\n */\nexport function categorizeChunk(\n name: string,\n functionBody: TSESTree.Node | null,\n isInStoreFile: boolean\n): { category: ChunkCategory; isReactRelated: boolean } {\n // Hooks: starts with \"use\" followed by uppercase\n if (/^use[A-Z]/.test(name)) {\n return { category: \"hook\", isReactRelated: true };\n }\n\n // Store: in a .store.ts file or name ends with Store\n if (isInStoreFile || /Store$/.test(name)) {\n return { category: \"store\", isReactRelated: false };\n }\n\n // Handler: starts with \"handle\" or \"on\" followed by uppercase\n if (/^handle[A-Z]/.test(name) || /^on[A-Z]/.test(name)) {\n return { category: \"handler\", isReactRelated: true };\n }\n\n // Component: contains JSX (check the function body)\n if (functionBody && containsJSX(functionBody)) {\n return { category: \"component\", isReactRelated: true };\n }\n\n // Default: utility\n return { category: \"utility\", isReactRelated: false };\n}\n\n/**\n * Extract function name from various AST patterns\n */\nfunction getFunctionName(\n node:\n | TSESTree.FunctionDeclaration\n | TSESTree.ArrowFunctionExpression\n | TSESTree.FunctionExpression,\n parent: TSESTree.Node | undefined\n): string | null {\n // Named function declaration\n if (node.type === \"FunctionDeclaration\" && node.id) {\n return node.id.name;\n }\n\n // Variable declarator: const foo = () => {}\n if (parent?.type === \"VariableDeclarator\" && parent.id.type === \"Identifier\") {\n return parent.id.name;\n }\n\n // Property: { foo: () => {} }\n if (parent?.type === \"Property\" && parent.key.type === \"Identifier\") {\n return parent.key.name;\n }\n\n return null;\n}\n\n/**\n * Find function ID in fnMap by matching name and location\n */\nfunction findFnId(\n name: string,\n loc: SourceLocation,\n fileCoverage: IstanbulFileCoverage | null\n): string | null {\n if (!fileCoverage) return null;\n\n for (const [fnId, fnInfo] of Object.entries(fileCoverage.fnMap)) {\n // Match by name first\n if (fnInfo.name === name) {\n return fnId;\n }\n\n // If name doesn't match (anonymous or different), match by location\n // Function declaration location should be close to our AST location\n if (\n fnInfo.decl.start.line === loc.start.line ||\n fnInfo.loc.start.line === loc.start.line\n ) {\n return fnId;\n }\n }\n\n return null;\n}\n\n/**\n * Calculate coverage for a specific function\n */\nfunction calculateChunkCoverage(\n fnId: string | null,\n loc: SourceLocation,\n fileCoverage: IstanbulFileCoverage | null\n): ChunkCoverageResult[\"coverage\"] {\n if (!fileCoverage) {\n return {\n functionCalled: false,\n statementsCovered: 0,\n statementsTotal: 0,\n percentage: 0,\n };\n }\n\n // Check if function was called\n const functionCalled = fnId !== null && (fileCoverage.f[fnId] ?? 0) > 0;\n\n // Find statements within function body\n const statementIds = findStatementsInRange(loc, fileCoverage);\n const stats = calculateCoverageFromStatements(statementIds, fileCoverage);\n\n return {\n functionCalled,\n statementsCovered: stats.covered,\n statementsTotal: stats.total,\n percentage: stats.percentage,\n };\n}\n\n/**\n * Collect all exported functions/classes from an AST\n */\ninterface ExportedFunction {\n name: string;\n node:\n | TSESTree.FunctionDeclaration\n | TSESTree.ArrowFunctionExpression\n | TSESTree.FunctionExpression;\n loc: SourceLocation;\n /** Location of just the declaration line (for highlighting) */\n declarationLoc: SourceLocation;\n body: TSESTree.Node | null;\n}\n\n/**\n * Get declaration location - just the first line of the function\n * This is used for error highlighting to avoid highlighting the entire function body\n */\nfunction getDeclarationLoc(loc: SourceLocation): SourceLocation {\n return {\n start: loc.start,\n end: { line: loc.start.line, column: 999 },\n };\n}\n\nfunction collectExportedFunctions(ast: TSESTree.Program): ExportedFunction[] {\n const exports: ExportedFunction[] = [];\n\n for (const node of ast.body) {\n // export function foo() {}\n if (\n node.type === \"ExportNamedDeclaration\" &&\n node.declaration?.type === \"FunctionDeclaration\" &&\n node.declaration.id\n ) {\n const loc = node.declaration.loc;\n exports.push({\n name: node.declaration.id.name,\n node: node.declaration,\n loc,\n declarationLoc: getDeclarationLoc(loc),\n body: node.declaration.body,\n });\n }\n\n // export const foo = () => {}\n if (\n node.type === \"ExportNamedDeclaration\" &&\n node.declaration?.type === \"VariableDeclaration\"\n ) {\n for (const decl of node.declaration.declarations) {\n if (\n decl.id.type === \"Identifier\" &&\n decl.init &&\n (decl.init.type === \"ArrowFunctionExpression\" ||\n decl.init.type === \"FunctionExpression\")\n ) {\n // For arrow functions, use the variable declaration line, not the function body\n const loc = decl.init.loc;\n const declarationLoc = getDeclarationLoc(decl.loc);\n exports.push({\n name: decl.id.name,\n node: decl.init,\n loc,\n declarationLoc,\n body: decl.init.body,\n });\n }\n }\n }\n\n // export default function foo() {}\n if (\n node.type === \"ExportDefaultDeclaration\" &&\n node.declaration.type === \"FunctionDeclaration\"\n ) {\n const name = node.declaration.id?.name ?? \"default\";\n const loc = node.declaration.loc;\n exports.push({\n name,\n node: node.declaration,\n loc,\n declarationLoc: getDeclarationLoc(loc),\n body: node.declaration.body,\n });\n }\n\n // export default () => {}\n if (\n node.type === \"ExportDefaultDeclaration\" &&\n (node.declaration.type === \"ArrowFunctionExpression\" ||\n node.declaration.type === \"FunctionExpression\")\n ) {\n const loc = node.declaration.loc;\n exports.push({\n name: \"default\",\n node: node.declaration,\n loc,\n declarationLoc: getDeclarationLoc(loc),\n body: node.declaration.body,\n });\n }\n }\n\n return exports;\n}\n\n/**\n * Analyze all exported chunks in a file for coverage\n *\n * @param ast - The parsed AST of the file\n * @param filePath - Path to the file (used for store detection)\n * @param fileCoverage - Istanbul coverage data for the file (or null)\n * @returns Array of chunks with their coverage information\n */\nexport function analyzeChunks(\n ast: TSESTree.Program,\n filePath: string,\n fileCoverage: IstanbulFileCoverage | null\n): ChunkCoverageResult[] {\n const isInStoreFile = /\\.store\\.(ts|tsx)$/.test(filePath);\n const exportedFunctions = collectExportedFunctions(ast);\n const results: ChunkCoverageResult[] = [];\n\n for (const exported of exportedFunctions) {\n const { category, isReactRelated } = categorizeChunk(\n exported.name,\n exported.body,\n isInStoreFile\n );\n\n const fnId = findFnId(exported.name, exported.loc, fileCoverage);\n const coverage = calculateChunkCoverage(fnId, exported.loc, fileCoverage);\n\n results.push({\n name: exported.name,\n category,\n isReactRelated,\n loc: exported.loc,\n declarationLoc: exported.declarationLoc,\n fnId,\n isExport: true,\n coverage,\n });\n }\n\n return results;\n}\n\n/**\n * Get the appropriate threshold for a chunk based on options\n */\nexport function getChunkThreshold(\n chunk: ChunkCoverageResult,\n options: {\n focusNonReact?: boolean;\n chunkThreshold?: number;\n relaxedThreshold?: number;\n }\n): number {\n const strictThreshold = options.chunkThreshold ?? 80;\n const relaxedThreshold = options.relaxedThreshold ?? 50;\n\n if (!options.focusNonReact) {\n // Uniform threshold for all chunks\n return strictThreshold;\n }\n\n // focusNonReact mode: relaxed threshold for React-related code\n if (chunk.category === \"component\" || chunk.category === \"handler\") {\n return relaxedThreshold;\n }\n\n // Strict threshold for utility, hook, store\n return strictThreshold;\n}\n"],"mappings":"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","names":["meta","existsSync","readFileSync","statSync","dirname","join","basename","existsSync","parse","readFileSync","existsSync","existsSync","readFileSync","parse","existsSync","findCoverageForFile","resolveImportPath","statementCount","coveredCount","findProjectRoot","dirname","existsSync","join","statSync","readFileSync","findCoverageForFile","basename"]}
|