mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-30 04:52:04 -04:00
feat(core): add a new function for finding missing peer dependencies (#4092)
This commit is contained in:
5
.changeset/fifty-wolves-bathe.md
Normal file
5
.changeset/fifty-wolves-bathe.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/resolve-dependencies": minor
|
||||
---
|
||||
|
||||
`resolveDependencies()` should return `peerDependenciesIssues`.
|
||||
5
.changeset/quick-apes-fetch.md
Normal file
5
.changeset/quick-apes-fetch.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/get-context": minor
|
||||
---
|
||||
|
||||
Export `GetContextOptions`.
|
||||
5
.changeset/tasty-kiwis-boil.md
Normal file
5
.changeset/tasty-kiwis-boil.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/core": minor
|
||||
---
|
||||
|
||||
New function added to the core API: `listMissingPeers()`.
|
||||
@@ -2,6 +2,7 @@ import link from './link'
|
||||
|
||||
export * from './install'
|
||||
export * from './link'
|
||||
export * from './listMissingPeers'
|
||||
export {
|
||||
link,
|
||||
}
|
||||
|
||||
@@ -511,7 +511,7 @@ export function createObjectChecksum (obj: Object) {
|
||||
return crypto.createHash('md5').update(s).digest('hex')
|
||||
}
|
||||
|
||||
function createReadPackageHook (
|
||||
export function createReadPackageHook (
|
||||
{
|
||||
lockfileDir,
|
||||
overrides,
|
||||
@@ -523,7 +523,7 @@ function createReadPackageHook (
|
||||
packageExtensions?: Record<string, PackageExtension>
|
||||
readPackageHook?: ReadPackageHook
|
||||
}
|
||||
) {
|
||||
): ReadPackageHook | undefined {
|
||||
const hooks: ReadPackageHook[] = []
|
||||
if (!isEmpty(overrides ?? {})) {
|
||||
hooks.push(createVersionsOverrider(overrides!, lockfileDir))
|
||||
|
||||
83
packages/core/src/listMissingPeers.ts
Normal file
83
packages/core/src/listMissingPeers.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import resolveDependencies from '@pnpm/resolve-dependencies'
|
||||
import getWantedDependencies from '@pnpm/resolve-dependencies/lib/getWantedDependencies'
|
||||
import getContext, { GetContextOptions, ProjectOptions } from '@pnpm/get-context'
|
||||
import { createReadPackageHook } from './install'
|
||||
import { getPreferredVersionsFromLockfile } from './install/getPreferredVersions'
|
||||
import { InstallOptions } from './install/extendInstallOptions'
|
||||
import { DEFAULT_REGISTRIES } from '@pnpm/normalize-registries'
|
||||
|
||||
export type ListMissingPeersOptions = Partial<GetContextOptions>
|
||||
& Pick<InstallOptions, 'hooks'
|
||||
| 'linkWorkspacePackagesDepth'
|
||||
| 'nodeVersion'
|
||||
| 'overrides'
|
||||
| 'packageExtensions'
|
||||
| 'preferWorkspacePackages'
|
||||
| 'saveWorkspaceProtocol'
|
||||
| 'storeController'
|
||||
| 'workspacePackages'
|
||||
>
|
||||
& Pick<GetContextOptions, 'storeDir'>
|
||||
|
||||
export async function listMissingPeers (
|
||||
projects: ProjectOptions[],
|
||||
opts: ListMissingPeersOptions
|
||||
) {
|
||||
const lockfileDir = opts.lockfileDir ?? process.cwd()
|
||||
const ctx = await getContext(projects, {
|
||||
force: false,
|
||||
forceSharedLockfile: false,
|
||||
extraBinPaths: [],
|
||||
lockfileDir,
|
||||
registries: DEFAULT_REGISTRIES,
|
||||
useLockfile: true,
|
||||
...opts,
|
||||
})
|
||||
const projectsToResolve = ctx.projects.map((project) => ({
|
||||
...project,
|
||||
updatePackageManifest: false,
|
||||
wantedDependencies: getWantedDependencies(project.manifest),
|
||||
}))
|
||||
const preferredVersions = ctx.wantedLockfile.packages ? getPreferredVersionsFromLockfile(ctx.wantedLockfile.packages) : undefined
|
||||
const {
|
||||
peerDependencyIssues,
|
||||
waitTillAllFetchingsFinish,
|
||||
} = await resolveDependencies(
|
||||
projectsToResolve,
|
||||
{
|
||||
currentLockfile: ctx.currentLockfile,
|
||||
defaultUpdateDepth: -1,
|
||||
dryRun: true,
|
||||
engineStrict: false,
|
||||
force: false,
|
||||
forceFullResolution: true,
|
||||
hooks: {
|
||||
readPackage: createReadPackageHook({
|
||||
lockfileDir,
|
||||
overrides: opts.overrides,
|
||||
packageExtensions: opts.packageExtensions,
|
||||
readPackageHook: opts.hooks?.readPackage,
|
||||
}),
|
||||
},
|
||||
linkWorkspacePackagesDepth: opts.linkWorkspacePackagesDepth ?? (opts.saveWorkspaceProtocol ? 0 : -1),
|
||||
lockfileDir,
|
||||
nodeVersion: opts.nodeVersion ?? process.version,
|
||||
pnpmVersion: '',
|
||||
preferWorkspacePackages: opts.preferWorkspacePackages,
|
||||
preferredVersions,
|
||||
preserveWorkspaceProtocol: false,
|
||||
registries: ctx.registries,
|
||||
saveWorkspaceProtocol: false, // this doesn't matter in our case. We won't write changes to package.json files
|
||||
storeController: opts.storeController,
|
||||
strictPeerDependencies: false,
|
||||
tag: 'latest',
|
||||
virtualStoreDir: ctx.virtualStoreDir,
|
||||
wantedLockfile: ctx.wantedLockfile,
|
||||
workspacePackages: opts.workspacePackages ?? {},
|
||||
}
|
||||
)
|
||||
|
||||
await waitTillAllFetchingsFinish()
|
||||
|
||||
return peerDependencyIssues
|
||||
}
|
||||
20
packages/core/test/listMissingPeers.test.ts
Normal file
20
packages/core/test/listMissingPeers.test.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { listMissingPeers } from '@pnpm/core'
|
||||
import { prepareEmpty } from '@pnpm/prepare'
|
||||
import { testDefaults } from './utils'
|
||||
|
||||
test('cannot resolve peer dependency for top-level dependency', async () => {
|
||||
prepareEmpty()
|
||||
|
||||
const peerDependencyIssues = await listMissingPeers([
|
||||
{
|
||||
manifest: {
|
||||
dependencies: {
|
||||
'ajv-keywords': '1.5.0',
|
||||
},
|
||||
},
|
||||
rootDir: process.cwd(),
|
||||
},
|
||||
], await testDefaults())
|
||||
|
||||
expect(peerDependencyIssues.length).toBe(1)
|
||||
})
|
||||
@@ -65,31 +65,33 @@ interface HookOptions {
|
||||
originalManifest?: ProjectManifest
|
||||
}
|
||||
|
||||
export interface GetContextOptions {
|
||||
force: boolean
|
||||
forceNewModules?: boolean
|
||||
forceSharedLockfile: boolean
|
||||
frozenLockfile?: boolean
|
||||
extraBinPaths: string[]
|
||||
lockfileDir: string
|
||||
modulesDir?: string
|
||||
hooks?: {
|
||||
readPackage?: ReadPackageHook
|
||||
}
|
||||
include?: IncludedDependencies
|
||||
registries: Registries
|
||||
storeDir: string
|
||||
useLockfile: boolean
|
||||
virtualStoreDir?: string
|
||||
|
||||
hoistPattern?: string[] | undefined
|
||||
forceHoistPattern?: boolean
|
||||
|
||||
publicHoistPattern?: string[] | undefined
|
||||
forcePublicHoistPattern?: boolean
|
||||
}
|
||||
|
||||
export default async function getContext<T> (
|
||||
projects: Array<ProjectOptions & HookOptions & T>,
|
||||
opts: {
|
||||
force: boolean
|
||||
forceNewModules?: boolean
|
||||
forceSharedLockfile: boolean
|
||||
frozenLockfile?: boolean
|
||||
extraBinPaths: string[]
|
||||
lockfileDir: string
|
||||
modulesDir?: string
|
||||
hooks?: {
|
||||
readPackage?: ReadPackageHook
|
||||
}
|
||||
include?: IncludedDependencies
|
||||
registries: Registries
|
||||
storeDir: string
|
||||
useLockfile: boolean
|
||||
virtualStoreDir?: string
|
||||
|
||||
hoistPattern?: string[] | undefined
|
||||
forceHoistPattern?: boolean
|
||||
|
||||
publicHoistPattern?: string[] | undefined
|
||||
forcePublicHoistPattern?: boolean
|
||||
}
|
||||
opts: GetContextOptions
|
||||
): Promise<PnpmContext<T>> {
|
||||
const modulesDir = opts.modulesDir ?? 'node_modules'
|
||||
let importersContext = await readProjectsContext(projects, { lockfileDir: opts.lockfileDir, modulesDir })
|
||||
|
||||
@@ -220,6 +220,7 @@ export default async function (
|
||||
outdatedDependencies,
|
||||
linkedDependenciesByProjectId,
|
||||
newLockfile,
|
||||
peerDependencyIssues,
|
||||
waitTillAllFetchingsFinish,
|
||||
wantedToBeSkippedPackageIds,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user