Skip to content

chore(deps): pin react-native-fs so Renovate dep PRs stop shipping a lockfile npm ci rejects - #40

Merged
dawsontoth merged 2 commits into
mainfrom
fix/renovate-lockfile-react-native-fs-prune
Aug 17, 2026
Merged

chore(deps): pin react-native-fs so Renovate dep PRs stop shipping a lockfile npm ci rejects#40
dawsontoth merged 2 commits into
mainfrom
fix/renovate-lockfile-react-native-fs-prune

Conversation

@dawsontoth

Copy link
Copy Markdown
Contributor

Problem

Every Renovate dependency PR shipped a package-lock.json that npm ci rejected on Node 24/26, requiring a manual lockfile regeneration each time (e.g. #39's fixup, and 55c5bba before it):

npm error code EUSAGE
npm error Missing: react-native-fs@2.20.0 from lock file
npm error Missing: react-native@0.84.1 from lock file
npm error Missing: react@19.2.8 from lock file

Root cause

harper > alasql declares react-native-fs as an optionalDependency, and react-native-fs has a required peer on react-native. This trips a genuine npm install/ci divergence:

  • npm install (both --package-lock-only and a full install) silently drops the entire optional react-native subtree whenever it resolves the tree from scratch — it never even attempts to place it.
  • npm ci computes an ideal tree that still includes that subtree, then rejects any lock missing it. npm 11 (Node 24/26) enforces this; Node 22's older npm was lenient, which is why CI failed only on the newer matrix legs.

Renovate regenerates the lockfile from scratch when it updates dependencies — its raw output for #39 had the whole ~250-package subtree pruned (1216 entries) — so the pruned lock landed on every dep PR.

Why skipInstalls: false (c5b68bb) didn't help

That change assumed --package-lock-only was the culprit and forced a full npm install. But a full install from scratch prunes the subtree identically — the drop is in npm's ideal-tree builder, not the reify step — so #39 still shipped a pruned lock with the override already in place.

Fix

Pin react-native-fs as a direct devDependency. That makes npm treat it as required rather than optional, so npm install and npm ci resolve it identically and it survives a from-scratch regeneration. The now-redundant, disproven skipInstalls: false is removed so Renovate returns to its faster default.

  • Lockfile is a clean superset: 0 added, 0 removed, 0 version changes. The large line delta is only "optional"/"peer" flags dropping off the now-required subtree.
  • It's a devDependency, so the published package is unaffected. fsevents and every other platform-gated package stay optional, so Linux CI still installs cleanly.

Verification

Check Result
npm ci on npm 11 (Node 24/26)
npm ci on npm 10 (Node 22)
Clean-room regen (rm lock + node_modules) + harper bump → npm ci ✅ subtree kept
Harshest full-install clean-room regen ✅ subtree kept
format:check + build + unit tests

The clean-room-regen-then-npm ci test faithfully simulates a future Renovate dep PR installing on Node 24/26.

🤖 Generated with Claude Code

dawsontoth and others added 2 commits August 17, 2026 15:31
`harper > alasql` declares `react-native-fs` as an optionalDependency, and
`react-native-fs` has a required peer dependency on `react-native`. npm's
install resolver silently drops this optional subtree whenever it resolves
the tree from scratch (no existing entry carried forward in node_modules or
the lock) — it never even attempts to place it. But `npm ci`'s validator
computes an ideal tree that *does* include the subtree, then rejects any
lock that lacks it:

    npm error code EUSAGE
    npm error Missing: react-native-fs@2.20.0 from lock file
    npm error Missing: react-native@0.84.1 from lock file
    npm error Missing: react@19.2.8 from lock file

npm 11 (Node 24/26) enforces this; Node 22's older npm was lenient. Renovate
regenerates package-lock.json from scratch when it updates dependencies (its
raw output for PR #39 had the entire ~250-package react-native subtree
pruned, 1216 entries), so every dep PR shipped a lock its own `npm ci`
rejected and had to be regenerated by hand.

`skipInstalls: false` could not fix this: a full `npm install` from scratch
prunes the subtree identically to `npm install --package-lock-only` — the
prune is in the ideal-tree builder, not the reify step.

Declaring `react-native-fs` as a direct devDependency makes npm treat it as
required rather than optional, so it (and its react-native peer subtree) is
resolved consistently by both `npm install` and `npm ci`, and survives a
from-scratch regeneration. The package set and every version are unchanged
versus the previous lock (0 added, 0 removed, 0 version changes); the large
line delta is only the `"optional": true`/`"peer": true` flags being dropped
from the now-required subtree. It is a devDependency, so the published
package is unaffected. fsevents and every other platform-gated package stay
optional, so Linux CI still installs cleanly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
c5b68bb added `skipInstalls: false` to force Renovate into a full
`npm install`, on the theory that `--package-lock-only` was what pruned the
optional react-native-fs subtree. That theory was wrong: a full install
prunes the subtree exactly the same way (the drop happens in npm's
ideal-tree builder, before the reify step), so PR #39 still shipped a pruned
lock with the override already in place.

The real fix pins react-native-fs as a direct devDependency, which holds
regardless of Renovate's install mode. Remove the override so Renovate
returns to its faster default.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds the react-native-fs dependency (version 2.20.0) to package.json and updates package-lock.json accordingly, which also removes the optional flag from numerous lockfile entries. Additionally, the skipInstalls configuration is removed from renovate.json. As there are no review comments, I have no feedback to provide.

@dawsontoth
dawsontoth merged commit b50f856 into main Aug 17, 2026
7 checks passed
@dawsontoth
dawsontoth deleted the fix/renovate-lockfile-react-native-fs-prune branch August 17, 2026 19:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant