@pnpm/deps.inspection.tree-builder 1101.0.7 → 1101.0.8
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/CHANGELOG.md +10 -0
- package/lib/buildDependenciesTree.js +143 -116
- package/lib/buildDependencyGraph.d.ts +3 -2
- package/lib/buildDependencyGraph.js +70 -52
- package/lib/buildDependentsTree.d.ts +10 -7
- package/lib/buildDependentsTree.js +182 -158
- package/lib/createPackagesSearcher.js +17 -14
- package/lib/getPkgInfo.js +75 -78
- package/lib/getTree.js +152 -130
- package/lib/resolvePackagePath.js +34 -32
- package/package.json +4 -4
package/lib/getTree.js
CHANGED
|
@@ -54,14 +54,21 @@ function materializeChildren(ctx, parentId, maxDepth, parentDir) {
|
|
|
54
54
|
if (!graphNode) {
|
|
55
55
|
throw new Error(`Node ${parentSerialized} not found in the dependency graph`);
|
|
56
56
|
}
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
const parent = {
|
|
58
|
+
id: parentId,
|
|
59
|
+
dir: parentDir,
|
|
60
|
+
peers: graphNode.peers,
|
|
61
|
+
childTreeMaxDepth: maxDepth - 1,
|
|
62
|
+
linkedPathBaseDir: parentId.type === 'importer'
|
|
63
|
+
? path.join(ctx.lockfileDir, parentId.importerId)
|
|
64
|
+
: ctx.lockfileDir,
|
|
65
|
+
};
|
|
66
|
+
const result = {
|
|
67
|
+
nodes: [],
|
|
68
|
+
count: 0,
|
|
69
|
+
hasSearchMatch: false,
|
|
70
|
+
searchMessages: ctx.showDedupedSearchMatches ? [] : undefined,
|
|
71
|
+
};
|
|
65
72
|
// Sort edges by alias so that deduplication is deterministic:
|
|
66
73
|
// the alphabetically-first dependency always gets fully expanded.
|
|
67
74
|
const sortedEdges = [...graphNode.edges].sort((a, b) => lexCompare(a.alias, b.alias));
|
|
@@ -69,132 +76,147 @@ function materializeChildren(ctx, parentId, maxDepth, parentDir) {
|
|
|
69
76
|
if (ctx.onlyProjects && !isProjectEdge(parentId, edge)) {
|
|
70
77
|
continue;
|
|
71
78
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
79
|
+
materializeEdge(ctx, { parent, edge, result });
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
count: result.count,
|
|
83
|
+
hasSearchMatch: result.hasSearchMatch,
|
|
84
|
+
nodes: result.nodes,
|
|
85
|
+
searchMessages: result.searchMessages ?? [],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function materializeEdge(ctx, { parent, edge, result }) {
|
|
89
|
+
const { pkgInfo: packageInfo, readManifest } = getPkgInfo({
|
|
90
|
+
...ctx,
|
|
91
|
+
alias: edge.alias,
|
|
92
|
+
ref: edge.ref,
|
|
93
|
+
peers: parent.peers,
|
|
94
|
+
linkedPathBaseDir: parent.linkedPathBaseDir,
|
|
95
|
+
parentDir: parent.dir,
|
|
96
|
+
});
|
|
97
|
+
// A project linked through its publish directory is listed at its own
|
|
98
|
+
// directory.
|
|
99
|
+
if (ctx.onlyProjects && edge.target?.nodeId.type === 'importer') {
|
|
100
|
+
packageInfo.path = path.join(ctx.lockfileDir, edge.target.nodeId.importerId);
|
|
101
|
+
}
|
|
102
|
+
const searchMatch = ctx.search?.({
|
|
103
|
+
alias: edge.alias,
|
|
104
|
+
name: packageInfo.name,
|
|
105
|
+
version: packageInfo.version,
|
|
106
|
+
readManifest,
|
|
107
|
+
});
|
|
108
|
+
const subtree = edge.target == null
|
|
109
|
+
? createEmptySubtree()
|
|
110
|
+
: materializeTarget(ctx, { target: edge.target, maxDepth: parent.childTreeMaxDepth, dir: packageInfo.path });
|
|
111
|
+
collectSubtreeSearchMatches(result, subtree);
|
|
112
|
+
const newEntry = edge.target == null
|
|
113
|
+
? selectUnresolvedEntry(ctx, packageInfo, searchMatch)
|
|
114
|
+
: selectTargetEntry(ctx, { packageInfo, searchMatch, subtree, target: edge.target });
|
|
115
|
+
if (newEntry == null)
|
|
116
|
+
return;
|
|
117
|
+
markSearchMatch(newEntry, { searchMatch, subtree, result });
|
|
118
|
+
if (!newEntry.isPeer || !ctx.excludePeerDependencies || newEntry.dependencies?.length) {
|
|
119
|
+
result.nodes.push(newEntry);
|
|
120
|
+
result.count += 1 + (newEntry.dependencies?.length ? subtree.count : 0);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function collectSubtreeSearchMatches(result, subtree) {
|
|
124
|
+
if (subtree.hasSearchMatch || subtree.dedupedHasSearchMatch) {
|
|
125
|
+
result.hasSearchMatch = true;
|
|
126
|
+
}
|
|
127
|
+
result.searchMessages?.push(...subtree.searchMessages, ...subtree.dedupedSearchMessages);
|
|
128
|
+
}
|
|
129
|
+
function createEmptySubtree() {
|
|
130
|
+
return {
|
|
131
|
+
dependencies: [],
|
|
132
|
+
count: 0,
|
|
133
|
+
hasSearchMatch: false,
|
|
134
|
+
searchMessages: [],
|
|
135
|
+
dedupedHasSearchMatch: false,
|
|
136
|
+
dedupedSearchMessages: [],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function materializeTarget(ctx, { target, maxDepth, dir }) {
|
|
140
|
+
if (ctx.ancestors.has(target.id))
|
|
141
|
+
return createEmptySubtree();
|
|
142
|
+
const cacheKey = materializeCacheKey(target.id, maxDepth);
|
|
143
|
+
const cached = ctx.materializationCache.get(cacheKey);
|
|
144
|
+
if (cached !== undefined) {
|
|
145
|
+
// This subtree was already returned to a parent elsewhere in
|
|
146
|
+
// the output tree — elide it to avoid repeating the same nodes.
|
|
147
|
+
return {
|
|
148
|
+
...createEmptySubtree(),
|
|
149
|
+
dedupedCount: cached.count > 0 ? cached.count : undefined,
|
|
150
|
+
dedupedHasSearchMatch: ctx.showDedupedSearchMatches ? cached.hasSearchMatch : false,
|
|
151
|
+
dedupedSearchMessages: ctx.showDedupedSearchMatches ? cached.searchMessages : [],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
ctx.ancestors.add(target.id);
|
|
155
|
+
const childResult = materializeChildren(ctx, target.nodeId, maxDepth, dir);
|
|
156
|
+
ctx.ancestors.delete(target.id);
|
|
157
|
+
// Always cache — even results with circular truncations.
|
|
158
|
+
ctx.materializationCache.set(cacheKey, {
|
|
159
|
+
count: childResult.count,
|
|
160
|
+
hasSearchMatch: childResult.hasSearchMatch,
|
|
161
|
+
searchMessages: childResult.searchMessages,
|
|
162
|
+
});
|
|
163
|
+
return {
|
|
164
|
+
dependencies: childResult.nodes,
|
|
165
|
+
count: childResult.count,
|
|
166
|
+
hasSearchMatch: childResult.hasSearchMatch,
|
|
167
|
+
searchMessages: childResult.searchMessages,
|
|
168
|
+
dedupedHasSearchMatch: false,
|
|
169
|
+
dedupedSearchMessages: [],
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
// External link or unresolvable — no traversal possible. With
|
|
173
|
+
// onlyProjects, this is a link to a project outside the lockfile, which
|
|
174
|
+
// buildDependenciesTree walks and prunes for the search afterwards.
|
|
175
|
+
function selectUnresolvedEntry(ctx, packageInfo, searchMatch) {
|
|
176
|
+
return ctx.search == null || searchMatch || ctx.onlyProjects ? packageInfo : undefined;
|
|
177
|
+
}
|
|
178
|
+
function selectTargetEntry(ctx, { packageInfo, searchMatch, subtree, target }) {
|
|
179
|
+
let newEntry;
|
|
180
|
+
if (subtree.dependencies.length > 0) {
|
|
181
|
+
newEntry = {
|
|
182
|
+
...packageInfo,
|
|
183
|
+
dependencies: subtree.dependencies,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
else if (ctx.search == null || searchMatch || subtree.dedupedHasSearchMatch) {
|
|
187
|
+
newEntry = packageInfo;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
if (subtree.dedupedCount != null) {
|
|
193
|
+
newEntry.deduped = true;
|
|
194
|
+
newEntry.dedupedDependenciesCount = subtree.dedupedCount;
|
|
195
|
+
}
|
|
196
|
+
if (target.nodeId.type === 'package') {
|
|
197
|
+
const peerHash = peersSuffixHashFromDepPath(target.nodeId.depPath);
|
|
198
|
+
if (peerHash != null) {
|
|
199
|
+
newEntry.peersSuffixHash = peerHash;
|
|
180
200
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
201
|
+
}
|
|
202
|
+
return newEntry;
|
|
203
|
+
}
|
|
204
|
+
function markSearchMatch(newEntry, { searchMatch, subtree, result }) {
|
|
205
|
+
if (searchMatch) {
|
|
206
|
+
newEntry.searched = true;
|
|
207
|
+
result.hasSearchMatch = true;
|
|
208
|
+
if (typeof searchMatch === 'string') {
|
|
209
|
+
newEntry.searchMessage = searchMatch;
|
|
210
|
+
result.searchMessages?.push(searchMatch);
|
|
186
211
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (subtree.dedupedHasSearchMatch) {
|
|
215
|
+
newEntry.searched = true;
|
|
216
|
+
if (subtree.dedupedSearchMessages.length > 0) {
|
|
217
|
+
newEntry.searchMessage = subtree.dedupedSearchMessages.join('\n');
|
|
190
218
|
}
|
|
191
219
|
}
|
|
192
|
-
return {
|
|
193
|
-
count: resultCount,
|
|
194
|
-
hasSearchMatch: resultHasSearchMatch,
|
|
195
|
-
nodes: resultDependencies,
|
|
196
|
-
searchMessages: resultSearchMessages ?? [],
|
|
197
|
-
};
|
|
198
220
|
}
|
|
199
221
|
/**
|
|
200
222
|
* Walks the materialized DependencyNode[] tree and marks circular back-edges.
|
|
@@ -9,39 +9,41 @@ import { depPathToFilename } from '@pnpm/deps.path';
|
|
|
9
9
|
* symlinks are resolved to find the actual store location.
|
|
10
10
|
*/
|
|
11
11
|
export function resolvePackagePath(opts) {
|
|
12
|
-
|
|
12
|
+
const fullPackagePath = path.join(opts.virtualStoreDir, depPathToFilename(opts.depPath, opts.virtualStoreDirMaxLength), 'node_modules', opts.name);
|
|
13
13
|
// Resolve symlink for global virtual store.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// /store/.../node_modules/@scope/pkg
|
|
26
|
-
// We need the node_modules directory to find sibling packages
|
|
27
|
-
nodeModulesDir = path.dirname(opts.parentDir);
|
|
28
|
-
// For scoped packages (@org/pkg), go up one more level
|
|
29
|
-
if (path.basename(nodeModulesDir).startsWith('@')) {
|
|
30
|
-
nodeModulesDir = path.dirname(nodeModulesDir);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
else if (opts.modulesDir) {
|
|
34
|
-
nodeModulesDir = opts.modulesDir;
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
return fullPackagePath;
|
|
38
|
-
}
|
|
39
|
-
fullPackagePath = fs.realpathSync(path.join(nodeModulesDir, opts.alias));
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
// Fallback to constructed path if symlink doesn't exist
|
|
43
|
-
}
|
|
14
|
+
if (!isGlobalVirtualStore(opts.virtualStoreDir, opts.modulesDir))
|
|
15
|
+
return fullPackagePath;
|
|
16
|
+
const nodeModulesDir = getSymlinkNodeModulesDir(opts);
|
|
17
|
+
if (nodeModulesDir == null)
|
|
18
|
+
return fullPackagePath;
|
|
19
|
+
try {
|
|
20
|
+
return fs.realpathSync(path.join(nodeModulesDir, opts.alias));
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Fallback to constructed path if symlink doesn't exist
|
|
24
|
+
return fullPackagePath;
|
|
44
25
|
}
|
|
45
|
-
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Global virtual store is detected when virtualStoreDir is outside the project's node_modules.
|
|
29
|
+
*/
|
|
30
|
+
function isGlobalVirtualStore(virtualStoreDir, modulesDir) {
|
|
31
|
+
const resolvedVirtualStoreDir = path.resolve(virtualStoreDir);
|
|
32
|
+
const resolvedModulesDir = modulesDir ? path.resolve(modulesDir) : undefined;
|
|
33
|
+
return Boolean(resolvedModulesDir &&
|
|
34
|
+
!resolvedVirtualStoreDir.startsWith(resolvedModulesDir + path.sep) &&
|
|
35
|
+
resolvedVirtualStoreDir !== resolvedModulesDir);
|
|
36
|
+
}
|
|
37
|
+
function getSymlinkNodeModulesDir(opts) {
|
|
38
|
+
if (!opts.parentDir)
|
|
39
|
+
return opts.modulesDir || undefined;
|
|
40
|
+
// parentDir example: /store/.../node_modules/express
|
|
41
|
+
// /store/.../node_modules/@scope/pkg
|
|
42
|
+
// We need the node_modules directory to find sibling packages
|
|
43
|
+
const nodeModulesDir = path.dirname(opts.parentDir);
|
|
44
|
+
// For scoped packages (@org/pkg), go up one more level
|
|
45
|
+
return path.basename(nodeModulesDir).startsWith('@')
|
|
46
|
+
? path.dirname(nodeModulesDir)
|
|
47
|
+
: nodeModulesDir;
|
|
46
48
|
}
|
|
47
49
|
//# sourceMappingURL=resolvePackagePath.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/deps.inspection.tree-builder",
|
|
3
|
-
"version": "1101.0.
|
|
3
|
+
"version": "1101.0.8",
|
|
4
4
|
"description": "Creates a dependencies hierarchy for a symlinked `node_modules`",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -36,13 +36,13 @@
|
|
|
36
36
|
"@pnpm/fs.read-modules-dir": "1100.0.3",
|
|
37
37
|
"@pnpm/installing.modules-yaml": "1101.0.5",
|
|
38
38
|
"@pnpm/lockfile.detect-dep-types": "1100.0.26",
|
|
39
|
-
"@pnpm/lockfile.fs": "1100.2.
|
|
39
|
+
"@pnpm/lockfile.fs": "1100.2.11",
|
|
40
40
|
"@pnpm/lockfile.peer-edges": "1100.0.1",
|
|
41
41
|
"@pnpm/lockfile.utils": "1102.1.5",
|
|
42
42
|
"@pnpm/npm-package-arg": "^2.0.0",
|
|
43
43
|
"@pnpm/pkg-manifest.reader": "1100.0.21",
|
|
44
44
|
"@pnpm/store.cafs": "1100.3.5",
|
|
45
|
-
"@pnpm/store.index": "
|
|
45
|
+
"@pnpm/store.index": "1101.0.0",
|
|
46
46
|
"@pnpm/text.ordinal-comparator": "1100.0.0",
|
|
47
47
|
"@pnpm/types": "1102.1.1",
|
|
48
48
|
"load-json-file": "^7.0.1",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@jest/globals": "30.4.1",
|
|
58
58
|
"@pnpm/constants": "1102.0.0",
|
|
59
|
-
"@pnpm/deps.inspection.tree-builder": "1101.0.
|
|
59
|
+
"@pnpm/deps.inspection.tree-builder": "1101.0.8",
|
|
60
60
|
"@pnpm/test-fixtures": "1100.0.1",
|
|
61
61
|
"@types/normalize-path": "^3.0.2",
|
|
62
62
|
"@types/semver": "7.8.0"
|