0
# Symbolic Links
1
2
The Link class extends Node to represent symbolic links within node_modules directories. Links delegate dependency resolution to their targets while maintaining their own position in the tree structure.
3
4
## Capabilities
5
6
### Constructor
7
8
Creates a new Link instance representing a symbolic link.
9
10
```javascript { .api }
11
/**
12
* Create a new symbolic link node
13
* @param options - Link configuration options
14
*/
15
constructor(options?: LinkOptions): Link;
16
17
interface LinkOptions extends NodeOptions {
18
/** Link target node */
19
target?: Node;
20
/** Real path to link target (required) */
21
realpath: string;
22
/** Is store link */
23
isStoreLink?: boolean;
24
}
25
```
26
27
**Usage Example:**
28
29
```javascript
30
const { Link } = require('@npmcli/arborist');
31
32
// Create a link to a target node
33
const link = new Link({
34
name: 'my-package',
35
parent: rootNode,
36
realpath: '/path/to/actual/package',
37
target: targetNode
38
});
39
```
40
41
### Link-Specific Properties
42
43
Properties that distinguish links from regular nodes.
44
45
```javascript { .api }
46
interface LinkSpecificProperties {
47
/** Always true for Link instances */
48
isLink: true;
49
/** Target node that the link points to */
50
target: Node;
51
/** Relative path to target (computed property) */
52
resolved: string;
53
/** Is store link */
54
isStoreLink: boolean;
55
/** Always empty Map (read-only) */
56
children: Map<string, Node>;
57
}
58
```
59
60
**Usage Examples:**
61
62
```javascript
63
// Check if node is a link
64
if (node.isLink) {
65
console.log(`Link target: ${node.target.name}@${node.target.version}`);
66
console.log(`Target path: ${node.target.path}`);
67
console.log(`Resolved path: ${node.resolved}`);
68
}
69
70
// Links have no direct children
71
console.log(`Link children count: ${node.children.size}`); // Always 0
72
73
// Access target's children instead
74
console.log(`Target children: ${Array.from(node.target.children.keys())}`);
75
```
76
77
### Target Delegation
78
79
Links delegate certain operations to their targets, particularly dependency resolution.
80
81
```javascript { .api }
82
/**
83
* Links inherit most Node methods but delegate resolution to target
84
* Dependencies are resolved from target location, not link location
85
*/
86
interface LinkDelegation {
87
/** Delegates to target.resolve() */
88
resolve(name: string): Node | undefined;
89
/** Target's package.json content */
90
package: object;
91
/** Target's version */
92
version: string;
93
/** Target's dependency edges */
94
edgesOut: Map<string, Edge>;
95
}
96
```
97
98
**Usage Example:**
99
100
```javascript
101
// Resolution happens from target location
102
const dependency = link.resolve('express');
103
// This is equivalent to:
104
const dependency2 = link.target.resolve('express');
105
106
// Package information comes from target
107
console.log(`Link package name: ${link.package.name}`);
108
console.log(`Link version: ${link.version}`);
109
110
// Dependencies come from target
111
console.log(`Link dependencies: ${Array.from(link.edgesOut.keys())}`);
112
```
113
114
### Path Resolution
115
116
Links maintain their own path while pointing to a target with a different path.
117
118
```javascript { .api }
119
interface LinkPathResolution {
120
/** Path to the link itself */
121
path: string;
122
/** Real path to the target */
123
realpath: string;
124
/** Computed relative path from link to target */
125
resolved: string;
126
}
127
```
128
129
**Usage Example:**
130
131
```javascript
132
console.log(`Link path: ${link.path}`); // e.g. /proj/node_modules/pkg
133
console.log(`Target path: ${link.realpath}`); // e.g. /proj/packages/pkg
134
console.log(`Resolved path: ${link.resolved}`); // e.g. ../../packages/pkg
135
```
136
137
### Store Links
138
139
Special type of links used for package stores and global installations.
140
141
```javascript { .api }
142
/**
143
* Store links are special links used in package stores
144
* @param isStoreLink - Whether this is a store link
145
*/
146
interface StoreLinkProperties {
147
/** Is this a store link */
148
isStoreLink: boolean;
149
}
150
```
151
152
**Usage Example:**
153
154
```javascript
155
// Check if this is a store link
156
if (link.isStoreLink) {
157
console.log('This is a store link (global or cached package)');
158
}
159
160
// Create a store link
161
const storeLink = new Link({
162
name: 'global-package',
163
realpath: '/usr/local/lib/node_modules/global-package',
164
target: globalPackageNode,
165
isStoreLink: true
166
});
167
```
168
169
## Key Differences from Node
170
171
### Children Handling
172
173
Links cannot have direct children - their children map is always empty.
174
175
```javascript
176
// Regular node can have children
177
const regularNode = new Node({ name: 'parent' });
178
regularNode.children.set('child', childNode); // Works
179
180
// Link cannot have children
181
const link = new Link({ name: 'link', target: targetNode });
182
link.children.set('child', childNode); // children remains empty
183
console.log(link.children.size); // Always 0
184
```
185
186
### Dependency Resolution
187
188
Dependencies are resolved from the target's location, not the link's location.
189
190
```javascript
191
// If link is at /project/node_modules/my-package
192
// But target is at /project/packages/my-package
193
// Resolution happens from /project/packages/my-package
194
const resolved = link.resolve('dependency');
195
// Uses Node.js resolution from target location
196
```
197
198
### Tree Structure
199
200
Links maintain their position in the tree while delegating content to targets.
201
202
```javascript
203
// Link has its own parent/location
204
console.log(`Link parent: ${link.parent.name}`);
205
console.log(`Link location: ${link.location}`);
206
207
// But content comes from target
208
console.log(`Package content: ${link.package.name}`);
209
console.log(`Dependencies: ${Array.from(link.edgesOut.keys())}`);
210
211
// Target might be elsewhere in tree or outside tree entirely
212
console.log(`Target parent: ${link.target.parent?.name || 'none'}`);
213
```
214
215
## Common Use Cases
216
217
### Workspace Links
218
219
Links between packages in a monorepo workspace.
220
221
```javascript
222
// Package A depends on Package B in same workspace
223
const workspaceLink = new Link({
224
name: 'package-b',
225
parent: packageANode,
226
realpath: '/workspace/packages/package-b',
227
target: packageBNode
228
});
229
```
230
231
### Global Package Links
232
233
Links to globally installed packages.
234
235
```javascript
236
// Global CLI tool linked into project
237
const globalLink = new Link({
238
name: 'some-cli',
239
parent: rootNode,
240
realpath: '/usr/local/lib/node_modules/some-cli',
241
target: globalCliNode,
242
isStoreLink: true
243
});
244
```
245
246
### Development Links
247
248
Links created during development (e.g., `npm link`).
249
250
```javascript
251
// Development package linked for testing
252
const devLink = new Link({
253
name: 'my-dev-package',
254
parent: testProjectNode,
255
realpath: '/home/dev/my-dev-package',
256
target: devPackageNode
257
});
258
```
259
260
### Store Cache Links
261
262
Links to packages in a content-addressable store.
263
264
```javascript
265
// Package from content store
266
const cacheLink = new Link({
267
name: 'cached-package',
268
parent: rootNode,
269
realpath: '/cache/store/abc123/node_modules/cached-package',
270
target: cachedPackageNode,
271
isStoreLink: true
272
});
273
```
274
275
## Working with Links
276
277
### Detecting Links
278
279
```javascript
280
// Check if a node is a link
281
if (node.isLink) {
282
console.log('This is a symbolic link');
283
console.log(`Points to: ${node.target.path}`);
284
} else {
285
console.log('This is a regular package');
286
}
287
```
288
289
### Following Links
290
291
```javascript
292
// Get the actual package content
293
const actualPackage = node.isLink ? node.target : node;
294
console.log(`Actual package: ${actualPackage.name}@${actualPackage.version}`);
295
```
296
297
### Finding Link Sources
298
299
```javascript
300
// Find what links to a target
301
if (node.linksIn.size > 0) {
302
console.log(`This package has ${node.linksIn.size} links pointing to it:`);
303
for (const link of node.linksIn) {
304
console.log(` - ${link.location} -> ${node.location}`);
305
}
306
}
307
```