@yarnpkg/nm 3.0.0-rc.1 → 3.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.
- package/lib/buildNodeModulesTree.js +127 -80
- package/lib/hoist.d.ts +1 -0
- package/lib/hoist.js +78 -29
- package/package.json +4 -4
|
@@ -49,7 +49,7 @@ const buildNodeModulesTree = (pnp, options) => {
|
|
|
49
49
|
const { packageTree, hoistingLimits, errors, preserveSymlinksRequired } = buildPackageTree(pnp, options);
|
|
50
50
|
let tree = null;
|
|
51
51
|
if (errors.length === 0) {
|
|
52
|
-
const hoistedTree = hoist_1.hoist(packageTree, { hoistingLimits });
|
|
52
|
+
const hoistedTree = (0, hoist_1.hoist)(packageTree, { hoistingLimits });
|
|
53
53
|
tree = populateNodeModulesTree(pnp, hoistedTree, options);
|
|
54
54
|
}
|
|
55
55
|
return { tree, errors, preserveSymlinksRequired };
|
|
@@ -84,6 +84,83 @@ const areRealLocatorsEqual = (a, b) => {
|
|
|
84
84
|
const realB = core_1.structUtils.isVirtualLocator(b) ? core_1.structUtils.devirtualizeLocator(b) : b;
|
|
85
85
|
return core_1.structUtils.areLocatorsEqual(realA, realB);
|
|
86
86
|
};
|
|
87
|
+
const isExternalSoftLink = (pkg, locator, pnp, topPkgPortableLocation) => {
|
|
88
|
+
if (pkg.linkType !== LinkType.SOFT)
|
|
89
|
+
return false;
|
|
90
|
+
const realSoftLinkPath = fslib_1.npath.toPortablePath(pnp.resolveVirtual && locator.reference && locator.reference.startsWith(`virtual:`) ? pnp.resolveVirtual(pkg.packageLocation) : pkg.packageLocation);
|
|
91
|
+
return fslib_1.ppath.contains(topPkgPortableLocation, realSoftLinkPath) === null;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Builds a map representing layout of nested workspaces and internal portals on the file system.
|
|
95
|
+
*/
|
|
96
|
+
const buildWorkspaceMap = (pnp) => {
|
|
97
|
+
const topPkg = pnp.getPackageInformation(pnp.topLevel);
|
|
98
|
+
if (topPkg === null)
|
|
99
|
+
throw new Error(`Assertion failed: Expected the top-level package to have been registered`);
|
|
100
|
+
const topLocator = pnp.findPackageLocator(topPkg.packageLocation);
|
|
101
|
+
if (topLocator === null)
|
|
102
|
+
throw new Error(`Assertion failed: Expected the top-level package to have a physical locator`);
|
|
103
|
+
const topPkgPortableLocation = fslib_1.npath.toPortablePath(topPkg.packageLocation.slice(0, -1));
|
|
104
|
+
const workspaceMap = new Map();
|
|
105
|
+
const workspaceTree = { children: new Map() };
|
|
106
|
+
const pnpRoots = pnp.getDependencyTreeRoots();
|
|
107
|
+
// Workspace and internal portal locations to locators map
|
|
108
|
+
const workspaceLikeLocators = new Map();
|
|
109
|
+
const seen = new Set();
|
|
110
|
+
const visit = (locator) => {
|
|
111
|
+
const locatorKey = stringifyLocator(locator);
|
|
112
|
+
if (seen.has(locatorKey))
|
|
113
|
+
return;
|
|
114
|
+
seen.add(locatorKey);
|
|
115
|
+
const pkg = pnp.getPackageInformation(locator);
|
|
116
|
+
if (pkg) {
|
|
117
|
+
if (pkg.linkType === LinkType.SOFT && !isExternalSoftLink(pkg, locator, pnp, topPkgPortableLocation))
|
|
118
|
+
workspaceLikeLocators.set(getRealPackageLocation(pkg, locator, pnp), locator);
|
|
119
|
+
for (const [name, referencish] of pkg.packageDependencies) {
|
|
120
|
+
if (referencish !== null) {
|
|
121
|
+
if (!pkg.packagePeers.has(name)) {
|
|
122
|
+
visit(pnp.getLocator(name, referencish));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
for (const locator of pnpRoots)
|
|
129
|
+
visit(locator);
|
|
130
|
+
const cwdSegments = topPkgPortableLocation.split(fslib_1.ppath.sep);
|
|
131
|
+
for (const locator of workspaceLikeLocators.values()) {
|
|
132
|
+
const pkg = pnp.getPackageInformation(locator);
|
|
133
|
+
const location = fslib_1.npath.toPortablePath(pkg.packageLocation.slice(0, -1));
|
|
134
|
+
const segments = location.split(fslib_1.ppath.sep).slice(cwdSegments.length);
|
|
135
|
+
let node = workspaceTree;
|
|
136
|
+
for (const segment of segments) {
|
|
137
|
+
let nextNode = node.children.get(segment);
|
|
138
|
+
if (!nextNode) {
|
|
139
|
+
nextNode = { children: new Map() };
|
|
140
|
+
node.children.set(segment, nextNode);
|
|
141
|
+
}
|
|
142
|
+
node = nextNode;
|
|
143
|
+
}
|
|
144
|
+
node.workspaceLocator = locator;
|
|
145
|
+
}
|
|
146
|
+
const addWorkspace = (node, parentWorkspaceLocator) => {
|
|
147
|
+
if (node.workspaceLocator) {
|
|
148
|
+
const parentLocatorKey = stringifyLocator(parentWorkspaceLocator);
|
|
149
|
+
let dependencies = workspaceMap.get(parentLocatorKey);
|
|
150
|
+
if (!dependencies) {
|
|
151
|
+
dependencies = new Set();
|
|
152
|
+
workspaceMap.set(parentLocatorKey, dependencies);
|
|
153
|
+
}
|
|
154
|
+
dependencies.add(node.workspaceLocator);
|
|
155
|
+
}
|
|
156
|
+
for (const child of node.children.values()) {
|
|
157
|
+
addWorkspace(child, node.workspaceLocator || parentWorkspaceLocator);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
for (const child of workspaceTree.children.values())
|
|
161
|
+
addWorkspace(child, workspaceTree.workspaceLocator);
|
|
162
|
+
return workspaceMap;
|
|
163
|
+
};
|
|
87
164
|
/**
|
|
88
165
|
* Traverses PnP tree and produces input for the `RawHoister`
|
|
89
166
|
*
|
|
@@ -92,81 +169,28 @@ const areRealLocatorsEqual = (a, b) => {
|
|
|
92
169
|
* @returns package tree, packages info and locators
|
|
93
170
|
*/
|
|
94
171
|
const buildPackageTree = (pnp, options) => {
|
|
95
|
-
const pnpRoots = pnp.getDependencyTreeRoots();
|
|
96
172
|
const errors = [];
|
|
97
173
|
let preserveSymlinksRequired = false;
|
|
98
174
|
const hoistingLimits = new Map();
|
|
99
|
-
const
|
|
175
|
+
const workspaceMap = buildWorkspaceMap(pnp);
|
|
100
176
|
const topPkg = pnp.getPackageInformation(pnp.topLevel);
|
|
101
177
|
if (topPkg === null)
|
|
102
178
|
throw new Error(`Assertion failed: Expected the top-level package to have been registered`);
|
|
103
179
|
const topLocator = pnp.findPackageLocator(topPkg.packageLocation);
|
|
104
180
|
if (topLocator === null)
|
|
105
181
|
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);
|
|
119
|
-
}
|
|
120
|
-
node = nextNode;
|
|
121
|
-
}
|
|
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);
|
|
131
|
-
}
|
|
132
|
-
dependencies.add(node.workspaceLocator);
|
|
133
|
-
}
|
|
134
|
-
for (const child of node.children.values()) {
|
|
135
|
-
addWorkspace(child, node.workspaceLocator || parentWorkspaceLocator);
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
for (const child of workspaceTree.children.values()) {
|
|
139
|
-
addWorkspace(child, workspaceTree.workspaceLocator);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
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);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
182
|
+
const topPkgPortableLocation = fslib_1.npath.toPortablePath(topPkg.packageLocation.slice(0, -1));
|
|
154
183
|
const packageTree = {
|
|
155
184
|
name: topLocator.name,
|
|
156
185
|
identName: topLocator.name,
|
|
157
186
|
reference: topLocator.reference,
|
|
158
187
|
peerNames: topPkg.packagePeers,
|
|
159
188
|
dependencies: new Set(),
|
|
189
|
+
isWorkspace: true,
|
|
160
190
|
};
|
|
161
191
|
const nodes = new Map();
|
|
162
192
|
const getNodeKey = (name, locator) => `${stringifyLocator(locator)}:${name}`;
|
|
163
|
-
const
|
|
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
|
-
const addPackageToTree = (name, pkg, locator, parent, parentDependencies, parentRelativeCwd, isHoistBorder) => {
|
|
193
|
+
const addPackageToTree = (name, pkg, locator, parent, parentPkg, parentDependencies, parentRelativeCwd, isHoistBorder) => {
|
|
170
194
|
var _a, _b;
|
|
171
195
|
const nodeKey = getNodeKey(name, locator);
|
|
172
196
|
let node = nodes.get(nodeKey);
|
|
@@ -175,18 +199,31 @@ const buildPackageTree = (pnp, options) => {
|
|
|
175
199
|
node = packageTree;
|
|
176
200
|
nodes.set(nodeKey, packageTree);
|
|
177
201
|
}
|
|
178
|
-
const isExternalSoftLinkPackage = isExternalSoftLink(pkg, locator);
|
|
202
|
+
const isExternalSoftLinkPackage = isExternalSoftLink(pkg, locator, pnp, topPkgPortableLocation);
|
|
179
203
|
if (!node) {
|
|
204
|
+
const isWorkspace = pkg.linkType === LinkType.SOFT && locator.name.endsWith(WORKSPACE_NAME_SUFFIX);
|
|
180
205
|
node = {
|
|
181
206
|
name,
|
|
182
207
|
identName: locator.name,
|
|
183
208
|
reference: locator.reference,
|
|
184
209
|
dependencies: new Set(),
|
|
185
|
-
|
|
186
|
-
|
|
210
|
+
// View peer dependencies as regular dependencies for workspaces
|
|
211
|
+
// (meeting workspace peer dependency constraints is sometimes hard, sometimes impossible for the nm linker)
|
|
212
|
+
peerNames: isWorkspace ? new Set() : pkg.packagePeers,
|
|
213
|
+
isWorkspace,
|
|
187
214
|
};
|
|
188
215
|
nodes.set(nodeKey, node);
|
|
189
216
|
}
|
|
217
|
+
let hoistPriority;
|
|
218
|
+
if (isExternalSoftLinkPackage)
|
|
219
|
+
// External soft link dependencies have the highest priority - we don't want to install inside them
|
|
220
|
+
hoistPriority = 2;
|
|
221
|
+
else if (parentPkg.linkType === LinkType.SOFT)
|
|
222
|
+
// Internal soft link dependencies should have priority over transitive dependencies - to maximize chances having only one top-level node_modules
|
|
223
|
+
hoistPriority = 1;
|
|
224
|
+
else
|
|
225
|
+
hoistPriority = 0;
|
|
226
|
+
node.hoistPriority = Math.max(node.hoistPriority || 0, hoistPriority);
|
|
190
227
|
if (isHoistBorder && !isExternalSoftLinkPackage) {
|
|
191
228
|
const parentLocatorKey = stringifyLocator({ name: parent.identName, reference: parent.reference });
|
|
192
229
|
const dependencyBorders = hoistingLimits.get(parentLocatorKey) || new Set();
|
|
@@ -209,15 +246,16 @@ const buildPackageTree = (pnp, options) => {
|
|
|
209
246
|
}
|
|
210
247
|
}
|
|
211
248
|
}
|
|
212
|
-
const locatorKey = stringifyLocator(locator);
|
|
213
|
-
const
|
|
214
|
-
if (
|
|
215
|
-
for (const workspaceLocator of
|
|
249
|
+
const locatorKey = stringifyLocator({ name: locator.name.replace(WORKSPACE_NAME_SUFFIX, ``), reference: locator.reference });
|
|
250
|
+
const innerWorkspaces = workspaceMap.get(locatorKey);
|
|
251
|
+
if (innerWorkspaces) {
|
|
252
|
+
for (const workspaceLocator of innerWorkspaces) {
|
|
216
253
|
allDependencies.set(`${workspaceLocator.name}${WORKSPACE_NAME_SUFFIX}`, workspaceLocator.reference);
|
|
217
254
|
}
|
|
218
255
|
}
|
|
219
256
|
parent.dependencies.add(node);
|
|
220
|
-
|
|
257
|
+
const isWorkspaceDependency = locator !== topLocator && pkg.linkType === LinkType.SOFT && !locator.name.endsWith(WORKSPACE_NAME_SUFFIX) && !isExternalSoftLinkPackage;
|
|
258
|
+
if (!isSeen && !isWorkspaceDependency) {
|
|
221
259
|
const siblingPortalDependencyMap = new Map();
|
|
222
260
|
for (const [depName, referencish] of allDependencies) {
|
|
223
261
|
if (referencish !== null) {
|
|
@@ -226,7 +264,7 @@ const buildPackageTree = (pnp, options) => {
|
|
|
226
264
|
const depPkg = pnp.getPackageInformation(pkgLocator);
|
|
227
265
|
if (depPkg === null)
|
|
228
266
|
throw new Error(`Assertion failed: Expected the package to have been registered`);
|
|
229
|
-
const isExternalSoftLinkDep = isExternalSoftLink(depPkg, depLocator);
|
|
267
|
+
const isExternalSoftLinkDep = isExternalSoftLink(depPkg, depLocator, pnp, topPkgPortableLocation);
|
|
230
268
|
if (options.validateExternalSoftLinks && options.project && isExternalSoftLinkDep) {
|
|
231
269
|
if (depPkg.packageDependencies.size > 0)
|
|
232
270
|
preserveSymlinksRequired = true;
|
|
@@ -278,14 +316,20 @@ const buildPackageTree = (pnp, options) => {
|
|
|
278
316
|
const isHoistBorder = parentHoistingLimits === NodeModulesHoistingLimits.DEPENDENCIES
|
|
279
317
|
|| depHoistingLimits === NodeModulesHoistingLimits.DEPENDENCIES
|
|
280
318
|
|| depHoistingLimits === NodeModulesHoistingLimits.WORKSPACES;
|
|
281
|
-
addPackageToTree(stringifyLocator(depLocator) === stringifyLocator(locator) ? name : depName, depPkg, depLocator, node, allDependencies, relativeDepCwd, isHoistBorder);
|
|
319
|
+
addPackageToTree(stringifyLocator(depLocator) === stringifyLocator(locator) ? name : depName, depPkg, depLocator, node, pkg, allDependencies, relativeDepCwd, isHoistBorder);
|
|
282
320
|
}
|
|
283
321
|
}
|
|
284
322
|
}
|
|
285
323
|
};
|
|
286
|
-
addPackageToTree(topLocator.name, topPkg, topLocator, packageTree, topPkg.packageDependencies, fslib_2.PortablePath.dot, false);
|
|
324
|
+
addPackageToTree(topLocator.name, topPkg, topLocator, packageTree, topPkg, topPkg.packageDependencies, fslib_2.PortablePath.dot, false);
|
|
287
325
|
return { packageTree, hoistingLimits, errors, preserveSymlinksRequired };
|
|
288
326
|
};
|
|
327
|
+
function getRealPackageLocation(pkg, locator, pnp) {
|
|
328
|
+
const realPath = pnp.resolveVirtual && locator.reference && locator.reference.startsWith(`virtual:`)
|
|
329
|
+
? pnp.resolveVirtual(pkg.packageLocation)
|
|
330
|
+
: pkg.packageLocation;
|
|
331
|
+
return fslib_1.npath.toPortablePath(realPath || pkg.packageLocation);
|
|
332
|
+
}
|
|
289
333
|
function getTargetLocatorPath(locator, pnp, options) {
|
|
290
334
|
const pkgLocator = pnp.getLocator(locator.name.replace(WORKSPACE_NAME_SUFFIX, ``), locator.reference);
|
|
291
335
|
const info = pnp.getPackageInformation(pkgLocator);
|
|
@@ -303,10 +347,7 @@ function getTargetLocatorPath(locator, pnp, options) {
|
|
|
303
347
|
linkType = LinkType.SOFT;
|
|
304
348
|
}
|
|
305
349
|
else {
|
|
306
|
-
|
|
307
|
-
? pnp.resolveVirtual(info.packageLocation)
|
|
308
|
-
: info.packageLocation;
|
|
309
|
-
target = fslib_1.npath.toPortablePath(truePath || info.packageLocation);
|
|
350
|
+
target = getRealPackageLocation(info, locator, pnp);
|
|
310
351
|
linkType = info.linkType;
|
|
311
352
|
}
|
|
312
353
|
return { linkType, target };
|
|
@@ -336,11 +377,11 @@ const populateNodeModulesTree = (pnp, hoistedTree, options) => {
|
|
|
336
377
|
const getPackageName = (identName) => {
|
|
337
378
|
const [nameOrScope, name] = identName.split(`/`);
|
|
338
379
|
return name ? {
|
|
339
|
-
scope: fslib_1.toFilename(nameOrScope),
|
|
340
|
-
name: fslib_1.toFilename(name),
|
|
380
|
+
scope: (0, fslib_1.toFilename)(nameOrScope),
|
|
381
|
+
name: (0, fslib_1.toFilename)(name),
|
|
341
382
|
} : {
|
|
342
383
|
scope: null,
|
|
343
|
-
name: fslib_1.toFilename(nameOrScope),
|
|
384
|
+
name: (0, fslib_1.toFilename)(nameOrScope),
|
|
344
385
|
};
|
|
345
386
|
};
|
|
346
387
|
const seenNodes = new Set();
|
|
@@ -362,7 +403,13 @@ const populateNodeModulesTree = (pnp, hoistedTree, options) => {
|
|
|
362
403
|
const nodeModulesLocation = fslib_1.ppath.join(nodeModulesDirPath, ...packageNameParts);
|
|
363
404
|
const nodePath = `${parentNodePath}/${locator.name}`;
|
|
364
405
|
const leafNode = makeLeafNode(locator, parentNodePath, references.slice(1));
|
|
365
|
-
|
|
406
|
+
// We don't want to create self-referencing symlinks for anonymous workspaces
|
|
407
|
+
let isAnonymousWorkspace = false;
|
|
408
|
+
if (leafNode.linkType === LinkType.SOFT && options.project) {
|
|
409
|
+
const workspace = options.project.workspacesByCwd.get(leafNode.target.slice(0, -1));
|
|
410
|
+
isAnonymousWorkspace = !!(workspace && !workspace.manifest.name);
|
|
411
|
+
}
|
|
412
|
+
if (!dep.name.endsWith(WORKSPACE_NAME_SUFFIX) && !isAnonymousWorkspace) {
|
|
366
413
|
const prevNode = tree.get(nodeModulesLocation);
|
|
367
414
|
if (prevNode) {
|
|
368
415
|
if (prevNode.dirList) {
|
|
@@ -384,7 +431,7 @@ const populateNodeModulesTree = (pnp, hoistedTree, options) => {
|
|
|
384
431
|
let segCount = segments.length - 1;
|
|
385
432
|
while (nodeModulesIdx >= 0 && segCount > nodeModulesIdx) {
|
|
386
433
|
const dirPath = fslib_1.npath.toPortablePath(segments.slice(0, segCount).join(fslib_1.ppath.sep));
|
|
387
|
-
const targetDir = fslib_1.toFilename(segments[segCount]);
|
|
434
|
+
const targetDir = (0, fslib_1.toFilename)(segments[segCount]);
|
|
388
435
|
const subdirs = tree.get(dirPath);
|
|
389
436
|
if (!subdirs) {
|
|
390
437
|
tree.set(dirPath, { dirList: new Set([targetDir]) });
|
|
@@ -424,7 +471,7 @@ const benchmarkRawHoisting = (packageTree) => {
|
|
|
424
471
|
const iterCount = 10;
|
|
425
472
|
const startTime = Date.now();
|
|
426
473
|
for (let iter = 0; iter < iterCount; iter++)
|
|
427
|
-
hoist_1.hoist(packageTree);
|
|
474
|
+
(0, hoist_1.hoist)(packageTree);
|
|
428
475
|
const endTime = Date.now();
|
|
429
476
|
return (endTime - startTime) / iterCount;
|
|
430
477
|
};
|
|
@@ -444,7 +491,7 @@ const benchmarkBuildTree = (pnp, options) => {
|
|
|
444
491
|
const startTime = Date.now();
|
|
445
492
|
for (let iter = 0; iter < iterCount; iter++) {
|
|
446
493
|
const { packageTree, hoistingLimits } = buildPackageTree(pnp, options);
|
|
447
|
-
const hoistedTree = hoist_1.hoist(packageTree, { hoistingLimits });
|
|
494
|
+
const hoistedTree = (0, hoist_1.hoist)(packageTree, { hoistingLimits });
|
|
448
495
|
populateNodeModulesTree(pnp, hoistedTree, options);
|
|
449
496
|
}
|
|
450
497
|
const endTime = Date.now();
|
package/lib/hoist.d.ts
CHANGED
package/lib/hoist.js
CHANGED
|
@@ -88,10 +88,6 @@ const getUsedDependencies = (rootNodePath) => {
|
|
|
88
88
|
const rootNode = rootNodePath[rootNodePath.length - 1];
|
|
89
89
|
const usedDependencies = new Map();
|
|
90
90
|
const seenNodes = new Set();
|
|
91
|
-
const reachableDependencies = new Map();
|
|
92
|
-
for (const node of rootNodePath)
|
|
93
|
-
for (const dep of node.dependencies.values())
|
|
94
|
-
reachableDependencies.set(dep.name, dep);
|
|
95
91
|
const hiddenDependencies = new Set();
|
|
96
92
|
const addUsedDependencies = (node, hiddenDependencies) => {
|
|
97
93
|
if (seenNodes.has(node))
|
|
@@ -99,9 +95,12 @@ const getUsedDependencies = (rootNodePath) => {
|
|
|
99
95
|
seenNodes.add(node);
|
|
100
96
|
for (const dep of node.hoistedDependencies.values()) {
|
|
101
97
|
if (!hiddenDependencies.has(dep.name)) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
let reachableDependency;
|
|
99
|
+
for (const node of rootNodePath) {
|
|
100
|
+
reachableDependency = node.dependencies.get(dep.name);
|
|
101
|
+
if (reachableDependency) {
|
|
102
|
+
usedDependencies.set(reachableDependency.name, reachableDependency);
|
|
103
|
+
}
|
|
105
104
|
}
|
|
106
105
|
}
|
|
107
106
|
}
|
|
@@ -135,7 +134,7 @@ const getUsedDependencies = (rootNodePath) => {
|
|
|
135
134
|
const decoupleGraphNode = (parent, node) => {
|
|
136
135
|
if (node.decoupled)
|
|
137
136
|
return node;
|
|
138
|
-
const { name, references, ident, locator, dependencies, originalDependencies, hoistedDependencies, peerNames, reasons, isHoistBorder, hoistPriority } = node;
|
|
137
|
+
const { name, references, ident, locator, dependencies, originalDependencies, hoistedDependencies, peerNames, reasons, isHoistBorder, hoistPriority, isWorkspace, hoistedFrom, hoistedTo } = node;
|
|
139
138
|
// To perform node hoisting from parent node we must clone parent nodes up to the root node,
|
|
140
139
|
// because some other package in the tree might depend on the parent package where hoisting
|
|
141
140
|
// cannot be performed
|
|
@@ -152,7 +151,9 @@ const decoupleGraphNode = (parent, node) => {
|
|
|
152
151
|
decoupled: true,
|
|
153
152
|
isHoistBorder,
|
|
154
153
|
hoistPriority,
|
|
155
|
-
|
|
154
|
+
isWorkspace,
|
|
155
|
+
hoistedFrom: new Map(hoistedFrom),
|
|
156
|
+
hoistedTo: new Map(hoistedTo),
|
|
156
157
|
};
|
|
157
158
|
const selfDep = clone.dependencies.get(name);
|
|
158
159
|
if (selfDep && selfDep.ident == clone.ident)
|
|
@@ -309,7 +310,7 @@ const hoistTo = (tree, rootNodePath, rootNodePathLocators, parentShadowedNodes,
|
|
|
309
310
|
}
|
|
310
311
|
return { anotherRoundNeeded, isGraphChanged };
|
|
311
312
|
};
|
|
312
|
-
const getNodeHoistInfo = (rootNode, rootNodePathLocators, nodePath, node, usedDependencies, hoistIdents, hoistIdentMap, shadowedNodes, { outputReason }) => {
|
|
313
|
+
const getNodeHoistInfo = (rootNode, rootNodePathLocators, nodePath, node, usedDependencies, hoistIdents, hoistIdentMap, shadowedNodes, { outputReason, fastLookupPossible }) => {
|
|
313
314
|
let reasonRoot;
|
|
314
315
|
let reason = null;
|
|
315
316
|
let dependsOn = new Set();
|
|
@@ -321,6 +322,29 @@ const getNodeHoistInfo = (rootNode, rootNodePathLocators, nodePath, node, usedDe
|
|
|
321
322
|
let isHoistable = !isSelfReference;
|
|
322
323
|
if (outputReason && !isHoistable)
|
|
323
324
|
reason = `- self-reference`;
|
|
325
|
+
if (isHoistable) {
|
|
326
|
+
isHoistable = !node.isWorkspace;
|
|
327
|
+
if (outputReason && !isHoistable) {
|
|
328
|
+
reason = `- workspace`;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (isHoistable) {
|
|
332
|
+
// Direct workspace dependencies must be hoisted to any common ancestor workspace of all the
|
|
333
|
+
// graph paths that include the dependency, because otherwise running app with
|
|
334
|
+
// `--preserve-symlinks` will become broken (without this flag the Node.js will pick dependency
|
|
335
|
+
// from the ancestor on the file system and with this flag it will pick ancestor from the graph
|
|
336
|
+
// and if these ancestors are different, the behavious of the application will be different).
|
|
337
|
+
// Another problem, which is prevented - is a creation of multiple hoisting layouts
|
|
338
|
+
// for the same workspace, because different dependencies of the same workspace might be hoisted
|
|
339
|
+
// differently, depending on the recepient workspace.
|
|
340
|
+
// It is difficult to find all common ancestors, but there is one easy to find common ancestor -
|
|
341
|
+
// the root workspace, so, for now, we either hoist direct dependencies into the root workspace, or we keep them
|
|
342
|
+
// unhoisted, thus we are safe from various pathological cases with `--preserve-symlinks`
|
|
343
|
+
isHoistable = !parentNode.isWorkspace || parentNode.hoistedFrom.has(node.name) || rootNodePathLocators.size === 1;
|
|
344
|
+
if (outputReason && !isHoistable) {
|
|
345
|
+
reason = parentNode.reasons.get(node.name);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
324
348
|
if (isHoistable) {
|
|
325
349
|
isHoistable = !rootNode.peerNames.has(node.name);
|
|
326
350
|
if (outputReason && !isHoistable) {
|
|
@@ -389,6 +413,17 @@ const getNodeHoistInfo = (rootNode, rootNodePathLocators, nodePath, node, usedDe
|
|
|
389
413
|
}
|
|
390
414
|
isHoistable = arePeerDepsSatisfied;
|
|
391
415
|
}
|
|
416
|
+
if (isHoistable && !fastLookupPossible) {
|
|
417
|
+
for (const origDep of node.hoistedDependencies.values()) {
|
|
418
|
+
const usedDep = usedDependencies.get(origDep.name);
|
|
419
|
+
if (!usedDep || origDep.ident !== usedDep.ident) {
|
|
420
|
+
isHoistable = false;
|
|
421
|
+
if (outputReason)
|
|
422
|
+
reason = `- previously hoisted dependency mismatch, needed: ${prettyPrintLocator(origDep.locator)}, available: ${prettyPrintLocator(usedDep === null || usedDep === void 0 ? void 0 : usedDep.locator)}`;
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
392
427
|
if (dependsOn !== null && dependsOn.size > 0) {
|
|
393
428
|
return { isHoistable: Hoistable.DEPENDS, dependsOn, reason };
|
|
394
429
|
}
|
|
@@ -417,7 +452,7 @@ const hoistGraph = (tree, rootNodePath, rootNodePathLocators, usedDependencies,
|
|
|
417
452
|
const dependantTree = new Map();
|
|
418
453
|
const hoistInfos = new Map();
|
|
419
454
|
for (const subDependency of getSortedRegularDependencies(parentNode)) {
|
|
420
|
-
const hoistInfo = getNodeHoistInfo(rootNode, rootNodePathLocators, [rootNode, ...nodePath, parentNode], subDependency, usedDependencies, hoistIdents, hoistIdentMap, shadowedNodes, { outputReason: options.debugLevel >= DebugLevel.REASONS });
|
|
455
|
+
const hoistInfo = getNodeHoistInfo(rootNode, rootNodePathLocators, [rootNode, ...nodePath, parentNode], subDependency, usedDependencies, hoistIdents, hoistIdentMap, shadowedNodes, { outputReason: options.debugLevel >= DebugLevel.REASONS, fastLookupPossible: options.fastLookupPossible });
|
|
421
456
|
hoistInfos.set(subDependency, hoistInfo);
|
|
422
457
|
if (hoistInfo.isHoistable === Hoistable.DEPENDS) {
|
|
423
458
|
for (const node of hoistInfo.dependsOn) {
|
|
@@ -450,25 +485,27 @@ const hoistGraph = (tree, rootNodePath, rootNodePathLocators, usedDependencies,
|
|
|
450
485
|
parentNode.hoistedDependencies.set(node.name, node);
|
|
451
486
|
parentNode.reasons.delete(node.name);
|
|
452
487
|
const hoistedNode = rootNode.dependencies.get(node.name);
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
488
|
+
if (options.debugLevel >= DebugLevel.REASONS) {
|
|
489
|
+
const hoistedFrom = Array.from(locatorPath).concat([parentNode.locator]).map(x => prettyPrintLocator(x)).join(`→`);
|
|
490
|
+
let hoistedFromArray = rootNode.hoistedFrom.get(node.name);
|
|
491
|
+
if (!hoistedFromArray) {
|
|
492
|
+
hoistedFromArray = [];
|
|
493
|
+
rootNode.hoistedFrom.set(node.name, hoistedFromArray);
|
|
494
|
+
}
|
|
495
|
+
hoistedFromArray.push(hoistedFrom);
|
|
496
|
+
parentNode.hoistedTo.set(node.name, Array.from(rootNodePath).map(x => prettyPrintLocator(x.locator)).join(`→`));
|
|
497
|
+
}
|
|
456
498
|
// Add hoisted node to root node, in case it is not already there
|
|
457
499
|
if (!hoistedNode) {
|
|
458
500
|
// Avoid adding other version of root node to itself
|
|
459
501
|
if (rootNode.ident !== node.ident) {
|
|
460
502
|
rootNode.dependencies.set(node.name, node);
|
|
461
|
-
if (options.debugLevel >= DebugLevel.REASONS)
|
|
462
|
-
node.hoistedFrom.push(hoistedFrom);
|
|
463
503
|
newNodes.add(node);
|
|
464
504
|
}
|
|
465
505
|
}
|
|
466
506
|
else {
|
|
467
507
|
for (const reference of node.references) {
|
|
468
508
|
hoistedNode.references.add(reference);
|
|
469
|
-
if (options.debugLevel >= DebugLevel.REASONS) {
|
|
470
|
-
hoistedNode.hoistedFrom.push(hoistedFrom);
|
|
471
|
-
}
|
|
472
509
|
}
|
|
473
510
|
}
|
|
474
511
|
}
|
|
@@ -513,7 +550,7 @@ const selfCheck = (tree) => {
|
|
|
513
550
|
const log = [];
|
|
514
551
|
const seenNodes = new Set();
|
|
515
552
|
const parents = new Set();
|
|
516
|
-
const checkNode = (node, parentDeps) => {
|
|
553
|
+
const checkNode = (node, parentDeps, parent) => {
|
|
517
554
|
if (seenNodes.has(node))
|
|
518
555
|
return;
|
|
519
556
|
seenNodes.add(node);
|
|
@@ -533,23 +570,28 @@ const selfCheck = (tree) => {
|
|
|
533
570
|
}
|
|
534
571
|
}
|
|
535
572
|
else {
|
|
573
|
+
const hoistedFrom = parent.hoistedFrom.get(node.name);
|
|
574
|
+
const originalHoistedTo = node.hoistedTo.get(origDep.name);
|
|
575
|
+
const prettyHoistedFrom = `${hoistedFrom ? ` hoisted from ${hoistedFrom.join(`, `)}` : ``}`;
|
|
576
|
+
const prettyOriginalHoistedTo = `${originalHoistedTo ? ` hoisted to ${originalHoistedTo}` : ``}`;
|
|
577
|
+
const prettyNodePath = `${prettyPrintTreePath()}${prettyHoistedFrom}`;
|
|
536
578
|
if (!dep) {
|
|
537
|
-
log.push(`${
|
|
579
|
+
log.push(`${prettyNodePath} - broken require promise: no required dependency ${origDep.name}${prettyOriginalHoistedTo} found`);
|
|
538
580
|
}
|
|
539
581
|
else if (dep.ident !== origDep.ident) {
|
|
540
|
-
log.push(`${
|
|
582
|
+
log.push(`${prettyNodePath} - broken require promise for ${origDep.name}${prettyOriginalHoistedTo}: expected ${origDep.ident}, but found: ${dep.ident}`);
|
|
541
583
|
}
|
|
542
584
|
}
|
|
543
585
|
}
|
|
544
586
|
parents.add(node);
|
|
545
587
|
for (const dep of node.dependencies.values()) {
|
|
546
588
|
if (!node.peerNames.has(dep.name)) {
|
|
547
|
-
checkNode(dep, dependencies);
|
|
589
|
+
checkNode(dep, dependencies, node);
|
|
548
590
|
}
|
|
549
591
|
}
|
|
550
592
|
parents.delete(node);
|
|
551
593
|
};
|
|
552
|
-
checkNode(tree, tree.dependencies);
|
|
594
|
+
checkNode(tree, tree.dependencies, tree);
|
|
553
595
|
return log.join(`\n`);
|
|
554
596
|
};
|
|
555
597
|
/**
|
|
@@ -572,14 +614,16 @@ const cloneTree = (tree, options) => {
|
|
|
572
614
|
decoupled: true,
|
|
573
615
|
isHoistBorder: true,
|
|
574
616
|
hoistPriority: 0,
|
|
575
|
-
|
|
617
|
+
isWorkspace: true,
|
|
618
|
+
hoistedFrom: new Map(),
|
|
619
|
+
hoistedTo: new Map(),
|
|
576
620
|
};
|
|
577
621
|
const seenNodes = new Map([[tree, treeCopy]]);
|
|
578
622
|
const addNode = (node, parentNode) => {
|
|
579
623
|
let workNode = seenNodes.get(node);
|
|
580
624
|
const isSeen = !!workNode;
|
|
581
625
|
if (!workNode) {
|
|
582
|
-
const { name, identName, reference, peerNames, hoistPriority } = node;
|
|
626
|
+
const { name, identName, reference, peerNames, hoistPriority, isWorkspace } = node;
|
|
583
627
|
const dependenciesNmHoistingLimits = options.hoistingLimits.get(parentNode.locator);
|
|
584
628
|
workNode = {
|
|
585
629
|
name,
|
|
@@ -594,7 +638,9 @@ const cloneTree = (tree, options) => {
|
|
|
594
638
|
decoupled: true,
|
|
595
639
|
isHoistBorder: dependenciesNmHoistingLimits ? dependenciesNmHoistingLimits.has(name) : false,
|
|
596
640
|
hoistPriority: hoistPriority || 0,
|
|
597
|
-
|
|
641
|
+
isWorkspace: isWorkspace || false,
|
|
642
|
+
hoistedFrom: new Map(),
|
|
643
|
+
hoistedTo: new Map(),
|
|
598
644
|
};
|
|
599
645
|
seenNodes.set(node, workNode);
|
|
600
646
|
}
|
|
@@ -698,7 +744,7 @@ const buildPreferenceMap = (rootNode) => {
|
|
|
698
744
|
seenNodes.add(node);
|
|
699
745
|
for (const dep of node.dependencies.values()) {
|
|
700
746
|
const entry = getOrCreatePreferenceEntry(dep);
|
|
701
|
-
entry.hoistPriority = Math.max(entry.hoistPriority,
|
|
747
|
+
entry.hoistPriority = Math.max(entry.hoistPriority, dep.hoistPriority);
|
|
702
748
|
if (node.peerNames.has(dep.name)) {
|
|
703
749
|
entry.peerDependents.add(node.ident);
|
|
704
750
|
}
|
|
@@ -714,6 +760,8 @@ const buildPreferenceMap = (rootNode) => {
|
|
|
714
760
|
return preferenceMap;
|
|
715
761
|
};
|
|
716
762
|
const prettyPrintLocator = (locator) => {
|
|
763
|
+
if (!locator)
|
|
764
|
+
return `none`;
|
|
717
765
|
const idx = locator.indexOf(`@`, 1);
|
|
718
766
|
let name = locator.substring(0, idx);
|
|
719
767
|
if (name.endsWith(`$wsroot$`))
|
|
@@ -761,7 +809,8 @@ const dumpDepTree = (tree) => {
|
|
|
761
809
|
if (!pkg.peerNames.has(dep.name) && dep !== pkg) {
|
|
762
810
|
const reason = pkg.reasons.get(dep.name);
|
|
763
811
|
const identName = getIdentName(dep.locator);
|
|
764
|
-
|
|
812
|
+
const hoistedFrom = pkg.hoistedFrom.get(dep.name) || [];
|
|
813
|
+
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`;
|
|
765
814
|
str += dumpPackage(dep, parents, `${prefix}${idx < dependencies.length - 1 ? `│ ` : ` `}`);
|
|
766
815
|
}
|
|
767
816
|
}
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yarnpkg/nm",
|
|
3
|
-
"version": "3.0.0-rc.
|
|
3
|
+
"version": "3.0.0-rc.10",
|
|
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.
|
|
10
|
-
"@yarnpkg/fslib": "^2.
|
|
9
|
+
"@yarnpkg/core": "^3.1.0-rc.10",
|
|
10
|
+
"@yarnpkg/fslib": "^2.6.0-rc.9"
|
|
11
11
|
},
|
|
12
12
|
"devDependencies": {
|
|
13
|
-
"@yarnpkg/pnp": "3.0
|
|
13
|
+
"@yarnpkg/pnp": "^3.1.0-rc.6"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"postpack": "rm -rf lib",
|