1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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
|
const debug = require('./debug.js')
const relpath = require('./relpath.js')
const Node = require('./node.js')
const _loadDeps = Symbol.for('Arborist.Node._loadDeps')
const _target = Symbol.for('_target')
const { dirname } = require('path')
// defined by Node class
const _delistFromMeta = Symbol.for('_delistFromMeta')
const _refreshLocation = Symbol.for('_refreshLocation')
class Link extends Node {
constructor (options) {
const { root, realpath, target, parent, fsParent } = options
if (!realpath && !(target && target.path)) {
throw new TypeError('must provide realpath for Link node')
}
super({
...options,
realpath: realpath || target.path,
root: root || (parent ? parent.root
: fsParent ? fsParent.root
: target ? target.root
: null),
})
if (target) {
this.target = target
} else if (this.realpath === this.root.path) {
this.target = this.root
} else {
this.target = new Node({
...options,
path: realpath,
parent: null,
fsParent: null,
root: this.root,
})
}
}
get version () {
return this.target ? this.target.version : this.package.version || ''
}
get target () {
return this[_target]
}
set target (target) {
const current = this[_target]
if (target === current) {
return
}
if (current && current.then) {
debug(() => {
throw Object.assign(new Error('cannot set target while awaiting'), {
path: this.path,
realpath: this.realpath,
})
})
}
if (target && target.then) {
// can set to a promise during an async tree build operation
// wait until then to assign it.
this[_target] = target
target.then(node => {
this[_target] = null
this.target = node
})
return
}
if (!target) {
if (current && current.linksIn) {
current.linksIn.delete(this)
}
if (this.path) {
this[_delistFromMeta]()
this[_target] = null
this.package = {}
this[_refreshLocation]()
} else {
this[_target] = null
}
return
}
if (!this.path) {
// temp node pending assignment to a tree
// we know it's not in the inventory yet, because no path.
if (target.path) {
this.realpath = target.path
} else {
target.path = target.realpath = this.realpath
}
target.root = this.root
this[_target] = target
target.linksIn.add(this)
this.package = target.package
return
}
// have to refresh metadata, because either realpath or package
// is very likely changing.
this[_delistFromMeta]()
this.package = target.package
this.realpath = target.path
this[_refreshLocation]()
target.root = this.root
}
// a link always resolves to the relative path to its target
get resolved () {
// the path/realpath guard is there for the benefit of setting
// these things in the "wrong" order
return this.path && this.realpath
? `file:${relpath(dirname(this.path), this.realpath).replace(/#/g, '%23')}`
: null
}
set resolved (r) {}
// deps are resolved on the target, not the Link
// so this is a no-op
[_loadDeps] () {}
// links can't have children, only their targets can
// fix it to an empty list so that we can still call
// things that iterate over them, just as a no-op
get children () {
return new Map()
}
set children (c) {}
get isLink () {
return true
}
}
module.exports = Link
|