@pnpm/installing.deps-installer 1012.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +259 -0
  3. package/lib/api.d.ts +3 -0
  4. package/lib/api.js +4 -0
  5. package/lib/getPeerDependencyIssues.d.ts +5 -0
  6. package/lib/getPeerDependencyIssues.js +70 -0
  7. package/lib/index.d.ts +9 -0
  8. package/lib/index.js +5 -0
  9. package/lib/install/checkCompatibility/BreakingChangeError.d.ts +12 -0
  10. package/lib/install/checkCompatibility/BreakingChangeError.js +13 -0
  11. package/lib/install/checkCompatibility/CatalogVersionMismatchError.d.ts +9 -0
  12. package/lib/install/checkCompatibility/CatalogVersionMismatchError.js +11 -0
  13. package/lib/install/checkCompatibility/ErrorRelatedSources.d.ts +5 -0
  14. package/lib/install/checkCompatibility/ErrorRelatedSources.js +2 -0
  15. package/lib/install/checkCompatibility/ModulesBreakingChangeError.d.ts +9 -0
  16. package/lib/install/checkCompatibility/ModulesBreakingChangeError.js +15 -0
  17. package/lib/install/checkCompatibility/UnexpectedStoreError.d.ts +11 -0
  18. package/lib/install/checkCompatibility/UnexpectedStoreError.js +13 -0
  19. package/lib/install/checkCompatibility/UnexpectedVirtualStoreDirError.d.ts +11 -0
  20. package/lib/install/checkCompatibility/UnexpectedVirtualStoreDirError.js +13 -0
  21. package/lib/install/checkCompatibility/index.d.ts +6 -0
  22. package/lib/install/checkCompatibility/index.js +32 -0
  23. package/lib/install/checkCustomResolverForceResolve.d.ts +10 -0
  24. package/lib/install/checkCustomResolverForceResolve.js +47 -0
  25. package/lib/install/extendInstallOptions.d.ts +163 -0
  26. package/lib/install/extendInstallOptions.js +156 -0
  27. package/lib/install/index.d.ts +110 -0
  28. package/lib/install/index.js +1346 -0
  29. package/lib/install/link.d.ts +51 -0
  30. package/lib/install/link.js +382 -0
  31. package/lib/install/reportPeerDependencyIssues.d.ts +12 -0
  32. package/lib/install/reportPeerDependencyIssues.js +116 -0
  33. package/lib/install/validateModules.d.ts +26 -0
  34. package/lib/install/validateModules.js +135 -0
  35. package/lib/parseWantedDependencies.d.ts +17 -0
  36. package/lib/parseWantedDependencies.js +51 -0
  37. package/lib/pnpmPkgJson.d.ts +3 -0
  38. package/lib/pnpmPkgJson.js +14 -0
  39. package/lib/types.d.ts +2 -0
  40. package/lib/types.js +2 -0
  41. package/lib/uninstall/removeDeps.d.ts +5 -0
  42. package/lib/uninstall/removeDeps.js +36 -0
  43. package/package.json +168 -0
@@ -0,0 +1,51 @@
1
+ import { type DepsStateCache } from '@pnpm/deps.graph-hasher';
2
+ import type { DependenciesGraph, LinkedDependency } from '@pnpm/installing.deps-resolver';
3
+ import type { InstallationResultStats } from '@pnpm/installing.deps-restorer';
4
+ import type { IncludedDependencies } from '@pnpm/installing.modules-yaml';
5
+ import type { LockfileObject } from '@pnpm/lockfile.fs';
6
+ import type { StoreController } from '@pnpm/store.controller-types';
7
+ import type { AllowBuild, DepPath, HoistedDependencies, Registries } from '@pnpm/types';
8
+ import type { ImporterToUpdate } from './index.js';
9
+ export interface LinkPackagesOptions {
10
+ allowBuild?: AllowBuild;
11
+ currentLockfile: LockfileObject;
12
+ dedupeDirectDeps: boolean;
13
+ dependenciesByProjectId: Record<string, Map<string, DepPath>>;
14
+ disableRelinkLocalDirDeps?: boolean;
15
+ force: boolean;
16
+ depsStateCache: DepsStateCache;
17
+ enableGlobalVirtualStore: boolean;
18
+ extraNodePaths: string[];
19
+ hoistedDependencies: HoistedDependencies;
20
+ hoistedModulesDir: string;
21
+ hoistPattern?: string[];
22
+ ignoreScripts: boolean;
23
+ publicHoistPattern?: string[];
24
+ include: IncludedDependencies;
25
+ linkedDependenciesByProjectId: Record<string, LinkedDependency[]>;
26
+ lockfileDir: string;
27
+ makePartialCurrentLockfile: boolean;
28
+ outdatedDependencies: Record<string, string>;
29
+ pruneStore: boolean;
30
+ pruneVirtualStore: boolean;
31
+ registries: Registries;
32
+ rootModulesDir: string;
33
+ sideEffectsCacheRead: boolean;
34
+ symlink: boolean;
35
+ skipped: Set<DepPath>;
36
+ storeController: StoreController;
37
+ virtualStoreDir: string;
38
+ virtualStoreDirMaxLength: number;
39
+ wantedLockfile: LockfileObject;
40
+ wantedToBeSkippedPackageIds: Set<string>;
41
+ hoistWorkspacePackages?: boolean;
42
+ virtualStoreOnly: boolean;
43
+ }
44
+ export interface LinkPackagesResult {
45
+ currentLockfile: LockfileObject;
46
+ newDepPaths: DepPath[];
47
+ newHoistedDependencies: HoistedDependencies;
48
+ removedDepPaths: Set<string>;
49
+ stats: InstallationResultStats;
50
+ }
51
+ export declare function linkPackages(projects: ImporterToUpdate[], depGraph: DependenciesGraph, opts: LinkPackagesOptions): Promise<LinkPackagesResult>;
@@ -0,0 +1,382 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { progressLogger, stageLogger, statsLogger, } from '@pnpm/core-loggers';
4
+ import { calcDepState } from '@pnpm/deps.graph-hasher';
5
+ import { symlinkDependency } from '@pnpm/fs.symlink-dependency';
6
+ import { linkDirectDeps } from '@pnpm/installing.linking.direct-dep-linker';
7
+ import { hoist } from '@pnpm/installing.linking.hoist';
8
+ import { prune } from '@pnpm/installing.linking.modules-cleaner';
9
+ import { filterLockfileByImporters, } from '@pnpm/lockfile.filtering';
10
+ import { logger } from '@pnpm/logger';
11
+ import { symlinkAllModules } from '@pnpm/worker';
12
+ import pLimit from 'p-limit';
13
+ import { pathExists } from 'path-exists';
14
+ import { difference, equals, isEmpty, pick, pickBy, props } from 'ramda';
15
+ const brokenModulesLogger = logger('_broken_node_modules');
16
+ export async function linkPackages(projects, depGraph, opts) {
17
+ let depNodes = Object.values(depGraph).filter(({ depPath, id }) => {
18
+ if (((opts.wantedLockfile.packages?.[depPath]) != null) && !opts.wantedLockfile.packages[depPath].optional) {
19
+ opts.skipped.delete(depPath);
20
+ return true;
21
+ }
22
+ if (opts.wantedToBeSkippedPackageIds.has(id)) {
23
+ opts.skipped.add(depPath);
24
+ return false;
25
+ }
26
+ opts.skipped.delete(depPath);
27
+ return true;
28
+ });
29
+ if (!opts.include.dependencies) {
30
+ depNodes = depNodes.filter(({ dev, optional }) => dev || optional);
31
+ }
32
+ if (!opts.include.devDependencies) {
33
+ depNodes = depNodes.filter(({ optional, prod }) => prod || optional);
34
+ }
35
+ if (!opts.include.optionalDependencies) {
36
+ depNodes = depNodes.filter(({ optional }) => !optional);
37
+ }
38
+ depGraph = Object.fromEntries(depNodes.map((depNode) => [depNode.depPath, depNode]));
39
+ const removedDepPaths = await prune(projects, {
40
+ currentLockfile: opts.currentLockfile,
41
+ dedupeDirectDeps: opts.dedupeDirectDeps,
42
+ hoistedDependencies: opts.hoistedDependencies,
43
+ hoistedModulesDir: (opts.hoistPattern != null) ? opts.hoistedModulesDir : undefined,
44
+ include: opts.include,
45
+ lockfileDir: opts.lockfileDir,
46
+ pruneStore: opts.pruneStore,
47
+ pruneVirtualStore: opts.pruneVirtualStore,
48
+ publicHoistedModulesDir: (opts.publicHoistPattern != null) ? opts.rootModulesDir : undefined,
49
+ skipped: opts.skipped,
50
+ storeController: opts.storeController,
51
+ virtualStoreDir: opts.virtualStoreDir,
52
+ virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
53
+ wantedLockfile: opts.wantedLockfile,
54
+ });
55
+ stageLogger.debug({
56
+ prefix: opts.lockfileDir,
57
+ stage: 'importing_started',
58
+ });
59
+ const projectIds = projects.map(({ id }) => id);
60
+ const filterOpts = {
61
+ include: opts.include,
62
+ registries: opts.registries,
63
+ skipped: opts.skipped,
64
+ };
65
+ const newCurrentLockfile = filterLockfileByImporters(opts.wantedLockfile, projectIds, {
66
+ ...filterOpts,
67
+ failOnMissingDependencies: true,
68
+ skipped: new Set(),
69
+ });
70
+ const { newDepPaths, added } = await linkNewPackages(filterLockfileByImporters(opts.currentLockfile, projectIds, {
71
+ ...filterOpts,
72
+ failOnMissingDependencies: false,
73
+ }), newCurrentLockfile, depGraph, {
74
+ allowBuild: opts.allowBuild,
75
+ disableRelinkLocalDirDeps: opts.disableRelinkLocalDirDeps,
76
+ enableGlobalVirtualStore: opts.enableGlobalVirtualStore,
77
+ force: opts.force,
78
+ depsStateCache: opts.depsStateCache,
79
+ ignoreScripts: opts.ignoreScripts,
80
+ lockfileDir: opts.lockfileDir,
81
+ optional: opts.include.optionalDependencies,
82
+ sideEffectsCacheRead: opts.sideEffectsCacheRead,
83
+ symlink: opts.symlink,
84
+ skipped: opts.skipped,
85
+ storeController: opts.storeController,
86
+ virtualStoreDir: opts.virtualStoreDir,
87
+ });
88
+ stageLogger.debug({
89
+ prefix: opts.lockfileDir,
90
+ stage: 'importing_done',
91
+ });
92
+ let currentLockfile;
93
+ const allImportersIncluded = equals(projectIds.sort(), Object.keys(opts.wantedLockfile.importers).sort());
94
+ if (opts.makePartialCurrentLockfile ||
95
+ !allImportersIncluded) {
96
+ const packages = opts.currentLockfile.packages ?? {};
97
+ if (opts.wantedLockfile.packages != null) {
98
+ for (const depPath in opts.wantedLockfile.packages) { // eslint-disable-line:forin
99
+ if (depGraph[depPath]) {
100
+ packages[depPath] = opts.wantedLockfile.packages[depPath];
101
+ }
102
+ }
103
+ }
104
+ const projects = {
105
+ ...opts.currentLockfile.importers,
106
+ ...pick(projectIds, opts.wantedLockfile.importers),
107
+ };
108
+ currentLockfile = filterLockfileByImporters({
109
+ ...opts.wantedLockfile,
110
+ importers: projects,
111
+ packages,
112
+ }, Object.keys(projects), {
113
+ ...filterOpts,
114
+ failOnMissingDependencies: false,
115
+ skipped: new Set(),
116
+ });
117
+ }
118
+ else if (opts.include.dependencies &&
119
+ opts.include.devDependencies &&
120
+ opts.include.optionalDependencies &&
121
+ opts.skipped.size === 0) {
122
+ currentLockfile = opts.wantedLockfile;
123
+ }
124
+ else {
125
+ currentLockfile = newCurrentLockfile;
126
+ }
127
+ let newHoistedDependencies;
128
+ if (opts.virtualStoreOnly || (opts.hoistPattern == null && opts.publicHoistPattern == null)) {
129
+ newHoistedDependencies = {};
130
+ }
131
+ else if (newDepPaths.length > 0 || removedDepPaths.size > 0) {
132
+ newHoistedDependencies = {
133
+ ...opts.hoistedDependencies,
134
+ ...await hoist({
135
+ extraNodePath: opts.extraNodePaths,
136
+ graph: depGraph,
137
+ directDepsByImporterId: {
138
+ ...opts.dependenciesByProjectId,
139
+ '.': new Map(Array.from(opts.dependenciesByProjectId['.']?.entries() ?? []).filter(([alias]) => {
140
+ return newCurrentLockfile.importers['.'].specifiers[alias];
141
+ })),
142
+ },
143
+ importerIds: projectIds,
144
+ privateHoistedModulesDir: opts.hoistedModulesDir,
145
+ privateHoistPattern: opts.hoistPattern ?? [],
146
+ publicHoistedModulesDir: opts.rootModulesDir,
147
+ publicHoistPattern: opts.publicHoistPattern ?? [],
148
+ virtualStoreDir: opts.virtualStoreDir,
149
+ virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
150
+ hoistedWorkspacePackages: opts.hoistWorkspacePackages
151
+ ? projects.reduce((hoistedWorkspacePackages, project) => {
152
+ if (project.manifest.name && project.id !== '.') {
153
+ hoistedWorkspacePackages[project.id] = {
154
+ dir: project.rootDir,
155
+ name: project.manifest.name,
156
+ };
157
+ }
158
+ return hoistedWorkspacePackages;
159
+ }, {})
160
+ : undefined,
161
+ skipped: opts.skipped,
162
+ }),
163
+ };
164
+ }
165
+ else {
166
+ newHoistedDependencies = opts.hoistedDependencies;
167
+ }
168
+ let linkedToRoot = 0;
169
+ if (opts.symlink && !opts.virtualStoreOnly) {
170
+ const projectsToLink = Object.fromEntries(await Promise.all(projects.map(async ({ id, manifest, modulesDir, rootDir }) => {
171
+ const deps = opts.dependenciesByProjectId[id];
172
+ const importerFromLockfile = newCurrentLockfile.importers[id];
173
+ return [id, {
174
+ dir: rootDir,
175
+ modulesDir,
176
+ dependencies: await Promise.all([
177
+ ...Array.from(deps.entries())
178
+ .filter(([rootAlias]) => importerFromLockfile.specifiers[rootAlias])
179
+ .map(([rootAlias, depPath]) => ({ rootAlias, depGraphNode: depGraph[depPath] }))
180
+ .filter(({ depGraphNode }) => depGraphNode)
181
+ .map(async ({ rootAlias, depGraphNode }) => {
182
+ const isDev = Boolean(manifest.devDependencies?.[depGraphNode.name]);
183
+ const isOptional = Boolean(manifest.optionalDependencies?.[depGraphNode.name]);
184
+ return {
185
+ alias: rootAlias,
186
+ name: depGraphNode.name,
187
+ version: depGraphNode.version,
188
+ dir: depGraphNode.dir,
189
+ id: depGraphNode.id,
190
+ dependencyType: (isDev && 'dev' || isOptional && 'optional' || 'prod'),
191
+ latest: opts.outdatedDependencies[depGraphNode.id],
192
+ isExternalLink: false,
193
+ };
194
+ }),
195
+ ...opts.linkedDependenciesByProjectId[id].map(async (linkedDependency) => {
196
+ const dir = resolvePath(rootDir, linkedDependency.resolution.directory);
197
+ return {
198
+ alias: linkedDependency.alias,
199
+ name: linkedDependency.name,
200
+ version: linkedDependency.version,
201
+ dir,
202
+ id: linkedDependency.resolution.directory,
203
+ dependencyType: (linkedDependency.dev && 'dev' || linkedDependency.optional && 'optional' || 'prod'),
204
+ isExternalLink: true,
205
+ };
206
+ }),
207
+ ]),
208
+ }];
209
+ })));
210
+ linkedToRoot = await linkDirectDeps(projectsToLink, { dedupe: opts.dedupeDirectDeps });
211
+ }
212
+ return {
213
+ currentLockfile,
214
+ newDepPaths,
215
+ newHoistedDependencies,
216
+ removedDepPaths,
217
+ stats: {
218
+ added,
219
+ removed: removedDepPaths.size,
220
+ linkedToRoot,
221
+ },
222
+ };
223
+ }
224
+ const isAbsolutePath = /^\/|^[A-Z]:/i;
225
+ // This function is copied from @pnpm/resolving.local-resolver
226
+ function resolvePath(where, spec) {
227
+ if (isAbsolutePath.test(spec))
228
+ return spec;
229
+ return path.resolve(where, spec);
230
+ }
231
+ async function linkNewPackages(currentLockfile, wantedLockfile, depGraph, opts) {
232
+ const wantedRelDepPaths = difference(Object.keys(wantedLockfile.packages ?? {}), Array.from(opts.skipped));
233
+ let newDepPathsSet;
234
+ if (opts.force) {
235
+ newDepPathsSet = new Set(wantedRelDepPaths
236
+ // when installing a new package, not all the nodes are analyzed
237
+ // just skip the ones that are in the lockfile but were not analyzed
238
+ .filter((depPath) => depGraph[depPath]));
239
+ }
240
+ else {
241
+ newDepPathsSet = await selectNewFromWantedDeps(wantedRelDepPaths, currentLockfile, depGraph);
242
+ }
243
+ const added = newDepPathsSet.size;
244
+ statsLogger.debug({
245
+ added,
246
+ prefix: opts.lockfileDir,
247
+ });
248
+ const existingWithUpdatedDeps = [];
249
+ if (!opts.force && (currentLockfile.packages != null) && (wantedLockfile.packages != null)) {
250
+ // add subdependencies that have been updated
251
+ // TODO: no need to relink everything. Can be relinked only what was changed
252
+ for (const depPath of wantedRelDepPaths) {
253
+ if (currentLockfile.packages[depPath] &&
254
+ (!equals(currentLockfile.packages[depPath].dependencies, wantedLockfile.packages[depPath].dependencies) ||
255
+ !isEmpty(currentLockfile.packages[depPath].optionalDependencies ?? {}) ||
256
+ !isEmpty(wantedLockfile.packages[depPath].optionalDependencies ?? {}))) {
257
+ // TODO: come up with a test that triggers the usecase of depGraph[depPath] undefined
258
+ // see related issue: https://github.com/pnpm/pnpm/issues/870
259
+ if (depGraph[depPath] && !newDepPathsSet.has(depPath)) {
260
+ existingWithUpdatedDeps.push(depGraph[depPath]);
261
+ }
262
+ }
263
+ }
264
+ }
265
+ if (!newDepPathsSet.size && (existingWithUpdatedDeps.length === 0))
266
+ return { newDepPaths: [], added };
267
+ const newDepPaths = Array.from(newDepPathsSet);
268
+ const newPkgs = props(newDepPaths, depGraph);
269
+ await Promise.all(newPkgs.map(async (depNode) => fs.mkdir(depNode.modules, { recursive: true })));
270
+ await Promise.all([
271
+ !opts.symlink
272
+ ? Promise.resolve()
273
+ : linkAllModules([...newPkgs, ...existingWithUpdatedDeps], depGraph, {
274
+ lockfileDir: opts.lockfileDir,
275
+ optional: opts.optional,
276
+ }),
277
+ linkAllPkgs(opts.storeController, newPkgs, {
278
+ allowBuild: opts.allowBuild,
279
+ depGraph,
280
+ depsStateCache: opts.depsStateCache,
281
+ disableRelinkLocalDirDeps: opts.disableRelinkLocalDirDeps,
282
+ enableGlobalVirtualStore: opts.enableGlobalVirtualStore,
283
+ force: opts.force,
284
+ ignoreScripts: opts.ignoreScripts,
285
+ lockfileDir: opts.lockfileDir,
286
+ sideEffectsCacheRead: opts.sideEffectsCacheRead,
287
+ }),
288
+ ]);
289
+ return { newDepPaths, added };
290
+ }
291
+ async function selectNewFromWantedDeps(wantedRelDepPaths, currentLockfile, depGraph) {
292
+ const newDeps = new Set();
293
+ const prevDeps = currentLockfile.packages ?? {};
294
+ await Promise.all(wantedRelDepPaths.map(async (depPath) => {
295
+ const depNode = depGraph[depPath];
296
+ if (!depNode)
297
+ return;
298
+ const prevDep = prevDeps[depPath];
299
+ if (prevDep &&
300
+ // Local file should always be treated as a new dependency
301
+ // https://github.com/pnpm/pnpm/issues/5381
302
+ depNode.resolution.type !== 'directory' &&
303
+ depNode.resolution.integrity === prevDep.resolution.integrity) {
304
+ if (await pathExists(depNode.dir)) {
305
+ return;
306
+ }
307
+ brokenModulesLogger.debug({
308
+ missing: depNode.dir,
309
+ });
310
+ }
311
+ newDeps.add(depPath);
312
+ }));
313
+ return newDeps;
314
+ }
315
+ const limitLinking = pLimit(16);
316
+ async function linkAllPkgs(storeController, depNodes, opts) {
317
+ await Promise.all(depNodes.map(async (depNode) => {
318
+ const { files } = await depNode.fetching();
319
+ depNode.requiresBuild = files.requiresBuild;
320
+ let sideEffectsCacheKey;
321
+ if (opts.sideEffectsCacheRead && files.sideEffectsMaps && !isEmpty(files.sideEffectsMaps)) {
322
+ if (opts?.allowBuild?.(depNode.name, depNode.version) !== false) {
323
+ sideEffectsCacheKey = calcDepState(opts.depGraph, opts.depsStateCache, depNode.depPath, {
324
+ includeDepGraphHash: !opts.ignoreScripts && depNode.requiresBuild, // true when is built
325
+ patchFileHash: depNode.patch?.hash,
326
+ });
327
+ }
328
+ }
329
+ const { importMethod, isBuilt } = await storeController.importPackage(depNode.dir, {
330
+ disableRelinkLocalDirDeps: opts.disableRelinkLocalDirDeps,
331
+ filesResponse: files,
332
+ force: opts.force,
333
+ safeToSkip: opts.enableGlobalVirtualStore,
334
+ sideEffectsCacheKey,
335
+ requiresBuild: depNode.patch != null || depNode.requiresBuild,
336
+ });
337
+ if (importMethod) {
338
+ progressLogger.debug({
339
+ method: importMethod,
340
+ requester: opts.lockfileDir,
341
+ status: 'imported',
342
+ to: depNode.dir,
343
+ });
344
+ }
345
+ depNode.isBuilt = isBuilt;
346
+ const selfDep = depNode.children[depNode.name];
347
+ if (selfDep) {
348
+ const pkg = opts.depGraph[selfDep];
349
+ if (!pkg || !pkg.installable && pkg.optional)
350
+ return;
351
+ const targetModulesDir = path.join(depNode.modules, depNode.name, 'node_modules');
352
+ await limitLinking(async () => symlinkDependency(pkg.dir, targetModulesDir, depNode.name));
353
+ }
354
+ }));
355
+ }
356
+ async function linkAllModules(depNodes, depGraph, opts) {
357
+ await symlinkAllModules({
358
+ deps: depNodes.map((depNode) => {
359
+ const children = opts.optional
360
+ ? depNode.children
361
+ : pickBy((_, childAlias) => !depNode.optionalDependencies.has(childAlias), depNode.children);
362
+ const childrenPaths = {};
363
+ for (const [alias, childDepPath] of Object.entries(children ?? {})) {
364
+ if (childDepPath.startsWith('link:')) {
365
+ childrenPaths[alias] = path.resolve(opts.lockfileDir, childDepPath.slice(5));
366
+ }
367
+ else {
368
+ const pkg = depGraph[childDepPath];
369
+ if (!pkg || !pkg.installable && pkg.optional || alias === depNode.name)
370
+ continue;
371
+ childrenPaths[alias] = pkg.dir;
372
+ }
373
+ }
374
+ return {
375
+ children: childrenPaths,
376
+ modules: depNode.modules,
377
+ name: depNode.name,
378
+ };
379
+ }),
380
+ });
381
+ }
382
+ //# sourceMappingURL=link.js.map
@@ -0,0 +1,12 @@
1
+ import { PnpmError } from '@pnpm/error';
2
+ import type { PeerDependencyIssuesByProjects, PeerDependencyRules } from '@pnpm/types';
3
+ export declare function reportPeerDependencyIssues(peerDependencyIssuesByProjects: PeerDependencyIssuesByProjects, opts: {
4
+ lockfileDir: string;
5
+ rules?: PeerDependencyRules;
6
+ strictPeerDependencies: boolean;
7
+ }): void;
8
+ export declare function filterPeerDependencyIssues(peerDependencyIssuesByProjects: PeerDependencyIssuesByProjects, rules?: PeerDependencyRules): PeerDependencyIssuesByProjects;
9
+ export declare class PeerDependencyIssuesError extends PnpmError {
10
+ issuesByProjects: PeerDependencyIssuesByProjects;
11
+ constructor(issues: PeerDependencyIssuesByProjects);
12
+ }
@@ -0,0 +1,116 @@
1
+ import { createMatcher } from '@pnpm/config.matcher';
2
+ import { parseOverrides } from '@pnpm/config.parse-overrides';
3
+ import { peerDependencyIssuesLogger } from '@pnpm/core-loggers';
4
+ import { PnpmError } from '@pnpm/error';
5
+ import { isEmpty } from 'ramda';
6
+ import semver from 'semver';
7
+ export function reportPeerDependencyIssues(peerDependencyIssuesByProjects, opts) {
8
+ const newPeerDependencyIssuesByProjects = filterPeerDependencyIssues(peerDependencyIssuesByProjects, opts.rules);
9
+ if (Object.values(newPeerDependencyIssuesByProjects).every((peerIssuesOfProject) => isEmpty(peerIssuesOfProject.bad) && (isEmpty(peerIssuesOfProject.missing) ||
10
+ peerIssuesOfProject.conflicts.length === 0 && Object.keys(peerIssuesOfProject.intersections).length === 0)))
11
+ return;
12
+ if (opts.strictPeerDependencies) {
13
+ throw new PeerDependencyIssuesError(newPeerDependencyIssuesByProjects);
14
+ }
15
+ peerDependencyIssuesLogger.debug({
16
+ issuesByProjects: newPeerDependencyIssuesByProjects,
17
+ });
18
+ }
19
+ export function filterPeerDependencyIssues(peerDependencyIssuesByProjects, rules) {
20
+ if (!rules)
21
+ return peerDependencyIssuesByProjects;
22
+ const ignoreMissingPatterns = [...new Set(rules?.ignoreMissing ?? [])];
23
+ const ignoreMissingMatcher = createMatcher(ignoreMissingPatterns);
24
+ const allowAnyPatterns = [...new Set(rules?.allowAny ?? [])];
25
+ const allowAnyMatcher = createMatcher(allowAnyPatterns);
26
+ const { allowedVersionsMatchAll, allowedVersionsByParentPkgName } = parseAllowedVersions(rules?.allowedVersions ?? {});
27
+ const newPeerDependencyIssuesByProjects = {};
28
+ for (const [projectId, { bad, missing, conflicts, intersections }] of Object.entries(peerDependencyIssuesByProjects)) {
29
+ newPeerDependencyIssuesByProjects[projectId] = { bad: {}, missing: {}, conflicts, intersections };
30
+ for (const [peerName, issues] of Object.entries(missing)) {
31
+ if (ignoreMissingMatcher(peerName) || issues.every(({ optional }) => optional)) {
32
+ continue;
33
+ }
34
+ newPeerDependencyIssuesByProjects[projectId].missing[peerName] = issues;
35
+ }
36
+ for (const [peerName, issues] of Object.entries(bad)) {
37
+ if (allowAnyMatcher(peerName))
38
+ continue;
39
+ const filteredIssues = [];
40
+ for (const issue of issues) {
41
+ if (allowedVersionsMatchAll[peerName]?.some((range) => semver.satisfies(issue.foundVersion, range)))
42
+ continue;
43
+ const currentParentPkg = issue.parents.at(-1);
44
+ if (currentParentPkg && allowedVersionsByParentPkgName[peerName]?.[currentParentPkg.name]) {
45
+ const allowedVersionsByParent = {};
46
+ for (const { targetPkg, parentPkg, ranges } of allowedVersionsByParentPkgName[peerName][currentParentPkg.name]) {
47
+ if (!parentPkg.bareSpecifier || currentParentPkg.version &&
48
+ (isSubRange(parentPkg.bareSpecifier, currentParentPkg.version) || semver.satisfies(currentParentPkg.version, parentPkg.bareSpecifier))) {
49
+ allowedVersionsByParent[targetPkg.name] = ranges;
50
+ }
51
+ }
52
+ if (allowedVersionsByParent[peerName]?.some((range) => semver.satisfies(issue.foundVersion, range)))
53
+ continue;
54
+ }
55
+ filteredIssues.push(issue);
56
+ }
57
+ if (filteredIssues.length) {
58
+ newPeerDependencyIssuesByProjects[projectId].bad[peerName] = filteredIssues;
59
+ }
60
+ }
61
+ }
62
+ return newPeerDependencyIssuesByProjects;
63
+ }
64
+ function isSubRange(superRange, subRange) {
65
+ return !superRange ||
66
+ subRange === superRange ||
67
+ semver.validRange(subRange) != null &&
68
+ semver.validRange(superRange) != null &&
69
+ semver.subset(subRange, superRange);
70
+ }
71
+ function tryParseAllowedVersions(allowedVersions) {
72
+ try {
73
+ return parseOverrides(allowedVersions ?? {});
74
+ }
75
+ catch (err) {
76
+ throw new PnpmError('INVALID_ALLOWED_VERSION_SELECTOR', `${err.message} in pnpm.peerDependencyRules.allowedVersions`);
77
+ }
78
+ }
79
+ function parseAllowedVersions(allowedVersions) {
80
+ const overrides = tryParseAllowedVersions(allowedVersions);
81
+ const allowedVersionsMatchAll = {};
82
+ const allowedVersionsByParentPkgName = {};
83
+ for (const { parentPkg, targetPkg, newBareSpecifier } of overrides) {
84
+ const ranges = parseVersions(newBareSpecifier);
85
+ if (!parentPkg) {
86
+ allowedVersionsMatchAll[targetPkg.name] = ranges;
87
+ continue;
88
+ }
89
+ if (!allowedVersionsByParentPkgName[targetPkg.name]) {
90
+ allowedVersionsByParentPkgName[targetPkg.name] = {};
91
+ }
92
+ if (!allowedVersionsByParentPkgName[targetPkg.name][parentPkg.name]) {
93
+ allowedVersionsByParentPkgName[targetPkg.name][parentPkg.name] = [];
94
+ }
95
+ allowedVersionsByParentPkgName[targetPkg.name][parentPkg.name].push({
96
+ parentPkg,
97
+ targetPkg,
98
+ ranges,
99
+ });
100
+ }
101
+ return {
102
+ allowedVersionsMatchAll,
103
+ allowedVersionsByParentPkgName,
104
+ };
105
+ }
106
+ function parseVersions(versions) {
107
+ return versions.split('||').map(v => v.trim());
108
+ }
109
+ export class PeerDependencyIssuesError extends PnpmError {
110
+ issuesByProjects;
111
+ constructor(issues) {
112
+ super('PEER_DEP_ISSUES', 'Unmet peer dependencies');
113
+ this.issuesByProjects = issues;
114
+ }
115
+ }
116
+ //# sourceMappingURL=reportPeerDependencyIssues.js.map
@@ -0,0 +1,26 @@
1
+ import type { IncludedDependencies, Modules } from '@pnpm/installing.modules-yaml';
2
+ import { type ProjectRootDir, type Registries } from '@pnpm/types';
3
+ export declare function validateModules(modules: Modules, projects: Array<{
4
+ modulesDir: string;
5
+ id: string;
6
+ rootDir: ProjectRootDir;
7
+ }>, opts: {
8
+ currentHoistPattern?: string[];
9
+ currentPublicHoistPattern?: string[];
10
+ forceNewModules: boolean;
11
+ include?: IncludedDependencies;
12
+ lockfileDir: string;
13
+ modulesDir: string;
14
+ registries: Registries;
15
+ storeDir: string;
16
+ virtualStoreDir: string;
17
+ virtualStoreDirMaxLength: number;
18
+ confirmModulesPurge?: boolean;
19
+ hoistPattern?: string[] | undefined;
20
+ forceHoistPattern?: boolean;
21
+ publicHoistPattern?: string[] | undefined;
22
+ forcePublicHoistPattern?: boolean;
23
+ global?: boolean;
24
+ }): Promise<{
25
+ purged: boolean;
26
+ }>;