Comprehensive node_modules tree management library for npm dependency resolution and reification
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
The Link class extends Node to represent symbolic links within node_modules directories. Links delegate dependency resolution to their targets while maintaining their own position in the tree structure.
Creates a new Link instance representing a symbolic link.
/**
* Create a new symbolic link node
* @param options - Link configuration options
*/
constructor(options?: LinkOptions): Link;
interface LinkOptions extends NodeOptions {
/** Link target node */
target?: Node;
/** Real path to link target (required) */
realpath: string;
/** Is store link */
isStoreLink?: boolean;
}Usage Example:
const { Link } = require('@npmcli/arborist');
// Create a link to a target node
const link = new Link({
name: 'my-package',
parent: rootNode,
realpath: '/path/to/actual/package',
target: targetNode
});Properties that distinguish links from regular nodes.
interface LinkSpecificProperties {
/** Always true for Link instances */
isLink: true;
/** Target node that the link points to */
target: Node;
/** Relative path to target (computed property) */
resolved: string;
/** Is store link */
isStoreLink: boolean;
/** Always empty Map (read-only) */
children: Map<string, Node>;
}Usage Examples:
// Check if node is a link
if (node.isLink) {
console.log(`Link target: ${node.target.name}@${node.target.version}`);
console.log(`Target path: ${node.target.path}`);
console.log(`Resolved path: ${node.resolved}`);
}
// Links have no direct children
console.log(`Link children count: ${node.children.size}`); // Always 0
// Access target's children instead
console.log(`Target children: ${Array.from(node.target.children.keys())}`);Links delegate certain operations to their targets, particularly dependency resolution.
/**
* Links inherit most Node methods but delegate resolution to target
* Dependencies are resolved from target location, not link location
*/
interface LinkDelegation {
/** Delegates to target.resolve() */
resolve(name: string): Node | undefined;
/** Target's package.json content */
package: object;
/** Target's version */
version: string;
/** Target's dependency edges */
edgesOut: Map<string, Edge>;
}Usage Example:
// Resolution happens from target location
const dependency = link.resolve('express');
// This is equivalent to:
const dependency2 = link.target.resolve('express');
// Package information comes from target
console.log(`Link package name: ${link.package.name}`);
console.log(`Link version: ${link.version}`);
// Dependencies come from target
console.log(`Link dependencies: ${Array.from(link.edgesOut.keys())}`);Links maintain their own path while pointing to a target with a different path.
interface LinkPathResolution {
/** Path to the link itself */
path: string;
/** Real path to the target */
realpath: string;
/** Computed relative path from link to target */
resolved: string;
}Usage Example:
console.log(`Link path: ${link.path}`); // e.g. /proj/node_modules/pkg
console.log(`Target path: ${link.realpath}`); // e.g. /proj/packages/pkg
console.log(`Resolved path: ${link.resolved}`); // e.g. ../../packages/pkgSpecial type of links used for package stores and global installations.
/**
* Store links are special links used in package stores
* @param isStoreLink - Whether this is a store link
*/
interface StoreLinkProperties {
/** Is this a store link */
isStoreLink: boolean;
}Usage Example:
// Check if this is a store link
if (link.isStoreLink) {
console.log('This is a store link (global or cached package)');
}
// Create a store link
const storeLink = new Link({
name: 'global-package',
realpath: '/usr/local/lib/node_modules/global-package',
target: globalPackageNode,
isStoreLink: true
});Links cannot have direct children - their children map is always empty.
// Regular node can have children
const regularNode = new Node({ name: 'parent' });
regularNode.children.set('child', childNode); // Works
// Link cannot have children
const link = new Link({ name: 'link', target: targetNode });
link.children.set('child', childNode); // children remains empty
console.log(link.children.size); // Always 0Dependencies are resolved from the target's location, not the link's location.
// If link is at /project/node_modules/my-package
// But target is at /project/packages/my-package
// Resolution happens from /project/packages/my-package
const resolved = link.resolve('dependency');
// Uses Node.js resolution from target locationLinks maintain their position in the tree while delegating content to targets.
// Link has its own parent/location
console.log(`Link parent: ${link.parent.name}`);
console.log(`Link location: ${link.location}`);
// But content comes from target
console.log(`Package content: ${link.package.name}`);
console.log(`Dependencies: ${Array.from(link.edgesOut.keys())}`);
// Target might be elsewhere in tree or outside tree entirely
console.log(`Target parent: ${link.target.parent?.name || 'none'}`);Links between packages in a monorepo workspace.
// Package A depends on Package B in same workspace
const workspaceLink = new Link({
name: 'package-b',
parent: packageANode,
realpath: '/workspace/packages/package-b',
target: packageBNode
});Links to globally installed packages.
// Global CLI tool linked into project
const globalLink = new Link({
name: 'some-cli',
parent: rootNode,
realpath: '/usr/local/lib/node_modules/some-cli',
target: globalCliNode,
isStoreLink: true
});Links created during development (e.g., npm link).
// Development package linked for testing
const devLink = new Link({
name: 'my-dev-package',
parent: testProjectNode,
realpath: '/home/dev/my-dev-package',
target: devPackageNode
});Links to packages in a content-addressable store.
// Package from content store
const cacheLink = new Link({
name: 'cached-package',
parent: rootNode,
realpath: '/cache/store/abc123/node_modules/cached-package',
target: cachedPackageNode,
isStoreLink: true
});// Check if a node is a link
if (node.isLink) {
console.log('This is a symbolic link');
console.log(`Points to: ${node.target.path}`);
} else {
console.log('This is a regular package');
}// Get the actual package content
const actualPackage = node.isLink ? node.target : node;
console.log(`Actual package: ${actualPackage.name}@${actualPackage.version}`);// Find what links to a target
if (node.linksIn.size > 0) {
console.log(`This package has ${node.linksIn.size} links pointing to it:`);
for (const link of node.linksIn) {
console.log(` - ${link.location} -> ${node.location}`);
}
}Install with Tessl CLI
npx tessl i tessl/npm-npmcli--arborist