@yarnpkg/nm 3.0.0-rc.9 → 3.0.1-rc.11

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.
@@ -39,6 +39,7 @@ export interface NodeModulesTreeOptions {
39
39
  pnpifyFs?: boolean;
40
40
  validateExternalSoftLinks?: boolean;
41
41
  hoistingLimitsByCwd?: Map<PortablePath, NodeModulesHoistingLimits>;
42
+ selfReferencesByCwd?: Map<PortablePath, Boolean>;
42
43
  project?: Project;
43
44
  }
44
45
  /** Package locator key for usage inside maps */
@@ -73,7 +73,15 @@ const buildLocatorMap = (nodeModulesTree) => {
73
73
  val.locations = val.locations.sort((loc1, loc2) => {
74
74
  const len1 = loc1.split(fslib_1.ppath.delimiter).length;
75
75
  const len2 = loc2.split(fslib_1.ppath.delimiter).length;
76
- return len1 !== len2 ? len2 - len1 : loc2.localeCompare(loc1);
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
+ }
77
85
  });
78
86
  }
79
87
  return map;
@@ -84,88 +92,120 @@ const areRealLocatorsEqual = (a, b) => {
84
92
  const realB = core_1.structUtils.isVirtualLocator(b) ? core_1.structUtils.devirtualizeLocator(b) : b;
85
93
  return core_1.structUtils.areLocatorsEqual(realA, realB);
86
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
+ };
87
101
  /**
88
- * Traverses PnP tree and produces input for the `RawHoister`
89
- *
90
- * @param pnp PnP API
91
- *
92
- * @returns package tree, packages info and locators
102
+ * Builds a map representing layout of nested workspaces and internal portals on the file system.
93
103
  */
94
- const buildPackageTree = (pnp, options) => {
95
- const pnpRoots = pnp.getDependencyTreeRoots();
96
- const errors = [];
97
- let preserveSymlinksRequired = false;
98
- const hoistingLimits = new Map();
99
- const workspaceDependenciesMap = new Map();
104
+ const buildWorkspaceMap = (pnp) => {
100
105
  const topPkg = pnp.getPackageInformation(pnp.topLevel);
101
106
  if (topPkg === null)
102
107
  throw new Error(`Assertion failed: Expected the top-level package to have been registered`);
103
108
  const topLocator = pnp.findPackageLocator(topPkg.packageLocation);
104
109
  if (topLocator === null)
105
110
  throw new Error(`Assertion failed: Expected the top-level package to have a physical locator`);
106
- const topPkgPortableLocation = fslib_1.npath.toPortablePath(topPkg.packageLocation);
107
- const topLocatorKey = stringifyLocator(topLocator);
108
- if (options.project) {
109
- const workspaceTree = { children: new Map() };
110
- const cwdSegments = options.project.cwd.split(fslib_1.ppath.sep);
111
- for (const [cwd, workspace] of options.project.workspacesByCwd) {
112
- const segments = cwd.split(fslib_1.ppath.sep).slice(cwdSegments.length);
113
- let node = workspaceTree;
114
- for (const segment of segments) {
115
- let nextNode = node.children.get(segment);
116
- if (!nextNode) {
117
- nextNode = { children: new Map() };
118
- node.children.set(segment, nextNode);
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);
119
133
  }
120
- node = nextNode;
121
134
  }
122
- node.workspaceLocator = { name: core_1.structUtils.stringifyIdent(workspace.anchoredLocator), reference: workspace.anchoredLocator.reference };
123
- }
124
- const addWorkspace = (node, parentWorkspaceLocator) => {
125
- if (node.workspaceLocator) {
126
- const parentLocatorKey = stringifyLocator(parentWorkspaceLocator);
127
- let dependencies = workspaceDependenciesMap.get(parentLocatorKey);
128
- if (!dependencies) {
129
- dependencies = new Set();
130
- workspaceDependenciesMap.set(parentLocatorKey, dependencies);
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
+ }
131
140
  }
132
- dependencies.add(node.workspaceLocator);
133
141
  }
134
- for (const child of node.children.values()) {
135
- addWorkspace(child, node.workspaceLocator || parentWorkspaceLocator);
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);
136
157
  }
137
- };
138
- for (const child of workspaceTree.children.values()) {
139
- addWorkspace(child, workspaceTree.workspaceLocator);
158
+ node = nextNode;
140
159
  }
