A build directory full of oversized images needs repeatable processing. This guide examines the publicly downloadable QuickShrink CLI 1.0.0 package, hosted on an Orthogonal.info subdomain; it is not an independent product review.
QuickShrink CLI converts local files, directories, or globs to JPEG, PNG, WebP, or AVIF using Sharp and libvips. It does not upload images to a compression service. This revision inspects QuickShrink 1.0.0 and tests it with synthetic images and Sharp 0.34.0.
Correction — September 18, 2026: PNG palette output is not guaranteed pixel-lossless, and metadata stripping does not auto-orient photographs. Added collision and overwrite warnings, corrected Node requirements, and replaced the incomplete CI setup with a reviewed local dependency and persistent scripts. Package behavior takes precedence over conflicting landing-page examples.
This is different from the browser implementation described in Canvas, toBlob, and image compression. Command settings can live beside a project, but repeatability still requires reviewed inputs and dependencies.
On this page
- Install a Reviewed Local Copy
- Resize Without Accidentally Enlarging Images
- Know What Each Encoder Setting Means
- Metadata Is Stripped Unless You Keep It
- Check Output Names Before Any Conversion
- Put the Command Behind an npm Script
- Use the Browser for One-Offs, the CLI for Repetition
- Editorial Basis and Sources
Install a Reviewed Local Copy#
The reviewed distribution is the self-hosted tarball. Do not substitute the bare npm package name or assume that a similarly named package contains these reviewed bytes.
Use a fresh review directory. These commands fetch and list bytes, without installing or running package code. Creating vendor first prevents silently overwriting an existing archive:
mkdir vendor &&
curl --fail --location --proto '=https' --proto-redir '=https' \
https://quickshrink.orthogonal.info/cli/quickshrink.tgz \
--output vendor/quickshrink-1.0.0.tgz &&
tar -tzf vendor/quickshrink-1.0.0.tgz
Check for unexpected paths or links in the listing before inspecting these regular files:
tar -xOf vendor/quickshrink-1.0.0.tgz package/package.json
tar -xOf vendor/quickshrink-1.0.0.tgz package/bin/quickshrink.js
tar -xOf vendor/quickshrink-1.0.0.tgz package/LICENSE
printf '%s %s\n' \
'd89e4804f0baf329bd902e3fedb846fc1ea96c4ab6a9660367479f166794881c' \
'vendor/quickshrink-1.0.0.tgz' | shasum -a 256 -c -
The checksum identifies reviewed bytes, not a security certification. Stop on mismatch. Review the JavaScript and dependencies before execution.
One-off npx can install and execute cached packages without adding a persistent project dependency. Instead, in a project with package.json, install the reviewed archive locally and pin Sharp:
npm install --save-dev --save-exact --ignore-scripts \
./vendor/quickshrink-1.0.0.tgz sharp@0.34.0
Commit the reviewed archive and both npm manifests. The URL is mutable; the package’s ^0.34.0 range alone does not pin dependencies. Disabled lifecycle scripts worked with the tested prebuilt binaries. Platforms needing native builds require separate review, not an automatic scripts-enabled retry.
The package says Node >=18, but Sharp 0.34.0 requires ^18.17.0 || ^20.3.0 || >=21.0.0, excluding early Node 18/20 and Node 19. Use a maintained compatible release. These checks used Node 26.7.0, not every platform.
Directories recurse automatically; --recursive is unsupported. Defaults are ./quickshrink-out, quality 80, and format keep. Paths follow the matched files’ common parent, not necessarily the input directory. Use the inspected package’s help over conflicting landing-page examples.
Resize Without Accidentally Enlarging Images#
After local installation, call the local binary and set dimension caps:
./node_modules/.bin/quickshrink ./photos \
--out ./public/photos \
--format avif \
--quality 72 \
--max-width 1600 \
--max-height 1200
Oversized inputs use fit: "inside" and withoutEnlargement: true. Aspect ratio is retained and small images are not enlarged. The comparison uses stored pixel dimensions, not EXIF-corrected orientation.
A synthetic 1800 by 1200 PNG fixture produces 800 by 533 WebP with an 800-pixel cap. This checks resizing, not photographic quality or compression savings.
Know What Each Encoder Setting Means#
--quality has encoder-specific meaning. JPEG enables mozjpeg; PNG uses compression level 9 and palette: true; WebP and AVIF receive their quality setting. Equal numbers do not imply equal appearance.
PNG compression is lossless, but this CLI’s palette quantization can change decoded pixels, including PNG-to-PNG conversion. --quality 100 does not promise exact pixels; there is no palette-disable switch. Preserve originals. For archival/scientific fidelity, use a separately verified non-quantizing workflow. Inspect text, transparency and gradients.
./node_modules/.bin/quickshrink "src/**/*.{jpg,jpeg,png}" \
--out ./public/assets \
--format webp \
--quality 78 \
--dry-run
Dry run lists sources and destinations without decoding images. It does not test quality, reject duplicate outputs or guarantee safe writes.
Metadata Is Stripped Unless You Keep It#
Metadata is removed by default. --keep-metadata calls Sharp’s withMetadata(), which uses an sRGB profile; it does not guarantee byte-for-byte archival preservation.
Orientation needs separate handling. The CLI calls neither autoOrient() nor no-argument rotate(). EXIF-dependent photos can become sideways or mirrored after tag removal. Keeping metadata does not rotate pixels and may retain private information. Normalize and visually verify working copies first; preserve originals. QuickShrink 1.0.0 has no auto-orient flag.
Inspect outputs rather than treating stripping as a complete privacy audit. The EXIF GPS teardown explains why metadata needs a deliberate decision.
Check Output Names Before Any Conversion#
hero.jpg and hero.png in the same source directory both map to hero.webp with --format webp, even without --flatten. Flattening also brings files from different directories into the same destination namespace. A shared --suffix does not distinguish two identical stems.
The package has no collision detection and no --no-overwrite option. It uses fs.writeFileSync() without exclusive creation, silently replacing destinations. Concurrent collisions have an unpredictable winner; lowering concurrency does not make them safe.
Use fresh output outside every input tree and stop on duplicate dry-run destinations. Give working copies distinct stems or split conflicting inputs into separate output trees. Never overwrite your only originals. A separate directory alone does not solve within-batch collisions.
Put the Command Behind an npm Script#
Merge these scripts into the existing package.json, preserving other fields. npm resolves the installed local binary:
{
"scripts": {
"images:build": "quickshrink \"src/images/**/*.{jpg,jpeg,png}\" -o build-images -f webp -q 78 -w 1600",
"images:check": "quickshrink \"src/images/**/*.{jpg,jpeg,png}\" -o build-images -f webp -q 78 -w 1600 --dry-run"
}
}
Review images:check output for unique destinations. This POSIX-shell CI sequence assumes that review and a fresh checkout. Existence checks stop rather than deleting or reusing output:
npm ci --ignore-scripts --include=dev &&
test ! -e build-images &&
test ! -L build-images &&
npm run images:check &&
npm run images:build
Keep dev dependencies for the build. Concurrency defaults to CPU-core count and can be lowered with --concurrency. Per-file failures produce a final count and nonzero exit status, but visually wrong images and overwrites may not. Without enforceable unique output names, do not automate conversion without an external collision check. No hardware purchase is required.
Use the Browser for One-Offs, the CLI for Repetition#
Use the reviewed archive’s help alongside the QuickShrink CLI page. Run --dry-run and inspect representative outputs before automating. Version settings without ignoring image correctness or output names.
Editorial Basis and Sources#
Sources are the archive above, Sharp encoder/metadata source, orientation code, Node requirements, and npm’s npx and npm ci documentation. Offline synthetic tests cover parsing, recursion, resizing, palette pixel changes, orientation, collisions and npm scripts with lifecycle scripts disabled. They are not a production CI deployment, security certification, visual-quality benchmark, or test of the browser service. Neither service nor package was changed.
Leave a Reply