Skip to content

Refactors the nm hoister to avoid tree structures #6852

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const mte = generatePkgDriver({
const yarnBinary = process.env.TEST_BINARY
?? require.resolve(`${__dirname}/../../../../yarnpkg-cli/bundles/yarn.js`);

const yarnBinaryArgs = yarnBinary.match(/\.[cm]js$/)
const yarnBinaryArgs = yarnBinary.match(/\.[cm]?js$/)
? [process.execPath, yarnBinary]
: [yarnBinary];

Expand Down
76 changes: 44 additions & 32 deletions packages/yarnpkg-nm/sources/buildNodeModulesTree.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {structUtils, Project, MessageName, Locator} from '@yarnpkg/core';
import {npath, ppath} from '@yarnpkg/fslib';
import {NativePath, PortablePath, Filename} from '@yarnpkg/fslib';
import {PnpApi, PhysicalPackageLocator, PackageInformation, DependencyTarget} from '@yarnpkg/pnp';
import {structUtils, Project, MessageName, Locator} from '@yarnpkg/core';
import {npath, ppath} from '@yarnpkg/fslib';
import {NativePath, PortablePath, Filename} from '@yarnpkg/fslib';
import {PnpApi, PhysicalPackageLocator, PackageInformation, DependencyTarget} from '@yarnpkg/pnp';

import {hoist, HoisterTree, HoisterResult, HoisterDependencyKind} from './hoist';
import {hoist, HoisterNode, HoisterResult, HoisterDependencyKind, HoisterTree} from './hoist';

// Babel doesn't support const enums, thats why we use non-const enum for LinkType in @yarnpkg/pnp
// But because of this TypeScript requires @yarnpkg/pnp during runtime
Expand Down Expand Up @@ -278,38 +278,37 @@ const buildPackageTree = (pnp: PnpApi, options: NodeModulesTreeOptions): {packag

const topPkgPortableLocation = npath.toPortablePath(topPkg.packageLocation.slice(0, -1));

const packageTree: HoisterTree = {
name: topLocator.name,
identName: topLocator.name,
reference: topLocator.reference,
peerNames: topPkg.packagePeers,
dependencies: new Set<HoisterTree>(),
dependencyKind: HoisterDependencyKind.WORKSPACE,
const treeNodes: Array<HoisterNode> = [];
const nodes = new Map<string, number>();

const getNodeKey = (name: string, locator: PhysicalPackageLocator) => {
return `${stringifyLocator(locator)}:${name}`;
};

const nodes = new Map<string, HoisterTree>();
const getNodeKey = (name: string, locator: PhysicalPackageLocator) => `${stringifyLocator(locator)}:${name}`;
const addPackageToTree = (name: string, pkg: PackageInformation<NativePath>, locator: PhysicalPackageLocator, parent: HoisterTree, parentPkg: PackageInformation<NativePath>, parentDependencies: Map<string, DependencyTarget>, parentRelativeCwd: PortablePath, isHoistBorder: boolean) => {
const addPackageToTree = (name: string, pkg: PackageInformation<NativePath>, locator: PhysicalPackageLocator, parentId: number, parentPkg: PackageInformation<NativePath>, parentDependencies: Map<string, DependencyTarget>, parentRelativeCwd: PortablePath, isHoistBorder: boolean) => {
const nodeKey = getNodeKey(name, locator);
let node = nodes.get(nodeKey);
let nodeId = nodes.get(nodeKey);

const isSeen = !!node;
const isSeen = typeof nodeId !== `undefined`;
if (!isSeen && locator.name === topLocator.name && locator.reference === topLocator.reference) {
node = packageTree;
nodes.set(nodeKey, packageTree);
nodeId = 0;
nodes.set(nodeKey, 0);
}

const isExternalSoftLinkPackage = isExternalSoftLink(pkg, locator, pnp, topPkgPortableLocation);

if (!node) {
if (!nodeId) {
let dependencyKind = HoisterDependencyKind.REGULAR;
if (isExternalSoftLinkPackage)
dependencyKind = HoisterDependencyKind.EXTERNAL_SOFT_LINK;
else if (pkg.linkType === LinkType.SOFT && locator.name.endsWith(WORKSPACE_NAME_SUFFIX))
dependencyKind = HoisterDependencyKind.WORKSPACE;

nodeId = treeNodes.length;
nodes.set(nodeKey, nodeId);

node = {
treeNodes.push({
id: nodeId,
name,
identName: locator.name,
reference: locator.reference,
Expand All @@ -318,12 +317,10 @@ const buildPackageTree = (pnp: PnpApi, options: NodeModulesTreeOptions): {packag
// (meeting workspace peer dependency constraints is sometimes hard, sometimes impossible for the nm linker)
peerNames: dependencyKind === HoisterDependencyKind.WORKSPACE ? new Set() : pkg.packagePeers,
dependencyKind,
};

nodes.set(nodeKey, node);
});
}

let hoistPriority;
let hoistPriority: number;
if (isExternalSoftLinkPackage)
// External soft link dependencies have the highest priority - we don't want to install inside them
hoistPriority = 2;
Expand All @@ -332,11 +329,16 @@ const buildPackageTree = (pnp: PnpApi, options: NodeModulesTreeOptions): {packag
hoistPriority = 1;
else
hoistPriority = 0;
node.hoistPriority = Math.max(node.hoistPriority || 0, hoistPriority);

const node = treeNodes[nodeId];
node.hoistPriority = Math.max(node.hoistPriority ?? 0, hoistPriority);

const parent = treeNodes[parentId];

if (isHoistBorder && !isExternalSoftLinkPackage) {
const parentLocatorKey = stringifyLocator({name: parent.identName, reference: parent.reference});
const dependencyBorders = hoistingLimits.get(parentLocatorKey) || new Set();
const dependencyBorders = hoistingLimits.get(parentLocatorKey) ?? new Set();

hoistingLimits.set(parentLocatorKey, dependencyBorders);
dependencyBorders.add(node.name);
}
Expand All @@ -350,16 +352,21 @@ const buildPackageTree = (pnp: PnpApi, options: NodeModulesTreeOptions): {packag
...Array.from(workspace.manifest.peerDependencies.values(), x => structUtils.stringifyIdent(x)),
...Array.from(workspace.manifest.peerDependenciesMeta.keys()),
]);

for (const peerName of peerCandidates) {
if (!allDependencies.has(peerName)) {
allDependencies.set(peerName, parentDependencies.get(peerName) || null);
allDependencies.set(peerName, parentDependencies.get(peerName) ?? null);
node.peerNames.add(peerName);
}
}
}
}

const locatorKey = stringifyLocator({name: locator.name.replace(WORKSPACE_NAME_SUFFIX, ``), reference: locator.reference});
const locatorKey = stringifyLocator({
name: locator.name.replace(WORKSPACE_NAME_SUFFIX, ``),
reference: locator.reference,
});

const innerWorkspaces = workspaceMap.get(locatorKey);
if (innerWorkspaces) {
for (const workspaceLocator of innerWorkspaces) {
Expand All @@ -368,7 +375,7 @@ const buildPackageTree = (pnp: PnpApi, options: NodeModulesTreeOptions): {packag
}

if (pkg !== parentPkg || pkg.linkType !== LinkType.SOFT || (!isExternalSoftLinkPackage && (!options.selfReferencesByCwd || options.selfReferencesByCwd.get(parentRelativeCwd))))
parent.dependencies.add(node);
parent.dependencies.add(nodeId);

const isWorkspaceDependency = locator !== topLocator && pkg.linkType === LinkType.SOFT && !locator.name.endsWith(WORKSPACE_NAME_SUFFIX) && !isExternalSoftLinkPackage;

Expand Down Expand Up @@ -436,13 +443,18 @@ const buildPackageTree = (pnp: PnpApi, options: NodeModulesTreeOptions): {packag
|| depHoistingLimits === NodeModulesHoistingLimits.DEPENDENCIES
|| depHoistingLimits === NodeModulesHoistingLimits.WORKSPACES;

addPackageToTree(depName, depPkg, depLocator, node, pkg, allDependencies, relativeDepCwd, isHoistBorder);
addPackageToTree(depName, depPkg, depLocator, nodeId, pkg, allDependencies, relativeDepCwd, isHoistBorder);
}
}
}
};

addPackageToTree(topLocator.name, topPkg, topLocator, packageTree, topPkg, topPkg.packageDependencies, PortablePath.dot, false);
addPackageToTree(topLocator.name, topPkg, topLocator, 0, topPkg, topPkg.packageDependencies, PortablePath.dot, false);

const packageTree: HoisterTree = {
nodes: treeNodes,
root: 0,
};

return {packageTree, hoistingLimits, errors, preserveSymlinksRequired};
};
Expand Down
Loading
Loading