Skip to content

Replace update-notifier-cjs with a zero-dependency equivalent (removes 46 packages) #573

Description

@HeversonSilva-gif

update-notifier-cjs pulls 46 packages into superstatic's dependency tree to check
whether a newer version has been published. That is 46 packages to resolve, audit and
trust, in service of one request to the registry and a box drawn on stderr. It is also
a fork that exists only because upstream update-notifier went ESM-only, and its last
release was January 2025.

I would like to propose replacing it with nano-update-notifier, which implements the
same public API — same options, same notifier.update shape, same suppression rules —
on Node.js built-ins alone, with no runtime dependencies. It ships both ESM and CommonJS
entry points, so the existing require keeps working.

The change is two lines: the manifest entry, and

-const updateNotifier = require("update-notifier-cjs");
+const updateNotifier = require("nano-update-notifier");

in src/bin/server.ts.

Measured

Against nano-update-notifier@1.1.0 at superstatic 4bd82b9, on Node 22.14.0 and
npm 10.9.2. Every row is a real run — npm ci on a clean checkout, then the two-line
change, then npm install:

Dependency delta added 1 package, and removed 46 packages — npm's own words. 536 installed entries down to 491
npm run build clean, exit 0
npm run lint 994 problems, 0 errors — identical to the same run before the change
npm run test-unit 181 passing, 10 pending, 19 failing — identical to the run before the change, and the set of failing test titles diffs empty against it

Those 46 entries are 39 distinct package names. Five of them — camelcase,
graceful-fs, semver, strip-json-comments and type-fest — are nested copies whose
name survives elsewhere in the tree, and ini goes from three paths at once. I lead
with 46 because that is what npm install prints; the 39 below is the list of names.

The 19 failures are pre-existing on my machine, all trailing slash cases, which look
like Windows path handling rather than anything related to this change. What matters is
that the swap does not move them.

The 39 names: @pnpm/config.env-replace, @pnpm/network.ca-file, @pnpm/npm-conf,
ansi-align, boxen, ci-info, cli-boxes, config-chain, configstore,
crypto-random-string, deep-extend, dot-prop, escape-goat, global-dirs,
has-yarn, import-lazy, ini, is-ci, is-installed-globally, is-npm, is-obj,
is-yarn-global, isomorphic-fetch, minimist, node-fetch, proto-list, pupa,
rc, registry-auth-token, registry-url, semver-diff, tr46, unique-string,
update-notifier-cjs, webidl-conversions, whatwg-fetch, whatwg-url,
widest-line, xdg-basedir.

The rendered notification is not byte-identical, and here is exactly how

I rendered the notification through superstatic's own call — updateCheckInterval of
one week, shouldNotifyInNpmScript: true — under both libraries and diffed the output.
Eight lines on each side. Lines 2 through 7, the borders, the padding and both text
lines, are byte-identical. Lines 1 and 8 are the blank margin lines, and they differ:

  • update-notifier-cjs renders through boxen 5, which emits a yellow-open escape
    immediately followed by a yellow-close on those lines.
  • nano-update-notifier replicates boxen 7, which emits nothing.

Each of those two lines is 10 bytes under update-notifier-cjs — the escape pair
ESC[33m ESC[39m with nothing between them — and 0 bytes here, so the whole output
differs by 20 bytes and nothing else. On the run I measured that was 601 bytes against
581. Both render as a blank line in any terminal, and stripping ANSI makes the two
byte-equal. I would rather state that than claim an equality I cannot support.

How compatibility is held

Differential test suites run update-notifier, boxen, wrap-ansi, semver and
string-width as oracles: the full box option matrix, notification rendering with
colour enabled, ANSI-aware wrapping, and fuzzed version comparison. 300 tests, green on
Ubuntu, Windows and macOS across Node 18, 22 and 24. The package is published from a
GitHub Actions release through npm trusted publishing, with SLSA provenance attached.

Not verified

test-integration, and any run on Linux or macOS — I measured on Windows.

Two things worth raising

Disclosure: I wrote nano-update-notifier. That is why this is an issue rather than
a pull request. CONTRIBUTING asks for discussion before a larger contribution, and a
proposal to adopt my own package deserves that more than most. If this is not something
you want to take on, saying so costs you nothing and I will not push it.

I understand the Google CLA is needed before anything can merge, and I am happy to sign
it. CONTRIBUTING notes it is not required until after review, so I am raising the idea
first rather than signing speculatively.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions