or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-pnpm--hoist

Hoists dependencies in a node_modules created by pnpm

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@pnpm/hoist@1002.0.x

To install, run

npx @tessl/cli install tessl/npm-pnpm--hoist@1002.0.0

index.mddocs/

@pnpm/hoist

@pnpm/hoist provides sophisticated dependency hoisting algorithms for pnpm-managed node_modules directories. It implements logic to move dependencies up the directory tree hierarchy to reduce duplication and improve module resolution performance, supporting both public and private hoisting patterns with configurable matching rules.

Package Information

  • Package Name: @pnpm/hoist
  • Package Type: npm
  • Language: TypeScript
  • Installation: pnpm add @pnpm/hoist

Core Imports

import { hoist, getHoistedDependencies, graphWalker } from "@pnpm/hoist";
import type { 
  HoistOpts, 
  DependenciesGraph, 
  DirectDependenciesByImporterId,
  HoistedWorkspaceProject 
} from "@pnpm/hoist";

For CommonJS:

const { hoist, getHoistedDependencies, graphWalker } = require("@pnpm/hoist");

Basic Usage

import { hoist } from "@pnpm/hoist";
import type { HoistOpts, DependenciesGraph } from "@pnpm/hoist";
import type { ProjectId } from "@pnpm/types";

// Basic hoisting operation
const graph: DependenciesGraph<string> = {
  // ... dependency graph data
};

const hoistedDeps = await hoist({
  graph,
  directDepsByImporterId: {
    '.': new Map([['lodash', 'lodash@4.17.21']]),
  },
  importerIds: ['.' as ProjectId],
  privateHoistPattern: ['*'],
  privateHoistedModulesDir: '/path/to/node_modules/.pnpm',
  publicHoistPattern: ['lodash'],
  publicHoistedModulesDir: '/path/to/node_modules',
  virtualStoreDir: '/path/to/.pnpm',
  virtualStoreDirMaxLength: 120,
  skipped: new Set(),
});

Architecture

@pnpm/hoist is built around several key components:

  • Hoisting Engine: Core algorithms that determine which dependencies should be hoisted and where
  • Pattern Matching: Configurable glob patterns to control public vs private hoisting behavior
  • Graph Analysis: Dependency graph traversal and analysis utilities
  • Symlink Management: Safe creation and management of symbolic links in the filesystem
  • Binary Linking: Automatic linking of executable binaries from hoisted packages

Capabilities

Main Hoisting Function

Performs complete dependency hoisting with symlink creation and binary linking.

/**
 * Performs dependency hoisting with symlink creation and binary linking
 * @param opts - Configuration options for hoisting operation
 * @returns Promise resolving to hoisted dependencies data or null if no hoisting needed
 */
async function hoist<T extends string>(opts: HoistOpts<T>): Promise<HoistedDependencies | null>;

interface HoistOpts<T extends string> extends GetHoistedDependenciesOpts<T> {
  /** Additional Node.js paths for binary resolution */
  extraNodePath?: string[];
  /** Whether to prefer symlinked executables over copied ones */
  preferSymlinkedExecutables?: boolean;
  /** Path to the virtual store directory */
  virtualStoreDir: string;
  /** Maximum length allowed for virtual store directory paths */
  virtualStoreDirMaxLength: number;
}

Hoisting Analysis

Computes which dependencies should be hoisted without creating symlinks.

/**
 * Computes which dependencies should be hoisted without actually creating symlinks
 * @param opts - Configuration options for hoisting analysis
 * @returns Hoisting analysis result or null if no dependencies to analyze
 */
function getHoistedDependencies<T extends string>(opts: GetHoistedDependenciesOpts<T>): {
  hoistedDependencies: HoistedDependencies;
  hoistedDependenciesByNodeId: Map<T | ProjectId, Record<string, 'public' | 'private'>>;
  hoistedAliasesWithBins: string[];
} | null;

interface GetHoistedDependenciesOpts<T extends string> {
  /** The dependency graph to analyze */
  graph: DependenciesGraph<T>;
  /** Set of dependency paths to skip */
  skipped: Set<DepPath>;
  /** Maps importer IDs to their direct dependencies */
  directDepsByImporterId: DirectDependenciesByImporterId<T>;
  /** Optional list of importer IDs to process */
  importerIds?: ProjectId[];
  /** Glob patterns for private hoisting (hoisted to .pnpm directory) */
  privateHoistPattern: string[];
  /** Directory for privately hoisted modules */
  privateHoistedModulesDir: string;
  /** Glob patterns for public hoisting (hoisted to root node_modules) */
  publicHoistPattern: string[];
  /** Directory for publicly hoisted modules */
  publicHoistedModulesDir: string;
  /** Optional workspace packages that should be hoisted */
  hoistedWorkspacePackages?: Record<ProjectId, HoistedWorkspaceProject>;
}

Graph Traversal

Creates a walker for efficient dependency graph traversal.