160
+ node.workspaceLocator = locator;
141
161
  }
142
- else {
143
- for (const locator of pnpRoots) {
144
- if (locator.name !== topLocator.name || locator.reference !== topLocator.reference) {
145
- let dependencies = workspaceDependenciesMap.get(topLocatorKey);
146
- if (!dependencies) {
147
- dependencies = new Set();
148
- workspaceDependenciesMap.set(topLocatorKey, dependencies);
149
- }
150
- dependencies.add(locator);
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);
151
169
  }
170
+ dependencies.add(node.workspaceLocator);
152
171
  }
153
- }
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));
154
199
  const packageTree = {
155
200
  name: topLocator.name,
156
201
  identName: topLocator.name,
157
202
  reference: topLocator.reference,
158
203
  peerNames: topPkg.packagePeers,
159
204
  dependencies: new Set(),
205
+ dependencyKind: hoist_1.HoisterDependencyKind.WORKSPACE,
160
206
  };
161
207
  const nodes = new Map();
162
208
  const getNodeKey = (name, locator) => `${stringifyLocator(locator)}:${name}`;
163
- const isExternalSoftLink = (pkg, locator) => {
164
- if (pkg.linkType !== LinkType.SOFT || !options.project)
165
- return false;
166
- const realSoftLinkPath = fslib_1.npath.toPortablePath(pnp.resolveVirtual && locator.reference && locator.reference.startsWith(`virtual:`) ? pnp.resolveVirtual(pkg.packageLocation) : pkg.packageLocation);
167
- return fslib_1.ppath.contains(options.project.cwd, realSoftLinkPath) === null;
168
- };
169
209
  const addPackageToTree = (name, pkg, locator, parent, parentPkg, parentDependencies, parentRelativeCwd, isHoistBorder) => {
170
210
  var _a, _b;
171
211
  const nodeKey = getNodeKey(name, locator);
@@ -175,18 +215,26 @@ const buildPackageTree = (pnp, options) => {
175
215
  node = packageTree;
176
216
  nodes.set(nodeKey, packageTree);
177
217
  }
218
+ const isExternalSoftLinkPackage = isExternalSoftLink(pkg, locator, pnp, topPkgPortableLocation);
178
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;
179
225
  node = {
180
226
  name,
181
227
  identName: locator.name,
182
228
  reference: locator.reference,
183
229
  dependencies: new Set(),
184
- peerNames: pkg.packagePeers,
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,
185
234
  };
186
235
  nodes.set(nodeKey, node);
187
236
  }
188
237
  let hoistPriority;
189
- const isExternalSoftLinkPackage = isExternalSoftLink(pkg, locator);
190
238
  if (isExternalSoftLinkPackage)
191
239
  // External soft link dependencies have the highest priority - we don't want to install inside them
192
240
  hoistPriority = 2;
@@ -218,15 +266,17 @@ const buildPackageTree = (pnp, options) => {
218
266
  }
219
267
  }
220
268
  }
221
- const locatorKey = stringifyLocator(locator);
222
- const workspaceDependencies = workspaceDependenciesMap.get(locatorKey);
223
- if (workspaceDependencies) {
224
- for (const workspaceLocator of workspaceDependencies) {
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) {
225
273
  allDependencies.set(`${workspaceLocator.name}${WORKSPACE_NAME_SUFFIX}`, workspaceLocator.reference);
226
274
  }
227
275
  }
