#!/usr/bin/env -S deno run --allow-read --allow-write import { parseArgs } from "@std/cli/parse-args"; import { scaleImage, type ScaleMode } from "./src/scaler.ts"; import { scanDirectory } from "./src/scanner.ts"; import { processBatch } from "./src/batch.ts"; const HELP = ` scale-image-fixer - Scale images to fixed dimensions Usage: deno run cli.ts [options] deno run cli.ts [options] # Multiple files deno run cli.ts [options] # Directory mode Arguments: input Input file path, multiple file paths, or directory path Options: -w, --width Output width (required) -h, --height Output height (required) -m, --mode Scale mode: stretch, fit, cover (default: fit) -o, --output Output path (file or directory) --help Show this help message Scale Modes: stretch Resize to exact dimensions (may distort aspect ratio) fit Fit within dimensions while preserving aspect ratio cover Cover dimensions while preserving aspect ratio (crops excess) Examples: deno run cli.ts input.png -w 800 -h 600 deno run cli.ts input.jpg -w 400 -h 400 -m cover -o output.jpg deno run cli.ts ./images -w 200 -h 200 -o ./resized `; interface Args { width?: number; height?: number; mode?: string; output?: string; help?: boolean; _: string[]; } function validateArgs(args: Args): { valid: boolean; error?: string } { if (args.help) { return { valid: true }; } if (args._.length === 0) { return { valid: false, error: "Input file, files, or directory is required" }; } if (!args.width || !args.height) { return { valid: false, error: "Width (-w) and height (-h) are required" }; } if (args.width <= 0 || args.height <= 0) { return { valid: false, error: "Width and height must be positive numbers" }; } const validModes = ["stretch", "fit", "cover"]; if (args.mode && !validModes.includes(args.mode)) { return { valid: false, error: `Mode must be one of: ${validModes.join(", ")}` }; } return { valid: true }; } async function main() { const args = parseArgs(Deno.args, { string: ["width", "height", "mode", "output"], alias: { w: "width", h: "height", m: "mode", o: "output", }, default: { mode: "fit", }, }) as Args; const validation = validateArgs(args); if (!validation.valid) { console.error(`Error: ${validation.error}`); console.log("\nUse --help for usage information"); Deno.exit(1); } if (args.help) { console.log(HELP); Deno.exit(0); } const inputs = args._.map(String); const scaleMode: ScaleMode = (args.mode as ScaleMode) || "fit"; const width = args.width!; const height = args.height!; const output = args.output; try { // Check if inputs are files or directory const firstInput = await Deno.stat(inputs[0]); if (firstInput.isDirectory) { // Directory mode: scan and batch process const images = await scanDirectory(inputs[0]); if (images.length === 0) { console.log("No supported images found in directory"); Deno.exit(0); } await processBatch(images, { width, height, mode: scaleMode, outputDir: output }); } else if (inputs.length > 1) { // Multiple files mode await processBatch(inputs, { width, height, mode: scaleMode, outputDir: output }); } else { // Single file mode const result = await scaleImage(inputs[0], { width, height, mode: scaleMode, output }); console.log(`Scaled: ${result.input} → ${result.output}`); } } catch (error) { if (error instanceof Deno.errors.NotFound) { console.error(`Error: File or directory not found: ${inputs[0]}`); } else if (error instanceof Error) { console.error(`Error: ${error.message}`); } else { console.error("An unexpected error occurred"); } Deno.exit(1); } } await main();