@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
package/lib/getTree.js
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { lexCompare } from '@pnpm/util.lex-comparator';
|
|
3
|
+
import { getPkgInfo } from './getPkgInfo.js';
|
|
4
|
+
import { peersSuffixHashFromDepPath } from './peersSuffixHash.js';
|
|
5
|
+
import { serializeTreeNodeId } from './TreeNodeId.js';
|
|
6
|
+
export function getTree(opts, parentId) {
|
|
7
|
+
const ancestors = new Set();
|
|
8
|
+
ancestors.add(serializeTreeNodeId(parentId));
|
|
9
|
+
const ctx = {
|
|
10
|
+
...opts,
|
|
11
|
+
ancestors,
|
|
12
|
+
};
|
|
13
|
+
const result = materializeChildren(ctx, parentId, opts.maxDepth, opts.parentDir);
|
|
14
|
+
// Mark circular back-edges. materializeChildren truncates dependencies
|
|
15
|
+
// at cycle boundaries but does not set the `circular` flag, so that cached
|
|
16
|
+
// subtrees stay context-independent. fixCircularRefs walks the final tree
|
|
17
|
+
// and adds `circular: true` wherever a node's path matches an ancestor.
|
|
18
|
+
//
|
|
19
|
+
// Seed the ancestors with parentDir (the filesystem path of parentId) so
|
|
20
|
+
// that back-edges to the root of this subtree are detected — the root
|
|
21
|
+
// itself does not appear as a node in the tree, only its children do.
|
|
22
|
+
const circularAncestors = new Set();
|
|
23
|
+
if (opts.parentDir) {
|
|
24
|
+
circularAncestors.add(opts.parentDir);
|
|
25
|
+
}
|
|
26
|
+
return fixCircularRefs(result.nodes, circularAncestors);
|
|
27
|
+
}
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Materialize DependencyNode[] tree from the graph
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
function materializeCacheKey(nodeId, depth) {
|
|
32
|
+
if (depth === Infinity)
|
|
33
|
+
return nodeId;
|
|
34
|
+
return `${nodeId}@d${depth}`;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Core materialization function. Walks the pre-built dependency graph to
|
|
38
|
+
* produce the `DependencyNode[]` tree that downstream renderers expect.
|
|
39
|
+
*
|
|
40
|
+
* The cache is keyed by `(nodeId, remainingDepth)` and stores the
|
|
41
|
+
* `DependencyNode[]` children of a given node. It is populated
|
|
42
|
+
* unconditionally, including results where recursion was truncated at a
|
|
43
|
+
* cycle boundary. Cycle detection uses a mutable `ancestors` Set to
|
|
44
|
+
* stop recursion but does NOT set the `circular` flag — that is handled
|
|
45
|
+
* by `fixCircularRefs` in a separate pass over the final tree. This
|
|
46
|
+
* keeps cached subtrees free of context-dependent circular markers.
|
|
47
|
+
*/
|
|
48
|
+
function materializeChildren(ctx, parentId, maxDepth, parentDir) {
|
|
49
|
+
if (maxDepth <= 0)
|
|
50
|
+
return { nodes: [], count: 0, hasSearchMatch: false, searchMessages: [] };
|
|
51
|
+
const parentSerialized = serializeTreeNodeId(parentId);
|
|
52
|
+
const graphNode = ctx.graph.nodes.get(parentSerialized);
|
|
53
|
+
if (!graphNode) {
|
|
54
|
+
throw new Error(`Node ${parentSerialized} not found in the dependency graph`);
|
|
55
|
+
}
|
|
56
|
+
const childTreeMaxDepth = maxDepth - 1;
|
|
57
|
+
const linkedPathBaseDir = parentId.type === 'importer'
|
|
58
|
+
? path.join(ctx.lockfileDir, parentId.importerId)
|
|
59
|
+
: ctx.lockfileDir;
|
|
60
|
+
const resultDependencies = [];
|
|
61
|
+
let resultCount = 0;
|
|
62
|
+
let resultHasSearchMatch = false;
|
|
63
|
+
const resultSearchMessages = ctx.showDedupedSearchMatches ? [] : undefined;
|
|
64
|
+
// Sort edges by alias so that deduplication is deterministic:
|
|
65
|
+
// the alphabetically-first dependency always gets fully expanded.
|
|
66
|
+
const sortedEdges = [...graphNode.edges].sort((a, b) => lexCompare(a.alias, b.alias));
|
|
67
|
+
for (const edge of sortedEdges) {
|
|
68
|
+
if (ctx.onlyProjects && edge.target?.nodeId.type !== 'importer') {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const { pkgInfo: packageInfo, readManifest } = getPkgInfo({
|
|
72
|
+
...ctx,
|
|
73
|
+
alias: edge.alias,
|
|
74
|
+
ref: edge.ref,
|
|
75
|
+
peers: graphNode.peers,
|
|
76
|
+
linkedPathBaseDir,
|
|
77
|
+
parentDir,
|
|
78
|
+
});
|
|
79
|
+
const searchMatch = ctx.search?.({
|
|
80
|
+
alias: edge.alias,
|
|
81
|
+
name: packageInfo.name,
|
|
82
|
+
version: packageInfo.version,
|
|
83
|
+
readManifest,
|
|
84
|
+
});
|
|
85
|
+
let newEntry = null;
|
|
86
|
+
let childCount = 0;
|
|
87
|
+
let dedupedHasSearchMatch = false;
|
|
88
|
+
let dedupedSearchMessages = [];
|
|
89
|
+
if (edge.target == null) {
|
|
90
|
+
// External link or unresolvable — no traversal possible
|
|
91
|
+
if (ctx.search == null || searchMatch) {
|
|
92
|
+
newEntry = packageInfo;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
let dependencies;
|
|
100
|
+
let childHasSearchMatch = false;
|
|
101
|
+
let childSearchMessages = [];
|
|
102
|
+
let dedupedCount;
|
|
103
|
+
const circular = ctx.ancestors.has(edge.target.id);
|
|
104
|
+
if (circular) {
|
|
105
|
+
dependencies = [];
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
const cacheKey = materializeCacheKey(edge.target.id, childTreeMaxDepth);
|
|
109
|
+
const cached = ctx.materializationCache.get(cacheKey);
|
|
110
|
+
if (cached !== undefined) {
|
|
111
|
+
// This subtree was already returned to a parent elsewhere in
|
|
112
|
+
// the output tree — elide it to avoid repeating the same nodes.
|
|
113
|
+
dependencies = [];
|
|
114
|
+
if (cached.count > 0) {
|
|
115
|
+
dedupedCount = cached.count;
|
|
116
|
+
}
|
|
117
|
+
if (ctx.showDedupedSearchMatches) {
|
|
118
|
+
dedupedHasSearchMatch = cached.hasSearchMatch;
|
|
119
|
+
dedupedSearchMessages = cached.searchMessages;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
ctx.ancestors.add(edge.target.id);
|
|
124
|
+
const childResult = materializeChildren(ctx, edge.target.nodeId, childTreeMaxDepth, packageInfo.path);
|
|
125
|
+
ctx.ancestors.delete(edge.target.id);
|
|
126
|
+
dependencies = childResult.nodes;
|
|
127
|
+
childCount = childResult.count;
|
|
128
|
+
childHasSearchMatch = childResult.hasSearchMatch;
|
|
129
|
+
childSearchMessages = childResult.searchMessages;
|
|
130
|
+
// Always cache — even results with circular truncations.
|
|
131
|
+
ctx.materializationCache.set(cacheKey, {
|
|
132
|
+
count: childCount,
|
|
133
|
+
hasSearchMatch: childHasSearchMatch,
|
|
134
|
+
searchMessages: childSearchMessages,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
if (childHasSearchMatch || dedupedHasSearchMatch) {
|
|
138
|
+
resultHasSearchMatch = true;
|
|
139
|
+
}
|
|
140
|
+
resultSearchMessages?.push(...childSearchMessages, ...dedupedSearchMessages);
|
|
141
|
+
}
|
|
142
|
+
if (dependencies.length > 0) {
|
|
143
|
+
newEntry = {
|
|
144
|
+
...packageInfo,
|
|
145
|
+
dependencies,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
else if (ctx.search == null || searchMatch || dedupedHasSearchMatch) {
|
|
149
|
+
newEntry = packageInfo;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (dedupedCount != null) {
|
|
155
|
+
newEntry.deduped = true;
|
|
156
|
+
newEntry.dedupedDependenciesCount = dedupedCount;
|
|
157
|
+
}
|
|
158
|
+
if (edge.target.nodeId.type === 'package') {
|
|
159
|
+
const peerHash = peersSuffixHashFromDepPath(edge.target.nodeId.depPath);
|
|
160
|
+
if (peerHash != null) {
|
|
161
|
+
newEntry.peersSuffixHash = peerHash;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (searchMatch) {
|
|
166
|
+
newEntry.searched = true;
|
|
167
|
+
resultHasSearchMatch = true;
|
|
168
|
+
if (typeof searchMatch === 'string') {
|
|
169
|
+
newEntry.searchMessage = searchMatch;
|
|
170
|
+
resultSearchMessages?.push(searchMatch);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else if (dedupedHasSearchMatch) {
|
|
174
|
+
newEntry.searched = true;
|
|
175
|
+
if (dedupedSearchMessages.length > 0) {
|
|
176
|
+
newEntry.searchMessage = dedupedSearchMessages.join('\n');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (!newEntry.isPeer || !ctx.excludePeerDependencies || newEntry.dependencies?.length) {
|
|
180
|
+
resultDependencies.push(newEntry);
|
|
181
|
+
resultCount += 1 + (newEntry.dependencies?.length ? childCount : 0);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
count: resultCount,
|
|
186
|
+
hasSearchMatch: resultHasSearchMatch,
|
|
187
|
+
nodes: resultDependencies,
|
|
188
|
+
searchMessages: resultSearchMessages ?? [],
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Walks the materialized DependencyNode[] tree and marks circular back-edges.
|
|
193
|
+
* A node whose `path` matches an ancestor is a cycle — it gets
|
|
194
|
+
* `circular: true` and its dependencies (if any) are stripped.
|
|
195
|
+
*
|
|
196
|
+
* With deduplication in place (deduped nodes are leaves), the walk is O(N).
|
|
197
|
+
*/
|
|
198
|
+
function fixCircularRefs(nodes, ancestors) {
|
|
199
|
+
let changed = false;
|
|
200
|
+
const result = nodes.map(node => {
|
|
201
|
+
// A node whose path matches an ancestor is a circular back-edge.
|
|
202
|
+
if (node.path && ancestors.has(node.path)) {
|
|
203
|
+
changed = true;
|
|
204
|
+
const { dependencies: _, deduped: _d, dedupedDependenciesCount: _c, ...rest } = node;
|
|
205
|
+
return { ...rest, circular: true };
|
|
206
|
+
}
|
|
207
|
+
if (!node.dependencies?.length)
|
|
208
|
+
return node;
|
|
209
|
+
ancestors.add(node.path);
|
|
210
|
+
const fixedDeps = fixCircularRefs(node.dependencies, ancestors);
|
|
211
|
+
ancestors.delete(node.path);
|
|
212
|
+
if (fixedDeps !== node.dependencies) {
|
|
213
|
+
changed = true;
|
|
214
|
+
return { ...node, dependencies: fixedDeps };
|
|
215
|
+
}
|
|
216
|
+
return node;
|
|
217
|
+
});
|
|
218
|
+
return changed ? result : nodes;
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=getTree.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ProjectSnapshot } from '@pnpm/lockfile.fs';
|
|
2
|
+
import type { TreeNodeId } from './TreeNodeId.js';
|
|
3
|
+
export interface GetTreeNodeChildIdOpts {
|
|
4
|
+
readonly parentId: TreeNodeId;
|
|
5
|
+
readonly dep: {
|
|
6
|
+
readonly alias: string;
|
|
7
|
+
readonly ref: string;
|
|
8
|
+
};
|
|
9
|
+
readonly lockfileDir: string;
|
|
10
|
+
readonly importers: Record<string, ProjectSnapshot>;
|
|
11
|
+
}
|
|
12
|
+
export declare function getTreeNodeChildId(opts: GetTreeNodeChildIdOpts): TreeNodeId | undefined;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { refToRelative } from '@pnpm/deps.path';
|
|
3
|
+
import { getLockfileImporterId } from '@pnpm/lockfile.fs';
|
|
4
|
+
export function getTreeNodeChildId(opts) {
|
|
5
|
+
const depPath = refToRelative(opts.dep.ref, opts.dep.alias);
|
|
6
|
+
if (depPath !== null) {
|
|
7
|
+
return { type: 'package', depPath };
|
|
8
|
+
}
|
|
9
|
+
switch (opts.parentId.type) {
|
|
10
|
+
case 'importer': {
|
|
11
|
+
// This should be a link given depPath is null.
|
|
12
|
+
//
|
|
13
|
+
// TODO: Consider updating refToRelative (or writing a new function) to
|
|
14
|
+
// return an enum so there's no implicit assumptions.
|
|
15
|
+
const linkValue = opts.dep.ref.slice('link:'.length);
|
|
16
|
+
// It's a bit roundabout to prepend the lockfile dir only to remove it
|
|
17
|
+
// through getLockfileImporterId, but we can be more certain the right
|
|
18
|
+
// importerId is created by reusing the getLockfileImporterId function.
|
|
19
|
+
const absoluteLinkedPath = path.join(opts.lockfileDir, opts.parentId.importerId, linkValue);
|
|
20
|
+
const childImporterId = getLockfileImporterId(opts.lockfileDir, absoluteLinkedPath);
|
|
21
|
+
// A 'link:' reference may refer to a package outside of the pnpm workspace.
|
|
22
|
+
// Return undefined in that case since it would be difficult to list/traverse
|
|
23
|
+
// that package outside of the pnpm workspace.
|
|
24
|
+
const isLinkOutsideWorkspace = opts.importers[childImporterId] == null;
|
|
25
|
+
return isLinkOutsideWorkspace
|
|
26
|
+
? undefined
|
|
27
|
+
: { type: 'importer', importerId: childImporterId };
|
|
28
|
+
}
|
|
29
|
+
case 'package':
|
|
30
|
+
// In theory an external package could be overridden to link to a
|
|
31
|
+
// dependency in the pnpm workspace. Avoid traversing through this
|
|
32
|
+
// edge case for now.
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=getTreeNodeChildId.js.map
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { buildDependenciesTree, type DependenciesTree } from './buildDependenciesTree.js';
|
|
2
|
+
export { buildDependentsTree, type DependentNode, type DependentsTree, type ImporterInfo } from './buildDependentsTree.js';
|
|
3
|
+
export { createPackagesSearcher } from './createPackagesSearcher.js';
|
|
4
|
+
export { type DependencyNode } from './DependencyNode.js';
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { buildDependenciesTree } from './buildDependenciesTree.js';
|
|
2
|
+
export { buildDependentsTree } from './buildDependentsTree.js';
|
|
3
|
+
export { createPackagesSearcher } from './createPackagesSearcher.js';
|
|
4
|
+
export {} from './DependencyNode.js';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function peersSuffixHashFromDepPath(depPath: string): string | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import { parseDepPath } from '@pnpm/deps.path';
|
|
3
|
+
export function peersSuffixHashFromDepPath(depPath) {
|
|
4
|
+
const { peerDepGraphHash } = parseDepPath(depPath);
|
|
5
|
+
if (!peerDepGraphHash)
|
|
6
|
+
return undefined;
|
|
7
|
+
return crypto.createHash('sha256').update(peerDepGraphHash).digest('hex').slice(0, 4);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=peersSuffixHash.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type StoreIndex } from '@pnpm/store.index';
|
|
2
|
+
import type { DependencyManifest } from '@pnpm/types';
|
|
3
|
+
/**
|
|
4
|
+
* Attempts to read a package manifest from the content-addressable store (CAFS)
|
|
5
|
+
* using its integrity hash. Returns `undefined` if the manifest cannot be read.
|
|
6
|
+
*/
|
|
7
|
+
export declare function readManifestFromCafs(storeDir: string, storeIndex: StoreIndex, pkg: {
|
|
8
|
+
integrity: string;
|
|
9
|
+
name: string;
|
|
10
|
+
version: string;
|
|
11
|
+
}): DependencyManifest | undefined;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getFilePathByModeInCafs } from '@pnpm/store.cafs';
|
|
2
|
+
import { storeIndexKey } from '@pnpm/store.index';
|
|
3
|
+
import { loadJsonFileSync } from 'load-json-file';
|
|
4
|
+
/**
|
|
5
|
+
* Attempts to read a package manifest from the content-addressable store (CAFS)
|
|
6
|
+
* using its integrity hash. Returns `undefined` if the manifest cannot be read.
|
|
7
|
+
*/
|
|
8
|
+
export function readManifestFromCafs(storeDir, storeIndex, pkg) {
|
|
9
|
+
try {
|
|
10
|
+
const pkgId = `${pkg.name}@${pkg.version}`;
|
|
11
|
+
const indexPath = storeIndexKey(pkg.integrity, pkgId);
|
|
12
|
+
const pkgIndex = storeIndex.get(indexPath);
|
|
13
|
+
if (!pkgIndex)
|
|
14
|
+
return undefined;
|
|
15
|
+
const pkgJsonEntry = pkgIndex.files.get('package.json');
|
|
16
|
+
if (pkgJsonEntry) {
|
|
17
|
+
const filePath = getFilePathByModeInCafs(storeDir, pkgJsonEntry.digest, pkgJsonEntry.mode);
|
|
18
|
+
return loadJsonFileSync(filePath);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Fall through to undefined
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=readManifestFromCafs.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the filesystem path for a package identified by its depPath.
|
|
3
|
+
*
|
|
4
|
+
* For local virtual stores, the path is constructed directly.
|
|
5
|
+
* For global virtual stores (where virtualStoreDir is outside modulesDir),
|
|
6
|
+
* symlinks are resolved to find the actual store location.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolvePackagePath(opts: {
|
|
9
|
+
depPath: string;
|
|
10
|
+
name: string;
|
|
11
|
+
alias: string;
|
|
12
|
+
virtualStoreDir: string;
|
|
13
|
+
virtualStoreDirMaxLength: number;
|
|
14
|
+
modulesDir?: string;
|
|
15
|
+
parentDir?: string;
|
|
16
|
+
}): string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { depPathToFilename } from '@pnpm/deps.path';
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the filesystem path for a package identified by its depPath.
|
|
6
|
+
*
|
|
7
|
+
* For local virtual stores, the path is constructed directly.
|
|
8
|
+
* For global virtual stores (where virtualStoreDir is outside modulesDir),
|
|
9
|
+
* symlinks are resolved to find the actual store location.
|
|
10
|
+
*/
|
|
11
|
+
export function resolvePackagePath(opts) {
|
|
12
|
+
let fullPackagePath = path.join(opts.virtualStoreDir, depPathToFilename(opts.depPath, opts.virtualStoreDirMaxLength), 'node_modules', opts.name);
|
|
13
|
+
// Resolve symlink for global virtual store.
|
|
14
|
+
// Global virtual store is detected when virtualStoreDir is outside the project's node_modules.
|
|
15
|
+
const resolvedVirtualStoreDir = path.resolve(opts.virtualStoreDir);
|
|
16
|
+
const resolvedModulesDir = opts.modulesDir ? path.resolve(opts.modulesDir) : undefined;
|
|
17
|
+
const isGlobalVirtualStore = resolvedModulesDir &&
|
|
18
|
+
!resolvedVirtualStoreDir.startsWith(resolvedModulesDir + path.sep) &&
|
|
19
|
+
resolvedVirtualStoreDir !== resolvedModulesDir;
|
|
20
|
+
if (isGlobalVirtualStore) {
|
|
21
|
+
try {
|
|
22
|
+
let nodeModulesDir;
|
|
23
|
+
if (opts.parentDir) {
|
|
24
|
+
// parentDir example: /store/.../node_modules/express
|
|
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
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return fullPackagePath;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=resolvePackagePath.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pnpm/deps.inspection.tree-builder",
|
|
3
|
+
"version": "1001.1.3",
|
|
4
|
+
"description": "Creates a dependencies hierarchy for a symlinked `node_modules`",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pnpm",
|
|
7
|
+
"pnpm11",
|
|
8
|
+
"dependencies",
|
|
9
|
+
"hierarchy",
|
|
10
|
+
"node_modules"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"funding": "https://opencollective.com/pnpm",
|
|
14
|
+
"repository": "https://github.com/pnpm/pnpm/tree/main/deps/inspection/tree-builder",
|
|
15
|
+
"homepage": "https://github.com/pnpm/pnpm/tree/main/deps/inspection/tree-builder#readme",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/pnpm/pnpm/issues"
|
|
18
|
+
},
|
|
19
|
+
"type": "module",
|
|
20
|
+
"main": "lib/index.js",
|
|
21
|
+
"types": "lib/index.d.ts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": "./lib/index.js"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"lib",
|
|
27
|
+
"!*.map"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@pnpm/npm-package-arg": "^2.0.0",
|
|
31
|
+
"@pnpm/util.lex-comparator": "^3.0.2",
|
|
32
|
+
"load-json-file": "^7.0.1",
|
|
33
|
+
"normalize-path": "^3.0.0",
|
|
34
|
+
"realpath-missing": "^2.0.0",
|
|
35
|
+
"resolve-link-target": "^3.0.0",
|
|
36
|
+
"semver": "^7.7.2",
|
|
37
|
+
"@pnpm/config.normalize-registries": "1000.1.4",
|
|
38
|
+
"@pnpm/config.matcher": "1000.1.0",
|
|
39
|
+
"@pnpm/deps.path": "1001.1.3",
|
|
40
|
+
"@pnpm/fs.read-modules-dir": "1000.0.0",
|
|
41
|
+
"@pnpm/lockfile.fs": "1001.1.21",
|
|
42
|
+
"@pnpm/installing.modules-yaml": "1000.3.6",
|
|
43
|
+
"@pnpm/lockfile.utils": "1003.0.3",
|
|
44
|
+
"@pnpm/lockfile.detect-dep-types": "1001.0.16",
|
|
45
|
+
"@pnpm/pkg-manifest.reader": "1000.1.2",
|
|
46
|
+
"@pnpm/store.index": "1000.0.0-0",
|
|
47
|
+
"@pnpm/store.cafs": "1000.0.19",
|
|
48
|
+
"@pnpm/types": "1000.9.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/normalize-path": "^3.0.2",
|
|
52
|
+
"@types/semver": "7.7.1",
|
|
53
|
+
"@pnpm/constants": "1001.3.1",
|
|
54
|
+
"@pnpm/deps.inspection.tree-builder": "1001.1.3",
|
|
55
|
+
"@pnpm/test-fixtures": "1000.0.0"
|
|
56
|
+
},
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=22.13"
|
|
59
|
+
},
|
|
60
|
+
"jest": {
|
|
61
|
+
"preset": "@pnpm/jest-config"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
|
|
65
|
+
"_test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169\" jest",
|
|
66
|
+
"test": "pnpm run compile && pnpm run _test",
|
|
67
|
+
"compile": "tsgo --build && pnpm run lint --fix"
|
|
68
|
+
}
|
|
69
|
+
}
|