{"id":47998954,"url":"https://github.com/j9t/image-guard","last_synced_at":"2026-04-04T12:10:45.025Z","repository":{"id":37394965,"uuid":"505717858","full_name":"j9t/image-guard","owner":"j9t","description":"Near-lossless image compressor that’s easy to use and easy to automate","archived":false,"fork":false,"pushed_at":"2026-03-26T06:53:57.000Z","size":2680,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-30T03:46:13.614Z","etag":null,"topics":["automation","avif","compression","gif","graphics","heic","heif","images","jpeg","jpg","lossless","optimization","performance","png","webp"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/image-guard","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"sumcumo/imagemin-merlin","license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/j9t.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE.md","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"j9t"}},"created_at":"2022-06-21T06:28:29.000Z","updated_at":"2026-03-26T06:53:59.000Z","dependencies_parsed_at":"2025-10-09T07:20:38.225Z","dependency_job_id":"765ad5fe-5323-4656-9a7d-95b4d9563a1d","html_url":"https://github.com/j9t/image-guard","commit_stats":{"total_commits":140,"total_committers":6,"mean_commits":"23.333333333333332","dds":0.3214285714285714,"last_synced_commit":"0a846af8a8ee283c7d6f686701a6b250d2980f12"},"previous_names":["j9t/image-guard"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/j9t/image-guard","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/j9t%2Fimage-guard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/j9t%2Fimage-guard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/j9t%2Fimage-guard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/j9t%2Fimage-guard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/j9t","download_url":"https://codeload.github.com/j9t/image-guard/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/j9t%2Fimage-guard/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31398902,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","avif","compression","gif","graphics","heic","heif","images","jpeg","jpg","lossless","optimization","performance","png","webp"],"created_at":"2026-04-04T12:10:44.572Z","updated_at":"2026-04-04T12:10:45.019Z","avatar_url":"https://github.com/j9t.png","language":"JavaScript","funding_links":["https://github.com/sponsors/j9t"],"categories":[],"sub_categories":[],"readme":"# Image Guard\n\n[![npm version](https://img.shields.io/npm/v/image-guard.svg)](https://www.npmjs.com/package/image-guard) [![Build status](https://github.com/j9t/image-guard/workflows/Tests/badge.svg)](https://github.com/j9t/image-guard/actions) [![Socket](https://badge.socket.dev/npm/package/image-guard)](https://socket.dev/npm/package/image-guard)\n\n(This project was based on [sum.cumo’s imagemin-merlin](https://github.com/sumcumo/imagemin-merlin). [Changes are documented](https://github.com/sumcumo/imagemin-merlin/compare/master...j9t:master), and include this README. Image Guard supports additional file formats—WebP, AVIF, and HEIC/HEIF—, comes with improved code and documentation, and is being maintained. For this reason, it’s [not based on any Imagemin packages](https://meiert.com/blog/image-guard-4/) anymore.)\n\nImage Guard takes care of near-lossless compression of your images, to help you avoid bloat in your repositories. It makes it convenient and as safe as possible to automatically compress PNG, JPG, GIF, WebP, and AVIF images. It can also convert HEIC/HEIF files to AVIF.\n\nIt’s convenient because setup is simple. Run it right away—done. Or install, run, add hook—done.\n\nIt’s as safe as possible because compression happens losslessly (near-lossless for JPG and GIF images). That allows you to stop worrying about forgetting to compress images, but also about sacrificing too much quality. (You can take care of additional optimizations manually or through other tooling.)\n\n## Installation and Use\n\n(Note available parameters below.)\n\n### Ways to Use Image Guard\n\n#### Option 1: Immediate Manual Use\n\nYou can use Image Guard right away, without installation, by running\n\n```console\nnpx image-guard\n```\n\n#### Option 2: Project-Linked Manual Use\n\nInstall Image Guard in your project:\n\n```console\nnpm i -D image-guard\n```\n\nRun Image Guard by calling\n\n```console\nnpx image-guard\n```\n\nTo make sure that _all_ images are being compressed, it’s recommended to run Image Guard like this at least once, after installation.\n\n#### Option 3: Automated Use\n\nInstall Image Guard in your project:\n\n```console\nnpm i -D image-guard\n```\n\nTo compress images already in the codebase, run Image Guard once by calling\n\n```console\nnpx image-guard\n```\n\nFor automated use, Image Guard should be triggered through a [Git hook](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) on `pre-commit`. You can choose between native Git hooks (recommended for simple projects) or [Husky](https://github.com/typicode/husky).\n\n##### Native Git Hooks\n\nNative Git hooks are simpler to set up and don’t require additional dependencies. Run these commands from your project root:\n\n```console\nmkdir -p .githooks;\\\ncat \u003e .githooks/pre-commit \u003c\u003c 'EOF'\n#!/bin/sh\n./node_modules/.bin/image-guard --staged\nEOF\nchmod +x .githooks/pre-commit;\\\ngit config core.hooksPath .githooks;\\\ngit add .githooks/pre-commit;\\\ngit commit -m \"feat: add Git pre-commit hook for Image Guard\";\\\nnpm pkg set scripts.postprepare=\"mkdir -p .githooks \u0026\u0026 cat \u003e .githooks/pre-commit \u003c\u003c 'EOF'\n#\\!/bin/sh\n./node_modules/.bin/image-guard --staged\nEOF\nchmod +x .githooks/pre-commit \u0026\u0026 git config core.hooksPath .githooks\"\n```\n\n##### Husky\n\nIf you already use [Husky](https://typicode.github.io/husky/), run the following commands in your project root (you can copy and execute them at once):\n\n```console\ngrep -qxF \"./node_modules/.bin/image-guard --staged\" .husky/pre-commit || printf '\\n./node_modules/.bin/image-guard --staged' \u003e\u003e .husky/pre-commit;\\\ngit add .husky/pre-commit;\\\ngit commit -m \"feat: add Husky pre-commit hook for Image Guard\";\\\nnpm pkg set scripts.postprepare=\"grep -qxF './node_modules/.bin/image-guard --staged' .husky/pre-commit || printf '\\\\n./node_modules/.bin/image-guard --staged' \u003e\u003e .husky/pre-commit\"\n```\n\nIf you don’t yet but want to use Husky, run the following commands from your project root:\n\n```console\nnpm i -D husky;\\\nnpx husky init;\\\necho \"./node_modules/.bin/image-guard --staged\" \u003e .husky/pre-commit;\\\ngit add .husky/pre-commit;\\\ngit commit -m \"feat: add Husky pre-commit hook for Image Guard\";\\\nnpm pkg set scripts.postprepare=\"grep -qxF './node_modules/.bin/image-guard --staged' .husky/pre-commit || printf '\\\\n./node_modules/.bin/image-guard --staged' \u003e\u003e .husky/pre-commit\"\n```\n\n(The `postprepare` script ensures that the hook is added to the repository whenever someone installs the package.)\n\n**Important:** When you commit images that have not yet been compressed, the automated compression process (triggered by the pre-commit hook) will modify those image files to reduce their size. As a result, after your initial commit attempt, you will see these images appear as changed files in Git. To include the optimized images in your repository, you need to stage and commit them again. In rare cases, if further compression is possible, you may need to repeat this process until no further changes are detected. This workflow is intentional and ensures that only optimally compressed images are committed. Many editors can display diffs for images, helping you review these changes.\n\n### Parameters\n\n* `--dry` allows you to run Image Guard in “dry mode.” All changes are shown in the terminal.\n\n* `--ignore` allows you to specify paths to be ignored (as in `--ignore=example,test`). Multiple paths must be separated by commas. The option supports glob patterns (e.g., `assets/**`, `**/*.png`); matching is case‑insensitive and honors `.gitignore`.\n\n* `--staged` (recommended with automated use) triggers a mode that watches PNG, JPG, GIF, WebP, and AVIF files (and HEIC/HEIF, when `--heic-to-avif` is used) in `git diff` and only processes those files—that approach makes Image Guard more efficient in operation.\n\n* `--heic-to-avif` converts HEIC/HEIF files to AVIF. Because HEIC sources are already lossy-compressed (HEVC), the conversion uses lossy AVIF encoding (quality 80) to produce files smaller than the originals at near-identical visual quality. Original HEIC/HEIF files are deleted after conversion by default. (Note: The conversion maps Display P3 colors—common on iPhones—to sRGB, which may result in slightly less vivid colors.)\n\n  - `--keep-heic` (used with `--heic-to-avif`) preserves the original HEIC/HEIF files instead of deleting them after conversion.\n\n* `--quiet` suppresses per‑file logs and prints only the final summary (plus errors). This reduces console noise and speeds up runs in CI and Git hooks.\n\n## What Does the Output Look Like?\n\nRoughly like this:\n\n![Screenshot of Image Guard in operation.](https://raw.githubusercontent.com/j9t/image-guard/master/media/output.png)\n\n* Green: The image file has been compressed.\n* Cyan: The image file has been converted (HEIC/HEIF to AVIF).\n* White (light gray): The image file has not been changed.\n* Blue: The image file had already been compressed more effectively than the new result, and was therefore skipped, too.\n\nTip: Use `--quiet` to suppress these per‑file lines and keep only the final summary.\n\n## How Does Image Guard Work?\n\nImage Guard is a Node script that uses [sharp](https://www.npmjs.com/package/sharp) under the hood.\n\nAutomated compression works by monitoring whether a given [change list](https://webglossary.info/terms/change-list/) includes any PNGs, JPGs, GIFs, WebPs, or AVIFs (and HEIC/HEIF when opted in). It’s initiated by a Git hook. Only those images are compressed where there is an improvement. HEIC conversion uses [heic-decode](https://www.npmjs.com/package/heic-decode) for patent-free decoding and sharp for lossy AVIF encoding (quality 80). The processed images can then be committed to the underlying repository.\n\nThrough this approach, though glossed over here, Image Guard makes up for what’s missing or complicated in other packages, namely easy, near-riskless, automatable, resource-friendly in-repo optimization.\n\n## Why Use Image Guard?\n\nYou use Image Guard when you need a simple, automatable, robust solution to compress images in a way that limits unnecessary image payload right from the start, in your repositories, and that reduces the risk that entirely uncompressed images go into production.\n\nAs Image Guard compresses near-losslessly, there’s little risk of quality issues from compression. (Lossless compression is not possible for every image format, however, so there’s a risk when excessively iterating over the same images. Doing so may eventually degrade quality.)\n\n## What Does Image Guard _Not_ Do?\n\nImage Guard is no substitute for image fine-tuning and micro-optimization. That’s difficult to do in an automated fashion, because this type of compression requires [balancing quality and performance](https://meiert.com/blog/understanding-image-compression/) and is context-dependent. In its most extreme form, when maximum quality at maximum performance is required from each graphic, micro-optimization is even challenging to do manually.\n\nThat is, micro-optimization still needs to be taken care of through other means, whether manually or through tools. Image Guard just solves the problem that images are checked in or go live that are not compressed _at all_.\n\n## What’s Next?\n\nThere are a few ideas, like adding light SVG support, or ensuring compatibility with projects in which the project’s .git folder is not at the same level as its package.json (currently, automatic mode doesn’t work in these cases).\n\nFeedback is appreciated: Please [file an issue](https://github.com/j9t/image-guard/issues/new) or send a pull request. Thank you!\n\n## License\n\nCopyright 2019 [sum.cumo GmbH](https://web.archive.org/web/20191208211414/https://www.sumcumo.com/)\u003cbr\u003e\nCopyright 2022 [Jens Oliver Meiert](https://meiert.com/)\n\nLicensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fj9t%2Fimage-guard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fj9t%2Fimage-guard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fj9t%2Fimage-guard/lists"}