@assetopt/cli
Advanced tools
+89
-30
@@ -27,3 +27,3 @@ #!/usr/bin/env node | ||
| // src/commands/analyze.ts | ||
| import { resolve as resolve2 } from "path"; | ||
| import { resolve as resolve3 } from "path"; | ||
| import { loadConfig, runPipeline, buildReport } from "@assetopt/core"; | ||
@@ -144,2 +144,33 @@ | ||
| // src/utils/config.ts | ||
| import { resolve as resolve2 } from "path"; | ||
| function collect(value, previous) { | ||
| return [...previous, value]; | ||
| } | ||
| function applyCliOverrides(config, flags) { | ||
| let effective = config; | ||
| if (flags.output !== void 0) { | ||
| effective = { | ||
| ...effective, | ||
| output: { ...effective.output, dir: resolve2(process.cwd(), flags.output) } | ||
| }; | ||
| } | ||
| if (flags.forceReencode === true) { | ||
| effective = { | ||
| ...effective, | ||
| output: { ...effective.output, forceReencode: true } | ||
| }; | ||
| } | ||
| if (flags.exclude !== void 0 && flags.exclude.length > 0) { | ||
| effective = { | ||
| ...effective, | ||
| input: { | ||
| ...effective.input, | ||
| exclude: [...effective.input?.exclude ?? [], ...flags.exclude] | ||
| } | ||
| }; | ||
| } | ||
| return effective; | ||
| } | ||
| // src/commands/analyze.ts | ||
@@ -150,7 +181,15 @@ function registerAnalyze(program2) { | ||
| "output directory used for cache lookup (overrides output.dir from config)" | ||
| ).option("--json", "output report as JSON instead of terminal format").option("--min-savings <percent>", "fail (exit 1) if total savings are below this percent").option("--no-cache", "bypass the incremental cache (re-analyze every asset from scratch)").action(async (dir = ".", options) => { | ||
| const cwd = resolve2(process.cwd(), dir); | ||
| ).option( | ||
| "--exclude <glob>", | ||
| "skip files matching this glob (repeatable, adds to config)", | ||
| collect, | ||
| [] | ||
| ).option("--json", "output report as JSON instead of terminal format").option("--min-savings <percent>", "fail (exit 1) if total savings are below this percent").option( | ||
| "--force-reencode", | ||
| "report savings for the re-encoded output even when it is larger than the source (disables the larger-output guard)" | ||
| ).option("--no-cache", "bypass the incremental cache (re-analyze every asset from scratch)").action(async (dir = ".", options) => { | ||
| const cwd = resolve3(process.cwd(), dir); | ||
| try { | ||
| const { config, source } = await loadConfig(); | ||
| const effectiveConfig = options.output ? { ...config, output: { ...config.output, dir: resolve2(process.cwd(), options.output) } } : config; | ||
| const effectiveConfig = applyCliOverrides(config, options); | ||
| if (!options.json) { | ||
@@ -184,10 +223,18 @@ printConfigSource(source); | ||
| // src/commands/optimize.ts | ||
| import { resolve as resolve3 } from "path"; | ||
| import { resolve as resolve4 } from "path"; | ||
| import { loadConfig as loadConfig2, runPipeline as runPipeline2, buildReport as buildReport2 } from "@assetopt/core"; | ||
| function registerOptimize(program2) { | ||
| program2.command("optimize [dir]").description("Optimize assets and write results to output directory").option("-o, --output <dir>", "output directory (overrides output.dir from config)").option("--json", "output report as JSON instead of terminal format").option("--min-savings <percent>", "fail (exit 1) if total savings are below this percent").option("--no-cache", "bypass the incremental cache (force re-processing of all assets)").action(async (dir = ".", options) => { | ||
| const cwd = resolve3(process.cwd(), dir); | ||
| program2.command("optimize [dir]").description("Optimize assets and write results to output directory").option("-o, --output <dir>", "output directory (overrides output.dir from config)").option( | ||
| "--exclude <glob>", | ||
| "skip files matching this glob (repeatable, adds to config)", | ||
| collect, | ||
| [] | ||
| ).option("--json", "output report as JSON instead of terminal format").option("--min-savings <percent>", "fail (exit 1) if total savings are below this percent").option( | ||
| "--force-reencode", | ||
| "write the re-encoded output even when it is larger than the source (disables the larger-output guard)" | ||
| ).option("--no-cache", "bypass the incremental cache (force re-processing of all assets)").action(async (dir = ".", options) => { | ||
| const cwd = resolve4(process.cwd(), dir); | ||
| try { | ||
| const { config, source } = await loadConfig2(); | ||
| const effectiveConfig = options.output ? { ...config, output: { ...config.output, dir: resolve3(process.cwd(), options.output) } } : config; | ||
| const effectiveConfig = applyCliOverrides(config, options); | ||
| if (!options.json) { | ||
@@ -220,3 +267,3 @@ printConfigSource(source); | ||
| // src/commands/audit.ts | ||
| import { resolve as resolve4 } from "path"; | ||
| import { resolve as resolve5 } from "path"; | ||
| import pc4 from "picocolors"; | ||
@@ -231,23 +278,35 @@ import { loadConfig as loadConfig3, scanDirectory, getAssetType, getFileSize, runPipeline as runPipeline3 } from "@assetopt/core"; | ||
| function registerAudit(program2) { | ||
| program2.command("audit [dir]").description("Identify problematic assets with optimization recommendations").option("--threshold <percent>", "minimum savings % to flag a file (requires --savings)", "10").option("--savings", "compute potential savings for each file (slower)").action(async (dir = ".", options) => { | ||
| const cwd = resolve4(process.cwd(), dir); | ||
| const minSavings = parseFloat(options.threshold); | ||
| try { | ||
| const { config, source } = await loadConfig3(); | ||
| printConfigSource(source); | ||
| console.log(`Auditing ${cwd}...`); | ||
| const start = Date.now(); | ||
| const outputDir = resolve4(process.cwd(), config.output?.dir ?? "./optimized"); | ||
| if (options.savings) { | ||
| await runFullAudit(cwd, config, minSavings, start); | ||
| } else { | ||
| await runFastAudit(cwd, start, outputDir); | ||
| program2.command("audit [dir]").description("Identify problematic assets with optimization recommendations").option("--threshold <percent>", "minimum savings % to flag a file (requires --savings)", "10").option("--savings", "compute potential savings for each file (slower)").option( | ||
| "--exclude <glob>", | ||
| "skip files matching this glob (repeatable, adds to config)", | ||
| collect, | ||
| [] | ||
| ).action( | ||
| async (dir = ".", options) => { | ||
| const cwd = resolve5(process.cwd(), dir); | ||
| const minSavings = parseFloat(options.threshold); | ||
| try { | ||
| const { config, source } = await loadConfig3(); | ||
| const effectiveConfig = applyCliOverrides(config, options); | ||
| printConfigSource(source); | ||
| console.log(`Auditing ${cwd}...`); | ||
| const start = Date.now(); | ||
| const outputDir = resolve5(process.cwd(), effectiveConfig.output?.dir ?? "./optimized"); | ||
| if (options.savings) { | ||
| await runFullAudit(cwd, effectiveConfig, minSavings, start); | ||
| } else { | ||
| await runFastAudit(cwd, effectiveConfig, start, outputDir); | ||
| } | ||
| } catch (err) { | ||
| handleCliError(err); | ||
| } | ||
| } catch (err) { | ||
| handleCliError(err); | ||
| } | ||
| ); | ||
| } | ||
| async function runFastAudit(cwd, config, start, outputDir) { | ||
| const files = await scanDirectory(cwd, { | ||
| excludePaths: [outputDir], | ||
| include: config.input?.include, | ||
| exclude: config.input?.exclude | ||
| }); | ||
| } | ||
| async function runFastAudit(cwd, start, outputDir) { | ||
| const files = await scanDirectory(cwd, { excludePaths: [outputDir] }); | ||
| if (files.length === 0) { | ||
@@ -323,6 +382,6 @@ console.log(pc4.yellow("No supported assets found.")); | ||
| import { rm, stat } from "fs/promises"; | ||
| import { resolve as resolve5, relative, isAbsolute } from "path"; | ||
| import { resolve as resolve6, relative, isAbsolute } from "path"; | ||
| import { loadConfig as loadConfig4, CACHE_FILE } from "@assetopt/core"; | ||
| function resolveOutputDir(configDir, override) { | ||
| return resolve5(process.cwd(), override ?? configDir ?? "./optimized"); | ||
| return resolve6(process.cwd(), override ?? configDir ?? "./optimized"); | ||
| } | ||
@@ -338,3 +397,3 @@ function targetContainsCwd(target) { | ||
| const outputDir = resolveOutputDir(config.output?.dir, options.output); | ||
| const target = options.all ? outputDir : resolve5(outputDir, CACHE_FILE); | ||
| const target = options.all ? outputDir : resolve6(outputDir, CACHE_FILE); | ||
| const label = options.all ? `output directory ${target}` : `cache ${target}`; | ||
@@ -341,0 +400,0 @@ if (options.all && targetContainsCwd(target)) { |
+1
-1
| { | ||
| "name": "@assetopt/cli", | ||
| "version": "1.0.3", | ||
| "version": "1.1.0", | ||
| "description": "Command-line tool to optimize images, CSS, JS and SVG assets — open source, MIT.", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
21586
7.51%439
14.62%