feat: add internal lockfileCheck option for lockfile only diff installs (#6404)

This commit is contained in:
Brandon Cheng
2023-04-16 14:58:03 -04:00
committed by GitHub
parent 49b15ac2e3
commit 6706a7d176
4 changed files with 43 additions and 2 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/plugin-commands-installation": patch
"@pnpm/core": patch
---
Add lockfileCheck option for lockfile only diff installs

View File

@@ -42,6 +42,7 @@ export interface StrictInstallOptions {
ignorePackageManifest: boolean
preferFrozenLockfile: boolean
saveWorkspaceProtocol: boolean | 'rolling'
lockfileCheck?: (prev: Lockfile, next: Lockfile) => void
lockfileIncludeTarballUrl: boolean
preferWorkspacePackages: boolean
preserveWorkspaceProtocol: boolean

View File

@@ -65,6 +65,7 @@ import pLimit from 'p-limit'
import pMapValues from 'p-map-values'
import flatten from 'ramda/src/flatten'
import mapValues from 'ramda/src/map'
import clone from 'ramda/src/clone'
import equals from 'ramda/src/equals'
import isEmpty from 'ramda/src/isEmpty'
import pickBy from 'ramda/src/pickBy'
@@ -801,6 +802,19 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
})
}
// The wanted lockfile is mutated during installation. To compare changes, a
// deep copy before installation is needed. This copy should represent the
// original wanted lockfile on disk as close as possible.
//
// This object can be quite large. Intentionally avoiding an expensive copy
// if no lockfileCheck option was passed in.
const originalLockfileForCheck = opts.lockfileCheck != null
? clone(ctx.wantedLockfile)
: null
// Aliasing for clarity in boolean expressions below.
const isInstallationOnlyForLockfileCheck = opts.lockfileCheck != null
ctx.wantedLockfile.importers = ctx.wantedLockfile.importers || {}
for (const { id } of projects) {
if (!ctx.wantedLockfile.importers[id]) {
@@ -965,7 +979,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
useGitBranchLockfile: opts.useGitBranchLockfile,
mergeGitBranchLockfiles: opts.mergeGitBranchLockfiles,
}
if (!opts.lockfileOnly && opts.enableModulesDir) {
if (!opts.lockfileOnly && !isInstallationOnlyForLockfileCheck && opts.enableModulesDir) {
const result = await linkPackages(
projects,
dependenciesGraph,
@@ -1199,7 +1213,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
}
} else {
await finishLockfileUpdates()
if (opts.useLockfile) {
if (opts.useLockfile && !isInstallationOnlyForLockfileCheck) {
await writeWantedLockfile(ctx.lockfileDir, newLockfile, lockfileOpts)
}
@@ -1223,6 +1237,14 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
strictPeerDependencies: opts.strictPeerDependencies,
})
// Similar to the sequencing for when the original wanted lockfile is
// copied, the new lockfile passed here should be as close as possible to
// what will eventually be written to disk. Ex: peers should be resolved,
// the afterAllResolved hook has been applied, etc.
if (originalLockfileForCheck != null) {
opts.lockfileCheck?.(originalLockfileForCheck, newLockfile)
}
return {
newLockfile,
projects: projects.map(({ id, manifest, rootDir }) => ({

View File

@@ -7,6 +7,7 @@ import { type Config } from '@pnpm/config'
import { PnpmError } from '@pnpm/error'
import { filterPkgsBySelectorObjects } from '@pnpm/filter-workspace-packages'
import { arrayOfWorkspacePackagesToMap, findWorkspacePackages } from '@pnpm/find-workspace-packages'
import { type Lockfile } from '@pnpm/lockfile-types'
import { rebuildProjects } from '@pnpm/plugin-commands-rebuild'
import { createOrConnectStoreController, type CreateStoreControllerOptions } from '@pnpm/store-connection-manager'
import { type IncludedDependencies, type Project, type ProjectsGraph } from '@pnpm/types'
@@ -88,6 +89,17 @@ export type InstallDepsOptions = Pick<Config,
include?: IncludedDependencies
includeDirect?: IncludedDependencies
latest?: boolean
/**
* If specified, the installation will only be performed for comparison of the
* wanted lockfile. The wanted lockfile will not be updated on disk and no
* modules will be linked.
*
* The given callback is passed the wanted lockfile before installation and
* after. This allows functions to reasonably determine whether the wanted
* lockfile will change on disk after installation. The lockfile arguments
* passed to this callback should not be mutated.
*/
lockfileCheck?: (prev: Lockfile, next: Lockfile) => void
update?: boolean
updateMatching?: (pkgName: string) => boolean
updatePackageManifest?: boolean