import { prepareEmpty } from '@pnpm/prepare' import { addDependenciesToPackage, install } from '@pnpm/core' import { addDistTag } from '@pnpm/registry-mock' import { testDefaults } from '../utils/index.js' test('prefer version ranges specified for top dependencies', async () => { const project = prepareEmpty() await addDistTag({ package: '@pnpm.e2e/dep-of-pkg-with-1-dep', version: '100.1.0', distTag: 'latest' }) await install( { dependencies: { '@pnpm.e2e/dep-of-pkg-with-1-dep': '100.0.0', '@pnpm.e2e/pkg-with-1-dep': '*', }, }, testDefaults() ) const lockfile = project.readLockfile() expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@100.0.0']) expect(lockfile.packages).not.toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@100.1.0']) }) test('prefer version ranges specified for top dependencies, when doing named installation', async () => { const project = prepareEmpty() await addDistTag({ package: '@pnpm.e2e/dep-of-pkg-with-1-dep', version: '100.1.0', distTag: 'latest' }) const { updatedManifest: manifest } = await install( { dependencies: { '@pnpm.e2e/dep-of-pkg-with-1-dep': '100.0.0', }, }, testDefaults() ) await addDependenciesToPackage(manifest, ['@pnpm.e2e/pkg-with-1-dep'], testDefaults()) const lockfile = project.readLockfile() expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@100.0.0']) expect(lockfile.packages).not.toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@100.1.0']) }) test('prefer version ranges specified for top dependencies, even if they are aliased', async () => { const project = prepareEmpty() await addDistTag({ package: '@pnpm.e2e/dep-of-pkg-with-1-dep', version: '100.1.0', distTag: 'latest' }) await install( { dependencies: { foo: 'npm:@pnpm.e2e/dep-of-pkg-with-1-dep@100.0.0', '@pnpm.e2e/pkg-with-1-dep': '*', }, }, testDefaults() ) const lockfile = project.readLockfile() expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@100.0.0']) expect(lockfile.packages).not.toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@100.1.0']) }) test('prefer version ranges specified for top dependencies, even if the subdependencies are aliased', async () => { const project = prepareEmpty() await addDistTag({ package: '@pnpm.e2e/dep-of-pkg-with-1-dep', version: '100.1.0', distTag: 'latest' }) await install( { dependencies: { '@pnpm.e2e/dep-of-pkg-with-1-dep': '100.0.0', '@pnpm.e2e/pkg-with-1-aliased-dep': '100.0.0', }, }, testDefaults() ) const lockfile = project.readLockfile() expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@100.0.0']) expect(lockfile.packages).not.toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@100.1.0']) }) test('ignore version of root dependency when it is incompatible with the indirect dependency\'s range', async () => { const project = prepareEmpty() await addDistTag({ package: '@pnpm.e2e/dep-of-pkg-with-1-dep', version: '100.0.0', distTag: 'latest' }) await install( { dependencies: { '@pnpm.e2e/dep-of-pkg-with-1-dep': '101.0.0', '@pnpm.e2e/pkg-with-1-dep': '100.0.0', }, }, testDefaults() ) const lockfile = project.readLockfile() expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@100.0.0']) expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@101.0.0']) }) test('prefer dist-tag specified for top dependency', async () => { const project = prepareEmpty() await addDistTag({ package: '@pnpm.e2e/dep-of-pkg-with-1-dep', version: '100.1.0', distTag: 'latest' }) await addDistTag({ package: '@pnpm.e2e/dep-of-pkg-with-1-dep', version: '100.0.0', distTag: 'stable' }) await install( { dependencies: { '@pnpm.e2e/dep-of-pkg-with-1-dep': 'stable', '@pnpm.e2e/pkg-with-1-dep': '100.0.0', }, }, testDefaults() ) const lockfile = project.readLockfile() expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@100.0.0']) expect(lockfile.packages).not.toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@100.1.0']) }) test('prefer version ranges passed in via opts.preferredVersions', async () => { await addDistTag({ package: '@pnpm.e2e/dep-of-pkg-with-1-dep', version: '100.1.0', distTag: 'latest' }) const project = prepareEmpty() await install( { dependencies: { '@pnpm.e2e/dep-of-pkg-with-1-dep': '^100.0.0', '@pnpm.e2e/pkg-with-1-dep': '*', }, }, testDefaults( { preferredVersions: { '@pnpm.e2e/dep-of-pkg-with-1-dep': { '100.0.0': 'version', }, }, } ) ) const lockfile = project.readLockfile() expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@100.0.0']) expect(lockfile.packages).not.toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@100.1.0']) }) // Covers https://github.com/pnpm/pnpm/issues/1187 test('prefer version of package that also satisfies the range of the same package higher in the dependency graph', async () => { const project = prepareEmpty() await addDistTag({ package: '@pnpm.e2e/foo', version: '100.1.0', distTag: 'latest' }) await addDependenciesToPackage( {}, ['@pnpm.e2e/has-foo-as-dep-and-subdep'], testDefaults() ) const lockfile = project.readLockfile() expect( Object.keys(lockfile.packages) ).toStrictEqual( [ '@pnpm.e2e/foo@100.0.0', '@pnpm.e2e/has-foo-as-dep-and-subdep@1.0.0', '@pnpm.e2e/requires-any-foo@1.0.0', ] ) }) test('dedupe subdependency when a newer version of the same package is installed', async () => { const project = prepareEmpty() await addDistTag({ package: '@pnpm.e2e/dep-of-pkg-with-1-dep', version: '100.0.0', distTag: 'latest' }) const { updatedManifest: manifest } = await addDependenciesToPackage({}, ['@pnpm.e2e/dep-of-pkg-with-1-dep@100.0.0', '@pnpm.e2e/pkg-with-1-dep@100.0.0'], testDefaults()) await addDistTag({ package: '@pnpm.e2e/dep-of-pkg-with-1-dep', version: '100.1.0', distTag: 'latest' }) await addDependenciesToPackage(manifest, ['@pnpm.e2e/dep-of-pkg-with-1-dep@100.1.0'], testDefaults()) const lockfile = project.readLockfile() expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@100.1.0']) expect(lockfile.packages).not.toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@100.0.0']) }) test('when resolving dependencies, prefer versions that are used by direct dependencies over versions used in subdeps', async () => { await addDistTag({ package: '@pnpm.e2e/foo', version: '100.1.0', distTag: 'latest' }) const project = prepareEmpty() const { updatedManifest: manifest } = await install({ dependencies: { '@pnpm.e2e/foo': '100.0.0', '@pnpm.e2e/has-foo-100.1.0-dep-1': '1.0.0', '@pnpm.e2e/has-foo-100.1.0-dep-2': '1.0.0', }, }, testDefaults()) await addDependenciesToPackage(manifest, ['@pnpm.e2e/has-foo-100.0.0-range-dep'], testDefaults()) const lockfile = project.readLockfile() expect(lockfile.snapshots['@pnpm.e2e/has-foo-100.0.0-range-dep@1.0.0']).toHaveProperty(['dependencies', '@pnpm.e2e/foo'], '100.0.0') }) test('when resolving dependencies, prefer versions that are used by direct dependencies over versions used in subdeps #2', async () => { await addDistTag({ package: '@pnpm.e2e/foo', version: '100.1.0', distTag: 'latest' }) const project = prepareEmpty() const { updatedManifest: manifest } = await install({ dependencies: { '@pnpm.e2e/foo': '100.0.0', '@pnpm.e2e/has-foo-100.1.0-dep-1': '1.0.0', '@pnpm.e2e/has-foo-100.1.0-dep-2': '1.0.0', }, }, testDefaults()) await addDependenciesToPackage(manifest, ['@pnpm.e2e/has-foo-100.0.0-range-dep'], testDefaults()) const lockfile = project.readLockfile() expect(lockfile.snapshots['@pnpm.e2e/has-foo-100.0.0-range-dep@1.0.0']).toHaveProperty(['dependencies', '@pnpm.e2e/foo'], '100.0.0') })