1const relpath = require('./relpath.js') 2const Node = require('./node.js') 3const _loadDeps = Symbol.for('Arborist.Node._loadDeps') 4const _target = Symbol.for('_target') 5const { dirname } = require('path') 6// defined by Node class 7const _delistFromMeta = Symbol.for('_delistFromMeta') 8const _refreshLocation = Symbol.for('_refreshLocation') 9class Link extends Node { 10 constructor (options) { 11 const { root, realpath, target, parent, fsParent, isStoreLink } = options 12 13 if (!realpath && !(target && target.path)) { 14 throw new TypeError('must provide realpath for Link node') 15 } 16 17 super({ 18 ...options, 19 realpath: realpath || target.path, 20 root: root || (parent ? parent.root 21 : fsParent ? fsParent.root 22 : target ? target.root 23 : null), 24 }) 25 26 this.isStoreLink = isStoreLink || false 27 28 if (target) { 29 this.target = target 30 } else if (this.realpath === this.root.path) { 31 this.target = this.root 32 } else { 33 this.target = new Node({ 34 ...options, 35 path: realpath, 36 parent: null, 37 fsParent: null, 38 root: this.root, 39 }) 40 } 41 } 42 43 get version () { 44 return this.target ? this.target.version : this.package.version || '' 45 } 46 47 get target () { 48 return this[_target] 49 } 50 51 set target (target) { 52 const current = this[_target] 53 if (target === current) { 54 return 55 } 56 57 if (!target) { 58 if (current && current.linksIn) { 59 current.linksIn.delete(this) 60 } 61 if (this.path) { 62 this[_delistFromMeta]() 63 this[_target] = null 64 this.package = {} 65 this[_refreshLocation]() 66 } else { 67 this[_target] = null 68 } 69 return 70 } 71 72 if (!this.path) { 73 // temp node pending assignment to a tree 74 // we know it's not in the inventory yet, because no path. 75 if (target.path) { 76 this.realpath = target.path 77 } else { 78 target.path = target.realpath = this.realpath 79 } 80 target.root = this.root 81 this[_target] = target 82 target.linksIn.add(this) 83 this.package = target.package 84 return 85 } 86 87 // have to refresh metadata, because either realpath or package 88 // is very likely changing. 89 this[_delistFromMeta]() 90 this.package = target.package 91 this.realpath = target.path 92 this[_refreshLocation]() 93 94 target.root = this.root 95 } 96 97 // a link always resolves to the relative path to its target 98 get resolved () { 99 // the path/realpath guard is there for the benefit of setting 100 // these things in the "wrong" order 101 return this.path && this.realpath 102 ? `file:${relpath(dirname(this.path), this.realpath).replace(/#/g, '%23')}` 103 : null 104 } 105 106 set resolved (r) {} 107 108 // deps are resolved on the target, not the Link 109 // so this is a no-op 110 [_loadDeps] () {} 111 112 // links can't have children, only their targets can 113 // fix it to an empty list so that we can still call 114 // things that iterate over them, just as a no-op 115 get children () { 116 return new Map() 117 } 118 119 set children (c) {} 120 121 get isLink () { 122 return true 123 } 124} 125 126module.exports = Link 127