sfcoe-ailabs
Version:
AI-powered code review tool with static analysis integration for comprehensive code quality assessment.
95 lines (94 loc) • 3.76 kB
JavaScript
import path from 'node:path';
import * as fs from 'node:fs';
import { execFileSync } from 'node:child_process';
import { Logger } from '../../observability/index.js';
import { COMMAND_DEFAULTS } from '../../utils/constants.js';
import SCATProvider from './scatProvider.js';
export default class PMDCodeAnalyzer extends SCATProvider {
fileList;
/**
* Creates a new PMD Code Analyzer instance
*
* @param scanDirectory - The directory to scan for non-Salesforce code
* @param configFile - Configuration file path for PMD
* @param fileList - Array of specific files to analyze
*/
constructor(scanDirectory, configFile, fileList) {
const sarifFileName = path.join(scanDirectory, COMMAND_DEFAULTS.DEFAULT_SARIF_FILE_NAME);
super(scanDirectory, configFile, sarifFileName);
this.fileList = fileList;
}
/**
* Runs the PMD analyzer on the specified files
*
* @param scanDirectory - Optional override for the scan directory
* @returns Promise that resolves to the SARIF analysis results
*/
async run(scanDirectory) {
const directoryToScan = scanDirectory ?? this.getScanDirectory();
if (this.fileList.length === 0) {
Logger.debug('No files to analyze with PMD');
// Return empty SARIF structure
return {
version: '2.1.0',
runs: [],
};
}
// Create a temporary file with the list of files to analyze
const fileListPath = path.join(directoryToScan, 'pmd-files-only.txt');
try {
// Write file list to temporary file
fs.writeFileSync(fileListPath, this.fileList.join('\n'));
// Use PMD rulesets from config file
const rulesets = this.getConfigFile();
// Validate inputs
if (!fs.existsSync(fileListPath)) {
throw new Error(`File list path does not exist: ${fileListPath}`);
}
if (!fs.existsSync(rulesets)) {
throw new Error(`Rulesets file does not exist: ${rulesets}`);
}
Logger.debug('Running PMD command for non-Salesforce files: \n ' +
`pmd check --file-list "${fileListPath}" --format sarif --rulesets "${rulesets}"`);
let result;
try {
result = execFileSync('pmd', [
'check',
'--file-list',
fileListPath,
'--format',
'sarif',
'--rulesets',
rulesets,
], {
cwd: directoryToScan,
encoding: 'utf8',
maxBuffer: 1024 * 1024 * 10, // 10MB buffer
});
}
catch (error) {
// PMD returns exit code 4 when violations are found, which is expected
const execError = error;
if (execError.status === 4 && execError.stdout) {
result = execError.stdout;
}
else {
Logger.error('PMD command failed:', error);
throw error;
}
}
// Write the SARIF result to the expected output file
fs.writeFileSync(this.getSarifFileName(), result, 'utf-8');
}
catch (error) {
Logger.error('PMD analysis execution failed:', error);
}
finally {
// Ensure the temporary file is removed even if an error occurs
if (fs.existsSync(fileListPath)) {
fs.unlinkSync(fileListPath);
}
}
return this.getSarifFile();
}
}