@yarnpkg/nm 3.0.2 → 4.0.0-rc.10

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.
@@ -0,0 +1,75 @@
1
+ import { Project, MessageName } from '@yarnpkg/core';
2
+ import { PortablePath, Filename } from '@yarnpkg/fslib';
3
+ import { PnpApi } from '@yarnpkg/pnp';
4
+ export declare enum LinkType {
5
+ HARD = "HARD",
6
+ SOFT = "SOFT"
7
+ }
8
+ export declare enum NodeModulesHoistingLimits {
9
+ WORKSPACES = "workspaces",
10
+ DEPENDENCIES = "dependencies",
11
+ NONE = "none"
12
+ }
13
+ export declare type NodeModulesBaseNode = {
14
+ dirList: Set<Filename>;
15
+ };
16
+ export declare type NodeModulesPackageNode = {
17
+ locator: LocatorKey;
18
+ target: PortablePath;
19
+ linkType: LinkType;
20
+ dirList?: undefined;
21
+ nodePath: string;
22
+ aliases: Array<string>;
23
+ };
24
+ /**
25
+ * Node modules tree - a map of every folder within the node_modules, along with their
26
+ * directory listing and whether they are a symlink and their location.
27
+ *
28
+ * Sample contents:
29
+ * /home/user/project/node_modules -> {dirList: ['foo', 'bar']}
30
+ * /home/user/project/node_modules/foo -> {target: '/home/user/project/.yarn/.cache/foo.zip/node_modules/foo', linkType: 'HARD'}
31
+ * /home/user/project/node_modules/bar -> {target: '/home/user/project/packages/bar', linkType: 'SOFT'}
32
+ */
33
+ export declare type NodeModulesTree = Map<PortablePath, NodeModulesBaseNode | NodeModulesPackageNode>;
34
+ export declare type NodeModulesTreeErrors = Array<{
35
+ messageName: MessageName;
36
+ text: string;
37
+ }>;
38
+ export interface NodeModulesTreeOptions {
39
+ pnpifyFs?: boolean;
40
+ validateExternalSoftLinks?: boolean;
41
+ hoistingLimitsByCwd?: Map<PortablePath, NodeModulesHoistingLimits>;
42
+ selfReferencesByCwd?: Map<PortablePath, Boolean>;
43
+ project?: Project;
44
+ }
45
+ /** Package locator key for usage inside maps */
46
+ declare type LocatorKey = string;
47
+ /**
48
+ * Returns path to archive, if package location is inside the archive.
49
+ *
50
+ * @param packagePath package location
51
+ *
52
+ * @returns path to archive is location is insde the archive or null otherwise
53
+ */
54
+ export declare const getArchivePath: (packagePath: PortablePath) => PortablePath | null;
55
+ /**
56
+ * Retrieve full package list and build hoisted `node_modules` directories
57
+ * representation in-memory.
58
+ *
59
+ * @param pnp PnP API
60
+ *
61
+ * @returns hoisted `node_modules` directories representation in-memory
62
+ */
63
+ export declare const buildNodeModulesTree: (pnp: PnpApi, options: NodeModulesTreeOptions) => {
64
+ tree: NodeModulesTree | null;
65
+ errors: NodeModulesTreeErrors;
66
+ preserveSymlinksRequired: boolean;
67
+ };
68
+ export declare type NodeModulesLocatorMap = Map<LocatorKey, {
69
+ target: PortablePath;
70
+ linkType: LinkType;
71
+ locations: Array<PortablePath>;
72
+ aliases: Array<string>;
73
+ }>;
74
+ export declare const buildLocatorMap: (nodeModulesTree: NodeModulesTree) => NodeModulesLocatorMap;
75
+ export {};
@@ -0,0 +1,600 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildLocatorMap = exports.buildNodeModulesTree = exports.getArchivePath = exports.NodeModulesHoistingLimits = exports.LinkType = void 0;
4
+ const core_1 = require("@yarnpkg/core");
5
+ const fslib_1 = require("@yarnpkg/fslib");
6
+ const fslib_2 = require("@yarnpkg/fslib");
7
+ const hoist_1 = require("./hoist");
8
+ // Babel doesn't support const enums, thats why we use non-const enum for LinkType in @yarnpkg/pnp
9
+ // But because of this TypeScript requires @yarnpkg/pnp during runtime
10
+ // To prevent this we redeclare LinkType enum here, to not depend on @yarnpkg/pnp during runtime
11
+ var LinkType;
12
+ (function (LinkType) {
13
+ LinkType["HARD"] = "HARD";
14
+ LinkType["SOFT"] = "SOFT";
15
+ })(LinkType = exports.LinkType || (exports.LinkType = {}));
16
+ var NodeModulesHoistingLimits;
17
+ (function (NodeModulesHoistingLimits) {
18
+ NodeModulesHoistingLimits["WORKSPACES"] = "workspaces";
19
+ NodeModulesHoistingLimits["DEPENDENCIES"] = "dependencies";
20
+ NodeModulesHoistingLimits["NONE"] = "none";
21
+ })(NodeModulesHoistingLimits = exports.NodeModulesHoistingLimits || (exports.NodeModulesHoistingLimits = {}));
22
+ /** node_modules path segment */
23
+ const NODE_MODULES = `node_modules`;
24
+ /**
25
+ * The workspace name suffix used internally by this implementation and appeneded to the name of workspace package.
26
+ * It is needed to create and distinguuish special nodes for workspaces
27
+ */
28
+ const WORKSPACE_NAME_SUFFIX = `$wsroot$`;
29
+ /**
30
+ * Returns path to archive, if package location is inside the archive.
31
+ *
32
+ * @param packagePath package location
33
+ *
34
+ * @returns path to archive is location is insde the archive or null otherwise
35
+ */
36
+ const getArchivePath = (packagePath) => packagePath.indexOf(`.zip/${NODE_MODULES}/`) >= 0 ?
37
+ fslib_1.npath.toPortablePath(packagePath.split(`/${NODE_MODULES}/`)[0]) :
38
+ null;
39
+ exports.getArchivePath = getArchivePath;
40
+ /**
41
+ * Retrieve full package list and build hoisted `node_modules` directories
42
+ * representation in-memory.
43
+ *
44
+ * @param pnp PnP API
45
+ *
46
+ * @returns hoisted `node_modules` directories representation in-memory
47
+ */
48
+ const buildNodeModulesTree = (pnp, options) => {
49
+ const { packageTree, hoistingLimits, errors, preserveSymlinksRequired } = buildPackageTree(pnp, options);
50
+ let tree = null;
51
+ if (errors.length === 0) {
52
+ const hoistedTree = (0, hoist_1.hoist)(packageTree, { hoistingLimits });
53
+ tree = populateNodeModulesTree(pnp, hoistedTree, options);
54
+ }
55
+ return { tree, errors, preserveSymlinksRequired };
56
+ };
57
+ exports.buildNodeModulesTree = buildNodeModulesTree;
58
+ const stringifyLocator = (locator) => `${locator.name}@${locator.reference}`;
59
+ const buildLocatorMap = (nodeModulesTree) => {
60
+ const map = new Map();
61
+ for (const [location, val] of nodeModulesTree.entries()) {
62
+ if (!val.dirList) {
63
+ let entry = map.get(val.locator);
64
+ if (!entry) {
65
+ entry = { target: val.target, linkType: val.linkType, locations: [], aliases: val.aliases };
66
+ map.set(val.locator, entry);
67
+ }
68
+ entry.locations.push(location);
69
+ }
70
+ }
71
+ for (const val of map.values()) {
72
+ // Sort locations by depth first and then alphabetically for determinism
73
+ val.locations = val.locations.sort((loc1, loc2) => {
74
+ const len1 = loc1.split(fslib_1.ppath.delimiter).length;
75
+ const len2 = loc2.split(fslib_1.ppath.delimiter).length;
76
+ if (loc2 === loc1) {
77
+ return 0;
78
+ }
79
+ else if (len1 !== len2) {
80
+ return len2 - len1;
81
+ }
82
+ else {
83
+ return loc2 > loc1 ? 1 : -1;
84
+ }
85
+ });
86
+ }
87
+ return map;
88
+ };
89
+ exports.buildLocatorMap = buildLocatorMap;
90
+ const areRealLocatorsEqual = (a, b) => {
91
+ const realA = core_1.structUtils.isVirtualLocator(a) ? core_1.structUtils.devirtualizeLocator(a) : a;
92
+ const realB = core_1.structUtils.isVirtualLocator(b) ? core_1.structUtils.devirtualizeLocator(b) : b;
93
+ return core_1.structUtils.areLocatorsEqual(realA, realB);
94
+ };
95
+ const isExternalSoftLink = (pkg, locator, pnp, topPkgPortableLocation) => {
96
+ if (pkg.linkType !== LinkType.SOFT)
97
+ return false;
98
+ const realSoftLinkPath = fslib_1.npath.toPortablePath(pnp.resolveVirtual && locator.reference && locator.reference.startsWith(`virtual:`) ? pnp.resolveVirtual(pkg.packageLocation) : pkg.packageLocation);
99
+ return fslib_1.ppath.contains(topPkgPortableLocation, realSoftLinkPath) === null;
100
+ };
101
+ /**
102
+ * Builds a map representing layout of nested workspaces and internal portals on the file system.
103
+ */
104
+ const buildWorkspaceMap = (pnp) => {
105
+ const topPkg = pnp.getPackageInformation(pnp.topLevel);
106
+ if (topPkg === null)
107
+ throw new Error(`Assertion failed: Expected the top-level package to have been registered`);
108
+ const topLocator = pnp.findPackageLocator(topPkg.packageLocation);
109
+ if (topLocator === null)
110
+ throw new Error(`Assertion failed: Expected the top-level package to have a physical locator`);
111
+ const topPkgPortableLocation = fslib_1.npath.toPortablePath(topPkg.packageLocation.slice(0, -1));
112
+ const workspaceMap = new Map();
113
+ const workspaceTree = { children: new Map() };
114
+ const pnpRoots = pnp.getDependencyTreeRoots();
115
+ // Workspace and internal portal locations to locators map
116
+ const workspaceLikeLocators = new Map();
117
+ const seen = new Set();
118
+ const visit = (locator, parentLocator) => {
119
+ const locatorKey = stringifyLocator(locator);
120
+ if (seen.has(locatorKey))
121
+ return;
122
+ seen.add(locatorKey);
123
+ const pkg = pnp.getPackageInformation(locator);
124
+ if (pkg) {
125
+ const parentLocatorKey = parentLocator ? stringifyLocator(parentLocator) : ``;
126
+ if (stringifyLocator(locator) !== parentLocatorKey && pkg.linkType === LinkType.SOFT && !isExternalSoftLink(pkg, locator, pnp, topPkgPortableLocation)) {
127
+ const location = getRealPackageLocation(pkg, locator, pnp);
128
+ const prevLocator = workspaceLikeLocators.get(location);
129
+ // Give workspaces a priority over portals and other protocols pointing to the same location
130
+ // The devDependencies are not installed for portals, but installed for workspaces
131
+ if (!prevLocator || locator.reference.startsWith(`workspace:`)) {
132
+ workspaceLikeLocators.set(location, locator);
133
+ }
134
+ }
135
+ for (const [name, referencish] of pkg.packageDependencies) {
136
+ if (referencish !== null) {
137
+ if (!pkg.packagePeers.has(name)) {
138
+ visit(pnp.getLocator(name, referencish), locator);
139
+ }
140
+ }
141
+ }
142
+ }
143
+ };
144
+ for (const locator of pnpRoots)
145
+ visit(locator, null);
146
+ const cwdSegments = topPkgPortableLocation.split(fslib_1.ppath.sep);
147
+ for (const locator of workspaceLikeLocators.values()) {
148
+ const pkg = pnp.getPackageInformation(locator);
149
+ const location = fslib_1.npath.toPortablePath(pkg.packageLocation.slice(0, -1));
150
+ const segments = location.split(fslib_1.ppath.sep).slice(cwdSegments.length);
151
+ let node = workspaceTree;
152
+ for (const segment of segments) {
153
+ let nextNode = node.children.get(segment);
154
+ if (!nextNode) {
155
+ nextNode = { children: new Map() };
156
+ node.children.set(segment, nextNode);
157
+ }
158
+ node = nextNode;
159
+ }
160
+ node.workspaceLocator = locator;
161
+ }
162
+ const addWorkspace = (node, parentWorkspaceLocator) => {
163
+ if (node.workspaceLocator) {
164
+ const parentLocatorKey = stringifyLocator(parentWorkspaceLocator);
165
+ let dependencies = workspaceMap.get(parentLocatorKey);
166
+ if (!dependencies) {
167
+ dependencies = new Set();
168
+ workspaceMap.set(parentLocatorKey, dependencies);
169
+ }
170
+ dependencies.add(node.workspaceLocator);
171
+ }
172
+ for (const child of node.children.values()) {
173
+ addWorkspace(child, node.workspaceLocator || parentWorkspaceLocator);
174
+ }
175
+ };
176
+ for (const child of workspaceTree.children.values())
177
+ addWorkspace(child, workspaceTree.workspaceLocator);
178
+ return workspaceMap;
179
+ };
180
+ /**
181
+ * Traverses PnP tree and produces input for the `RawHoister`
182
+ *
183
+ * @param pnp PnP API
184
+ *
185
+ * @returns package tree, packages info and locators
186
+ */
187
+ const buildPackageTree = (pnp, options) => {
188
+ const errors = [];
189
+ let preserveSymlinksRequired = false;
190
+ const hoistingLimits = new Map();
191
+ const workspaceMap = buildWorkspaceMap(pnp);
192
+ const topPkg = pnp.getPackageInformation(pnp.topLevel);
193
+ if (topPkg === null)
194
+ throw new Error(`Assertion failed: Expected the top-level package to have been registered`);
195
+ const topLocator = pnp.findPackageLocator(topPkg.packageLocation);
196
+ if (topLocator === null)
197
+ throw new Error(`Assertion failed: Expected the top-level package to have a physical locator`);
198
+ const topPkgPortableLocation = fslib_1.npath.toPortablePath(topPkg.packageLocation.slice(0, -1));
199
+ const packageTree = {
200
+ name: topLocator.name,
201
+ identName: topLocator.name,
202
+ reference: topLocator.reference,
203
+ peerNames: topPkg.packagePeers,
204
+ dependencies: new Set(),
205
+ dependencyKind: hoist_1.HoisterDependencyKind.WORKSPACE,
206
+ };
207
+ const nodes = new Map();
208
+ const getNodeKey = (name, locator) => `${stringifyLocator(locator)}:${name}`;
209
+ const addPackageToTree = (name, pkg, locator, parent, parentPkg, parentDependencies, parentRelativeCwd, isHoistBorder) => {
210
+ var _a, _b;
211
+ const nodeKey = getNodeKey(name, locator);
212
+ let node = nodes.get(nodeKey);
213
+ const isSeen = !!node;
214
+ if (!isSeen && locator.name === topLocator.name && locator.reference === topLocator.reference) {
215
+ node = packageTree;
216
+ nodes.set(nodeKey, packageTree);
217
+ }
218
+ const isExternalSoftLinkPackage = isExternalSoftLink(pkg, locator, pnp, topPkgPortableLocation);
219
+ if (!node) {
220
+ let dependencyKind = hoist_1.HoisterDependencyKind.REGULAR;
221
+ if (isExternalSoftLinkPackage)
222
+ dependencyKind = hoist_1.HoisterDependencyKind.EXTERNAL_SOFT_LINK;
223
+ else if (pkg.linkType === LinkType.SOFT && locator.name.endsWith(WORKSPACE_NAME_SUFFIX))
224
+ dependencyKind = hoist_1.HoisterDependencyKind.WORKSPACE;
225
+ node = {
226
+ name,
227
+ identName: locator.name,
228
+ reference: locator.reference,
229
+ dependencies: new Set(),
230
+ // View peer dependencies as regular dependencies for workspaces
231
+ // (meeting workspace peer dependency constraints is sometimes hard, sometimes impossible for the nm linker)
232
+ peerNames: dependencyKind === hoist_1.HoisterDependencyKind.WORKSPACE ? new Set() : pkg.packagePeers,
233
+ dependencyKind,
234
+ };
235
+ nodes.set(nodeKey, node);
236
+ }
237
+ let hoistPriority;
238
+ if (isExternalSoftLinkPackage)
239
+ // External soft link dependencies have the highest priority - we don't want to install inside them
240
+ hoistPriority = 2;
241
+ else if (parentPkg.linkType === LinkType.SOFT)
242
+ // Internal soft link dependencies should have priority over transitive dependencies - to maximize chances having only one top-level node_modules
243
+ hoistPriority = 1;
244
+ else
245
+ hoistPriority = 0;
246
+ node.hoistPriority = Math.max(node.hoistPriority || 0, hoistPriority);
247
+ if (isHoistBorder && !isExternalSoftLinkPackage) {
248
+ const parentLocatorKey = stringifyLocator({ name: parent.identName, reference: parent.reference });
249
+ const dependencyBorders = hoistingLimits.get(parentLocatorKey) || new Set();
250
+ hoistingLimits.set(parentLocatorKey, dependencyBorders);
251
+ dependencyBorders.add(node.name);
252
+ }
253
+ const allDependencies = new Map(pkg.packageDependencies);
254
+ if (options.project) {
255
+ const workspace = options.project.workspacesByCwd.get(fslib_1.npath.toPortablePath(pkg.packageLocation.slice(0, -1)));
256
+ if (workspace) {
257
+ const peerCandidates = new Set([
258
+ ...Array.from(workspace.manifest.peerDependencies.values(), x => core_1.structUtils.stringifyIdent(x)),
259
+ ...Array.from(workspace.manifest.peerDependenciesMeta.keys()),
260
+ ]);
261
+ for (const peerName of peerCandidates) {
262
+ if (!allDependencies.has(peerName)) {
263
+ allDependencies.set(peerName, parentDependencies.get(peerName) || null);
264
+ node.peerNames.add(peerName);
265
+ }
266
+ }
267
+ }
268
+ }
269
+ const locatorKey = stringifyLocator({ name: locator.name.replace(WORKSPACE_NAME_SUFFIX, ``), reference: locator.reference });
270
+ const innerWorkspaces = workspaceMap.get(locatorKey);
271
+ if (innerWorkspaces) {
272
+ for (const workspaceLocator of innerWorkspaces) {
273
+ allDependencies.set(`${workspaceLocator.name}${WORKSPACE_NAME_SUFFIX}`, workspaceLocator.reference);
274
+ }
275
+ }
276
+ if (pkg !== parentPkg || pkg.linkType !== LinkType.SOFT || (!isExternalSoftLinkPackage && (!options.selfReferencesByCwd || options.selfReferencesByCwd.get(parentRelativeCwd))))
277
+ parent.dependencies.add(node);
278
+ const isWorkspaceDependency = locator !== topLocator && pkg.linkType === LinkType.SOFT && !locator.name.endsWith(WORKSPACE_NAME_SUFFIX) && !isExternalSoftLinkPackage;
279
+ if (!isSeen && !isWorkspaceDependency) {
280
+ const siblingPortalDependencyMap = new Map();
281
+ for (const [depName, referencish] of allDependencies) {
282
+ if (referencish !== null) {
283
+ const depLocator = pnp.getLocator(depName, referencish);
284
+ const pkgLocator = pnp.getLocator(depName.replace(WORKSPACE_NAME_SUFFIX, ``), referencish);
285
+ const depPkg = pnp.getPackageInformation(pkgLocator);
286
+ if (depPkg === null)
287
+ throw new Error(`Assertion failed: Expected the package to have been registered`);
288
+ const isExternalSoftLinkDep = isExternalSoftLink(depPkg, depLocator, pnp, topPkgPortableLocation);
289
+ if (options.validateExternalSoftLinks && options.project && isExternalSoftLinkDep) {
290
+ if (depPkg.packageDependencies.size > 0)
291
+ preserveSymlinksRequired = true;
292
+ for (const [name, referencish] of depPkg.packageDependencies) {
293
+ if (referencish !== null) {
294
+ const portalDependencyLocator = core_1.structUtils.parseLocator(Array.isArray(referencish) ? `${referencish[0]}@${referencish[1]}` : `${name}@${referencish}`);
295
+ // Ignore self-references during portal hoistability check
296
+ if (stringifyLocator(portalDependencyLocator) !== stringifyLocator(depLocator)) {
297
+ const parentDependencyReferencish = allDependencies.get(name);
298
+ if (parentDependencyReferencish) {
299
+ const parentDependencyLocator = core_1.structUtils.parseLocator(Array.isArray(parentDependencyReferencish) ? `${parentDependencyReferencish[0]}@${parentDependencyReferencish[1]}` : `${name}@${parentDependencyReferencish}`);
300
+ if (!areRealLocatorsEqual(parentDependencyLocator, portalDependencyLocator)) {
301
+ errors.push({
302
+ messageName: core_1.MessageName.NM_CANT_INSTALL_EXTERNAL_SOFT_LINK,
303
+ text: `Cannot link ${core_1.structUtils.prettyIdent(options.project.configuration, core_1.structUtils.parseIdent(depLocator.name))} ` +
304
+ `into ${core_1.structUtils.prettyLocator(options.project.configuration, core_1.structUtils.parseLocator(`${locator.name}@${locator.reference}`))} ` +
305
+ `dependency ${core_1.structUtils.prettyLocator(options.project.configuration, portalDependencyLocator)} ` +
306
+ `conflicts with parent dependency ${core_1.structUtils.prettyLocator(options.project.configuration, parentDependencyLocator)}`,
307
+ });
308
+ }
309
+ }
310
+ else {
311
+ const siblingPortalDependency = siblingPortalDependencyMap.get(name);
312
+ if (siblingPortalDependency) {
313
+ const siblingReferncish = siblingPortalDependency.target;
314
+ const siblingPortalDependencyLocator = core_1.structUtils.parseLocator(Array.isArray(siblingReferncish) ? `${siblingReferncish[0]}@${siblingReferncish[1]}` : `${name}@${siblingReferncish}`);
315
+ if (!areRealLocatorsEqual(siblingPortalDependencyLocator, portalDependencyLocator)) {
316
+ errors.push({
317
+ messageName: core_1.MessageName.NM_CANT_INSTALL_EXTERNAL_SOFT_LINK,
318
+ text: `Cannot link ${core_1.structUtils.prettyIdent(options.project.configuration, core_1.structUtils.parseIdent(depLocator.name))} ` +
319
+ `into ${core_1.structUtils.prettyLocator(options.project.configuration, core_1.structUtils.parseLocator(`${locator.name}@${locator.reference}`))} ` +
320
+ `dependency ${core_1.structUtils.prettyLocator(options.project.configuration, portalDependencyLocator)} ` +
321
+ `conflicts with dependency ${core_1.structUtils.prettyLocator(options.project.configuration, siblingPortalDependencyLocator)} ` +
322
+ `from sibling portal ${core_1.structUtils.prettyIdent(options.project.configuration, core_1.structUtils.parseIdent(siblingPortalDependency.portal.name))}`,
323
+ });
324
+ }
325
+ }
326
+ else {
327
+ siblingPortalDependencyMap.set(name, { target: portalDependencyLocator.reference, portal: depLocator });
328
+ }
329
+ }
330
+ }
331
+ }
332
+ }
333
+ }
334
+ const parentHoistingLimits = (_a = options.hoistingLimitsByCwd) === null || _a === void 0 ? void 0 : _a.get(parentRelativeCwd);
335
+ const relativeDepCwd = isExternalSoftLinkDep ? parentRelativeCwd : fslib_1.ppath.relative(topPkgPortableLocation, fslib_1.npath.toPortablePath(depPkg.packageLocation)) || fslib_2.PortablePath.dot;
336
+ const depHoistingLimits = (_b = options.hoistingLimitsByCwd) === null || _b === void 0 ? void 0 : _b.get(relativeDepCwd);
337
+ const isHoistBorder = parentHoistingLimits === NodeModulesHoistingLimits.DEPENDENCIES
338
+ || depHoistingLimits === NodeModulesHoistingLimits.DEPENDENCIES
339
+ || depHoistingLimits === NodeModulesHoistingLimits.WORKSPACES;
340
+ addPackageToTree(depName, depPkg, depLocator, node, pkg, allDependencies, relativeDepCwd, isHoistBorder);
341
+ }
342
+ }
343
+ }
344
+ };
345
+ addPackageToTree(topLocator.name, topPkg, topLocator, packageTree, topPkg, topPkg.packageDependencies, fslib_2.PortablePath.dot, false);
346
+ return { packageTree, hoistingLimits, errors, preserveSymlinksRequired };
347
+ };
348
+ function getRealPackageLocation(pkg, locator, pnp) {
349
+ const realPath = pnp.resolveVirtual && locator.reference && locator.reference.startsWith(`virtual:`)
350
+ ? pnp.resolveVirtual(pkg.packageLocation)
351
+ : pkg.packageLocation;
352
+ return fslib_1.npath.toPortablePath(realPath || pkg.packageLocation);
353
+ }
354
+ function getTargetLocatorPath(locator, pnp, options) {
355
+ const pkgLocator = pnp.getLocator(locator.name.replace(WORKSPACE_NAME_SUFFIX, ``), locator.reference);
356
+ const info = pnp.getPackageInformation(pkgLocator);
357
+ if (info === null)
358
+ throw new Error(`Assertion failed: Expected the package to be registered`);
359
+ return options.pnpifyFs
360
+ // In case of pnpifyFs we represent modules as symlinks to archives in NodeModulesFS
361
+ // `/home/user/project/foo` is a symlink to `/home/user/project/.yarn/.cache/foo.zip/node_modules/foo`
362
+ // To make this fs layout work with legacy tools we make
363
+ // `/home/user/project/.yarn/.cache/foo.zip/node_modules/foo/node_modules` (which normally does not exist inside archive) a symlink to:
364
+ // `/home/user/project/node_modules/foo/node_modules`, so that the tools were able to access it
365
+ ? { linkType: LinkType.SOFT, target: fslib_1.npath.toPortablePath(info.packageLocation) }
366
+ : { linkType: info.linkType, target: getRealPackageLocation(info, locator, pnp) };
367
+ }
368
+ /**
369
+ * Converts hoisted tree to node modules map
370
+ *
371
+ * @param pnp PnP API
372
+ * @param hoistedTree hoisted package tree from `RawHoister`
373
+ * @param locators locators
374
+ * @param packages package weights
375
+ *
376
+ * @returns node modules map
377
+ */
378
+ const populateNodeModulesTree = (pnp, hoistedTree, options) => {
379
+ const tree = new Map();
380
+ const makeLeafNode = (locator, nodePath, aliases) => {
381
+ const { linkType, target } = getTargetLocatorPath(locator, pnp, options);
382
+ return {
383
+ locator: stringifyLocator(locator),
384
+ nodePath,
385
+ target,
386
+ linkType,
387
+ aliases,
388
+ };
389
+ };
390
+ const getPackageName = (identName) => {
391
+ const [nameOrScope, name] = identName.split(`/`);
392
+ return name ? {
393
+ scope: (0, fslib_1.toFilename)(nameOrScope),
394
+ name: (0, fslib_1.toFilename)(name),
395
+ } : {
396
+ scope: null,
397
+ name: (0, fslib_1.toFilename)(nameOrScope),
398
+ };
399
+ };
400
+ const seenNodes = new Set();
401
+ const buildTree = (pkg, locationPrefix, parentNodePath) => {
402
+ if (seenNodes.has(pkg))
403
+ return;
404
+ seenNodes.add(pkg);
405
+ for (const dep of pkg.dependencies) {
406
+ // We do not want self-references in node_modules, since they confuse existing tools
407
+ if (dep === pkg)
408
+ continue;
409
+ const references = Array.from(dep.references).sort();
410
+ const locator = { name: dep.identName, reference: references[0] };
411
+ const { name, scope } = getPackageName(dep.name);
412
+ const packageNameParts = scope
413
+ ? [scope, name]
414
+ : [name];
415
+ const nodeModulesDirPath = fslib_1.ppath.join(locationPrefix, NODE_MODULES);
416
+ const nodeModulesLocation = fslib_1.ppath.join(nodeModulesDirPath, ...packageNameParts);
417
+ const nodePath = `${parentNodePath}/${locator.name}`;
418
+ const leafNode = makeLeafNode(locator, parentNodePath, references.slice(1));
419
+ // We don't want to create self-referencing symlinks for anonymous workspaces
420
+ let isAnonymousWorkspace = false;
421
+ if (leafNode.linkType === LinkType.SOFT && options.project) {
422
+ const workspace = options.project.workspacesByCwd.get(leafNode.target.slice(0, -1));
423
+ isAnonymousWorkspace = !!(workspace && !workspace.manifest.name);
424
+ }
425
+ if (!dep.name.endsWith(WORKSPACE_NAME_SUFFIX) && !isAnonymousWorkspace) {
426
+ const prevNode = tree.get(nodeModulesLocation);
427
+ if (prevNode) {
428
+ if (prevNode.dirList) {
429
+ throw new Error(`Assertion failed: ${nodeModulesLocation} cannot merge dir node with leaf node`);
430
+ }
431
+ else {
432
+ const locator1 = core_1.structUtils.parseLocator(prevNode.locator);
433
+ const locator2 = core_1.structUtils.parseLocator(leafNode.locator);
434
+ if (prevNode.linkType !== leafNode.linkType)
435
+ throw new Error(`Assertion failed: ${nodeModulesLocation} cannot merge nodes with different link types ${prevNode.nodePath}/${core_1.structUtils.stringifyLocator(locator1)} and ${parentNodePath}/${core_1.structUtils.stringifyLocator(locator2)}`);
436
+ else if (locator1.identHash !== locator2.identHash)
437
+ throw new Error(`Assertion failed: ${nodeModulesLocation} cannot merge nodes with different idents ${prevNode.nodePath}/${core_1.structUtils.stringifyLocator(locator1)} and ${parentNodePath}/s${core_1.structUtils.stringifyLocator(locator2)}`);
438
+ leafNode.aliases = [...leafNode.aliases, ...prevNode.aliases, core_1.structUtils.parseLocator(prevNode.locator).reference];
439
+ }
440
+ }
441
+ tree.set(nodeModulesLocation, leafNode);
442
+ const segments = nodeModulesLocation.split(`/`);
443
+ const nodeModulesIdx = segments.indexOf(NODE_MODULES);
444
+ for (let segCount = segments.length - 1; nodeModulesIdx >= 0 && segCount > nodeModulesIdx; segCount--) {
445
+ const dirPath = fslib_1.npath.toPortablePath(segments.slice(0, segCount).join(fslib_1.ppath.sep));
446
+ const targetDir = (0, fslib_1.toFilename)(segments[segCount]);
447
+ const subdirs = tree.get(dirPath);
448
+ if (!subdirs) {
449
+ tree.set(dirPath, { dirList: new Set([targetDir]) });
450
+ }
451
+ else if (subdirs.dirList) {
452
+ if (subdirs.dirList.has(targetDir)) {
453
+ break;
454
+ }
455
+ else {
456
+ subdirs.dirList.add(targetDir);
457
+ }
458
+ }
459
+ }
460
+ }
461
+ buildTree(dep, leafNode.linkType === LinkType.SOFT ? leafNode.target : nodeModulesLocation, nodePath);
462
+ }
463
+ };
464
+ const rootNode = makeLeafNode({ name: hoistedTree.name, reference: Array.from(hoistedTree.references)[0] }, ``, []);
465
+ const rootPath = rootNode.target;
466
+ tree.set(rootPath, rootNode);
467
+ buildTree(hoistedTree, rootPath, ``);
468
+ return tree;
469
+ };
470
+ /**
471
+ * Benchmarks raw hoisting performance.
472
+ *
473
+ * The function is used for troubleshooting purposes only.
474
+ *
475
+ * @param packageTree package tree
476
+ * @param packages package info
477
+ *
478
+ * @returns average raw hoisting time
479
+ */
480
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
481
+ const benchmarkRawHoisting = (packageTree) => {
482
+ const iterCount = 10;
483
+ const startTime = Date.now();
484
+ for (let iter = 0; iter < iterCount; iter++)
485
+ (0, hoist_1.hoist)(packageTree);
486
+ const endTime = Date.now();
487
+ return (endTime - startTime) / iterCount;
488
+ };
489
+ /**
490
+ * Benchmarks node_modules tree building.
491
+ *
492
+ * The function is used for troubleshooting purposes only.
493
+ *
494
+ * @param packageTree package tree
495
+ * @param packages package info
496
+ *
497
+ * @returns average raw hoisting time
498
+ */
499
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
500
+ const benchmarkBuildTree = (pnp, options) => {
501
+ const iterCount = 100;
502
+ const startTime = Date.now();
503
+ for (let iter = 0; iter < iterCount; iter++) {
504
+ const { packageTree, hoistingLimits } = buildPackageTree(pnp, options);
505
+ const hoistedTree = (0, hoist_1.hoist)(packageTree, { hoistingLimits });
506
+ populateNodeModulesTree(pnp, hoistedTree, options);
507
+ }
508
+ const endTime = Date.now();
509
+ return (endTime - startTime) / iterCount;
510
+ };
511
+ /**
512
+ * Pretty-prints node_modules tree.
513
+ *
514
+ * The function is used for troubleshooting purposes only.
515
+ *
516
+ * @param tree node_modules tree
517
+ * @param rootPath top-level project root folder
518
+ *
519
+ * @returns sorted node_modules tree
520
+ */
521
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
522
+ const dumpNodeModulesTree = (tree, rootPath) => {
523
+ const sortedTree = new Map();
524
+ const keys = Array.from(tree.keys()).sort();
525
+ for (const key of keys) {
526
+ const val = tree.get(key);
527
+ sortedTree.set(key, val.dirList ? { dirList: new Set(Array.from(val.dirList).sort()) } : val);
528
+ }
529
+ const seenPaths = new Set();
530
+ const dumpTree = (nodePath, prefix = ``, dirPrefix = ``) => {
531
+ const node = sortedTree.get(nodePath);
532
+ if (!node)
533
+ return ``;
534
+ seenPaths.add(nodePath);
535
+ let str = ``;
536
+ if (node.dirList) {
537
+ const dirs = Array.from(node.dirList);
538
+ for (let idx = 0; idx < dirs.length; idx++) {
539
+ const dir = dirs[idx];
540
+ str += `${prefix}${idx < dirs.length - 1 ? `├─` : `└─`}${dirPrefix}${dir}\n`;
541
+ str += dumpTree(fslib_1.ppath.join(nodePath, dir), `${prefix}${idx < dirs.length - 1 ? `│ ` : ` `}`);
542
+ }
543
+ }
544
+ else {
545
+ const { target, linkType } = node;
546
+ str += dumpTree(fslib_1.ppath.join(nodePath, NODE_MODULES), `${prefix}│ `, `${NODE_MODULES}/`);
547
+ str += `${prefix}└─${linkType === LinkType.SOFT ? `s>` : `>`}${target}\n`;
548
+ }
549
+ return str;
550
+ };
551
+ let str = dumpTree(fslib_1.ppath.join(rootPath, NODE_MODULES));
552
+ for (const key of sortedTree.keys()) {
553
+ if (!seenPaths.has(key)) {
554
+ str += `${key.replace(rootPath, ``)}\n${dumpTree(key)}`;
555
+ }
556
+ }
557
+ return str;
558
+ };
559
+ /**
560
+ * Pretty-prints dependency tree in the `yarn why`-like format
561
+ *
562
+ * The function is used for troubleshooting purposes only.
563
+ *
564
+ * @param pkg node_modules tree
565
+ *
566
+ * @returns sorted node_modules tree
567
+ */
568
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
569
+ const dumpDepTree = (tree) => {
570
+ const dumpLocator = (locator) => {
571
+ if (locator.reference === `workspace:.`) {
572
+ return `.`;
573
+ }
574
+ else if (!locator.reference) {
575
+ return `${locator.name}@${locator.reference}`;
576
+ }
577
+ else {
578
+ const version = (locator.reference.indexOf(`#`) > 0 ? locator.reference.split(`#`)[1] : locator.reference).replace(`npm:`, ``);
579
+ if (locator.reference.startsWith(`virtual`)) {
580
+ return `v:${locator.name}@${version}`;
581
+ }
582
+ else {
583
+ return `${locator.name}@${version}`;
584
+ }
585
+ }
586
+ };
587
+ const dumpPackage = (pkg, parents, prefix = ``) => {
588
+ if (parents.includes(pkg))
589
+ return ``;
590
+ const dependencies = Array.from(pkg.dependencies);
591
+ let str = ``;
592
+ for (let idx = 0; idx < dependencies.length; idx++) {
593
+ const dep = dependencies[idx];
594
+ str += `${prefix}${idx < dependencies.length - 1 ? `├─` : `└─`}${(parents.includes(dep) ? `>` : ``) + dumpLocator({ name: dep.name, reference: Array.from(dep.references)[0] })}\n`;
595
+ str += dumpPackage(dep, [...parents, dep], `${prefix}${idx < dependencies.length - 1 ? `│ ` : ` `}`);
596
+ }
597
+ return str;
598
+ };
599
+ return dumpPackage(tree, []);
600
+ };