img-subir 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +9 -0
- package/README.md +147 -0
- package/dist/chunk-CWZBNFPK.js +1004 -0
- package/dist/chunk-CWZBNFPK.js.map +1 -0
- package/dist/cli.js +295 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +215 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
- package/skills/img-subir/SKILL.md +56 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/presets.ts","../src/core/classify.ts","../src/core/discover.ts","../src/core/probe.ts","../src/core/rewrite-refs.ts","../src/version.ts","../src/core/optimize.ts","../src/core/quality.ts","../src/core/encode.ts","../src/core/svg.ts","../src/core/write.ts","../src/config.ts"],"sourcesContent":["/** Thrown for bad invocations (unknown preset, missing path, invalid flag). The CLI maps it to exit code 2. */\nexport class UsageError extends Error {\n override name = \"UsageError\";\n}\n","import { UsageError } from \"./errors.js\";\nimport type { Kind, Preset, PresetName } from \"./types.js\";\n\nconst stdFloor = { ui: 0.985, photo: 0.96, graphic: 0.985 } as const;\nconst stdQuality = {\n ui: { webp: 82, avif: 60, jpeg: 85, png: 95 },\n photo: { webp: 80, avif: 55, jpeg: 78, png: 90 },\n graphic: { webp: 90, avif: 65, jpeg: 88, png: 100 },\n} as const satisfies Preset[\"quality\"];\n\nconst base = {\n floor: stdFloor,\n quality: stdQuality,\n avifMinGain: 0.15,\n avifMaxPixels: 4_000_000,\n losslessWebp: false,\n lossless: false,\n minGainPercent: 5,\n} satisfies Partial<Preset>;\n\nexport const presets: Record<PresetName, Preset> = {\n web: {\n ...base,\n name: \"web\",\n maxWidth: 1920,\n candidates: [\"webp\", \"avif\", \"png\", \"jpeg\"],\n note: \"Website assets, Subir pages, docs. AVIF kept only when ≥15% smaller than WebP.\",\n },\n github: {\n ...base,\n name: \"github\",\n maxWidth: 1600,\n candidates: [\"webp\", \"png\", \"jpeg\"],\n note: \"README / PR screenshots. Never emits AVIF.\",\n },\n screenshot: {\n ...base,\n name: \"screenshot\",\n maxWidth: 1600,\n candidates: [\"webp\", \"png\"],\n floor: { ui: 0.99, photo: 0.99, graphic: 0.99 },\n losslessWebp: true,\n forceKind: \"ui\",\n note: \"Crisp UI captures. Forces kind=ui and also tries lossless WebP.\",\n },\n og: {\n ...base,\n name: \"og\",\n maxWidth: 1200,\n maxHeight: 630,\n candidates: [\"jpeg\", \"png\"],\n floor: { ui: 0.97, photo: 0.95, graphic: 0.97 },\n note: \"Open Graph / social cards. JPEG/PNG only; many crawlers dislike WebP.\",\n },\n thumb: {\n ...base,\n name: \"thumb\",\n maxWidth: 480,\n candidates: [\"webp\", \"avif\"],\n floor: { ui: 0.95, photo: 0.93, graphic: 0.95 },\n quality: {\n ui: { webp: 75, avif: 50, jpeg: 75, png: 85 },\n photo: { webp: 70, avif: 45, jpeg: 70, png: 80 },\n graphic: { webp: 80, avif: 55, jpeg: 80, png: 90 },\n },\n note: \"Thumbnails, avatars, cards. Aggressive; pair with --target-kb.\",\n },\n lossless: {\n ...base,\n name: \"lossless\",\n candidates: [\"png\", \"webp\"],\n floor: { ui: 1, photo: 1, graphic: 1 },\n losslessWebp: true,\n lossless: true,\n minGainPercent: 1,\n note: \"Pixel-exact. No resize, no lossy encoders.\",\n },\n};\n\nexport const presetNames = Object.keys(presets) as PresetName[];\n\nexport function getPreset(name: string | undefined): Preset {\n const n = (name ?? \"web\") as PresetName;\n const p = presets[n];\n if (!p) {\n throw new UsageError(`Unknown preset \"${name}\". Valid presets: ${presetNames.join(\", \")}`);\n }\n return p;\n}\n\nexport const kinds: Kind[] = [\"ui\", \"photo\", \"graphic\"];\n","import path from \"node:path\";\nimport sharp from \"sharp\";\nimport type { ImageInfo, Kind } from \"../types.js\";\n\nconst UI_NAME_HINTS = /screen ?shot|cleanshot|@[23]x|capture|screencap|frame|mockup|ui-|-ui\\b/i;\nconst PHOTO_NAME_HINTS = /\\b(img|dsc|pxl|imag|photo|pic)[_-]?\\d{3,}/i;\nconst DEVICE_SIZES = new Set([\n \"2880x1800\", \"3024x1964\", \"3456x2234\", \"2560x1600\", \"2560x1664\", \"3072x1920\", \"5120x2880\", \"3840x2160\",\n \"1170x2532\", \"1179x2556\", \"1290x2796\", \"1284x2778\", \"1125x2436\", \"1080x2340\", \"1440x3120\", \"2048x2732\",\n]);\n\nexport interface ClassifyStats {\n distinctColors: number;\n flatShare: number;\n /** Share of sampled pixels that are not fully opaque. 0 means the alpha channel is unused. */\n transparentShare: number;\n score: number;\n}\n\n/**\n * Cheap heuristics, no ML. Samples a 256px thumbnail and measures how \"flat\" it is.\n * UI screenshots: few distinct colors, long runs of identical pixels.\n * Photos: the opposite.\n */\nexport async function classify(info: ImageInfo, input: string | Buffer = info.path): Promise<{ kind: Kind; stats: ClassifyStats }> {\n const { data, info: raw } = await sharp(input, { pages: 1 })\n .rotate()\n .resize({ width: 256, height: 256, fit: \"inside\", withoutEnlargement: true, kernel: \"nearest\" })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n\n const { width, height, channels } = raw;\n const colors = new Set<number>();\n let flat = 0;\n let total = 0;\n let transparent = 0;\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const i = (y * width + x) * channels;\n const c = (data[i] << 16) | (data[i + 1] << 8) | data[i + 2];\n if (colors.size < 200_000) colors.add(c);\n if (data[i + 3] < 255) transparent++;\n if (x > 0) {\n const j = i - channels;\n total++;\n if (data[j] === data[i] && data[j + 1] === data[i + 1] && data[j + 2] === data[i + 2]) flat++;\n }\n }\n }\n const flatShare = total ? flat / total : 0;\n const distinctColors = colors.size;\n const transparentShare = width * height ? transparent / (width * height) : 0;\n const hasTransparency = transparentShare > 0.001;\n\n // score > 0 → ui, < 0 → photo\n let score = 0;\n if (flatShare > 0.45) score += 2;\n else if (flatShare > 0.25) score += 1;\n else if (flatShare < 0.08) score -= 2;\n else if (flatShare < 0.15) score -= 1;\n\n if (distinctColors < 2_000) score += 1;\n else if (distinctColors > 20_000) score -= 1;\n\n const name = path.basename(info.path);\n if (UI_NAME_HINTS.test(name)) score += 1;\n if (PHOTO_NAME_HINTS.test(name)) score -= 1;\n if (DEVICE_SIZES.has(`${info.width}x${info.height}`)) score += 1;\n\n let kind: Kind;\n if (hasTransparency && distinctColors < 4_000 && flatShare > 0.3) kind = \"graphic\";\n else if (score >= 1) kind = \"ui\";\n else if (score <= -1) kind = \"photo\";\n else kind = flatShare > 0.2 ? \"ui\" : \"photo\";\n\n return { kind, stats: { distinctColors, flatShare: Number(flatShare.toFixed(3)), transparentShare: Number(transparentShare.toFixed(4)), score } };\n}\n","import fg from \"fast-glob\";\nimport { stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { UsageError } from \"../errors.js\";\n\nexport const BACKUP_DIR = \".img-subir-originals\";\nexport const IMAGE_EXTS = [\"png\", \"jpg\", \"jpeg\", \"webp\", \"avif\", \"gif\", \"svg\"] as const;\n\nconst IGNORE = [\"**/node_modules/**\", \"**/.git/**\", `**/${BACKUP_DIR}/**`];\n\nfunction extPattern(): string {\n const all = IMAGE_EXTS.flatMap((e) => [e, e.toUpperCase()]);\n return `**/*.{${all.join(\",\")}}`;\n}\n\nexport interface Discovered {\n files: string[];\n /** Directories the user passed (used as rewrite roots). */\n roots: string[];\n}\n\n/**\n * @param excludeDirs absolute directories whose contents are skipped (e.g. the --out dir when it sits inside an input dir)\n */\nexport async function discover(inputs: string[], cwd: string, excludeDirs: string[] = []): Promise<Discovered> {\n const files = new Set<string>();\n const roots = new Set<string>();\n const ignore = IGNORE;\n const excluded = (f: string) => excludeDirs.some((d) => f === d || f.startsWith(d + path.sep));\n\n for (const input of inputs) {\n const abs = path.resolve(cwd, input);\n let s: Awaited<ReturnType<typeof stat>> | null = null;\n try {\n s = await stat(abs);\n } catch {\n s = null;\n }\n if (s?.isDirectory()) {\n roots.add(abs);\n const found = await fg(extPattern(), { cwd: abs, absolute: true, ignore, onlyFiles: true, dot: false });\n for (const f of found) files.add(path.normalize(f));\n } else if (s?.isFile()) {\n files.add(abs);\n roots.add(path.dirname(abs));\n } else {\n // treat as glob relative to cwd\n const found = await fg(input, { cwd, absolute: true, ignore, onlyFiles: true, dot: false });\n if (found.length === 0) {\n throw new UsageError(`No such file, directory or glob match: ${input}`);\n }\n for (const f of found) files.add(path.normalize(f));\n roots.add(cwd);\n }\n }\n return { files: [...files].filter((f) => !excluded(f)).sort(), roots: [...roots] };\n}\n\nexport function isImagePath(p: string): boolean {\n const ext = path.extname(p).slice(1).toLowerCase();\n return (IMAGE_EXTS as readonly string[]).includes(ext);\n}\n","import { stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport sharp from \"sharp\";\nimport type { Format, ImageInfo } from \"../types.js\";\n\nfunction normalizeFormat(f: string | undefined, filePath: string): Format {\n switch (f) {\n case \"jpeg\":\n case \"jpg\":\n return \"jpeg\";\n case \"png\":\n case \"webp\":\n case \"gif\":\n case \"svg\":\n return f;\n case \"heif\":\n return \"avif\";\n default: {\n const ext = path.extname(filePath).slice(1).toLowerCase();\n if (ext === \"jpg\") return \"jpeg\";\n if (ext === \"avif\") return \"avif\";\n return (ext as Format) || \"png\";\n }\n }\n}\n\nexport async function probe(filePath: string): Promise<ImageInfo> {\n const s = await stat(filePath);\n const ext = path.extname(filePath).toLowerCase();\n if (ext === \".svg\") {\n let width = 0;\n let height = 0;\n try {\n const m = await sharp(filePath).metadata();\n width = m.width ?? 0;\n height = m.height ?? 0;\n } catch {\n /* svg without intrinsic size is fine */\n }\n return { path: filePath, format: \"svg\", width, height, bytes: s.size, hasAlpha: true, animated: false };\n }\n const m = await sharp(filePath, { animated: true }).metadata();\n const pages = m.pages ?? 1;\n const width = m.width ?? 0;\n // for animated input sharp reports the full strip height; pageHeight is the frame height\n const height = pages > 1 ? (m.pageHeight ?? Math.round((m.height ?? 0) / pages)) : (m.height ?? 0);\n const swap = m.orientation !== undefined && m.orientation >= 5;\n return {\n path: filePath,\n format: normalizeFormat(m.format, filePath),\n width: swap ? height : width,\n height: swap ? width : height,\n bytes: s.size,\n hasAlpha: Boolean(m.hasAlpha),\n animated: pages > 1,\n };\n}\n","import fg from \"fast-glob\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { Rename, RewrittenRef } from \"../types.js\";\nimport { BACKUP_DIR } from \"./discover.js\";\n\nexport const DEFAULT_REF_GLOBS = [\"**/*.{html,htm,md,mdx,tsx,jsx,ts,js,mjs,css,scss,vue,svelte,astro,json,yml,yaml}\"];\n\nfunction escapeRe(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\n/**\n * The spellings a text file might use to point at `target`:\n * - relative to the document's own directory (`logo.png`, `./logo.png`, `../img/logo.png`)\n * - site-absolute from a root (`/img/logo.png`), since a leading slash means \"from the site root\"\n * - the percent-encoded form of each (`Screenshot%202026.png`)\n * Bare root-relative spellings are deliberately NOT used: `logo.png` inside `sub/page.html`\n * refers to `sub/logo.png`, not `<root>/logo.png`.\n */\nfunction spellings(textFile: string, target: string, roots: string[]): string[] {\n const out = new Set<string>();\n const rel = toPosix(path.relative(path.dirname(textFile), target));\n if (rel && !rel.startsWith(\"/\")) {\n out.add(rel);\n if (!rel.startsWith(\".\")) out.add(`./${rel}`);\n }\n for (const root of roots) {\n const fromRoot = toPosix(path.relative(root, target));\n if (fromRoot && !fromRoot.startsWith(\"..\") && !path.isAbsolute(fromRoot)) out.add(`/${fromRoot}`);\n }\n for (const s of [...out]) {\n const enc = encodeURI(s);\n if (enc !== s) out.add(enc);\n }\n return [...out];\n}\n\n// Token boundaries: quotes, backticks, parens, angle brackets (html attrs / markdown <path>), whitespace, `=`, `,`.\nconst LEAD = String.raw`(^|[\\s\"'\\`(<=,])`;\nconst TRAIL = String.raw`(?=$|[\\s\"'\\`)>?#,;])`;\n\n/**\n * Rewrites references to renamed files inside text files under `roots`.\n * Only whole-token matches are replaced, so `logo.png` never matches `logo.png.bak` or `mylogo.png`.\n */\nexport async function rewriteRefs(\n renamed: Rename[],\n roots: string[],\n globs: string[] = DEFAULT_REF_GLOBS,\n dryRun = false,\n): Promise<RewrittenRef[]> {\n if (renamed.length === 0) return [];\n const results: RewrittenRef[] = [];\n const renamedSet = new Set(renamed.map((r) => path.normalize(r.to)));\n const seen = new Set<string>();\n\n for (const root of roots) {\n const files = (\n await fg(globs, {\n cwd: root,\n absolute: true,\n onlyFiles: true,\n ignore: [\"**/node_modules/**\", \"**/.git/**\", `**/${BACKUP_DIR}/**`, \"**/dist/**\", \"**/build/**\"],\n })\n ).map((f) => path.normalize(f));\n for (const file of files) {\n if (seen.has(file) || renamedSet.has(file)) continue;\n seen.add(file);\n let text: string;\n try {\n text = await readFile(file, \"utf8\");\n } catch {\n continue;\n }\n if (text.includes(\"\\0\")) continue; // binary\n let changed = false;\n for (const r of renamed) {\n const fromSpellings = spellings(file, r.from, roots);\n const toSpellings = spellings(file, r.to, roots);\n let count = 0;\n for (let i = 0; i < fromSpellings.length; i++) {\n const from = fromSpellings[i];\n const to = toSpellings[i] ?? toSpellings[0];\n const re = new RegExp(`${LEAD}${escapeRe(from)}${TRAIL}`, \"g\");\n text = text.replace(re, (_m, pre: string) => {\n count++;\n return `${pre}${to}`;\n });\n }\n if (count > 0) {\n changed = true;\n results.push({ file, from: r.from, to: r.to, count });\n }\n }\n if (changed && !dryRun) await writeFile(file, text);\n }\n }\n return results;\n}\n","import { createRequire } from \"node:module\";\nconst require = createRequire(import.meta.url);\nexport const VERSION: string = (require(\"../package.json\") as { version: string }).version;\n","import { access, copyFile, mkdir, readFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport sharp from \"sharp\";\nimport { getPreset } from \"../presets.js\";\nimport type {\n Candidate,\n FileResult,\n ImageInfo,\n InspectResult,\n Kind,\n OptimizeOptions,\n OutputInfo,\n Preset,\n RasterFormat,\n Rename,\n Report,\n Totals,\n} from \"../types.js\";\nimport { classify } from \"./classify.js\";\nimport { discover } from \"./discover.js\";\nimport { applyEncoder, encode, extFor, type EncodeSpec } from \"./encode.js\";\nimport { probe } from \"./probe.js\";\nimport { alphaIsOpaque, compareSsim, fromRaw, stripAlpha, toComparable, type Comparable, type RawImage } from \"./quality.js\";\nimport { DEFAULT_REF_GLOBS, rewriteRefs } from \"./rewrite-refs.js\";\nimport { optimizeSvg } from \"./svg.js\";\nimport { writeOutput } from \"./write.js\";\nimport { VERSION } from \"../version.js\";\nimport { UsageError } from \"../errors.js\";\n\nconst LOSSY_INPUTS = new Set([\"webp\", \"jpeg\", \"avif\"]);\n\ninterface Ctx {\n opts: Required<Pick<OptimizeOptions, \"cwd\" | \"backup\" | \"rename\" | \"dryRun\" | \"fast\">> & OptimizeOptions;\n preset: Preset;\n roots: string[];\n}\n\nexport function resolveOptions(opts: OptimizeOptions): Ctx[\"opts\"] {\n return {\n ...opts,\n cwd: opts.cwd ?? process.cwd(),\n backup: opts.backup ?? true,\n rename: opts.rename ?? true,\n dryRun: opts.dryRun ?? false,\n fast: opts.fast ?? false,\n format: opts.format ?? \"auto\",\n kind: opts.kind ?? \"auto\",\n };\n}\n\nexport async function optimize(options: OptimizeOptions): Promise<Report> {\n const started = Date.now();\n const opts = resolveOptions(options);\n const preset = getPreset(opts.preset);\n const outAbs = opts.out ? path.resolve(opts.cwd, opts.out) : undefined;\n const { files, roots } = await discover(opts.paths, opts.cwd, outAbs ? [outAbs] : []);\n if (outAbs && roots.some((r) => path.resolve(r) === outAbs)) {\n throw new UsageError(`--out must differ from the input directory (${path.relative(opts.cwd, outAbs) || \".\"}); drop --out to optimize in place`);\n }\n const ctx: Ctx = { opts, preset, roots };\n\n const concurrency = Math.max(1, opts.concurrency ?? Math.max(1, os.cpus().length - 1));\n const results: FileResult[] = new Array(files.length);\n let next = 0;\n await Promise.all(\n Array.from({ length: Math.min(concurrency, files.length) }, async () => {\n while (next < files.length) {\n const i = next++;\n const r = await optimizeFile(files[i], ctx);\n results[i] = r;\n opts.onFile?.(r);\n }\n }),\n );\n\n const renamed: Rename[] = results\n .filter((r) => r.output && r.status !== \"failed\" && r.status !== \"kept\" && path.basename(r.output.path) !== path.basename(r.input.path))\n .map((r) => ({ from: r.input.path, to: r.output!.path }));\n\n let rewrittenRefs: Report[\"rewrittenRefs\"] = [];\n if (opts.rewriteRefs && !outAbs) {\n const globs = Array.isArray(opts.rewriteRefs) ? opts.rewriteRefs : DEFAULT_REF_GLOBS;\n rewrittenRefs = await rewriteRefs(renamed, roots, globs, opts.dryRun);\n }\n\n const backupDirs = [...new Set(results.filter((r) => r.backupPath).map((r) => path.dirname(r.backupPath!)))];\n\n return {\n schemaVersion: 1,\n version: VERSION,\n preset: preset.name,\n dryRun: opts.dryRun,\n files: results,\n totals: totals(results, Date.now() - started),\n renamed,\n rewrittenRefs,\n backupDirs,\n };\n}\n\nfunction totals(results: FileResult[], durationMs: number): Totals {\n const t: Totals = {\n files: results.length, inputBytes: 0, outputBytes: 0, savedBytes: 0, savedPercent: 0,\n optimized: 0, kept: 0, failed: 0, budgetMissed: 0, durationMs,\n };\n for (const r of results) {\n t.inputBytes += r.input.bytes;\n t.outputBytes += r.output && r.status !== \"kept\" && r.status !== \"failed\" ? r.output.bytes : r.input.bytes;\n if (r.status === \"optimized\") t.optimized++;\n else if (r.status === \"kept\") t.kept++;\n else if (r.status === \"failed\") t.failed++;\n else if (r.status === \"budget-missed\") t.budgetMissed++;\n }\n t.savedBytes = t.inputBytes - t.outputBytes;\n t.savedPercent = t.inputBytes ? round1((t.savedBytes / t.inputBytes) * 100) : 0;\n return t;\n}\n\nfunction round1(n: number): number {\n return Math.round(n * 10) / 10;\n}\n\nfunction isInside(file: string, dir: string): boolean {\n const rel = path.relative(dir, file);\n return rel !== \"\" && !rel.startsWith(\"..\") && !path.isAbsolute(rel);\n}\n\nfunction rootFor(file: string, roots: string[]): string {\n let best = \"\";\n for (const r of roots) if (isInside(file, r) && r.length > best.length) best = r;\n return best || path.dirname(file);\n}\n\n/** Mirrored location of an unchanged input under --out. */\nfunction mirrorPath(input: string, ctx: Ctx): string {\n const root = rootFor(input, ctx.roots);\n return path.join(path.resolve(ctx.opts.cwd, ctx.opts.out!), path.relative(root, input));\n}\n\nfunction outputPathFor(input: string, format: string, ctx: Ctx): string {\n const { opts } = ctx;\n const parsed = path.parse(input);\n const ext = opts.rename ? extFor(format as RasterFormat | \"svg\") : parsed.ext;\n const name = `${parsed.name}${opts.suffix ?? \"\"}${ext}`;\n if (opts.out) {\n const root = rootFor(input, ctx.roots);\n return path.join(path.resolve(opts.cwd, opts.out), path.relative(root, parsed.dir), name);\n }\n return path.join(parsed.dir, name);\n}\n\nfunction targetSize(info: ImageInfo, ctx: Ctx): { width: number; height: number; resized: boolean } {\n const maxW = ctx.opts.maxWidth ?? (ctx.preset.lossless ? undefined : ctx.preset.maxWidth);\n const maxH = ctx.opts.maxHeight ?? (ctx.preset.lossless ? undefined : ctx.preset.maxHeight);\n let { width, height } = info;\n if (!width || !height) return { width, height, resized: false };\n let scale = 1;\n if (maxW && width > maxW) scale = Math.min(scale, maxW / width);\n if (maxH && height > maxH) scale = Math.min(scale, maxH / height);\n if (scale < 1) {\n width = Math.max(1, Math.round(width * scale));\n height = Math.max(1, Math.round(height * scale));\n return { width, height, resized: true };\n }\n return { width, height, resized: false };\n}\n\nfunction allowedFormats(info: ImageInfo, ctx: Ctx): RasterFormat[] {\n const f = ctx.opts.format ?? \"auto\";\n if (f === \"keep\") return info.format === \"svg\" ? [] : [info.format === \"gif\" ? \"webp\" : info.format];\n if (f !== \"auto\") return [f];\n return ctx.preset.candidates;\n}\n\nfunction buildSpecs(info: ImageInfo, kind: Kind, pixels: number, ctx: Ctx): EncodeSpec[] {\n const { preset, opts } = ctx;\n const formats = allowedFormats(info, ctx);\n const explicit = opts.format && opts.format !== \"auto\" && opts.format !== \"keep\";\n const q = preset.quality[kind];\n const specs: EncodeSpec[] = [];\n for (const f of formats) {\n switch (f) {\n case \"webp\":\n if (!preset.lossless) specs.push({ format: \"webp\", quality: opts.quality ?? q.webp });\n if (preset.lossless || (preset.losslessWebp && kind !== \"photo\")) specs.push({ format: \"webp\", lossless: true });\n break;\n case \"avif\":\n if (explicit || pixels <= preset.avifMaxPixels) specs.push({ format: \"avif\", quality: opts.quality ?? q.avif });\n break;\n case \"jpeg\":\n if (!info.hasAlpha && !preset.lossless) specs.push({ format: \"jpeg\", quality: opts.quality ?? q.jpeg });\n break;\n case \"png\":\n if (preset.lossless || kind === \"graphic\") specs.push({ format: \"png\", lossless: true });\n if (!preset.lossless) specs.push({ format: \"png\", quality: opts.quality ?? q.png });\n break;\n case \"gif\":\n break;\n }\n }\n return specs;\n}\n\nexport async function optimizeFile(file: string, ctx: Ctx): Promise<FileResult> {\n const started = Date.now();\n let info: ImageInfo;\n try {\n info = await probe(file);\n } catch (e) {\n return failed({ path: file, format: \"png\", width: 0, height: 0, bytes: 0, hasAlpha: false, animated: false }, e, started);\n }\n let result: FileResult;\n try {\n if (info.format === \"svg\") result = await handleSvg(info, ctx, started);\n else if (info.animated) result = await handleAnimated(info, ctx, started);\n else result = await handleRaster(info, ctx, started);\n } catch (e) {\n return failed(info, e, started);\n }\n // With --out, an unchanged file still belongs in the output tree.\n if (result.status === \"kept\" && ctx.opts.out) {\n const dest = mirrorPath(info.path, ctx);\n if (!ctx.opts.dryRun) {\n try {\n await mkdir(path.dirname(dest), { recursive: true });\n await copyFile(info.path, dest);\n } catch (e) {\n return failed(info, e, started);\n }\n }\n result.copiedTo = dest;\n }\n // The --target-kb contract applies to whatever ends up on disk, kept files included.\n if (ctx.opts.targetKb && result.status !== \"failed\") {\n const finalBytes = result.output && result.status !== \"kept\" ? result.output.bytes : result.input.bytes;\n if (finalBytes > ctx.opts.targetKb * 1024) result.status = \"budget-missed\";\n }\n return result;\n}\n\nfunction failed(info: ImageInfo, e: unknown, started: number): FileResult {\n return {\n input: info, output: null, kind: \"photo\", status: \"failed\", savedBytes: 0, savedPercent: 0,\n actions: [], candidates: [], error: e instanceof Error ? e.message : String(e), durationMs: Date.now() - started,\n };\n}\n\nfunction kept(info: ImageInfo, kind: FileResult[\"kind\"], reason: string, candidates: Candidate[], actions: string[], started: number): FileResult {\n return { input: info, output: null, kind, status: \"kept\", savedBytes: 0, savedPercent: 0, actions, candidates, reason, durationMs: Date.now() - started };\n}\n\nasync function exists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function finish(\n info: ImageInfo, kind: FileResult[\"kind\"], data: Buffer | string, out: OutputInfo, candidates: Candidate[],\n actions: string[], ctx: Ctx, started: number, status: FileResult[\"status\"] = \"optimized\",\n): Promise<FileResult> {\n const separateOut = Boolean(ctx.opts.out) && path.resolve(out.path) !== path.resolve(info.path);\n // Never clobber a different file that already sits at the output path (e.g. a.png → a.webp when a.webp exists).\n if (out.path !== info.path && !separateOut && (await exists(out.path))) {\n return kept(info, kind, `output path already exists: ${path.basename(out.path)} (remove it or use --suffix/--out)`, candidates, actions, started);\n }\n let backupPath: string | undefined;\n if (!ctx.opts.dryRun) {\n const w = await writeOutput(data, { inputPath: info.path, outputPath: out.path, backup: ctx.opts.backup, separateOut });\n backupPath = w.backupPath;\n }\n const savedBytes = info.bytes - out.bytes;\n return {\n input: info, output: out, kind, status, savedBytes, savedPercent: round1((savedBytes / info.bytes) * 100),\n actions, candidates, backupPath, durationMs: Date.now() - started,\n };\n}\n\nfunction belowMinGain(info: ImageInfo, bytes: number, ctx: Ctx, sameLossyFormat: boolean): string | null {\n const gain = ((info.bytes - bytes) / info.bytes) * 100;\n const min = sameLossyFormat ? Math.max(ctx.preset.minGainPercent, 10) : ctx.preset.minGainPercent;\n if (bytes >= info.bytes) return `no candidate smaller than original (best: ${fmtBytes(bytes)})`;\n if (gain < min) return `gain ${round1(gain)}% is below the ${min}% threshold${sameLossyFormat ? \" for re-encoding an already lossy file\" : \"\"}`;\n return null;\n}\n\nasync function handleSvg(info: ImageInfo, ctx: Ctx, started: number): Promise<FileResult> {\n const src = await readFile(info.path, \"utf8\");\n const outData = optimizeSvg(src);\n const bytes = Buffer.byteLength(outData);\n const candidates: Candidate[] = [{ format: \"svg\", bytes, lossless: true, passed: true }];\n const why = belowMinGain(info, bytes, ctx, false);\n if (why) return kept(info, \"svg\", why, candidates, [], started);\n const outPath = outputPathFor(info.path, \"svg\", ctx);\n return finish(info, \"svg\", outData, { path: outPath, format: \"svg\", width: info.width, height: info.height, bytes, lossless: true }, candidates, [\"svgo\"], ctx, started);\n}\n\nasync function handleAnimated(info: ImageInfo, ctx: Ctx, started: number): Promise<FileResult> {\n const formats = allowedFormats(info, ctx);\n if (!formats.includes(\"webp\")) {\n return kept(info, \"animated\", `animated input; preset \"${ctx.preset.name}\" does not allow WebP so it was left as-is`, [], [], started);\n }\n const size = targetSize(info, ctx);\n const actions: string[] = [];\n const q = ctx.opts.quality ?? ctx.preset.quality.ui.webp;\n let img = sharp(info.path, { animated: true });\n if (size.resized) {\n img = img.resize({ width: size.width, height: size.height, fit: \"inside\", withoutEnlargement: true, kernel: \"lanczos3\" });\n actions.push(`resize:${size.width}x${size.height}`);\n }\n actions.push(\"strip-metadata\");\n const lossless = ctx.preset.lossless;\n const buf = await (lossless ? img.webp({ lossless: true, effort: 6 }) : img.webp({ quality: q, effort: 4 })).toBuffer();\n const candidates: Candidate[] = [{ format: \"webp\", bytes: buf.length, quality: lossless ? undefined : q, lossless, passed: true }];\n const why = belowMinGain(info, buf.length, ctx, info.format === \"webp\" && !lossless);\n if (why) return kept(info, \"animated\", why, candidates, actions, started);\n actions.push(lossless ? \"encode:webp-animated-lossless\" : `encode:webp-animated@${q}`);\n const outPath = outputPathFor(info.path, \"webp\", ctx);\n return finish(\n info, \"animated\", buf,\n { path: outPath, format: \"webp\", width: size.width, height: size.height, bytes: buf.length, quality: lossless ? undefined : q, lossless },\n candidates, actions, ctx, started,\n );\n}\n\nasync function handleRaster(input: ImageInfo, ctx: Ctx, started: number): Promise<FileResult> {\n let info = input;\n const { preset, opts } = ctx;\n const actions: string[] = [];\n const explicitKind: Kind | undefined = preset.forceKind ?? (opts.kind && opts.kind !== \"auto\" ? opts.kind : undefined);\n const kind: Kind = explicitKind ?? (await classify(info)).kind;\n const size = targetSize(info, ctx);\n\n let pipeline = sharp(info.path, { pages: 1 }).rotate();\n if (size.resized) {\n pipeline = pipeline.resize({ width: size.width, height: size.height, fit: \"inside\", withoutEnlargement: true, kernel: \"lanczos3\" });\n actions.push(`resize:${size.width}x${size.height}`);\n }\n pipeline = pipeline.toColourspace(\"srgb\");\n pipeline = info.hasAlpha ? pipeline.ensureAlpha() : pipeline.removeAlpha();\n const { data, info: rawInfo } = await pipeline.raw().toBuffer({ resolveWithObject: true });\n let raw: RawImage = { data, width: rawInfo.width, height: rawInfo.height, channels: rawInfo.channels };\n // Many screenshot PNGs carry an alpha channel that is 255 everywhere. Checked on the actual pixels\n // (not a sample): dropping it unlocks JPEG and smaller WebP/AVIF without changing what is displayed.\n if (info.hasAlpha && alphaIsOpaque(raw)) {\n raw = stripAlpha(raw);\n info = { ...info, hasAlpha: false };\n actions.push(\"drop-unused-alpha\");\n }\n actions.push(\"strip-metadata\");\n\n const specs = buildSpecs(info, kind, raw.width * raw.height, ctx);\n if (specs.length === 0) return kept(info, kind, \"no encoder allowed for this input under the current preset/format\", [], actions, started);\n\n const floor = preset.floor[kind];\n const reference = opts.fast ? null : await toComparable(fromRaw(raw), raw.width, info.hasAlpha);\n const targetBytes = opts.targetKb ? opts.targetKb * 1024 : undefined;\n\n const encoded = await Promise.all(\n specs.map(async (spec) => {\n if (spec.lossless) {\n const buf = await encode(raw, spec);\n return { spec, buf, cand: { format: spec.format, bytes: buf.length, lossless: true, ssim: 1, passed: true } as Candidate };\n }\n return searchQuality(raw, spec, reference, floor, targetBytes);\n }),\n );\n\n const candidates = encoded.map((e) => e.cand);\n const passed = encoded.filter((e) => e.cand.passed);\n if (passed.length === 0) {\n return kept(info, kind, `no candidate met the SSIM floor of ${floor} (try --fast, a lower --preset floor, or --kind photo)`, candidates, actions, started);\n }\n passed.sort((a, b) => a.cand.bytes - b.cand.bytes);\n let best = passed[0];\n if (best.spec.format === \"avif\") {\n const bestOther = passed.find((e) => e.spec.format !== \"avif\");\n if (bestOther && best.cand.bytes > bestOther.cand.bytes * (1 - preset.avifMinGain)) best = bestOther;\n }\n\n // Re-encoding a lossy file (or palette-quantising a PNG again) for a tiny gain only degrades it.\n const sameLossy = best.spec.format === info.format && !best.cand.lossless && (LOSSY_INPUTS.has(info.format) || info.format === \"png\") && !size.resized;\n const why = belowMinGain(info, best.cand.bytes, ctx, sameLossy);\n if (why) return kept(info, kind, why, candidates, actions, started);\n\n actions.push(`encode:${best.spec.format}${best.cand.lossless ? \"-lossless\" : `@${best.cand.quality}`}`);\n const outPath = outputPathFor(info.path, best.spec.format, ctx);\n if (!opts.rename && extFor(best.spec.format) !== path.extname(info.path).toLowerCase().replace(\".jpeg\", \".jpg\")) {\n actions.push(`warn:extension-mismatch(${path.extname(info.path)} contains ${best.spec.format})`);\n }\n const status: FileResult[\"status\"] = targetBytes && best.cand.bytes > targetBytes ? \"budget-missed\" : \"optimized\";\n return finish(\n info, kind, best.buf,\n { path: outPath, format: best.spec.format, width: raw.width, height: raw.height, bytes: best.cand.bytes, quality: best.cand.quality, lossless: best.cand.lossless },\n candidates, actions, ctx, started, status,\n );\n}\n\ninterface Encoded {\n spec: EncodeSpec;\n buf: Buffer;\n cand: Candidate;\n}\n\nconst MAX_Q = 100;\nconst MIN_Q = 1;\nconst STEP = 6;\nconst RETRIES = 3;\n\nasync function searchQuality(\n raw: RawImage, spec: EncodeSpec, reference: Comparable | null, floor: number, targetBytes?: number,\n): Promise<Encoded> {\n const cache = new Map<number, Buffer>();\n const enc = async (q: number) => {\n let b = cache.get(q);\n if (!b) {\n b = await encode(raw, { ...spec, quality: q });\n cache.set(q, b);\n }\n return b;\n };\n const measure = async (q: number): Promise<Encoded> => {\n const buf = await enc(q);\n const ssim = reference ? await compareSsim(reference, buf) : undefined;\n const passed = ssim === undefined ? true : ssim >= floor;\n return { spec: { ...spec, quality: q }, buf, cand: { format: spec.format, bytes: buf.length, quality: q, ssim: ssim === undefined ? undefined : Number(ssim.toFixed(4)), passed } };\n };\n\n const startQ = Math.min(MAX_Q, Math.max(MIN_Q, spec.quality ?? 80));\n\n const floorSearch = async (from: number): Promise<Encoded> => {\n let q = from;\n let r = await measure(q);\n for (let i = 0; i < RETRIES && !r.cand.passed && q < MAX_Q; i++) {\n q = Math.min(MAX_Q, q + STEP);\n r = await measure(q);\n }\n return r;\n };\n\n if (!targetBytes) return floorSearch(startQ);\n\n // Highest quality whose size fits the budget (size grows with quality).\n let lo = MIN_Q;\n let hi = Math.min(MAX_Q, startQ + 10);\n let fit: number | null = null;\n while (lo <= hi) {\n const mid = Math.floor((lo + hi) / 2);\n const b = await enc(mid);\n if (b.length <= targetBytes) {\n fit = mid;\n lo = mid + 1;\n } else hi = mid - 1;\n }\n const budgeted = await floorSearch(fit ?? MIN_Q);\n if (budgeted.cand.passed) return budgeted;\n // Budget is unreachable within the quality floor: fall back to the normal search so the\n // caller still gets the best passing candidate (reported as budget-missed).\n const fallback = await floorSearch(startQ);\n return fallback.cand.passed ? fallback : budgeted;\n}\n\nexport async function inspect(paths: string[], options: Omit<OptimizeOptions, \"paths\"> = {}): Promise<InspectResult[]> {\n const opts = resolveOptions({ ...options, paths });\n const preset = getPreset(opts.preset);\n const { files, roots } = await discover(paths, opts.cwd);\n const ctx: Ctx = { opts, preset, roots };\n const out: InspectResult[] = [];\n for (const f of files) {\n let info = await probe(f);\n let kind: InspectResult[\"kind\"];\n let floor: number | null = null;\n let candidates: string[] = [];\n if (info.format === \"svg\") {\n kind = \"svg\";\n candidates = [\"svgo\"];\n } else if (info.animated) {\n kind = \"animated\";\n candidates = allowedFormats(info, ctx).includes(\"webp\") ? [\"webp-animated\"] : [];\n } else {\n const cls = await classify(info);\n if (info.hasAlpha && cls.stats.transparentShare === 0) info = { ...info, hasAlpha: false }; // best-effort preview; optimize checks every pixel\n kind = preset.forceKind ?? (opts.kind && opts.kind !== \"auto\" ? opts.kind : cls.kind);\n floor = preset.floor[kind];\n const size = targetSize(info, ctx);\n candidates = buildSpecs(info, kind, size.width * size.height, ctx).map((s) => `${s.format}${s.lossless ? \"-lossless\" : `@${s.quality}`}`);\n }\n const size = targetSize(info, ctx);\n out.push({ input: info, kind, plan: { targetWidth: size.width, targetHeight: size.height, candidates, floor } });\n }\n return out;\n}\n\nexport function fmtBytes(n: number): string {\n if (n < 1024) return `${n} B`;\n if (n < 1024 * 1024) return `${(n / 1024).toFixed(n < 10 * 1024 ? 1 : 0)} KB`;\n return `${(n / (1024 * 1024)).toFixed(2)} MB`;\n}\n\n// re-export for the doctor command\nexport { applyEncoder };\n","import sharp from \"sharp\";\nimport { createRequire } from \"node:module\";\n\nconst require = createRequire(import.meta.url);\n// ssim.js is CommonJS; its default export is the ssim() function.\nconst ssimLib = require(\"ssim.js\") as { default?: SsimFn } & SsimFn;\ntype SsimImage = { data: Uint8ClampedArray; width: number; height: number };\ntype SsimFn = (a: SsimImage, b: SsimImage, opts?: Record<string, unknown>) => { mssim: number };\nconst ssim: SsimFn = (ssimLib.default ?? ssimLib) as SsimFn;\n\nexport interface RawImage {\n data: Buffer;\n width: number;\n height: number;\n channels: number;\n}\n\n/** What we compare: the image flattened over gray, plus its alpha channel (as gray) when it has one. */\nexport interface Comparable {\n color: SsimImage;\n alpha?: SsimImage;\n width: number;\n height: number;\n}\n\nconst SSIM_MAX_WIDTH = 512;\nconst FLATTEN_BG = { r: 128, g: 128, b: 128 };\n\nfunction asImage(data: Buffer, width: number, height: number): SsimImage {\n return { data: new Uint8ClampedArray(data.buffer, data.byteOffset, data.byteLength), width, height };\n}\n\n/**\n * Downsample to a comparison-friendly size. Color is compared flattened over mid-gray (what a viewer sees);\n * alpha is compared separately so transparency damage can't hide behind the flatten background.\n */\nexport async function toComparable(input: ReturnType<typeof sharp>, width: number, hasAlpha: boolean, height?: number): Promise<Comparable> {\n const target = Math.min(SSIM_MAX_WIDTH, width);\n const resize = height ? { width: target, height, fit: \"fill\" as const } : { width: target, withoutEnlargement: true, kernel: \"lanczos3\" as const };\n const base = input.clone().resize(resize).ensureAlpha();\n const { data: rgba, info } = await base.clone().raw().toBuffer({ resolveWithObject: true });\n const flat = await sharp(rgba, { raw: { width: info.width, height: info.height, channels: 4 } })\n .flatten({ background: FLATTEN_BG })\n .ensureAlpha()\n .raw()\n .toBuffer();\n const out: Comparable = { color: asImage(flat, info.width, info.height), width: info.width, height: info.height };\n if (hasAlpha) {\n // alpha → gray RGBA so ssim.js can consume it\n const a = Buffer.alloc(info.width * info.height * 4);\n for (let i = 0, j = 0; i < rgba.length; i += 4, j += 4) {\n const v = rgba[i + 3];\n a[j] = v; a[j + 1] = v; a[j + 2] = v; a[j + 3] = 255;\n }\n out.alpha = asImage(a, info.width, info.height);\n }\n return out;\n}\n\nexport function fromRaw(raw: RawImage): ReturnType<typeof sharp> {\n return sharp(raw.data, { raw: { width: raw.width, height: raw.height, channels: raw.channels as 1 | 2 | 3 | 4 } });\n}\n\n/** Structural similarity in [0, 1] between the reference and an encoded candidate (min of color and alpha). */\nexport async function compareSsim(reference: Comparable, encoded: Buffer): Promise<number> {\n const hasAlpha = Boolean(reference.alpha);\n let cand = await toComparable(sharp(encoded, { pages: 1 }), reference.width, hasAlpha);\n if (cand.width !== reference.width || cand.height !== reference.height) {\n cand = await toComparable(sharp(encoded, { pages: 1 }), reference.width, hasAlpha, reference.height);\n }\n let score = ssim(reference.color, cand.color, { ssim: \"fast\" }).mssim;\n if (reference.alpha && cand.alpha) score = Math.min(score, ssim(reference.alpha, cand.alpha, { ssim: \"fast\" }).mssim);\n return score;\n}\n\n/** True when every alpha value in a 4-channel raw buffer is 255. */\nexport function alphaIsOpaque(raw: RawImage): boolean {\n if (raw.channels !== 4) return true;\n const d = raw.data;\n for (let i = 3; i < d.length; i += 4) if (d[i] !== 255) return false;\n return true;\n}\n\n/** Drop the alpha channel from a 4-channel raw buffer. */\nexport function stripAlpha(raw: RawImage): RawImage {\n if (raw.channels !== 4) return raw;\n const px = raw.width * raw.height;\n const out = Buffer.alloc(px * 3);\n const d = raw.data;\n for (let i = 0, j = 0; i < px; i++, j += 3) {\n const k = i * 4;\n out[j] = d[k]; out[j + 1] = d[k + 1]; out[j + 2] = d[k + 2];\n }\n return { data: out, width: raw.width, height: raw.height, channels: 3 };\n}\n","import sharp from \"sharp\";\nimport type { RasterFormat } from \"../types.js\";\nimport { fromRaw, type RawImage } from \"./quality.js\";\n\nexport interface EncodeSpec {\n format: RasterFormat;\n quality?: number;\n lossless?: boolean;\n}\n\nexport function encode(raw: RawImage, spec: EncodeSpec): Promise<Buffer> {\n const img = fromRaw(raw);\n return applyEncoder(img, spec).toBuffer();\n}\n\nexport function applyEncoder(img: ReturnType<typeof sharp>, spec: EncodeSpec): ReturnType<typeof sharp> {\n const q = spec.quality ?? 80;\n switch (spec.format) {\n case \"webp\":\n return spec.lossless\n ? img.webp({ lossless: true, effort: 6 })\n : img.webp({ quality: q, effort: 4, alphaQuality: 90, smartSubsample: true });\n case \"avif\":\n return img.avif({ quality: q, effort: 4, chromaSubsampling: \"4:2:0\" });\n case \"jpeg\":\n return img.jpeg({ quality: q, mozjpeg: true, progressive: true });\n case \"png\":\n return spec.lossless\n ? img.png({ compressionLevel: 9, adaptiveFiltering: true, palette: false, effort: 10 })\n : img.png({ compressionLevel: 9, palette: true, quality: q, effort: 8, dither: 0.6 });\n case \"gif\":\n return img.gif({ effort: 8 });\n }\n}\n\nexport function extFor(format: RasterFormat | \"svg\"): string {\n return format === \"jpeg\" ? \".jpg\" : `.${format}`;\n}\n","import { optimize as svgoOptimize } from \"svgo\";\n\n/**\n * svgo with the default preset, minus two things that silently break pages:\n * id minification (ids are referenced from CSS/JS) and viewBox removal.\n * width/height are kept: removing them changes the intrinsic size of an <img>.\n */\nexport function optimizeSvg(source: string): string {\n const r = svgoOptimize(source, {\n multipass: true,\n plugins: [\n {\n name: \"preset-default\",\n params: {\n overrides: {\n cleanupIds: false,\n },\n },\n },\n ],\n });\n return r.data;\n}\n","import { access, mkdir, rename, rm, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { randomBytes } from \"node:crypto\";\nimport { BACKUP_DIR } from \"./discover.js\";\n\nexport interface WritePlan {\n inputPath: string;\n outputPath: string;\n backup: boolean;\n /** True when the output lives somewhere else (--out); the original is left untouched. */\n separateOut: boolean;\n}\n\nexport interface WriteResult {\n backupPath?: string;\n}\n\n/**\n * Atomic write. The encoded data goes to a temp file next to the destination, then:\n * - output path == input path: move the original to the backup dir (if enabled), rename temp over it,\n * and restore the original if that rename fails;\n * - output path != input path: rename temp into place FIRST, and only then move/delete the original,\n * so a failed publish never loses the input.\n */\nexport async function writeOutput(data: Buffer | string, plan: WritePlan): Promise<WriteResult> {\n const outDir = path.dirname(plan.outputPath);\n await mkdir(outDir, { recursive: true });\n const tmp = path.join(outDir, `.${path.basename(plan.outputPath)}.${randomBytes(6).toString(\"hex\")}.tmp`);\n await writeFile(tmp, data, { flag: \"wx\" });\n const res: WriteResult = {};\n const samePath = path.resolve(plan.inputPath) === path.resolve(plan.outputPath);\n\n try {\n if (plan.separateOut && !samePath) {\n await rename(tmp, plan.outputPath);\n return res;\n }\n if (samePath) {\n let backupPath: string | undefined;\n if (plan.backup) {\n backupPath = await moveToBackup(plan.inputPath);\n res.backupPath = backupPath;\n }\n try {\n await rename(tmp, plan.outputPath);\n } catch (e) {\n if (backupPath) await rename(backupPath, plan.inputPath).catch(() => undefined);\n throw e;\n }\n return res;\n }\n // different name in the same directory (extension changed)\n await rename(tmp, plan.outputPath);\n if (plan.backup) res.backupPath = await moveToBackup(plan.inputPath);\n else await rm(plan.inputPath, { force: true });\n return res;\n } finally {\n await rm(tmp, { force: true }).catch(() => undefined);\n }\n}\n\nasync function moveToBackup(inputPath: string): Promise<string> {\n const backupDir = path.join(path.dirname(inputPath), BACKUP_DIR);\n await mkdir(backupDir, { recursive: true });\n const backupPath = await uniquePath(path.join(backupDir, path.basename(inputPath)));\n await rename(inputPath, backupPath);\n return backupPath;\n}\n\nasync function uniquePath(p: string): Promise<string> {\n let candidate = p;\n let i = 1;\n for (;;) {\n try {\n await access(candidate);\n } catch {\n return candidate;\n }\n const ext = path.extname(p);\n candidate = path.join(path.dirname(p), `${path.basename(p, ext)}.${i++}${ext}`);\n }\n}\n","import { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { UsageError } from \"./errors.js\";\nimport type { OptimizeOptions } from \"./types.js\";\n\nexport type FileConfig = Partial<Omit<OptimizeOptions, \"paths\" | \"cwd\" | \"onFile\">>;\n\nconst CANDIDATES = [\"img-subir.config.json\", \".img-subirrc.json\", \".img-subirrc\"];\n\nasync function readIfExists(file: string): Promise<string | null> {\n try {\n return await readFile(file, \"utf8\");\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === \"ENOENT\" || (e as NodeJS.ErrnoException).code === \"ENOTDIR\") return null;\n throw new UsageError(`Cannot read config ${file}: ${(e as Error).message}`);\n }\n}\n\nfunction parse(file: string, text: string): unknown {\n try {\n return JSON.parse(text);\n } catch (e) {\n throw new UsageError(`Invalid JSON in ${file}: ${(e as Error).message}`);\n }\n}\n\n/**\n * Looks in cwd and its ancestors for a config file or a package.json \"img-subir\" key.\n * A malformed config is an error, not a silent fallback to defaults.\n */\nexport async function loadConfig(cwd: string): Promise<{ config: FileConfig; source?: string }> {\n let dir = path.resolve(cwd);\n for (let depth = 0; depth < 8; depth++) {\n for (const name of CANDIDATES) {\n const file = path.join(dir, name);\n const text = await readIfExists(file);\n if (text !== null) return { config: parse(file, text) as FileConfig, source: file };\n }\n const pkgFile = path.join(dir, \"package.json\");\n const pkgText = await readIfExists(pkgFile);\n if (pkgText !== null) {\n const pkg = parse(pkgFile, pkgText) as { \"img-subir\"?: FileConfig };\n if (pkg[\"img-subir\"]) return { config: pkg[\"img-subir\"], source: pkgFile };\n }\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return { config: {} };\n}\n"],"mappings":"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","names":["path","path","stat","path","sharp","width","height","m","fg","path","path","fg","require","access","mkdir","readFile","path","sharp","sharp","createRequire","require","base","writeFile","path","path","writeFile","path","mkdir","access","readFile","sharp","ssim","size","readFile","path","readFile","path"]}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
UsageError,
|
|
4
|
+
VERSION,
|
|
5
|
+
fmtBytes,
|
|
6
|
+
getPreset,
|
|
7
|
+
inspect,
|
|
8
|
+
loadConfig,
|
|
9
|
+
optimize,
|
|
10
|
+
presetNames,
|
|
11
|
+
presets
|
|
12
|
+
} from "./chunk-CWZBNFPK.js";
|
|
13
|
+
|
|
14
|
+
// src/cli.ts
|
|
15
|
+
import { defineCommand, renderUsage, runCommand } from "citty";
|
|
16
|
+
import { readFile } from "fs/promises";
|
|
17
|
+
import path from "path";
|
|
18
|
+
import { fileURLToPath } from "url";
|
|
19
|
+
import pc from "picocolors";
|
|
20
|
+
import sharp from "sharp";
|
|
21
|
+
var sharedArgs = {
|
|
22
|
+
preset: { type: "string", alias: "p", description: `Preset: ${presetNames.join(" | ")} (default: web)` },
|
|
23
|
+
format: { type: "string", alias: "f", description: "auto | keep | webp | avif | png | jpeg (default: auto)" },
|
|
24
|
+
kind: { type: "string", description: "auto | ui | photo | graphic (default: auto)" },
|
|
25
|
+
"max-width": { type: "string", description: "Cap width in px (overrides preset)" },
|
|
26
|
+
"max-height": { type: "string", description: "Cap height in px (overrides preset)" },
|
|
27
|
+
quality: { type: "string", alias: "q", description: "Starting quality 1-100 for lossy encoders (overrides preset)" },
|
|
28
|
+
json: { type: "boolean", description: "Machine-readable JSON on stdout; human log goes to stderr" },
|
|
29
|
+
quiet: { type: "boolean", description: "Only print the summary line" },
|
|
30
|
+
verbose: { type: "boolean", description: "Print every candidate that was tried" }
|
|
31
|
+
};
|
|
32
|
+
function num(v, name, opts = {}) {
|
|
33
|
+
if (v === void 0 || v === null) return void 0;
|
|
34
|
+
if (v === "" || typeof v === "string" && v.startsWith("-")) throw new UsageError(`--${name} needs a value`);
|
|
35
|
+
const n = Number(v);
|
|
36
|
+
const { min = 0, max = Number.POSITIVE_INFINITY, integer = false } = opts;
|
|
37
|
+
if (!Number.isFinite(n) || n <= min || n > max || integer && !Number.isInteger(n)) {
|
|
38
|
+
throw new UsageError(`--${name} must be ${integer ? "an integer" : "a number"} ${min === 0 ? "> 0" : `\u2265 ${min + 1}`}${Number.isFinite(max) ? ` and \u2264 ${max}` : ""}, got "${String(v)}"`);
|
|
39
|
+
}
|
|
40
|
+
return n;
|
|
41
|
+
}
|
|
42
|
+
function camel(s) {
|
|
43
|
+
return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
44
|
+
}
|
|
45
|
+
function rejectUnknownArgs(args, defs) {
|
|
46
|
+
const known = /* @__PURE__ */ new Set(["_"]);
|
|
47
|
+
for (const [name, def] of Object.entries(defs)) {
|
|
48
|
+
known.add(name);
|
|
49
|
+
known.add(camel(name));
|
|
50
|
+
const alias = def.alias ?? [];
|
|
51
|
+
for (const a of [alias].flat()) known.add(a);
|
|
52
|
+
}
|
|
53
|
+
const unknown = Object.keys(args).filter((k) => !known.has(k));
|
|
54
|
+
if (unknown.length) throw new UsageError(`Unknown option${unknown.length > 1 ? "s" : ""}: ${unknown.map((u) => `--${u}`).join(", ")}. See --help.`);
|
|
55
|
+
for (const [k, v] of Object.entries(args)) {
|
|
56
|
+
if (k !== "_" && typeof v === "string" && v.startsWith("-")) throw new UsageError(`--${k} needs a value (got "${v}")`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function sharedOptions(args, config) {
|
|
60
|
+
const presetName = args.preset ?? config.preset ?? "web";
|
|
61
|
+
getPreset(presetName);
|
|
62
|
+
const format = args.format ?? config.format ?? "auto";
|
|
63
|
+
if (!["auto", "keep", "webp", "avif", "png", "jpeg"].includes(format)) throw new UsageError(`--format must be auto|keep|webp|avif|png|jpeg, got "${format}"`);
|
|
64
|
+
const kind = args.kind ?? config.kind ?? "auto";
|
|
65
|
+
if (!["auto", "ui", "photo", "graphic"].includes(kind)) throw new UsageError(`--kind must be auto|ui|photo|graphic, got "${kind}"`);
|
|
66
|
+
return {
|
|
67
|
+
preset: presetName,
|
|
68
|
+
format,
|
|
69
|
+
kind,
|
|
70
|
+
maxWidth: num(args["max-width"], "max-width", { integer: true }) ?? config.maxWidth,
|
|
71
|
+
maxHeight: num(args["max-height"], "max-height", { integer: true }) ?? config.maxHeight,
|
|
72
|
+
quality: num(args.quality, "quality", { integer: true, max: 100 }) ?? config.quality
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function pct(n) {
|
|
76
|
+
return `${n >= 0 ? "\u2212" : "+"}${Math.abs(n).toFixed(1)}%`;
|
|
77
|
+
}
|
|
78
|
+
function logFile(r, log, cwd, verbose) {
|
|
79
|
+
const rel = (p) => path.relative(cwd, p) || p;
|
|
80
|
+
const dims = r.input.width ? `${r.input.width}\xD7${r.input.height}` : "";
|
|
81
|
+
const left = `${rel(r.input.path)} ${pc.dim(`${dims} ${r.input.format.toUpperCase()} ${fmtBytes(r.input.bytes)}`)}`;
|
|
82
|
+
if (r.status === "failed") {
|
|
83
|
+
log(` ${pc.red("\u2716")} ${left} ${pc.red(`failed: ${r.error}`)}`);
|
|
84
|
+
} else if (r.status === "kept" || r.status === "budget-missed" && !r.output) {
|
|
85
|
+
log(` ${pc.yellow("\xB7")} ${left} ${pc.dim(`kept: ${r.reason}${r.copiedTo ? " (copied to --out)" : ""}`)}${r.status === "budget-missed" ? pc.yellow(" (over --target-kb budget)") : ""}`);
|
|
86
|
+
} else if (r.output) {
|
|
87
|
+
const mark = r.status === "budget-missed" ? pc.yellow("!") : pc.green("\u2714");
|
|
88
|
+
const odims = `${r.output.width}\xD7${r.output.height}`;
|
|
89
|
+
const q = r.output.lossless ? "lossless" : r.output.quality ? `q${r.output.quality}` : "";
|
|
90
|
+
log(
|
|
91
|
+
` ${mark} ${left} ${pc.yellow("\u2192")} ${path.basename(r.output.path)} ${pc.dim(`${odims} ${q}`)} ${pc.green(`${fmtBytes(r.output.bytes)} ${pct(r.savedPercent)}`)}${r.status === "budget-missed" ? pc.yellow(" (over --target-kb budget)") : ""}`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
if (verbose && r.candidates.length) {
|
|
95
|
+
for (const c of r.candidates) {
|
|
96
|
+
const tag = `${c.format}${c.lossless ? "-lossless" : c.quality ? `@${c.quality}` : ""}`;
|
|
97
|
+
log(pc.dim(` ${c.passed ? "ok " : "rej"} ${tag.padEnd(16)} ${fmtBytes(c.bytes).padStart(9)}${c.ssim !== void 0 ? ` ssim ${c.ssim}` : ""}`));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function summary(rep, log) {
|
|
102
|
+
const t = rep.totals;
|
|
103
|
+
const parts = [`${t.optimized} optimized`, t.kept ? `${t.kept} kept` : "", t.budgetMissed ? `${t.budgetMissed} over budget` : "", t.failed ? pc.red(`${t.failed} failed`) : ""].filter(Boolean);
|
|
104
|
+
log("");
|
|
105
|
+
log(`${pc.bold(pc.green(`Saved ${fmtBytes(t.inputBytes)} \u2192 ${fmtBytes(t.outputBytes)} (${pct(t.savedPercent)})`))} ${pc.dim(`in ${(t.durationMs / 1e3).toFixed(1)}s \xB7 ${parts.join(", ")}${rep.dryRun ? " \xB7 DRY RUN, nothing written" : ""}`)}`);
|
|
106
|
+
if (rep.rewrittenRefs.length) {
|
|
107
|
+
const files = new Set(rep.rewrittenRefs.map((r) => r.file)).size;
|
|
108
|
+
log(pc.dim(`Rewrote ${rep.rewrittenRefs.reduce((a, r) => a + r.count, 0)} reference(s) in ${files} file(s).`));
|
|
109
|
+
} else if (rep.renamed.length && !rep.dryRun) {
|
|
110
|
+
log(pc.yellow(`${rep.renamed.length} file(s) changed extension. Update references or re-run with --rewrite-refs.`));
|
|
111
|
+
}
|
|
112
|
+
if (rep.backupDirs.length) {
|
|
113
|
+
log(pc.dim(`Originals kept in ${rep.backupDirs.map((d) => path.relative(process.cwd(), d) || d).join(", ")} \u2014 delete or ignore before upload/commit (or use --no-backup).`));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
var optimizeArgs = {
|
|
117
|
+
...sharedArgs,
|
|
118
|
+
"target-kb": { type: "string", description: "Binary-search quality until each file is \u2264 this many KB (exit 3 if impossible within the quality floor)" },
|
|
119
|
+
out: { type: "string", alias: "o", description: "Write to this directory (mirrors structure) instead of in place" },
|
|
120
|
+
suffix: { type: "string", description: 'Insert before the extension, e.g. ".min"' },
|
|
121
|
+
backup: { type: "boolean", description: "Keep originals in .img-subir-originals/ (default)", negativeDescription: "Overwrite/delete originals without a backup" },
|
|
122
|
+
rename: { type: "boolean", description: "Change extension when the format changes (default)", negativeDescription: "Keep the original filename even if the format changes" },
|
|
123
|
+
"rewrite-refs": { type: "boolean", description: "Update src/href/url() references in html/md/tsx/css files that point at renamed images" },
|
|
124
|
+
"dry-run": { type: "boolean", alias: "n", description: "Report what would happen; write nothing" },
|
|
125
|
+
fast: { type: "boolean", description: "Skip SSIM checks and use fixed qualities (~3\xD7 faster)" },
|
|
126
|
+
concurrency: { type: "string", alias: "c", description: "Files in flight at once (default: cpus \u2212 1)" }
|
|
127
|
+
};
|
|
128
|
+
var jsonArgs = { json: { type: "boolean", description: "Output JSON" } };
|
|
129
|
+
var optimizeCmd = defineCommand({
|
|
130
|
+
meta: {
|
|
131
|
+
name: "img-subir",
|
|
132
|
+
version: VERSION,
|
|
133
|
+
description: "Optimize images for websites, READMEs and Subir pages. Agent-friendly.\n\nUsage: img-subir <paths...> [options]\nCommands: inspect <paths...> \xB7 presets \xB7 skill \xB7 doctor"
|
|
134
|
+
},
|
|
135
|
+
args: optimizeArgs,
|
|
136
|
+
async run({ args }) {
|
|
137
|
+
rejectUnknownArgs(args, optimizeArgs);
|
|
138
|
+
const paths = args._.filter(Boolean);
|
|
139
|
+
if (paths.length === 0) {
|
|
140
|
+
throw new UsageError("Pass at least one file, directory or glob. Try: img-subir ./assets");
|
|
141
|
+
}
|
|
142
|
+
const cwd = process.cwd();
|
|
143
|
+
const { config, source } = await loadConfig(cwd);
|
|
144
|
+
const json = Boolean(args.json);
|
|
145
|
+
const log = (s) => (json ? process.stderr : process.stdout).write(`${s}
|
|
146
|
+
`);
|
|
147
|
+
const quiet = Boolean(args.quiet);
|
|
148
|
+
const shared = sharedOptions(args, config);
|
|
149
|
+
const opts = {
|
|
150
|
+
paths,
|
|
151
|
+
cwd,
|
|
152
|
+
...shared,
|
|
153
|
+
targetKb: num(args["target-kb"], "target-kb") ?? config.targetKb,
|
|
154
|
+
out: args.out ?? config.out,
|
|
155
|
+
suffix: args.suffix ?? config.suffix,
|
|
156
|
+
backup: args.backup ?? config.backup ?? true,
|
|
157
|
+
rename: args.rename ?? config.rename ?? true,
|
|
158
|
+
rewriteRefs: args["rewrite-refs"] ?? config.rewriteRefs ?? false,
|
|
159
|
+
dryRun: Boolean(args["dry-run"]),
|
|
160
|
+
fast: args.fast ?? config.fast ?? false,
|
|
161
|
+
concurrency: num(args.concurrency, "concurrency", { integer: true }) ?? config.concurrency,
|
|
162
|
+
onFile: quiet ? void 0 : (r) => logFile(r, log, cwd, Boolean(args.verbose))
|
|
163
|
+
};
|
|
164
|
+
if (opts.out === "") throw new UsageError("--out needs a directory");
|
|
165
|
+
if (!quiet) {
|
|
166
|
+
log(`${pc.bold(pc.blue("img-subir"))} ${pc.dim(`v${VERSION} \xB7 preset: ${shared.preset}${shared.format !== "auto" ? ` \xB7 format: ${shared.format}` : ""}${opts.dryRun ? " \xB7 dry run" : ""}${source ? ` \xB7 config: ${path.relative(cwd, source)}` : ""}`)}`);
|
|
167
|
+
log("");
|
|
168
|
+
}
|
|
169
|
+
const report = await optimize(opts);
|
|
170
|
+
summary(report, log);
|
|
171
|
+
if (json) process.stdout.write(`${JSON.stringify(report, null, 2)}
|
|
172
|
+
`);
|
|
173
|
+
if (report.totals.failed > 0) process.exitCode = 1;
|
|
174
|
+
else if (report.totals.budgetMissed > 0) process.exitCode = 3;
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
var inspectCmd = defineCommand({
|
|
178
|
+
meta: { name: "inspect", description: "Show metadata, detected kind and what optimize would do. Writes nothing." },
|
|
179
|
+
args: sharedArgs,
|
|
180
|
+
async run({ args }) {
|
|
181
|
+
rejectUnknownArgs(args, sharedArgs);
|
|
182
|
+
const paths = args._.filter(Boolean);
|
|
183
|
+
if (paths.length === 0) throw new UsageError("Pass at least one file, directory or glob.");
|
|
184
|
+
const cwd = process.cwd();
|
|
185
|
+
const { config } = await loadConfig(cwd);
|
|
186
|
+
const results = await inspect(paths, { cwd, ...sharedOptions(args, config) });
|
|
187
|
+
if (args.json) {
|
|
188
|
+
process.stdout.write(`${JSON.stringify({ schemaVersion: 1, version: VERSION, files: results }, null, 2)}
|
|
189
|
+
`);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
for (const r of results) {
|
|
193
|
+
const i = r.input;
|
|
194
|
+
const resized = r.plan.targetWidth !== i.width ? ` \u2192 ${r.plan.targetWidth}\xD7${r.plan.targetHeight}` : "";
|
|
195
|
+
console.log(`${pc.bold(path.relative(cwd, i.path))}`);
|
|
196
|
+
console.log(` ${i.format.toUpperCase()} ${i.width}\xD7${i.height} ${fmtBytes(i.bytes)}${i.hasAlpha ? " alpha" : ""}${i.animated ? " animated" : ""}${resized}`);
|
|
197
|
+
console.log(` kind: ${pc.cyan(r.kind)}${r.plan.floor !== null ? ` ssim floor: ${r.plan.floor}` : ""}`);
|
|
198
|
+
console.log(` would try: ${r.plan.candidates.length ? r.plan.candidates.join(", ") : pc.dim("nothing")}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
var presetsCmd = defineCommand({
|
|
203
|
+
meta: { name: "presets", description: "List presets and their settings." },
|
|
204
|
+
args: jsonArgs,
|
|
205
|
+
run({ args }) {
|
|
206
|
+
rejectUnknownArgs(args, jsonArgs);
|
|
207
|
+
if (args.json) {
|
|
208
|
+
process.stdout.write(`${JSON.stringify(presets, null, 2)}
|
|
209
|
+
`);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
for (const p of Object.values(presets)) {
|
|
213
|
+
console.log(`${pc.bold(p.name)}${p.name === "web" ? pc.dim(" (default)") : ""}`);
|
|
214
|
+
console.log(` ${p.note}`);
|
|
215
|
+
console.log(pc.dim(` maxWidth: ${p.maxWidth ?? "none"}${p.maxHeight ? ` maxHeight: ${p.maxHeight}` : ""} \xB7 candidates: ${p.candidates.join(", ")}${p.losslessWebp ? " + webp-lossless" : ""} \xB7 floor ui/photo: ${p.floor.ui}/${p.floor.photo}`));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
var skillCmd = defineCommand({
|
|
220
|
+
meta: { name: "skill", description: "Print the SKILL.md that teaches an agent how to use img-subir." },
|
|
221
|
+
async run() {
|
|
222
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
223
|
+
const candidates = [path.join(here, "..", "skills", "img-subir", "SKILL.md"), path.join(here, "..", "..", "skills", "img-subir", "SKILL.md")];
|
|
224
|
+
for (const c of candidates) {
|
|
225
|
+
try {
|
|
226
|
+
process.stdout.write(await readFile(c, "utf8"));
|
|
227
|
+
return;
|
|
228
|
+
} catch {
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
throw new Error("SKILL.md not found in package");
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
var doctorCmd = defineCommand({
|
|
235
|
+
meta: { name: "doctor", description: "Verify the sharp binary and which encoders are available." },
|
|
236
|
+
args: jsonArgs,
|
|
237
|
+
async run({ args }) {
|
|
238
|
+
rejectUnknownArgs(args, jsonArgs);
|
|
239
|
+
const checks = {};
|
|
240
|
+
const nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
241
|
+
checks.node = { ok: nodeMajor >= 20, detail: `v${process.versions.node}${nodeMajor < 20 ? " (need >= 20)" : ""}` };
|
|
242
|
+
checks.sharp = { ok: true, detail: `sharp ${sharp.versions.sharp} \xB7 libvips ${sharp.versions.vips}` };
|
|
243
|
+
const px = await sharp({ create: { width: 8, height: 8, channels: 4, background: { r: 200, g: 50, b: 50, alpha: 0.8 } } }).png().toBuffer();
|
|
244
|
+
const tryEnc = async (name, fn) => {
|
|
245
|
+
try {
|
|
246
|
+
const b = await fn();
|
|
247
|
+
checks[name] = { ok: true, detail: `${b.length} bytes` };
|
|
248
|
+
} catch (e) {
|
|
249
|
+
checks[name] = { ok: false, detail: e instanceof Error ? e.message : String(e) };
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
await tryEnc("webp", () => sharp(px).webp().toBuffer());
|
|
253
|
+
await tryEnc("webp-lossless", () => sharp(px).webp({ lossless: true }).toBuffer());
|
|
254
|
+
await tryEnc("avif", () => sharp(px).avif({ effort: 2 }).toBuffer());
|
|
255
|
+
await tryEnc("jpeg-mozjpeg", () => sharp(px).jpeg({ mozjpeg: true }).toBuffer());
|
|
256
|
+
await tryEnc("png-palette", () => sharp(px).png({ palette: true }).toBuffer());
|
|
257
|
+
await tryEnc("gif", () => sharp(px).gif().toBuffer());
|
|
258
|
+
await tryEnc("animated-webp", async () => {
|
|
259
|
+
const gif = await sharp(px).gif().toBuffer();
|
|
260
|
+
return sharp(gif, { animated: true }).webp().toBuffer();
|
|
261
|
+
});
|
|
262
|
+
await tryEnc("svg-decode", () => sharp(Buffer.from('<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8"><rect width="8" height="8"/></svg>')).png().toBuffer());
|
|
263
|
+
const allOk = Object.values(checks).every((c) => c.ok);
|
|
264
|
+
if (args.json) {
|
|
265
|
+
process.stdout.write(`${JSON.stringify({ ok: allOk, version: VERSION, checks }, null, 2)}
|
|
266
|
+
`);
|
|
267
|
+
} else {
|
|
268
|
+
console.log(`${pc.bold("img-subir doctor")} v${VERSION}`);
|
|
269
|
+
for (const [k, v] of Object.entries(checks)) console.log(` ${v.ok ? pc.green("\u2714") : pc.red("\u2716")} ${k.padEnd(16)} ${pc.dim(v.detail)}`);
|
|
270
|
+
console.log(allOk ? pc.green("All good.") : pc.red("Some encoders are unavailable."));
|
|
271
|
+
}
|
|
272
|
+
if (!allOk) process.exitCode = 1;
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
var subCommands = { optimize: optimizeCmd, inspect: inspectCmd, presets: presetsCmd, skill: skillCmd, doctor: doctorCmd };
|
|
276
|
+
var first = process.argv[2];
|
|
277
|
+
var cmd = first && first in subCommands ? subCommands[first] : optimizeCmd;
|
|
278
|
+
var rawArgs = cmd === optimizeCmd && first !== "optimize" ? process.argv.slice(2) : process.argv.slice(3);
|
|
279
|
+
async function main() {
|
|
280
|
+
if (rawArgs.includes("--help") || rawArgs.includes("-h")) {
|
|
281
|
+
console.log(await renderUsage(cmd));
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (rawArgs.includes("--version") || rawArgs.includes("-v")) {
|
|
285
|
+
console.log(VERSION);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
await runCommand(cmd, { rawArgs });
|
|
289
|
+
}
|
|
290
|
+
main().catch((e) => {
|
|
291
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
292
|
+
console.error(pc.red(`img-subir: ${msg}`));
|
|
293
|
+
process.exit(e instanceof UsageError ? 2 : 1);
|
|
294
|
+
});
|
|
295
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { defineCommand, renderUsage, runCommand, type CommandDef } from \"citty\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport pc from \"picocolors\";\nimport sharp from \"sharp\";\nimport { loadConfig } from \"./config.js\";\nimport { fmtBytes, inspect, optimize } from \"./core/optimize.js\";\nimport { getPreset, presetNames, presets } from \"./presets.js\";\nimport type { FileResult, FormatChoice, Kind, OptimizeOptions, PresetName, Report } from \"./types.js\";\nimport { VERSION } from \"./version.js\";\n\nconst sharedArgs = {\n preset: { type: \"string\", alias: \"p\", description: `Preset: ${presetNames.join(\" | \")} (default: web)` },\n format: { type: \"string\", alias: \"f\", description: \"auto | keep | webp | avif | png | jpeg (default: auto)\" },\n kind: { type: \"string\", description: \"auto | ui | photo | graphic (default: auto)\" },\n \"max-width\": { type: \"string\", description: \"Cap width in px (overrides preset)\" },\n \"max-height\": { type: \"string\", description: \"Cap height in px (overrides preset)\" },\n quality: { type: \"string\", alias: \"q\", description: \"Starting quality 1-100 for lossy encoders (overrides preset)\" },\n json: { type: \"boolean\", description: \"Machine-readable JSON on stdout; human log goes to stderr\" },\n quiet: { type: \"boolean\", description: \"Only print the summary line\" },\n verbose: { type: \"boolean\", description: \"Print every candidate that was tried\" },\n} as const;\n\nfunction num(v: unknown, name: string, opts: { min?: number; max?: number; integer?: boolean } = {}): number | undefined {\n if (v === undefined || v === null) return undefined;\n if (v === \"\" || (typeof v === \"string\" && v.startsWith(\"-\"))) throw new UsageError(`--${name} needs a value`);\n const n = Number(v);\n const { min = 0, max = Number.POSITIVE_INFINITY, integer = false } = opts;\n if (!Number.isFinite(n) || n <= min || n > max || (integer && !Number.isInteger(n))) {\n throw new UsageError(`--${name} must be ${integer ? \"an integer\" : \"a number\"} ${min === 0 ? \"> 0\" : `≥ ${min + 1}`}${Number.isFinite(max) ? ` and ≤ ${max}` : \"\"}, got \"${String(v)}\"`);\n }\n return n;\n}\n\nfunction camel(s: string): string {\n return s.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());\n}\n\n/** citty accepts any --flag; an unattended agent typing --dryrun must not silently get real writes. */\nfunction rejectUnknownArgs(args: Record<string, unknown>, defs: Record<string, unknown>): void {\n const known = new Set<string>([\"_\"]);\n for (const [name, def] of Object.entries(defs)) {\n known.add(name);\n known.add(camel(name));\n const alias = (def as { alias?: string | string[] }).alias ?? [];\n for (const a of [alias].flat()) known.add(a);\n }\n const unknown = Object.keys(args).filter((k) => !known.has(k));\n if (unknown.length) throw new UsageError(`Unknown option${unknown.length > 1 ? \"s\" : \"\"}: ${unknown.map((u) => `--${u}`).join(\", \")}. See --help.`);\n for (const [k, v] of Object.entries(args)) {\n if (k !== \"_\" && typeof v === \"string\" && v.startsWith(\"-\")) throw new UsageError(`--${k} needs a value (got \"${v}\")`);\n }\n}\n\ntype SharedArgs = Record<string, unknown> & { _: string[] };\n\n/** Preset/format/kind/size/quality options shared by optimize and inspect: flags beat config beat preset. */\nfunction sharedOptions(args: SharedArgs, config: Awaited<ReturnType<typeof loadConfig>>[\"config\"]): Pick<OptimizeOptions, \"preset\" | \"format\" | \"kind\" | \"maxWidth\" | \"maxHeight\" | \"quality\"> {\n const presetName = (args.preset ?? config.preset ?? \"web\") as PresetName;\n getPreset(presetName); // validates\n const format = (args.format ?? config.format ?? \"auto\") as FormatChoice;\n if (![\"auto\", \"keep\", \"webp\", \"avif\", \"png\", \"jpeg\"].includes(format)) throw new UsageError(`--format must be auto|keep|webp|avif|png|jpeg, got \"${format}\"`);\n const kind = (args.kind ?? config.kind ?? \"auto\") as \"auto\" | Kind;\n if (![\"auto\", \"ui\", \"photo\", \"graphic\"].includes(kind)) throw new UsageError(`--kind must be auto|ui|photo|graphic, got \"${kind}\"`);\n return {\n preset: presetName,\n format,\n kind,\n maxWidth: num(args[\"max-width\"], \"max-width\", { integer: true }) ?? config.maxWidth,\n maxHeight: num(args[\"max-height\"], \"max-height\", { integer: true }) ?? config.maxHeight,\n quality: num(args.quality, \"quality\", { integer: true, max: 100 }) ?? config.quality,\n };\n}\n\nimport { UsageError } from \"./errors.js\";\n\nfunction pct(n: number): string {\n return `${n >= 0 ? \"−\" : \"+\"}${Math.abs(n).toFixed(1)}%`;\n}\n\nfunction logFile(r: FileResult, log: (s: string) => void, cwd: string, verbose: boolean): void {\n const rel = (p: string) => path.relative(cwd, p) || p;\n const dims = r.input.width ? `${r.input.width}×${r.input.height}` : \"\";\n const left = `${rel(r.input.path)} ${pc.dim(`${dims} ${r.input.format.toUpperCase()} ${fmtBytes(r.input.bytes)}`)}`;\n if (r.status === \"failed\") {\n log(` ${pc.red(\"✖\")} ${left} ${pc.red(`failed: ${r.error}`)}`);\n } else if (r.status === \"kept\" || (r.status === \"budget-missed\" && !r.output)) {\n log(` ${pc.yellow(\"·\")} ${left} ${pc.dim(`kept: ${r.reason}${r.copiedTo ? \" (copied to --out)\" : \"\"}`)}${r.status === \"budget-missed\" ? pc.yellow(\" (over --target-kb budget)\") : \"\"}`);\n } else if (r.output) {\n const mark = r.status === \"budget-missed\" ? pc.yellow(\"!\") : pc.green(\"✔\");\n const odims = `${r.output.width}×${r.output.height}`;\n const q = r.output.lossless ? \"lossless\" : r.output.quality ? `q${r.output.quality}` : \"\";\n log(\n ` ${mark} ${left} ${pc.yellow(\"→\")} ${path.basename(r.output.path)} ${pc.dim(`${odims} ${q}`)} ${pc.green(`${fmtBytes(r.output.bytes)} ${pct(r.savedPercent)}`)}${\n r.status === \"budget-missed\" ? pc.yellow(\" (over --target-kb budget)\") : \"\"\n }`,\n );\n }\n if (verbose && r.candidates.length) {\n for (const c of r.candidates) {\n const tag = `${c.format}${c.lossless ? \"-lossless\" : c.quality ? `@${c.quality}` : \"\"}`;\n log(pc.dim(` ${c.passed ? \"ok \" : \"rej\"} ${tag.padEnd(16)} ${fmtBytes(c.bytes).padStart(9)}${c.ssim !== undefined ? ` ssim ${c.ssim}` : \"\"}`));\n }\n }\n}\n\nfunction summary(rep: Report, log: (s: string) => void): void {\n const t = rep.totals;\n const parts = [`${t.optimized} optimized`, t.kept ? `${t.kept} kept` : \"\", t.budgetMissed ? `${t.budgetMissed} over budget` : \"\", t.failed ? pc.red(`${t.failed} failed`) : \"\"].filter(Boolean);\n log(\"\");\n log(`${pc.bold(pc.green(`Saved ${fmtBytes(t.inputBytes)} → ${fmtBytes(t.outputBytes)} (${pct(t.savedPercent)})`))} ${pc.dim(`in ${(t.durationMs / 1000).toFixed(1)}s · ${parts.join(\", \")}${rep.dryRun ? \" · DRY RUN, nothing written\" : \"\"}`)}`);\n if (rep.rewrittenRefs.length) {\n const files = new Set(rep.rewrittenRefs.map((r) => r.file)).size;\n log(pc.dim(`Rewrote ${rep.rewrittenRefs.reduce((a, r) => a + r.count, 0)} reference(s) in ${files} file(s).`));\n } else if (rep.renamed.length && !rep.dryRun) {\n log(pc.yellow(`${rep.renamed.length} file(s) changed extension. Update references or re-run with --rewrite-refs.`));\n }\n if (rep.backupDirs.length) {\n log(pc.dim(`Originals kept in ${rep.backupDirs.map((d) => path.relative(process.cwd(), d) || d).join(\", \")} — delete or ignore before upload/commit (or use --no-backup).`));\n }\n}\n\nconst optimizeArgs = {\n ...sharedArgs,\n \"target-kb\": { type: \"string\", description: \"Binary-search quality until each file is ≤ this many KB (exit 3 if impossible within the quality floor)\" },\n out: { type: \"string\", alias: \"o\", description: \"Write to this directory (mirrors structure) instead of in place\" },\n suffix: { type: \"string\", description: 'Insert before the extension, e.g. \".min\"' },\n backup: { type: \"boolean\", description: \"Keep originals in .img-subir-originals/ (default)\", negativeDescription: \"Overwrite/delete originals without a backup\" },\n rename: { type: \"boolean\", description: \"Change extension when the format changes (default)\", negativeDescription: \"Keep the original filename even if the format changes\" },\n \"rewrite-refs\": { type: \"boolean\", description: \"Update src/href/url() references in html/md/tsx/css files that point at renamed images\" },\n \"dry-run\": { type: \"boolean\", alias: \"n\", description: \"Report what would happen; write nothing\" },\n fast: { type: \"boolean\", description: \"Skip SSIM checks and use fixed qualities (~3× faster)\" },\n concurrency: { type: \"string\", alias: \"c\", description: \"Files in flight at once (default: cpus − 1)\" },\n} as const;\n\nconst jsonArgs = { json: { type: \"boolean\", description: \"Output JSON\" } } as const;\n\nconst optimizeCmd = defineCommand({\n meta: {\n name: \"img-subir\",\n version: VERSION,\n description: \"Optimize images for websites, READMEs and Subir pages. Agent-friendly.\\n\\nUsage: img-subir <paths...> [options]\\nCommands: inspect <paths...> · presets · skill · doctor\",\n },\n args: optimizeArgs,\n async run({ args }) {\n rejectUnknownArgs(args, optimizeArgs);\n const paths = args._.filter(Boolean);\n if (paths.length === 0) {\n throw new UsageError(\"Pass at least one file, directory or glob. Try: img-subir ./assets\");\n }\n const cwd = process.cwd();\n const { config, source } = await loadConfig(cwd);\n const json = Boolean(args.json);\n const log = (s: string) => (json ? process.stderr : process.stdout).write(`${s}\\n`);\n const quiet = Boolean(args.quiet);\n const shared = sharedOptions(args as SharedArgs, config);\n\n const opts: OptimizeOptions = {\n paths,\n cwd,\n ...shared,\n targetKb: num(args[\"target-kb\"], \"target-kb\") ?? config.targetKb,\n out: args.out ?? config.out,\n suffix: args.suffix ?? config.suffix,\n backup: args.backup ?? config.backup ?? true,\n rename: args.rename ?? config.rename ?? true,\n rewriteRefs: args[\"rewrite-refs\"] ?? config.rewriteRefs ?? false,\n dryRun: Boolean(args[\"dry-run\"]),\n fast: args.fast ?? config.fast ?? false,\n concurrency: num(args.concurrency, \"concurrency\", { integer: true }) ?? config.concurrency,\n onFile: quiet ? undefined : (r) => logFile(r, log, cwd, Boolean(args.verbose)),\n };\n if (opts.out === \"\") throw new UsageError(\"--out needs a directory\");\n\n if (!quiet) {\n log(`${pc.bold(pc.blue(\"img-subir\"))} ${pc.dim(`v${VERSION} · preset: ${shared.preset}${shared.format !== \"auto\" ? ` · format: ${shared.format}` : \"\"}${opts.dryRun ? \" · dry run\" : \"\"}${source ? ` · config: ${path.relative(cwd, source)}` : \"\"}`)}`);\n log(\"\");\n }\n\n const report = await optimize(opts);\n summary(report, log);\n if (json) process.stdout.write(`${JSON.stringify(report, null, 2)}\\n`);\n if (report.totals.failed > 0) process.exitCode = 1;\n else if (report.totals.budgetMissed > 0) process.exitCode = 3;\n },\n});\n\nconst inspectCmd = defineCommand({\n meta: { name: \"inspect\", description: \"Show metadata, detected kind and what optimize would do. Writes nothing.\" },\n args: sharedArgs,\n async run({ args }) {\n rejectUnknownArgs(args, sharedArgs);\n const paths = args._.filter(Boolean);\n if (paths.length === 0) throw new UsageError(\"Pass at least one file, directory or glob.\");\n const cwd = process.cwd();\n const { config } = await loadConfig(cwd);\n const results = await inspect(paths, { cwd, ...sharedOptions(args as SharedArgs, config) });\n if (args.json) {\n process.stdout.write(`${JSON.stringify({ schemaVersion: 1, version: VERSION, files: results }, null, 2)}\\n`);\n return;\n }\n for (const r of results) {\n const i = r.input;\n const resized = r.plan.targetWidth !== i.width ? ` → ${r.plan.targetWidth}×${r.plan.targetHeight}` : \"\";\n console.log(`${pc.bold(path.relative(cwd, i.path))}`);\n console.log(` ${i.format.toUpperCase()} ${i.width}×${i.height} ${fmtBytes(i.bytes)}${i.hasAlpha ? \" alpha\" : \"\"}${i.animated ? \" animated\" : \"\"}${resized}`);\n console.log(` kind: ${pc.cyan(r.kind)}${r.plan.floor !== null ? ` ssim floor: ${r.plan.floor}` : \"\"}`);\n console.log(` would try: ${r.plan.candidates.length ? r.plan.candidates.join(\", \") : pc.dim(\"nothing\")}`);\n }\n },\n});\n\nconst presetsCmd = defineCommand({\n meta: { name: \"presets\", description: \"List presets and their settings.\" },\n args: jsonArgs,\n run({ args }) {\n rejectUnknownArgs(args, jsonArgs);\n if (args.json) {\n process.stdout.write(`${JSON.stringify(presets, null, 2)}\\n`);\n return;\n }\n for (const p of Object.values(presets)) {\n console.log(`${pc.bold(p.name)}${p.name === \"web\" ? pc.dim(\" (default)\") : \"\"}`);\n console.log(` ${p.note}`);\n console.log(pc.dim(` maxWidth: ${p.maxWidth ?? \"none\"}${p.maxHeight ? ` maxHeight: ${p.maxHeight}` : \"\"} · candidates: ${p.candidates.join(\", \")}${p.losslessWebp ? \" + webp-lossless\" : \"\"} · floor ui/photo: ${p.floor.ui}/${p.floor.photo}`));\n }\n },\n});\n\nconst skillCmd = defineCommand({\n meta: { name: \"skill\", description: \"Print the SKILL.md that teaches an agent how to use img-subir.\" },\n async run() {\n const here = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [path.join(here, \"..\", \"skills\", \"img-subir\", \"SKILL.md\"), path.join(here, \"..\", \"..\", \"skills\", \"img-subir\", \"SKILL.md\")];\n for (const c of candidates) {\n try {\n process.stdout.write(await readFile(c, \"utf8\"));\n return;\n } catch {\n /* try next */\n }\n }\n throw new Error(\"SKILL.md not found in package\");\n },\n});\n\nconst doctorCmd = defineCommand({\n meta: { name: \"doctor\", description: \"Verify the sharp binary and which encoders are available.\" },\n args: jsonArgs,\n async run({ args }) {\n rejectUnknownArgs(args, jsonArgs);\n const checks: Record<string, { ok: boolean; detail: string }> = {};\n const nodeMajor = Number(process.versions.node.split(\".\")[0]);\n checks.node = { ok: nodeMajor >= 20, detail: `v${process.versions.node}${nodeMajor < 20 ? \" (need >= 20)\" : \"\"}` };\n checks.sharp = { ok: true, detail: `sharp ${sharp.versions.sharp} · libvips ${sharp.versions.vips}` };\n const px = await sharp({ create: { width: 8, height: 8, channels: 4, background: { r: 200, g: 50, b: 50, alpha: 0.8 } } }).png().toBuffer();\n const tryEnc = async (name: string, fn: () => Promise<Buffer>) => {\n try {\n const b = await fn();\n checks[name] = { ok: true, detail: `${b.length} bytes` };\n } catch (e) {\n checks[name] = { ok: false, detail: e instanceof Error ? e.message : String(e) };\n }\n };\n await tryEnc(\"webp\", () => sharp(px).webp().toBuffer());\n await tryEnc(\"webp-lossless\", () => sharp(px).webp({ lossless: true }).toBuffer());\n await tryEnc(\"avif\", () => sharp(px).avif({ effort: 2 }).toBuffer());\n await tryEnc(\"jpeg-mozjpeg\", () => sharp(px).jpeg({ mozjpeg: true }).toBuffer());\n await tryEnc(\"png-palette\", () => sharp(px).png({ palette: true }).toBuffer());\n await tryEnc(\"gif\", () => sharp(px).gif().toBuffer());\n await tryEnc(\"animated-webp\", async () => {\n const gif = await sharp(px).gif().toBuffer();\n return sharp(gif, { animated: true }).webp().toBuffer();\n });\n await tryEnc(\"svg-decode\", () => sharp(Buffer.from('<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"8\" height=\"8\"><rect width=\"8\" height=\"8\"/></svg>')).png().toBuffer());\n const allOk = Object.values(checks).every((c) => c.ok);\n if (args.json) {\n process.stdout.write(`${JSON.stringify({ ok: allOk, version: VERSION, checks }, null, 2)}\\n`);\n } else {\n console.log(`${pc.bold(\"img-subir doctor\")} v${VERSION}`);\n for (const [k, v] of Object.entries(checks)) console.log(` ${v.ok ? pc.green(\"✔\") : pc.red(\"✖\")} ${k.padEnd(16)} ${pc.dim(v.detail)}`);\n console.log(allOk ? pc.green(\"All good.\") : pc.red(\"Some encoders are unavailable.\"));\n }\n if (!allOk) process.exitCode = 1;\n },\n});\n\n// citty would treat the first positional as a subcommand name, so dispatch by hand:\n// `img-subir ./assets` runs optimize, `img-subir inspect ./assets` runs inspect.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst subCommands: Record<string, CommandDef<any>> = { optimize: optimizeCmd, inspect: inspectCmd, presets: presetsCmd, skill: skillCmd, doctor: doctorCmd };\nconst first = process.argv[2];\nconst cmd = first && first in subCommands ? subCommands[first] : optimizeCmd;\nconst rawArgs = cmd === optimizeCmd && first !== \"optimize\" ? process.argv.slice(2) : process.argv.slice(3);\nasync function main(): Promise<void> {\n if (rawArgs.includes(\"--help\") || rawArgs.includes(\"-h\")) {\n console.log(await renderUsage(cmd));\n return;\n }\n if (rawArgs.includes(\"--version\") || rawArgs.includes(\"-v\")) {\n console.log(VERSION);\n return;\n }\n await runCommand(cmd, { rawArgs });\n}\n\nmain().catch((e: unknown) => {\n const msg = e instanceof Error ? e.message : String(e);\n console.error(pc.red(`img-subir: ${msg}`));\n process.exit(e instanceof UsageError ? 2 : 1);\n});\n"],"mappings":"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","names":[]}
|