Repository navigation
Description
Verify latest release
- I verified that the issue exists in the latest pnpm release (12.12.1; also checked 12.0.0 and every 12.x minor)
pnpm version
12.0.0 through 12.12.1. pnpm 11.0.0, 11.28.5 and 10.34.7 match npm.
Which area(s) of pnpm are affected? (leave empty if unsure)
CLI (pnpm pack / pnpm publish)
Reproduction steps
mkdir -p repro-pkg/src/util && cd repro-pkg
echo '{"name":"repro-pkg","version":"1.0.0","files":["src"]}' > package.json
echo x > src/index.js; echo t > src/index.test.js
echo f > src/util/format.js; echo t > src/util/format.test.js
echo '*.test.js' > src/.npmignore
pnx npm@latest pack --dry-run # no *.test.js
pnx pnpm@11 pack --dry-run # no *.test.js
pnx pnpm@12.12.1 pack --dry-run # packs src/index.test.js and src/util/format.test.jsDescribe the Bug
With a files allowlist, pnpm 12 skips every .npmignore / .gitignore, including ones inside the listed directories. npm and pnpm 11 skip only the root ones and keep honoring nested ones within their subtree.
files: ["src"], src/.npmignore = *.test.js:
| path | npm 12.2.0 | pnpm 11.0.0 | pnpm 11.28.5 | pnpm 12.0.0 | pnpm 12.12.1 |
|---|---|---|---|---|---|
| package.json | x | x | x | x | x |
| src/.npmignore | - | - | - | x | - |
| src/index.js | x | x | x | x | x |
| src/index.test.js | - | - | - | x | x |
| src/util/format.js | x | x | x | x | x |
| src/util/format.test.js | - | - | - | x | x |
(npm 10.9.9 and 11.21.0 give the same list as npm 12.2.0. The packed src/.npmignore in 12.0.0 to 12.11.x was fixed by #16746.)
Same result for a deeper nested ignore file (files: ["src"] + src/sub/.npmignore), a glob entry (files: ["src/**"] + src/.npmignore) and a nested .gitignore in a directory without .npmignore: npm and pnpm 11 apply it, pnpm 12 does not. Without a files field pnpm 12.12.1 honors nested .npmignore files correctly, and a root .npmignore is ignored by all tools when files is set, as documented.
Packages that keep tests, fixtures or other private files out of a listed directory with a nested .npmignore start publishing them with pnpm 12, with no warning.
Expected Behavior
Match npm-packlist and pnpm 11: when files is set, ignore only the root .npmignore / .gitignore, and keep applying .npmignore (or .gitignore when there is no .npmignore) in subdirectories.
- npm docs,
files: "You can also provide a.npmignorefile in the root of your package or in subdirectories, which will keep files from being included. At the root of your package it will not override the "files" field, but in subdirectories it will." https://docs.npmjs.com/cli/v11/configuring-npm/package-json#files - npm-packlist README, "Interaction with
.npmignore": ".npmignorefiles in subdirectories are still honored within their subtree." https://github.com/npm/npm-packlist#interaction-with-npmignore - npm-packlist test: https://github.com/npm/npm-packlist/blob/main/test/package-json-filed-dir-nested-npmignore.js (
files: ["lib"],lib/.npmignore=two.js, expects nolib/two.js)
Root cause pointer: in pnpm/crates/fs-packlist/src/lib.rs (v12.12.1, lines 200-204) a usable files matcher selects IgnoreFiles::Skip, and in ignore_walk.rs Skip both empties the root rules (line 40) and sets read_ignore_files: false (line 55), so walk_dir never reads a subdirectory's ignore file (line 108). The crate docs (lib.rs lines 10-12) describe this as intended: "a usable files allowlist disables .gitignore and .npmignore". #13005 introduced the "files disables both" tier and noted that "subdirectory behavior is documented as a known divergence"; now that #16761 walks per directory like ignore-walk, Skip could drop only the root (and workspace-inherited) rules and keep reading nested ones.
pnpm 11 used npm-packlist (pnpm11/fs/packlist, npm-packlist 10.0.4), which is why it matches npm.
Related (same fs-packlist area): #16889 ("files": [] packs everything).
Which Node.js version are you using?
v26.10.0
Which operating systems have you used?
- macOS
- Windows
- Linux
If your OS is a Linux based, which one it is? (Include the version if relevant)
x86_64
Activity
github-actions commentedon Oct 13, 2026
@everett1992, are you planning to open a pull request for this? If so, reply here and a maintainer will assign the issue to you, so nobody else starts on the same fix.
zkochan commentedon Oct 14, 2026
Fix is open in #16920 and covers the whole issue.
With
filesset,pnpm pack/pnpm publishnow skip only the root (and workspace-inherited) ignore files. A subdirectory's.npmignore, or its.gitignorewhen it has no.npmignore, applies within its subtree again, as in npm and pnpm 11. Your repro, thesrc/**glob variant and the nested.gitignorevariant are all covered by tests.One deliberate difference from npm: a file that a
filesentry names explicitly still ships even if a nested ignore file excludes it, at any depth. npm keeps it only when it sits directly in the ignore file's directory.Written by an agent (Claude Code, claude-opus-5-5).
fix(fs-packlist): honor nested ignore files when `files` is set (#16920)
fix: check distribution