woaru
Version:
Universal Project Setup Autopilot - Analyze and automatically configure development tools for ANY programming language
121 lines • 5.34 kB
JavaScript
import { BaseAction } from './BaseAction.js';
import { t } from '../config/i18n.js';
import * as path from 'path';
export class HuskyAction extends BaseAction {
name = 'husky';
description = 'Install and configure Husky with lint-staged for Git hooks';
async canExecute(projectPath) {
const packageJsonPath = path.join(projectPath, 'package.json');
const packageJson = await this.readJsonFile(packageJsonPath);
if (!packageJson)
return false;
// Check if husky is already installed
const hasHusky = packageJson.devDependencies?.husky || packageJson.dependencies?.husky;
return !hasHusky;
}
async execute(projectPath, options) {
try {
const packageJsonPath = path.join(projectPath, 'package.json');
const huskyDir = path.join(projectPath, '.husky');
if (options.dryRun) {
console.log(t('actions.husky.dry_run_install'));
console.log(t('actions.husky.dry_run_create_hooks'));
console.log(t('actions.husky.dry_run_configure'));
return true;
}
// Create backups
if (!options.skipBackup) {
await this.createBackup(packageJsonPath);
}
// Install packages
const packages = ['husky', 'lint-staged'];
const installResult = await this.runCommand('npm', ['install', '--save-dev', ...packages], projectPath);
if (!installResult.success) {
throw new Error(t('actions.failed_to_install_packages', {
output: installResult.output,
}));
}
// Initialize husky
const huskyInitResult = await this.runCommand('npx', ['husky', 'init'], projectPath);
if (!huskyInitResult.success) {
throw new Error(`Failed to initialize husky: ${huskyInitResult.output}`);
}
// Read package.json to detect available scripts
const packageJson = await this.readJsonFile(packageJsonPath);
const hasLint = packageJson?.scripts?.lint;
const hasFormat = packageJson?.scripts?.format;
const hasTypeCheck = packageJson?.scripts?.['type-check'] || packageJson?.scripts?.tsc;
// Create pre-commit hook
const preCommitPath = path.join(huskyDir, 'pre-commit');
const preCommitContent = '#!/usr/bin/env sh\n. "$(dirname -- "$0")/_/husky.sh"\n\nnpx lint-staged\n';
const fs = await import('fs-extra');
await fs.writeFile(preCommitPath, preCommitContent);
await this.runCommand('chmod', ['+x', preCommitPath], projectPath);
// Configure lint-staged
const lintStagedConfig = {};
// Configure for TypeScript/JavaScript files
const jsPattern = '*.{js,jsx,ts,tsx}';
const commands = [];
if (hasLint) {
commands.push('eslint --fix');
}
if (hasFormat) {
commands.push('prettier --write');
}
if (commands.length > 0) {
lintStagedConfig[jsPattern] = commands;
}
// Add type-checking if available
if (hasTypeCheck) {
lintStagedConfig['*.{ts,tsx}'] = [
...(lintStagedConfig['*.{ts,tsx}'] || commands),
'tsc --noEmit',
];
}
// Configure for other file types
if (hasFormat) {
lintStagedConfig['*.{json,md,yml,yaml}'] = ['prettier --write'];
}
// Add lint-staged configuration to package.json
if (packageJson) {
packageJson['lint-staged'] = lintStagedConfig;
// Add prepare script for husky
if (!packageJson.scripts) {
packageJson.scripts = {};
}
packageJson.scripts.prepare = 'husky';
await this.writeJsonFile(packageJsonPath, packageJson);
}
console.log(t('actions.husky.setup_success'));
return true;
}
catch (error) {
console.error(t('actions.husky.setup_failed'), error);
return false;
}
}
async rollback(projectPath) {
try {
const fs = await import('fs-extra');
const glob = await import('glob');
// Find backup files
const backupFiles = await glob.glob(path.join(projectPath, '*.woaru-backup-*'));
for (const backupFile of backupFiles) {
const originalFile = backupFile.replace(/\.woaru-backup-\d+$/, '');
await fs.move(backupFile, originalFile, { overwrite: true });
}
// Remove .husky directory
const huskyDir = path.join(projectPath, '.husky');
if (await fs.pathExists(huskyDir)) {
await fs.remove(huskyDir);
}
console.log(t('actions.husky.rollback_success'));
return true;
}
catch (error) {
console.error(t('actions.husky.rollback_failed'), error);
return false;
}
}
}
//# sourceMappingURL=HuskyAction.js.map