@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.
- package/LICENSE +22 -0
- package/README.md +259 -0
- package/lib/api.d.ts +3 -0
- package/lib/api.js +4 -0
- package/lib/getPeerDependencyIssues.d.ts +5 -0
- package/lib/getPeerDependencyIssues.js +70 -0
- package/lib/index.d.ts +9 -0
- package/lib/index.js +5 -0
- package/lib/install/checkCompatibility/BreakingChangeError.d.ts +12 -0
- package/lib/install/checkCompatibility/BreakingChangeError.js +13 -0
- package/lib/install/checkCompatibility/CatalogVersionMismatchError.d.ts +9 -0
- package/lib/install/checkCompatibility/CatalogVersionMismatchError.js +11 -0
- package/lib/install/checkCompatibility/ErrorRelatedSources.d.ts +5 -0
- package/lib/install/checkCompatibility/ErrorRelatedSources.js +2 -0
- package/lib/install/checkCompatibility/ModulesBreakingChangeError.d.ts +9 -0
- package/lib/install/checkCompatibility/ModulesBreakingChangeError.js +15 -0
- package/lib/install/checkCompatibility/UnexpectedStoreError.d.ts +11 -0
- package/lib/install/checkCompatibility/UnexpectedStoreError.js +13 -0
- package/lib/install/checkCompatibility/UnexpectedVirtualStoreDirError.d.ts +11 -0
- package/lib/install/checkCompatibility/UnexpectedVirtualStoreDirError.js +13 -0
- package/lib/install/checkCompatibility/index.d.ts +6 -0
- package/lib/install/checkCompatibility/index.js +32 -0
- package/lib/install/checkCustomResolverForceResolve.d.ts +10 -0
- package/lib/install/checkCustomResolverForceResolve.js +47 -0
- package/lib/install/extendInstallOptions.d.ts +163 -0
- package/lib/install/extendInstallOptions.js +156 -0
- package/lib/install/index.d.ts +110 -0
- package/lib/install/index.js +1346 -0
- package/lib/install/link.d.ts +51 -0
- package/lib/install/link.js +382 -0
- package/lib/install/reportPeerDependencyIssues.d.ts +12 -0
- package/lib/install/reportPeerDependencyIssues.js +116 -0
- package/lib/install/validateModules.d.ts +26 -0
- package/lib/install/validateModules.js +135 -0
- package/lib/parseWantedDependencies.d.ts +17 -0
- package/lib/parseWantedDependencies.js +51 -0
- package/lib/pnpmPkgJson.d.ts +3 -0
- package/lib/pnpmPkgJson.js +14 -0
- package/lib/types.d.ts +2 -0
- package/lib/types.js +2 -0
- package/lib/uninstall/removeDeps.d.ts +5 -0
- package/lib/uninstall/removeDeps.js +36 -0
- 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
|
+
}>;
|