A custom resolver that returns a `file:` or git-hosted tarball without a manifest installs the package without its dependencies
pnpm version
12.4.2
Code to reproduce the issue
Follow-up to #15000, which reported the same defect for a remote https: tarball and is fixed by #15013.
manifest is optional in the ResolveResult a pnpmfile resolvers hook returns, and the hook's resolution is unconstrained. A resolver can therefore claim a dependency and answer with a file: tarball, or with a git host's archive URL:
module.exports = {
resolvers: [
{
canResolve: (wanted) => wanted.alias === 'debug',
async resolve () {
return {
id: '[email protected]',
resolution: { tarball: 'file:./vendor/debug-4.3.4.tgz', integrity: 'sha512-...' },
}
},
},
],
}Expected behavior
The package installs with its own dependencies, as it does for an https: tarball after #15013.
Actual behavior
MissingTarballMetadata::of in pnpm/crates/package-manager/src/prefetching_resolver.rs excludes file: and git-hosted tarball URLs from the resolve-time archive read. The exclusion is right for the resolvers that own those shapes: resolving-local-resolver reads a file: tarball's manifest itself, and resolving-git-resolver reads the archive's manifest and hashes it. A custom resolver bypasses both, so its result reaches walk::fallback_manifest, which synthesizes an identity-only manifest, and the package installs with no dependencies.
Additional information
The two shapes need different work and neither is a variation of the other:
file:—fetch_and_extract_oncealready dispatches afile:URL to the local reader, soFetchTarballForResolutionworks on one as it stands. What is missing is the relative-path rewrite: the install pass resolves a relativefile:against the workspace root inlocal_file_tarball_install_url, and the resolve-time read would have to derive the same absolute URL.- git-hosted — the read needs
manifest_subdirset from the resolution'spath, asresolving-git-resolverdoes for#path:/packages/foo. It must also leave the integrity alone: a git-hosted archive is anchored by its commit SHA, and the exclusion by URL rather than by thegit_hostedflag is deliberate, because that flag is tamper-prone lockfile input. Note also that such an archive is not the package until the fetcher has runprepareand the packlist over it, so its raw root manifest and the installed one can differ.
Raised in review of #15013 by both CodeRabbit-adjacent reviewers on that PR; filed separately rather than carried there because neither shape was reported and both change behavior the local and git resolvers currently own.
Node.js version
24.18.1
Operating System
macOS
Written by an agent (Claude Code, claude-opus-5).
Source: pnpm/pnpm