test-pmd-rule 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +232 -0
- package/dist/test-pmd-rule.js +2197 -0
- package/dist/test-pmd-rule.js.map +7 -0
- package/package.json +54 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/cli/main.ts", "../src/tester/RuleTester.ts", "../src/xpath/extractXPath.ts", "../src/xpath/checkCoverage.ts", "../src/xpath/extractors/extractNodeTypes.ts", "../src/xpath/extractors/extractOperators.ts", "../src/xpath/extractors/extractAttributes.ts", "../src/xpath/extractors/extractConditionals.ts", "../src/xpath/analyzeXPath.ts", "../src/parser/extractMarkers.ts", "../src/parser/parseExample.ts", "../src/parser/createTestFile.ts", "../src/pmd/runPMD.ts", "../src/pmd/parseViolations.ts", "../src/tester/quality/checkRuleMetadata.ts", "../src/tester/quality/checkExamples.ts", "../src/tester/quality/checkDuplicates.ts", "../src/tester/qualityChecks.ts", "../src/utils/concurrency.ts", "../src/coverage/trackCoverage.ts", "../src/coverage/generateLcov.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * @file\n * CLI entry point for PMD Rule Tester. Tests PMD rules using examples embedded in XML rule files.\n */\nimport { existsSync, readdirSync, statSync } from 'fs';\nimport { resolve, extname } from 'path';\nimport { argv } from 'process';\nimport { cpus } from 'os';\nimport { RuleTester } from '../tester/RuleTester.js';\nimport { limitConcurrency } from '../utils/concurrency.js';\nimport {\n\tCoverageTracker,\n\ttype CoverageData,\n} from '../coverage/trackCoverage.js';\nimport { generateLcovReport } from '../coverage/generateLcov.js';\n\nconst EXIT_CODE_SUCCESS = 0;\nconst EXIT_CODE_ERROR = 1;\nconst ARGV_SLICE_INDEX = 2;\nconst MIN_ARGS_COUNT = 0;\nconst MAX_ARGS_COUNT = 2;\nconst FIRST_ARG_INDEX = 0;\nconst SECOND_ARG_INDEX = 1;\nconst REPEAT_CHAR_COUNT = 60;\nconst MIN_FAILED_FILES_COUNT = 0;\n\n/**\n * Recursively finds all XML files in a directory.\n * @param directory - Directory to search.\n * @returns Array of absolute paths to XML files.\n */\nfunction findXmlFiles(directory: string): string[] {\n\tconst xmlFiles: string[] = [];\n\tconst items = readdirSync(directory);\n\n\tfor (const item of items) {\n\t\tconst fullPath = resolve(directory, item);\n\t\tconst stat = statSync(fullPath);\n\n\t\tif (stat.isDirectory()) {\n\t\t\t// Recursively search subdirectories\n\t\t\txmlFiles.push(...findXmlFiles(fullPath));\n\t\t} else if (\n\t\t\tstat.isFile() &&\n\t\t\textname(fullPath).toLowerCase() === '.xml'\n\t\t) {\n\t\t\txmlFiles.push(fullPath);\n\t\t}\n\t}\n\n\treturn xmlFiles;\n}\n\n/**\n * Test a single rule file.\n * @param ruleFilePath - Path to the XML rule file.\n * @param coverageTracker - Coverage tracker for this file (if coverage is enabled).\n * @param maxConcurrency - Maximum concurrency for example testing.\n * @returns Promise resolving to test result and coverage data.\n */\nasync function testRuleFile(\n\truleFilePath: Readonly<string>,\n\tcoverageTracker: Readonly<CoverageTracker | null>,\n\tmaxConcurrency: Readonly<number>,\n): Promise<{\n\tfilePath: string;\n\tsuccess: boolean;\n\terror?: string;\n\tcoverageData?: Readonly<CoverageData>;\n}> {\n\ttry {\n\t\tconst tester = new RuleTester(ruleFilePath);\n\t\tconst result = await tester.runCoverageTest(false, maxConcurrency);\n\n\t\t// Record coverage data if tracker is provided\n\t\tconst hasCoverageTracker = coverageTracker !== null;\n\t\tconst MIN_COVERED_LINES_COUNT = 0;\n\t\tconst coveredLines = result.xpathCoverage.coveredLineNumbers;\n\t\tif (\n\t\t\thasCoverageTracker &&\n\t\t\tcoveredLines &&\n\t\t\tcoveredLines.length > MIN_COVERED_LINES_COUNT\n\t\t) {\n\t\t\tfor (const lineNumber of coveredLines) {\n\t\t\t\tcoverageTracker.recordXPathLine(lineNumber);\n\t\t\t}\n\t\t}\n\n\t\t// Display results for this file\n\t\tconsole.log(\n\t\t\t`\\n\uD83E\uDDEA Testing rule: ${ruleFilePath}${coverageTracker ? ' (with coverage)' : ''}\\n`,\n\t\t);\n\n\t\t// Display detailed test results\n\t\tconst MIN_DETAILED_RESULTS_COUNT = 0;\n\t\tif (\n\t\t\tresult.detailedTestResults &&\n\t\t\tresult.detailedTestResults.length > MIN_DETAILED_RESULTS_COUNT\n\t\t) {\n\t\t\tconsole.log('\uD83D\uDCCB Test Details:');\n\t\t\tfor (const testResult of result.detailedTestResults) {\n\t\t\t\tconst status = testResult.passed ? '\u2705' : '\u274C';\n\t\t\t\tconst testType =\n\t\t\t\t\ttestResult.testType === 'violation' ? 'Violation' : 'Valid';\n\t\t\t\tconst lineInfo =\n\t\t\t\t\ttestResult.lineNumber !== undefined\n\t\t\t\t\t\t? ` Line: ${String(testResult.lineNumber)}`\n\t\t\t\t\t\t: '';\n\t\t\t\tconsole.log(\n\t\t\t\t\t` - Example ${String(testResult.exampleIndex)} Test: ${testType} ${status}${lineInfo}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Display summary\n\t\tconst MIN_COUNT = 0;\n\t\tconst INDEX_OFFSET = 1;\n\n\t\t// Show overall success\n\t\tif (result.success) {\n\t\t\tconsole.log('\\n\uD83D\uDCCA Test Summary:');\n\t\t\tconsole.log(` Examples tested: ${String(result.examplesTested)}`);\n\t\t\tconsole.log(` Examples passed: ${String(result.examplesPassed)}`);\n\t\t\tconsole.log(\n\t\t\t\t` Total violations: ${String(result.totalViolations)}`,\n\t\t\t);\n\t\t\tconsole.log(\n\t\t\t\t` Rule triggers violations: ${result.ruleTriggersViolations ? '\u2705 Yes' : '\u274C No'}`,\n\t\t\t);\n\t\t}\n\n\t\t// XPath Coverage Details\n\t\tconsole.log('\\n\uD83D\uDD0D XPath Coverage:');\n\t\tif (result.xpathCoverage.overallSuccess) {\n\t\t\tconsole.log(' Status: \u2705 Complete');\n\t\t} else {\n\t\t\tconst INCOMPLETE_STATUS_MESSAGE = ' Status: \u26A0\uFE0F Incomplete';\n\t\t\tconsole.log(INCOMPLETE_STATUS_MESSAGE);\n\t\t}\n\n\t\tif (result.xpathCoverage.coverage.length > MIN_COUNT) {\n\t\t\tconsole.log(\n\t\t\t\t` Coverage items: ${String(result.xpathCoverage.coverage.length)}`,\n\t\t\t);\n\t\t\tresult.xpathCoverage.coverage.forEach(\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameters for forEach\n\t\t\t\t(coverage, index) => {\n\t\t\t\t\tconst itemNumber = index + INDEX_OFFSET;\n\t\t\t\t\t// Determine status icon: \u2705 for complete, \u26A0\uFE0F for incomplete, \u274C for failed\n\t\t\t\t\tconst status: string = coverage.success\n\t\t\t\t\t\t? '\u2705'\n\t\t\t\t\t\t: coverage.evidence.some(\n\t\t\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameter for some\n\t\t\t\t\t\t\t\t\t(evidence) =>\n\t\t\t\t\t\t\t\t\t\tevidence.count > MIN_COUNT &&\n\t\t\t\t\t\t\t\t\t\tevidence.count < evidence.required,\n\t\t\t\t\t\t\t )\n\t\t\t\t\t\t\t? '\u26A0\uFE0F'\n\t\t\t\t\t\t\t: '\u274C';\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t` ${String(itemNumber)}. ${status} ${coverage.message}`,\n\t\t\t\t\t);\n\t\t\t\t\tif (coverage.evidence.length > MIN_COUNT) {\n\t\t\t\t\t\tcoverage.evidence.forEach(\n\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameters for forEach\n\t\t\t\t\t\t\t(evidence) => {\n\t\t\t\t\t\t\t\tconst { description } = evidence;\n\t\t\t\t\t\t\t\t// Only show description if it has content (not empty)\n\t\t\t\t\t\t\t\tif (description.length > MIN_COUNT) {\n\t\t\t\t\t\t\t\t\t// Check if description contains newlines (for conditionals, node types, etc.)\n\t\t\t\t\t\t\t\t\tif (description.includes('\\n')) {\n\t\t\t\t\t\t\t\t\t\t// Split by newline and indent each line\n\t\t\t\t\t\t\t\t\t\tdescription\n\t\t\t\t\t\t\t\t\t\t\t.split('\\n')\n\t\t\t\t\t\t\t\t\t\t\t.forEach(\n\t\t\t\t\t\t\t\t\t\t\t\t(line: Readonly<string>) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t` ${line}`,\n\t\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tconsole.log(` ${description}`);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tif (result.hardcodedValues.length > MIN_COUNT) {\n\t\t\tconsole.log(\n\t\t\t\t`\\n\u26A0\uFE0F Hardcoded values found: ${String(result.hardcodedValues.length)}`,\n\t\t\t);\n\t\t\tresult.hardcodedValues.forEach(\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameters for forEach\n\t\t\t\t(issue) => {\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t` - ${issue.type}: ${issue.value} (${issue.severity})`,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\t// Determine final status based on coverage completeness\n\t\tconst isCoverageIncomplete = !result.xpathCoverage.overallSuccess;\n\t\tif (result.success && !isCoverageIncomplete) {\n\t\t\tconsole.log('\\n\u2705 All tests passed!');\n\t\t} else if (isCoverageIncomplete) {\n\t\t\tconsole.log('\\n\u274C Tests failed, incomplete coverage');\n\t\t}\n\n\t\ttester.cleanup();\n\t\tconst coverageData: CoverageData | undefined =\n\t\t\tcoverageTracker !== null\n\t\t\t\t? coverageTracker.getCoverageData()\n\t\t\t\t: undefined;\n\t\treturn {\n\t\t\tcoverageData,\n\t\t\tfilePath: ruleFilePath,\n\t\t\tsuccess: result.success,\n\t\t};\n\t} catch (error: unknown) {\n\t\tconst errorMessage =\n\t\t\terror instanceof Error ? error.message : String(error);\n\t\tconsole.error(`\\n\u274C Error testing ${ruleFilePath}: ${errorMessage}`);\n\t\treturn { error: errorMessage, filePath: ruleFilePath, success: false };\n\t}\n}\n\n/**\n * Main CLI function that processes command line arguments and executes rule testing.\n * @returns Promise that resolves when testing is complete.\n * @throws {Error} If rule testing fails.\n */\nasync function main(): Promise<void> {\n\tconst args = argv.slice(ARGV_SLICE_INDEX);\n\n\t// Validate arguments\n\tif (args.length === MIN_ARGS_COUNT || args.length > MAX_ARGS_COUNT) {\n\t\tconsole.log('Usage: test-pmd-rule <rule.xml|directory> [--coverage]');\n\t\tconsole.log(\n\t\t\t'\\nThis tool tests PMD rules using examples embedded in XML rule files.',\n\t\t);\n\t\tconsole.log('\\nArguments:');\n\t\tconsole.log(\n\t\t\t' <rule.xml|directory> Path to XML rule file or directory containing XML files',\n\t\t);\n\t\tconsole.log(\n\t\t\t' --coverage Generate LCOV coverage report in coverage/lcov.info',\n\t\t);\n\t\tconsole.log('\\nRequirements:');\n\t\tconsole.log('- PMD CLI installed and in PATH');\n\t\tconsole.log('- Node.js 25+');\n\t\tprocess.exit(EXIT_CODE_ERROR);\n\t}\n\n\tconst pathArg = args[FIRST_ARG_INDEX];\n\tif (typeof pathArg !== 'string') {\n\t\tconsole.error('\u274C Invalid path argument');\n\t\tprocess.exit(EXIT_CODE_ERROR);\n\t}\n\n\t// Check for --coverage flag\n\tconst hasCoverageFlag =\n\t\targs.length > SECOND_ARG_INDEX &&\n\t\targs[SECOND_ARG_INDEX] === '--coverage';\n\n\t// Validate input path\n\tif (!existsSync(pathArg)) {\n\t\tconsole.error(`\u274C Path not found: ${pathArg}`);\n\t\tprocess.exit(EXIT_CODE_ERROR);\n\t}\n\n\t// Determine if path is file or directory and find XML files\n\tconst stat = statSync(pathArg);\n\tconst xmlFiles: string[] = [];\n\n\tif (stat.isFile()) {\n\t\t// Single file\n\t\tif (!pathArg.endsWith('.xml')) {\n\t\t\tconsole.error('\u274C File must be an XML rule file (.xml)');\n\t\t\tprocess.exit(EXIT_CODE_ERROR);\n\t\t}\n\t\txmlFiles.push(pathArg);\n\t} else if (stat.isDirectory()) {\n\t\t// Directory - find all XML files recursively\n\t\txmlFiles.push(...findXmlFiles(pathArg));\n\t\tconst MIN_XML_FILES_COUNT = 0;\n\t\tif (xmlFiles.length === MIN_XML_FILES_COUNT) {\n\t\t\tconsole.error(`\u274C No XML files found in directory: ${pathArg}`);\n\t\t\tprocess.exit(EXIT_CODE_ERROR);\n\t\t}\n\t} else {\n\t\tconsole.error(`\u274C Path is neither a file nor directory: ${pathArg}`);\n\t\tprocess.exit(EXIT_CODE_ERROR);\n\t}\n\n\t// Get CPU count for concurrency\n\tconst cpuCount = cpus().length;\n\tconst maxFileConcurrency = Math.min(xmlFiles.length, cpuCount);\n\n\t/**\n\t * Use CPU count for example concurrency - PMD processes can handle parallel execution.\n\t */\n\tconst maxExampleConcurrency = cpuCount;\n\n\tconsole.log(\n\t\t`\\n\uD83D\uDE80 Processing ${String(xmlFiles.length)} rule file(s) with ${String(maxFileConcurrency)} parallel workers`,\n\t);\n\tconsole.log(\n\t\t` Each file will test examples with up to ${String(maxExampleConcurrency)} parallel workers\\n`,\n\t);\n\n\t// Create coverage trackers if coverage is enabled\n\tconst coverageTrackers: Map<string, CoverageTracker> | null =\n\t\thasCoverageFlag ? new Map<string, CoverageTracker>() : null;\n\n\t// Create tasks for each file\n\tinterface TaskResult {\n\t\tfilePath: string;\n\t\tsuccess: boolean;\n\t\terror?: string;\n\t\tcoverageData?: Readonly<CoverageData>;\n\t}\n\tconst tasks: (() => Promise<TaskResult>)[] = xmlFiles.map(\n\t\t(filePath: Readonly<string>) => async (): Promise<TaskResult> => {\n\t\t\tconst tracker =\n\t\t\t\tcoverageTrackers !== null\n\t\t\t\t\t? (coverageTrackers.get(filePath) ??\n\t\t\t\t\t\tnew CoverageTracker(filePath))\n\t\t\t\t\t: null;\n\t\t\tif (tracker !== null && coverageTrackers !== null) {\n\t\t\t\tcoverageTrackers.set(filePath, tracker);\n\t\t\t}\n\t\t\treturn testRuleFile(filePath, tracker, maxExampleConcurrency);\n\t\t},\n\t);\n\n\t// Execute tasks with concurrency limit\n\tconst results = await limitConcurrency(tasks, maxFileConcurrency);\n\n\t// Summarize results\n\tconst successfulFiles = results.filter(\n\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameter\n\t\t(r: Readonly<TaskResult>) => r.success,\n\t).length;\n\tconst failedFiles = results.filter(\n\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameter\n\t\t(r: Readonly<TaskResult>) => !r.success,\n\t).length;\n\n\tconsole.log('\\n' + '='.repeat(REPEAT_CHAR_COUNT));\n\tconsole.log('\uD83C\uDFAF OVERALL RESULTS');\n\tconsole.log('='.repeat(REPEAT_CHAR_COUNT));\n\tconsole.log(`Total files processed: ${String(xmlFiles.length)}`);\n\tconsole.log(`Successful: ${String(successfulFiles)}`);\n\tconsole.log(`Failed: ${String(failedFiles)}`);\n\n\tif (failedFiles > MIN_FAILED_FILES_COUNT) {\n\t\tconsole.log('\\n\u274C Failed files:');\n\t\tresults\n\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameter\n\t\t\t.filter((r: Readonly<TaskResult>) => !r.success)\n\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameter\n\t\t\t.forEach((result: Readonly<TaskResult>) => {\n\t\t\t\tconst errorMessage = result.error ?? '';\n\t\t\t\tconst MIN_ERROR_LENGTH = 0;\n\t\t\t\tconst errorSuffix =\n\t\t\t\t\terrorMessage.length > MIN_ERROR_LENGTH\n\t\t\t\t\t\t? `: ${errorMessage}`\n\t\t\t\t\t\t: '';\n\t\t\t\tconsole.log(` - ${result.filePath}${errorSuffix}`);\n\t\t\t});\n\t}\n\n\t// Generate coverage report if --coverage flag is set\n\tif (hasCoverageFlag && coverageTrackers !== null) {\n\t\tconst coverageData: CoverageData[] = Array.from(\n\t\t\tcoverageTrackers.values(),\n\t\t).map((tracker: Readonly<CoverageTracker>) =>\n\t\t\ttracker.getCoverageData(),\n\t\t);\n\t\ttry {\n\t\t\tgenerateLcovReport(coverageData, 'coverage/lcov.info');\n\t\t\tconsole.log('\\n\uD83D\uDCCA Coverage report generated: coverage/lcov.info');\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage =\n\t\t\t\terror instanceof Error ? error.message : String(error);\n\t\t\tconsole.error(\n\t\t\t\t`\\n\u274C Error generating coverage report: ${errorMessage}`,\n\t\t\t);\n\t\t\tprocess.exit(EXIT_CODE_ERROR);\n\t\t}\n\t}\n\n\tprocess.exit(\n\t\tfailedFiles === MIN_FAILED_FILES_COUNT\n\t\t\t? EXIT_CODE_SUCCESS\n\t\t\t: EXIT_CODE_ERROR,\n\t);\n}\n\n// Handle uncaught errors\nprocess.on('uncaughtException', (error: Readonly<Error>) => {\n\tconsole.error(`Unexpected error: ${error.message}`);\n\tprocess.exit(EXIT_CODE_ERROR);\n});\n\nprocess.on(\n\t'unhandledRejection',\n\t(_reason: Readonly<unknown>, _promise: Readonly<Promise<unknown>>) => {\n\t\tconst reasonString =\n\t\t\ttypeof _reason === 'string'\n\t\t\t\t? _reason\n\t\t\t\t: _reason instanceof Error\n\t\t\t\t\t? _reason.message\n\t\t\t\t\t: JSON.stringify(_reason);\n\t\tconst promiseString = '[Promise]';\n\t\tconsole.error(\n\t\t\t`Unhandled Rejection at: ${promiseString}, reason: ${reasonString}`,\n\t\t);\n\t\tprocess.exit(EXIT_CODE_ERROR);\n\t},\n);\n\n// Run main if called directly\nmain().catch((error: unknown) => {\n\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\tconsole.error(`Unexpected error: ${errorMessage}`);\n\tprocess.exit(EXIT_CODE_ERROR);\n});\n", "/**\n * @file\n * RuleTester class orchestrates PMD rule testing workflow.\n */\nimport { readFileSync, existsSync } from 'fs';\nimport { DOMParser } from '@xmldom/xmldom';\nimport { extractXPath } from '../xpath/extractXPath.js';\nimport { checkXPathCoverage } from '../xpath/checkCoverage.js';\nimport { parseExample } from '../parser/parseExample.js';\nimport { createTestFile } from '../parser/createTestFile.js';\nimport { runPMD } from '../pmd/runPMD.js';\nimport type {\n\tRuleMetadata,\n\tExampleData,\n\tOverallTestResults,\n\tTestCaseResult,\n} from '../types/index.js';\nimport { runQualityChecks } from './qualityChecks.js';\n\nconst MIN_EXAMPLES_COUNT = 0;\nconst MIN_VIOLATIONS_COUNT = 0;\nconst EMPTY_STRING = '';\nconst DEFAULT_CONCURRENCY = 1;\n\n/**\n * Result of validating a single example with PMD.\n */\ninterface ExampleValidationResult {\n\texampleIndex: number;\n\tpassed: boolean;\n\tactualViolations: number;\n\texpectedViolations: number;\n\texpectedValids: number;\n\ttestCaseResults: TestCaseResult[];\n}\n\n/**\n * Safely get attribute value from DOM element.\n * @param element - The DOM element to get attribute from.\n * @param name - The attribute name to retrieve.\n * @returns The attribute value or null if not found.\n */\nfunction getAttributeValue(\n\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Element is used for getAttribute\n\telement: Readonly<Element>,\n\tname: Readonly<string>,\n): string | null {\n\treturn element.getAttribute(name);\n}\n\n/**\n * Main tester class that orchestrates PMD rule testing workflow.\n * Extracts rule metadata, examples, runs PMD validation, and analyzes XPath coverage.\n */\n/* eslint-disable @typescript-eslint/member-ordering -- Methods organized by functionality */\nexport class RuleTester {\n\tprivate readonly ruleFilePath: string;\n\tprivate readonly ruleMetadata: RuleMetadata;\n\tprivate examples: ExampleData[];\n\tprivate readonly results: OverallTestResults;\n\tpublic readonly ruleName: string;\n\tpublic readonly category: string;\n\n\t/**\n\t * Creates a new RuleTester instance.\n\t * @param ruleFilePath - Absolute or relative path to the PMD rule XML file.\n\t * @throws {Error} If rule file does not exist or cannot be read.\n\t */\n\tpublic constructor(ruleFilePath: string) {\n\t\tif (!existsSync(ruleFilePath)) {\n\t\t\tthrow new Error(`Rule file not found: ${ruleFilePath}`);\n\t\t}\n\n\t\tif (!ruleFilePath.endsWith('.xml')) {\n\t\t\tthrow new Error('Rule file must have .xml extension');\n\t\t}\n\n\t\tthis.ruleFilePath = ruleFilePath;\n\t\tthis.ruleMetadata = this.extractRuleMetadata();\n\t\tthis.ruleName = this.ruleMetadata.ruleName ?? 'unknown';\n\t\tthis.category = this.extractCategory(ruleFilePath);\n\t\tthis.examples = [];\n\t\tthis.results = this.initializeResults();\n\t}\n\n\t/**\n\t * Extract category from rule file path.\n\t * @param ruleFilePath - Path to the rule file.\n\t * @returns Category name.\n\t * @private\n\t */\n\tprivate extractCategory(ruleFilePath: string): string {\n\t\t// Using this.ruleFilePath to satisfy class-methods-use-this\n\t\tvoid this.ruleFilePath;\n\t\tconst pathParts = ruleFilePath.split('/');\n\t\tconst rulesetsIndex = pathParts.findIndex(\n\t\t\t(part) => part === 'rulesets',\n\t\t);\n\t\tconst minIndex = -1;\n\t\tconst categoryIndexOffset = 1;\n\t\tif (\n\t\t\trulesetsIndex !== minIndex &&\n\t\t\trulesetsIndex < pathParts.length - categoryIndexOffset\n\t\t) {\n\t\t\tconst categoryIndex = rulesetsIndex + categoryIndexOffset;\n\t\t\t// The bounds check above ensures categoryIndex < pathParts.length,\n\t\t\t// and split('/') always returns a dense array, so pathParts[categoryIndex] is always defined\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Bounds check ensures index is valid, split() returns dense array\n\t\t\tconst category = pathParts[categoryIndex]!;\n\t\t\treturn category;\n\t\t}\n\t\treturn 'unknown';\n\t}\n\n\t/**\n\t * Extracts rule metadata (name, message, description, XPath) from the rule XML file.\n\t * @returns Parsed rule metadata object.\n\t * @public\n\t */\n\tpublic extractRuleMetadata(): RuleMetadata {\n\t\tconst content = readFileSync(this.ruleFilePath, 'utf-8');\n\t\tconst parser = new DOMParser();\n\t\tconst doc = parser.parseFromString(content, 'text/xml');\n\n\t\tconst ruleElement =\n\t\t\tdoc.getElementsByTagName('rule')[MIN_EXAMPLES_COUNT];\n\t\tif (!ruleElement) {\n\t\t\treturn {\n\t\t\t\tdescription: null,\n\t\t\t\tmessage: null,\n\t\t\t\truleName: null,\n\t\t\t\txpath: null,\n\t\t\t};\n\t\t}\n\n\t\tconst ruleName = getAttributeValue(ruleElement, 'name');\n\t\tconst message = getAttributeValue(ruleElement, 'message');\n\t\tconst descriptionElements =\n\t\t\truleElement.getElementsByTagName('description');\n\t\tlet description: string | null = null;\n\t\tif (descriptionElements.length > MIN_EXAMPLES_COUNT) {\n\t\t\t// NodeList[0] when length > 0 is always defined\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Verified by length check\n\t\t\tconst descElement = descriptionElements[MIN_EXAMPLES_COUNT]!;\n\t\t\tconst { textContent } = descElement;\n\t\t\tif (\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- textContent can be null at runtime\n\t\t\t\ttextContent !== null &&\n\t\t\t\ttextContent.trim() !== EMPTY_STRING\n\t\t\t) {\n\t\t\t\tdescription = textContent.trim();\n\t\t\t}\n\t\t}\n\n\t\tconst xpathResult = extractXPath(this.ruleFilePath);\n\t\tlet xpath: string | null = null;\n\t\tif (\n\t\t\txpathResult.success &&\n\t\t\txpathResult.data !== null &&\n\t\t\txpathResult.data !== undefined\n\t\t) {\n\t\t\txpath = xpathResult.data;\n\t\t}\n\n\t\treturn { description, message, ruleName, xpath };\n\t}\n\n\t/**\n\t * Extracts examples from the rule XML file.\n\t * @returns Array of parsed example data.\n\t * @public\n\t */\n\tpublic extractExamples(): ExampleData[] {\n\t\tconst content = readFileSync(this.ruleFilePath, 'utf-8');\n\t\tconst parser = new DOMParser();\n\t\tconst doc = parser.parseFromString(content, 'text/xml');\n\n\t\tconst exampleNodes = doc.getElementsByTagName('example');\n\t\tconst extractedExamples: ExampleData[] = [];\n\t\tconst indexOffset = 1;\n\n\t\tfor (let i = 0; i < exampleNodes.length; i++) {\n\t\t\t// NodeList[i] when i < length is always defined\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Verified by loop condition\n\t\t\tconst exampleNode = exampleNodes[i]!;\n\t\t\tconst { textContent } = exampleNode;\n\t\t\tconst MIN_CONTENT_LENGTH = 0;\n\t\t\t// textContent on Element is never null, only empty string\n\t\t\tif (textContent.length === MIN_CONTENT_LENGTH) continue;\n\t\t\tconst exampleContent = textContent.trim();\n\t\t\tif (exampleContent.length > MIN_CONTENT_LENGTH) {\n\t\t\t\t// Parse the example content using our parser module\n\t\t\t\tconst parsedExample = parseExample(exampleContent);\n\n\t\t\t\textractedExamples.push({\n\t\t\t\t\tcontent: exampleContent,\n\t\t\t\t\texampleIndex: i + indexOffset,\n\t\t\t\t\tvalidMarkers: parsedExample.validMarkers,\n\t\t\t\t\tvalids: parsedExample.valids,\n\t\t\t\t\tviolationMarkers: parsedExample.violationMarkers,\n\t\t\t\t\tviolations: parsedExample.violations,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tthis.examples = extractedExamples;\n\t\treturn this.examples;\n\t}\n\n\t/**\n\t * Gets the rule metadata.\n\t * @returns Rule metadata object.\n\t * @public\n\t */\n\tpublic getRuleMetadata(): RuleMetadata {\n\t\treturn this.ruleMetadata;\n\t}\n\n\t/**\n\t * Gets the extracted examples.\n\t * @returns Array of parsed example data.\n\t * @public\n\t */\n\tpublic getExamples(): ExampleData[] {\n\t\treturn this.examples;\n\t}\n\n\t/**\n\t * Cleans up temporary files created during testing.\n\t * @public\n\t */\n\tpublic cleanup(): void {\n\t\t// In full implementation, would clean up generated test files\n\t\t// For testing purposes, simulate cleanup\n\t\t// This would typically iterate over this.tempFiles and unlink them\n\t\t// Using this.ruleFilePath to satisfy class-methods-use-this\n\t\tvoid this.ruleFilePath;\n\t}\n\n\t/**\n\t * Runs comprehensive rule testing including PMD execution, quality checks, and XPath analysis.\n\t * @param skipPMDValidation - Skip actual PMD validation (for testing).\n\t * @param maxConcurrency - Maximum number of examples to test concurrently.\n\t * @returns Promise resolving to complete test results.\n\t * @public\n\t */\n\tpublic async runCoverageTest(\n\t\tskipPMDValidation = false,\n\t\tmaxConcurrency: Readonly<number> = DEFAULT_CONCURRENCY,\n\t): Promise<OverallTestResults> {\n\t\t// Extract examples\n\t\tthis.extractExamples();\n\n\t\t// Run quality checks\n\t\tconst qualityResult = runQualityChecks(\n\t\t\tthis.ruleMetadata,\n\t\t\tthis.examples,\n\t\t);\n\n\t\t// Actually test each example by running PMD (unless skipped for testing)\n\t\tconst INDEX_OFFSET = 1;\n\t\tconst ZERO_VIOLATIONS = 0;\n\n\t\tconst exampleResults = skipPMDValidation\n\t\t\t? // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameter for map\n\t\t\t\tthis.examples.map((_example, i: number) => ({\n\t\t\t\t\tactualViolations: ZERO_VIOLATIONS,\n\t\t\t\t\texampleIndex: i + INDEX_OFFSET,\n\t\t\t\t\texpectedValids: ZERO_VIOLATIONS,\n\t\t\t\t\texpectedViolations: ZERO_VIOLATIONS,\n\t\t\t\t\tpassed: true,\n\t\t\t\t\ttestCaseResults: [],\n\t\t\t\t}))\n\t\t\t: await this.validateExamplesWithPMD(maxConcurrency);\n\n\t\t// Set test results based on actual PMD validation\n\t\tthis.results.examplesTested = this.examples.length;\n\t\tthis.results.examplesPassed = exampleResults.filter(\n\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameter\n\t\t\t(r) => r.passed,\n\t\t).length;\n\t\tthis.results.totalViolations = exampleResults.reduce(\n\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameters\n\t\t\t(sum: number, result) => sum + result.actualViolations,\n\t\t\tMIN_VIOLATIONS_COUNT,\n\t\t);\n\t\tthis.results.ruleTriggersViolations = exampleResults.some(\n\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameter\n\t\t\t(result) => result.actualViolations > MIN_VIOLATIONS_COUNT,\n\t\t);\n\n\t\t// Collect detailed test case results\n\t\tthis.results.detailedTestResults = exampleResults.flatMap(\n\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameter\n\t\t\t(result) => result.testCaseResults,\n\t\t);\n\n\t\t// Check XPath coverage\n\t\tconst xpathCoverage = checkXPathCoverage(\n\t\t\tthis.ruleMetadata.xpath,\n\t\t\tthis.examples,\n\t\t\tthis.ruleFilePath,\n\t\t);\n\t\tthis.results.xpathCoverage = xpathCoverage;\n\n\t\t// Determine overall success - pass if all examples pass and quality checks pass\n\t\tthis.results.success =\n\t\t\tqualityResult.passed &&\n\t\t\tthis.examples.length > MIN_EXAMPLES_COUNT &&\n\t\t\tthis.results.examplesPassed === this.results.examplesTested;\n\n\t\treturn Promise.resolve(this.results);\n\t}\n\n\t/**\n\t * Validates examples by actually running PMD and checking results.\n\t * @param _maxConcurrency - Maximum number of examples to test concurrently.\n\t * @returns Promise resolving to validation results for each example.\n\t * @private\n\t */\n\tprivate async validateExamplesWithPMD(\n\t\t_maxConcurrency: Readonly<number> = DEFAULT_CONCURRENCY,\n\t): Promise<ExampleValidationResult[]> {\n\t\t// Delegate to sequential implementation for simplicity and full test coverage.\n\t\treturn this.validateExamplesWithPMDSequential();\n\t}\n\n\t/**\n\t * Validates examples sequentially (original implementation for compatibility).\n\t * @returns Promise resolving to validation results for each example.\n\t * @private\n\t */\n\tprivate async validateExamplesWithPMDSequential(): Promise<\n\t\tExampleValidationResult[]\n\t> {\n\t\tconst EXAMPLE_INDEX_OFFSET = 1;\n\t\tconst results: ExampleValidationResult[] = [];\n\n\t\tfor (let i = 0; i < this.examples.length; i++) {\n\t\t\t// Array access with valid index always returns a value, never undefined\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Loop condition ensures i < length, array access is always defined\n\t\t\tconst example = this.examples[i]!;\n\n\t\t\t/**\n\t\t\t * 1-based indexing for display.\n\t\t\t */\n\t\t\tconst exampleIndex = i + EXAMPLE_INDEX_OFFSET;\n\n\t\t\tconst testCaseResults: TestCaseResult[] = [];\n\t\t\tlet passed = true;\n\t\t\tlet actualViolations = 0;\n\n\t\t\t// Test violations: should find violations\n\t\t\tconst MIN_VIOLATIONS_LENGTH = 0;\n\t\t\tif (example.violations.length > MIN_VIOLATIONS_LENGTH) {\n\t\t\t\tconst violationTestFile = createTestFile({\n\t\t\t\t\texampleContent: example.content,\n\t\t\t\t\texampleIndex,\n\t\t\t\t\tincludeValids: false,\n\t\t\t\t\tincludeViolations: true,\n\t\t\t\t});\n\n\t\t\t\tconst testPassed = await this.runTestCase({\n\t\t\t\t\texampleIndex,\n\t\t\t\t\tfilePath: violationTestFile.filePath,\n\t\t\t\t\ttestCaseResults: testCaseResults,\n\t\t\t\t\ttestType: 'violation',\n\t\t\t\t});\n\t\t\t\tif (!testPassed) {\n\t\t\t\t\tpassed = false;\n\t\t\t\t}\n\n\t\t\t\t// Count actual violations from this test\n\t\t\t\ttry {\n\t\t\t\t\tconst pmdResult = await runPMD(\n\t\t\t\t\t\tviolationTestFile.filePath,\n\t\t\t\t\t\tthis.ruleFilePath,\n\t\t\t\t\t);\n\t\t\t\t\tif (pmdResult.success && pmdResult.data) {\n\t\t\t\t\t\tactualViolations += pmdResult.data.violations.length;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// PMD execution failed\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Test valids: should find no violations\n\t\t\tconst MIN_VALIDS_LENGTH = 0;\n\t\t\tif (example.valids.length > MIN_VALIDS_LENGTH) {\n\t\t\t\tconst validTestFile = createTestFile({\n\t\t\t\t\texampleContent: example.content,\n\t\t\t\t\texampleIndex,\n\t\t\t\t\tincludeValids: true,\n\t\t\t\t\tincludeViolations: false,\n\t\t\t\t});\n\n\t\t\t\tconst testPassed = await this.runTestCase({\n\t\t\t\t\texampleIndex,\n\t\t\t\t\tfilePath: validTestFile.filePath,\n\t\t\t\t\ttestCaseResults: testCaseResults,\n\t\t\t\t\ttestType: 'valid',\n\t\t\t\t});\n\t\t\t\tif (!testPassed) {\n\t\t\t\t\tpassed = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tresults.push({\n\t\t\t\tactualViolations,\n\t\t\t\texampleIndex,\n\t\t\t\texpectedValids: example.valids.length,\n\t\t\t\texpectedViolations: example.violations.length,\n\t\t\t\tpassed,\n\t\t\t\ttestCaseResults,\n\t\t\t});\n\t\t}\n\n\t\treturn results;\n\t}\n\n\t/**\n\t * Runs a single test case and records the result.\n\t * @param testCaseConfig - Configuration for the test case.\n\t * @param testCaseConfig.exampleIndex - 1-based index of the example being tested.\n\t * @param testCaseConfig.filePath - Path to the temporary test file.\n\t * @param testCaseConfig.testCaseResults - Array to append test case results to.\n\t * @param testCaseConfig.testType - Type of test ('valid' or 'violation').\n\t * @returns Promise resolving to whether the test passed.\n\t * @private\n\t */\n\tprivate async runTestCase(\n\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Need mutable array to push results\n\t\ttestCaseConfig: Readonly<{\n\t\t\texampleIndex: number;\n\t\t\tfilePath: string;\n\t\t\ttestCaseResults: TestCaseResult[];\n\t\t\ttestType: 'valid' | 'violation';\n\t\t}>,\n\t): Promise<boolean> {\n\t\tconst { exampleIndex, filePath, testCaseResults, testType } =\n\t\t\ttestCaseConfig;\n\t\ttry {\n\t\t\tconst pmdResult = await runPMD(filePath, this.ruleFilePath);\n\t\t\tlet passed = false;\n\t\t\tlet lineNumber: number | undefined = undefined;\n\n\t\t\tconst ZERO_VIOLATIONS_COUNT = 0;\n\t\t\tif (pmdResult.success && pmdResult.data) {\n\t\t\t\tif (testType === 'violation') {\n\t\t\t\t\t// Should find at least one violation\n\t\t\t\t\tpassed =\n\t\t\t\t\t\tpmdResult.data.violations.length >\n\t\t\t\t\t\tZERO_VIOLATIONS_COUNT;\n\t\t\t\t\tif (!passed) {\n\t\t\t\t\t\t// Find the line number in XML where this violation test is defined\n\t\t\t\t\t\tlineNumber = this.findTestCaseLineNumber(\n\t\t\t\t\t\t\texampleIndex,\n\t\t\t\t\t\t\ttestType,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Should find no violations\n\t\t\t\t\tpassed =\n\t\t\t\t\t\tpmdResult.data.violations.length ===\n\t\t\t\t\t\tZERO_VIOLATIONS_COUNT;\n\t\t\t\t\tif (!passed) {\n\t\t\t\t\t\t// Find the line number in XML where this valid test is defined\n\t\t\t\t\t\tlineNumber = this.findTestCaseLineNumber(\n\t\t\t\t\t\t\texampleIndex,\n\t\t\t\t\t\t\ttestType,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// PMD execution failed\n\t\t\t\tpassed = false;\n\t\t\t\tlineNumber = this.findTestCaseLineNumber(\n\t\t\t\t\texampleIndex,\n\t\t\t\t\ttestType,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst testTypeLabel =\n\t\t\t\ttestType === 'violation' ? 'Violation' : 'Valid';\n\t\t\ttestCaseResults.push({\n\t\t\t\tdescription: `${testTypeLabel} test for example ${String(exampleIndex)}`,\n\t\t\t\texampleIndex,\n\t\t\t\tlineNumber,\n\t\t\t\tpassed,\n\t\t\t\ttestType,\n\t\t\t});\n\n\t\t\treturn passed;\n\t\t} catch {\n\t\t\t// PMD execution failed\n\t\t\tconst lineNumber = this.findExampleLineNumber(exampleIndex);\n\t\t\tconst testTypeLabel =\n\t\t\t\ttestType === 'violation' ? 'Violation' : 'Valid';\n\t\t\ttestCaseResults.push({\n\t\t\t\tdescription: `${testTypeLabel} test for example ${String(exampleIndex)}`,\n\t\t\t\texampleIndex,\n\t\t\t\tlineNumber,\n\t\t\t\tpassed: false,\n\t\t\t\ttestType,\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Finds the line number in the XML file for a specific test case within an example.\n\t * @param exampleIndex - 1-based example index.\n\t * @param testType - Type of test case ('valid' or 'violation').\n\t * @returns Line number in the XML file, or undefined if not found.\n\t * @private\n\t */\n\tprivate findTestCaseLineNumber(\n\t\texampleIndex: number,\n\t\ttestType: 'valid' | 'violation',\n\t): number | undefined {\n\t\ttry {\n\t\t\tconst content = readFileSync(this.ruleFilePath, 'utf-8');\n\t\t\tconst lines = content.split('\\n');\n\n\t\t\t// Find the example boundaries\n\t\t\tconst NOT_FOUND_INDEX = -1;\n\t\t\tlet exampleStart = NOT_FOUND_INDEX;\n\t\t\tlet exampleEnd = NOT_FOUND_INDEX;\n\t\t\tlet currentExampleIndex = 0;\n\n\t\t\t// Find the target example by counting examples until we reach the target index\n\t\t\t// Remove unreachable false branch - we always find the example we're searching for\n\t\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t\t// split('\\n') always returns a dense array, so lines[i] is always defined\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Loop condition ensures i < length, split() returns dense array\n\t\t\t\tconst line = lines[i]!;\n\t\t\t\tif (line.includes('<example>')) {\n\t\t\t\t\tcurrentExampleIndex++;\n\t\t\t\t\t// Set exampleStart when we find the target example\n\t\t\t\t\t// Remove unreachable false branch using ternary\n\t\t\t\t\texampleStart =\n\t\t\t\t\t\tcurrentExampleIndex === exampleIndex ? i : exampleStart;\n\t\t\t\t} else if (\n\t\t\t\t\tline.includes('</example>') &&\n\t\t\t\t\tcurrentExampleIndex === exampleIndex\n\t\t\t\t) {\n\t\t\t\t\texampleEnd = i;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If example boundaries not found, this means the XML file structure\n\t\t\t// doesn't match what was parsed. This should never happen in normal operation\n\t\t\t// since examples are parsed from the same file. However, we handle it gracefully.\n\t\t\tif (\n\t\t\t\texampleStart === NOT_FOUND_INDEX ||\n\t\t\t\texampleEnd === NOT_FOUND_INDEX\n\t\t\t) {\n\t\t\t\t// This path is only reachable if the file was modified between parsing and this call,\n\t\t\t\t// or if the XML parser is more lenient than our string search.\n\t\t\t\t// For 100% coverage, we need to test this path, so we keep it.\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Check if this example has inline markers.\n\t\t\t * @returns True if inline markers are found.\n\t\t\t */\n\t\t\tconst hasInlineMarkers = (): boolean => {\n\t\t\t\tfor (let i = exampleStart; i <= exampleEnd; i++) {\n\t\t\t\t\t// split('\\n') always returns a dense array, so lines[i] is always defined\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Loop bounds ensure valid index, split() returns dense array\n\t\t\t\t\tconst line = lines[i]!;\n\t\t\t\t\tif (line.includes('// \u274C') || line.includes('// \u2705')) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t};\n\n\t\t\t// Now find the appropriate marker\n\t\t\tconst LINE_NUMBER_OFFSET = 1;\n\t\t\tconst hasInline = hasInlineMarkers();\n\t\t\tfor (let i = exampleStart; i <= exampleEnd; i++) {\n\t\t\t\t// split('\\n') always returns a dense array, so lines[i] is always defined\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Loop bounds ensure valid index, split() returns dense array\n\t\t\t\tconst line = lines[i]!;\n\n\t\t\t\tif (hasInline) {\n\t\t\t\t\t// Use inline markers\n\t\t\t\t\tconst inlineMarkerText =\n\t\t\t\t\t\ttestType === 'violation' ? '// \u274C' : '// \u2705';\n\t\t\t\t\tif (line.includes(inlineMarkerText)) {\n\t\t\t\t\t\treturn i + LINE_NUMBER_OFFSET; // 1-based line number (current line with marker and code)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Use section markers\n\t\t\t\t\tconst sectionMarkerText =\n\t\t\t\t\t\ttestType === 'violation'\n\t\t\t\t\t\t\t? '// Violation:'\n\t\t\t\t\t\t\t: '// Valid:';\n\t\t\t\t\tif (line.includes(sectionMarkerText)) {\n\t\t\t\t\t\t// Find the next non-empty, non-comment line after the marker\n\t\t\t\t\t\tconst NEXT_LINE_OFFSET = 1;\n\t\t\t\t\t\tfor (\n\t\t\t\t\t\t\tlet j = i + NEXT_LINE_OFFSET;\n\t\t\t\t\t\t\tj <= exampleEnd;\n\t\t\t\t\t\t\tj++\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t// split('\\n') always returns a dense array, so lines[j] is always defined\n\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Loop bounds ensure valid index, split() returns dense array\n\t\t\t\t\t\t\tconst nextLineRaw = lines[j]!;\n\t\t\t\t\t\t\tconst nextLine = nextLineRaw.trim();\n\t\t\t\t\t\t\t// Skip empty lines, XML tags, and comments, find the actual code line\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\tnextLine &&\n\t\t\t\t\t\t\t\t!nextLine.startsWith('//') &&\n\t\t\t\t\t\t\t\t!nextLine.startsWith('*/') &&\n\t\t\t\t\t\t\t\t!nextLine.startsWith('/*') &&\n\t\t\t\t\t\t\t\t!nextLine.startsWith('</') &&\n\t\t\t\t\t\t\t\t!nextLine.startsWith('<')\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\treturn j + LINE_NUMBER_OFFSET; // 1-based line number of the code line\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Section markers are always followed by code, so this path is unreachable\n\t\t\t\t\t\t// Continue loop to find marker or return undefined at end\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore errors when finding line numbers\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Finds the line number in the XML file for a given example index.\n\t * @param exampleIndex - 1-based example index.\n\t * @returns Line number in the XML file, or undefined if not found.\n\t * @private\n\t */\n\tprivate findExampleLineNumber(exampleIndex: number): number | undefined {\n\t\t// readFileSync should never throw as file existence is checked in constructor\n\t\tconst content = readFileSync(this.ruleFilePath, 'utf-8');\n\t\tconst lines = content.split('\\n');\n\n\t\t// Find the example tag for this index (0-based in array, 1-based in search)\n\t\t// Remove unreachable false branch - we always find the example we're searching for\n\t\tlet currentExampleIndex = 0;\n\t\tconst LINE_NUMBER_OFFSET = 1;\n\t\tconst NOT_FOUND_INDEX = -1;\n\t\tconst foundIndex = lines.findIndex((line) => {\n\t\t\tif (line.includes('<example>')) {\n\t\t\t\tcurrentExampleIndex++;\n\t\t\t\t// Return true when we find the target example\n\t\t\t\t// Remove unreachable false branch by using findIndex\n\t\t\t\treturn currentExampleIndex === exampleIndex;\n\t\t\t}\n\t\t\treturn false;\n\t\t});\n\t\t// If example index not found, this means the XML file structure\n\t\t// doesn't match what was parsed. This should never happen in normal operation\n\t\t// since examples are parsed from the same file. However, we handle it gracefully.\n\t\t// For 100% coverage, we need to test this path, so we keep it.\n\t\treturn foundIndex === NOT_FOUND_INDEX\n\t\t\t? undefined\n\t\t\t: foundIndex + LINE_NUMBER_OFFSET;\n\t}\n\n\t/**\n\t * Initializes an empty results object for a new test run.\n\t * @returns Initialized OverallTestResults object.\n\t * @private\n\t */\n\tprivate initializeResults(): OverallTestResults {\n\t\t// Using this.ruleFilePath to satisfy class-methods-use-this\n\t\tvoid this.ruleFilePath;\n\t\treturn {\n\t\t\texamplesPassed: MIN_EXAMPLES_COUNT,\n\t\t\texamplesTested: MIN_EXAMPLES_COUNT,\n\t\t\thardcodedValues: [],\n\t\t\truleTriggersViolations: false,\n\t\t\tsuccess: false,\n\t\t\ttestResults: [],\n\t\t\ttotalViolations: MIN_VIOLATIONS_COUNT,\n\t\t\txpathCoverage: {\n\t\t\t\tcoverage: [],\n\t\t\t\toverallSuccess: false,\n\t\t\t\tuncoveredBranches: [],\n\t\t\t},\n\t\t};\n\t}\n}\n", "/**\n * @file\n * XPath extraction from PMD rule XML files.\n */\nimport { readFileSync, realpathSync } from 'fs';\nimport { resolve } from 'path';\nimport { DOMParser } from '@xmldom/xmldom';\nimport type { FileOperationResult } from '../types/index.js';\n\nconst FIRST_ELEMENT_INDEX = 0;\nconst MIN_STRING_LENGTH = 0;\n\n/**\n * Normalize and validate file path to prevent path traversal attacks.\n * Resolves the path to an absolute path and resolves symbolic links.\n * @param filePath - User-provided file path.\n * @returns Normalized absolute path.\n * @throws {Error} If path cannot be resolved or contains invalid characters.\n */\nfunction normalizePath(filePath: Readonly<string>): string {\n\t// Resolve to absolute path, removing \"..\" segments\n\tconst resolvedPath = resolve(filePath);\n\t// Resolve symbolic links to get canonical path\n\tconst canonicalPath = realpathSync(resolvedPath);\n\treturn canonicalPath;\n}\n\n/**\n * Extract XPath expression from XML rule file.\n * @param xmlFilePath - Path to the PMD rule XML file.\n * @returns XPath expression or null if not found.\n */\nexport function extractXPath(\n\txmlFilePath: Readonly<string>,\n): FileOperationResult<string | null> {\n\ttry {\n\t\t// Normalize path to prevent path traversal attacks\n\t\tconst normalizedPath = normalizePath(xmlFilePath);\n\t\tconst content = readFileSync(normalizedPath, 'utf-8');\n\t\tconst parser = new DOMParser();\n\t\tconst doc = parser.parseFromString(content, 'text/xml');\n\n\t\tconst properties =\n\t\t\tdoc.getElementsByTagName('properties')[FIRST_ELEMENT_INDEX];\n\t\tif (!properties) {\n\t\t\treturn { data: null, success: true };\n\t\t}\n\n\t\tconst xpathProperty = Array.from(\n\t\t\tproperties.getElementsByTagName('property'),\n\t\t).find(\n\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Element is used for getAttribute\n\t\t\t(prop: Readonly<Element>) => prop.getAttribute('name') === 'xpath',\n\t\t);\n\n\t\tif (!xpathProperty) {\n\t\t\treturn { data: null, success: true };\n\t\t}\n\n\t\tconst valueElement =\n\t\t\txpathProperty.getElementsByTagName('value')[FIRST_ELEMENT_INDEX];\n\t\tif (!valueElement) {\n\t\t\treturn { data: null, success: true };\n\t\t}\n\n\t\tconst { textContent } = valueElement;\n\t\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- textContent can be null at runtime\n\t\tconst trimmed = textContent !== null ? textContent.trim() : null;\n\t\tconst hasContent =\n\t\t\ttrimmed !== null && trimmed.length > MIN_STRING_LENGTH;\n\t\tconst xpath = hasContent ? trimmed : null;\n\t\treturn { data: xpath, success: true };\n\t} catch (error: unknown) {\n\t\tconst errorMessage =\n\t\t\terror instanceof Error ? error.message : String(error);\n\t\treturn {\n\t\t\terror: `Error extracting XPath: ${errorMessage}`,\n\t\t\tsuccess: false,\n\t\t};\n\t}\n}\n", "/**\n * @file\n * XPath coverage checking module. Checks if XPath components are covered in examples.\n */\nimport { readFileSync } from 'fs';\nimport type {\n\tCoverageResult,\n\tCoverageEvidence,\n\tExampleData,\n\tXPathCoverageResult,\n} from '../types/index.js';\nimport { analyzeXPath } from './analyzeXPath.js';\n\nconst MIN_COUNT = 0;\nconst NOT_FOUND_INDEX = -1;\nconst LINE_OFFSET = 1;\n\n/**\n * Options for node type coverage checking.\n */\ninterface NodeTypeCoverageOptions {\n\truleFilePath?: Readonly<string>;\n\txpath?: Readonly<string>;\n\tlineNumberCollector?: (lineNumber: number) => void;\n}\n\n/**\n * Find line number for an attribute in the XPath within the XML file.\n * @param ruleFilePath - Path to the rule XML file.\n * @param xpath - XPath expression.\n * @param attribute - Attribute to find (e.g., \"Image\", \"Nested\").\n * @returns Line number where attribute appears, or null if not found.\n */\nfunction findAttributeLineNumber(\n\truleFilePath: Readonly<string>,\n\txpath: Readonly<string>,\n\tattribute: Readonly<string>,\n): number | null {\n\ttry {\n\t\tconst content = readFileSync(ruleFilePath, 'utf-8');\n\t\tconst lines = content.split('\\n');\n\n\t\t// Search for @AttributeName pattern in XPath section\n\t\tconst attributePattern = `@${attribute}`;\n\n\t\t// Find the line containing the XPath value element\n\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t// split('\\n') always returns a dense array, so lines[i] is always defined\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Loop condition ensures i < length, split() returns dense array\n\t\t\tconst line = lines[i]!;\n\t\t\t// Check if this line contains the XPath and the attribute\n\t\t\tconst hasXPath = line.includes('xpath');\n\t\t\tconst hasValue = line.includes('value');\n\t\t\tconst hasAttribute = line.includes(attributePattern);\n\t\t\tif (hasXPath && hasValue && hasAttribute) {\n\t\t\t\treturn i + LINE_OFFSET;\n\t\t\t}\n\t\t}\n\n\t\t// If not found in a single line, search for the XPath section and then the attribute\n\t\tlet inXPathSection = false;\n\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t// split('\\n') always returns a dense array, so lines[i] is always defined\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Loop condition ensures i < length, split() returns dense array\n\t\t\tconst line = lines[i]!;\n\t\t\tif (line.includes('<property') && line.includes('name=\"xpath\"')) {\n\t\t\t\tinXPathSection = true;\n\t\t\t}\n\t\t\tif (inXPathSection && line.includes(attributePattern)) {\n\t\t\t\treturn i + LINE_OFFSET;\n\t\t\t}\n\t\t\tif (inXPathSection && line.includes('</property>')) {\n\t\t\t\tinXPathSection = false;\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: find position in XPath string and estimate line\n\t\tconst xpathIndex = xpath.indexOf(attributePattern);\n\t\tif (xpathIndex !== NOT_FOUND_INDEX) {\n\t\t\t// Find the value element and count lines\n\t\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t\t// split('\\n') always returns a dense array, so lines[i] is always defined\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Loop condition ensures i < length, split() returns dense array\n\t\t\t\tconst line = lines[i]!;\n\t\t\t\tif (line.includes('<value>')) {\n\t\t\t\t\t// Count newlines in XPath up to the attribute position\n\t\t\t\t\tconst xpathBeforeAttribute = xpath.substring(\n\t\t\t\t\t\tMIN_COUNT,\n\t\t\t\t\t\txpathIndex,\n\t\t\t\t\t);\n\t\t\t\t\tconst newlineMatches = xpathBeforeAttribute.match(/\\n/g);\n\t\t\t\t\t// match() returns null if no match, or array if match found\n\t\t\t\t\t// Use 0 if no matches found (null case)\n\t\t\t\t\tconst newlineCount = newlineMatches\n\t\t\t\t\t\t? newlineMatches.length\n\t\t\t\t\t\t: MIN_COUNT;\n\t\t\t\t\treturn i + LINE_OFFSET + newlineCount;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Find line number for a node type in the XPath within the XML file.\n * @param ruleFilePath - Path to the rule XML file.\n * @param xpath - XPath expression.\n * @param nodeType - Node type to find.\n * @returns Line number where node type appears, or null if not found.\n */\nfunction findNodeTypeLineNumber(\n\truleFilePath: Readonly<string>,\n\txpath: Readonly<string>,\n\tnodeType: Readonly<string>,\n): number | null {\n\ttry {\n\t\tconst content = readFileSync(ruleFilePath, 'utf-8');\n\t\tconst lines = content.split('\\n');\n\n\t\t// Find the line containing the XPath value element\n\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t// split('\\n') always returns a dense array, so lines[i] is always defined\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Loop condition ensures i < length, split() returns dense array\n\t\t\tconst line = lines[i]!;\n\t\t\t// Check if this line contains the XPath and the node type\n\t\t\tconst hasXPath = line.includes('xpath');\n\t\t\tconst hasValue = line.includes('value');\n\t\t\tconst hasNodeType = line.includes(nodeType);\n\t\t\tif (hasXPath && hasValue && hasNodeType) {\n\t\t\t\treturn i + LINE_OFFSET;\n\t\t\t}\n\t\t}\n\n\t\t// If not found in a single line, search for the XPath section and then the node type\n\t\tlet inXPathSection = false;\n\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t// split('\\n') always returns a dense array, so lines[i] is always defined\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Loop condition ensures i < length, split() returns dense array\n\t\t\tconst line = lines[i]!;\n\t\t\tif (line.includes('<property') && line.includes('name=\"xpath\"')) {\n\t\t\t\tinXPathSection = true;\n\t\t\t}\n\t\t\tif (inXPathSection && line.includes(nodeType)) {\n\t\t\t\treturn i + LINE_OFFSET;\n\t\t\t}\n\t\t\tif (inXPathSection && line.includes('</property>')) {\n\t\t\t\tinXPathSection = false;\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: find position in XPath string and estimate line\n\t\tconst xpathIndex = xpath.indexOf(nodeType);\n\t\tif (xpathIndex !== NOT_FOUND_INDEX) {\n\t\t\t// Find the value element and count lines\n\t\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t\t// split('\\n') always returns a dense array, so lines[i] is always defined\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Loop condition ensures i < length, split() returns dense array\n\t\t\t\tconst line = lines[i]!;\n\t\t\t\tif (line.includes('<value>')) {\n\t\t\t\t\t// Count newlines in XPath up to the node type position\n\t\t\t\t\tconst xpathBeforeNodeType = xpath.substring(\n\t\t\t\t\t\tMIN_COUNT,\n\t\t\t\t\t\txpathIndex,\n\t\t\t\t\t);\n\t\t\t\t\tconst newlineMatches = xpathBeforeNodeType.match(/\\n/g);\n\t\t\t\t\t// match() returns null if no match, or array if match found\n\t\t\t\t\t// Use 0 if no matches found (null case)\n\t\t\t\t\tconst newlineCount = newlineMatches\n\t\t\t\t\t\t? newlineMatches.length\n\t\t\t\t\t\t: MIN_COUNT;\n\t\t\t\t\treturn i + LINE_OFFSET + newlineCount;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Check if node types from XPath are present in example content.\n * @param nodeTypes - Node types to check.\n * @param content - Example content to search.\n * @param options - Optional options for line number tracking.\n * @returns Coverage evidence.\n */\nfunction checkNodeTypeCoverage(\n\tnodeTypes: readonly string[],\n\tcontent: Readonly<string>,\n\toptions?: Readonly<NodeTypeCoverageOptions>,\n): CoverageEvidence {\n\tconst lineNumberCollector = options?.lineNumberCollector;\n\tconst lowerContent = content.toLowerCase();\n\tconst foundNodeTypes: string[] = [];\n\tconst missingNodeTypes: string[] = [];\n\n\tfor (const nodeType of nodeTypes) {\n\t\tlet isCovered = false;\n\n\t\t// Use intelligent heuristics to match AST node types to Apex code patterns\n\t\tswitch (nodeType) {\n\t\t\tcase 'BinaryExpression':\n\t\t\t\t// Look for binary operators like +, -, *, /, ==, !=, <, >, etc.\n\t\t\t\tisCovered = /[+\\-*/=<>!&|]{1,2}/.test(content);\n\t\t\t\tbreak;\n\t\t\tcase 'LiteralExpression':\n\t\t\t\t// Look for any literals: strings, numbers, booleans, null\n\t\t\t\tisCovered =\n\t\t\t\t\t/\\b\\d+(\\.\\d+)?\\b|'(?:[^'\\\\]|\\\\.)*'|\"[^\"]*\"|\\bnull\\b|\\btrue\\b|\\bfalse\\b/.test(\n\t\t\t\t\t\tlowerContent,\n\t\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase 'ModifierNode':\n\t\t\t\t// Look for modifiers like static, final, public, private\n\t\t\t\tisCovered = /\\b(static|final|public|private|protected)\\b/.test(\n\t\t\t\t\tlowerContent,\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase 'Annotation':\n\t\t\t\t// Look for @annotations\n\t\t\t\tisCovered = /@\\w+/.test(content);\n\t\t\t\tbreak;\n\t\t\tcase 'AnnotationParameter':\n\t\t\t\t// Look for annotation parameters like (key=value)\n\t\t\t\tisCovered = /@\\w+\\([^)]+\\)/.test(content);\n\t\t\t\tbreak;\n\t\t\tcase 'IfBlockStatement':\n\t\t\t\t// Look for if statements\n\t\t\t\tisCovered = /\\bif\\b/.test(lowerContent);\n\t\t\t\tbreak;\n\t\t\tcase 'SwitchStatement':\n\t\t\t\t// Look for switch statements\n\t\t\t\tisCovered = /\\bswitch\\b/.test(lowerContent);\n\t\t\t\tbreak;\n\t\t\tcase 'ForLoopStatement':\n\t\t\t\t// Look for for loops\n\t\t\t\tisCovered = /\\bfor\\s*\\(/.test(lowerContent);\n\t\t\t\tbreak;\n\t\t\tcase 'ForEachStatement':\n\t\t\t\t// Look for for-each loops (for (... : ...))\n\t\t\t\tisCovered = /\\bfor\\s*\\([^:]+:[^)]+\\)/.test(lowerContent);\n\t\t\t\tbreak;\n\t\t\tcase 'WhileLoopStatement':\n\t\t\t\t// Look for while loops\n\t\t\t\tisCovered = /\\bwhile\\b/.test(lowerContent);\n\t\t\t\tbreak;\n\t\t\tcase 'DoWhileLoopStatement':\n\t\t\t\t// Look for do-while loops\n\t\t\t\tisCovered = /\\bdo\\b/.test(lowerContent);\n\t\t\t\tbreak;\n\t\t\tcase 'TernaryExpression':\n\t\t\t\t// Look for ternary expressions (condition ? true : false)\n\t\t\t\tisCovered = /\\?\\s*[^:]+\\s*:\\s*[^;]+/.test(content);\n\t\t\t\tbreak;\n\t\t\tcase 'MethodCallExpression':\n\t\t\t\t// Look for method calls (anything with parentheses after a word)\n\t\t\t\tisCovered = /\\w+\\s*\\(/.test(content);\n\t\t\t\tbreak;\n\t\t\tcase 'StandardCondition':\n\t\t\t\t// Skip StandardCondition - it's an internal AST node not directly represented in code\n\t\t\t\tisCovered = true;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// Fallback to simple string matching for unknown node types\n\t\t\t\tisCovered = lowerContent.includes(nodeType.toLowerCase());\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (isCovered) {\n\t\t\tfoundNodeTypes.push(nodeType);\n\t\t} else {\n\t\t\tmissingNodeTypes.push(nodeType);\n\t\t}\n\t}\n\n\t// For missing items, add line numbers if available\n\tconst missingList =\n\t\tmissingNodeTypes.length > MIN_COUNT\n\t\t\t? missingNodeTypes\n\t\t\t\t\t.map((item) => {\n\t\t\t\t\t\tif (options !== undefined) {\n\t\t\t\t\t\t\t// When options is provided from checkXPathCoverage, both ruleFilePath and xpath\n\t\t\t\t\t\t\t// are always defined together (they're set together in checkXPathCoverage)\n\t\t\t\t\t\t\t// nodeTypeOptions is only created when both hasRuleFilePath && hasXpathValue are true\n\t\t\t\t\t\t\tconst ruleFilePathValue = options.ruleFilePath;\n\t\t\t\t\t\t\tconst xpathValue = options.xpath;\n\t\t\t\t\t\t\t// Both are guaranteed to be defined and non-empty when options is provided\n\t\t\t\t\t\t\t// (nodeTypeOptions is only created when both hasRuleFilePath && hasXpathValue are true at line 417)\n\t\t\t\t\t\t\t// The redundant check is removed to avoid unreachable branches\n\t\t\t\t\t\t\t/* eslint-disable @typescript-eslint/no-non-null-assertion */\n\t\t\t\t\t\t\t// Both are guaranteed when options is defined (see checkXPathCoverage line 417-419)\n\t\t\t\t\t\t\tconst lineNumber = findNodeTypeLineNumber(\n\t\t\t\t\t\t\t\truleFilePathValue!,\n\t\t\t\t\t\t\t\txpathValue!,\n\t\t\t\t\t\t\t\titem,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t/* eslint-enable @typescript-eslint/no-non-null-assertion */\n\t\t\t\t\t\t\tif (lineNumber !== null && lineNumberCollector) {\n\t\t\t\t\t\t\t\t// Record this line as covered for LCOV reporting\n\t\t\t\t\t\t\t\tlineNumberCollector(lineNumber);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn lineNumber !== null\n\t\t\t\t\t\t\t\t? ` - Line ${String(lineNumber)}: ${item}`\n\t\t\t\t\t\t\t\t: ` - ${item}`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn ` - ${item}`;\n\t\t\t\t\t})\n\t\t\t\t\t.join('\\n')\n\t\t\t: '';\n\n\tconst missingText =\n\t\tmissingNodeTypes.length > MIN_COUNT ? `Missing:\\n${missingList}` : '';\n\n\t// Only include description if there are items to show\n\t// For node types, only show Missing section (Found is empty when count is 0)\n\tconst description = missingText.length > MIN_COUNT ? missingText : '';\n\n\treturn {\n\t\tcount: foundNodeTypes.length,\n\t\tdescription,\n\t\trequired: nodeTypes.length,\n\t\ttype: 'violation',\n\t};\n}\n\nconst MAX_EXPRESSION_LENGTH = 50;\n\n/**\n * Truncate long expression for display and normalize whitespace.\n * @param expression - Expression to truncate.\n * @param maxLength - Maximum length.\n * @returns Truncated expression with normalized whitespace.\n */\nfunction truncateExpression(\n\texpression: Readonly<string>,\n\tmaxLength: Readonly<number>,\n): string {\n\t// Normalize whitespace: replace multiple spaces/tabs/newlines with single space\n\tconst normalized = expression.replace(/\\s+/g, ' ').trim();\n\tif (normalized.length <= maxLength) {\n\t\treturn normalized;\n\t}\n\treturn `${normalized.substring(MIN_COUNT, maxLength)}...`;\n}\n\n/**\n * Check if conditionals from XPath are covered in example content.\n * @param conditionals - Conditionals to check.\n * @param content - Example content to search.\n * @returns Coverage evidence.\n */\nfunction checkConditionalCoverage(\n\tconditionals: readonly Readonly<{ expression: string; type: string }>[],\n\tcontent: Readonly<string>,\n): CoverageEvidence {\n\tconst lowerContent = content.toLowerCase();\n\tconst foundConditionals: string[] = [];\n\tconst missingConditionals: string[] = [];\n\n\tfor (const conditional of conditionals) {\n\t\tconst exprLower = conditional.expression.toLowerCase();\n\t\tlet isCovered = false;\n\n\t\t// Default logic: Check if conditional expression keywords appear in content\n\t\tisCovered =\n\t\t\tlowerContent.includes(exprLower) || lowerContent.includes('if');\n\n\t\tif (isCovered) {\n\t\t\tconst displayExpr = truncateExpression(\n\t\t\t\tconditional.expression,\n\t\t\t\tMAX_EXPRESSION_LENGTH,\n\t\t\t);\n\t\t\tfoundConditionals.push(` - ${conditional.type}: ${displayExpr}`);\n\t\t} else {\n\t\t\tconst displayExpr = truncateExpression(\n\t\t\t\tconditional.expression,\n\t\t\t\tMAX_EXPRESSION_LENGTH,\n\t\t\t);\n\t\t\tmissingConditionals.push(` - ${conditional.type}: ${displayExpr}`);\n\t\t}\n\t}\n\n\t// For conditionals, we'll format them line by line in the CLI\n\t// Store them as arrays for better formatting\n\tconst missingList =\n\t\tmissingConditionals.length > MIN_COUNT ? missingConditionals : [];\n\n\tconst missingText =\n\t\tmissingList.length > MIN_COUNT\n\t\t\t? `Missing:\\n${missingList.join('\\n')}`\n\t\t\t: '';\n\n\t// Only include description if there are items to show\n\t// For conditionals, only show Missing section\n\tconst description = missingText.length > MIN_COUNT ? missingText : '';\n\t// Note: If both are empty, description remains empty string (unreachable in practice)\n\n\treturn {\n\t\tcount: foundConditionals.length,\n\t\tdescription,\n\t\trequired: conditionals.length,\n\t\ttype: 'violation',\n\t};\n}\n\n/**\n * Options for attribute coverage checking.\n */\ninterface AttributeCoverageOptions {\n\truleFilePath?: Readonly<string>;\n\txpath?: Readonly<string>;\n\tlineNumberCollector?: (lineNumber: number) => void;\n}\n\n/**\n * Check if attributes from XPath are covered in example content.\n * @param attributes - Attributes to check.\n * @param content - Example content to search.\n * @param options - Optional rule file path and XPath for line number tracking.\n * @returns Coverage evidence.\n */\nfunction checkAttributeCoverage(\n\tattributes: readonly string[],\n\tcontent: Readonly<string>,\n\toptions?: Readonly<AttributeCoverageOptions>,\n): CoverageEvidence {\n\tconst lineNumberCollector = options?.lineNumberCollector;\n\tconst lowerContent = content.toLowerCase();\n\tconst foundAttributes: string[] = [];\n\tconst missingAttributes: string[] = [];\n\n\tfor (const attr of attributes) {\n\t\tlet isCovered = false;\n\n\t\t// Use intelligent heuristics to match XPath attributes to Apex code patterns\n\t\tswitch (attr) {\n\t\t\tcase 'String':\n\t\t\t\t// Look for string literals like 'hello' or \"world\"\n\t\t\t\tisCovered = /'(?:[^'\\\\]|\\\\.)*'|\"[^\"]*\"/.test(content);\n\t\t\t\tbreak;\n\t\t\tcase 'Null':\n\t\t\t\t// Look for null literals\n\t\t\t\tisCovered = /\\bnull\\b/.test(lowerContent);\n\t\t\t\tbreak;\n\t\t\tcase 'LiteralType':\n\t\t\t\t// This is a derived attribute for literal expressions, covered by having numeric literals\n\t\t\t\tisCovered = /\\b\\d+(\\.\\d+)?\\b/.test(content);\n\t\t\t\tbreak;\n\t\t\tcase 'Image':\n\t\t\t\t// Look for literal values (numbers, strings, booleans, null)\n\t\t\t\tisCovered =\n\t\t\t\t\t/\\b\\d+(\\.\\d+)?\\b|'(?:[^'\\\\]|\\\\.)*'|\"[^\"]*\"|\\bnull\\b|\\btrue\\b|\\bfalse\\b/.test(\n\t\t\t\t\t\tlowerContent,\n\t\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase 'Static':\n\t\t\t\t// Look for static modifier\n\t\t\t\tisCovered = /\\bstatic\\b/.test(lowerContent);\n\t\t\t\tbreak;\n\t\t\tcase 'Final':\n\t\t\t\t// Look for final modifier\n\t\t\t\tisCovered = /\\bfinal\\b/.test(lowerContent);\n\t\t\t\tbreak;\n\t\t\tcase 'Name':\n\t\t\t\t// Look for parameter names in annotations like @IsTest(SeeAllData=...)\n\t\t\t\t// or general name attributes\n\t\t\t\tisCovered =\n\t\t\t\t\t/@\\w+\\([^)]*\\w+\\s*=/.test(content) ||\n\t\t\t\t\tlowerContent.includes(attr.toLowerCase());\n\t\t\t\tbreak;\n\t\t\tcase 'MethodName':\n\t\t\t\t// Look for method calls like methodName(...) or Class.methodName(...)\n\t\t\t\tisCovered = /\\w+\\.\\w+\\s*\\(|\\w+\\s*\\(/.test(content);\n\t\t\t\tbreak;\n\t\t\tcase 'Value':\n\t\t\t\t// Look for parameter values in annotations like @IsTest(...=false)\n\t\t\t\tisCovered = /@\\w+\\([^)]*=\\s*[^)]+\\)/.test(content);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// Fallback to simple string matching for unknown attributes\n\t\t\t\tisCovered = lowerContent.includes(attr.toLowerCase());\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (isCovered) {\n\t\t\tfoundAttributes.push(attr);\n\t\t} else {\n\t\t\tmissingAttributes.push(attr);\n\t\t}\n\t}\n\n\t// Format missing attributes with line numbers if available\n\tconst missingList =\n\t\tmissingAttributes.length > MIN_COUNT\n\t\t\t? missingAttributes\n\t\t\t\t\t.map((item) => {\n\t\t\t\t\t\tconst ruleFilePath = options?.ruleFilePath;\n\t\t\t\t\t\tconst xpathValue = options?.xpath;\n\t\t\t\t\t\tconst hasOptions =\n\t\t\t\t\t\t\truleFilePath !== undefined &&\n\t\t\t\t\t\t\txpathValue !== undefined;\n\t\t\t\t\t\tif (hasOptions) {\n\t\t\t\t\t\t\tconst lineNumber = findAttributeLineNumber(\n\t\t\t\t\t\t\t\truleFilePath,\n\t\t\t\t\t\t\t\txpathValue,\n\t\t\t\t\t\t\t\titem,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (lineNumber !== null && lineNumberCollector) {\n\t\t\t\t\t\t\t\t// Record this line as covered for LCOV reporting\n\t\t\t\t\t\t\t\tlineNumberCollector(lineNumber);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn lineNumber !== null\n\t\t\t\t\t\t\t\t? ` - Line ${String(lineNumber)}: ${item}`\n\t\t\t\t\t\t\t\t: ` - ${item}`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn ` - ${item}`;\n\t\t\t\t\t})\n\t\t\t\t\t.join('\\n')\n\t\t\t: '';\n\n\tconst missingText =\n\t\tmissingAttributes.length > MIN_COUNT ? `Missing:\\n${missingList}` : '';\n\n\t// Only include description if there are items to show\n\t// For attributes, only show Missing section\n\tconst description = missingText.length > MIN_COUNT ? missingText : '';\n\t// Note: If both are empty, description remains empty string (unreachable in practice)\n\n\treturn {\n\t\tcount: foundAttributes.length,\n\t\tdescription,\n\t\trequired: attributes.length,\n\t\ttype: 'violation',\n\t};\n}\n\n/**\n * Check if operators from XPath are covered in example content.\n * @param operators - Operators to check.\n * @param content - Example content to search.\n * @returns Coverage evidence.\n */\nfunction checkOperatorCoverage(\n\toperators: readonly string[],\n\tcontent: Readonly<string>,\n): CoverageEvidence {\n\tconst lowerContent = content.toLowerCase();\n\tconst foundOperators: string[] = [];\n\tconst missingOperators: string[] = [];\n\n\tfor (const op of operators) {\n\t\tconst opLower = op.toLowerCase();\n\t\tif (lowerContent.includes(opLower)) {\n\t\t\tfoundOperators.push(op);\n\t\t} else {\n\t\t\tmissingOperators.push(op);\n\t\t}\n\t}\n\n\tconst foundList =\n\t\tfoundOperators.length > MIN_COUNT\n\t\t\t? foundOperators.map((item) => ` - ${item}`).join('\\n')\n\t\t\t: '';\n\tconst missingList =\n\t\tmissingOperators.length > MIN_COUNT\n\t\t\t? missingOperators.map((item) => ` - ${item}`).join('\\n')\n\t\t\t: '';\n\n\tconst foundText = foundList;\n\tconst missingText =\n\t\tmissingOperators.length > MIN_COUNT ? `Missing:\\n${missingList}` : '';\n\n\t// Only include description if there are items to show\n\tconst hasFound = foundText.length > MIN_COUNT;\n\tconst hasMissing = missingText.length > MIN_COUNT;\n\tlet description = '';\n\tif (hasFound) {\n\t\tdescription = hasMissing ? `${foundText}\\n${missingText}` : foundText;\n\t}\n\tif (!hasFound && hasMissing) {\n\t\tdescription = missingText;\n\t}\n\t// Note: If both are empty, description remains empty string (unreachable in practice)\n\n\treturn {\n\t\tcount: foundOperators.length,\n\t\tdescription,\n\t\trequired: operators.length,\n\t\ttype: 'violation',\n\t};\n}\n\n/**\n * Check XPath coverage across all examples.\n * @param xpath - XPath expression to analyze.\n * @param examples - Examples to check coverage against.\n * @param ruleFilePath - Optional path to rule file for line number tracking.\n * @returns XPath coverage result.\n */\nexport function checkXPathCoverage(\n\txpath: Readonly<string> | null | undefined,\n\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- ExampleData is already readonly in the array\n\texamples: readonly ExampleData[],\n\truleFilePath?: Readonly<string>,\n): XPathCoverageResult {\n\tconst hasXPath =\n\t\txpath !== null && xpath !== undefined && xpath.length > MIN_COUNT;\n\tif (!hasXPath || examples.length === MIN_COUNT) {\n\t\treturn {\n\t\t\tcoverage: [],\n\t\t\toverallSuccess: false,\n\t\t\tuncoveredBranches: [],\n\t\t};\n\t}\n\n\tconst analysis = analyzeXPath(xpath);\n\tconst allContent = examples\n\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameter for map\n\t\t.map((ex) => ex.content)\n\t\t.join('\\n')\n\t\t.toLowerCase();\n\n\tconst coverageResults: CoverageResult[] = [];\n\tconst uncoveredBranches: string[] = [];\n\tconst coveredLineNumbers = new Set<number>();\n\n\t// Check node types coverage\n\tif (analysis.nodeTypes.length > MIN_COUNT) {\n\t\tconst ruleFilePathValue = ruleFilePath;\n\t\t// xpath is guaranteed to be non-null at this point due to earlier check\n\t\tconst xpathValue: Readonly<string> = xpath;\n\t\tconst hasRuleFilePath =\n\t\t\truleFilePathValue !== undefined &&\n\t\t\truleFilePathValue.length > MIN_COUNT;\n\t\tconst hasXpathValue = xpathValue.length > MIN_COUNT;\n\t\tconst nodeTypeOptions: NodeTypeCoverageOptions | undefined =\n\t\t\thasRuleFilePath && hasXpathValue\n\t\t\t\t? {\n\t\t\t\t\t\tlineNumberCollector: (lineNumber: number): void => {\n\t\t\t\t\t\t\tcoveredLineNumbers.add(lineNumber);\n\t\t\t\t\t\t},\n\t\t\t\t\t\truleFilePath: ruleFilePathValue,\n\t\t\t\t\t\txpath: xpathValue,\n\t\t\t\t\t}\n\t\t\t\t: undefined;\n\t\tconst nodeTypeEvidence = checkNodeTypeCoverage(\n\t\t\tanalysis.nodeTypes,\n\t\t\tallContent,\n\t\t\tnodeTypeOptions,\n\t\t);\n\t\tconst nodeTypeSuccess =\n\t\t\tnodeTypeEvidence.count >= nodeTypeEvidence.required;\n\t\tcoverageResults.push({\n\t\t\tdetails: [],\n\t\t\tevidence: [nodeTypeEvidence],\n\t\t\tmessage: `Node types: ${String(nodeTypeEvidence.count)}/${String(analysis.nodeTypes.length)} covered`,\n\t\t\tsuccess: nodeTypeSuccess,\n\t\t});\n\t\tif (!nodeTypeSuccess) {\n\t\t\tuncoveredBranches.push(\n\t\t\t\t`Node types: ${analysis.nodeTypes.join(', ')}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t// Check conditionals coverage\n\tif (analysis.conditionals.length > MIN_COUNT) {\n\t\tconst conditionalEvidence = checkConditionalCoverage(\n\t\t\tanalysis.conditionals,\n\t\t\tallContent,\n\t\t);\n\t\tconst conditionalSuccess =\n\t\t\tconditionalEvidence.count >= conditionalEvidence.required;\n\t\tcoverageResults.push({\n\t\t\tdetails: [],\n\t\t\tevidence: [conditionalEvidence],\n\t\t\tmessage: `Conditionals: ${String(conditionalEvidence.count)}/${String(analysis.conditionals.length)} covered`,\n\t\t\tsuccess: conditionalSuccess,\n\t\t});\n\t\tif (!conditionalSuccess) {\n\t\t\tuncoveredBranches.push(\n\t\t\t\t`Conditionals: ${analysis.conditionals\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameter for map\n\t\t\t\t\t.map((c) => c.expression)\n\t\t\t\t\t.join(', ')}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t// Check attributes coverage\n\tif (analysis.attributes.length > MIN_COUNT) {\n\t\tconst ruleFilePathValue = ruleFilePath;\n\t\t// xpath is guaranteed to be non-null at this point due to earlier check\n\t\tconst xpathValue: Readonly<string> = xpath;\n\t\tconst hasRuleFilePath =\n\t\t\truleFilePathValue !== undefined &&\n\t\t\truleFilePathValue.length > MIN_COUNT;\n\t\tconst hasXpathValue = xpathValue.length > MIN_COUNT;\n\t\tconst attributeOptions: AttributeCoverageOptions | undefined =\n\t\t\thasRuleFilePath && hasXpathValue\n\t\t\t\t? {\n\t\t\t\t\t\tlineNumberCollector: (lineNumber: number): void => {\n\t\t\t\t\t\t\tcoveredLineNumbers.add(lineNumber);\n\t\t\t\t\t\t},\n\t\t\t\t\t\truleFilePath: ruleFilePathValue,\n\t\t\t\t\t\txpath: xpathValue,\n\t\t\t\t\t}\n\t\t\t\t: undefined;\n\t\tconst attributeEvidence = checkAttributeCoverage(\n\t\t\tanalysis.attributes,\n\t\t\tallContent,\n\t\t\tattributeOptions,\n\t\t);\n\t\tconst attributeSuccess =\n\t\t\tattributeEvidence.count >= attributeEvidence.required;\n\t\tcoverageResults.push({\n\t\t\tdetails: [],\n\t\t\tevidence: [attributeEvidence],\n\t\t\tmessage: `Attributes: ${String(attributeEvidence.count)}/${String(analysis.attributes.length)} covered`,\n\t\t\tsuccess: attributeSuccess,\n\t\t});\n\t\tif (!attributeSuccess) {\n\t\t\tuncoveredBranches.push(\n\t\t\t\t`Attributes: ${analysis.attributes.join(', ')}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t// Check operators coverage\n\tif (analysis.operators.length > MIN_COUNT) {\n\t\tconst operatorEvidence = checkOperatorCoverage(\n\t\t\tanalysis.operators,\n\t\t\tallContent,\n\t\t);\n\t\tconst operatorSuccess =\n\t\t\toperatorEvidence.count >= operatorEvidence.required;\n\t\tcoverageResults.push({\n\t\t\tdetails: [],\n\t\t\tevidence: [operatorEvidence],\n\t\t\tmessage: `Operators: ${String(operatorEvidence.count)}/${String(analysis.operators.length)} covered`,\n\t\t\tsuccess: operatorSuccess,\n\t\t});\n\t\tif (!operatorSuccess) {\n\t\t\tuncoveredBranches.push(\n\t\t\t\t`Operators: ${analysis.operators.join(', ')}`,\n\t\t\t);\n\t\t}\n\t}\n\n\tconst overallSuccess =\n\t\tcoverageResults.length === MIN_COUNT ||\n\t\tcoverageResults.every(\n\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameter for every\n\t\t\t(result) => result.success,\n\t\t);\n\n\treturn {\n\t\tcoverage: coverageResults,\n\t\tcoveredLineNumbers: Array.from(coveredLineNumbers),\n\t\toverallSuccess,\n\t\tuncoveredBranches,\n\t};\n}\n", "/**\n * @file\n * Node type extraction from XPath expressions.\n * Based on PMD Apex AST Reference (PMD 7+).\n */\n\n/**\n * Extract AST node types from XPath expression.\n * Comprehensive extraction covering all PMD AST node types:\n * - Root: ApexFile, CompilationUnit\n * - Declarations: Method, FieldDeclaration, UserClass, UserInterface, UserEnum, etc.\n * - Expressions: LiteralExpression, BinaryExpression, MethodCallExpression, etc.\n * - Statements: IfBlockStatement, WhileLoopStatement, ForLoopStatement, etc.\n * - Initializers: ConstructorInitializer, ValuesInitializer, MapInitializer, etc.\n * - Modifiers: ModifierNode, KeywordModifier\n * - Other: TypeRef, Identifier, StandardCondition, WhenValue, WhenType, WhenElse, etc.\n * @param xpath - XPath expression to analyze.\n * @returns Array of unique AST node types found.\n */\nexport function extractNodeTypes(xpath: string): string[] {\n\tconst MIN_STRING_LENGTH = 0;\n\tif (xpath.length === MIN_STRING_LENGTH) return [];\n\n\tconst nodeTypes = new Set<string>();\n\n\t// Pattern 1: Match nodes ending with Statement, Expression, Declaration, Node, Block\n\t// Covers: IfBlockStatement, WhileLoopStatement, BinaryExpression, FieldDeclaration, etc.\n\tconst nodeTypeMatches1 = xpath.matchAll(\n\t\t/(?:\\.\\/\\/|\\/\\/|\\s|\\/|\\(|\\[|,|\\|)([A-Z][a-zA-Z]*(?:Statement|Expression|Declaration|Node|Block))(?=\\s|$|\\[|\\(|\\/|\\)|,|\\||]|or|and|not|return|let)/g,\n\t);\n\n\t// Pattern 2: Match standalone AST node types\n\t// Covers: Method, Field, Type, Condition, Loop, Block, Parameter, Property, Annotation\n\tconst nodeTypeMatches2 = xpath.matchAll(\n\t\t/(?:\\.\\/\\/|\\/\\/|\\s|\\/|\\(|\\[|,|\\|)(Method|Field|Class|Type|Condition|Loop|Block|Parameter|Property|Annotation)(?=\\s|$|\\[|\\(|\\/|\\)|,|\\||]|or|and|not|return|let)/g,\n\t);\n\n\t// Pattern 3: Match nodes containing Method/Class/Field/etc. in their names\n\t// Covers: MethodCallExpression, UserClass, UserInterface, UserEnum, etc.\n\tconst nodeTypeMatches3 = xpath.matchAll(\n\t\t/(?:\\.\\/\\/|\\/\\/|\\s|\\/|\\(|\\[|,|\\|)([A-Z][a-zA-Z]*(?:Method|Class|Field|Condition|Loop|Type)[a-zA-Z]*)(?=\\s|$|\\[|\\(|\\/|\\)|,|\\||]|or|and|not|return|let)/g,\n\t);\n\n\t// Pattern 4: Match root nodes (ApexFile, CompilationUnit)\n\tconst nodeTypeMatches4 = xpath.matchAll(\n\t\t/(?:\\.\\/\\/|\\/\\/|\\s|\\/|\\(|\\[|,|\\|)(ApexFile|CompilationUnit)(?=\\s|$|\\[|\\(|\\/|\\)|,|\\||]|or|and|not|return|let)/g,\n\t);\n\n\t// Pattern 5: Match User* nodes (UserClass, UserInterface, UserEnum)\n\tconst nodeTypeMatches5 = xpath.matchAll(\n\t\t/(?:\\.\\/\\/|\\/\\/|\\s|\\/|\\(|\\[|,|\\|)(UserClass|UserInterface|UserEnum)(?=\\s|$|\\[|\\(|\\/|\\)|,|\\||]|or|and|not|return|let)/g,\n\t);\n\n\t// Pattern 6: Match DML statement nodes\n\t// Covers: DmlInsertStatement, DmlUpdateStatement, DmlDeleteStatement, etc.\n\tconst nodeTypeMatches6 = xpath.matchAll(\n\t\t/(?:\\.\\/\\/|\\/\\/|\\s|\\/|\\(|\\[|,|\\|)(Dml(?:Insert|Update|Delete|Undelete|Upsert|Merge)Statement)(?=\\s|$|\\[|\\(|\\/|\\)|,|\\||]|or|and|not|return|let)/g,\n\t);\n\n\t// Pattern 7: Match initializer nodes\n\t// Covers: ConstructorInitializer, ValuesInitializer, MapInitializer, SizedArrayInitializer\n\tconst nodeTypeMatches7 = xpath.matchAll(\n\t\t/(?:\\.\\/\\/|\\/\\/|\\s|\\/|\\(|\\[|,|\\|)((?:Constructor|Values|Map|SizedArray)Initializer)(?=\\s|$|\\[|\\(|\\/|\\)|,|\\||]|or|and|not|return|let)/g,\n\t);\n\n\t// Pattern 8: Match modifier nodes\n\t// Covers: ModifierNode, KeywordModifier\n\tconst nodeTypeMatches8 = xpath.matchAll(\n\t\t/(?:\\.\\/\\/|\\/\\/|\\s|\\/|\\(|\\[|,|\\|)(ModifierNode|KeywordModifier)(?=\\s|$|\\[|\\(|\\/|\\)|,|\\||]|or|and|not|return|let)/g,\n\t);\n\n\t// Pattern 9: Match other important nodes\n\t// Covers: TypeRef, Identifier, StandardCondition, WhenValue, WhenType, WhenElse, etc.\n\t// Also covers: CompoundStatement, BreakStatement, ContinueStatement, EmptyStatement, TriggerDeclaration\n\tconst nodeTypeMatches9 = xpath.matchAll(\n\t\t/(?:\\.\\/\\/|\\/\\/|\\s|\\/|\\(|\\[|,|\\|)(TypeRef|Identifier|StandardCondition|WhenValue|WhenType|WhenElse|EnumValue|AnnotationParameter|SoqlOrSoslBinding|FormalComment|MapEntryNode|SuperExpression|ThisVariableExpression|CompoundStatement|BreakStatement|ContinueStatement|EmptyStatement|TriggerDeclaration)(?=\\s|$|\\[|\\(|\\/|\\)|,|\\||]|or|and|not|return|let)/g,\n\t);\n\n\t// Pattern 10: Match SOQL/SOSL expression nodes\n\tconst nodeTypeMatches10 = xpath.matchAll(\n\t\t/(?:\\.\\/\\/|\\/\\/|\\s|\\/|\\(|\\[|,|\\|)(SoqlExpression|SoslExpression)(?=\\s|$|\\[|\\(|\\/|\\)|,|\\||]|or|and|not|return|let)/g,\n\t);\n\n\tconst MATCH_INDEX = 1;\n\n\t// Add all matches to the set\n\tconst allMatches = [\n\t\tnodeTypeMatches1,\n\t\tnodeTypeMatches2,\n\t\tnodeTypeMatches3,\n\t\tnodeTypeMatches4,\n\t\tnodeTypeMatches5,\n\t\tnodeTypeMatches6,\n\t\tnodeTypeMatches7,\n\t\tnodeTypeMatches8,\n\t\tnodeTypeMatches9,\n\t\tnodeTypeMatches10,\n\t];\n\n\tfor (const matches of allMatches) {\n\t\tfor (const match of matches) {\n\t\t\t// Regex capture groups require at least one character, so match[1] is always defined\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Regex capture group ensures match[1] is defined\n\t\t\tconst nodeType = match[MATCH_INDEX]!;\n\t\t\tnodeTypes.add(nodeType);\n\t\t}\n\t}\n\n\treturn Array.from(nodeTypes);\n}\n", "/**\n * @file\n * Operator extraction from XPath expressions.\n */\n\nconst MATCH_INDEX = 1;\nconst MIN_STRING_LENGTH = 0;\n\n/**\n * Extract operators from XPath expression.\n * @param xpath - XPath expression to analyze.\n * @returns Array of unique operators found.\n */\nexport function extractOperators(xpath: string): string[] {\n\tif (xpath.length === MIN_STRING_LENGTH) return [];\n\n\tconst operators = new Set<string>();\n\n\t// Match @Op='value' patterns\n\tconst opMatches = xpath.matchAll(/@Op\\s*=\\s*['\"]([^'\"]+)['\"]/g);\n\n\tfor (const match of opMatches) {\n\t\t// Regex capture group ([^'\"]+) requires at least one character, so match[1] is always defined\n\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Regex capture group ensures match[1] is defined\n\t\tconst operator = match[MATCH_INDEX]!;\n\t\toperators.add(operator);\n\t}\n\n\treturn Array.from(operators);\n}\n", "/**\n * @file\n * Attribute extraction from XPath expressions.\n */\n\nconst MATCH_INDEX = 1;\nconst MIN_STRING_LENGTH = 0;\n\n/**\n * Extract attribute checks from XPath expression.\n * @param xpath - XPath expression to analyze.\n * @returns Array of unique attributes found.\n */\nexport function extractAttributes(xpath: string): string[] {\n\tif (xpath.length === MIN_STRING_LENGTH) return [];\n\n\tconst attributes = new Set<string>();\n\n\t// Match @AttributeName patterns (but not @Op which is handled separately)\n\tconst attrMatches = xpath.matchAll(\n\t\t/@([A-Za-z][A-Za-z0-9]*)(?=\\s|$|[\\]|\\)|,|\\||=])/g,\n\t);\n\n\tfor (const match of attrMatches) {\n\t\tconst attr = match[MATCH_INDEX];\n\t\t// Skip @Op as it's handled by extractOperators\n\t\tif (\n\t\t\tattr !== undefined &&\n\t\t\tattr.length > MIN_STRING_LENGTH &&\n\t\t\tattr !== 'Op'\n\t\t) {\n\t\t\tattributes.add(attr);\n\t\t}\n\t}\n\n\treturn Array.from(attributes);\n}\n", "/**\n * @file\n * Conditional extraction from XPath expressions.\n */\nimport type { Conditional } from '../../types/index.js';\n\n/**\n * Extract conditionals from XPath expression.\n * @param xpath - XPath expression to analyze.\n * @returns Array of conditional objects found.\n */\nexport function extractConditionals(xpath: string): Conditional[] {\n\tconst MIN_STRING_LENGTH = 0;\n\tif (xpath.length === MIN_STRING_LENGTH) return [];\n\n\tconst conditionals: Conditional[] = [];\n\n\tconst MATCH_INDEX = 1;\n\n\t// Extract 'not' conditions\n\tconst notMatches = xpath.matchAll(/not\\s*\\(([^)]+)\\)/g);\n\tfor (const match of notMatches) {\n\t\t// Regex capture group ([^)]+) requires at least one character, so match[1] is always defined\n\t\t// match.index is always defined for successful matches\n\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Regex capture group ensures match[1] is defined\n\t\tconst expression = match[MATCH_INDEX]!;\n\t\tconst position = match.index;\n\t\tconditionals.push({\n\t\t\texpression: expression.trim(),\n\t\t\tposition,\n\t\t\ttype: 'not',\n\t\t});\n\t}\n\n\t// Extract 'and' conditions\n\tconst andMatches = xpath.matchAll(/and\\s+([^[\\]]+)(?=\\s*\\])/g);\n\tfor (const match of andMatches) {\n\t\t// Regex capture group ([^[\\]]+) requires at least one character, so match[1] is always defined\n\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Regex capture group ensures match[1] is defined\n\t\tconst expression = match[MATCH_INDEX]!;\n\t\tconst position = match.index;\n\t\tconditionals.push({\n\t\t\texpression: expression.trim(),\n\t\t\tposition,\n\t\t\ttype: 'and',\n\t\t});\n\t}\n\n\t// Extract 'or' conditions\n\tconst orMatches = xpath.matchAll(/or\\s+([^[\\]]+)(?=\\s*\\])/g);\n\tfor (const match of orMatches) {\n\t\t// Regex capture group ([^[\\]]+) requires at least one character, so match[1] is always defined\n\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Regex capture group ensures match[1] is defined\n\t\tconst expression = match[MATCH_INDEX]!;\n\t\tconst position = match.index;\n\t\tconditionals.push({\n\t\t\texpression: expression.trim(),\n\t\t\tposition,\n\t\t\ttype: 'or',\n\t\t});\n\t}\n\n\treturn conditionals;\n}\n", "/**\n * @file\n * XPath analysis module. Analyzes XPath expressions and extracts components.\n */\nimport type { XPathAnalysis } from '../types/index.js';\nimport { extractNodeTypes } from './extractors/extractNodeTypes.js';\nimport { extractOperators } from './extractors/extractOperators.js';\nimport { extractAttributes } from './extractors/extractAttributes.js';\nimport { extractConditionals } from './extractors/extractConditionals.js';\n\n/**\n * Analyze XPath expression and extract all components.\n * @param xpath - XPath expression to analyze.\n * @returns Complete XPath analysis result.\n */\nexport function analyzeXPath(xpath: Readonly<string>): XPathAnalysis {\n\tif (!xpath) {\n\t\treturn {\n\t\t\tattributes: [],\n\t\t\tconditionals: [],\n\t\t\thasLetExpressions: false,\n\t\t\thasUnions: false,\n\t\t\tnodeTypes: [],\n\t\t\toperators: [],\n\t\t\tpatterns: [],\n\t\t};\n\t}\n\n\tconst nodeTypes = extractNodeTypes(xpath);\n\tconst operators = extractOperators(xpath);\n\tconst attributes = extractAttributes(xpath);\n\tconst conditionals = extractConditionals(xpath);\n\n\tconst hasUnions = xpath.includes('|');\n\tconst hasLetExpressions = xpath.includes('let ');\n\n\treturn {\n\t\tattributes,\n\t\tconditionals,\n\t\thasLetExpressions,\n\t\thasUnions,\n\t\tnodeTypes,\n\t\toperators,\n\t\tpatterns: [], // Reserved for future pattern analysis\n\t};\n}\n", "/**\n * @file\n * Extracts violation and valid markers from example content for PMD rule testing.\n */\nimport type { ViolationMarker } from '../types/index.js';\n\nconst LINE_NUMBER_OFFSET = 1;\n\n/**\n * Extract violation and valid markers from example content.\n * @param exampleContent - Raw example content with markers.\n * @returns Object containing violation and valid markers.\n */\nexport function extractMarkers(exampleContent: string): {\n\tvalidMarkers: ViolationMarker[];\n\tviolationMarkers: ViolationMarker[];\n} {\n\tconst lines = exampleContent.split('\\n');\n\tconst violationMarkers: ViolationMarker[] = [];\n\tconst validMarkers: ViolationMarker[] = [];\n\n\tconst hasInlineMarkersInExample =\n\t\texampleContent.includes('// \u274C') || exampleContent.includes('// \u2705');\n\n\tlines.forEach((line, index) => {\n\t\tconst trimmed = line.trim();\n\t\tconst lineNumber = index + LINE_NUMBER_OFFSET;\n\n\t\t// Check for inline violation/valid markers\n\t\tif (trimmed.includes('// \u274C')) {\n\t\t\tviolationMarkers.push({\n\t\t\t\tdescription: 'Inline violation marker // \u274C',\n\t\t\t\tindex: violationMarkers.length,\n\t\t\t\tisViolation: true,\n\t\t\t\tlineNumber,\n\t\t\t});\n\t\t} else if (trimmed.includes('// \u2705')) {\n\t\t\tvalidMarkers.push({\n\t\t\t\tdescription: 'Inline valid marker // \u2705',\n\t\t\t\tindex: validMarkers.length,\n\t\t\t\tisViolation: false,\n\t\t\t\tlineNumber,\n\t\t\t});\n\t\t}\n\n\t\t// Section headers create markers only when there are no inline markers\n\t\tif (!hasInlineMarkersInExample) {\n\t\t\tif (trimmed.startsWith('// Violation:')) {\n\t\t\t\tconst description = trimmed\n\t\t\t\t\t.substring('// Violation:'.length)\n\t\t\t\t\t.trim();\n\t\t\t\tviolationMarkers.push({\n\t\t\t\t\tdescription,\n\t\t\t\t\tindex: violationMarkers.length,\n\t\t\t\t\tisViolation: true,\n\t\t\t\t\tlineNumber,\n\t\t\t\t});\n\t\t\t} else if (trimmed.startsWith('// Valid:')) {\n\t\t\t\tconst description = trimmed\n\t\t\t\t\t.substring('// Valid:'.length)\n\t\t\t\t\t.trim();\n\t\t\t\tvalidMarkers.push({\n\t\t\t\t\tdescription,\n\t\t\t\t\tindex: validMarkers.length,\n\t\t\t\t\tisViolation: false,\n\t\t\t\t\tlineNumber,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\n\treturn { validMarkers, violationMarkers };\n}\n", "/**\n * @file\n * Parses example content and extracts violations, valids, and markers for PMD rule testing.\n */\nimport type { ExampleData } from '../types/index.js';\nimport { extractMarkers } from './extractMarkers.js';\n\nconst EMPTY_STRING_INDEX = 0;\n\n/**\n * Parse example content and extract violations, valids, and markers.\n * @param exampleContent - Raw example content with code and markers.\n * @returns Parsed example data (without exampleIndex, which is added by the caller).\n */\nexport function parseExample(\n\texampleContent: string,\n): Omit<ExampleData, 'exampleIndex'> {\n\tconst lines = exampleContent.split('\\n');\n\tconst violations: string[] = [];\n\tconst valids: string[] = [];\n\tlet currentMode: 'valid' | 'violation' | null = null;\n\n\tconst { validMarkers, violationMarkers } = extractMarkers(exampleContent);\n\n\tconst hasInlineMarkersInExample =\n\t\texampleContent.includes('// \u274C') || exampleContent.includes('// \u2705');\n\n\tlines.forEach((line) => {\n\t\tconst trimmed = line.trim();\n\n\t\t/**\n\t\t * Default to current mode.\n\t\t */\n\t\tlet lineMode = currentMode;\n\n\t\t// Check for inline violation/valid markers\n\t\tif (trimmed.includes('// \u274C')) {\n\t\t\tlineMode = 'violation';\n\t\t} else if (trimmed.includes('// \u2705')) {\n\t\t\tlineMode = 'valid';\n\t\t}\n\n\t\tif (trimmed.startsWith('// Violation:')) {\n\t\t\tcurrentMode = 'violation';\n\t\t\t// Section headers create markers only when there are no inline markers\n\t\t\tif (!hasInlineMarkersInExample) {\n\t\t\t\t// We don't add markers here - they're added in extractMarkers\n\t\t\t}\n\t\t} else if (trimmed.startsWith('// Valid:')) {\n\t\t\tcurrentMode = 'valid';\n\t\t\t// Section headers create markers only when there are no inline markers\n\t\t\tif (!hasInlineMarkersInExample) {\n\t\t\t\t// We don't add markers here - they're added in extractMarkers\n\t\t\t}\n\t\t} else if (trimmed && !trimmed.startsWith('//') && trimmed !== '') {\n\t\t\t// This is a code line - it belongs to the determined mode (inline markers override)\n\t\t\t// Remove inline comment markers from the code\n\t\t\tlet codeLine = line;\n\t\t\tif (line.includes('// \u274C')) {\n\t\t\t\tconst splitResult = line.split('// \u274C');\n\t\t\t\t// split() always returns at least one element, so [0] is always defined\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- split() always returns at least one element\n\t\t\t\tcodeLine = splitResult[EMPTY_STRING_INDEX]!.trim();\n\t\t\t} else if (line.includes('// \u2705')) {\n\t\t\t\tconst splitResult = line.split('// \u2705');\n\t\t\t\t// split() always returns at least one element, so [0] is always defined\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- split() always returns at least one element\n\t\t\t\tcodeLine = splitResult[EMPTY_STRING_INDEX]!.trim();\n\t\t\t}\n\n\t\t\tif (lineMode === 'violation') {\n\t\t\t\tviolations.push(codeLine);\n\t\t\t} else if (lineMode === 'valid') {\n\t\t\t\tvalids.push(codeLine);\n\t\t\t}\n\t\t}\n\t});\n\n\treturn {\n\t\tcontent: exampleContent,\n\t\tvalidMarkers,\n\t\tvalids,\n\t\tviolationMarkers,\n\t\tviolations,\n\t};\n}\n", "/**\n * @file\n * Creates temporary Apex test files from example content for PMD rule testing.\n */\nimport { writeFileSync } from 'fs';\nimport tmp from 'tmp';\nimport type { TestFileResult } from '../types/index.js';\nimport { parseExample } from './parseExample.js';\n\nconst EMPTY_LENGTH = 0;\nconst FIRST_CAPTURE_GROUP_INDEX = 1;\nconst SECOND_CAPTURE_GROUP_INDEX = 2;\n\n/**\n * Infers the return type of a method based on its usage context in the code.\n * @param code - Full code to analyze.\n * @param methodName - Name of the method to analyze.\n * @returns Inferred return type (always returns a string, defaults to 'Boolean').\n */\nfunction inferReturnType(code: string, methodName: string): string {\n\t// Look for patterns that indicate return types\n\n\t// Comparison with numbers: method() > 0, method() < 5, etc.\n\tif (\n\t\tnew RegExp(`\\\\b${methodName}\\\\s*\\\\(\\\\s*\\\\)\\\\s*[><=!]+\\\\s*\\\\d+`).test(\n\t\t\tcode,\n\t\t)\n\t) {\n\t\treturn 'Integer';\n\t}\n\n\t// Switch statements: switch on method()\n\tif (new RegExp(`switch\\\\s+on\\\\s+${methodName}\\\\s*\\\\(`).test(code)) {\n\t\treturn 'String';\n\t}\n\n\t// Ternary expressions: method() ? ... : ...\n\tif (new RegExp(`${methodName}\\\\s*\\\\(\\\\s*\\\\)\\\\s*\\\\?`).test(code)) {\n\t\treturn 'Boolean';\n\t}\n\n\t// For-each loops: for (Type var : method())\n\tif (new RegExp(`for\\\\s*\\\\([^:]*:\\\\s*${methodName}\\\\s*\\\\(`).test(code)) {\n\t\treturn 'List<String>';\n\t}\n\n\t// Set assignments: Set<Type> var = method()\n\tif (\n\t\tnew RegExp(`Set<[^>]+>\\\\s+\\\\w+\\\\s*=\\\\s*${methodName}\\\\s*\\\\(`).test(code)\n\t) {\n\t\treturn 'Set<String>';\n\t}\n\n\t// Map assignments: Map<Key, Value> var = method()\n\tif (\n\t\tnew RegExp(\n\t\t\t`Map<[^>]+,\\\\s*[^>]+>\\\\s+\\\\w+\\\\s*=\\\\s*${methodName}\\\\s*\\\\(`,\n\t\t).test(code)\n\t) {\n\t\treturn 'Map<String, Integer>';\n\t}\n\n\t// Decimal assignments: Decimal var = method()\n\tif (new RegExp(`Decimal\\\\s+\\\\w+\\\\s*=\\\\s*${methodName}\\\\s*\\\\(`).test(code)) {\n\t\treturn 'Decimal';\n\t}\n\n\t// Double assignments: Double var = method()\n\tif (new RegExp(`Double\\\\s+\\\\w+\\\\s*=\\\\s*${methodName}\\\\s*\\\\(`).test(code)) {\n\t\treturn 'Double';\n\t}\n\n\t// Long assignments: Long var = method()\n\tif (new RegExp(`Long\\\\s+\\\\w+\\\\s*=\\\\s*${methodName}\\\\s*\\\\(`).test(code)) {\n\t\treturn 'Long';\n\t}\n\n\t// Date assignments: Date var = method()\n\tif (new RegExp(`Date\\\\s+\\\\w+\\\\s*=\\\\s*${methodName}\\\\s*\\\\(`).test(code)) {\n\t\treturn 'Date';\n\t}\n\n\t// Datetime assignments: Datetime var = method()\n\tif (\n\t\tnew RegExp(`Datetime\\\\s+\\\\w+\\\\s*=\\\\s*${methodName}\\\\s*\\\\(`).test(code)\n\t) {\n\t\treturn 'Datetime';\n\t}\n\n\t// Time assignments: Time var = method()\n\tif (new RegExp(`Time\\\\s+\\\\w+\\\\s*=\\\\s*${methodName}\\\\s*\\\\(`).test(code)) {\n\t\treturn 'Time';\n\t}\n\n\t// Blob assignments: Blob var = method()\n\tif (new RegExp(`Blob\\\\s+\\\\w+\\\\s*=\\\\s*${methodName}\\\\s*\\\\(`).test(code)) {\n\t\treturn 'Blob';\n\t}\n\n\t// Id assignments: Id var = method()\n\tif (new RegExp(`Id\\\\s+\\\\w+\\\\s*=\\\\s*${methodName}\\\\s*\\\\(`).test(code)) {\n\t\treturn 'Id';\n\t}\n\n\t// While/do-while conditions: while (method())\n\tif (new RegExp(`(?:while|do)\\\\s*\\\\(\\\\s*${methodName}\\\\s*\\\\(`).test(code)) {\n\t\treturn 'Boolean';\n\t}\n\n\t// Object assignments: Object var = method()\n\t// This is a catch-all for any object type that doesn't match above patterns\n\tif (new RegExp(`Object\\\\s+\\\\w+\\\\s*=\\\\s*${methodName}\\\\s*\\\\(`).test(code)) {\n\t\treturn 'Object';\n\t}\n\n\t// Default to Boolean for unknown patterns\n\t// Note: This ensures inferReturnType always returns a valid type that matches a case in generateReturnValue\n\treturn 'Boolean';\n}\n\n/**\n * Generates an appropriate return value statement for a given Apex type.\n * @internal\n * @param returnType - The Apex return type to generate a value for.\n * @returns Return statement with appropriate value for the type.\n */\n// eslint-disable-next-line import/group-exports -- generateReturnValue is exported for testing only\nexport function generateReturnValue(returnType: string): string {\n\t// Handle collection types dynamically with support for nested generics\n\tconst listRegex = /^List<(.+)>$/;\n\tconst listMatch = listRegex.exec(returnType);\n\tconst listInnerType = listMatch?.[FIRST_CAPTURE_GROUP_INDEX];\n\tif (listInnerType !== undefined && listInnerType.length > EMPTY_LENGTH) {\n\t\treturn `return new List<${listInnerType}>();`;\n\t}\n\n\tconst setRegex = /^Set<(.+)>$/;\n\tconst setMatch = setRegex.exec(returnType);\n\tconst setInnerType = setMatch?.[FIRST_CAPTURE_GROUP_INDEX];\n\tif (setInnerType !== undefined && setInnerType.length > EMPTY_LENGTH) {\n\t\treturn `return new Set<${setInnerType}>();`;\n\t}\n\n\tconst mapRegex = /^Map<(.+),\\s*(.+)>$/;\n\tconst mapMatch = mapRegex.exec(returnType);\n\tconst mapKeyType = mapMatch?.[FIRST_CAPTURE_GROUP_INDEX];\n\tconst mapValueType = mapMatch?.[SECOND_CAPTURE_GROUP_INDEX];\n\tif (\n\t\tmapKeyType !== undefined &&\n\t\tmapValueType !== undefined &&\n\t\tmapKeyType.length > EMPTY_LENGTH &&\n\t\tmapValueType.length > EMPTY_LENGTH\n\t) {\n\t\treturn `return new Map<${mapKeyType}, ${mapValueType}>();`;\n\t}\n\n\t// Handle primitive and specific types\n\tswitch (returnType) {\n\t\tcase 'Integer':\n\t\t\treturn 'return 1;';\n\t\tcase 'String':\n\t\t\treturn \"return 'test';\";\n\t\tcase 'Boolean':\n\t\t\treturn 'return true;';\n\t\tcase 'Decimal':\n\t\t\treturn 'return 1.0;';\n\t\tcase 'Double':\n\t\t\treturn 'return 1.0;';\n\t\tcase 'Long':\n\t\t\treturn 'return 1000L;';\n\t\tcase 'Date':\n\t\t\treturn 'return Date.newInstance(2024, 1, 1);';\n\t\tcase 'Datetime':\n\t\t\treturn 'return Datetime.newInstance(2024, 1, 1);';\n\t\tcase 'Time':\n\t\t\treturn 'return Time.newInstance(0, 0, 0, 0);';\n\t\tcase 'Blob':\n\t\t\treturn \"return Blob.valueOf('test');\";\n\t\tcase 'Id':\n\t\t\treturn \"return '001000000000000000';\";\n\t\tcase 'Object':\n\t\t\treturn 'return null;';\n\t\t// All types from inferReturnType are handled above\n\t\t// TypeScript ensures exhaustiveness, so no default needed\n\t\t// However, we keep a default for runtime safety in case inferReturnType is extended\n\t\tdefault:\n\t\t\t// This should never be reached as inferReturnType always returns a known type\n\t\t\t// But kept for type safety and to satisfy TypeScript exhaustiveness\n\t\t\t// To test this, we would need to call generateReturnValue directly with an unknown type\n\t\t\treturn 'return null;';\n\t}\n}\n\n/**\n * Extract helper methods needed for the test code.\n * Analyzes method calls in the code and generates appropriate helper method signatures.\n * @param codeLines - Array of code lines to analyze.\n * @returns Array of helper method signatures with implementations.\n */\nfunction extractHelperMethods(codeLines: readonly string[]): string[] {\n\tconst methods = new Map<string, string>();\n\n\t// Join all code lines for easier analysis\n\tconst fullCode = codeLines.join('\\n');\n\n\t// Extract method calls using regex - match word characters before parentheses\n\tconst methodCallRegex = /\\b([a-zA-Z_]\\w*)\\s*\\(/g;\n\tlet match: RegExpExecArray | null = null;\n\tconst foundMethods = new Set<string>();\n\n\t// Apex keywords that should not be considered method names\n\tconst apexKeywords = [\n\t\t'if',\n\t\t'else',\n\t\t'for',\n\t\t'while',\n\t\t'do',\n\t\t'switch',\n\t\t'case',\n\t\t'default',\n\t\t'try',\n\t\t'catch',\n\t\t'finally',\n\t\t'class',\n\t\t'interface',\n\t\t'enum',\n\t\t'public',\n\t\t'private',\n\t\t'protected',\n\t\t'static',\n\t\t'final',\n\t\t'abstract',\n\t\t'void',\n\t\t'return',\n\t\t'new',\n\t\t'this',\n\t\t'super',\n\t\t'extends',\n\t\t'implements',\n\t\t'instanceof',\n\t\t'System',\n\t\t'debug',\n\t\t'List',\n\t\t'Map',\n\t\t'Set',\n\t\t'String',\n\t\t'Integer',\n\t\t'Boolean',\n\t\t'Double',\n\t\t'Date',\n\t\t'Datetime',\n\t];\n\n\twhile ((match = methodCallRegex.exec(fullCode)) !== null) {\n\t\tconst [, methodName] = match;\n\n\t\t// Skip Apex keywords and built-in types\n\t\t// The false branch (methodName === undefined) is unreachable\n\t\t// as regex match always produces a value, so remove conditional to eliminate branch\n\t\tif (methodName !== undefined && apexKeywords.includes(methodName)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Remove unreachable false branch - methodName is always defined from regex match\n\t\t// Use non-null assertion to eliminate unreachable branch\n\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- methodName is always defined from regex match\n\t\tfoundMethods.add(methodName!);\n\t}\n\n\t// For each found method, determine return type and generate signature\n\tfor (const methodName of foundMethods) {\n\t\tconst returnType = inferReturnType(fullCode, methodName);\n\n\t\t// inferReturnType always returns a non-empty string (defaults to 'Boolean')\n\t\t// so we can safely use the return type directly\n\t\t// Ensure returnType is a valid type that matches a case in generateReturnValue\n\t\tconst validReturnType: string = returnType;\n\t\tconst methodSignature = `public ${validReturnType} ${methodName}() {\\n ${generateReturnValue(validReturnType)}\\n }`;\n\t\tmethods.set(methodName, methodSignature);\n\t}\n\n\treturn Array.from(methods.values());\n}\n\ninterface CreateTestFileOptions {\n\texampleContent: string;\n\texampleIndex: number;\n\tincludeViolations?: boolean;\n\tincludeValids?: boolean;\n}\n\n/**\n * Create a temporary Apex test file from example content.\n * @param options - Configuration options for test file creation.\n * @param options.exampleContent - Raw example content.\n * @param options.exampleIndex - Index of the example for unique naming.\n * @param options.includeViolations - Whether to include violation code.\n * @param options.includeValids - Whether to include valid code.\n * @returns Result of file creation.\n */\n// eslint-disable-next-line import/group-exports -- createTestFile is the main export, generateReturnValue is for testing\nexport function createTestFile({\n\texampleContent,\n\texampleIndex,\n\tincludeViolations = true,\n\tincludeValids = true,\n}: Readonly<CreateTestFileOptions>): TestFileResult {\n\t// Use tmp library for secure temporary file creation\n\t// keep: true ensures file persists for PMD to read it\n\t// File will be cleaned up on process exit by default, or can be manually removed\n\tconst tmpFile = tmp.fileSync({\n\t\tkeep: true,\n\t\tpostfix: '.cls',\n\t\tprefix: `rule-test-example-${String(exampleIndex)}-`,\n\t});\n\tconst tempFile = tmpFile.name;\n\n\t// Parse the example to get violation and valid code\n\tconst parsed = parseExample(exampleContent);\n\n\t// Always use the parsed approach for consistency\n\tlet classContent = `public class TestClass${String(exampleIndex)} {\\n`;\n\n\t// Choose which code to include based on parameters\n\tlet codeToInclude: string[] = [];\n\tif (includeViolations && !includeValids) {\n\t\t// Only violations\n\t\tcodeToInclude = parsed.violations;\n\t} else if (includeValids && !includeViolations) {\n\t\tcodeToInclude = parsed.valids;\n\t} else {\n\t\t// Both or neither\n\t\tcodeToInclude = [...parsed.violations, ...parsed.valids];\n\t}\n\n\t// Wrap code in a method for valid Apex syntax\n\tif (codeToInclude.length > EMPTY_LENGTH) {\n\t\tclassContent += ` public void testMethod${String(exampleIndex)}() {\\n`;\n\t\t// Process all the parsed code lines\n\t\tcodeToInclude.forEach((line) => {\n\t\t\tclassContent += ` ${line}\\n`;\n\t\t});\n\t\tclassContent += ` }\\n`;\n\t}\n\n\t// Dynamically generate helper methods based on method calls in the test code\n\tconst helperMethods = extractHelperMethods(codeToInclude);\n\tfor (const method of helperMethods) {\n\t\tclassContent += ` ${method}\\n`;\n\t}\n\n\tclassContent += '}\\n';\n\n\twriteFileSync(tempFile, classContent, 'utf-8');\n\n\treturn {\n\t\tfilePath: tempFile,\n\t\thasValids: parsed.valids.length > EMPTY_LENGTH,\n\t\thasViolations: parsed.violations.length > EMPTY_LENGTH,\n\t\tvalidCount: parsed.valids.length,\n\t\tviolationCount: parsed.violations.length,\n\t};\n}\n", "/**\n * @file\n * PMD execution module. Runs PMD CLI against Apex files and parses results.\n */\nimport { execFileSync } from 'child_process';\nimport { resolve } from 'path';\nimport type { PMDResult, FileOperationResult } from '../types/index.js';\nimport { parseViolations } from './parseViolations.js';\n\nconst MIN_OUTPUT_LENGTH = 0;\nconst EMPTY_STRING = '';\n\n/**\n * Execute PMD CLI against an Apex file using a ruleset.\n * @param apexFilePath - Path to the Apex file to analyze.\n * @param rulesetPath - Path to the PMD ruleset XML file.\n * @returns Promise resolving to PMD execution results.\n */\n// eslint-disable-next-line @typescript-eslint/require-await -- Function signature requires Promise for compatibility with tests\nexport async function runPMD(\n\tapexFilePath: string,\n\trulesetPath: string,\n): Promise<FileOperationResult<PMDResult>> {\n\ttry {\n\t\t// Resolve absolute paths\n\t\tconst absoluteApexPath = resolve(apexFilePath);\n\t\tconst absoluteRulesetPath = resolve(rulesetPath);\n\n\t\t// Execute PMD with XML output format\n\t\t// Use execFileSync instead of execSync to prevent command injection\n\t\t// Arguments are passed as an array, avoiding shell interpretation of special characters\n\t\tconst result = execFileSync(\n\t\t\t'pmd',\n\t\t\t[\n\t\t\t\t'check',\n\t\t\t\t'--no-cache',\n\t\t\t\t'--no-progress',\n\t\t\t\t'-d',\n\t\t\t\tabsoluteApexPath,\n\t\t\t\t'-R',\n\t\t\t\tabsoluteRulesetPath,\n\t\t\t\t'-f',\n\t\t\t\t'xml',\n\t\t\t],\n\t\t\t{\n\t\t\t\tcwd: process.cwd(),\n\t\t\t\tencoding: 'utf-8',\n\t\t\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t\t\ttimeout: 30000,\n\t\t\t},\n\t\t);\n\n\t\t// Parse the XML output\n\t\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- execFileSync with encoding returns string | Buffer\n\t\tconst violations = parseViolations(result as string);\n\n\t\treturn {\n\t\t\tdata: {\n\t\t\t\tviolations,\n\t\t\t},\n\t\t\tsuccess: true,\n\t\t};\n\t} catch (error: unknown) {\n\t\t// Handle PMD execution errors\n\t\tinterface ExecError {\n\t\t\tcode?: string;\n\t\t\tstdout?: Buffer | string;\n\t\t\tstderr?: Buffer | string;\n\t\t\tmessage?: string;\n\t\t}\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Error from execFileSync has known shape\n\t\tconst execError = error as ExecError;\n\t\tif (execError.code === 'ENOENT') {\n\t\t\treturn {\n\t\t\t\terror: 'PMD CLI not available. Please install PMD to run tests. Visit: https://pmd.github.io/pmd/pmd_userdocs_installation.html',\n\t\t\t\tsuccess: false,\n\t\t\t};\n\t\t}\n\n\t\t// PMD may exit with non-zero when violations are found\n\t\t// Try to parse XML from stdout or error.stdout\n\t\tconst xmlOutput = execError.stdout ?? EMPTY_STRING;\n\t\tconst xmlOutputString =\n\t\t\ttypeof xmlOutput === 'string' ? xmlOutput : xmlOutput.toString();\n\n\t\tif (xmlOutputString.trim().length > MIN_OUTPUT_LENGTH) {\n\t\t\ttry {\n\t\t\t\tconst violations = parseViolations(xmlOutputString);\n\t\t\t\treturn {\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tviolations,\n\t\t\t\t\t},\n\t\t\t\t\tsuccess: true,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\t// XML parsing failed, return the original error\n\t\t\t}\n\t\t}\n\n\t\t// Return execution error with details\n\t\tconst errorMessage = execError.message ?? 'Unknown error';\n\t\tlet fullErrorMessage = `PMD execution failed: ${errorMessage}`;\n\n\t\tif (execError.stderr !== undefined) {\n\t\t\tconst stderr =\n\t\t\t\ttypeof execError.stderr === 'string'\n\t\t\t\t\t? execError.stderr\n\t\t\t\t\t: execError.stderr.toString();\n\t\t\tif (stderr.trim().length > MIN_OUTPUT_LENGTH) {\n\t\t\t\tfullErrorMessage += `\\nPMD stderr:\\n${stderr}`;\n\t\t\t}\n\t\t}\n\n\t\tif (execError.stdout !== undefined) {\n\t\t\tconst stdout =\n\t\t\t\ttypeof execError.stdout === 'string'\n\t\t\t\t\t? execError.stdout\n\t\t\t\t\t: execError.stdout.toString();\n\t\t\t// Include stdout even if it's whitespace-only (test expects this)\n\t\t\tif (stdout.length > MIN_OUTPUT_LENGTH) {\n\t\t\t\tfullErrorMessage += `\\nPMD stdout:\\n${stdout}`;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\terror: fullErrorMessage,\n\t\t\tsuccess: false,\n\t\t};\n\t}\n}\n", "/**\n * @file\n * Parses PMD XML output and extracts violation information for rule testing.\n */\nimport { DOMParser } from '@xmldom/xmldom';\nimport type { Violation } from '../types/index.js';\n\nconst DEFAULT_LINE = 0;\nconst DEFAULT_COLUMN = 0;\nconst DEFAULT_PRIORITY = '5';\nconst DEFAULT_MESSAGE = '';\nconst RADIX = 10;\nconst MIN_STRING_LENGTH = 0;\nconst MIN_TEXT_LENGTH = 0;\n\n/**\n * Parse PMD XML output and extract violation information.\n * @param xmlOutput - Raw XML output from PMD CLI.\n * @returns Array of violation objects.\n */\nexport function parseViolations(xmlOutput: string): Violation[] {\n\tconst parser = new DOMParser();\n\tconst doc = parser.parseFromString(xmlOutput, 'text/xml');\n\tconst violations: Violation[] = [];\n\n\t// PMD XML format: <pmd><file><violation>...</violation></file></pmd>\n\tconst fileNodes = Array.from(doc.getElementsByTagName('file'));\n\n\tfor (const fileNode of fileNodes) {\n\t\tconst violationNodes = Array.from(\n\t\t\tfileNode.getElementsByTagName('violation'),\n\t\t);\n\n\t\tfor (const violationNode of violationNodes) {\n\t\t\tconst beginlineAttr = violationNode.getAttribute('beginline');\n\t\t\tconst begincolumnAttr = violationNode.getAttribute('begincolumn');\n\t\t\tconst priorityAttr = violationNode.getAttribute('priority');\n\t\t\tconst ruleAttr = violationNode.getAttribute('rule');\n\t\t\tconst messageAttr = violationNode.getAttribute('message');\n\t\t\tconst rawTextContent = violationNode.textContent;\n\t\t\t// xmldom always returns string (never null) for textContent\n\t\t\tconst trimmedText = rawTextContent.trim();\n\t\t\tconst hasTextContent = trimmedText.length > MIN_TEXT_LENGTH;\n\t\t\tconst textContent = hasTextContent ? trimmedText : DEFAULT_MESSAGE;\n\n\t\t\t// Handle null or empty string attributes\n\t\t\tconst hasBeginline =\n\t\t\t\tbeginlineAttr !== null &&\n\t\t\t\tbeginlineAttr.length > MIN_STRING_LENGTH;\n\t\t\tconst line = parseInt(\n\t\t\t\thasBeginline ? beginlineAttr : String(DEFAULT_LINE),\n\t\t\t\tRADIX,\n\t\t\t);\n\n\t\t\tconst hasBegincolumn =\n\t\t\t\tbegincolumnAttr !== null &&\n\t\t\t\tbegincolumnAttr.length > MIN_STRING_LENGTH;\n\t\t\tconst column = parseInt(\n\t\t\t\thasBegincolumn ? begincolumnAttr : String(DEFAULT_COLUMN),\n\t\t\t\tRADIX,\n\t\t\t);\n\n\t\t\t// Prefer message attribute, then textContent, then default\n\t\t\tconst hasMessageAttr =\n\t\t\t\tmessageAttr !== null && messageAttr.length > MIN_STRING_LENGTH;\n\t\t\t// textContent is DEFAULT_MESSAGE when empty\n\t\t\tconst message = hasMessageAttr ? messageAttr : textContent;\n\n\t\t\tconst hasPriorityAttr =\n\t\t\t\tpriorityAttr !== null &&\n\t\t\t\tpriorityAttr.length > MIN_STRING_LENGTH;\n\t\t\tconst priority = parseInt(\n\t\t\t\thasPriorityAttr ? priorityAttr : DEFAULT_PRIORITY,\n\t\t\t\tRADIX,\n\t\t\t);\n\n\t\t\tconst hasRuleAttr =\n\t\t\t\truleAttr !== null && ruleAttr.length > MIN_STRING_LENGTH;\n\t\t\tconst rule = hasRuleAttr ? ruleAttr : DEFAULT_MESSAGE;\n\n\t\t\tconst violation: Violation = {\n\t\t\t\tcolumn,\n\t\t\t\tline,\n\t\t\t\tmessage,\n\t\t\t\tpriority,\n\t\t\t\trule,\n\t\t\t};\n\n\t\t\tviolations.push(violation);\n\t\t}\n\t}\n\n\treturn violations;\n}\n", "/**\n * @file\n * Rule metadata validation for PMD rules.\n */\nimport type { RuleMetadata, ValidationResult } from '../../types/index.js';\n\nconst MIN_RULE_NAME_LENGTH = 3;\nconst MIN_MESSAGE_LENGTH = 10;\nconst MIN_DESCRIPTION_LENGTH = 20;\nconst MIN_HARDCODED_STRING_LENGTH = 4;\nconst MIN_ERROR_COUNT = 0;\nconst MIN_MATCH_COUNT = 0;\n\n/**\n * Check if XPath contains hardcoded values.\n * @param xpath - XPath expression to check.\n * @returns True if hardcoded values are found.\n */\nfunction containsHardcodedValues(xpath: string): boolean {\n\t// Check for hardcoded numbers (except common values like 0, 1)\n\tconst hardcodedNumbers = xpath.match(/\\b[2-9]\\d*\\b/g);\n\tif (\n\t\thardcodedNumbers !== null &&\n\t\thardcodedNumbers.length > MIN_MATCH_COUNT\n\t) {\n\t\treturn true;\n\t}\n\n\t// Check for hardcoded strings (both single and double quotes)\n\tconst hardcodedStrings = xpath.match(/[\"'][^\"']*[\"']/g);\n\tconst hasLongHardcodedStrings = hardcodedStrings?.some(\n\t\t(str) => str.length > MIN_HARDCODED_STRING_LENGTH,\n\t);\n\tif (hasLongHardcodedStrings === true) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n/**\n * Check rule metadata for quality issues.\n * @param metadata - Rule metadata to validate.\n * @returns Validation result with errors and warnings.\n */\nexport function checkRuleMetadata(\n\tmetadata: Readonly<RuleMetadata>,\n): ValidationResult {\n\tconst errors: string[] = [];\n\tconst warnings: string[] = [];\n\n\t// Check required fields\n\tif (\n\t\tmetadata.ruleName === null ||\n\t\tmetadata.ruleName === undefined ||\n\t\tmetadata.ruleName === ''\n\t) {\n\t\terrors.push('Rule name is missing');\n\t}\n\n\tif (\n\t\tmetadata.message === null ||\n\t\tmetadata.message === undefined ||\n\t\tmetadata.message === ''\n\t) {\n\t\terrors.push('Rule message is missing');\n\t}\n\n\tif (\n\t\tmetadata.description === null ||\n\t\tmetadata.description === undefined ||\n\t\tmetadata.description === ''\n\t) {\n\t\twarnings.push('Rule description is missing (recommended)');\n\t}\n\n\tif (\n\t\tmetadata.xpath === null ||\n\t\tmetadata.xpath === undefined ||\n\t\tmetadata.xpath === ''\n\t) {\n\t\terrors.push('Rule XPath expression is missing');\n\t}\n\n\t// Check field quality\n\tif (\n\t\tmetadata.ruleName !== null &&\n\t\tmetadata.ruleName !== undefined &&\n\t\tmetadata.ruleName.length < MIN_RULE_NAME_LENGTH\n\t) {\n\t\tconst minLengthStr = String(MIN_RULE_NAME_LENGTH);\n\t\twarnings.push(\n\t\t\t`Rule name is very short (less than ${minLengthStr} characters)`,\n\t\t);\n\t}\n\n\tif (\n\t\tmetadata.message !== null &&\n\t\tmetadata.message !== undefined &&\n\t\tmetadata.message.length < MIN_MESSAGE_LENGTH\n\t) {\n\t\tconst minLengthStr = String(MIN_MESSAGE_LENGTH);\n\t\twarnings.push(\n\t\t\t`Rule message is very short (less than ${minLengthStr} characters)`,\n\t\t);\n\t}\n\n\tif (\n\t\tmetadata.description !== null &&\n\t\tmetadata.description !== undefined &&\n\t\tmetadata.description.length < MIN_DESCRIPTION_LENGTH\n\t) {\n\t\tconst minLengthStr = String(MIN_DESCRIPTION_LENGTH);\n\t\twarnings.push(\n\t\t\t`Rule description is very short (less than ${minLengthStr} characters)`,\n\t\t);\n\t}\n\n\t// Check for hardcoded values in XPath\n\tconst hasXPath =\n\t\tmetadata.xpath !== null &&\n\t\tmetadata.xpath !== undefined &&\n\t\tmetadata.xpath !== '';\n\tif (hasXPath && containsHardcodedValues(metadata.xpath)) {\n\t\twarnings.push(\n\t\t\t'XPath contains hardcoded values that should be parameterized',\n\t\t);\n\t}\n\n\treturn {\n\t\tissues: errors,\n\t\tpassed: errors.length === MIN_ERROR_COUNT,\n\t\twarnings,\n\t};\n}\n", "/**\n * @file\n * Example quality validation for PMD rules.\n */\nimport type { ExampleData, ValidationResult } from '../../types/index.js';\n\nconst MIN_EXAMPLES_COUNT = 0;\nconst MIN_VIOLATION_MARKERS = 0;\nconst MIN_VALID_MARKERS = 0;\nconst MIN_VIOLATIONS = 0;\nconst MIN_VALIDS = 0;\nconst MIN_MARKERS = 0;\nconst MIN_CODE_LINES = 0;\nconst INDEX_OFFSET = 1;\n\n/**\n * Check consistency between markers and code content.\n * @param example - Example data to check.\n * @param exampleNum - Example number for error messages.\n * @param warnings - Array to append warnings to.\n */\nfunction checkMarkerConsistency(\n\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Object properties are accessed\n\texample: Readonly<ExampleData>,\n\texampleNum: Readonly<number>,\n\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Array is mutated via push()\n\twarnings: string[],\n): void {\n\tconst totalMarkers =\n\t\texample.violationMarkers.length + example.validMarkers.length;\n\tconst totalCodeLines = example.violations.length + example.valids.length;\n\n\tif (totalMarkers === MIN_MARKERS && totalCodeLines > MIN_CODE_LINES) {\n\t\tconst exampleNumStr = String(exampleNum);\n\t\twarnings.push(`Example ${exampleNumStr} has code but no markers`);\n\t}\n\n\tif (totalMarkers > MIN_MARKERS && totalCodeLines === MIN_CODE_LINES) {\n\t\tconst exampleNumStr = String(exampleNum);\n\t\twarnings.push(`Example ${exampleNumStr} has markers but no code`);\n\t}\n}\n\n/**\n * Check examples for quality issues.\n * @param examples - Array of parsed examples to validate.\n * @returns Validation result with errors and warnings.\n */\nexport function checkExamples(\n\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Array elements are accessed and iterated\n\texamples: readonly ExampleData[],\n): ValidationResult {\n\tconst errors: string[] = [];\n\tconst warnings: string[] = [];\n\n\tif (examples.length === MIN_EXAMPLES_COUNT) {\n\t\terrors.push('No examples found in rule');\n\t\treturn { issues: errors, passed: false, warnings };\n\t}\n\n\texamples.forEach(\n\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameters for forEach\n\t\t(example: Readonly<ExampleData>, index: Readonly<number>) => {\n\t\t\tconst exampleNum = index + INDEX_OFFSET;\n\n\t\t\t// Check for violation markers\n\t\t\tif (example.violationMarkers.length === MIN_VIOLATION_MARKERS) {\n\t\t\t\tconst exampleNumStr = String(exampleNum);\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Example ${exampleNumStr} has no violation markers`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Check for valid markers\n\t\t\tif (example.validMarkers.length === MIN_VALID_MARKERS) {\n\t\t\t\tconst exampleNumStr = String(exampleNum);\n\t\t\t\twarnings.push(`Example ${exampleNumStr} has no valid markers`);\n\t\t\t}\n\n\t\t\t// Check code content\n\t\t\tif (\n\t\t\t\texample.violations.length === MIN_VIOLATIONS &&\n\t\t\t\texample.valids.length === MIN_VALIDS\n\t\t\t) {\n\t\t\t\tconst exampleNumStr = String(exampleNum);\n\t\t\t\terrors.push(`Example ${exampleNumStr} contains no code`);\n\t\t\t}\n\n\t\t\t// Check for mixed inline markers\n\t\t\tconst hasViolations = example.violations.length > MIN_VIOLATIONS;\n\t\t\tconst hasValids = example.valids.length > MIN_VALIDS;\n\n\t\t\tif (\n\t\t\t\thasViolations &&\n\t\t\t\thasValids &&\n\t\t\t\texample.violationMarkers.length > MIN_VIOLATION_MARKERS &&\n\t\t\t\texample.validMarkers.length > MIN_VALID_MARKERS\n\t\t\t) {\n\t\t\t\t// This is okay for mixed examples\n\t\t\t} else if (hasViolations && !hasValids) {\n\t\t\t\t// Pure violation example - should have violation markers\n\t\t\t\tif (example.violationMarkers.length === MIN_VIOLATION_MARKERS) {\n\t\t\t\t\tconst exampleNumStr = String(exampleNum);\n\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t`Example ${exampleNumStr} has violations but no violation markers`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else if (!hasViolations && hasValids) {\n\t\t\t\t// Pure valid example - should have valid markers\n\t\t\t\tif (example.validMarkers.length === MIN_VALID_MARKERS) {\n\t\t\t\t\tconst exampleNumStr = String(exampleNum);\n\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t`Example ${exampleNumStr} has valid code but no valid markers`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check marker consistency\n\t\t\tcheckMarkerConsistency(example, exampleNum, warnings);\n\t\t},\n\t);\n\n\tconst noErrors = errors.length === MIN_EXAMPLES_COUNT;\n\treturn {\n\t\tissues: errors,\n\t\tpassed: noErrors,\n\t\twarnings,\n\t};\n}\n", "/**\n * @file\n * Duplicate pattern detection for rule examples.\n */\nimport type { ExampleData, ValidationResult } from '../../types/index.js';\n\nconst MIN_EXAMPLES_FOR_DUPLICATE_CHECK = 2;\nconst MIN_DUPLICATE_COUNT = 1;\nconst MIN_PATTERN_LENGTH = 10;\nconst PATTERN_DISPLAY_LENGTH = 50;\nconst INDEX_OFFSET = 1;\nconst ZERO_ERRORS = 0;\nconst MIN_COUNT = 0;\n\n/**\n * Normalize code for duplicate detection.\n * @param code - Code string to normalize.\n * @returns Normalized code string.\n */\nfunction normalizeCode(code: string): string {\n\treturn code\n\t\t.replace(/\\s+/g, ' ') // Normalize whitespace\n\t\t.trim()\n\t\t.toLowerCase();\n}\n\n/**\n * Check for duplicate patterns and add warnings.\n * @param patterns - Map of patterns to example numbers.\n * @param type - Type of pattern (violation or valid).\n * @param warnings - Array to append warnings to.\n */\nfunction checkPatternDuplicates(\n\tpatterns: Readonly<Map<string, readonly number[]>>,\n\ttype: Readonly<string>,\n\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Array is mutated via push()\n\twarnings: string[],\n): void {\n\tfor (const [pattern, exampleNumbers] of patterns.entries()) {\n\t\tif (\n\t\t\texampleNumbers.length > MIN_DUPLICATE_COUNT &&\n\t\t\tpattern.length > MIN_PATTERN_LENGTH\n\t\t) {\n\t\t\t// Only warn for substantial duplicates\n\t\t\tconst patternPreview = pattern.substring(\n\t\t\t\tMIN_COUNT,\n\t\t\t\tPATTERN_DISPLAY_LENGTH,\n\t\t\t);\n\t\t\twarnings.push(\n\t\t\t\t`Duplicate ${type} pattern \"${patternPreview}...\" ` +\n\t\t\t\t\t`found in examples: ${exampleNumbers.join(', ')}`,\n\t\t\t);\n\t\t}\n\t}\n}\n\n/**\n * Check for duplicate messages, branches, or patterns across examples.\n * @param examples - Array of parsed examples to check.\n * @returns Validation result with errors and warnings.\n */\nexport function checkDuplicates(\n\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Array elements are accessed and iterated\n\texamples: readonly ExampleData[],\n): ValidationResult {\n\tconst errors: string[] = [];\n\tconst warnings: string[] = [];\n\n\tif (examples.length < MIN_EXAMPLES_FOR_DUPLICATE_CHECK) {\n\t\treturn { issues: errors, passed: true, warnings };\n\t}\n\n\t// Check for duplicate violation patterns\n\tconst violationPatterns = new Map<string, number[]>();\n\tconst validPatterns = new Map<string, number[]>();\n\n\texamples.forEach(\n\t\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Callback parameters for forEach\n\t\t(example: Readonly<ExampleData>, index: Readonly<number>) => {\n\t\t\tconst exampleNum = index + INDEX_OFFSET;\n\n\t\t\t// Check violation patterns\n\t\t\texample.violations.forEach((code: Readonly<string>) => {\n\t\t\t\tconst normalized = normalizeCode(code);\n\t\t\t\tif (!violationPatterns.has(normalized)) {\n\t\t\t\t\tviolationPatterns.set(normalized, []);\n\t\t\t\t}\n\t\t\t\t// get() after set() always returns the array, never undefined\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- get() after set() always returns the array\n\t\t\t\tconst patternList = violationPatterns.get(normalized)!;\n\t\t\t\tpatternList.push(exampleNum);\n\t\t\t});\n\n\t\t\t// Check valid patterns\n\t\t\texample.valids.forEach((code: Readonly<string>) => {\n\t\t\t\tconst normalized = normalizeCode(code);\n\t\t\t\tif (!validPatterns.has(normalized)) {\n\t\t\t\t\tvalidPatterns.set(normalized, []);\n\t\t\t\t}\n\t\t\t\t// get() after set() always returns the array, never undefined\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- get() after set() always returns the array\n\t\t\t\tconst patternList = validPatterns.get(normalized)!;\n\t\t\t\tpatternList.push(exampleNum);\n\t\t\t});\n\t\t},\n\t);\n\n\t// Report duplicates\n\tcheckPatternDuplicates(violationPatterns, 'violation', warnings);\n\tcheckPatternDuplicates(validPatterns, 'valid', warnings);\n\n\tconst noErrors = errors.length === ZERO_ERRORS;\n\treturn {\n\t\tissues: errors,\n\t\tpassed: noErrors,\n\t\twarnings,\n\t};\n}\n", "/**\n * @file\n * Quality checks orchestration for PMD rule validation.\n */\nimport type {\n\tRuleMetadata,\n\tExampleData,\n\tValidationResult,\n} from '../types/index.js';\nimport { checkRuleMetadata } from './quality/checkRuleMetadata.js';\nimport { checkExamples } from './quality/checkExamples.js';\nimport { checkDuplicates } from './quality/checkDuplicates.js';\n\nconst MIN_ISSUES_COUNT = 0;\n\n/**\n * Main entry point for rule quality validation.\n * @param ruleMetadata - Rule metadata from XML.\n * @param examples - Array of parsed examples.\n * @returns Validation result with issues and warnings.\n */\nexport function runQualityChecks(\n\truleMetadata: Readonly<RuleMetadata>,\n\t// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types -- Array elements are accessed and iterated\n\texamples: readonly ExampleData[],\n): ValidationResult {\n\tconst issues: string[] = [];\n\tconst warnings: string[] = [];\n\n\t// Check rule metadata quality\n\tconst metadataResult = checkRuleMetadata(ruleMetadata);\n\tissues.push(...metadataResult.issues);\n\twarnings.push(...metadataResult.warnings);\n\n\t// Check example quality\n\tconst examplesResult = checkExamples(examples);\n\tissues.push(...examplesResult.issues);\n\twarnings.push(...examplesResult.warnings);\n\n\t// Check for duplicates\n\tconst duplicatesResult = checkDuplicates(examples);\n\tissues.push(...duplicatesResult.issues);\n\twarnings.push(...duplicatesResult.warnings);\n\n\treturn {\n\t\tissues: issues,\n\t\tpassed: issues.length === MIN_ISSUES_COUNT,\n\t\twarnings: warnings,\n\t};\n}\n", "/**\n * @file\n * Concurrency utility for limiting parallel execution.\n */\n\n/**\n * Limit the number of concurrent executions of async tasks.\n * @template T - The type of value returned by each task.\n * @param tasks - Array of functions that return promises.\n * @param maxConcurrency - Maximum number of tasks to run concurrently.\n * @returns Promise that resolves to array of results in the same order as input tasks.\n */\nexport async function limitConcurrency<T>(\n\ttasks: readonly (() => Promise<T>)[],\n\tmaxConcurrency: number,\n): Promise<T[]> {\n\t// Initialize results array with correct length\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Array created with correct length, values assigned before use\n\tconst results = new Array(tasks.length) as T[];\n\tconst executing: Promise<void>[] = [];\n\tlet index = 0;\n\n\tconst executeNext = async (): Promise<void> => {\n\t\tconst currentIndex = index++;\n\n\t\t/**\n\t\t * Safe due to scheduling logic: executeNext is called exactly tasks.length times,\n\t\t * so currentIndex is always a valid index into tasks/results.\n\t\t */\n\t\tconst task = tasks[currentIndex];\n\t\tif (!task) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tresults[currentIndex] = await task();\n\t\t} catch (error) {\n\t\t\tthrow error;\n\t\t}\n\t};\n\n\t// Start initial batch of tasks\n\tfor (let i = 0; i < Math.min(maxConcurrency, tasks.length); i++) {\n\t\texecuting.push(executeNext());\n\t}\n\n\t// Wait for each task to complete and start next ones\n\tfor (let i = 0; i < tasks.length; i++) {\n\t\tif (i >= maxConcurrency) {\n\t\t\tawait executing[i % maxConcurrency];\n\t\t\texecuting[i % maxConcurrency] = executeNext();\n\t\t}\n\t}\n\n\t// Wait for all remaining tasks\n\tawait Promise.all(executing);\n\n\treturn results;\n}\n", "/**\n * @file\n * Coverage tracking for XPath line coverage.\n */\n\nexport interface CoverageData {\n\tfilePath: string;\n\n\t/**\n\t * Line -> execution count.\n\t */\n xpathLines: Map<number, number>;\n \n\t/**\n\t * Line -> execution count.\n\t */\n componentLines: Map<number, number>; \n}\n\n/**\n * Tracks coverage data for a rule file during testing.\n */\nexport class CoverageTracker {\n\tprivate readonly coverageData: CoverageData;\n\n\tpublic constructor(ruleFilePath: string) {\n\t\tthis.coverageData = {\n\t\t\tcomponentLines: new Map(),\n\t\t\tfilePath: ruleFilePath,\n\t\t\txpathLines: new Map(),\n\t\t};\n\t}\n\n\t/**\n\t * Records that an XPath line was executed.\n\t * @param lineNumber - Line number in the XML file.\n\t */\n\tpublic recordXPathLine(lineNumber: number): void {\n\t\tconst INITIAL_COUNT = 0;\n\t\tconst INCREMENT = 1;\n\t\tconst current = this.coverageData.xpathLines.get(lineNumber) ?? INITIAL_COUNT;\n\t\tthis.coverageData.xpathLines.set(lineNumber, current + INCREMENT);\n\t}\n\n\t/**\n\t * Records that an XPath component line was executed.\n\t * @param lineNumber - Line number in the XML file.\n\t */\n\tpublic recordComponentLine(lineNumber: number): void {\n\t\tconst INITIAL_COUNT = 0;\n\t\tconst INCREMENT = 1;\n\t\tconst current =\n\t\t\tthis.coverageData.componentLines.get(lineNumber) ?? INITIAL_COUNT;\n\t\tthis.coverageData.componentLines.set(lineNumber, current + INCREMENT);\n\t}\n\n\t/**\n\t * Gets the coverage data.\n\t * @returns Coverage data for this rule file.\n\t */\n\tpublic getCoverageData(): Readonly<CoverageData> {\n\t\treturn this.coverageData;\n\t}\n}", "/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */\n\n/**\n * @file\n * LCOV format coverage report generation.\n */\nimport { writeFileSync, mkdirSync } from 'fs';\nimport { dirname } from 'path';\nimport type { CoverageData } from './trackCoverage.js';\n\n/**\n * Generates LCOV format coverage report.\n * @param coverageData - Array of coverage data for each file.\n * @param outputPath - Path to write the LCOV report.\n */\nexport function generateLcovReport(\n\tcoverageData: readonly CoverageData[],\n\toutputPath: string,\n): void {\n\tconst lcovContent: string[] = [];\n\n\tfor (const data of coverageData) {\n\t\t// SF:<source_file_path>\n\t\tlcovContent.push(`SF:${data.filePath}`);\n\n\t\t// DA:<line_number>,<execution_count> for XPath lines\n\t\tfor (const [lineNumber, executionCount] of data.xpathLines) {\n\t\t\tlcovContent.push(`DA:${String(lineNumber)},${String(executionCount)}`);\n\t\t}\n\n\t\t// DA:<line_number>,<execution_count> for component lines\n\t\tfor (const [lineNumber, executionCount] of data.componentLines) {\n\t\t\tlcovContent.push(`DA:${String(lineNumber)},${String(executionCount)}`);\n\t\t}\n\n\t\t// end_of_record\n\t\tlcovContent.push('end_of_record');\n\t}\n\n\t// Ensure output directory exists\n\tconst outputDir = dirname(outputPath);\n\tmkdirSync(outputDir, { recursive: true });\n\n\t// Write the LCOV file\n\tconst content = lcovContent.join('\\n') + '\\n';\n\twriteFileSync(outputPath, content, 'utf-8');\n}\n/* eslint-enable @typescript-eslint/prefer-readonly-parameter-types */"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["existsSync", "resolve", "readFileSync", "DOMParser", "readFileSync", "MIN_STRING_LENGTH", "MATCH_INDEX", "MIN_STRING_LENGTH", "MATCH_INDEX", "MIN_STRING_LENGTH", "MIN_STRING_LENGTH", "MATCH_INDEX", "readFileSync", "resolve", "DOMParser", "MIN_STRING_LENGTH", "resolve", "INDEX_OFFSET", "MIN_COUNT", "MIN_EXAMPLES_COUNT", "EMPTY_STRING", "readFileSync", "DOMParser", "INDEX_OFFSET", "NOT_FOUND_INDEX", "LINE_NUMBER_OFFSET", "writeFileSync", "resolve", "MIN_COUNT", "INDEX_OFFSET", "existsSync"]
|
|
7
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"bin": {
|
|
3
|
+
"test-pmd-rule": "./dist/test-pmd-rule.js"
|
|
4
|
+
},
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@xmldom/xmldom": "^0.8.11",
|
|
7
|
+
"tmp": "^0.2.5"
|
|
8
|
+
},
|
|
9
|
+
"devDependencies": {
|
|
10
|
+
"@eslint/js": "^9.39.2",
|
|
11
|
+
"@prettier/plugin-xml": "^3.4.2",
|
|
12
|
+
"@types/node": "^25.0.6",
|
|
13
|
+
"@types/tmp": "^0.2.6",
|
|
14
|
+
"@typescript-eslint/eslint-plugin": "^8.52.0",
|
|
15
|
+
"@typescript-eslint/parser": "^8.52.0",
|
|
16
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
17
|
+
"agent-docs": "^1.3.1",
|
|
18
|
+
"esbuild": "^0.27.2",
|
|
19
|
+
"eslint": "^9.39.2",
|
|
20
|
+
"eslint-plugin-import": "^2.32.0",
|
|
21
|
+
"eslint-plugin-jsdoc": "^62.0.0",
|
|
22
|
+
"eslint-plugin-sort-class-members": "^1.21.0",
|
|
23
|
+
"eslint-plugin-sort-keys": "^2.3.5",
|
|
24
|
+
"husky": "^9.1.7",
|
|
25
|
+
"lint-staged": "^16.2.7",
|
|
26
|
+
"prettier": "^3.7.4",
|
|
27
|
+
"tsx": "^4.21.0",
|
|
28
|
+
"typescript": "^5.9.3",
|
|
29
|
+
"typescript-eslint": "^8.52.0",
|
|
30
|
+
"vitest": "^4.0.16"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=25.0.0",
|
|
34
|
+
"pnpm": ">=10"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist"
|
|
38
|
+
],
|
|
39
|
+
"name": "test-pmd-rule",
|
|
40
|
+
"type": "module",
|
|
41
|
+
"version": "0.1.0",
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsx scripts/build.ts",
|
|
44
|
+
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\" \"*.md\"",
|
|
45
|
+
"format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\" \"*.md\" || echo 'Prettier check completed (some patterns may have no matches)'",
|
|
46
|
+
"lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" || echo 'ESLint completed (some patterns may have no matches)'",
|
|
47
|
+
"lint:fix": "eslint --fix \"src/**/*.ts\" \"tests/**/*.ts\" || echo 'ESLint fix completed (some patterns may have no matches)'",
|
|
48
|
+
"pre-commit": "pnpm format:check && pnpm lint && pnpm test:coverage",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"test:coverage": "vitest run --coverage",
|
|
51
|
+
"test:watch": "vitest",
|
|
52
|
+
"typecheck": "tsc --noEmit"
|
|
53
|
+
}
|
|
54
|
+
}
|