@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.
- package/lib/buildNodeModulesTree.d.ts +1 -0
- package/lib/buildNodeModulesTree.js +131 -72
- package/lib/hoist.d.ts +6 -0
- package/lib/hoist.js +92 -23
- package/package.json +5 -5
|
@@ -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
|
-
|
|
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
|
-
*
|
|
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
|
|
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
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
135
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
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
|
|
223
|
-
if (
|
|
224
|
-
for (const workspaceLocator of
|
|
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
|
-
|
|
229
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
453
|
-
|
|
454
|
-
|
|
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(`${
|
|
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(`${
|
|
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
|
-
|
|
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
|
-
|
|
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) =>
|
|
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
|
-
|
|
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.
|
|
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.
|
|
10
|
-
"@yarnpkg/fslib": "^2.6.
|
|
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.
|
|
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": "
|
|
36
|
+
"stableVersion": "3.0.0"
|
|
37
37
|
}
|