/**
 * Creates a walker for traversing the dependency graph
 * @param graph - The dependency graph to traverse
 * @param directDepsByImporterId - Maps importer IDs to their direct dependencies
 * @param opts - Optional traversal configuration
 * @returns Graph walker instance for step-by-step traversal
 */
function graphWalker<T extends string>(
  graph: DependenciesGraph<T>,
  directDepsByImporterId: DirectDependenciesByImporterId<T>,
  opts?: {
    include?: { [dependenciesField in DependenciesField]: boolean };
    skipped?: Set<DepPath>;
  }
): GraphWalker<T>;

interface GraphWalker<T extends string> {
  /** Direct dependencies found during graph analysis */
  directDeps: Array<{
    alias: string;
    nodeId: T;
  }>;
  /** Initial step for graph traversal */
  step: GraphWalkerStep<T>;
}

interface GraphWalkerStep<T extends string> {
  /** Dependencies found at this step */
  dependencies: Array<GraphDependency<T>>;
  /** Linked dependencies found at this step */
  links: string[];
  /** Missing dependencies found at this step */
  missing: string[];
}

interface GraphDependency<T extends string> {
  /** Node ID for this dependency */
  nodeId: T;
  /** The dependency graph node */
  node: DependenciesGraphNode<T>;
  /** Function to get the next step in traversal */
  next: () => GraphWalkerStep<T>;
}

Core Types

Dependency Graph Structure

interface DependenciesGraphNode<T extends string> {
  /** Directory path where the dependency is located */
  dir: string;
  /** Child dependencies mapped by alias to node ID */
  children: Record<string, T>;
  /** Set of optional dependency aliases */
  optionalDependencies: Set<string>;
  /** Whether this dependency has executable binaries */
  hasBin: boolean;
  /** Package name */
  name: string;
  /** Dependency path identifier */
  depPath: DepPath;
}

type DependenciesGraph<T extends string> = Record<T, DependenciesGraphNode<T>>;

interface DirectDependenciesByImporterId<T extends string> {
  [importerId: string]: Map<string, T>;
}

Workspace Support

interface HoistedWorkspaceProject {
  /** Package name */
  name: string;
  /** Directory path of the workspace project */
  dir: string;
}

Dependency Representation

interface Dependency<T extends string> {
  /** Child dependencies mapped by alias to node ID or project ID */
  children: Record<string, T | ProjectId>;
  /** Node ID for this dependency */
  nodeId: T;
  /** Depth level in the dependency tree */
  depth: number;
}

Imported Types

These types are imported from @pnpm/types:

type DepPath = string & { __brand: 'DepPath' };
type ProjectId = string & { __brand: 'ProjectId' };
type DependenciesField = 'optionalDependencies' | 'dependencies' | 'devDependencies';
type HoistedDependencies = Record<DepPath | ProjectId, Record<string, 'public' | 'private'>>;

Error Handling

The hoisting functions handle various error conditions:

  • Missing Dependencies: Logged as debug messages, do not cause failures
  • Symlink Conflicts: Existing symlinks are safely replaced if they point to the virtual store
  • External Symlinks: External symlinks are preserved and not overwritten
  • Directory Conflicts: Regular directories at target locations are preserved
  • Binary Linking Errors: Binary linking failures are caught and logged but don't prevent hoisting

Usage Examples

Basic Public Hoisting

import { hoist } from "@pnpm/hoist";

const result = await hoist({
  graph: dependencyGraph,
  directDepsByImporterId: { '.': directDeps },
  importerIds: ['.'],
  privateHoistPattern: [],
  privateHoistedModulesDir: '/project/node_modules/.pnpm',
  publicHoistPattern: ['lodash', 'react*'],
  publicHoistedModulesDir: '/project/node_modules',
  virtualStoreDir: '/project/node_modules/.pnpm',
  virtualStoreDirMaxLength: 120,
  skipped: new Set(),
});

Analysis Only (No Symlinks)

import { getHoistedDependencies } from "@pnpm/hoist";

const analysis = getHoistedDependencies({
  graph: dependencyGraph,
  directDepsByImporterId: { '.': directDeps },
  privateHoistPattern: ['*'],
  privateHoistedModulesDir: '/project/node_modules/.pnpm',
  publicHoistPattern: ['popular-*'],
  publicHoistedModulesDir: '/project/node_modules',
  skipped: new Set(),
});

Graph Traversal

import { graphWalker } from "@pnpm/hoist";

const walker = graphWalker(graph, directDepsByImporterId, {
  include: { 
    dependencies: true, 
    devDependencies: false,
    optionalDependencies: true,
    peerDependencies: true
  }
});

// Process direct dependencies
for (const { alias, nodeId } of walker.directDeps) {
  console.log(`Direct dependency: ${alias} -> ${nodeId}`);
}

// Walk through dependency steps
let currentStep = walker.step;
while (currentStep.dependencies.length > 0) {
  for (const dep of currentStep.dependencies) {
    console.log(`Processing: ${dep.nodeId}`);
    currentStep = dep.next();
  }
}