ccguard
Version:
Automated enforcement of net-negative LOC, complexity constraints, and quality standards for Claude code
106 lines • 3.71 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CommandFormatter = void 0;
const BaseFormatter_1 = require("./formatters/BaseFormatter");
const child_process_1 = require("child_process");
/**
* Formatter that executes shell commands to format code
*/
class CommandFormatter extends BaseFormatter_1.BaseFormatter {
config;
cache = new Map();
constructor(config) {
super();
this.config = config;
}
/**
* Get supported extensions from config
*/
getSupportedExtensions() {
return Object.keys(this.config.commands);
}
/**
* Format content using the configured command
*/
async format(content, filePath) {
const extension = this.getFileExtension(filePath);
const commandConfig = this.config.commands[extension];
if (!commandConfig) {
// No formatter configured for this file type
return content;
}
// Check cache
const cacheKey = `${filePath}:${content.length}:${content.substring(0, 100)}`;
const cached = this.cache.get(cacheKey);
if (cached) {
return cached;
}
try {
const formatted = await this.executeFormatter(content, filePath, commandConfig);
// Cache the result
this.cache.set(cacheKey, formatted);
// Limit cache size
if (this.cache.size > 100) {
const firstKey = this.cache.keys().next().value;
if (firstKey !== undefined) {
this.cache.delete(firstKey);
}
}
return formatted;
}
catch (error) {
if (this.config.fallbackOnError !== false) {
// Log error but continue with unformatted content
console.warn(`Formatter failed for ${filePath}:`, error);
return content;
}
throw error;
}
}
/**
* Execute the formatter command
*/
async executeFormatter(content, filePath, commandConfig) {
return new Promise((resolve, reject) => {
// Parse command safely without shell
const parts = commandConfig.command.split(' ');
const cmd = parts[0];
const args = parts.slice(1).map(arg => arg === '{filepath}' ? filePath : arg).concat(commandConfig.args || []);
const child = (0, child_process_1.spawn)(cmd, args, {
timeout: this.config.timeout || 5000,
});
let stdout = '';
let stderr = '';
child.stdout.on('data', (data) => {
stdout += data.toString();
});
child.stderr.on('data', (data) => {
stderr += data.toString();
});
child.on('error', (error) => {
reject(new Error(`Formatter error: ${error.message}`));
});
child.on('close', (code) => {
if (code !== 0) {
reject(new Error(`Formatter exited with code ${code}: ${stderr}`));
}
else {
resolve(stdout || content); // Fallback to original if no output
}
});
// Send content via stdin if configured (default: true)
if (commandConfig.stdin !== false) {
child.stdin.write(content);
child.stdin.end();
}
});
}
/**
* Clear the formatting cache
*/
clearCache() {
this.cache.clear();
}
}
exports.CommandFormatter = CommandFormatter;
//# sourceMappingURL=CommandFormatter.js.map