diff --git a/.changeset/legal-ways-accept.md b/.changeset/legal-ways-accept.md new file mode 100644 index 0000000000..acf6efd234 --- /dev/null +++ b/.changeset/legal-ways-accept.md @@ -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). \ No newline at end of file diff --git a/resolving/npm-resolver/src/index.ts b/resolving/npm-resolver/src/index.ts index f7d6a56dde..4c10449afd 100644 --- a/resolving/npm-resolver/src/index.ts +++ b/resolving/npm-resolver/src/index.ts @@ -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, diff --git a/resolving/npm-resolver/test/index.ts b/resolving/npm-resolver/test/index.ts index 106363b437..c08afff438 100644 --- a/resolving/npm-resolver/test/index.ts +++ b/resolving/npm-resolver/test/index.ts @@ -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') +})