UNPKG

@regele/devtools

Version:

A collection of developer utilities for code processing and text analysis

184 lines (158 loc) 6.52 kB
#!/usr/bin/env node try { // Direct implementation of beautification const fs = require('fs'); const path = require('path'); const { Command } = require('commander'); const chalk = require('chalk'); const ora = require('ora'); const fg = require('fast-glob'); const prettier = require('prettier'); // Ensure ora is properly imported const createSpinner = ora.default || ora; // Create the beautify command const command = new Command('devtools-beautify'); command .description('Format and beautify code files') .argument('<patterns...>', 'File patterns to beautify (e.g., "src/**/*.js")') .option('--config <path>', 'Path to prettier config') .option('--ignore <pattern>', 'Files to ignore (comma-separated)', value => value.split(',').map(item => item.trim())) .option('--write', 'Write changes to files', true) .option('--silent', 'Suppress output', false) .action(async (patterns, options) => { const silent = options.silent; try { // Start spinner const spinner = createSpinner({ text: 'Beautifying files...', color: 'cyan', spinner: 'dots' }).start(); // Process each file let results = []; // Default ignore patterns const defaultIgnore = [ '**/node_modules/**', '**/dist/**', '**/build/**', '**/.git/**', '**/package-lock.json', '**/yarn.lock', '**/*.min.js', '**/*.min.css', '**/*.bundle.js' ]; // Combine with user-provided ignore patterns const ignorePatterns = options.ignore ? [...defaultIgnore, ...options.ignore] : defaultIgnore; // Normalize patterns for Windows paths const normalizedPatterns = patterns.map(pattern => { // Convert Windows backslashes to forward slashes for glob return pattern.replace(/\\/g, '/'); }); // Find all files matching the patterns const files = await fg(normalizedPatterns, { ignore: ignorePatterns, onlyFiles: true, absolute: true, // Use absolute paths to handle Windows paths better followSymbolicLinks: false // Don't follow symlinks to avoid circular references }); // Load prettier config if provided let prettierConfig = {}; if (options.config) { try { const configPath = path.resolve(options.config); prettierConfig = await prettier.resolveConfig(configPath) || {}; } catch (error) { console.error(`Error loading prettier config: ${error.message}`); } } for (const file of files) { try { // Read the file const content = fs.readFileSync(file, 'utf8'); const originalSize = content.length; // Determine parser based on file extension const ext = path.extname(file).toLowerCase(); let parser; if (['.js', '.cjs', '.mjs'].includes(ext)) parser = 'babel'; else if (['.jsx'].includes(ext)) parser = 'babel'; else if (['.ts'].includes(ext)) parser = 'typescript'; else if (['.tsx'].includes(ext)) parser = 'typescript'; else if (['.json'].includes(ext)) parser = 'json'; else if (['.css'].includes(ext)) parser = 'css'; else if (['.scss', '.sass'].includes(ext)) parser = 'scss'; else if (['.less'].includes(ext)) parser = 'less'; else if (['.html', '.htm', '.xhtml'].includes(ext)) parser = 'html'; else if (['.xml', '.svg'].includes(ext)) parser = 'xml'; else if (['.md', '.markdown'].includes(ext)) parser = 'markdown'; else if (['.yaml', '.yml'].includes(ext)) parser = 'yaml'; else if (['.graphql', '.gql'].includes(ext)) parser = 'graphql'; else { // Try to infer parser from content if (content.includes('<?php')) parser = 'php'; else if (content.includes('<!DOCTYPE html>') || content.includes('<html>')) parser = 'html'; else if (content.match(/^import|export|class|function/m)) parser = 'babel'; else if (content.match(/^const|let|var/m)) parser = 'babel'; else parser = 'babel'; // Default to babel } // Format the content const formattedContent = await prettier.format(content, { ...prettierConfig, parser, filepath: file }); const newSize = formattedContent.length; const diffSize = newSize - originalSize; const diffPercentage = originalSize > 0 ? (diffSize / originalSize) * 100 : 0; // Write the file if requested if (options.write) { fs.writeFileSync(file, formattedContent, 'utf8'); } results.push({ path: file, success: true, originalSize, newSize, diffSize, diffPercentage }); } catch (error) { results.push({ path: file, success: false, error: error.message }); } } // Stop spinner spinner.succeed(`Processed ${results.length} files`); // Log results if (!silent) { const successCount = results.filter(r => r.success).length; const errorCount = results.length - successCount; console.log('\n' + chalk.bold('Results:')); console.log(`${chalk.green(`${successCount} files`)} processed successfully`); if (errorCount > 0) { console.log(`${chalk.red(`${errorCount} files`)} failed to process`); console.log('\n' + chalk.bold('Errors:')); results .filter(r => !r.success) .forEach(result => { console.log(` ${chalk.red('✗')} ${result.path}: ${result.error}`); }); } console.log('\n' + chalk.bold.green(`Beautification completed.`)); } } catch (error) { console.error(chalk.red('✗ ') + error.message); process.exit(1); } }); // Execute the command command.parse(process.argv); } catch (error) { console.error('Error loading command:', error.message); process.exit(1); }