mirror of
https://github.com/pnpm/pnpm.git
synced 2026-07-25 15:07:06 -04:00
297 lines
11 KiB
TypeScript
297 lines
11 KiB
TypeScript
import path from 'path'
|
|
import fs from 'fs'
|
|
import { sync as readYamlFile } from 'read-yaml-file'
|
|
import { PnpmError } from '@pnpm/error'
|
|
import { prepare, prepareEmpty, preparePackages } from '@pnpm/prepare'
|
|
import { addDistTag } from '@pnpm/registry-mock'
|
|
import { WANTED_LOCKFILE } from '@pnpm/constants'
|
|
import { type MutatedProject, type ProjectOptions, addDependenciesToPackage, mutateModulesInSingleProject, mutateModules } from '@pnpm/core'
|
|
import { type LockfileFile } from '@pnpm/lockfile.types'
|
|
import { type ProjectRootDir, type ProjectManifest } from '@pnpm/types'
|
|
import { testDefaults } from '../utils/index.js'
|
|
|
|
test('versions are replaced with versions specified through overrides option', async () => {
|
|
const project = prepareEmpty()
|
|
|
|
await addDistTag({ package: '@pnpm.e2e/bar', version: '100.0.0', distTag: 'latest' })
|
|
await addDistTag({ package: '@pnpm.e2e/foo', version: '100.0.0', distTag: 'latest' })
|
|
|
|
const overrides: Record<string, string> = {
|
|
'@pnpm.e2e/foobarqar>@pnpm.e2e/foo': 'npm:@pnpm.e2e/qar@100.0.0',
|
|
'@pnpm.e2e/bar@^100.0.0': '100.1.0',
|
|
'@pnpm.e2e/dep-of-pkg-with-1-dep': '101.0.0',
|
|
}
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({},
|
|
['@pnpm.e2e/pkg-with-1-dep@100.0.0', '@pnpm.e2e/foobar@100.0.0', '@pnpm.e2e/foobarqar@1.0.0'],
|
|
testDefaults({ overrides })
|
|
)
|
|
|
|
{
|
|
const lockfile = project.readLockfile()
|
|
expect(lockfile.snapshots['@pnpm.e2e/foobarqar@1.0.0'].dependencies?.['@pnpm.e2e/foo']).toBe('@pnpm.e2e/qar@100.0.0')
|
|
expect(lockfile.snapshots['@pnpm.e2e/foobar@100.0.0'].dependencies?.['@pnpm.e2e/foo']).toBe('100.0.0')
|
|
expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@101.0.0'])
|
|
expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/bar@100.1.0'])
|
|
expect(lockfile.overrides).toStrictEqual({
|
|
'@pnpm.e2e/foobarqar>@pnpm.e2e/foo': 'npm:@pnpm.e2e/qar@100.0.0',
|
|
'@pnpm.e2e/bar@^100.0.0': '100.1.0',
|
|
'@pnpm.e2e/dep-of-pkg-with-1-dep': '101.0.0',
|
|
})
|
|
const currentLockfile = project.readCurrentLockfile()
|
|
expect(lockfile.overrides).toStrictEqual(currentLockfile.overrides)
|
|
}
|
|
// shall be able to install when package manifest is ignored
|
|
await mutateModulesInSingleProject({
|
|
manifest,
|
|
mutation: 'install',
|
|
rootDir: process.cwd() as ProjectRootDir,
|
|
}, { ...testDefaults(), ignorePackageManifest: true, overrides })
|
|
|
|
// The lockfile is updated if the overrides are changed
|
|
overrides['@pnpm.e2e/bar@^100.0.0'] = '100.0.0'
|
|
// A direct dependency may be overridden as well
|
|
overrides['@pnpm.e2e/foobarqar'] = '1.0.1'
|
|
await mutateModulesInSingleProject({
|
|
manifest,
|
|
mutation: 'install',
|
|
rootDir: process.cwd() as ProjectRootDir,
|
|
}, testDefaults({ overrides }))
|
|
|
|
{
|
|
const lockfile = project.readLockfile()
|
|
expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/dep-of-pkg-with-1-dep@101.0.0'])
|
|
expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/bar@100.0.0'])
|
|
expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/foobarqar@1.0.1'])
|
|
expect(lockfile.overrides).toStrictEqual({
|
|
'@pnpm.e2e/foobarqar': '1.0.1',
|
|
'@pnpm.e2e/foobarqar>@pnpm.e2e/foo': 'npm:@pnpm.e2e/qar@100.0.0',
|
|
'@pnpm.e2e/bar@^100.0.0': '100.0.0',
|
|
'@pnpm.e2e/dep-of-pkg-with-1-dep': '101.0.0',
|
|
})
|
|
const currentLockfile = project.readCurrentLockfile()
|
|
expect(lockfile.overrides).toStrictEqual(currentLockfile.overrides)
|
|
}
|
|
|
|
await mutateModulesInSingleProject({
|
|
manifest,
|
|
mutation: 'install',
|
|
rootDir: process.cwd() as ProjectRootDir,
|
|
}, testDefaults({ frozenLockfile: true, overrides }))
|
|
|
|
{
|
|
const lockfile = project.readLockfile()
|
|
expect(lockfile.overrides).toStrictEqual({
|
|
'@pnpm.e2e/foobarqar': '1.0.1',
|
|
'@pnpm.e2e/foobarqar>@pnpm.e2e/foo': 'npm:@pnpm.e2e/qar@100.0.0',
|
|
'@pnpm.e2e/bar@^100.0.0': '100.0.0',
|
|
'@pnpm.e2e/dep-of-pkg-with-1-dep': '101.0.0',
|
|
})
|
|
const currentLockfile = project.readCurrentLockfile()
|
|
expect(lockfile.overrides).toStrictEqual(currentLockfile.overrides)
|
|
}
|
|
|
|
overrides['@pnpm.e2e/bar@^100.0.0'] = '100.0.1'
|
|
await expect(
|
|
mutateModulesInSingleProject({
|
|
manifest,
|
|
mutation: 'install',
|
|
rootDir: process.cwd() as ProjectRootDir,
|
|
}, testDefaults({ frozenLockfile: true, overrides }))
|
|
).rejects.toThrow(
|
|
new PnpmError('LOCKFILE_CONFIG_MISMATCH',
|
|
'Cannot proceed with the frozen installation. The current "overrides" configuration doesn\'t match the value found in the lockfile'
|
|
)
|
|
)
|
|
})
|
|
|
|
test('when adding a new dependency that is present in the overrides, use the spec from the override', async () => {
|
|
prepareEmpty()
|
|
|
|
await addDistTag({ package: '@pnpm.e2e/bar', version: '100.0.0', distTag: 'latest' })
|
|
|
|
const overrides = {
|
|
'@pnpm.e2e/bar': '100.1.0',
|
|
}
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({},
|
|
['@pnpm.e2e/bar'],
|
|
testDefaults({ overrides })
|
|
)
|
|
|
|
expect(manifest.dependencies?.['@pnpm.e2e/bar']).toBe(overrides['@pnpm.e2e/bar'])
|
|
})
|
|
|
|
test('explicitly specifying a version at install will ignore overrides', async () => {
|
|
prepareEmpty()
|
|
|
|
await addDistTag({ package: '@pnpm.e2e/bar', version: '100.0.0', distTag: 'latest' })
|
|
|
|
const overrides = {
|
|
'@pnpm.e2e/bar': '100.1.0',
|
|
}
|
|
const EXACT_VERSION = '100.0.0'
|
|
const { updatedManifest: manifest } = await addDependenciesToPackage({},
|
|
[`@pnpm.e2e/bar@${EXACT_VERSION}`],
|
|
testDefaults({ overrides })
|
|
)
|
|
|
|
expect(manifest.dependencies?.['@pnpm.e2e/bar']).toBe(EXACT_VERSION)
|
|
})
|
|
|
|
test('overrides with local file and link specs', async () => {
|
|
interface LocationAndManifest {
|
|
location: string
|
|
package: ProjectManifest
|
|
}
|
|
const root: LocationAndManifest = {
|
|
location: '.',
|
|
package: {
|
|
name: 'root',
|
|
},
|
|
}
|
|
const direct: LocationAndManifest = {
|
|
location: 'packages/direct',
|
|
package: {
|
|
name: 'direct',
|
|
dependencies: {
|
|
'relative-file-pkg': '*',
|
|
'absolute-file-pkg': '*',
|
|
'relative-link-pkg': '*',
|
|
'absolute-link-pkg': '*',
|
|
},
|
|
},
|
|
}
|
|
const indirect: LocationAndManifest = {
|
|
location: 'packages/indirect',
|
|
package: {
|
|
name: 'indirect',
|
|
dependencies: {
|
|
'@pnpm.e2e/depends-on-pkg-abcd': '1.0.0',
|
|
},
|
|
},
|
|
}
|
|
const pkg: LocationAndManifest = {
|
|
location: 'overrides/pkg',
|
|
package: {
|
|
name: 'pkg',
|
|
version: '0.0.0',
|
|
},
|
|
}
|
|
preparePackages([
|
|
root,
|
|
direct,
|
|
indirect,
|
|
pkg,
|
|
])
|
|
|
|
const importers = [root, direct, indirect].map(({ location }): MutatedProject => ({
|
|
mutation: 'install',
|
|
rootDir: path.resolve(location) as ProjectRootDir,
|
|
}))
|
|
const allProjects = [root, direct, indirect].map((input): ProjectOptions => ({
|
|
buildIndex: 0,
|
|
manifest: input.package,
|
|
rootDir: path.resolve(input.location) as ProjectRootDir,
|
|
}))
|
|
await mutateModules(importers, {
|
|
...testDefaults({ allProjects }),
|
|
overrides: {
|
|
'relative-file-pkg': 'file:./overrides/pkg',
|
|
'absolute-file-pkg': `file:${path.resolve('overrides/pkg')}`,
|
|
'relative-link-pkg': 'link:./overrides/pkg',
|
|
'absolute-link-pkg': `link:${path.resolve('overrides/pkg')}`,
|
|
'@pnpm.e2e/pkg-a': 'file:./overrides/pkg',
|
|
'@pnpm.e2e/pkg-b': `file:${path.resolve('overrides/pkg')}`,
|
|
'@pnpm.e2e/pkg-c': 'link:./overrides/pkg',
|
|
'@pnpm.e2e/pkg-d': `link:${path.resolve('overrides/pkg')}`,
|
|
},
|
|
})
|
|
|
|
const lockfile = readYamlFile<LockfileFile>(WANTED_LOCKFILE)
|
|
|
|
expect(lockfile.importers?.['packages/direct']).toStrictEqual({
|
|
dependencies: {
|
|
'relative-file-pkg': {
|
|
specifier: 'file:../../overrides/pkg',
|
|
version: 'pkg@file:overrides/pkg',
|
|
},
|
|
'absolute-file-pkg': {
|
|
specifier: `file:${path.resolve('overrides/pkg')}`,
|
|
version: 'pkg@file:overrides/pkg',
|
|
},
|
|
'relative-link-pkg': {
|
|
specifier: 'link:../../overrides/pkg',
|
|
version: 'link:../../overrides/pkg',
|
|
},
|
|
'absolute-link-pkg': {
|
|
specifier: `link:${path.resolve('overrides/pkg')}`,
|
|
version: 'link:../../overrides/pkg',
|
|
},
|
|
},
|
|
})
|
|
|
|
expect(lockfile.snapshots?.['@pnpm.e2e/depends-on-pkg-abcd@1.0.0']).toStrictEqual({
|
|
dependencies: {
|
|
'@pnpm.e2e/pkg-a': 'pkg@file:overrides/pkg',
|
|
'@pnpm.e2e/pkg-b': 'pkg@file:overrides/pkg',
|
|
'@pnpm.e2e/pkg-c': 'link:overrides/pkg',
|
|
'@pnpm.e2e/pkg-d': 'link:overrides/pkg',
|
|
},
|
|
})
|
|
|
|
const directPrefix = 'packages/direct/node_modules'
|
|
expect(fs.realpathSync(path.join(directPrefix, 'absolute-file-pkg'))).toBe(path.resolve('node_modules/.pnpm/pkg@file+overrides+pkg/node_modules/pkg'))
|
|
expect(fs.realpathSync(path.join(directPrefix, 'relative-file-pkg'))).toBe(path.resolve('node_modules/.pnpm/pkg@file+overrides+pkg/node_modules/pkg'))
|
|
expect(fs.realpathSync(path.join(directPrefix, 'absolute-link-pkg'))).toBe(path.resolve('overrides/pkg'))
|
|
expect(fs.realpathSync(path.join(directPrefix, 'relative-link-pkg'))).toBe(path.resolve('overrides/pkg'))
|
|
|
|
const indirectPrefix = 'node_modules/.pnpm/@pnpm.e2e+depends-on-pkg-abcd@1.0.0/node_modules'
|
|
expect(fs.realpathSync(path.join(indirectPrefix, '@pnpm.e2e/pkg-a'))).toBe(path.resolve('node_modules/.pnpm/pkg@file+overrides+pkg/node_modules/pkg'))
|
|
expect(fs.realpathSync(path.join(indirectPrefix, '@pnpm.e2e/pkg-b'))).toBe(path.resolve('node_modules/.pnpm/pkg@file+overrides+pkg/node_modules/pkg'))
|
|
expect(fs.realpathSync(path.join(indirectPrefix, '@pnpm.e2e/pkg-c'))).toBe(path.resolve('overrides/pkg'))
|
|
expect(fs.realpathSync(path.join(indirectPrefix, '@pnpm.e2e/pkg-d'))).toBe(path.resolve('overrides/pkg'))
|
|
})
|
|
|
|
test('overrides remove dependencies', async () => {
|
|
const manifest: ProjectManifest = {
|
|
dependencies: {
|
|
'@pnpm.e2e/pkg-with-good-optional': '1.0.0',
|
|
},
|
|
pnpm: {
|
|
overrides: {
|
|
'@pnpm.e2e/pkg-with-good-optional>is-positive': '-',
|
|
},
|
|
},
|
|
}
|
|
|
|
const project = prepare(manifest)
|
|
|
|
await mutateModulesInSingleProject({
|
|
manifest,
|
|
mutation: 'install',
|
|
rootDir: process.cwd() as ProjectRootDir,
|
|
}, testDefaults({
|
|
overrides: manifest.pnpm?.overrides,
|
|
}))
|
|
|
|
// assert that @pnpm.e2e/pkg-with-good-optional@1.0.0 depends on is-positive@1.0.0
|
|
expect(project.requireModule('@pnpm.e2e/pkg-with-good-optional/package.json')).toMatchObject({
|
|
version: '1.0.0',
|
|
optionalDependencies: {
|
|
'is-positive': '1.0.0',
|
|
},
|
|
})
|
|
|
|
// yet because of the overrides, it installs @pnpm.e2e/pkg-with-good-optional@1.0.0 without is-positive@1.0.0
|
|
const lockfile = project.readLockfile()
|
|
expect(lockfile.snapshots).toHaveProperty(['@pnpm.e2e/pkg-with-good-optional@1.0.0'])
|
|
expect(lockfile.snapshots['@pnpm.e2e/pkg-with-good-optional@1.0.0']).not.toHaveProperty(['optionalDependencies', 'is-positive'])
|
|
expect(lockfile.snapshots).not.toHaveProperty(['is-positive@1.0.0'])
|
|
expect(lockfile.packages).toHaveProperty(['@pnpm.e2e/pkg-with-good-optional@1.0.0'])
|
|
expect(lockfile.packages).not.toHaveProperty(['is-positive@1.0.0'])
|
|
expect(
|
|
fs.existsSync('node_modules/.pnpm/@pnpm.e2e+pkg-with-good-optional@1.0.0/node_modules/is-positive')
|
|
).toBe(false)
|
|
const currentLockfile = project.readCurrentLockfile()
|
|
expect(lockfile.overrides).toStrictEqual(currentLockfile.overrides)
|
|
})
|