@@ -32,7 +32,7 @@ import {
3232 type PatchFile ,
3333} from '@pnpm/lockfile-file'
3434import { writePnpFile } from '@pnpm/lockfile-to-pnp'
35- import { extendProjectsWithTargetDirs } from '@pnpm/lockfile-utils'
35+ import { extendProjectsWithTargetDirs , satisfiesPackageManifest } from '@pnpm/lockfile-utils'
3636import { logger , globalInfo , streamParser } from '@pnpm/logger'
3737import { getAllDependenciesFromManifest } from '@pnpm/manifest-utils'
3838import { writeModulesManifest } from '@pnpm/modules-yaml'
@@ -251,7 +251,7 @@ export async function mutateModules (
251251 currentLockfile : ctx . currentLockfile ,
252252 wantedLockfile : ctx . wantedLockfile ,
253253 existsCurrentLockfile : ctx . existsCurrentLockfile ,
254- existsWantedLockfile : ctx . existsWantedLockfile ,
254+ existsNonEmptyWantedLockfile : ctx . existsNonEmptyWantedLockfile ,
255255 lockfileDir : ctx . lockfileDir ,
256256 storeDir : ctx . storeDir ,
257257 registries : ctx . registries ,
@@ -327,7 +327,7 @@ export async function mutateModules (
327327 } ) , patchedDependencies )
328328 : undefined
329329 const frozenLockfile = opts . frozenLockfile ||
330- opts . frozenLockfileIfExists && ctx . existsWantedLockfile
330+ opts . frozenLockfileIfExists && ctx . existsNonEmptyWantedLockfile
331331 let outdatedLockfileSettings = false
332332 if ( ! opts . ignorePackageManifest ) {
333333 const outdatedLockfileSettingName = getOutdatedLockfileSetting ( ctx . wantedLockfile , {
@@ -375,7 +375,7 @@ export async function mutateModules (
375375 ! needsFullResolution &&
376376 opts . preferFrozenLockfile &&
377377 ( ! opts . pruneLockfileImporters || Object . keys ( ctx . wantedLockfile . importers ) . length === Object . keys ( ctx . projects ) . length ) &&
378- ctx . existsWantedLockfile &&
378+ ctx . existsNonEmptyWantedLockfile &&
379379 (
380380 ctx . wantedLockfile . lockfileVersion === LOCKFILE_VERSION ||
381381 ctx . wantedLockfile . lockfileVersion === LOCKFILE_VERSION_V6
@@ -401,14 +401,39 @@ Note that in CI environments, this setting is enabled by default.`,
401401 }
402402 )
403403 }
404+ if ( ! opts . ignorePackageManifest ) {
405+ const _satisfiesPackageManifest = satisfiesPackageManifest . bind ( null , {
406+ autoInstallPeers : opts . autoInstallPeers ,
407+ excludeLinksFromLockfile : opts . excludeLinksFromLockfile ,
408+ } )
409+ for ( const { id, manifest, rootDir } of Object . values ( ctx . projects ) ) {
410+ const { satisfies, detailedReason } = _satisfiesPackageManifest ( ctx . wantedLockfile . importers [ id ] , manifest )
411+ if ( ! satisfies ) {
412+ if ( ! ctx . existsWantedLockfile ) {
413+ throw new PnpmError ( 'NO_LOCKFILE' ,
414+ `Cannot install with "frozen-lockfile" because ${ WANTED_LOCKFILE } is present` , {
415+ hint : 'Note that in CI environments this setting is true by default. If you still need to run install in such cases, use "pnpm install --no-frozen-lockfile"' ,
416+ } )
417+ }
418+ throw new PnpmError ( 'OUTDATED_LOCKFILE' ,
419+ `Cannot install with "frozen-lockfile" because ${ WANTED_LOCKFILE } is not up to date with ` +
420+ path . relative ( opts . lockfileDir , path . join ( rootDir , 'package.json' ) ) , {
421+ hint : `Note that in CI environments this setting is true by default. If you still need to run install in such cases, use "pnpm install --no-frozen-lockfile"
422+
423+ Failure reason:
424+ ${ detailedReason ?? '' } ` ,
425+ } )
426+ }
427+ }
428+ }
404429 if ( opts . lockfileOnly ) {
405430 // The lockfile will only be changed if the workspace will have new projects with no dependencies.
406431 await writeWantedLockfile ( ctx . lockfileDir , ctx . wantedLockfile )
407432 return {
408433 updatedProjects : projects . map ( ( mutatedProject ) => ctx . projects [ mutatedProject . rootDir ] ) ,
409434 }
410435 }
411- if ( ! ctx . existsWantedLockfile ) {
436+ if ( ! ctx . existsNonEmptyWantedLockfile ) {
412437 if ( Object . values ( ctx . projects ) . some ( ( project ) => pkgHasDependencies ( project . manifest ) ) ) {
413438 throw new Error ( `Headless installation requires a ${ WANTED_LOCKFILE } file` )
414439 }
@@ -463,7 +488,7 @@ Note that in CI environments, this setting is enabled by default.`,
463488 error . code !== 'ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY' &&
464489 ! BROKEN_LOCKFILE_INTEGRITY_ERRORS . has ( error . code )
465490 ) ||
466- ( ! ctx . existsWantedLockfile && ! ctx . existsCurrentLockfile )
491+ ( ! ctx . existsNonEmptyWantedLockfile && ! ctx . existsCurrentLockfile )
467492 ) throw error
468493 if ( BROKEN_LOCKFILE_INTEGRITY_ERRORS . has ( error . code ) ) {
469494 needsFullResolution = true
@@ -641,12 +666,12 @@ Note that in CI environments, this setting is enabled by default.`,
641666 // Unfortunately, the private lockfile may differ from the public one.
642667 // A user might run named installations on a project that has a pnpm-lock.yaml file before running a noop install
643668 const makePartialCurrentLockfile = ! installsOnly && (
644- ctx . existsWantedLockfile && ! ctx . existsCurrentLockfile ||
669+ ctx . existsNonEmptyWantedLockfile && ! ctx . existsCurrentLockfile ||
645670 ! ctx . currentLockfileIsUpToDate
646671 )
647672 const result = await installInContext ( projectsToInstall , ctx , {
648673 ...opts ,
649- currentLockfileIsUpToDate : ! ctx . existsWantedLockfile || ctx . currentLockfileIsUpToDate ,
674+ currentLockfileIsUpToDate : ! ctx . existsNonEmptyWantedLockfile || ctx . currentLockfileIsUpToDate ,
650675 makePartialCurrentLockfile,
651676 needsFullResolution,
652677 pruneVirtualStore,
@@ -1377,7 +1402,7 @@ const installInContext: InstallFunction = async (projects, ctx, opts) => {
13771402 } catch ( error : any ) { // eslint-disable-line
13781403 if (
13791404 ! BROKEN_LOCKFILE_INTEGRITY_ERRORS . has ( error . code ) ||
1380- ( ! ctx . existsWantedLockfile && ! ctx . existsCurrentLockfile )
1405+ ( ! ctx . existsNonEmptyWantedLockfile && ! ctx . existsCurrentLockfile )
13811406 ) throw error
13821407 opts . needsFullResolution = true
13831408 // Ideally, we would not update but currently there is no other way to redownload the integrity of the package
0 commit comments