Skip to content

fix(lockfile): write npm metadata for new package-lock entries - #7

Draft
mwolson wants to merge 1 commit into
contrib/pnpm-lock-peer-suffix-shapefrom
contrib/npm-lock-new-entry-metadata
Draft

mwolson wants to merge 1 commit into
contrib/pnpm-lock-peer-suffix-shapefrom
contrib/npm-lock-new-entry-metadata

Conversation

@mwolson

@mwolson mwolson commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Summary

New package-lock.json entries written by aube now carry the metadata npm
writes: hasInstallScript even when aube resolved from full packuments, plus
normalized bin and full funding. A later real npm ci therefore runs the
packages' install scripts. aube's internal npm metadata also stays out of
bun.lock. Upstream
#1846 npm-lock-new-entry-install-script
records the abbreviated packument's flag,
#1858 npm-lock-new-entry-bin-deprecated
writes deprecated, and
#1874 npm-libc-rule decides when
libc is written; this layer covers full packuments, the remaining fields and
small additions to that libc rule.

Problem and Fix

Problem and Why it Happened Fix
With the default minimumReleaseAge, aube resolves from full packuments, which carry scripts and no hasInstallScript, so new entries still lacked the flag and npm ci skipped install scripts (core-js, msw). The registry metadata derives the flag from a non-empty preinstall, install or postinstall script, including npm's implicit node-gyp rebuild, and upstream's insertion writes it.
New entries wrote a scoped package's string bin as ./cli.js (npm writes {"metadata": "cli.js"}) and kept only the first funding URL. npm bin normalization runs only in the npm writer (other lockfile formats keep upstream's shape), and funding is kept as published.
A registry package that bundles an optional dependency got __aube_npm_bundled_optional in its bun.lock metadata slot, which Bun never writes and drops on its next install. The bun writer skips aube's internal npm metadata.
Upstream's libc rule ignores an existing libc field that is empty or a scalar, and accepts a loose version in packageManager. On top of upstream's rule, any existing libc field counts as the lockfile using it, only a plain semver pin selects npm's convention, and existing libc fields stay as written.

Validation

  • Repros npm-lock-new-entry-install-script and
    bun-lock-npm-bundled-optional-meta pass (both fail on upstream v2.8.4), and
    npm-lock-new-entry-libc passes.
  • aube add of a package with libc is byte-identical to npm with
    packageManager pins 11.20.0, 11.6.2 and none.
  • A local-registry comparison where every field matches npm 11.6.2.
  • A private React frontend: all 48 new entries match npm 10.8.2, and npm ci
    runs msw's postinstall.
  • Full-metadata cost, measured 2026-10-25: no difference when adding one
    dependency to an existing npm lockfile, and 0 to 7 percent on medians for a
    whole-tree update --lockfile-only, within network noise.

@mwolson
mwolson added this pull request to stack #9 October 24, 2026 23:01
@mwolson
mwolson removed this pull request from stack #9 October 25, 2026 00:54
@mwolson
mwolson force-pushed the contrib/npm-lock-new-entry-metadata branch from d687b90 to 6fa2023 Compare October 25, 2026 00:54
@mwolson
mwolson added this pull request to stack #17 October 25, 2026 00:55
@mwolson
mwolson removed this pull request from stack #17 October 25, 2026 03:08
@mwolson
mwolson force-pushed the contrib/npm-lock-new-entry-metadata branch from 6fa2023 to eab2dbe Compare October 25, 2026 03:08
@mwolson
mwolson added this pull request to stack #19 October 25, 2026 03:08
@mwolson
mwolson removed this pull request from stack #19 October 25, 2026 17:27
@mwolson
mwolson force-pushed the contrib/npm-lock-new-entry-metadata branch from eab2dbe to a3ec6ee Compare October 25, 2026 17:27
@mwolson
mwolson added this pull request to stack #20 October 25, 2026 17:27
jdx added a commit to aubepkg/aube that referenced this pull request Oct 25, 2026
)

<!-- entire-trail-link-start -->
https://entire.com/gh/aubepkg/-/trails/203/changes/aube/203
<!-- entire-trail-link-end -->

## Problem

