UNPKG

frontend-standards-checker

Version:

A comprehensive frontend standards validation tool with TypeScript support

274 lines • 12.3 kB
#!/usr/bin/env node import { Command } from 'commander'; import chalk from 'chalk'; import { readFileSync } from 'fs'; import { dirname, join } from 'path'; import { fileURLToPath } from 'url'; import { FrontendStandardsChecker } from '../src/index.js'; let __dirname; // Use different approaches for __dirname depending on environment if (process.env.NODE_ENV === 'test') { // Use a mock path during tests __dirname = '/Users/test/cg-frontend-validator/bin'; } else { // Use import.meta.url in production const __filename = fileURLToPath(import.meta.url); __dirname = dirname(__filename); } // Function to find package.json in various possible locations function findPackageJson() { const possibleLocations = [ join(__dirname, '../package.json'), // Local development join(__dirname, '../../package.json'), // Installed as a dependency join(process.cwd(), 'package.json'), // Current working directory join(__dirname, '../../../package.json'), // Another possible location in node_modules ]; for (const location of possibleLocations) { try { return JSON.parse(readFileSync(location, 'utf8')); } catch (e) { // Try the next location } } // If no package.json is found, use default values return { version: '0.0.12', name: 'frontend-standards-checker', }; } const packageJson = findPackageJson(); import { writeFileSync, existsSync, appendFileSync, copyFileSync } from 'fs'; const program = new Command(); program .name('frontend-standards-checker') .description('A comprehensive frontend standards validation tool with TypeScript support') .version(packageJson.version); // Main validation command program .command('check') .description('Run standards validation') .option('-z, --zones <zones...>', 'Specific zones to check (space-separated)', []) .option('-c, --config <path>', 'Path to custom configuration file') .option('-v, --verbose', 'Show verbose output') .option('--debug', 'Show debug information about file scanning') .option('--skip-structure', 'Skip directory structure validation') .option('--skip-naming', 'Skip naming convention validation') .option('--skip-content', 'Skip content validation') .option('--only-changed-files', 'Only check files that are staged for commit (default: true)') .option('--all-files', 'Check all files in the project, not just staged files (overrides config)') .option('--pipeline-mode', 'Force pipeline mode for git file detection (useful in CI/CD environments)') .action(async (options) => { try { console.log(chalk.blue(`šŸ” Frontend Standards Checker v${packageJson.version}`)); console.log(chalk.gray('Analyzing your frontend project with TypeScript support...\n')); const checkerOptions = { zones: options.zones || [], config: options.config || null, verbose: options.verbose || false, debug: options.debug || false, skipStructure: options.skipStructure || false, skipNaming: options.skipNaming || false, skipContent: options.skipContent || false, pipelineMode: options.pipelineMode || false, }; // Handle onlyChangedFiles logic with precedence // --all-files flag overrides both config and --only-changed-files if (options.allFiles) { checkerOptions.onlyChangedFiles = false; } else if (typeof options.onlyChangedFiles === 'boolean') { checkerOptions.onlyChangedFiles = options.onlyChangedFiles; } // If neither flag is provided, let the config or default handle it const checker = new FrontendStandardsChecker(checkerOptions); const result = await checker.run(); const exitCode = result.success ? 0 : 1; if (result.success) { console.log(chalk.green('\nāœ… All validations passed!')); } else { console.log(chalk.red(`\nāŒ Found ${result.totalErrors} errors`)); if (result.totalWarnings > 0) { console.log(chalk.yellow(`āš ļø Found ${result.totalWarnings} warnings`)); } } process.exit(exitCode); } catch (error) { console.error(chalk.red('šŸ’„ Error running validation:')); console.error(error); process.exit(1); } }); // init command to add scripts and update .gitignore program .command('init') .description('Add standards script to package.json, update .gitignore, and copy guide/config files') .action(() => { const cwd = process.cwd(); const pkgPath = join(cwd, 'package.json'); const gitignorePath = join(cwd, '.gitignore'); // 1. Copy guide and configuration files if they don't exist const filesToCopy = [ { src: join(__dirname, '../../checkFrontendStandards.COMPLETE-GUIDE.md'), dest: join(cwd, 'checkFrontendStandards.COMPLETE-GUIDE.md'), label: 'Complete Guide', }, { src: join(__dirname, '../../checkFrontendStandards.config.mjs'), dest: join(cwd, 'checkFrontendStandards.config.mjs'), label: 'Configuration File', }, ]; for (const file of filesToCopy) { try { if (!existsSync(file.dest)) { copyFileSync(file.src, file.dest); console.log(chalk.green(`āœ… ${file.label} copied: ${file.dest}`)); } else { console.log(chalk.gray(`ā„¹ļø ${file.label} already exists: ${file.dest}`)); } } catch (e) { console.error(chalk.red(`āŒ Could not copy ${file.label}:`), e); } } // 2. Update package.json try { const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')); if (!pkg.scripts) pkg.scripts = {}; if (!pkg.scripts['standards']) { pkg.scripts['standards'] = 'frontend-standards-checker check'; console.log(chalk.green('āœ… Script "standards" added to package.json.')); } else { console.log(chalk.yellow('ā„¹ļø Script "standards" already exists in package.json.')); } writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n'); } catch (e) { console.error(chalk.red('āŒ Could not update package.json:'), e); } // 3. Update .gitignore const ignoreList = ['logs-standards-validations/']; try { let gitignoreContent = ''; if (existsSync(gitignorePath)) { gitignoreContent = readFileSync(gitignorePath, 'utf8'); } let added = false; for (const item of ignoreList) { if (!gitignoreContent.split('\n').includes(item)) { appendFileSync(gitignorePath, (gitignoreContent && !gitignoreContent.endsWith('\n') ? '\n' : '') + item + '\n'); added = true; console.log(chalk.green(`āœ… Added to .gitignore: ${item}`)); } else { console.log(chalk.gray(`ā„¹ļø Already exists in .gitignore: ${item}`)); } } if (!added) { console.log(chalk.yellow('ā„¹ļø All items were already in .gitignore.')); } } catch (e) { console.error(chalk.red('āŒ Could not update .gitignore:'), e); } // 4. Add Husky pre-commit hook (only if the environment is initialized) const huskyDir = join(cwd, '.husky'); const huskyPreCommit = join(huskyDir, 'pre-commit'); const huskySh = join(huskyDir, '_', 'husky.sh'); try { if (existsSync(huskySh)) { let preCommitContent = ''; if (existsSync(huskyPreCommit)) { preCommitContent = readFileSync(huskyPreCommit, 'utf8'); const usesYarn = existsSync(join(cwd, 'yarn.lock')); const basicCmd = usesYarn ? 'yarn standards' : 'npm run standards'; const robustCmd = usesYarn ? `echo "šŸ” Running frontend standards validation..." && yarn standards || { echo "āŒ Frontend standards validation failed"; exit 1; }` : `echo "šŸ” Running frontend standards validation..." && npm run standards || { echo "āŒ Frontend standards validation failed"; exit 1; }`; // Check if it already has the robust version const hasRobustVersion = /echo.*frontend standards.*&&.*(yarn|npm run) standards.*\|\|/i.test(preCommitContent); if (hasRobustVersion) { console.log(chalk.gray('ā„¹ļø The pre-commit hook already contains the robust version of the standards command.')); } else if (preCommitContent.includes(basicCmd)) { const lines = preCommitContent.split('\n'); let wasUpdated = false; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line && line.trim() === basicCmd) { lines[i] = robustCmd; wasUpdated = true; console.log(chalk.green(`āœ… Updated '${basicCmd}' version`)); break; } } if (wasUpdated) { writeFileSync(huskyPreCommit, lines.join('\n'), { mode: 0o755 }); } } else { // Command does not exist, add it as a robust version const marker = 'echo "Pre-commit hooks completed"'; if (preCommitContent.includes(marker)) { // Insert before the marker const lines = preCommitContent.split('\n'); const idx = lines.findIndex((line) => line.trim() === marker); if (idx !== -1) { lines.splice(idx, 0, robustCmd); writeFileSync(huskyPreCommit, lines.join('\n'), { mode: 0o755, }); console.log(chalk.green(`āœ… Inserted command before '${marker}' in .husky/pre-commit`)); } else { appendFileSync(huskyPreCommit, `\n${robustCmd}\n`); console.log(chalk.green(`āœ… Inserted command before '${marker}' in .husky/pre-commit`)); } } else { appendFileSync(huskyPreCommit, `\n${robustCmd}\n`); console.log(chalk.green(`āœ… Inserted command before '${marker}' in .husky/pre-commit`)); } } } else { const usesYarn = existsSync(join(cwd, 'yarn.lock')); const cmd = usesYarn ? 'yarn standards' : 'npm run standards'; const script = `#!/bin/sh\n. "$(dirname "$0")/_/husky.sh"\n${cmd}\n`; writeFileSync(huskyPreCommit, script, { mode: 0o755 }); console.log(chalk.green(`āœ… .husky/pre-commit created with '${cmd}'`)); } } else { console.log(chalk.yellow('āš ļø Husky is not initialized in this project. If you want to use hooks, run "npx husky install" first.')); } } catch (e) { console.error(chalk.red('āŒ Could not create or update Husky pre-commit:'), e); } // Final message console.log(chalk.blue('\nšŸŽ‰ Configuration complete. You can use "yarn standards" or "npm run standards" to validate your project.')); }); // Handle unknown commands program.on('command:*', function (operands) { console.error(chalk.red(`Unknown command: ${operands[0]}`)); console.log(chalk.yellow('Use --help to see available commands')); process.exit(1); }); // Parse command line arguments program.parse(process.argv); // Show help if no arguments provided if (!process.argv.slice(2).length) { program.outputHelp(); } //# sourceMappingURL=cli.js.map