frontend-standards-checker
Version:
A comprehensive frontend standards validation tool with TypeScript support
265 lines ⢠12.1 kB
JavaScript
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';
const __filename = fileURLToPath(import.meta.url);
const __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