@pnpm/installing.deps-restorer 1006.0.0
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 +13 -0
- package/lib/extendProjectsWithTargetDirs.d.ts +8 -0
- package/lib/extendProjectsWithTargetDirs.js +21 -0
- package/lib/index.d.ts +109 -0
- package/lib/index.js +729 -0
- package/lib/linkHoistedModules.d.ts +14 -0
- package/lib/linkHoistedModules.js +106 -0
- package/lib/lockfileToHoistedDepGraph.d.ts +38 -0
- package/lib/lockfileToHoistedDepGraph.js +251 -0
- package/package.json +108 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,729 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { linkBins, linkBinsOfPackages } from '@pnpm/bins.linker';
|
|
4
|
+
import { buildModules } from '@pnpm/building.during-install';
|
|
5
|
+
import { createAllowBuildFunction } from '@pnpm/building.policy';
|
|
6
|
+
import { LAYOUT_VERSION, WANTED_LOCKFILE, } from '@pnpm/constants';
|
|
7
|
+
import { packageManifestLogger, progressLogger, stageLogger, statsLogger, summaryLogger, } from '@pnpm/core-loggers';
|
|
8
|
+
import { lockfileToDepGraph, } from '@pnpm/deps.graph-builder';
|
|
9
|
+
import { calcDepState } from '@pnpm/deps.graph-hasher';
|
|
10
|
+
import * as dp from '@pnpm/deps.path';
|
|
11
|
+
import { PnpmError } from '@pnpm/error';
|
|
12
|
+
import { makeNodeRequireOption, runLifecycleHooksConcurrently, } from '@pnpm/exec.lifecycle';
|
|
13
|
+
import { symlinkDependency } from '@pnpm/fs.symlink-dependency';
|
|
14
|
+
import { linkDirectDeps } from '@pnpm/installing.linking.direct-dep-linker';
|
|
15
|
+
import { hoist } from '@pnpm/installing.linking.hoist';
|
|
16
|
+
import { prune } from '@pnpm/installing.linking.modules-cleaner';
|
|
17
|
+
import { writeModulesManifest, } from '@pnpm/installing.modules-yaml';
|
|
18
|
+
import { filterLockfileByEngine, filterLockfileByImportersAndEngine, } from '@pnpm/lockfile.filtering';
|
|
19
|
+
import { getLockfileImporterId, readCurrentLockfile, readWantedLockfile, writeCurrentLockfile, writeLockfiles, } from '@pnpm/lockfile.fs';
|
|
20
|
+
import { writePnpFile } from '@pnpm/lockfile.to-pnp';
|
|
21
|
+
import { nameVerFromPkgSnapshot, } from '@pnpm/lockfile.utils';
|
|
22
|
+
import { logger, streamParser, } from '@pnpm/logger';
|
|
23
|
+
import { readPackageJsonFromDir } from '@pnpm/pkg-manifest.reader';
|
|
24
|
+
import { DEPENDENCIES_FIELDS, } from '@pnpm/types';
|
|
25
|
+
import { symlinkAllModules } from '@pnpm/worker';
|
|
26
|
+
import { readProjectManifestOnly, safeReadProjectManifestOnly } from '@pnpm/workspace.project-manifest-reader';
|
|
27
|
+
import pLimit from 'p-limit';
|
|
28
|
+
import { pathAbsolute } from 'path-absolute';
|
|
29
|
+
import { equals, isEmpty, omit, pick, pickBy, props, union } from 'ramda';
|
|
30
|
+
import { realpathMissing } from 'realpath-missing';
|
|
31
|
+
import { extendProjectsWithTargetDirs } from './extendProjectsWithTargetDirs.js';
|
|
32
|
+
import { linkHoistedModules } from './linkHoistedModules.js';
|
|
33
|
+
import { lockfileToHoistedDepGraph } from './lockfileToHoistedDepGraph.js';
|
|
34
|
+
export { extendProjectsWithTargetDirs } from './extendProjectsWithTargetDirs.js';
|
|
35
|
+
export async function headlessInstall(opts) {
|
|
36
|
+
const reporter = opts.reporter;
|
|
37
|
+
if ((reporter != null) && typeof reporter === 'function') {
|
|
38
|
+
streamParser.on('data', reporter);
|
|
39
|
+
}
|
|
40
|
+
const lockfileDir = opts.lockfileDir;
|
|
41
|
+
const wantedLockfile = opts.wantedLockfile ?? await readWantedLockfile(lockfileDir, {
|
|
42
|
+
ignoreIncompatible: false,
|
|
43
|
+
useGitBranchLockfile: opts.useGitBranchLockfile,
|
|
44
|
+
// mergeGitBranchLockfiles is intentionally not supported in headless
|
|
45
|
+
mergeGitBranchLockfiles: false,
|
|
46
|
+
});
|
|
47
|
+
if (wantedLockfile == null) {
|
|
48
|
+
throw new Error(`Headless installation requires a ${WANTED_LOCKFILE} file`);
|
|
49
|
+
}
|
|
50
|
+
const depsStateCache = {};
|
|
51
|
+
const relativeModulesDir = opts.modulesDir ?? 'node_modules';
|
|
52
|
+
const rootModulesDir = await realpathMissing(path.join(lockfileDir, relativeModulesDir));
|
|
53
|
+
const internalPnpmDir = path.join(rootModulesDir, '.pnpm');
|
|
54
|
+
const currentLockfile = opts.currentLockfile ?? await readCurrentLockfile(internalPnpmDir, { ignoreIncompatible: false });
|
|
55
|
+
const virtualStoreDir = pathAbsolute(opts.virtualStoreDir ?? path.join(relativeModulesDir, '.pnpm'), lockfileDir);
|
|
56
|
+
const hoistedModulesDir = path.join(opts.enableGlobalVirtualStore ? internalPnpmDir : virtualStoreDir, 'node_modules');
|
|
57
|
+
const publicHoistedModulesDir = rootModulesDir;
|
|
58
|
+
const selectedProjects = Object.values(pick(opts.selectedProjectDirs, opts.allProjects));
|
|
59
|
+
const scriptsOpts = {
|
|
60
|
+
optional: false,
|
|
61
|
+
extraBinPaths: opts.extraBinPaths,
|
|
62
|
+
extraNodePaths: opts.extraNodePaths,
|
|
63
|
+
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
|
64
|
+
extraEnv: opts.extraEnv,
|
|
65
|
+
rawConfig: opts.rawConfig,
|
|
66
|
+
resolveSymlinksInInjectedDirs: opts.resolveSymlinksInInjectedDirs,
|
|
67
|
+
scriptsPrependNodePath: opts.scriptsPrependNodePath,
|
|
68
|
+
scriptShell: opts.scriptShell,
|
|
69
|
+
shellEmulator: opts.shellEmulator,
|
|
70
|
+
stdio: opts.ownLifecycleHooksStdio ?? 'inherit',
|
|
71
|
+
storeController: opts.storeController,
|
|
72
|
+
unsafePerm: opts.unsafePerm || false,
|
|
73
|
+
};
|
|
74
|
+
if (opts.virtualStoreOnly && opts.enableModulesDir === false && !opts.enableGlobalVirtualStore) {
|
|
75
|
+
throw new PnpmError('CONFIG_CONFLICT_VIRTUAL_STORE_ONLY_WITH_NO_MODULES_DIR', 'Cannot use virtualStoreOnly when enableModulesDir is false (the standard virtual store requires node_modules/.pnpm)');
|
|
76
|
+
}
|
|
77
|
+
const skipPostImportLinking = opts.virtualStoreOnly === true;
|
|
78
|
+
const skipped = opts.skipped || new Set();
|
|
79
|
+
const filterOpts = {
|
|
80
|
+
include: opts.include,
|
|
81
|
+
registries: opts.registries,
|
|
82
|
+
skipped,
|
|
83
|
+
currentEngine: opts.currentEngine,
|
|
84
|
+
engineStrict: opts.engineStrict,
|
|
85
|
+
failOnMissingDependencies: true,
|
|
86
|
+
includeIncompatiblePackages: opts.force,
|
|
87
|
+
lockfileDir,
|
|
88
|
+
supportedArchitectures: opts.supportedArchitectures,
|
|
89
|
+
};
|
|
90
|
+
let removed = 0;
|
|
91
|
+
if (opts.nodeLinker !== 'hoisted') {
|
|
92
|
+
if (currentLockfile != null && !opts.ignorePackageManifest) {
|
|
93
|
+
const removedDepPaths = await prune(selectedProjects, {
|
|
94
|
+
currentLockfile,
|
|
95
|
+
dedupeDirectDeps: opts.dedupeDirectDeps,
|
|
96
|
+
dryRun: false,
|
|
97
|
+
hoistedDependencies: opts.hoistedDependencies,
|
|
98
|
+
hoistedModulesDir: (opts.hoistPattern == null) ? undefined : hoistedModulesDir,
|
|
99
|
+
include: opts.include,
|
|
100
|
+
lockfileDir,
|
|
101
|
+
pruneStore: opts.pruneStore,
|
|
102
|
+
pruneVirtualStore: opts.pruneVirtualStore,
|
|
103
|
+
publicHoistedModulesDir: (opts.publicHoistPattern == null) ? undefined : publicHoistedModulesDir,
|
|
104
|
+
skipped,
|
|
105
|
+
storeController: opts.storeController,
|
|
106
|
+
virtualStoreDir,
|
|
107
|
+
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
|
|
108
|
+
wantedLockfile: filterLockfileByEngine(wantedLockfile, filterOpts).lockfile,
|
|
109
|
+
});
|
|
110
|
+
removed = removedDepPaths.size;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
statsLogger.debug({
|
|
114
|
+
prefix: lockfileDir,
|
|
115
|
+
removed: 0,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
stageLogger.debug({
|
|
120
|
+
prefix: lockfileDir,
|
|
121
|
+
stage: 'importing_started',
|
|
122
|
+
});
|
|
123
|
+
const initialImporterIds = (opts.ignorePackageManifest === true || opts.nodeLinker === 'hoisted')
|
|
124
|
+
? Object.keys(wantedLockfile.importers)
|
|
125
|
+
: selectedProjects.map(({ id }) => id);
|
|
126
|
+
const { lockfile: filteredLockfile, selectedImporterIds: importerIds } = filterLockfileByImportersAndEngine(wantedLockfile, initialImporterIds, filterOpts);
|
|
127
|
+
if (opts.excludeLinksFromLockfile) {
|
|
128
|
+
for (const { id, manifest, rootDir } of selectedProjects) {
|
|
129
|
+
if (filteredLockfile.importers[id]) {
|
|
130
|
+
for (const depType of DEPENDENCIES_FIELDS) {
|
|
131
|
+
filteredLockfile.importers[id][depType] = {
|
|
132
|
+
...filteredLockfile.importers[id][depType],
|
|
133
|
+
...Object.entries(manifest[depType] ?? {})
|
|
134
|
+
.filter(([_, spec]) => spec.startsWith('link:'))
|
|
135
|
+
.reduce((acc, [depName, spec]) => {
|
|
136
|
+
const linkPath = spec.substring(5);
|
|
137
|
+
acc[depName] = path.isAbsolute(linkPath) ? `link:${path.relative(rootDir, spec.substring(5))}` : spec;
|
|
138
|
+
return acc;
|
|
139
|
+
}, {}),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Update selectedProjects to add missing projects. importerIds will have the updated ids, found from deeply linked workspace projects
|
|
146
|
+
const initialImporterIdSet = new Set(initialImporterIds);
|
|
147
|
+
const missingIds = importerIds.filter((importerId) => !initialImporterIdSet.has(importerId));
|
|
148
|
+
if (missingIds.length > 0) {
|
|
149
|
+
for (const project of Object.values(opts.allProjects)) {
|
|
150
|
+
if (missingIds.includes(project.id)) {
|
|
151
|
+
selectedProjects.push(project);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const allowBuild = createAllowBuildFunction(opts);
|
|
156
|
+
const lockfileToDepGraphOpts = {
|
|
157
|
+
...opts,
|
|
158
|
+
allowBuild,
|
|
159
|
+
importerIds,
|
|
160
|
+
lockfileDir,
|
|
161
|
+
skipped,
|
|
162
|
+
virtualStoreDir,
|
|
163
|
+
nodeVersion: opts.currentEngine.nodeVersion,
|
|
164
|
+
pnpmVersion: opts.currentEngine.pnpmVersion,
|
|
165
|
+
supportedArchitectures: opts.supportedArchitectures,
|
|
166
|
+
includeUnchangedDeps: (!equals(opts.currentHoistPattern ?? [], opts.hoistPattern ?? [])) ||
|
|
167
|
+
(!equals(opts.currentPublicHoistPattern ?? [], opts.publicHoistPattern ?? [])) ||
|
|
168
|
+
(opts.enableGlobalVirtualStore === true && !equals(opts.modulesFile?.allowBuilds ?? {}, opts.allowBuilds ?? {})),
|
|
169
|
+
};
|
|
170
|
+
const { directDependenciesByImporterId, graph, hierarchy, hoistedLocations, injectionTargetsByDepPath, prevGraph, symlinkedDirectDependenciesByImporterId, } = await (opts.nodeLinker === 'hoisted'
|
|
171
|
+
? lockfileToHoistedDepGraph(filteredLockfile, currentLockfile, lockfileToDepGraphOpts)
|
|
172
|
+
: lockfileToDepGraph(filteredLockfile, opts.force ? null : currentLockfile, lockfileToDepGraphOpts));
|
|
173
|
+
if (opts.enablePnp) {
|
|
174
|
+
const importerNames = Object.fromEntries(selectedProjects.map(({ manifest, id }) => [id, manifest.name ?? id]));
|
|
175
|
+
await writePnpFile(filteredLockfile, {
|
|
176
|
+
importerNames,
|
|
177
|
+
lockfileDir,
|
|
178
|
+
virtualStoreDir,
|
|
179
|
+
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
|
|
180
|
+
registries: opts.registries,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
const depNodes = Object.values(graph);
|
|
184
|
+
const added = depNodes.filter(({ fetching }) => fetching).length;
|
|
185
|
+
statsLogger.debug({
|
|
186
|
+
added,
|
|
187
|
+
prefix: lockfileDir,
|
|
188
|
+
});
|
|
189
|
+
function warn(message) {
|
|
190
|
+
logger.info({
|
|
191
|
+
message,
|
|
192
|
+
prefix: lockfileDir,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
let newHoistedDependencies;
|
|
196
|
+
let linkedToRoot = 0;
|
|
197
|
+
if (opts.nodeLinker === 'hoisted' && hierarchy && prevGraph) {
|
|
198
|
+
if (!skipPostImportLinking) {
|
|
199
|
+
await linkHoistedModules(opts.storeController, graph, prevGraph, hierarchy, {
|
|
200
|
+
allowBuild,
|
|
201
|
+
depsStateCache,
|
|
202
|
+
disableRelinkLocalDirDeps: opts.disableRelinkLocalDirDeps,
|
|
203
|
+
force: opts.force,
|
|
204
|
+
ignoreScripts: opts.ignoreScripts,
|
|
205
|
+
lockfileDir: opts.lockfileDir,
|
|
206
|
+
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
|
207
|
+
sideEffectsCacheRead: opts.sideEffectsCacheRead,
|
|
208
|
+
});
|
|
209
|
+
stageLogger.debug({
|
|
210
|
+
prefix: lockfileDir,
|
|
211
|
+
stage: 'importing_done',
|
|
212
|
+
});
|
|
213
|
+
linkedToRoot = await symlinkDirectDependencies({
|
|
214
|
+
directDependenciesByImporterId: symlinkedDirectDependenciesByImporterId,
|
|
215
|
+
dedupe: Boolean(opts.dedupeDirectDeps),
|
|
216
|
+
filteredLockfile,
|
|
217
|
+
lockfileDir,
|
|
218
|
+
projects: selectedProjects,
|
|
219
|
+
registries: opts.registries,
|
|
220
|
+
symlink: opts.symlink,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
else if (opts.enableModulesDir !== false || opts.enableGlobalVirtualStore) {
|
|
225
|
+
if (opts.enableModulesDir !== false) {
|
|
226
|
+
await Promise.all(depNodes.map(async (depNode) => fs.mkdir(depNode.modules, { recursive: true })));
|
|
227
|
+
}
|
|
228
|
+
await Promise.all([
|
|
229
|
+
opts.symlink === false || opts.enableModulesDir === false
|
|
230
|
+
? Promise.resolve()
|
|
231
|
+
: linkAllModules(depNodes, {
|
|
232
|
+
optional: opts.include.optionalDependencies,
|
|
233
|
+
}),
|
|
234
|
+
linkAllPkgs(opts.storeController, depNodes, {
|
|
235
|
+
allowBuild,
|
|
236
|
+
force: opts.force,
|
|
237
|
+
disableRelinkLocalDirDeps: opts.disableRelinkLocalDirDeps,
|
|
238
|
+
depGraph: graph,
|
|
239
|
+
depsStateCache,
|
|
240
|
+
enableGlobalVirtualStore: opts.enableGlobalVirtualStore,
|
|
241
|
+
ignoreScripts: opts.ignoreScripts,
|
|
242
|
+
lockfileDir: opts.lockfileDir,
|
|
243
|
+
sideEffectsCacheRead: opts.sideEffectsCacheRead,
|
|
244
|
+
storeDir: opts.storeDir,
|
|
245
|
+
}),
|
|
246
|
+
]);
|
|
247
|
+
stageLogger.debug({
|
|
248
|
+
prefix: lockfileDir,
|
|
249
|
+
stage: 'importing_done',
|
|
250
|
+
});
|
|
251
|
+
if (opts.ignorePackageManifest !== true && !skipPostImportLinking && (opts.hoistPattern != null || opts.publicHoistPattern != null)) {
|
|
252
|
+
newHoistedDependencies = {
|
|
253
|
+
...opts.hoistedDependencies,
|
|
254
|
+
...await hoist({
|
|
255
|
+
extraNodePath: opts.extraNodePaths,
|
|
256
|
+
graph,
|
|
257
|
+
directDepsByImporterId: Object.fromEntries(Object.entries(directDependenciesByImporterId).map(([projectId, deps]) => [
|
|
258
|
+
projectId,
|
|
259
|
+
new Map(Object.entries(deps)),
|
|
260
|
+
])),
|
|
261
|
+
importerIds,
|
|
262
|
+
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
|
263
|
+
privateHoistedModulesDir: hoistedModulesDir,
|
|
264
|
+
privateHoistPattern: opts.hoistPattern ?? [],
|
|
265
|
+
publicHoistedModulesDir,
|
|
266
|
+
publicHoistPattern: opts.publicHoistPattern ?? [],
|
|
267
|
+
virtualStoreDir,
|
|
268
|
+
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
|
|
269
|
+
hoistedWorkspacePackages: opts.hoistWorkspacePackages
|
|
270
|
+
? Object.values(opts.allProjects).reduce((hoistedWorkspacePackages, project) => {
|
|
271
|
+
if (project.manifest.name && project.id !== '.') {
|
|
272
|
+
hoistedWorkspacePackages[project.id] = {
|
|
273
|
+
dir: project.rootDir,
|
|
274
|
+
name: project.manifest.name,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
return hoistedWorkspacePackages;
|
|
278
|
+
}, {})
|
|
279
|
+
: undefined,
|
|
280
|
+
skipped: opts.skipped,
|
|
281
|
+
}),
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
newHoistedDependencies = {};
|
|
286
|
+
}
|
|
287
|
+
if (!skipPostImportLinking) {
|
|
288
|
+
await linkAllBins(graph, {
|
|
289
|
+
extraNodePaths: opts.extraNodePaths,
|
|
290
|
+
optional: opts.include.optionalDependencies,
|
|
291
|
+
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
|
292
|
+
warn,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
if ((currentLockfile != null) && !equals(importerIds.sort(), Object.keys(filteredLockfile.importers).sort())) {
|
|
296
|
+
Object.assign(filteredLockfile.packages, currentLockfile.packages);
|
|
297
|
+
}
|
|
298
|
+
/** Skip linking and due to no project manifest */
|
|
299
|
+
if (!opts.ignorePackageManifest && !skipPostImportLinking) {
|
|
300
|
+
linkedToRoot = await symlinkDirectDependencies({
|
|
301
|
+
dedupe: Boolean(opts.dedupeDirectDeps),
|
|
302
|
+
directDependenciesByImporterId,
|
|
303
|
+
filteredLockfile,
|
|
304
|
+
lockfileDir,
|
|
305
|
+
projects: selectedProjects,
|
|
306
|
+
registries: opts.registries,
|
|
307
|
+
symlink: opts.symlink,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (opts.ignoreScripts) {
|
|
312
|
+
for (const { id, manifest } of selectedProjects) {
|
|
313
|
+
if (opts.ignoreScripts && ((manifest?.scripts) != null) &&
|
|
314
|
+
(manifest.scripts.preinstall ?? manifest.scripts.prepublish ??
|
|
315
|
+
manifest.scripts.install ??
|
|
316
|
+
manifest.scripts.postinstall ??
|
|
317
|
+
manifest.scripts.prepare)) {
|
|
318
|
+
opts.pendingBuilds.push(id);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// we can use concat here because we always only append new packages, which are guaranteed to not be there by definition
|
|
322
|
+
opts.pendingBuilds = opts.pendingBuilds
|
|
323
|
+
.concat(depNodes
|
|
324
|
+
.filter(({ requiresBuild }) => requiresBuild)
|
|
325
|
+
.map(({ depPath }) => depPath));
|
|
326
|
+
}
|
|
327
|
+
let ignoredBuilds;
|
|
328
|
+
if ((!opts.ignoreScripts || Object.keys(opts.patchedDependencies ?? {}).length > 0) && opts.enableModulesDir !== false) {
|
|
329
|
+
const directNodes = new Set();
|
|
330
|
+
for (const id of union(importerIds, ['.'])) {
|
|
331
|
+
const directDependencies = directDependenciesByImporterId[id];
|
|
332
|
+
for (const alias in directDependencies) {
|
|
333
|
+
const loc = directDependencies[alias];
|
|
334
|
+
if (!graph[loc])
|
|
335
|
+
continue;
|
|
336
|
+
directNodes.add(loc);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
const extraBinPaths = [...opts.extraBinPaths ?? []];
|
|
340
|
+
if (opts.hoistPattern != null) {
|
|
341
|
+
extraBinPaths.unshift(path.join(hoistedModulesDir, '.bin'));
|
|
342
|
+
}
|
|
343
|
+
let extraEnv = opts.extraEnv;
|
|
344
|
+
if (opts.enablePnp) {
|
|
345
|
+
extraEnv = {
|
|
346
|
+
...extraEnv,
|
|
347
|
+
...makeNodeRequireOption(path.join(opts.lockfileDir, '.pnp.cjs')),
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
ignoredBuilds = (await buildModules(graph, Array.from(directNodes), {
|
|
351
|
+
allowBuild,
|
|
352
|
+
childConcurrency: opts.childConcurrency,
|
|
353
|
+
extraBinPaths,
|
|
354
|
+
extraEnv,
|
|
355
|
+
depsStateCache,
|
|
356
|
+
ignoreScripts: opts.ignoreScripts || opts.ignoreDepScripts,
|
|
357
|
+
hoistedLocations,
|
|
358
|
+
lockfileDir,
|
|
359
|
+
optional: opts.include.optionalDependencies,
|
|
360
|
+
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
|
361
|
+
rawConfig: opts.rawConfig,
|
|
362
|
+
rootModulesDir: virtualStoreDir,
|
|
363
|
+
scriptsPrependNodePath: opts.scriptsPrependNodePath,
|
|
364
|
+
scriptShell: opts.scriptShell,
|
|
365
|
+
shellEmulator: opts.shellEmulator,
|
|
366
|
+
sideEffectsCacheWrite: opts.sideEffectsCacheWrite,
|
|
367
|
+
storeController: opts.storeController,
|
|
368
|
+
unsafePerm: opts.unsafePerm,
|
|
369
|
+
userAgent: opts.userAgent,
|
|
370
|
+
enableGlobalVirtualStore: opts.enableGlobalVirtualStore,
|
|
371
|
+
})).ignoredBuilds;
|
|
372
|
+
if (opts.modulesFile?.ignoredBuilds?.size) {
|
|
373
|
+
ignoredBuilds ??= new Set();
|
|
374
|
+
for (const ignoredBuild of opts.modulesFile.ignoredBuilds.values()) {
|
|
375
|
+
if (filteredLockfile.packages?.[ignoredBuild]) {
|
|
376
|
+
ignoredBuilds.add(ignoredBuild);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
const projectsToBeBuilt = extendProjectsWithTargetDirs(selectedProjects, injectionTargetsByDepPath);
|
|
382
|
+
if (opts.enableModulesDir !== false) {
|
|
383
|
+
if (!skipPostImportLinking) {
|
|
384
|
+
const rootProjectDeps = !opts.dedupeDirectDeps ? {} : (directDependenciesByImporterId['.'] ?? {});
|
|
385
|
+
/** Skip linking and due to no project manifest */
|
|
386
|
+
if (!opts.ignorePackageManifest) {
|
|
387
|
+
await Promise.all(selectedProjects.map(async (project) => {
|
|
388
|
+
if (opts.nodeLinker === 'hoisted' || opts.publicHoistPattern?.length && path.relative(opts.lockfileDir, project.rootDir) === '') {
|
|
389
|
+
await linkBinsOfImporter(project, {
|
|
390
|
+
extraNodePaths: opts.extraNodePaths,
|
|
391
|
+
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
let directPkgDirs;
|
|
396
|
+
if (project.id === '.') {
|
|
397
|
+
directPkgDirs = Object.values(directDependenciesByImporterId[project.id]);
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
directPkgDirs = [];
|
|
401
|
+
for (const [alias, dir] of Object.entries(directDependenciesByImporterId[project.id])) {
|
|
402
|
+
if (rootProjectDeps[alias] !== dir) {
|
|
403
|
+
directPkgDirs.push(dir);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
await linkBinsOfPackages((await Promise.all(directPkgDirs.map(async (dir) => ({
|
|
408
|
+
location: dir,
|
|
409
|
+
manifest: await safeReadProjectManifestOnly(dir),
|
|
410
|
+
}))))
|
|
411
|
+
.filter(({ manifest }) => manifest != null), project.binsDir, {
|
|
412
|
+
extraNodePaths: opts.extraNodePaths,
|
|
413
|
+
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}));
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
const injectedDeps = {};
|
|
420
|
+
for (const project of projectsToBeBuilt) {
|
|
421
|
+
if (project.targetDirs.length > 0) {
|
|
422
|
+
injectedDeps[project.id] = project.targetDirs.map((targetDir) => path.relative(opts.lockfileDir, targetDir));
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
await writeModulesManifest(rootModulesDir, {
|
|
426
|
+
hoistedDependencies: newHoistedDependencies,
|
|
427
|
+
hoistPattern: opts.hoistPattern,
|
|
428
|
+
included: opts.include,
|
|
429
|
+
injectedDeps,
|
|
430
|
+
ignoredBuilds,
|
|
431
|
+
layoutVersion: LAYOUT_VERSION,
|
|
432
|
+
hoistedLocations,
|
|
433
|
+
nodeLinker: opts.nodeLinker,
|
|
434
|
+
packageManager: `${opts.packageManager.name}@${opts.packageManager.version}`,
|
|
435
|
+
pendingBuilds: opts.pendingBuilds,
|
|
436
|
+
publicHoistPattern: opts.publicHoistPattern,
|
|
437
|
+
prunedAt: opts.pruneVirtualStore === true || opts.prunedAt == null
|
|
438
|
+
? new Date().toUTCString()
|
|
439
|
+
: opts.prunedAt,
|
|
440
|
+
registries: opts.registries,
|
|
441
|
+
skipped: Array.from(skipped),
|
|
442
|
+
storeDir: opts.storeDir,
|
|
443
|
+
virtualStoreDir,
|
|
444
|
+
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
|
|
445
|
+
allowBuilds: opts.allowBuilds,
|
|
446
|
+
});
|
|
447
|
+
const currentLockfileDir = path.join(rootModulesDir, '.pnpm');
|
|
448
|
+
if (opts.useLockfile) {
|
|
449
|
+
// We need to write the wanted lockfile as well.
|
|
450
|
+
// Even though it will only be changed if the workspace will have new projects with no dependencies.
|
|
451
|
+
await writeLockfiles({
|
|
452
|
+
wantedLockfileDir: opts.lockfileDir,
|
|
453
|
+
currentLockfileDir,
|
|
454
|
+
wantedLockfile,
|
|
455
|
+
currentLockfile: filteredLockfile,
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
await writeCurrentLockfile(currentLockfileDir, filteredLockfile);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
// waiting till package requests are finished
|
|
463
|
+
await Promise.all(depNodes.map(async ({ fetching }) => {
|
|
464
|
+
try {
|
|
465
|
+
await fetching?.();
|
|
466
|
+
}
|
|
467
|
+
catch { }
|
|
468
|
+
}));
|
|
469
|
+
summaryLogger.debug({ prefix: lockfileDir });
|
|
470
|
+
if (!opts.ignoreScripts && !opts.ignorePackageManifest && !skipPostImportLinking) {
|
|
471
|
+
await runLifecycleHooksConcurrently(['preinstall', 'install', 'postinstall', 'preprepare', 'prepare', 'postprepare'], projectsToBeBuilt, opts.childConcurrency ?? 5, scriptsOpts);
|
|
472
|
+
}
|
|
473
|
+
if ((reporter != null) && typeof reporter === 'function') {
|
|
474
|
+
streamParser.removeListener('data', reporter);
|
|
475
|
+
}
|
|
476
|
+
return {
|
|
477
|
+
stats: {
|
|
478
|
+
added,
|
|
479
|
+
removed,
|
|
480
|
+
linkedToRoot,
|
|
481
|
+
},
|
|
482
|
+
ignoredBuilds,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
async function symlinkDirectDependencies({ filteredLockfile, dedupe, directDependenciesByImporterId, lockfileDir, projects, registries, symlink, }) {
|
|
486
|
+
for (const { rootDir, manifest } of projects) {
|
|
487
|
+
// Even though headless installation will never update the package.json
|
|
488
|
+
// this needs to be logged because otherwise install summary won't be printed
|
|
489
|
+
packageManifestLogger.debug({
|
|
490
|
+
prefix: rootDir,
|
|
491
|
+
updated: manifest,
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
if (symlink === false)
|
|
495
|
+
return 0;
|
|
496
|
+
const importerManifestsByImporterId = {};
|
|
497
|
+
for (const { id, manifest } of projects) {
|
|
498
|
+
importerManifestsByImporterId[id] = manifest;
|
|
499
|
+
}
|
|
500
|
+
const projectsToLink = Object.fromEntries(await Promise.all(projects.map(async ({ rootDir, id, modulesDir }) => ([id, {
|
|
501
|
+
dir: rootDir,
|
|
502
|
+
modulesDir,
|
|
503
|
+
dependencies: await getRootPackagesToLink(filteredLockfile, {
|
|
504
|
+
importerId: id,
|
|
505
|
+
importerModulesDir: modulesDir,
|
|
506
|
+
lockfileDir,
|
|
507
|
+
projectDir: rootDir,
|
|
508
|
+
importerManifestsByImporterId,
|
|
509
|
+
registries,
|
|
510
|
+
rootDependencies: directDependenciesByImporterId[id],
|
|
511
|
+
}),
|
|
512
|
+
}]))));
|
|
513
|
+
const rootProject = projectsToLink['.'];
|
|
514
|
+
if (rootProject && dedupe) {
|
|
515
|
+
const rootDeps = Object.fromEntries(rootProject.dependencies.map((dep) => [dep.alias, dep.dir]));
|
|
516
|
+
for (const project of Object.values(omit(['.'], projectsToLink))) {
|
|
517
|
+
project.dependencies = project.dependencies.filter((dep) => dep.dir !== rootDeps[dep.alias]);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return linkDirectDeps(projectsToLink, { dedupe: Boolean(dedupe) });
|
|
521
|
+
}
|
|
522
|
+
async function linkBinsOfImporter({ manifest, modulesDir, binsDir, rootDir }, { extraNodePaths, preferSymlinkedExecutables } = {}) {
|
|
523
|
+
const warn = (message) => {
|
|
524
|
+
logger.info({ message, prefix: rootDir });
|
|
525
|
+
};
|
|
526
|
+
return linkBins(modulesDir, binsDir, {
|
|
527
|
+
extraNodePaths,
|
|
528
|
+
allowExoticManifests: true,
|
|
529
|
+
preferSymlinkedExecutables,
|
|
530
|
+
projectManifest: manifest,
|
|
531
|
+
warn,
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
async function getRootPackagesToLink(lockfile, opts) {
|
|
535
|
+
const projectSnapshot = lockfile.importers[opts.importerId];
|
|
536
|
+
const allDeps = {
|
|
537
|
+
...projectSnapshot.devDependencies,
|
|
538
|
+
...projectSnapshot.dependencies,
|
|
539
|
+
...projectSnapshot.optionalDependencies,
|
|
540
|
+
};
|
|
541
|
+
return (await Promise.all(Object.entries(allDeps)
|
|
542
|
+
.map(async ([alias, ref]) => {
|
|
543
|
+
if (ref.startsWith('link:')) {
|
|
544
|
+
const isDev = Boolean(projectSnapshot.devDependencies?.[alias]);
|
|
545
|
+
const isOptional = Boolean(projectSnapshot.optionalDependencies?.[alias]);
|
|
546
|
+
ref = ref.slice(5);
|
|
547
|
+
const packageDir = path.isAbsolute(ref) ? ref : path.join(opts.projectDir, ref);
|
|
548
|
+
const linkedPackage = await (async () => {
|
|
549
|
+
const importerId = getLockfileImporterId(opts.lockfileDir, packageDir);
|
|
550
|
+
if (opts.importerManifestsByImporterId[importerId]) {
|
|
551
|
+
return opts.importerManifestsByImporterId[importerId];
|
|
552
|
+
}
|
|
553
|
+
try {
|
|
554
|
+
// TODO: cover this case with a test
|
|
555
|
+
return await readProjectManifestOnly(packageDir);
|
|
556
|
+
}
|
|
557
|
+
catch (err) { // eslint-disable-line
|
|
558
|
+
if (err['code'] !== 'ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND')
|
|
559
|
+
throw err;
|
|
560
|
+
return { name: alias, version: '0.0.0' };
|
|
561
|
+
}
|
|
562
|
+
})();
|
|
563
|
+
return {
|
|
564
|
+
alias,
|
|
565
|
+
name: linkedPackage.name,
|
|
566
|
+
version: linkedPackage.version,
|
|
567
|
+
dir: packageDir,
|
|
568
|
+
id: ref,
|
|
569
|
+
isExternalLink: true,
|
|
570
|
+
dependencyType: isDev && 'dev' ||
|
|
571
|
+
isOptional && 'optional' ||
|
|
572
|
+
'prod',
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
const dir = opts.rootDependencies[alias];
|
|
576
|
+
// Skipping linked packages
|
|
577
|
+
if (!dir) {
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
const isDev = Boolean(projectSnapshot.devDependencies?.[alias]);
|
|
581
|
+
const isOptional = Boolean(projectSnapshot.optionalDependencies?.[alias]);
|
|
582
|
+
const depPath = dp.refToRelative(ref, alias);
|
|
583
|
+
if (depPath === null)
|
|
584
|
+
return;
|
|
585
|
+
const pkgSnapshot = lockfile.packages?.[depPath];
|
|
586
|
+
if (pkgSnapshot == null)
|
|
587
|
+
return; // this won't ever happen. Just making typescript happy
|
|
588
|
+
const pkgId = pkgSnapshot.id ?? dp.refToRelative(ref, alias) ?? undefined;
|
|
589
|
+
const pkgInfo = nameVerFromPkgSnapshot(depPath, pkgSnapshot);
|
|
590
|
+
return {
|
|
591
|
+
alias,
|
|
592
|
+
isExternalLink: false,
|
|
593
|
+
name: pkgInfo.name,
|
|
594
|
+
version: pkgInfo.version,
|
|
595
|
+
dependencyType: isDev && 'dev' || isOptional && 'optional' || 'prod',
|
|
596
|
+
dir,
|
|
597
|
+
id: pkgId,
|
|
598
|
+
};
|
|
599
|
+
})))
|
|
600
|
+
.filter(Boolean);
|
|
601
|
+
}
|
|
602
|
+
const limitLinking = pLimit(16);
|
|
603
|
+
async function linkAllPkgs(storeController, depNodes, opts) {
|
|
604
|
+
// Create a marker source file that will be added to filesMap for GVS packages
|
|
605
|
+
// that need building. The importer treats it as just another file, so it's
|
|
606
|
+
// atomically included in the staged directory and renamed with the package.
|
|
607
|
+
let needsBuildMarkerSrc;
|
|
608
|
+
if (opts.enableGlobalVirtualStore) {
|
|
609
|
+
needsBuildMarkerSrc = path.join(opts.storeDir, '.pnpm-needs-build-marker');
|
|
610
|
+
await fs.writeFile(needsBuildMarkerSrc, '');
|
|
611
|
+
}
|
|
612
|
+
await Promise.all(depNodes.map(async (depNode) => {
|
|
613
|
+
if (!depNode.fetching)
|
|
614
|
+
return;
|
|
615
|
+
let filesResponse;
|
|
616
|
+
try {
|
|
617
|
+
filesResponse = (await depNode.fetching()).files;
|
|
618
|
+
}
|
|
619
|
+
catch (err) { // eslint-disable-line
|
|
620
|
+
if (depNode.optional)
|
|
621
|
+
return;
|
|
622
|
+
throw err;
|
|
623
|
+
}
|
|
624
|
+
depNode.requiresBuild = filesResponse.requiresBuild;
|
|
625
|
+
let sideEffectsCacheKey;
|
|
626
|
+
if (opts.sideEffectsCacheRead && filesResponse.sideEffectsMaps && !isEmpty(filesResponse.sideEffectsMaps)) {
|
|
627
|
+
if (opts?.allowBuild?.(depNode.name, depNode.version) !== false) {
|
|
628
|
+
sideEffectsCacheKey = calcDepState(opts.depGraph, opts.depsStateCache, depNode.dir, {
|
|
629
|
+
includeDepGraphHash: !opts.ignoreScripts && depNode.requiresBuild, // true when is built
|
|
630
|
+
patchFileHash: depNode.patch?.hash,
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
// For GVS packages that need building, add a .pnpm-needs-build marker to the
|
|
635
|
+
// filesMap. The import pipeline treats it as a normal file, so it gets
|
|
636
|
+
// written into the staging directory and atomically renamed with the rest
|
|
637
|
+
// of the package. On the next install, GVS fast paths detect the marker
|
|
638
|
+
// and force a re-fetch/re-import/re-build.
|
|
639
|
+
// Skip the marker when cached side effects will be applied (the package
|
|
640
|
+
// is already built and no build will run).
|
|
641
|
+
const hasCachedSideEffects = sideEffectsCacheKey != null &&
|
|
642
|
+
filesResponse.sideEffectsMaps?.has(sideEffectsCacheKey) === true;
|
|
643
|
+
const needsBuildMarker = needsBuildMarkerSrc != null &&
|
|
644
|
+
!hasCachedSideEffects &&
|
|
645
|
+
(depNode.requiresBuild || depNode.patch != null);
|
|
646
|
+
let effectiveFilesResponse = filesResponse;
|
|
647
|
+
if (needsBuildMarker) {
|
|
648
|
+
effectiveFilesResponse = {
|
|
649
|
+
...filesResponse,
|
|
650
|
+
filesMap: new Map([...filesResponse.filesMap, ['.pnpm-needs-build', needsBuildMarkerSrc]]),
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
const { importMethod, isBuilt } = await storeController.importPackage(depNode.dir, {
|
|
654
|
+
filesResponse: effectiveFilesResponse,
|
|
655
|
+
force: depNode.forceImportPackage ?? opts.force,
|
|
656
|
+
disableRelinkLocalDirDeps: opts.disableRelinkLocalDirDeps,
|
|
657
|
+
requiresBuild: depNode.patch != null || depNode.requiresBuild,
|
|
658
|
+
safeToSkip: opts.enableGlobalVirtualStore,
|
|
659
|
+
sideEffectsCacheKey,
|
|
660
|
+
});
|
|
661
|
+
if (importMethod) {
|
|
662
|
+
progressLogger.debug({
|
|
663
|
+
method: importMethod,
|
|
664
|
+
requester: opts.lockfileDir,
|
|
665
|
+
status: 'imported',
|
|
666
|
+
to: depNode.dir,
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
depNode.isBuilt = isBuilt;
|
|
670
|
+
const selfDep = depNode.children[depNode.name];
|
|
671
|
+
if (selfDep) {
|
|
672
|
+
const pkg = opts.depGraph[selfDep];
|
|
673
|
+
if (!pkg)
|
|
674
|
+
return;
|
|
675
|
+
const targetModulesDir = path.join(depNode.modules, depNode.name, 'node_modules');
|
|
676
|
+
await limitLinking(async () => symlinkDependency(pkg.dir, targetModulesDir, depNode.name));
|
|
677
|
+
}
|
|
678
|
+
}));
|
|
679
|
+
}
|
|
680
|
+
async function linkAllBins(depGraph, opts) {
|
|
681
|
+
await Promise.all(Object.values(depGraph)
|
|
682
|
+
.map(async (depNode) => limitLinking(async () => {
|
|
683
|
+
const childrenToLink = opts.optional
|
|
684
|
+
? depNode.children
|
|
685
|
+
: pickBy((_, childAlias) => !depNode.optionalDependencies.has(childAlias), depNode.children);
|
|
686
|
+
const binPath = path.join(depNode.dir, 'node_modules/.bin');
|
|
687
|
+
const pkgSnapshots = props(Object.values(childrenToLink), depGraph);
|
|
688
|
+
if (pkgSnapshots.includes(undefined)) { // eslint-disable-line
|
|
689
|
+
await linkBins(depNode.modules, binPath, {
|
|
690
|
+
extraNodePaths: opts.extraNodePaths,
|
|
691
|
+
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
|
692
|
+
warn: opts.warn,
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
const pkgs = await Promise.all(pkgSnapshots
|
|
697
|
+
.filter(({ hasBin }) => hasBin)
|
|
698
|
+
.map(async ({ dir }) => ({
|
|
699
|
+
location: dir,
|
|
700
|
+
manifest: await readPackageJsonFromDir(dir),
|
|
701
|
+
})));
|
|
702
|
+
await linkBinsOfPackages(pkgs, binPath, {
|
|
703
|
+
extraNodePaths: opts.extraNodePaths,
|
|
704
|
+
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
// link also the bundled dependencies` bins
|
|
708
|
+
if (depNode.hasBundledDependencies) {
|
|
709
|
+
const bundledModules = path.join(depNode.dir, 'node_modules');
|
|
710
|
+
await linkBins(bundledModules, binPath, {
|
|
711
|
+
extraNodePaths: opts.extraNodePaths,
|
|
712
|
+
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
|
713
|
+
warn: opts.warn,
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
})));
|
|
717
|
+
}
|
|
718
|
+
async function linkAllModules(depNodes, opts) {
|
|
719
|
+
await symlinkAllModules({
|
|
720
|
+
deps: depNodes.map((depNode) => ({
|
|
721
|
+
children: opts.optional
|
|
722
|
+
? depNode.children
|
|
723
|
+
: pickBy((_, childAlias) => !depNode.optionalDependencies.has(childAlias), depNode.children),
|
|
724
|
+
modules: depNode.modules,
|
|
725
|
+
name: depNode.name,
|
|
726
|
+
})),
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
//# sourceMappingURL=index.js.map
|