woaru
Version:
Universal Project Setup Autopilot - Analyze and automatically configure development tools for ANY programming language
141 lines • 6.02 kB
JavaScript
import { BaseAction } from './BaseAction.js';
import { t } from '../config/i18n.js';
import * as path from 'path';
export class EslintAction extends BaseAction {
name = 'eslint';
description = 'Install and configure ESLint for code linting';
async canExecute(projectPath) {
const packageJsonPath = path.join(projectPath, 'package.json');
const packageJson = await this.readJsonFile(packageJsonPath);
if (!packageJson)
return false;
// Check if eslint is already installed
const hasEslint = packageJson.devDependencies?.eslint || packageJson.dependencies?.eslint;
return !hasEslint;
}
async execute(projectPath, options) {
try {
const packageJsonPath = path.join(projectPath, 'package.json');
const eslintrcPath = path.join(projectPath, '.eslintrc.json');
if (options.dryRun) {
console.log(t('actions.eslint.dry_run_install'));
console.log(t('actions.eslint.dry_run_create_config'));
return true;
}
// Create backups
if (!options.skipBackup) {
await this.createBackup(packageJsonPath);
await this.createBackup(eslintrcPath);
}
// Detect project characteristics
const packageJson = await this.readJsonFile(packageJsonPath);
const hasNext = packageJson?.dependencies?.next || packageJson?.devDependencies?.next;
const hasReact = packageJson?.dependencies?.react || packageJson?.devDependencies?.react;
const hasTypeScript = await this.fileExists(path.join(projectPath, 'tsconfig.json'));
// Determine packages to install
const packages = ['eslint'];
if (hasNext) {
packages.push('eslint-config-next');
}
else if (hasReact) {
packages.push('@eslint/js', 'eslint-plugin-react', 'eslint-plugin-react-hooks');
}
if (hasTypeScript) {
packages.push('@typescript-eslint/eslint-plugin', '@typescript-eslint/parser');
}
// Install packages
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,
}));
}
// Create ESLint configuration
const eslintConfig = {
env: {
browser: true,
es2021: true,
node: true,
},
extends: [],
};
if (hasNext) {
eslintConfig.extends.push('next/core-web-vitals');
}
else {
eslintConfig.extends.push('eslint:recommended');
if (hasReact) {
eslintConfig.extends.push('plugin:react/recommended', 'plugin:react-hooks/recommended');
eslintConfig.plugins = ['react', 'react-hooks'];
eslintConfig.settings = {
react: {
version: 'detect',
},
};
}
}
if (hasTypeScript) {
eslintConfig.extends.push('@typescript-eslint/recommended');
eslintConfig.parser = '@typescript-eslint/parser';
eslintConfig.plugins = [
...(eslintConfig.plugins || []),
'@typescript-eslint',
];
eslintConfig.parserOptions = {
ecmaVersion: 'latest',
sourceType: 'module',
...(hasReact && { ecmaFeatures: { jsx: true } }),
};
}
eslintConfig.rules = {
'no-unused-vars': 'warn',
'no-console': 'warn',
...(hasReact && { 'react/react-in-jsx-scope': 'off' }),
};
await this.writeJsonFile(eslintrcPath, eslintConfig);
// Add scripts to package.json
if (packageJson) {
if (!packageJson.scripts) {
packageJson.scripts = {};
}
packageJson.scripts['lint'] = 'eslint . --ext .js,.jsx,.ts,.tsx';
packageJson.scripts['lint:fix'] =
'eslint . --ext .js,.jsx,.ts,.tsx --fix';
await this.writeJsonFile(packageJsonPath, packageJson);
}
console.log(t('actions.eslint.setup_success'));
return true;
}
catch (error) {
console.error(t('actions.eslint.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 created files
const filesToRemove = ['.eslintrc.json'];
for (const file of filesToRemove) {
const filePath = path.join(projectPath, file);
if (await fs.pathExists(filePath)) {
await fs.remove(filePath);
}
}
console.log(t('actions.eslint.rollback_success'));
return true;
}
catch (error) {
console.error(t('actions.eslint.rollback_failed'), error);
return false;
}
}
}
//# sourceMappingURL=EslintAction.js.map