GoogleChrome / GoogleChrome/workbox
workbox-cli throws ERR_REQUIRE_ESM on Node 20.0–20.18, inside its own `engines` range
- 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
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