or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

tessl/npm-lerna--run-topologically

Executes operations on packages in topological order with configurable concurrency and dependency graph support

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@lerna/run-topologically@6.4.x

To install, run

npx @tessl/cli install tessl/npm-lerna--run-topologically@6.4.0

0

# @lerna/run-topologically

1

2

@lerna/run-topologically is an internal Lerna utility that executes operations on packages in maximally-saturated topological order. It orchestrates package processing by building a dependency graph and running tasks in parallel while respecting dependency relationships, using a queue-based approach with configurable concurrency.

3

4

## Package Information

5

6

- **Package Name**: @lerna/run-topologically

7

- **Package Type**: npm

8

- **Language**: JavaScript/TypeScript (CommonJS)

9

- **Installation**: This is an internal Lerna tool, install via `npm install lerna`

10

11

## Core Imports

12

13

```javascript

14

const { runTopologically } = require("@lerna/run-topologically");

15

```

16

17

ES modules import (if supported by your environment):

18

19

```javascript

20

import { runTopologically } from "@lerna/run-topologically";

21

```

22

23

## Basic Usage

24

25

```javascript

26

const { runTopologically } = require("@lerna/run-topologically");

27

28

// Execute a build operation on packages in dependency order

29

const packages = [/* array of Package instances */];

30

31

const buildResults = await runTopologically(

32

packages,

33

async (pkg) => {

34

console.log(`Building ${pkg.name}...`);

35

// Perform build operation

36

return { name: pkg.name, success: true };

37

},

38

{ concurrency: 4 }

39

);

40

41

console.log("All builds completed:", buildResults);

42

```

43

44

## Architecture

45

46

The package is built around these key components:

47

48

- **Topological Execution**: Processes packages in dependency order using graph traversal with maximally-saturated parallel execution

49

- **Concurrency Control**: Uses `p-queue` to limit parallel execution while respecting dependencies (default: unlimited concurrency)

50

- **Dependency Graph**: Leverages `@lerna/query-graph` for building and traversing package dependency relationships, with support for both `allDependencies` and `dependencies` graph types

51

- **Cycle Handling**: Supports collapsing and processing dependency cycles when `rejectCycles` is false

52

- **Promise Aggregation**: Collects and returns all results from runner executions in completion order

53

- **Recursive Queuing**: Automatically queues next available packages as dependencies are satisfied

54

55

## Capabilities

56

57

### Topological Package Processing

58

59

Executes operations on packages in dependency order with controlled concurrency.

60

61

```javascript { .api }

62

/**

63

* Run callback in maximally-saturated topological order.

64

* @template T

65

* @param {import("@lerna/package").Package[]} packages - List of Package instances from @lerna/package

66

* @param {(pkg: import("@lerna/package").Package) => Promise<T>} runner - Callback function (pkg: Package) => Promise<T>

67

* @param {TopologicalConfig} [options] - Configuration options with destructuring defaults

68

* @returns {Promise<T[]>} Array of values returned by runner callbacks

69

*/

70

function runTopologically(packages, runner, { concurrency, graphType, rejectCycles } = {});

71

```

72

73

**Parameters:**

74

75

- `packages`: Array of `Package` instances from `@lerna/package`

76

- `runner`: Async callback function that processes each package and returns a value

77

- `options`: Optional configuration object

78

79

**Options Configuration:**

80

81

```javascript { .api }

82

/**

83

* Configuration object for topological execution

84

* @typedef {import("@lerna/query-graph").QueryGraphConfig & { concurrency: number }} TopologicalConfig

85

* @property {number} [concurrency] - Maximum number of concurrent operations (default: Infinity)

86

* @property {'allDependencies'|'dependencies'} [graphType='allDependencies'] - Type of dependencies to consider

87

* @property {boolean} [rejectCycles] - Whether to reject dependency cycles

88

*/

89

```

90

91

**Usage Examples:**

92

93

```javascript

94

const { runTopologically } = require("@lerna/run-topologically");

95

96

// Basic usage with default options (unlimited concurrency)

97

const results = await runTopologically(

98

packages,

99

async (pkg) => {

100

// Process package

101

return pkg.name;

102

}

103

);

104

105

// With concurrency control

106

const buildResults = await runTopologically(

107

packages,

108

async (pkg) => {

109

console.log(`Building ${pkg.name}...`);

110

// Simulate build process

111

await new Promise(resolve => setTimeout(resolve, 1000));

112

return { package: pkg.name, buildTime: Date.now() };

113

},

114

{ concurrency: 2 }

115

);

116

117

// With specific dependency graph type and cycle rejection

118

const testResults = await runTopologically(

119

packages,

120

async (pkg) => {

121

// Run tests only considering production dependencies

122

return runTests(pkg);

123

},

124

{

125

graphType: 'dependencies', // Exclude devDependencies from graph

126

concurrency: 4,

127

rejectCycles: true // Reject if cycles detected

128

}

129

);

130

131

// Example with error handling

132

try {

133

const publishResults = await runTopologically(

134

packages,

135

async (pkg) => {

136

if (pkg.private) {

137

return { name: pkg.name, skipped: true };

138

}

139

// Perform publish operation

140

return { name: pkg.name, published: true };

141

},

142

{ concurrency: 1, rejectCycles: true }

143

);

144

} catch (error) {

145

console.error("Topological execution failed:", error.message);

146

}

147

```

148

149

## Types

150

151

```javascript { .api }

152

/**

153

* Configuration object extending QueryGraphConfig with concurrency control

154

* @typedef {import("@lerna/query-graph").QueryGraphConfig & { concurrency: number }} TopologicalConfig

155

* @property {number} [concurrency] - Maximum number of concurrent operations

156

* @property {'allDependencies'|'dependencies'} [graphType='allDependencies'] - Dependencies to consider

157

* @property {boolean} [rejectCycles] - Whether to reject dependency cycles

158

*/

159

160

/**

161

* Package instance from @lerna/package

162

* @typedef {Object} Package

163

* @property {string} name - Package name

164

* @property {string} version - Package version

165

* @property {string} location - Package location on filesystem

166

* @property {string} manifestLocation - Path to package.json file

167

* @property {string} nodeModulesLocation - Path to node_modules directory

168

* @property {string} binLocation - Path to .bin directory

169

* @property {string} contents - Path to package contents (may differ from location if publishConfig.directory is set)

170

* @property {boolean} private - Whether package is marked as private

171

* @property {Object} resolved - npm-package-arg resolved metadata

172

* @property {string} rootPath - Root path of the monorepo

173

* @property {Object} scripts - Package scripts from package.json

174

* @property {Object} bin - Package bin entries

175

* @property {Object} [dependencies] - Production dependencies

176

* @property {Object} [devDependencies] - Development dependencies

177

* @property {Object} [optionalDependencies] - Optional dependencies

178

* @property {Object} [peerDependencies] - Peer dependencies

179

* @property {function} get - Get arbitrary property from package.json

180

* @property {function} set - Set arbitrary property in package.json

181

* @property {function} toJSON - Get shallow copy of package.json

182

* @property {function} refresh - Refresh package state from disk

183

* @property {function} serialize - Write package.json changes to disk

184

*/

185

```

186

187

## Implementation Details

188

189

**Execution Flow:**

190

1. Creates a `PQueue` instance with specified concurrency limit

191

2. Builds a `QueryGraph` from packages using the specified graph type

192

3. Recursively processes available packages (those with satisfied dependencies):

193

- Marks packages as "taken" to prevent duplicate processing

194

- Queues runner execution for each available package

195

- Marks packages as "done" upon completion

196

- Automatically queues newly available packages

197

4. Returns collected results when the queue becomes idle

198

199

**Performance Characteristics:**

200

- **Maximally-saturated execution**: Runs as many packages in parallel as possible within dependency constraints

201

- **Memory efficient**: Uses iterative queuing rather than pre-computing entire execution plan

202

- **Cycle aware**: Handles dependency cycles gracefully when `rejectCycles` is false

203

204

## Error Handling

205

206

The function will reject if:

207

208

- The runner callback throws an error or returns a rejected promise

209

- Dependency cycles are detected when `rejectCycles: true` is set

210

- Invalid package dependency relationships are encountered

211

212

```javascript

213

try {

214

const results = await runTopologically(packages, runner, options);

215

} catch (error) {

216

console.error("Topological execution failed:", error.message);

217

}

218

```

219

220

**Common Error Scenarios:**

221

- Runner function failures propagate immediately, stopping execution

222

- Cycle detection with `rejectCycles: true` prevents execution start

223

- Invalid package objects or missing dependencies cause initialization errors