GoogleChrome / GoogleChrome/workbox

workbox-cli throws ERR_REQUIRE_ESM on Node 20.0–20.18, inside its own `engines` range

Open Beginner friendly
#3,549 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
13k
Forks
880
Avg merge
2h 44m
Merged PRs (30d)
8

Description

`workbox-cli@7.4.1` declares `"engines": {"node": ">=20.0.0"}`, but it does not run on
Node 20.0 through 20.18. Every invocation fails before doing anything:

```
$ node node_modules/workbox-cli/build/bin.js
node_modules/workbox-cli/build/bin.js:15
const update_notifier_1 = __importDefault(require("update-notifier"));
^
Error [ERR_REQUIRE_ESM]: require() of ES Module .../update-notifier/index.js
from .../workbox-cli/build/bin.js not supported.
```

`update-notifier@7` is ESM-only — `"type": "module"`, `"exports": "./index.js"`, no
`main` — and `build/bin.js` is CommonJS, so it `require()`s it.

## Why it looks fine on newer Node

`require(esm)` became enabled by default in **Node 20.19**. Everything at or above that
works, which is why this is easy to miss:

| Node | result |
| --- | --- |
| 18.19.1 | `ERR_REQUIRE_ESM` (below `engines`, so not your problem) |
| **20.18.0** | **`ERR_REQUIRE_ESM` — inside `engines: >=20.0.0`** |
| 20.20.0 | works |
| 22.14.0 | works |

Measured on Windows with `workbox-cli@7.4.1` installed fresh from npm, running
`node node_modules/workbox-cli/build/bin.js` with no arguments.

`workbox --version` and `--help` appear to work even on the broken versions, because
`meow` handles those flags and exits before line 15 is reached. Any real command hits it.

## The fix that needs nothing from anyone

Node's own error message says it: change the `require` to a dynamic `import()`. In
`src/bin.ts` that is turning the top-level import into an `await import('update-notifier')`
inside the existing async IIFE. No dependency changes, no new packages.

That is the whole bug report. The rest is optional and I have an interest in it, so treat
it accordingly.

## An alternative, disclosed

I wrote `nano-update-notifier`, a zero-dependency reimplementation of `update-notifier`'s
API that ships **both** ESM and CommonJS entry points. Pointing `update-notifier` at it
makes the existing `require()` work unchanged, and shrinks the install:

| | |
| --- | --- |
| `npm install workbox-cli` | `added 498 packages` |
| with the swap | `added 440 packages` |
| difference | **58 fewer** |

Verified: with the swap, `workbox-cli` runs on **Node 20.18.0**, the version that fails
today.

Two things to be straight about:

1. `src/bin.ts` writes `params.pkg as updateNotifier.Package`. That resolves because
`@types/update-notifier@^4.1.1` — three majors behind the runtime you depend on — used
`export =` with a namespace. My package exports `PackageInformation` and no namespace,
so **that line would need changing too**, or I would need to add a namespace on my
side. I would rather say this up front than have you discover it in a build.
2. Removing 58 packages includes `boxen`, `configstore`, `rc`, `registry-auth-token`,
`package-json`, `latest-version` and `ky`. Issues #3093 and #3331 here were both
security reports about `got`, which reached you through this same chain.

If you would rather just fix the `require` and keep `update-notifier`, that closes the
bug and costs you nothing, and I will not push the alternative.

Contributor guide

Open the contributing guide

Research direction

Start in src/bin.ts and inspect the existing async IIFE around the update-notifier import and the params.pkg type usage. Build workbox-cli and run a real command on Node 20.18.0; done means it no longer throws ERR_REQUIRE_ESM while retaining the existing CLI behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js, typescript
Domain
cli
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.