or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.md

index.mddocs/

0

# @pnpm/hoist

1

2

@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.

3

4

## Package Information

5

6

- **Package Name**: @pnpm/hoist

7

- **Package Type**: npm

8

- **Language**: TypeScript

9

- **Installation**: `pnpm add @pnpm/hoist`

10

11

## Core Imports

12

13

```typescript

14

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

15

import type {

16

HoistOpts,

17

DependenciesGraph,

18

DirectDependenciesByImporterId,

19

HoistedWorkspaceProject

20

} from "@pnpm/hoist";

21

```

22

23

For CommonJS:

24

25

```javascript

26

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

27

```

28

29

## Basic Usage

30

31

```typescript

32

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

33

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

34

import type { ProjectId } from "@pnpm/types";

35

36

// Basic hoisting operation

37

const graph: DependenciesGraph<string> = {

38

// ... dependency graph data

39

};

40

41

const hoistedDeps = await hoist({

42

graph,

43

directDepsByImporterId: {

44

'.': new Map([['lodash', 'lodash@4.17.21']]),

45

},

46

importerIds: ['.' as ProjectId],

47

privateHoistPattern: ['*'],

48

privateHoistedModulesDir: '/path/to/node_modules/.pnpm',

49

publicHoistPattern: ['lodash'],

50

publicHoistedModulesDir: '/path/to/node_modules',

51

virtualStoreDir: '/path/to/.pnpm',

52

virtualStoreDirMaxLength: 120,

53

skipped: new Set(),

54

});

55

```

56

57

## Architecture

58

59

@pnpm/hoist is built around several key components:

60

61

- **Hoisting Engine**: Core algorithms that determine which dependencies should be hoisted and where

62

- **Pattern Matching**: Configurable glob patterns to control public vs private hoisting behavior

63

- **Graph Analysis**: Dependency graph traversal and analysis utilities

64

- **Symlink Management**: Safe creation and management of symbolic links in the filesystem

65

- **Binary Linking**: Automatic linking of executable binaries from hoisted packages

66

67

## Capabilities

68

69

### Main Hoisting Function

70

71

Performs complete dependency hoisting with symlink creation and binary linking.

72

73

```typescript { .api }

74

/**

75

* Performs dependency hoisting with symlink creation and binary linking

76

* @param opts - Configuration options for hoisting operation

77

* @returns Promise resolving to hoisted dependencies data or null if no hoisting needed

78

*/

79

async function hoist<T extends string>(opts: HoistOpts<T>): Promise<HoistedDependencies | null>;

80

81

interface HoistOpts<T extends string> extends GetHoistedDependenciesOpts<T> {

82

/** Additional Node.js paths for binary resolution */

83

extraNodePath?: string[];

84

/** Whether to prefer symlinked executables over copied ones */

85

preferSymlinkedExecutables?: boolean;

86

/** Path to the virtual store directory */

87

virtualStoreDir: string;

88

/** Maximum length allowed for virtual store directory paths */

89

virtualStoreDirMaxLength: number;

90

}

91

```

92

93

### Hoisting Analysis

94

95

Computes which dependencies should be hoisted without creating symlinks.

96

97

```typescript { .api }

98

/**

99

* Computes which dependencies should be hoisted without actually creating symlinks

100

* @param opts - Configuration options for hoisting analysis

101

* @returns Hoisting analysis result or null if no dependencies to analyze

102

*/

103

function getHoistedDependencies<T extends string>(opts: GetHoistedDependenciesOpts<T>): {

104

hoistedDependencies: HoistedDependencies;

105

hoistedDependenciesByNodeId: Map<T | ProjectId, Record<string, 'public' | 'private'>>;

106

hoistedAliasesWithBins: string[];

107

} | null;

108

109

interface GetHoistedDependenciesOpts<T extends string> {

110

/** The dependency graph to analyze */

111

graph: DependenciesGraph<T>;

112

/** Set of dependency paths to skip */

113

skipped: Set<DepPath>;

114

/** Maps importer IDs to their direct dependencies */

115

directDepsByImporterId: DirectDependenciesByImporterId<T>;

116

/** Optional list of importer IDs to process */

117

importerIds?: ProjectId[];

118

/** Glob patterns for private hoisting (hoisted to .pnpm directory) */

119

privateHoistPattern: string[];

120

/** Directory for privately hoisted modules */

121

privateHoistedModulesDir: string;

122

/** Glob patterns for public hoisting (hoisted to root node_modules) */

123

publicHoistPattern: string[];

124

/** Directory for publicly hoisted modules */

125

publicHoistedModulesDir: string;

126

/** Optional workspace packages that should be hoisted */

127

hoistedWorkspacePackages?: Record<ProjectId, HoistedWorkspaceProject>;

128

}

129

```

130

131

### Graph Traversal

132

133

Creates a walker for efficient dependency graph traversal.

134

135

```typescript { .api }

136

/**

137

* Creates a walker for traversing the dependency graph

138

* @param graph - The dependency graph to traverse

139

* @param directDepsByImporterId - Maps importer IDs to their direct dependencies

140

* @param opts - Optional traversal configuration

141

* @returns Graph walker instance for step-by-step traversal

142

*/

143

function graphWalker<T extends string>(

144

graph: DependenciesGraph<T>,

145

directDepsByImporterId: DirectDependenciesByImporterId<T>,

146

opts?: {

147

include?: { [dependenciesField in DependenciesField]: boolean };

148

skipped?: Set<DepPath>;

149

}

150

): GraphWalker<T>;

151

152

interface GraphWalker<T extends string> {

153

/** Direct dependencies found during graph analysis */

154

directDeps: Array<{

155

alias: string;

156

nodeId: T;

157

}>;

158

/** Initial step for graph traversal */

159

step: GraphWalkerStep<T>;

160

}

161

162

interface GraphWalkerStep<T extends string> {

163

/** Dependencies found at this step */

164

dependencies: Array<GraphDependency<T>>;

165

/** Linked dependencies found at this step */

166

links: string[];

167

/** Missing dependencies found at this step */

168

missing: string[];

169

}

170

171

interface GraphDependency<T extends string> {

172

/** Node ID for this dependency */

173

nodeId: T;

174

/** The dependency graph node */

175

node: DependenciesGraphNode<T>;

176

/** Function to get the next step in traversal */

177

next: () => GraphWalkerStep<T>;

178

}

179

```

180

181

## Core Types

182

183

### Dependency Graph Structure

184

185

```typescript { .api }

186

interface DependenciesGraphNode<T extends string> {

187

/** Directory path where the dependency is located */

188

dir: string;

189

/** Child dependencies mapped by alias to node ID */

190

children: Record<string, T>;

191

/** Set of optional dependency aliases */

192

optionalDependencies: Set<string>;

193

/** Whether this dependency has executable binaries */

194

hasBin: boolean;

195

/** Package name */

196

name: string;

197

/** Dependency path identifier */

198

depPath: DepPath;

199

}

200

201

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

202

203

interface DirectDependenciesByImporterId<T extends string> {

204

[importerId: string]: Map<string, T>;

205

}

206

```

207

208

### Workspace Support

209

210

```typescript { .api }

211

interface HoistedWorkspaceProject {

212

/** Package name */

213

name: string;

214

/** Directory path of the workspace project */

215

dir: string;

216

}

217

```

218

219

### Dependency Representation

220

221

```typescript { .api }

222

interface Dependency<T extends string> {

223

/** Child dependencies mapped by alias to node ID or project ID */

224

children: Record<string, T | ProjectId>;

225

/** Node ID for this dependency */

226

nodeId: T;

227

/** Depth level in the dependency tree */

228

depth: number;

229

}

230

```

231

232

233

## Imported Types

234

235

These types are imported from `@pnpm/types`:

236

237

```typescript { .api }

238

type DepPath = string & { __brand: 'DepPath' };

239

type ProjectId = string & { __brand: 'ProjectId' };

240

type DependenciesField = 'optionalDependencies' | 'dependencies' | 'devDependencies';

241

type HoistedDependencies = Record<DepPath | ProjectId, Record<string, 'public' | 'private'>>;

242

```

243

244

## Error Handling

245

246

The hoisting functions handle various error conditions:

247

248

- **Missing Dependencies**: Logged as debug messages, do not cause failures

249

- **Symlink Conflicts**: Existing symlinks are safely replaced if they point to the virtual store

250

- **External Symlinks**: External symlinks are preserved and not overwritten

251

- **Directory Conflicts**: Regular directories at target locations are preserved

252

- **Binary Linking Errors**: Binary linking failures are caught and logged but don't prevent hoisting

253

254

## Usage Examples

255

256

### Basic Public Hoisting

257

258

```typescript

259

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

260

261

const result = await hoist({

262

graph: dependencyGraph,

263

directDepsByImporterId: { '.': directDeps },

264

importerIds: ['.'],

265

privateHoistPattern: [],

266

privateHoistedModulesDir: '/project/node_modules/.pnpm',

267

publicHoistPattern: ['lodash', 'react*'],

268

publicHoistedModulesDir: '/project/node_modules',

269

virtualStoreDir: '/project/node_modules/.pnpm',

270

virtualStoreDirMaxLength: 120,

271

skipped: new Set(),

272

});

273

```

274

275

### Analysis Only (No Symlinks)

276

277

```typescript

278

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

279

280

const analysis = getHoistedDependencies({

281

graph: dependencyGraph,

282

directDepsByImporterId: { '.': directDeps },

283

privateHoistPattern: ['*'],

284

privateHoistedModulesDir: '/project/node_modules/.pnpm',

285

publicHoistPattern: ['popular-*'],

286

publicHoistedModulesDir: '/project/node_modules',

287

skipped: new Set(),

288

});

289

```

290

291

### Graph Traversal

292

293

```typescript

294

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

295

296

const walker = graphWalker(graph, directDepsByImporterId, {

297

include: {

298

dependencies: true,

299

devDependencies: false,

300

optionalDependencies: true,

301

peerDependencies: true

302

}

303

});

304

305

// Process direct dependencies

306

for (const { alias, nodeId } of walker.directDeps) {

307

console.log(`Direct dependency: ${alias} -> ${nodeId}`);

308

}

309

310

// Walk through dependency steps

311

let currentStep = walker.step;

312

while (currentStep.dependencies.length > 0) {

313

for (const dep of currentStep.dependencies) {

314

console.log(`Processing: ${dep.nodeId}`);

315

currentStep = dep.next();

316

}

317

}

318

```