@regele/devtools
Version:
A collection of developer utilities for code processing and text analysis
184 lines (158 loc) • 6.52 kB
JavaScript
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);
}