Skip to content

feat: exclude patchedDependencies patch files from the package tarball#291

Merged
owlstronaut merged 1 commit into
npm:mainfrom
manzoorwanijk:feat/exclude-patched-dependencies
Jun 5, 2026
Merged

feat: exclude patchedDependencies patch files from the package tarball#291
owlstronaut merged 1 commit into
npm:mainfrom
manzoorwanijk:feat/exclude-patched-dependencies

Conversation

@manzoorwanijk
Copy link
Copy Markdown
Contributor

@manzoorwanijk manzoorwanijk commented Jun 5, 2026

Part of native dependency patching (npm/rfcs#862). This force-excludes the patch files declared in the root package's patchedDependencies from the packed file list, even when they are listed in files.

Why

patchedDependencies maps a dependency selector to a project-local patch file (e.g. "abbrev@2.0.0": "patches/abbrev@2.0.0.patch"). Those patches are a property of the project, not something a consumer of the published package applies. Without this, publishing a patched project would ship the patch files — and, once pacote strips the patchedDependencies field from the tarball's package.json, they would be dangling, unreferenced files. Excluding them keeps the published tarball clean.

How

In PackWalker.processPackage, when the walker is the project root and the manifest declares patchedDependencies, each declared patch file path is pushed onto the strict (un-overridable) rule set, so it is excluded even if files lists it. Design choices:

  • Exact files, not directories. Only the declared patch files are excluded — never their directory. A dedicated patches/ dir becomes empty and drops out naturally, but a patch that lives in a shared directory (e.g. src/foo.patch) does not take the rest of src/ down with it.
  • --patches-dir honored for free. The location is read straight off the patchedDependencies values, which already encode wherever the patches were written.
  • Root-only. patchedDependencies is root-only state, so the block is gated to the project root and never prunes a bundled dependency's files.
  • Path safety. Absolute paths and paths that escape the package root are skipped (they are never packed anyway).
  • Warns when a files entry pulled a patch file in (directly or via its directory), so the override is not silent.

References

Part of

Related to

@manzoorwanijk manzoorwanijk marked this pull request as ready for review June 5, 2026 12:31
@manzoorwanijk manzoorwanijk requested a review from a team as a code owner June 5, 2026 12:31
@owlstronaut owlstronaut merged commit 7e2513d into npm:main Jun 5, 2026
24 checks passed
@github-actions github-actions Bot mentioned this pull request Jun 5, 2026
owlstronaut pushed a commit to npm/pacote that referenced this pull request Jun 5, 2026
Part of native dependency patching
([npm/rfcs#862](npm/rfcs#862)). When packing a
`directory` spec (the `npm publish` / `npm pack` path), this strips a
top-level `patchedDependencies` field from the `package.json` written
**into the tarball**.

## Why

`patchedDependencies` declares project-local patches against installed
dependencies. It is honored only in a root manifest, so it is
meaningless to consumers of a published package and should never travel
through the registry. The published *packument* manifest is already
stripped in `libnpmpublish`; this closes the other half — the
`package.json` inside the tarball itself — so `npm pack --dry-run` and
the published tarball no longer carry the field. It pairs with the
npm-packlist change that excludes the patch files themselves; together
they guarantee a patched project publishes clean.

## How

`DirFetcher` packs the raw on-disk files via `tar.c`, so the tarball's
`package.json` is the literal file on disk — there is no manifest seam
to edit. The new `#tarOptions()`:

1. Reads the on-disk `package.json` (after `prepare`) via
`@npmcli/package-json`. If it has no `patchedDependencies`, returns the
existing options unchanged — **non-patched packs are byte-for-byte
identical to before**.
2. Otherwise deletes the field and re-serializes preserving the original
indent, newline, and key order (the indent/newline symbols
`@npmcli/package-json` attaches; `JSON.stringify` ignores them), writes
the stripped copy to a temp dir, and removes the temp dir if the write
fails.
3. Sets node-tar's `onWriteEntry` to redirect **only** the top-level
`package.json` entry's `absolute` at the stripped copy and fix its
`stat.size`/`nlink`. `onWriteEntry` runs before the header and the
file's hardlink check, so the override is honored; every other file is
untouched.
4. The temp dir is removed once the tar source stream emits
`end`/`error`, so it outlives content consumption.

No behavior change for any package without `patchedDependencies`.

## References

Part of
- npm/rfcs#862

Related to
- npm/cli#9439
- npm/npm-packlist#291
@manzoorwanijk manzoorwanijk deleted the feat/exclude-patched-dependencies branch June 5, 2026 15:12
owlstronaut pushed a commit that referenced this pull request Jun 5, 2026
🤖 I have created a release *beep* *boop*
---


##
[11.2.0](v11.1.0...v11.2.0)
(2026-06-05)
### Features
*
[`7e2513d`](7e2513d)
[#291](#291) exclude
patchedDependencies patch files from the package tarball (#291)
(@manzoorwanijk)

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

2 participants