228
- parent.dependencies.add(node);
229
- if (!isSeen) {
276
+ if (pkg !== parentPkg || pkg.linkType !== LinkType.SOFT || !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) {
230
280
  const siblingPortalDependencyMap = new Map();
231
281
  for (const [depName, referencish] of allDependencies) {
232
282
  if (referencish !== null) {
@@ -235,7 +285,7 @@ const buildPackageTree = (pnp, options) => {
235
285
  const depPkg = pnp.getPackageInformation(pkgLocator);
236
286
  if (depPkg === null)
237
287
  throw new Error(`Assertion failed: Expected the package to have been registered`);
238
- const isExternalSoftLinkDep = isExternalSoftLink(depPkg, depLocator);
288
+ const isExternalSoftLinkDep = isExternalSoftLink(depPkg, depLocator, pnp, topPkgPortableLocation);
239
289
  if (options.validateExternalSoftLinks && options.project && isExternalSoftLinkDep) {
240
290
  if (depPkg.packageDependencies.size > 0)
241
291
  preserveSymlinksRequired = true;
@@ -287,7 +337,7 @@ const buildPackageTree = (pnp, options) => {
287
337
  const isHoistBorder = parentHoistingLimits === NodeModulesHoistingLimits.DEPENDENCIES
288
338
  || depHoistingLimits === NodeModulesHoistingLimits.DEPENDENCIES
289
339
  || depHoistingLimits === NodeModulesHoistingLimits.WORKSPACES;
290
- addPackageToTree(stringifyLocator(depLocator) === stringifyLocator(locator) ? name : depName, depPkg, depLocator, node, pkg, allDependencies, relativeDepCwd, isHoistBorder);
340
+ addPackageToTree(depName, depPkg, depLocator, node, pkg, allDependencies, relativeDepCwd, isHoistBorder);
291
341
  }
292
342
  }
293
343
  }
@@ -295,6 +345,12 @@ const buildPackageTree = (pnp, options) => {
295
345
  addPackageToTree(topLocator.name, topPkg, topLocator, packageTree, topPkg, topPkg.packageDependencies, fslib_2.PortablePath.dot, false);
296
346
  return { packageTree, hoistingLimits, errors, preserveSymlinksRequired };
297
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
+ }
298
354
  function getTargetLocatorPath(locator, pnp, options) {
299
355
  const pkgLocator = pnp.getLocator(locator.name.replace(WORKSPACE_NAME_SUFFIX, ``), locator.reference);
300
356
  const info = pnp.getPackageInformation(pkgLocator);
@@ -312,10 +368,7 @@ function getTargetLocatorPath(locator, pnp, options) {
312
368
  linkType = LinkType.SOFT;
313
369
  }
314
370
  else {
315
- const truePath = pnp.resolveVirtual && locator.reference && locator.reference.startsWith(`virtual:`)
316
- ? pnp.resolveVirtual(info.packageLocation)
317
- : info.packageLocation;
318
- target = fslib_1.npath.toPortablePath(truePath || info.packageLocation);
371
+ target = getRealPackageLocation(info, locator, pnp);
319
372
  linkType = info.linkType;
320
373
  }
321
374
  return { linkType, target };
@@ -371,7 +424,13 @@ const populateNodeModulesTree = (pnp, hoistedTree, options) => {
371
424
  const nodeModulesLocation = fslib_1.ppath.join(nodeModulesDirPath, ...packageNameParts);
372
425
  const nodePath = `${parentNodePath}/${locator.name}`;
373
426
  const leafNode = makeLeafNode(locator, parentNodePath, references.slice(1));
374
- if (!dep.name.endsWith(WORKSPACE_NAME_SUFFIX)) {
427
+ // We don't want to create self-referencing symlinks for anonymous workspaces
428
+ let isAnonymousWorkspace = false;
429
+ if (leafNode.linkType === LinkType.SOFT && options.project) {
430
+ const workspace = options.project.workspacesByCwd.get(leafNode.target.slice(0, -1));
431
+ isAnonymousWorkspace = !!(workspace && !workspace.manifest.name);
432
+ }
433
+ if (!dep.name.endsWith(WORKSPACE_NAME_SUFFIX) && !isAnonymousWorkspace) {
375
434
  const prevNode = tree.get(nodeModulesLocation);
376
435
  if (prevNode) {
377
436
  if (prevNode.dirList) {
package/lib/hoist.d.ts CHANGED
@@ -46,6 +46,11 @@
46
46
  * until you run out of candidates for current tree root.
47
47
  */
48
48
  declare type PackageName = string;
49
+ export declare enum HoisterDependencyKind {
50
+ REGULAR = 0,
51
+ WORKSPACE = 1,
52
+ EXTERNAL_SOFT_LINK = 2
53
+ }
49
54
  export declare type HoisterTree = {
50
55
  name: PackageName;
51
56
  identName: PackageName;
@@ -53,6 +58,7 @@ export declare type HoisterTree = {
53
58
  dependencies: Set<HoisterTree>;
54
59
  peerNames: Set<PackageName>;
55
60
  hoistPriority?: number;
61
+ dependencyKind?: HoisterDependencyKind;
56
62
  };
57
63
  export declare type HoisterResult = {
58
64
  name: PackageName;
package/lib/hoist.js CHANGED
@@ -1,6 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.hoist = void 0;
3
+ exports.hoist = exports.HoisterDependencyKind = void 0;
4
+ var HoisterDependencyKind;
5
+ (function (HoisterDependencyKind) {
6
+ HoisterDependencyKind[HoisterDependencyKind["REGULAR"] = 0] = "REGULAR";
7
+ HoisterDependencyKind[HoisterDependencyKind["WORKSPACE"] = 1] = "WORKSPACE";
8
+ HoisterDependencyKind[HoisterDependencyKind["EXTERNAL_SOFT_LINK"] = 2] = "EXTERNAL_SOFT_LINK";
9
+ })(HoisterDependencyKind = exports.HoisterDependencyKind || (exports.HoisterDependencyKind = {}));
4
10
  var Hoistable;
5
11
  (function (Hoistable) {
6
12
  Hoistable[Hoistable["YES"] = 0] = "YES";
@@ -134,7 +140,7 @@ const getUsedDependencies = (rootNodePath) => {
134
140
  const decoupleGraphNode = (parent, node) => {
135
141
  if (node.decoupled)
136
142
  return node;
137
- const { name, references, ident, locator, dependencies, originalDependencies, hoistedDependencies, peerNames, reasons, isHoistBorder, hoistPriority } = node;
143
+ const { name, references, ident, locator, dependencies, originalDependencies, hoistedDependencies, peerNames, reasons, isHoistBorder, hoistPriority, dependencyKind, hoistedFrom, hoistedTo } = node;
138
144
  // To perform node hoisting from parent node we must clone parent nodes up to the root node,
139
145
  // because some other package in the tree might depend on the parent package where hoisting
140
146
  // cannot be performed
@@ -151,7 +157,9 @@ const decoupleGraphNode = (parent, node) => {
151
157
  decoupled: true,
152
158
  isHoistBorder,
153
159
  hoistPriority,
154
- hoistedFrom: [],
160
+ dependencyKind,
161
+ hoistedFrom: new Map(hoistedFrom),
162
+ hoistedTo: new Map(hoistedTo),
155
163
  };
156
164
  const selfDep = clone.dependencies.get(name);
157
165
  if (selfDep && selfDep.ident == clone.ident)
@@ -308,7 +316,7 @@ const hoistTo = (tree, rootNodePath, rootNodePathLocators, parentShadowedNodes,
308
316
  }
309
317
  return { anotherRoundNeeded, isGraphChanged };
310
318
  };
311
- const getNodeHoistInfo = (rootNode, rootNodePathLocators, nodePath, node, usedDependencies, hoistIdents, hoistIdentMap, shadowedNodes, { outputReason }) => {
319
+ const getNodeHoistInfo = (rootNode, rootNodePathLocators, nodePath, node, usedDependencies, hoistIdents, hoistIdentMap, shadowedNodes, { outputReason, fastLookupPossible }) => {
312
320
  let reasonRoot;
313
321
  let reason = null;
314
322
  let dependsOn = new Set();
@@ -320,6 +328,35 @@ const getNodeHoistInfo = (rootNode, rootNodePathLocators, nodePath, node, usedDe
320
328
  let isHoistable = !isSelfReference;
321
329
  if (outputReason && !isHoistable)
322
330
  reason = `- self-reference`;
331
+ if (isHoistable) {
332
+ isHoistable = node.dependencyKind !== HoisterDependencyKind.WORKSPACE;
333
+ if (outputReason && !isHoistable) {
334
+ reason = `- workspace`;
335
+ }
336
+ }
337
+ if (isHoistable) {
338
+ isHoistable = node.dependencyKind !== HoisterDependencyKind.EXTERNAL_SOFT_LINK || node.dependencies.size === 0;
339
+ if (outputReason && !isHoistable) {
340
+ reason = `- external soft link with unhoisted dependencies`;
341
+ }
342
+ }
343
+ if (isHoistable) {
344
+ // Direct workspace dependencies must be hoisted to any common ancestor workspace of all the
345
+ // graph paths that include the dependency, because otherwise running app with
346
+ // `--preserve-symlinks` will become broken (without this flag the Node.js will pick dependency
347
+ // from the ancestor on the file system and with this flag it will pick ancestor from the graph
348
+ // and if these ancestors are different, the behavious of the application will be different).
349
+ // Another problem, which is prevented - is a creation of multiple hoisting layouts
350
+ // for the same workspace, because different dependencies of the same workspace might be hoisted
351
+ // differently, depending on the recepient workspace.
352
+ // It is difficult to find all common ancestors, but there is one easy to find common ancestor -
353
+ // the root workspace, so, for now, we either hoist direct dependencies into the root workspace, or we keep them
354
+ // unhoisted, thus we are safe from various pathological cases with `--preserve-symlinks`
355
+ isHoistable = parentNode.dependencyKind !== HoisterDependencyKind.WORKSPACE || parentNode.hoistedFrom.has(node.name) || rootNodePathLocators.size === 1;
356
+ if (outputReason && !isHoistable) {
357
+ reason = parentNode.reasons.get(node.name);
358
+ }
359
+ }
323
360
  if (isHoistable) {
324
361
  isHoistable = !rootNode.peerNames.has(node.name);
325
362
  if (outputReason && !isHoistable) {
@@ -388,6 +425,17 @@ const getNodeHoistInfo = (rootNode, rootNodePathLocators, nodePath, node, usedDe
388
425
  }
389
426
  isHoistable = arePeerDepsSatisfied;
390
427
  }
428
+ if (isHoistable && !fastLookupPossible) {
429
+ for (const origDep of node.hoistedDependencies.values()) {
430
+ const usedDep = usedDependencies.get(origDep.name);
431
+ if (!usedDep || origDep.ident !== usedDep.ident) {
432
+ isHoistable = false;
433
+ if (outputReason)
434
+ reason = `- previously hoisted dependency mismatch, needed: ${prettyPrintLocator(origDep.locator)}, available: ${prettyPrintLocator(usedDep === null || usedDep === void 0 ? void 0 : usedDep.locator)}`;
435
+ break;
436
+ }
437
+ }
438
+ }
391
439
  if (dependsOn !== null && dependsOn.size > 0) {
392
440
  return { isHoistable: Hoistable.DEPENDS, dependsOn, reason };
393
441
  }
@@ -416,7 +464,7 @@ const hoistGraph = (tree, rootNodePath, rootNodePathLocators, usedDependencies,
416
464
  const dependantTree = new Map();
417
465
  const hoistInfos = new Map();
418
466
  for (const subDependency of getSortedRegularDependencies(parentNode)) {
419
- const hoistInfo = getNodeHoistInfo(rootNode, rootNodePathLocators, [rootNode, ...nodePath, parentNode], subDependency, usedDependencies, hoistIdents, hoistIdentMap, shadowedNodes, { outputReason: options.debugLevel >= DebugLevel.REASONS });
467
+ const hoistInfo = getNodeHoistInfo(rootNode, rootNodePathLocators, [rootNode, ...nodePath, parentNode], subDependency, usedDependencies, hoistIdents, hoistIdentMap, shadowedNodes, { outputReason: options.debugLevel >= DebugLevel.REASONS, fastLookupPossible: options.fastLookupPossible });
420
468
  hoistInfos.set(subDependency, hoistInfo);
421
469
  if (hoistInfo.isHoistable === Hoistable.DEPENDS) {
422
470
  for (const node of hoistInfo.dependsOn) {
@@ -449,25 +497,27 @@ const hoistGraph = (tree, rootNodePath, rootNodePathLocators, usedDependencies,
449
497
  parentNode.hoistedDependencies.set(node.name, node);
450
498
  parentNode.reasons.delete(node.name);
451
499
  const hoistedNode = rootNode.dependencies.get(node.name);
452
- let hoistedFrom = null;
453
- if (options.debugLevel >= DebugLevel.REASONS)
454
- hoistedFrom = Array.from(locatorPath).concat([parentNode.locator]).map(x => prettyPrintLocator(x)).join(`→`);
500
+ if (options.debugLevel >= DebugLevel.REASONS) {
501
+ const hoistedFrom = Array.from(locatorPath).concat([parentNode.locator]).map(x => prettyPrintLocator(x)).join(`→`);
502
+ let hoistedFromArray = rootNode.hoistedFrom.get(node.name);
503
+ if (!hoistedFromArray) {
504
+ hoistedFromArray = [];
505
+ rootNode.hoistedFrom.set(node.name, hoistedFromArray);
506
+ }
507
+ hoistedFromArray.push(hoistedFrom);
508
+ parentNode.hoistedTo.set(node.name, Array.from(rootNodePath).map(x => prettyPrintLocator(x.locator)).join(`→`));
509
+ }
455
510
  // Add hoisted node to root node, in case it is not already there
456
511
  if (!hoistedNode) {
457
512
  // Avoid adding other version of root node to itself
458
513
  if (rootNode.ident !== node.ident) {
459
514
  rootNode.dependencies.set(node.name, node);
460
- if (options.debugLevel >= DebugLevel.REASONS)
461
- node.hoistedFrom.push(hoistedFrom);
462
515
  newNodes.add(node);
463
516
  }
464
517
  }
465
518
  else {
466
519
  for (const reference of node.references) {
467
520
  hoistedNode.references.add(reference);
468
- if (options.debugLevel >= DebugLevel.REASONS) {
469
- hoistedNode.hoistedFrom.push(hoistedFrom);
470
- }
471
521
  }
472
522
  }
473
523
  }
@@ -512,7 +562,7 @@ const selfCheck = (tree) => {
512
562
  const log = [];
513
563
  const seenNodes = new Set();
514
564
  const parents = new Set();
515
- const checkNode = (node, parentDeps) => {
565
+ const checkNode = (node, parentDeps, parent) => {
516
566
  if (seenNodes.has(node))
517
567
  return;
518
568
  seenNodes.add(node);
@@ -532,23 +582,28 @@ const selfCheck = (tree) => {
532
582
  }
533
583
  }
534
584
  else {
585
+ const hoistedFrom = parent.hoistedFrom.get(node.name);
586
+ const originalHoistedTo = node.hoistedTo.get(origDep.name);
587
+ const prettyHoistedFrom = `${hoistedFrom ? ` hoisted from ${hoistedFrom.join(`, `)}` : ``}`;
588
+ const prettyOriginalHoistedTo = `${originalHoistedTo ? ` hoisted to ${originalHoistedTo}` : ``}`;
589
+ const prettyNodePath = `${prettyPrintTreePath()}${prettyHoistedFrom}`;
535
590
  if (!dep) {
536
- log.push(`${prettyPrintTreePath()} - broken require promise: no required dependency ${origDep.locator} found`);
591
+ log.push(`${prettyNodePath} - broken require promise: no required dependency ${origDep.name}${prettyOriginalHoistedTo} found`);
537
592
  }
538
593
  else if (dep.ident !== origDep.ident) {
539
- log.push(`${prettyPrintTreePath()} - broken require promise for ${origDep.name}: expected ${origDep.ident}, but found: ${dep.ident}`);
594
+ log.push(`${prettyNodePath} - broken require promise for ${origDep.name}${prettyOriginalHoistedTo}: expected ${origDep.ident}, but found: ${dep.ident}`);
540
595
  }
541
596
  }
542
597
  }
543
598
  parents.add(node);
544
599
  for (const dep of node.dependencies.values()) {
545
600
  if (!node.peerNames.has(dep.name)) {
546
- checkNode(dep, dependencies);
601
+ checkNode(dep, dependencies, node);
547
602
  }
548
603
  }
549
604
  parents.delete(node);
550
605
  };
551
- checkNode(tree, tree.dependencies);
606
+ checkNode(tree, tree.dependencies, tree);
552
607
  return log.join(`\n`);
553
608
  };
554
609
  /**
@@ -571,14 +626,16 @@ const cloneTree = (tree, options) => {
571
626
  decoupled: true,
572
627
  isHoistBorder: true,
573
628
  hoistPriority: 0,
574
- hoistedFrom: [],
629
+ dependencyKind: HoisterDependencyKind.WORKSPACE,
630
+ hoistedFrom: new Map(),
631
+ hoistedTo: new Map(),
575
632
  };
576
633
  const seenNodes = new Map([[tree, treeCopy]]);
577
634
  const addNode = (node, parentNode) => {
578
635
  let workNode = seenNodes.get(node);
579
636
  const isSeen = !!workNode;
580
637
  if (!workNode) {
581
- const { name, identName, reference, peerNames, hoistPriority } = node;
638
+ const { name, identName, reference, peerNames, hoistPriority, dependencyKind } = node;
582
639
  const dependenciesNmHoistingLimits = options.hoistingLimits.get(parentNode.locator);
583
640
  workNode = {
584
641
  name,
@@ -593,7 +650,9 @@ const cloneTree = (tree, options) => {
593
650
  decoupled: true,
594
651
  isHoistBorder: dependenciesNmHoistingLimits ? dependenciesNmHoistingLimits.has(name) : false,
595
652
  hoistPriority: hoistPriority || 0,
596
- hoistedFrom: [],
653
+ dependencyKind: dependencyKind || HoisterDependencyKind.REGULAR,
654
+ hoistedFrom: new Map(),
655
+ hoistedTo: new Map(),
597
656
  };
598
657
  seenNodes.set(node, workNode);
599
658
  }
@@ -713,6 +772,8 @@ const buildPreferenceMap = (rootNode) => {
713
772
  return preferenceMap;
714
773
  };
715
774
  const prettyPrintLocator = (locator) => {
775
+ if (!locator)
776
+ return `none`;
716
777
  const idx = locator.indexOf(`@`, 1);
717
778
  let name = locator.substring(0, idx);
718
779
  if (name.endsWith(`$wsroot$`))
@@ -752,7 +813,14 @@ const dumpDepTree = (tree) => {
752
813
  if (nodeCount > MAX_NODES_TO_DUMP || parents.has(pkg))
753
814
  return ``;
754
815
  nodeCount++;
755
- const dependencies = Array.from(pkg.dependencies.values()).sort((n1, n2) => n1.name.localeCompare(n2.name));
816
+ const dependencies = Array.from(pkg.dependencies.values()).sort((n1, n2) => {
817
+ if (n1.name === n2.name) {
818
+ return 0;
819
+ }
820
+ else {
821
+ return n1.name > n2.name ? 1 : -1;
822
+ }
823
+ });
756
824
  let str = ``;
757
825
  parents.add(pkg);
758
826
  for (let idx = 0; idx < dependencies.length; idx++) {
@@ -760,7 +828,8 @@ const dumpDepTree = (tree) => {
760
828
  if (!pkg.peerNames.has(dep.name) && dep !== pkg) {
761
829
  const reason = pkg.reasons.get(dep.name);
762
830
  const identName = getIdentName(dep.locator);
763
- str += `${prefix}${idx < dependencies.length - 1 ? `├─` : `└─`}${(parents.has(dep) ? `>` : ``) + (identName !== dep.name ? `a:${dep.name}:` : ``) + prettyPrintLocator(dep.locator) + (reason ? ` ${reason}` : ``) + (dep !== pkg && dep.hoistedFrom.length > 0 ? `, hoisted from: ${dep.hoistedFrom.join(`, `)}` : ``)}\n`;
831
+ const hoistedFrom = pkg.hoistedFrom.get(dep.name) || [];
832
+ str += `${prefix}${idx < dependencies.length - 1 ? `├─` : `└─`}${(parents.has(dep) ? `>` : ``) + (identName !== dep.name ? `a:${dep.name}:` : ``) + prettyPrintLocator(dep.locator) + (reason ? ` ${reason}` : ``) + (dep !== pkg && hoistedFrom.length > 0 ? `, hoisted from: ${hoistedFrom.join(`, `)}` : ``)}\n`;
764
833
  str += dumpPackage(dep, parents, `${prefix}${idx < dependencies.length - 1 ? `│ ` : ` `}`);
765
834
  }
766
835
  }
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@yarnpkg/nm",
3
- "version": "3.0.0-rc.9",
3
+ "version": "3.0.1-rc.11",
4
4
  "license": "BSD-2-Clause",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
7
7
  "sideEffects": false,
8
8
  "dependencies": {
9
- "@yarnpkg/core": "^3.1.0-rc.9",
10
- "@yarnpkg/fslib": "^2.6.0-rc.8"
9
+ "@yarnpkg/core": "^3.2.0-rc.11",
10
+ "@yarnpkg/fslib": "^2.6.1-rc.6"
11
11
  },
12
12
  "devDependencies": {
13
- "@yarnpkg/pnp": "^3.1.0-rc.5"
13
+ "@yarnpkg/pnp": "^3.1.1-rc.11"
14
14
  },
15
15
  "scripts": {
16
16
  "postpack": "rm -rf lib",
@@ -33,5 +33,5 @@
33
33
  "engines": {
34
34
  "node": ">=12 <14 || 14.2 - 14.9 || >14.10.0"
35
35
  },
36
- "stableVersion": "2.4.1"
36
+ "stableVersion": "3.0.0"
37
37
  }