mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-27 18:46:18 -04:00
fix(npm-resolver): respect version constraints when falling back to workspace packages (#10704)
* fix(npm-resolver): respect version constraints when falling back to workspace packages When link-workspace-packages=true, the fallback resolution paths (registry 404 and no matching registry version) pass update: Boolean(opts.update) to tryResolveFromWorkspacePackages. On fresh installs without a lockfile entry, opts.update is 'compatible' (truthy), which overrides the version spec to '*' and matches any workspace package regardless of version. Change both fallback call sites to pass update: false so version constraints are always respected for non-workspace-protocol dependencies. The workspace: protocol path returns before these blocks and correctly continues to use opts.update. Close #10173 * test: clarify npm-resolver test names for workspace version mismatch scenarios --------- Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
6
.changeset/legal-ways-accept.md
Normal file
6
.changeset/legal-ways-accept.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/npm-resolver": patch
|
||||
pnpm: patch
|
||||
---
|
||||
|
||||
Fix `link-workspace-packages=true` incorrectly linking workspace packages when the requested version doesn't match the workspace package's version. Previously, on fresh installs the version constraint is overridden to `*` in the fallback resolution paths, causing any workspace package with a matching name to be linked regardless of version [#10173](https://github.com/pnpm/pnpm/issues/10173).
|
||||
@@ -361,7 +361,7 @@ async function resolveNpm (
|
||||
projectDir: opts.projectDir,
|
||||
lockfileDir: opts.lockfileDir,
|
||||
hardLinkLocalPackages: opts.injectWorkspacePackages === true || wantedDependency.injected,
|
||||
update: Boolean(opts.update),
|
||||
update: false,
|
||||
saveWorkspaceProtocol: ctx.saveWorkspaceProtocol,
|
||||
calcSpecifier: opts.calcSpecifier,
|
||||
pinnedVersion: opts.pinnedVersion,
|
||||
@@ -382,7 +382,7 @@ async function resolveNpm (
|
||||
projectDir: opts.projectDir,
|
||||
lockfileDir: opts.lockfileDir,
|
||||
hardLinkLocalPackages: opts.injectWorkspacePackages === true || wantedDependency.injected,
|
||||
update: Boolean(opts.update),
|
||||
update: false,
|
||||
saveWorkspaceProtocol: ctx.saveWorkspaceProtocol,
|
||||
calcSpecifier: opts.calcSpecifier,
|
||||
pinnedVersion: opts.pinnedVersion,
|
||||
|
||||
@@ -2015,3 +2015,96 @@ test('pick lowest version by * when there are only prerelease versions', async (
|
||||
expect(resolveResult!.manifest!.name).toBe('is-positive')
|
||||
expect(resolveResult!.manifest!.version).toBe('1.0.0-alpha.1')
|
||||
})
|
||||
|
||||
test('throws when workspace package version does not match and package is not found in the registry', async () => {
|
||||
nock(registries.default)
|
||||
.get('/is-positive')
|
||||
.reply(404, {})
|
||||
|
||||
const cacheDir = temporaryDirectory()
|
||||
const { resolveFromNpm } = createResolveFromNpm({
|
||||
storeDir: temporaryDirectory(),
|
||||
cacheDir,
|
||||
registries,
|
||||
})
|
||||
|
||||
await expect(
|
||||
resolveFromNpm({ alias: 'is-positive', bareSpecifier: '2.0.0' }, {
|
||||
projectDir: '/home/istvan/src',
|
||||
update: 'compatible',
|
||||
workspacePackages: new Map([
|
||||
['is-positive', new Map([
|
||||
['1.0.0', {
|
||||
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
|
||||
manifest: {
|
||||
name: 'is-positive',
|
||||
version: '1.0.0',
|
||||
},
|
||||
}],
|
||||
])],
|
||||
]),
|
||||
})
|
||||
).rejects.toThrow()
|
||||
})
|
||||
|
||||
test('throws NoMatchingVersionError when workspace package version does not match and registry has no matching version', async () => {
|
||||
nock(registries.default)
|
||||
.get('/is-positive')
|
||||
.reply(200, isPositiveMeta)
|
||||
|
||||
const cacheDir = temporaryDirectory()
|
||||
const { resolveFromNpm } = createResolveFromNpm({
|
||||
storeDir: temporaryDirectory(),
|
||||
cacheDir,
|
||||
registries,
|
||||
})
|
||||
|
||||
await expect(
|
||||
resolveFromNpm({ alias: 'is-positive', bareSpecifier: '99.0.0' }, {
|
||||
projectDir: '/home/istvan/src',
|
||||
update: 'compatible',
|
||||
workspacePackages: new Map([
|
||||
['is-positive', new Map([
|
||||
['1.0.0', {
|
||||
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
|
||||
manifest: {
|
||||
name: 'is-positive',
|
||||
version: '1.0.0',
|
||||
},
|
||||
}],
|
||||
])],
|
||||
]),
|
||||
})
|
||||
).rejects.toThrow(NoMatchingVersionError)
|
||||
})
|
||||
|
||||
test('resolve from registry when workspace package version does not match the requested version', async () => {
|
||||
nock(registries.default)
|
||||
.get('/is-positive')
|
||||
.reply(200, isPositiveMeta)
|
||||
|
||||
const cacheDir = temporaryDirectory()
|
||||
const { resolveFromNpm } = createResolveFromNpm({
|
||||
storeDir: temporaryDirectory(),
|
||||
cacheDir,
|
||||
registries,
|
||||
})
|
||||
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '3.1.0' }, {
|
||||
projectDir: '/home/istvan/src',
|
||||
update: 'compatible',
|
||||
workspacePackages: new Map([
|
||||
['is-positive', new Map([
|
||||
['1.0.0', {
|
||||
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
|
||||
manifest: {
|
||||
name: 'is-positive',
|
||||
version: '1.0.0',
|
||||
},
|
||||
}],
|
||||
])],
|
||||
]),
|
||||
})
|
||||
|
||||
expect(resolveResult!.resolvedVia).toBe('npm-registry')
|
||||
expect(resolveResult!.id).toBe('is-positive@3.1.0')
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user