When `aube add` (or any resolve) adds a new package to an npm
`package-lock.json`, the new entry never gets `"hasInstallScript":
true`, even when the package declares install scripts. `npm ci` only
runs a dependency's `preinstall`/`install`/`postinstall` when that flag
is present, so a lockfile written by aube makes npm silently skip them.
Reported in [discussion
#1824](#1824).

```sh
# package-lock.json is an empty root-only npm lockfile
aube add core-js@3.46.0 --ignore-scripts
npm ci --foreground-scripts   # core-js postinstall should run
```

**Before:** the `node_modules/core-js` entry has no `hasInstallScript`,
and `npm ci` skips its postinstall.
**After:** the entry carries `"hasInstallScript": true`, matching what
`npm install` writes, so `npm ci` runs the postinstall. Entries that
were already in the lockfile were already preserved; this only affects
newly resolved ones.

## Change

The packument's `hasInstallScript` was already parsed but dropped at
resolve time. The resolver now records it in the package's `extra_meta`
(next to `deprecated`), which the npm writer already reads. One resolver
test covers a package with and without the flag.

## Not included

The discussion also lists four other differences on new npm entries:
scoped string `bin` naming, complete `funding` objects, `deprecated`,
and `libc` being written. They are left out here: the fork patch for
them is much larger (full-metadata packument fetching, registry cache
format changes) and deserves its own review. Note `deprecated` is
already recorded in `extra_meta` by the resolver; the npm writer just
does not emit it yet.

## Validation

- `cargo test -p aube-resolver -p aube-lockfile`, `cargo fmt --check`
- `cargo clippy -p aube-resolver --all-targets -- -D warnings`
(workspace-wide clippy hit an unrelated mbx build-script shim error in
this environment, so I ran it for the changed crate only)
- Not run: bats, and a real `npm ci` against an aube-written lockfile.

Based on mwolson's fork change (mwolson#7).

*AI-assisted — Tool: Claude Code; model: anthropic/claude-sonnet-5-5;
version: unavailable.*

🤖 Generated with [Claude Code](https://claude.com/claude-code)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **New Features**
* Resolved package information now records when a package declares an
install script, alongside existing registry deprecation details. The
install-script indicator is included when the package metadata marks it
as present and omitted otherwise. This makes the declaration available
consistently in the results of a fresh package resolution.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Mike Olson <mwolson@member.fsf.org>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
@mwolson
mwolson removed this pull request from stack #20 October 25, 2026 21:33
@mwolson
mwolson force-pushed the contrib/npm-lock-new-entry-metadata branch from a3ec6ee to 90bcde4 Compare October 25, 2026 21:33
@mwolson
mwolson added this pull request to stack #21 October 25, 2026 21:33
@mwolson
mwolson removed this pull request from stack #21 October 26, 2026 01:06
@mwolson
mwolson changed the base branch from contrib/npm-lock-peer-entries to contrib/pnpm-lock-peer-suffix-shape October 26, 2026 01:07
@mwolson
mwolson force-pushed the contrib/npm-lock-new-entry-metadata branch from 90bcde4 to 8c5bec9 Compare October 26, 2026 01:07
@mwolson
mwolson added this pull request to stack #22 October 26, 2026 01:07
@mwolson
mwolson removed this pull request from stack #22 October 26, 2026 02:36
@mwolson
mwolson added this pull request to stack #24 October 26, 2026 02:36
jdx added a commit to aubepkg/aube that referenced this pull request Oct 26, 2026
…ct's npm does (#1874)

<!-- entire-trail-link-start -->
https://entire.com/gh/aubepkg/-/trails/230/changes/aube/230
<!-- entire-trail-link-end -->

Adding a package that declares `libc` (for example
`@rollup/rollup-linux-x64-gnu`) always wrote `"libc": [...]` on its new
npm lockfile entry. npm only started writing that field in 11.11.0, and
older npm strips it on the next write, so teammates on npm 10 or 11.6
got lockfile churn after every `aube add`. `npm ci` accepts both shapes.

A new entry now gets `libc` only when the project's npm would write it:

1. any entry in the existing `package-lock.json` already has `libc`, or
2. the root `packageManager` is `npm@11.11.0` or later.

Otherwise it is omitted. Entries already in the lockfile keep whatever
they had.

```json
{ "packageManager": "npm@11.6.2" }
```

With this manifest, `aube add @rollup/rollup-linux-x64-gnu` no longer
adds `libc` to the lockfile; with `npm@11.20.0` it does.

Based on the rule proposed in the discussion (mwolson#7).

Tests cover each branch plus existing entries; `cargo test -p
aube-lockfile` and clippy pass. Not run against real npm here.

Discussion:
#1824 (comment)

*AI-assisted — Tool: Claude Code; model: anthropic/claude-sonnet-5-5;
version: unavailable.*

🤖 Generated with [Claude Code](https://claude.com/claude-code)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **Bug Fixes**
* npm lockfiles now preserve nonempty `libc` metadata for existing
package entries. Existing entries without this metadata remain without
it, including when adding an alias of the same package.
* New package entries include `libc` metadata when the project specifies
npm 11.11.0 or later. Older or unspecified npm versions and pnpm omit
the metadata by default.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
@mwolson
mwolson removed this pull request from stack #24 October 26, 2026 11:35
@mwolson
mwolson added this pull request to stack #26 October 26, 2026 11:36
Retain complete funding and derive install-script metadata from full
packuments. Match the project's libc lockfile convention while retaining
install-time platform constraints and imported libc presence and shape.
Only a plain npm semver pin at or above 11.11.0 opts into new libc fields
when the existing lockfile has no libc metadata.

Normalize executable names and paths only when writing npm lockfiles;
keep registry paths unchanged on the graph for other lockfile formats.
Retain implicit-name executables across compact cache writes and reads,
including older sentinel maps. Rehydrate empty object keys as the package
command before each writer applies its format's bin handling.

Skip npm-only entry, bundle, retained-entry and workspace metadata when
writing bun.lock.
@mwolson
mwolson force-pushed the contrib/npm-lock-new-entry-metadata branch from 8c5bec9 to 4681743 Compare October 30, 2026 15:54
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