npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dnbhq/sharp-lint-staged

v1.0.2

Published

sharp-powered image optimisation CLI designed for lint-staged usage with sensible defaults

Downloads

323

Readme

Sharp-lint-staged

sharp-powered image optimisation CLI designed for lint-staged usage with sensible defaults

Version PR Workflow

This is a drop-in successor to @davidsneighbour/imagemin-lint-staged. It keeps the same workflow — optimise staged images on commit — but replaces the unmaintained imagemin toolchain (which ships a long tail of security advisories on its native binary dependencies) with sharp for raster images and the actively maintained svgo for SVG.

Raster formats (GIF, PNG, JPEG) are handled by sharp's modern libvips-based encoders. SVG is handled by svgo directly, with no imagemin wrapper in between.

Why switch from imagemin?

  • imagemin and its plugins are effectively unmaintained and pull in native binaries (gifsicle, jpegtran, optipng) that generate repeated npm audit advisories.
  • sharp is a single, well-maintained, prebuilt native dependency covering GIF, PNG and JPEG (plus WebP, AVIF and TIFF if you want them later).
  • svgo replaces imagemin-svgo with no loss of functionality.

Installation

npm i --save-dev @dnbhq/sharp-lint-staged

Requires Node.js 22 or newer.

Usage

Use in conjunction with lint-staged. In your package.json:

"lint-staged": {
  "*.{png,jpeg,jpg,gif,svg}": ["sharp-lint-staged"]
}

On commit, every staged image matching the glob is optimised in place. lint-staged automatically re-stages the modified files. The optimised buffer is only written back when it is strictly smaller than the original, so already-optimised images are left untouched and repeated runs converge to a stable file.

You can also call the API directly:

import { optimizeFile } from '@dnbhq/sharp-lint-staged';

const wasRewritten = await optimizeFile('assets/logo.png');

optimizeFile resolves to true when the file was rewritten with a smaller result, and false when it was left unchanged (already optimal, or an unsupported format).

Configuration

The package uses cosmiconfig with the module name sharp-lint-staged. You can configure the encoders from a project-level configuration file instead of changing the package source.

Configuration is searched from the current working directory. For normal lint-staged usage this is usually the repository root, because lint-staged runs from there.

Supported configuration locations include:

  • package.json, using the sharp-lint-staged property
  • .sharp-lint-stagedrc
  • .sharp-lint-stagedrc.{json,yaml,yml,js,ts,mjs,cjs}
  • .config/sharp-lint-stagedrc
  • .config/sharp-lint-stagedrc.{json,yaml,yml,js,ts,mjs,cjs}
  • sharp-lint-staged.config.{js,ts,mjs,cjs}

cosmiconfig's asynchronous API is used, so ESM configuration files are supported. In ESM projects, prefer sharp-lint-staged.config.js or sharp-lint-staged.config.mjs with export default.

Default configuration

The built-in defaults favour the strongest encoder effort available in sharp while keeping JPEG re-encoding visually close to the source. SVG optimisation mirrors the lossless, accessibility-friendly preset from the predecessor package.

export default {
  jpeg: {
    quality: 80,
    mozjpeg: true,
    progressive: true,
  },
  png: {
    compressionLevel: 9,
    effort: 10,
    palette: false,
    progressive: false,
  },
  gif: {
    effort: 10,
  },
  svg: {
    multipass: true,
    plugins: [
      {
        name: 'preset-default',
        params: {
          // svgo v4 keeps `viewBox` by default; only the remaining
          // safety overrides are needed.
          overrides: {
            cleanupIds: false,
            removeDesc: false,
          },
        },
      },
    ],
  },
  resize: null,
};

Example using package.json

{
  "sharp-lint-staged": {
    "jpeg": { "quality": 82, "mozjpeg": true },
    "png": { "compressionLevel": 9, "effort": 10 },
    "gif": { "effort": 10 }
  }
}

Example using .sharp-lint-stagedrc.json

{
  "png": { "compressionLevel": 9, "effort": 10, "palette": true, "quality": 90 },
  "svg": {
    "multipass": true,
    "plugins": [{ "name": "preset-default" }]
  }
}

Example using sharp-lint-staged.config.mjs

export default {
  // Resize is OFF by default. Enable it to cap the maximum width:
  resize: { width: 2000, withoutEnlargement: true },
  jpeg: { quality: 80, mozjpeg: true, progressive: true },
};

Options

All options are optional. Each top-level key configures one encoder.

| Option | Engine | Passed to | Default | | -------- | ------ | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | | jpeg | sharp | sharp.jpeg() | { quality: 80, mozjpeg: true, progressive: true } | | png | sharp | sharp.png() | { compressionLevel: 9, effort: 10, palette: false, progressive: false } | | gif | sharp | sharp.gif() | { effort: 10 } | | svg | svgo | svgo.optimize() | preset-default with cleanupIds/removeDesc disabled (viewBox kept) | | resize | sharp | sharp.resize() (only when non-null) | null (no resizing) |

The object for each key is passed directly to the matching engine method.

Default option details

  • jpeg uses mozjpeg: true for the best compression sharp offers, plus progressive: true for better perceived load time. quality: 80 is a widely-used near-transparent default. JPEG is re-encoded rather than losslessly rewritten — see the trade-offs below.
  • png uses the maximum compressionLevel (9) and effort (10). palette: false keeps output lossless; set palette: true (optionally with quality/colours) to enable lossy palette quantisation for much smaller files.
  • gif uses the maximum effort (10). Animated GIFs are preserved (all frames are read and written back).
  • svg uses svgo's preset-default. svgo v4 keeps viewBox by default (so scaling is preserved); cleanupIds is disabled to avoid breaking inlined/scripted/externally-referenced SVGs; removeDesc is disabled to preserve accessibility metadata.
  • resize is null (disabled) to preserve image dimensions by default. Provide a sharp resize options object to cap dimensions; using withoutEnlargement: true is recommended so smaller images are not upscaled.

Behaviour

  • If no configuration file is found, all defaults above are used.
  • If a key is missing, that format uses its built-in default.
  • If a key is present, its value replaces the built-in default for that format. The configuration is not deep-merged.
  • The format is detected from the file content (and the extension for SVG), so the file extension does not have to match the real format.
  • Unsupported formats are skipped silently, which makes a broad lint-staged glob safe.
  • A file is only rewritten when the optimised result is strictly smaller than the original.

Trade-offs and known differences

Because sharp re-encodes raster images rather than performing the byte-level lossless transforms that jpegtran/optipng/gifsicle did, there are intentional behavioural differences from imagemin-lint-staged. These are tracked in ToDo.md:

  • JPEG is lossy. sharp has no lossless JPEG optimiser equivalent to jpegtran. The "strictly smaller" write guard keeps repeated commits from snowballing quality loss, but the first optimisation of a JPEG is a re-encode.
  • PNG/GIF are lossless by default and effectively idempotent thanks to the write guard.
  • SVG is handled by svgo, matching the previous behaviour.

Migrating from imagemin-lint-staged

  1. Replace the dependency:

    npm rm @davidsneighbour/imagemin-lint-staged
    npm i -D @dnbhq/sharp-lint-staged
  2. Update the lint-staged command from imagemin-lint-staged to sharp-lint-staged.

  3. Rename any config file/key from imagemin-lint-staged to sharp-lint-staged. The config shape changed: gifsicle/jpegtran/optipng become gif/jpeg/png (sharp options), while svgo becomes svg (svgo options).

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Development

npm install     # install dependencies
npm run build   # compile TypeScript from src/ to dist/
npm test        # run the vitest suite against real fixtures