image-asset-manager
Version:
A comprehensive image asset management tool for frontend projects
463 lines (461 loc) โข 20.1 kB
JavaScript
#!/usr/bin/env node
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const commander_1 = require("commander");
const fs_1 = require("fs");
const ImageAssetManager_1 = require("./core/ImageAssetManager");
const config_1 = require("./config");
const progress_1 = require("./utils/progress");
const cli_validator_1 = require("./utils/cli-validator");
const program = new commander_1.Command();
const packageJson = require("../package.json");
program
.name("image-asset-manager")
.description("๐ผ๏ธ A comprehensive image asset management tool for frontend projects")
.version(packageJson.version)
.helpOption("-h, --help", "Display help information")
.addHelpText("after", `
Examples:
$ image-asset-manager # Start with default settings
$ image-asset-manager ./my-project # Scan specific project
$ image-asset-manager --optimize # Start with optimization enabled
$ image-asset-manager scan --json # Scan and output JSON format
$ image-asset-manager serve -p 8080 # Start web interface on port 8080
$ image-asset-manager optimize --quality 90 # Optimize with custom quality
$ image-asset-manager generate --framework vue # Generate Vue components
Configuration:
Create image-asset.config.js or add "imageAssetManager" section to package.json
Use --init to generate example configuration file
For more information, visit: https://github.com/your-repo/image-asset-manager
`);
// ไธปๅฝไปค - ๅฏๅจๅฎๆดๅ่ฝ
program
.argument("[project-path]", "Path to the project directory", process.cwd())
.option("-c, --config <config>", "Path to configuration file")
.option("-e, --exclude <patterns...>", "Patterns to exclude from scanning")
.option("-i, --include <patterns...>", "Patterns to include in scanning")
.option("--no-watch", "Disable file watching")
.option("--optimize", "Enable automatic optimization")
.option("--silent", "Suppress non-error output")
.option("--verbose", "Show detailed output")
.action(async (projectPath, options) => {
const startTime = Date.now();
const spinner = new progress_1.SpinnerDisplay();
try {
if (!options.silent) {
spinner.start("Initializing Image Asset Manager...");
}
const configManager = new config_1.ConfigManager(options.config);
const cliOptions = configManager.toCLIOptions({
projectPath: projectPath || process.cwd(),
config: options.config,
exclude: options.exclude,
include: options.include,
watch: options.watch !== false,
optimize: options.optimize || false,
});
const manager = new ImageAssetManager_1.ImageAssetManager(configManager);
if (!options.silent) {
spinner.stop();
console.log(`๐ Starting Image Asset Manager v${packageJson.version}`);
console.log(`๐ Project: ${cliOptions.projectPath}`);
console.log(`๐ Port: ${cliOptions.port}`);
console.log(`๐ Watch: ${cliOptions.watch ? "โ
" : "โ"}`);
console.log(`โก Optimize: ${cliOptions.optimize ? "โ
" : "โ"}`);
console.log("");
}
await manager.start(cliOptions);
if (!options.silent) {
const duration = (0, progress_1.formatDuration)(Date.now() - startTime);
console.log(`โ
Started successfully in ${duration}`);
}
}
catch (error) {
spinner.stop();
console.error("โ Error starting image asset manager:", error);
if (options.verbose && error instanceof Error) {
console.error("Stack trace:", error.stack);
}
process.exit(1);
}
});
// ๆซๆๅฝไปค
program
.command("scan")
.description("๐ Scan project for image assets")
.argument("[project-path]", "Path to the project directory", process.cwd())
.option("-c, --config <config>", "Path to configuration file")
.option("-e, --exclude <patterns...>", "Patterns to exclude from scanning")
.option("-i, --include <patterns...>", "Patterns to include in scanning")
.option("--json", "Output results in JSON format")
.option("--csv", "Output results in CSV format")
.option("--duplicates", "Show duplicate files only")
.option("--unused", "Show unused files only")
.option("--stats", "Show project statistics")
.option("--silent", "Suppress non-error output")
.option("--verbose", "Show detailed output")
.addHelpText("after", `
Examples:
$ image-asset-manager scan # Basic scan
$ image-asset-manager scan --json # Output JSON
$ image-asset-manager scan --duplicates # Find duplicates
$ image-asset-manager scan --unused # Find unused images
$ image-asset-manager scan --stats # Show statistics
`)
.action(async (projectPath, options) => {
const startTime = Date.now();
const spinner = new progress_1.SpinnerDisplay();
try {
if (!options.silent) {
spinner.start("Scanning project for image assets...");
}
const configManager = new config_1.ConfigManager(options.config);
const cliOptions = configManager.toCLIOptions({
projectPath: projectPath || process.cwd(),
exclude: options.exclude || undefined,
include: options.include || undefined,
watch: false,
optimize: false,
});
// ้ช่ฏ้้กน
const validator = new cli_validator_1.CLIValidator(configManager);
const scanOptions = {
outputFormat: options.json
? "json"
: options.csv
? "csv"
: "table",
showDuplicates: options.duplicates,
showUnused: options.unused,
showStats: options.stats,
silent: options.silent,
verbose: options.verbose,
};
const validationResult = validator.validateScanOptions(cliOptions, scanOptions);
if (!(0, cli_validator_1.displayValidationResult)(validationResult, options.silent)) {
process.exit(1);
}
const manager = new ImageAssetManager_1.ImageAssetManager(configManager);
if (!options.silent) {
spinner.stop();
console.log(`๐ Scanning: ${cliOptions.projectPath}`);
}
await manager.scan(cliOptions, scanOptions);
if (!options.silent) {
const duration = (0, progress_1.formatDuration)(Date.now() - startTime);
console.log(`โ
Scan completed in ${duration}`);
}
}
catch (error) {
spinner.stop();
console.error("โ Error scanning project:", error);
if (options.verbose && error instanceof Error) {
console.error("Stack trace:", error.stack);
}
process.exit(1);
}
});
// ๆๅกๅฝไปค
program
.command("serve")
.description("๐ Start the web interface")
.argument("[project-path]", "Path to the project directory", process.cwd())
.option("-p, --port <port>", "Port for the web server", "3000")
.option("-c, --config <config>", "Path to configuration file")
.option("--host <host>", "Host to bind the server", "localhost")
.option("--no-cors", "Disable CORS")
.option("--no-watch", "Disable file watching")
.option("--auth <token>", "Enable authentication with token")
.option("--ssl-cert <cert>", "SSL certificate file")
.option("--ssl-key <key>", "SSL private key file")
.option("--silent", "Suppress non-error output")
.option("--verbose", "Show detailed output")
.addHelpText("after", `
Examples:
$ image-asset-manager serve # Start on localhost:3000
$ image-asset-manager serve -p 8080 # Custom port
$ image-asset-manager serve --host 0.0.0.0 # Bind to all interfaces
$ image-asset-manager serve --auth secret123 # Enable authentication
`)
.action(async (projectPath, options) => {
const startTime = Date.now();
const spinner = new progress_1.SpinnerDisplay();
try {
if (!options.silent) {
spinner.start("Starting web server...");
}
const configManager = new config_1.ConfigManager(options.config);
const cliOptions = configManager.toCLIOptions({
projectPath: projectPath || process.cwd(),
port: parseInt(options.port, 10),
watch: options.watch !== false,
optimize: false,
});
const manager = new ImageAssetManager_1.ImageAssetManager(configManager);
if (!options.silent) {
spinner.stop();
console.log(`๐ Starting web server...`);
console.log(`๐ Project: ${cliOptions.projectPath}`);
console.log(`๐ Address: http://${options.host || "localhost"}:${cliOptions.port}`);
console.log(`๐ Watch: ${cliOptions.watch ? "โ
" : "โ"}`);
if (options.auth) {
console.log(`๐ Auth: โ
(token required)`);
}
console.log("");
}
await manager.serve(cliOptions, {
host: options.host,
cors: options.cors !== false,
auth: options.auth,
ssl: options.sslCert && options.sslKey
? {
cert: options.sslCert,
key: options.sslKey,
}
: undefined,
silent: options.silent,
verbose: options.verbose,
});
if (!options.silent) {
const duration = (0, progress_1.formatDuration)(Date.now() - startTime);
console.log(`โ
Server started in ${duration}`);
}
}
catch (error) {
spinner.stop();
console.error("โ Error starting web server:", error);
if (options.verbose && error instanceof Error) {
console.error("Stack trace:", error.stack);
}
process.exit(1);
}
});
// ไผๅๅฝไปค
program
.command("optimize")
.description("โก Optimize image assets")
.argument("[project-path]", "Path to the project directory", process.cwd())
.option("-c, --config <config>", "Path to configuration file")
.option("-o, --output <dir>", "Output directory for optimized images")
.option("-q, --quality <quality>", "Image quality (1-100)", "80")
.option("--png-quality <quality>", "PNG quality (1-100)")
.option("--jpg-quality <quality>", "JPG quality (1-100)")
.option("--webp-quality <quality>", "WebP quality (1-100)")
.option("--no-progressive", "Disable progressive encoding")
.option("--no-keep-original", "Don't keep original files")
.option("--batch-size <size>", "Batch processing size", "10")
.option("--parallel <count>", "Parallel processing count", "4")
.option("--dry-run", "Preview optimization without saving")
.option("--silent", "Suppress non-error output")
.option("--verbose", "Show detailed output")
.addHelpText("after", `
Examples:
$ image-asset-manager optimize # Basic optimization
$ image-asset-manager optimize -q 90 # Custom quality
$ image-asset-manager optimize -o ./optimized # Custom output
$ image-asset-manager optimize --dry-run # Preview only
$ image-asset-manager optimize --parallel 8 # More parallel jobs
`)
.action(async (projectPath, options) => {
const startTime = Date.now();
const spinner = new progress_1.SpinnerDisplay();
try {
if (!options.silent) {
spinner.start("Optimizing image assets...");
}
const configManager = new config_1.ConfigManager(options.config);
const cliOptions = configManager.toCLIOptions({
projectPath: projectPath || process.cwd(),
watch: false,
optimize: true,
});
const manager = new ImageAssetManager_1.ImageAssetManager(configManager);
if (!options.silent) {
spinner.stop();
console.log(`โก Optimizing images in: ${cliOptions.projectPath}`);
if (options.output) {
console.log(`๐ Output directory: ${options.output}`);
}
if (options.dryRun) {
console.log(`๐ Dry run mode - no files will be modified`);
}
console.log("");
}
await manager.optimize(cliOptions, {
outputDir: options.output,
quality: parseInt(options.quality, 10),
pngQuality: options.pngQuality
? parseInt(options.pngQuality, 10)
: undefined,
jpgQuality: options.jpgQuality
? parseInt(options.jpgQuality, 10)
: undefined,
webpQuality: options.webpQuality
? parseInt(options.webpQuality, 10)
: undefined,
progressive: options.progressive !== false,
keepOriginal: options.keepOriginal !== false,
batchSize: parseInt(options.batchSize, 10),
parallel: parseInt(options.parallel, 10),
dryRun: options.dryRun,
silent: options.silent,
verbose: options.verbose,
});
if (!options.silent) {
const duration = (0, progress_1.formatDuration)(Date.now() - startTime);
console.log(`โ
Optimization completed in ${duration}`);
}
}
catch (error) {
spinner.stop();
console.error("โ Error optimizing images:", error);
if (options.verbose && error instanceof Error) {
console.error("Stack trace:", error.stack);
}
process.exit(1);
}
});
// ็ๆๅฝไปค
program
.command("generate")
.description("๐ง Generate type definitions and import files")
.argument("[project-path]", "Path to the project directory", process.cwd())
.option("-c, --config <config>", "Path to configuration file")
.option("-f, --framework <framework>", "Target framework (react|vue|angular|svelte)", "react")
.option("-o, --output <dir>", "Output directory", "src/assets")
.option("--typescript", "Generate TypeScript definitions")
.option("--no-typescript", "Generate JavaScript only")
.option("--component-prefix <prefix>", "Component name prefix", "Icon")
.option("--barrel-file", "Generate barrel export file")
.option("--watch", "Watch for changes and regenerate")
.option("--silent", "Suppress non-error output")
.option("--verbose", "Show detailed output")
.addHelpText("after", `
Examples:
$ image-asset-manager generate # Generate React components
$ image-asset-manager generate -f vue # Generate Vue components
$ image-asset-manager generate --typescript # Include TypeScript
$ image-asset-manager generate --watch # Watch mode
$ image-asset-manager generate --barrel-file # Create index file
`)
.action(async (projectPath, options) => {
const startTime = Date.now();
const spinner = new progress_1.SpinnerDisplay();
try {
if (!options.silent) {
spinner.start("Generating code files...");
}
const configManager = new config_1.ConfigManager(options.config);
const cliOptions = configManager.toCLIOptions({
projectPath: projectPath || process.cwd(),
watch: options.watch || false,
optimize: false,
});
const manager = new ImageAssetManager_1.ImageAssetManager(configManager);
if (!options.silent) {
spinner.stop();
console.log(`๐ง Generating ${options.framework} code for: ${cliOptions.projectPath}`);
console.log(`๐ Output directory: ${options.output}`);
console.log(`๐ TypeScript: ${options.typescript !== false ? "โ
" : "โ"}`);
if (options.watch) {
console.log(`๐ Watch mode: โ
`);
}
console.log("");
}
await manager.generate(cliOptions, {
framework: options.framework,
outputDir: options.output,
typescript: options.typescript !== false,
componentPrefix: options.componentPrefix,
barrelFile: options.barrelFile,
watch: options.watch,
silent: options.silent,
verbose: options.verbose,
});
if (!options.silent) {
const duration = (0, progress_1.formatDuration)(Date.now() - startTime);
console.log(`โ
Code generation completed in ${duration}`);
}
}
catch (error) {
spinner.stop();
console.error("โ Error generating code:", error);
if (options.verbose && error instanceof Error) {
console.error("Stack trace:", error.stack);
}
process.exit(1);
}
});
// ๅๅงๅ้
็ฝฎๅฝไปค
program
.command("init")
.description("๐ Initialize configuration file")
.option("--js", "Generate JavaScript config file", false)
.option("--json", "Generate JSON config file", false)
.option("--package", "Add config to package.json", false)
.option("--force", "Overwrite existing config file", false)
.addHelpText("after", `
Examples:
$ image-asset-manager init # Generate image-asset.config.js
$ image-asset-manager init --json # Generate .image-asset.json
$ image-asset-manager init --package # Add to package.json
$ image-asset-manager init --force # Overwrite existing
`)
.action(async (options) => {
try {
let configPath;
let configContent;
if (options.package) {
configPath = "package.json";
if ((0, fs_1.existsSync)(configPath) && !options.force) {
const pkg = JSON.parse(require("fs").readFileSync(configPath, "utf-8"));
if (pkg.imageAssetManager) {
console.log("โ ๏ธ Configuration already exists in package.json");
console.log("Use --force to overwrite");
return;
}
pkg.imageAssetManager = JSON.parse(config_1.ConfigManager.generateExampleConfig().replace(/^\/\/ .*\n|module\.exports = |;$/gm, ""));
configContent = JSON.stringify(pkg, null, 2);
}
else {
console.error("โ package.json not found");
process.exit(1);
}
}
else if (options.json) {
configPath = ".image-asset.json";
configContent = config_1.ConfigManager.generateExampleConfig().replace(/^\/\/ .*\n|module\.exports = |;$/gm, "");
}
else {
configPath = "image-asset.config.js";
configContent = config_1.ConfigManager.generateExampleConfig();
}
if ((0, fs_1.existsSync)(configPath) && !options.force && !options.package) {
console.log(`โ ๏ธ Configuration file ${configPath} already exists`);
console.log("Use --force to overwrite");
return;
}
(0, fs_1.writeFileSync)(configPath, configContent);
console.log(`โ
Configuration file created: ${configPath}`);
console.log("");
console.log("Next steps:");
console.log("1. Edit the configuration file to match your project needs");
console.log("2. Run 'image-asset-manager scan' to test the configuration");
console.log("3. Run 'image-asset-manager serve' to start the web interface");
}
catch (error) {
console.error("โ Error creating configuration file:", error);
process.exit(1);
}
});
// ๅ
จๅฑ้่ฏฏๅค็
process.on("uncaughtException", (error) => {
console.error("โ Uncaught Exception:", error);
process.exit(1);
});
process.on("unhandledRejection", (reason, promise) => {
console.error("โ Unhandled Rejection at:", promise, "reason:", reason);
process.exit(1);
});
program.parse();
//# sourceMappingURL=cli.js.map