Skip to content

fix(deps): declare react-native-fs so npm stops pruning alasql's optional subtree - #159

Merged
dawsontoth merged 1 commit into
mainfrom
fix/lockfile-optional-subtree-prune
Sep 11, 2026
Merged

fix(deps): declare react-native-fs so npm stops pruning alasql's optional subtree#159
dawsontoth merged 1 commit into
mainfrom
fix/lockfile-optional-subtree-prune

Conversation

@dawsontoth

Copy link
Copy Markdown
Contributor

What

Adds react-native-fs to devDependencies and tells Renovate to ignore it. That's the whole fix — package.json +1 line, renovate.json +5, and a metadata-only lockfile diff.

Why

npm ci has failed on nine consecutive renovate/all-minor-patch heads (#158) with ~246 Missing: ... from lock file errors. Each one was healed by hand. This stops it happening again.

Root cause

alasql, nested under harper, declares:

"optionalDependencies": { "react-native-fs": "^2.20.0" }

npm omits optional subtrees when it resolves a tree with no lockfile present, but materializes them when reifying against an existing lock. Renovate regenerates package-lock.json from scratch, so it takes the omitting path and writes a lock that npm ci then rejects.

That last part is what took nine occurrences to pin down. Earlier attempts to reproduce it kept "failing" because they started from a lockfile that already had the subtree — that path always keeps it. Deleting the lock first reproduces it every time.

Measured on main, Node 24.19.0 / npm 11.17.0:

keys react-native* npm ci
main as committed 1649 28 exit 0
main, lock deleted + regenerated 1383 0 exit 1, 246 Missing:
this branch, lock deleted + regenerated 1653 37 exit 0

Root-level entries aren't subject to the omission, so declaring it at the root is enough.

Why devDependencies and not optionalDependencies

I tried optionalDependencies first. It fixes the prune, but harper is a devDependency here and this package is published, so an optional root entry flips the whole react-native subtree from dev: true to a production dependency — shipping React Native to everyone who runs npm i -g @harperfast/agent. The lockfile showed it plainly: 307 entries changing their dev flag.

As a devDependency the subtree keeps dev: true and production installs are untouched. Dev install weight doesn't change either — react-native and react-native-fs are already in node_modules today.

The lockfile diff is metadata only

entries added:     0
entries removed:   0
version changes:   0
field-only changes: 249   (dev / optional flags)

The 250 deleted lines are "optional": true / "dev": true flags being reclassified, not packages going away.

Alternatives ruled out

  • postUpdateOptions: ["npmDedupe"] — does fix the prune, but npm dedupe re-resolves to latest-in-range: 71 version changes even on a perfectly healthy lock (esbuild 0.27.2→0.27.7, zod 4.5.4→4.6.2, agent-base 7→9, …). Every dependency PR would silently carry ~70 unrelated bumps.
  • skipInstalls: false — already set in this repo; doesn't help (occurrences 1-9 all happened with it on).
  • --package-lock-only regeneration — never materializes the subtree, however many passes.
  • A CI job that heals and pushes — works, but needs an App token, pushes commits to Renovate branches, and trips Renovate's Edited/Blocked state. A one-line manifest change beats standing automation.

Verification

On this branch, Node 24.19.0 / npm 11.17.0:

npm ci          exit 0
npm run lint    exit 0
npm run format  exit 0
npm run build   exit 0   (ESM + DTS)
npm test        53 files / 345 tests passed

Plus the from-scratch regenerate test in the table above — the one that actually proves the fix.

Note for whoever merges

This does not retroactively fix #158. That branch still carries a pruned lock and needs one full npm install (or a rebase once this is on main).

🤖 Generated with Claude Code

…onal subtree

`npm ci` has failed on nine consecutive `renovate/all-minor-patch` heads
(#158, occurrences 1-9) with ~246 `Missing: ... from lock file` errors for
the react-native / metro / jest / babel tree.

Root cause: `alasql`, nested under `harper`, declares
`optionalDependencies: { "react-native-fs": "^2.20.0" }`. npm omits optional
subtrees when it resolves a tree with **no lockfile present**, but
materializes them when reifying against an existing lock. Renovate
regenerates `package-lock.json` from scratch, so it hits the omitting path
and writes a lock that `npm ci` then rejects.

Reproduced on main (Node 24.19.0, npm 11.17.0) by deleting the lock and
regenerating: keys 1649 -> 1383, `react-native*` entries 28 -> 0,
`npm ci` exit 1 with 246 `Missing:`.

Declaring `react-native-fs` at the root makes it survive that resolve,
because root-level entries are not subject to the omission. Same test on
this branch: keys -> 1653, `react-native*` -> 37, `npm ci` exit 0.

It goes in `devDependencies`, not `optionalDependencies`, on purpose.
`harper` is a devDependency here and this package is published, so an
optional root entry would flip the react-native subtree from `dev: true` to
a production dep and ship React Native to everyone who installs the CLI.
As a devDependency the subtree keeps `dev: true` and production installs are
untouched.

The lockfile diff is metadata only: 0 entries added, 0 removed, 0 version
changes - just `dev`/`optional` flag reclassification on 249 entries.

Renovate is set to ignore the package, since nothing here imports it and
its only job is to keep npm honest.

Verified locally: npm ci, lint, format, build, and 53 files / 345 tests all pass.

@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 react-native-fs as a root dependency in package.json and package-lock.json, and configures renovate.json to ignore updates for it. This change is a workaround to prevent npm from dropping optional subtrees (specifically react-native-fs under alasql/harper) when Renovate regenerates the lockfile without an existing lockfile, which previously resulted in a lockfile missing many entries and causing npm ci failures. There are no review comments to assess, and I have no feedback to provide.

@dawsontoth
dawsontoth merged commit a7262d6 into main Sep 11, 2026
5 checks passed
@dawsontoth
dawsontoth deleted the fix/lockfile-optional-subtree-prune branch September 11, 2026 14:36
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 0.16.50 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant