@pnpm/deps.inspection.tree-builder 1001.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +19 -0
- package/lib/DependencyNode.d.ts +28 -0
- package/lib/DependencyNode.js +2 -0
- package/lib/TreeNodeId.d.ts +18 -0
- package/lib/TreeNodeId.js +17 -0
- package/lib/buildDependenciesTree.d.ts +25 -0
- package/lib/buildDependenciesTree.js +184 -0
- package/lib/buildDependencyGraph.d.ts +30 -0
- package/lib/buildDependencyGraph.js +77 -0
- package/lib/buildDependentsTree.d.ts +46 -0
- package/lib/buildDependentsTree.js +309 -0
- package/lib/createPackagesSearcher.d.ts +2 -0
- package/lib/createPackagesSearcher.js +53 -0
- package/lib/getPkgInfo.d.ts +58 -0
- package/lib/getPkgInfo.js +104 -0
- package/lib/getTree.d.ts +53 -0
- package/lib/getTree.js +220 -0
- package/lib/getTreeNodeChildId.d.ts +12 -0
- package/lib/getTreeNodeChildId.js +36 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +5 -0
- package/lib/peersSuffixHash.d.ts +1 -0
- package/lib/peersSuffixHash.js +9 -0
- package/lib/readManifestFromCafs.d.ts +11 -0
- package/lib/readManifestFromCafs.js +26 -0
- package/lib/resolvePackagePath.d.ts +16 -0
- package/lib/resolvePackagePath.js +47 -0
- package/package.json +69 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { normalizeRegistries } from '@pnpm/config.normalize-registries';
|
|
3
|
+
import { readModulesManifest } from '@pnpm/installing.modules-yaml';
|
|
4
|
+
import { getLockfileImporterId, } from '@pnpm/lockfile.fs';
|
|
5
|
+
import { nameVerFromPkgSnapshot } from '@pnpm/lockfile.utils';
|
|
6
|
+
import { StoreIndex } from '@pnpm/store.index';
|
|
7
|
+
import { lexCompare } from '@pnpm/util.lex-comparator';
|
|
8
|
+
import { realpathMissing } from 'realpath-missing';
|
|
9
|
+
import semver from 'semver';
|
|
10
|
+
import { buildDependencyGraph } from './buildDependencyGraph.js';
|
|
11
|
+
import { createPackagesSearcher } from './createPackagesSearcher.js';
|
|
12
|
+
import { getPkgInfo } from './getPkgInfo.js';
|
|
13
|
+
import { peersSuffixHashFromDepPath } from './peersSuffixHash.js';
|
|
14
|
+
export async function buildDependentsTree(packages, projectPaths, opts) {
|
|
15
|
+
const modulesDir = await realpathMissing(path.join(opts.lockfileDir, opts.modulesDir ?? 'node_modules'));
|
|
16
|
+
const modules = await readModulesManifest(modulesDir);
|
|
17
|
+
const registries = normalizeRegistries({
|
|
18
|
+
...opts.registries,
|
|
19
|
+
...modules?.registries,
|
|
20
|
+
});
|
|
21
|
+
const storeDir = modules?.storeDir;
|
|
22
|
+
const storeIndex = storeDir ? new StoreIndex(storeDir) : undefined;
|
|
23
|
+
const virtualStoreDir = modules?.virtualStoreDir ?? path.join(modulesDir, '.pnpm');
|
|
24
|
+
const virtualStoreDirMaxLength = modules?.virtualStoreDirMaxLength ?? 120;
|
|
25
|
+
const include = opts.include ?? {
|
|
26
|
+
dependencies: true,
|
|
27
|
+
devDependencies: true,
|
|
28
|
+
optionalDependencies: true,
|
|
29
|
+
};
|
|
30
|
+
// Build root IDs from the selected project paths (respects --filter / --recursive)
|
|
31
|
+
const allRootIds = [];
|
|
32
|
+
for (const projectPath of projectPaths) {
|
|
33
|
+
const importerId = getLockfileImporterId(opts.lockfileDir, projectPath);
|
|
34
|
+
if (opts.lockfile.importers[importerId]) {
|
|
35
|
+
allRootIds.push({ type: 'importer', importerId });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const graph = buildDependencyGraph(allRootIds, {
|
|
39
|
+
currentPackages: opts.lockfile.packages ?? {},
|
|
40
|
+
importers: opts.lockfile.importers,
|
|
41
|
+
include,
|
|
42
|
+
lockfileDir: opts.lockfileDir,
|
|
43
|
+
});
|
|
44
|
+
const reverseMap = invertGraph(graph);
|
|
45
|
+
const search = createPackagesSearcher(packages, opts.finders);
|
|
46
|
+
const currentPackages = opts.lockfile.packages ?? {};
|
|
47
|
+
// Pre-compute resolved filesystem paths for all package nodes by walking the
|
|
48
|
+
// graph top-down from importers. This is needed for global virtual store
|
|
49
|
+
// where symlinks must be resolved through each parent's node_modules.
|
|
50
|
+
const resolvedPackageNodes = resolvePackageNodes(graph, currentPackages, {
|
|
51
|
+
virtualStoreDir,
|
|
52
|
+
virtualStoreDirMaxLength,
|
|
53
|
+
modulesDir,
|
|
54
|
+
registries,
|
|
55
|
+
wantedPackages: currentPackages,
|
|
56
|
+
storeDir,
|
|
57
|
+
storeIndex,
|
|
58
|
+
});
|
|
59
|
+
// Scan all package nodes for matches.
|
|
60
|
+
// A package matches if any of the aliases used to refer to it (from incoming
|
|
61
|
+
// edges in the graph) or its canonical name match the search query.
|
|
62
|
+
// Each distinct depPath (i.e. different peer dep resolutions) is kept as a
|
|
63
|
+
// separate result so that peer variants are visible in the output.
|
|
64
|
+
const trees = [];
|
|
65
|
+
const ctx = {
|
|
66
|
+
reverseMap,
|
|
67
|
+
graph,
|
|
68
|
+
importers: opts.lockfile.importers,
|
|
69
|
+
currentPackages,
|
|
70
|
+
importerInfoMap: opts.importerInfoMap,
|
|
71
|
+
resolvedPackageNodes,
|
|
72
|
+
nameFormatter: opts.nameFormatter,
|
|
73
|
+
visited: new Set(),
|
|
74
|
+
expanded: new Set(),
|
|
75
|
+
};
|
|
76
|
+
for (const [serialized, node] of graph.nodes) {
|
|
77
|
+
if (node.nodeId.type !== 'package')
|
|
78
|
+
continue;
|
|
79
|
+
const depPath = node.nodeId.depPath;
|
|
80
|
+
const snapshot = currentPackages[depPath];
|
|
81
|
+
if (snapshot == null)
|
|
82
|
+
continue;
|
|
83
|
+
const { name, version } = nameVerFromPkgSnapshot(depPath, snapshot);
|
|
84
|
+
const pkgNode = resolvedPackageNodes.get(serialized);
|
|
85
|
+
if (!pkgNode)
|
|
86
|
+
continue;
|
|
87
|
+
const readManifest = pkgNode.readManifest;
|
|
88
|
+
// Check canonical name first
|
|
89
|
+
let matched = search({ alias: name, name, version, readManifest });
|
|
90
|
+
// Also check aliases from incoming edges (handles npm: protocol aliases)
|
|
91
|
+
if (!matched) {
|
|
92
|
+
const incomingEdges = reverseMap.get(serialized);
|
|
93
|
+
if (incomingEdges) {
|
|
94
|
+
for (const edge of incomingEdges) {
|
|
95
|
+
if (edge.alias !== name) {
|
|
96
|
+
matched = search({ alias: edge.alias, name, version, readManifest });
|
|
97
|
+
if (matched)
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (!matched)
|
|
104
|
+
continue;
|
|
105
|
+
ctx.visited = new Set([serialized]);
|
|
106
|
+
ctx.expanded = new Set();
|
|
107
|
+
const dependents = walkReverse(serialized, ctx);
|
|
108
|
+
const peersSuffixHash = peersSuffixHashFromDepPath(depPath);
|
|
109
|
+
const displayName = opts.nameFormatter
|
|
110
|
+
? opts.nameFormatter({ name, version, manifest: readManifest() })
|
|
111
|
+
: undefined;
|
|
112
|
+
const tree = {
|
|
113
|
+
name,
|
|
114
|
+
displayName,
|
|
115
|
+
version,
|
|
116
|
+
path: pkgNode.path,
|
|
117
|
+
peersSuffixHash,
|
|
118
|
+
dependents,
|
|
119
|
+
};
|
|
120
|
+
if (typeof matched === 'string') {
|
|
121
|
+
tree.searchMessage = matched;
|
|
122
|
+
}
|
|
123
|
+
trees.push(tree);
|
|
124
|
+
}
|
|
125
|
+
trees.sort((a, b) => {
|
|
126
|
+
const nameCmp = lexCompare(a.name, b.name);
|
|
127
|
+
if (nameCmp !== 0)
|
|
128
|
+
return nameCmp;
|
|
129
|
+
const versionCmp = semver.valid(a.version) && semver.valid(b.version)
|
|
130
|
+
? semver.compare(a.version, b.version)
|
|
131
|
+
: lexCompare(a.version, b.version);
|
|
132
|
+
if (versionCmp !== 0)
|
|
133
|
+
return versionCmp;
|
|
134
|
+
return lexCompare(a.peersSuffixHash ?? '', b.peersSuffixHash ?? '');
|
|
135
|
+
});
|
|
136
|
+
storeIndex?.close();
|
|
137
|
+
return trees;
|
|
138
|
+
}
|
|
139
|
+
function invertGraph(graph) {
|
|
140
|
+
const reverse = new Map();
|
|
141
|
+
for (const [parentSerialized, node] of graph.nodes) {
|
|
142
|
+
for (const edge of node.edges) {
|
|
143
|
+
if (edge.target == null)
|
|
144
|
+
continue;
|
|
145
|
+
const childSerialized = edge.target.id;
|
|
146
|
+
let entries = reverse.get(childSerialized);
|
|
147
|
+
if (entries == null) {
|
|
148
|
+
entries = [];
|
|
149
|
+
reverse.set(childSerialized, entries);
|
|
150
|
+
}
|
|
151
|
+
entries.push({
|
|
152
|
+
parentSerialized,
|
|
153
|
+
parentNodeId: node.nodeId,
|
|
154
|
+
alias: edge.alias,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return reverse;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Walks the dependency graph top-down from importer nodes and resolves the
|
|
162
|
+
* filesystem path for every package node. This is necessary for global virtual
|
|
163
|
+
* store where the correct path can only be obtained by following symlinks
|
|
164
|
+
* through each parent's node_modules directory.
|
|
165
|
+
*/
|
|
166
|
+
function resolvePackageNodes(graph, currentPackages, opts) {
|
|
167
|
+
const resolved = new Map();
|
|
168
|
+
function walk(serialized, parentDir) {
|
|
169
|
+
const node = graph.nodes.get(serialized);
|
|
170
|
+
if (!node)
|
|
171
|
+
return;
|
|
172
|
+
for (const edge of node.edges) {
|
|
173
|
+
if (edge.target == null)
|
|
174
|
+
continue;
|
|
175
|
+
const childSerialized = edge.target.id;
|
|
176
|
+
if (resolved.has(childSerialized))
|
|
177
|
+
continue;
|
|
178
|
+
if (edge.target.nodeId.type !== 'package')
|
|
179
|
+
continue;
|
|
180
|
+
const { pkgInfo, readManifest } = getPkgInfo({
|
|
181
|
+
...opts,
|
|
182
|
+
alias: edge.alias,
|
|
183
|
+
currentPackages,
|
|
184
|
+
depTypes: {},
|
|
185
|
+
linkedPathBaseDir: opts.modulesDir, // This might need adjustment for linked deps?
|
|
186
|
+
parentDir,
|
|
187
|
+
ref: edge.target.nodeId.depPath,
|
|
188
|
+
skipped: new Set(),
|
|
189
|
+
});
|
|
190
|
+
resolved.set(childSerialized, { path: pkgInfo.path, readManifest });
|
|
191
|
+
walk(childSerialized, pkgInfo.path);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
for (const [serialized, node] of graph.nodes) {
|
|
195
|
+
if (node.nodeId.type === 'importer') {
|
|
196
|
+
walk(serialized, undefined);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return resolved;
|
|
200
|
+
}
|
|
201
|
+
function walkReverse(nodeId, ctx) {
|
|
202
|
+
const reverseEdges = ctx.reverseMap.get(nodeId);
|
|
203
|
+
if (reverseEdges == null || reverseEdges.length === 0)
|
|
204
|
+
return [];
|
|
205
|
+
// Sort edges by parent name (with serialized ID as tiebreaker) so that
|
|
206
|
+
// deduplication is deterministic: the first parent always gets fully expanded.
|
|
207
|
+
const sortedEdges = [...reverseEdges].sort((a, b) => {
|
|
208
|
+
const cmp = lexCompare(resolveParentName(a, ctx), resolveParentName(b, ctx));
|
|
209
|
+
if (cmp !== 0)
|
|
210
|
+
return cmp;
|
|
211
|
+
return lexCompare(a.parentSerialized, b.parentSerialized);
|
|
212
|
+
});
|
|
213
|
+
const dependents = [];
|
|
214
|
+
for (const edge of sortedEdges) {
|
|
215
|
+
// Cycle detection: this node is already on our current path
|
|
216
|
+
if (ctx.visited.has(edge.parentSerialized)) {
|
|
217
|
+
const parentNode = ctx.graph.nodes.get(edge.parentSerialized);
|
|
218
|
+
if (parentNode?.nodeId.type === 'importer') {
|
|
219
|
+
const info = ctx.importerInfoMap.get(parentNode.nodeId.importerId);
|
|
220
|
+
if (info) {
|
|
221
|
+
dependents.push({
|
|
222
|
+
name: info.name,
|
|
223
|
+
version: info.version,
|
|
224
|
+
circular: true,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
else if (parentNode?.nodeId.type === 'package') {
|
|
229
|
+
const snapshot = ctx.currentPackages[parentNode.nodeId.depPath];
|
|
230
|
+
if (snapshot) {
|
|
231
|
+
const { name, version } = nameVerFromPkgSnapshot(parentNode.nodeId.depPath, snapshot);
|
|
232
|
+
const displayName = resolveDisplayName(edge.parentSerialized, name, version, ctx);
|
|
233
|
+
dependents.push({ name, displayName, version, circular: true });
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const parentGraphNode = ctx.graph.nodes.get(edge.parentSerialized);
|
|
239
|
+
if (parentGraphNode == null)
|
|
240
|
+
continue;
|
|
241
|
+
if (parentGraphNode.nodeId.type === 'importer') {
|
|
242
|
+
const importerId = parentGraphNode.nodeId.importerId;
|
|
243
|
+
const info = ctx.importerInfoMap.get(importerId) ?? { name: importerId, version: '' };
|
|
244
|
+
const depField = getDepFieldForAlias(edge.alias, ctx.importers[importerId]);
|
|
245
|
+
dependents.push({
|
|
246
|
+
name: info.name,
|
|
247
|
+
version: info.version,
|
|
248
|
+
depField,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
else if (parentGraphNode.nodeId.type === 'package') {
|
|
252
|
+
const snapshot = ctx.currentPackages[parentGraphNode.nodeId.depPath];
|
|
253
|
+
if (snapshot == null)
|
|
254
|
+
continue;
|
|
255
|
+
const { name, version } = nameVerFromPkgSnapshot(parentGraphNode.nodeId.depPath, snapshot);
|
|
256
|
+
const peersSuffixHash = peersSuffixHashFromDepPath(parentGraphNode.nodeId.depPath);
|
|
257
|
+
// Deduplication: if this package was already expanded elsewhere in the
|
|
258
|
+
// tree, show it as a leaf to keep the output bounded.
|
|
259
|
+
const displayName = resolveDisplayName(edge.parentSerialized, name, version, ctx);
|
|
260
|
+
if (ctx.expanded.has(edge.parentSerialized)) {
|
|
261
|
+
dependents.push({ name, displayName, version, peersSuffixHash, deduped: true });
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
ctx.visited.add(edge.parentSerialized);
|
|
265
|
+
ctx.expanded.add(edge.parentSerialized);
|
|
266
|
+
const childDependents = walkReverse(edge.parentSerialized, ctx);
|
|
267
|
+
ctx.visited.delete(edge.parentSerialized);
|
|
268
|
+
dependents.push({
|
|
269
|
+
name,
|
|
270
|
+
displayName,
|
|
271
|
+
version,
|
|
272
|
+
peersSuffixHash,
|
|
273
|
+
dependents: childDependents.length > 0 ? childDependents : undefined,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return dependents;
|
|
278
|
+
}
|
|
279
|
+
function resolveParentName(edge, ctx) {
|
|
280
|
+
const graphNode = ctx.graph.nodes.get(edge.parentSerialized);
|
|
281
|
+
if (graphNode == null)
|
|
282
|
+
return '';
|
|
283
|
+
if (graphNode.nodeId.type === 'importer') {
|
|
284
|
+
const info = ctx.importerInfoMap.get(graphNode.nodeId.importerId);
|
|
285
|
+
return info?.name ?? graphNode.nodeId.importerId;
|
|
286
|
+
}
|
|
287
|
+
const snapshot = ctx.currentPackages[graphNode.nodeId.depPath];
|
|
288
|
+
if (snapshot == null)
|
|
289
|
+
return '';
|
|
290
|
+
return nameVerFromPkgSnapshot(graphNode.nodeId.depPath, snapshot).name;
|
|
291
|
+
}
|
|
292
|
+
function resolveDisplayName(serialized, name, version, ctx) {
|
|
293
|
+
if (!ctx.nameFormatter)
|
|
294
|
+
return undefined;
|
|
295
|
+
const pkgNode = ctx.resolvedPackageNodes.get(serialized);
|
|
296
|
+
if (!pkgNode)
|
|
297
|
+
return undefined;
|
|
298
|
+
return ctx.nameFormatter({ name, version, manifest: pkgNode.readManifest() });
|
|
299
|
+
}
|
|
300
|
+
function getDepFieldForAlias(alias, importerSnapshot) {
|
|
301
|
+
if (importerSnapshot.devDependencies?.[alias] != null)
|
|
302
|
+
return 'devDependencies';
|
|
303
|
+
if (importerSnapshot.optionalDependencies?.[alias] != null)
|
|
304
|
+
return 'optionalDependencies';
|
|
305
|
+
if (importerSnapshot.dependencies?.[alias] != null)
|
|
306
|
+
return 'dependencies';
|
|
307
|
+
return undefined;
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=buildDependentsTree.js.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createMatcher } from '@pnpm/config.matcher';
|
|
2
|
+
import npa from '@pnpm/npm-package-arg';
|
|
3
|
+
import semver from 'semver';
|
|
4
|
+
export function createPackagesSearcher(queries, finders) {
|
|
5
|
+
const searchers = queries
|
|
6
|
+
.map(parseSearchQuery)
|
|
7
|
+
.map((packageSelector) => search.bind(null, packageSelector));
|
|
8
|
+
return (pkg) => {
|
|
9
|
+
if (searchers.length > 0 && searchers.some((search) => search(pkg))) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
if (finders == null)
|
|
13
|
+
return false;
|
|
14
|
+
const messages = [];
|
|
15
|
+
let found = false;
|
|
16
|
+
for (const finder of finders) {
|
|
17
|
+
const result = finder(pkg);
|
|
18
|
+
if (result) {
|
|
19
|
+
found = true;
|
|
20
|
+
if (typeof result === 'string') {
|
|
21
|
+
messages.push(result);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (messages.length)
|
|
26
|
+
return messages.join('\n');
|
|
27
|
+
return found;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function search(packageSelector, { alias, name, version }) {
|
|
31
|
+
const nameMatches = packageSelector.matchName(name) || packageSelector.matchName(alias);
|
|
32
|
+
if (!nameMatches) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
if (packageSelector.matchVersion == null) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return !version.startsWith('link:') && packageSelector.matchVersion(version);
|
|
39
|
+
}
|
|
40
|
+
function parseSearchQuery(query) {
|
|
41
|
+
const parsed = npa(query);
|
|
42
|
+
if (parsed.raw === parsed.name) {
|
|
43
|
+
return { matchName: createMatcher(parsed.name) };
|
|
44
|
+
}
|
|
45
|
+
if (parsed.type !== 'version' && parsed.type !== 'range') {
|
|
46
|
+
throw new Error(`Invalid query - ${query}. List can search only by version or range`);
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
matchName: createMatcher(parsed.name),
|
|
50
|
+
matchVersion: (version) => semver.satisfies(version, parsed.fetchSpec),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=createPackagesSearcher.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type DepTypes } from '@pnpm/lockfile.detect-dep-types';
|
|
2
|
+
import type { PackageSnapshots } from '@pnpm/lockfile.fs';
|
|
3
|
+
import type { StoreIndex } from '@pnpm/store.index';
|
|
4
|
+
import type { DependencyManifest, Registries } from '@pnpm/types';
|
|
5
|
+
export interface GetPkgInfoOpts {
|
|
6
|
+
readonly alias: string;
|
|
7
|
+
readonly ref: string;
|
|
8
|
+
readonly currentPackages: PackageSnapshots;
|
|
9
|
+
readonly peers?: Set<string>;
|
|
10
|
+
readonly registries: Registries;
|
|
11
|
+
readonly skipped: Set<string>;
|
|
12
|
+
readonly storeDir?: string;
|
|
13
|
+
readonly storeIndex?: StoreIndex;
|
|
14
|
+
readonly wantedPackages: PackageSnapshots;
|
|
15
|
+
readonly virtualStoreDir?: string;
|
|
16
|
+
readonly virtualStoreDirMaxLength: number;
|
|
17
|
+
readonly depTypes: DepTypes;
|
|
18
|
+
/**
|
|
19
|
+
* The base dir if the `ref` argument is a `"link:"` relative path.
|
|
20
|
+
*/
|
|
21
|
+
readonly linkedPathBaseDir: string;
|
|
22
|
+
/**
|
|
23
|
+
* If the `ref` argument is a `"link:"` relative path, the ref is reused for
|
|
24
|
+
* the version field. (Since the true semver may not be known.)
|
|
25
|
+
*
|
|
26
|
+
* Optionally rewrite this relative path to a base dir before writing it to
|
|
27
|
+
* version.
|
|
28
|
+
*/
|
|
29
|
+
readonly rewriteLinkVersionDir?: string;
|
|
30
|
+
/**
|
|
31
|
+
* The node_modules directory to resolve symlinks from when using global virtual store.
|
|
32
|
+
* This is used for top-level dependencies.
|
|
33
|
+
*/
|
|
34
|
+
readonly modulesDir?: string;
|
|
35
|
+
/**
|
|
36
|
+
* The resolved path of the parent package. When provided, the symlink resolution
|
|
37
|
+
* will use the parent's node_modules directory instead of the top-level modulesDir.
|
|
38
|
+
* This is needed for subdependencies when using global virtual store.
|
|
39
|
+
*/
|
|
40
|
+
readonly parentDir?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function getPkgInfo(opts: GetPkgInfoOpts): {
|
|
43
|
+
pkgInfo: PackageInfo;
|
|
44
|
+
readManifest: () => DependencyManifest;
|
|
45
|
+
};
|
|
46
|
+
interface PackageInfo {
|
|
47
|
+
alias: string;
|
|
48
|
+
isMissing: boolean;
|
|
49
|
+
isPeer: boolean;
|
|
50
|
+
isSkipped: boolean;
|
|
51
|
+
name: string;
|
|
52
|
+
path: string;
|
|
53
|
+
version: string;
|
|
54
|
+
resolved?: string;
|
|
55
|
+
optional?: true;
|
|
56
|
+
dev?: boolean;
|
|
57
|
+
}
|
|
58
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { refToRelative } from '@pnpm/deps.path';
|
|
3
|
+
import { DepType } from '@pnpm/lockfile.detect-dep-types';
|
|
4
|
+
import { nameVerFromPkgSnapshot, pkgSnapshotToResolution, } from '@pnpm/lockfile.utils';
|
|
5
|
+
import { readPackageJsonFromDirSync } from '@pnpm/pkg-manifest.reader';
|
|
6
|
+
import normalizePath from 'normalize-path';
|
|
7
|
+
import { readManifestFromCafs } from './readManifestFromCafs.js';
|
|
8
|
+
import { resolvePackagePath } from './resolvePackagePath.js';
|
|
9
|
+
export function getPkgInfo(opts) {
|
|
10
|
+
let name;
|
|
11
|
+
let version;
|
|
12
|
+
let resolved;
|
|
13
|
+
let depType;
|
|
14
|
+
let optional;
|
|
15
|
+
let isSkipped = false;
|
|
16
|
+
let isMissing = false;
|
|
17
|
+
let integrity;
|
|
18
|
+
const depPath = refToRelative(opts.ref, opts.alias);
|
|
19
|
+
if (depPath) {
|
|
20
|
+
let pkgSnapshot;
|
|
21
|
+
if (opts.currentPackages[depPath]) {
|
|
22
|
+
pkgSnapshot = opts.currentPackages[depPath];
|
|
23
|
+
const parsed = nameVerFromPkgSnapshot(depPath, pkgSnapshot);
|
|
24
|
+
name = parsed.name;
|
|
25
|
+
version = parsed.version;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
pkgSnapshot = opts.wantedPackages[depPath];
|
|
29
|
+
if (pkgSnapshot) {
|
|
30
|
+
const parsed = nameVerFromPkgSnapshot(depPath, pkgSnapshot);
|
|
31
|
+
name = parsed.name;
|
|
32
|
+
version = parsed.version;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
name = opts.alias;
|
|
36
|
+
version = opts.ref;
|
|
37
|
+
}
|
|
38
|
+
isMissing = true;
|
|
39
|
+
isSkipped = opts.skipped.has(depPath);
|
|
40
|
+
}
|
|
41
|
+
if (pkgSnapshot) {
|
|
42
|
+
resolved = pkgSnapshotToResolution(depPath, pkgSnapshot, opts.registries).tarball;
|
|
43
|
+
optional = pkgSnapshot.optional;
|
|
44
|
+
if ('integrity' in pkgSnapshot.resolution) {
|
|
45
|
+
integrity = pkgSnapshot.resolution.integrity;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
depType = opts.depTypes[depPath];
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
name = opts.alias;
|
|
52
|
+
version = opts.ref;
|
|
53
|
+
}
|
|
54
|
+
if (!version) {
|
|
55
|
+
version = opts.ref;
|
|
56
|
+
}
|
|
57
|
+
const fullPackagePath = depPath
|
|
58
|
+
? resolvePackagePath({
|
|
59
|
+
depPath,
|
|
60
|
+
name,
|
|
61
|
+
alias: opts.alias,
|
|
62
|
+
virtualStoreDir: opts.virtualStoreDir ?? '.pnpm',
|
|
63
|
+
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
|
|
64
|
+
modulesDir: opts.modulesDir,
|
|
65
|
+
parentDir: opts.parentDir,
|
|
66
|
+
})
|
|
67
|
+
: path.join(opts.linkedPathBaseDir, opts.ref.slice(5));
|
|
68
|
+
if (version.startsWith('link:') && opts.rewriteLinkVersionDir) {
|
|
69
|
+
version = `link:${normalizePath(path.relative(opts.rewriteLinkVersionDir, fullPackagePath))}`;
|
|
70
|
+
}
|
|
71
|
+
const packageInfo = {
|
|
72
|
+
alias: opts.alias,
|
|
73
|
+
isMissing,
|
|
74
|
+
isPeer: Boolean(opts.peers?.has(opts.alias)),
|
|
75
|
+
isSkipped,
|
|
76
|
+
name,
|
|
77
|
+
path: fullPackagePath,
|
|
78
|
+
version,
|
|
79
|
+
};
|
|
80
|
+
if (resolved) {
|
|
81
|
+
packageInfo.resolved = resolved;
|
|
82
|
+
}
|
|
83
|
+
if (optional === true) {
|
|
84
|
+
packageInfo.optional = true;
|
|
85
|
+
}
|
|
86
|
+
if (depType === DepType.DevOnly) {
|
|
87
|
+
packageInfo.dev = true;
|
|
88
|
+
}
|
|
89
|
+
else if (depType === DepType.ProdOnly) {
|
|
90
|
+
packageInfo.dev = false;
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
pkgInfo: packageInfo,
|
|
94
|
+
readManifest: () => {
|
|
95
|
+
if (integrity && opts.storeDir && opts.storeIndex) {
|
|
96
|
+
const manifest = readManifestFromCafs(opts.storeDir, opts.storeIndex, { integrity, name, version });
|
|
97
|
+
if (manifest)
|
|
98
|
+
return manifest;
|
|
99
|
+
}
|
|
100
|
+
return readPackageJsonFromDirSync(fullPackagePath);
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=getPkgInfo.js.map
|
package/lib/getTree.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { DepTypes } from '@pnpm/lockfile.detect-dep-types';
|
|
2
|
+
import type { PackageSnapshots, ProjectSnapshot } from '@pnpm/lockfile.fs';
|
|
3
|
+
import type { StoreIndex } from '@pnpm/store.index';
|
|
4
|
+
import type { Finder, Registries } from '@pnpm/types';
|
|
5
|
+
import type { DependencyGraph } from './buildDependencyGraph.js';
|
|
6
|
+
import type { DependencyNode } from './DependencyNode.js';
|
|
7
|
+
import { type TreeNodeId } from './TreeNodeId.js';
|
|
8
|
+
export interface BaseTreeOpts {
|
|
9
|
+
include: {
|
|
10
|
+
dependencies?: boolean;
|
|
11
|
+
devDependencies?: boolean;
|
|
12
|
+
optionalDependencies?: boolean;
|
|
13
|
+
};
|
|
14
|
+
excludePeerDependencies?: boolean;
|
|
15
|
+
lockfileDir: string;
|
|
16
|
+
onlyProjects?: boolean;
|
|
17
|
+
search?: Finder;
|
|
18
|
+
skipped: Set<string>;
|
|
19
|
+
registries: Registries;
|
|
20
|
+
depTypes: DepTypes;
|
|
21
|
+
storeDir?: string;
|
|
22
|
+
storeIndex?: StoreIndex;
|
|
23
|
+
virtualStoreDir?: string;
|
|
24
|
+
virtualStoreDirMaxLength: number;
|
|
25
|
+
modulesDir?: string;
|
|
26
|
+
showDedupedSearchMatches?: boolean;
|
|
27
|
+
graph: DependencyGraph;
|
|
28
|
+
materializationCache: MaterializationCache;
|
|
29
|
+
}
|
|
30
|
+
interface GetTreeOpts extends BaseTreeOpts {
|
|
31
|
+
maxDepth: number;
|
|
32
|
+
rewriteLinkVersionDir: string;
|
|
33
|
+
importers: Record<string, ProjectSnapshot>;
|
|
34
|
+
currentPackages: PackageSnapshots;
|
|
35
|
+
wantedPackages: PackageSnapshots;
|
|
36
|
+
parentDir?: string;
|
|
37
|
+
}
|
|
38
|
+
interface CachedSubtree {
|
|
39
|
+
/** Total number of DependencyNode objects in the subtree (recursive). */
|
|
40
|
+
count: number;
|
|
41
|
+
/** Whether any node in this subtree matched the search. */
|
|
42
|
+
hasSearchMatch: boolean;
|
|
43
|
+
/** Search match messages (string-typed matches) found in this subtree. */
|
|
44
|
+
searchMessages: string[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Caches already-materialized subtrees. When a subtree is encountered a
|
|
48
|
+
* second time (cache hit), an empty array is returned and the node is marked
|
|
49
|
+
* as deduped — bounding the total output to O(N) nodes.
|
|
50
|
+
*/
|
|
51
|
+
export type MaterializationCache = Map<string, CachedSubtree>;
|
|
52
|
+
export declare function getTree(opts: GetTreeOpts, parentId: TreeNodeId): DependencyNode[];
|
|
53
|
+
export {};
|