mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-30 20:16:38 -04:00
fix: up-to-date check on local tarball with peers (#9807)
* test: add allProjectsAreUpToDate test for local tarball with peers * fix: up-to-date check on local tarball with peers
This commit is contained in:
6
.changeset/bitter-monkeys-attack.md
Normal file
6
.changeset/bitter-monkeys-attack.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/lockfile.verification": patch
|
||||
pnpm: patch
|
||||
---
|
||||
|
||||
Fix a bug causing `pnpm install` to incorrectly assume the lockfile is up to date after changing a local tarball that has peers dependencies.
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getTarballIntegrity } from '@pnpm/crypto.hash'
|
||||
import { refToRelative } from '@pnpm/dependency-path'
|
||||
import * as dp from '@pnpm/dependency-path'
|
||||
import {
|
||||
type ProjectSnapshot,
|
||||
type PackageSnapshots,
|
||||
@@ -53,11 +53,34 @@ export async function localTarballDepsAreUpToDate (
|
||||
return pEvery(
|
||||
Object.entries(lockfileDeps),
|
||||
async ([depName, ref]) => {
|
||||
if (!refIsLocalTarball(ref)) {
|
||||
if (!ref.startsWith('file:')) {
|
||||
return true
|
||||
}
|
||||
|
||||
// The tarball ref can contain peers. Ex: file:bar.tgz(react@19.1.0)
|
||||
//
|
||||
// Trim out the peer suffix version to get a path to the local tarball.
|
||||
//
|
||||
// - file:bar.tgz → file:bar.tgz
|
||||
// - file:bar.tgz(react@19.1.0) → file:bar.tgz
|
||||
//
|
||||
const depPath = dp.refToRelative(ref, depName)
|
||||
if (depPath == null) {
|
||||
return true
|
||||
}
|
||||
const parsed = dp.parse(depPath)
|
||||
const tarballRefWithoutPeersSuffix = parsed.nonSemverVersion
|
||||
|
||||
// Tarball refs aren't "semver" versions. If the nonSemverVersion field
|
||||
// is empty, this isn't a depPath for a tarball.
|
||||
if (tarballRefWithoutPeersSuffix == null) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (!refIsLocalTarball(tarballRefWithoutPeersSuffix)) {
|
||||
return true
|
||||
}
|
||||
|
||||
const depPath = refToRelative(ref, depName)
|
||||
const packageSnapshot = depPath != null ? lockfilePackages?.[depPath] : null
|
||||
|
||||
// If there's no snapshot for this local tarball yet, the project is out
|
||||
@@ -67,7 +90,8 @@ export async function localTarballDepsAreUpToDate (
|
||||
return false
|
||||
}
|
||||
|
||||
const filePath = path.join(lockfileDir, ref.slice('file:'.length))
|
||||
const fileRelativePath = tarballRefWithoutPeersSuffix.slice('file:'.length)
|
||||
const filePath = path.join(lockfileDir, fileRelativePath)
|
||||
|
||||
const fileIntegrityPromise = fileIntegrityCache.get(filePath) ?? getTarballIntegrity(filePath)
|
||||
if (!fileIntegrityCache.has(filePath)) {
|
||||
|
||||
@@ -591,6 +591,112 @@ describe('local tgz file dependency', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// Regression tests for https://github.com/pnpm/pnpm/pull/9807.
|
||||
describe('local tgz file dependency with peer dependencies', () => {
|
||||
beforeEach(async () => {
|
||||
prepareEmpty()
|
||||
})
|
||||
|
||||
const projects = [
|
||||
{
|
||||
id: 'bar' as ProjectId,
|
||||
manifest: {
|
||||
dependencies: {
|
||||
'@pnpm.e2e/foo': '1.0.0',
|
||||
'local-tarball': 'file:local-tarball.tar',
|
||||
},
|
||||
},
|
||||
rootDir: 'bar' as ProjectRootDir,
|
||||
},
|
||||
]
|
||||
|
||||
const wantedLockfile: LockfileObject = {
|
||||
lockfileVersion: LOCKFILE_VERSION,
|
||||
importers: {
|
||||
['bar' as ProjectId]: {
|
||||
dependencies: {
|
||||
'@pnpm.e2e/foo': '1.0.0',
|
||||
'local-tarball': 'file:local-tarball.tar(@pnpm.e2e/foo@1.0.0)',
|
||||
},
|
||||
specifiers: {
|
||||
'@pnpm.e2e/foo': '1.0.0',
|
||||
'local-tarball': 'file:local-tarball.tar',
|
||||
},
|
||||
},
|
||||
},
|
||||
packages: {
|
||||
['@pnpm.e2e/foo@1.0.0' as DepPath]: {
|
||||
resolution: {
|
||||
integrity: 'sha512-/HITDx7DEbvGeznQ5aq9qK5rn7YlVGST+fW2cQ0QAoO7/kVn/QJkN7VYAB0nvRIFkFsaAMJZ61zB8pJo9Fonng==',
|
||||
},
|
||||
version: '1.0.0',
|
||||
},
|
||||
['local-tarball@file:local-tarball.tar(@pnpm.e2e/foo@1.0.0)' as DepPath]: {
|
||||
resolution: {
|
||||
integrity: 'sha512-dVXphRGPXHhIt6CKeest8Tkbva4FatStRw4PZbJ4zFszWppqAkZureR6mOF0mT/9Drr5wZ5y9tPaqcmsf/a5cw==',
|
||||
tarball: 'file:local-tarball.tar',
|
||||
},
|
||||
version: '1.0.0',
|
||||
dependencies: {
|
||||
'@pnpm.e2e/foo': '1.0.0',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const options = {
|
||||
autoInstallPeers: false,
|
||||
catalogs: {},
|
||||
excludeLinksFromLockfile: false,
|
||||
linkWorkspacePackages: true,
|
||||
wantedLockfile,
|
||||
workspacePackages,
|
||||
lockfileDir: process.cwd(),
|
||||
}
|
||||
|
||||
test('allProjectsAreUpToDate(): returns true if local file not changed', async () => {
|
||||
expect.hasAssertions()
|
||||
|
||||
const pack = tar.pack()
|
||||
pack.entry({ name: 'package.json', mtime: new Date('1970-01-01T00:00:00.000Z') }, JSON.stringify({
|
||||
name: 'local-tarball',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {
|
||||
'@pnpm.e2e/foo': '1.0.0',
|
||||
},
|
||||
}))
|
||||
pack.finalize()
|
||||
|
||||
await pipeline(pack, createWriteStream('./local-tarball.tar'))
|
||||
|
||||
// Make sure the test is set up correctly and the local-tarball.tar created
|
||||
// above has the expected integrity hash.
|
||||
await expect(getTarballIntegrity('./local-tarball.tar')).resolves.toEqual('sha512-dVXphRGPXHhIt6CKeest8Tkbva4FatStRw4PZbJ4zFszWppqAkZureR6mOF0mT/9Drr5wZ5y9tPaqcmsf/a5cw==')
|
||||
|
||||
const lockfileDir = process.cwd()
|
||||
expect(await allProjectsAreUpToDate(projects, { ...options, lockfileDir })).toBeTruthy()
|
||||
})
|
||||
|
||||
test('allProjectsAreUpToDate(): returns false if local file has changed', async () => {
|
||||
expect.hasAssertions()
|
||||
|
||||
const pack = tar.pack()
|
||||
pack.entry({ name: 'package.json', mtime: new Date('2000-01-01T00:00:00') }, JSON.stringify({
|
||||
name: 'local-tarball',
|
||||
// Incrementing the version from 1.0.0 to 2.0.0.
|
||||
version: '2.0.0',
|
||||
peerDependencies: {
|
||||
'@pnpm.e2e/foo': '1.0.0',
|
||||
},
|
||||
}))
|
||||
pack.finalize()
|
||||
await pipeline(pack, createWriteStream('./local-tarball.tar'))
|
||||
|
||||
const lockfileDir = process.cwd()
|
||||
expect(await allProjectsAreUpToDate(projects, { ...options, lockfileDir })).toBeFalsy()
|
||||
})
|
||||
})
|
||||
|
||||
test('allProjectsAreUpToDate(): returns true if workspace dependency\'s version type is tag', async () => {
|
||||
const projects = [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user