node/deps/npm/node_modules/@npmcli/arborist/lib/edge.js
Ruy Adorno 1f287b04a2 deps: upgrade npm to 7.4.2
PR-URL: https://github.com/nodejs/node/pull/36953
Reviewed-By: Myles Borins <myles.borins@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
2021-01-18 12:10:57 +01:00

223 lines
4.8 KiB
JavaScript

// An edge in the dependency graph
// Represents a dependency relationship of some kind
const util = require('util')
const npa = require('npm-package-arg')
const depValid = require('./dep-valid.js')
const _from = Symbol('_from')
const _to = Symbol('_to')
const _type = Symbol('_type')
const _spec = Symbol('_spec')
const _accept = Symbol('_accept')
const _name = Symbol('_name')
const _error = Symbol('_error')
const _loadError = Symbol('_loadError')
const _setFrom = Symbol('_setFrom')
const _explain = Symbol('_explain')
const _explanation = Symbol('_explanation')
const types = new Set([
'prod',
'dev',
'optional',
'peer',
'peerOptional',
'workspace',
])
class ArboristEdge {}
const printableEdge = (edge) => {
const edgeFrom = edge.from && edge.from.location
const edgeTo = edge.to && edge.to.location
return Object.assign(new ArboristEdge(), {
name: edge.name,
spec: edge.spec,
type: edge.type,
...(edgeFrom != null ? { from: edgeFrom } : {}),
...(edgeTo ? { to: edgeTo } : {}),
...(edge.error ? { error: edge.error } : {}),
})
}
class Edge {
constructor (options) {
const { type, name, spec, accept, from } = options
if (typeof spec !== 'string')
throw new TypeError('must provide string spec')
if (type === 'workspace' && npa(spec).type !== 'directory')
throw new TypeError('workspace edges must be a symlink')
this[_spec] = spec
if (accept !== undefined) {
if (typeof accept !== 'string')
throw new TypeError('accept field must be a string if provided')
this[_accept] = accept || '*'
}
if (typeof name !== 'string')
throw new TypeError('must provide dependency name')
this[_name] = name
if (!types.has(type)) {
throw new TypeError(
`invalid type: ${type}\n` +
`(valid types are: ${Edge.types.join(', ')})`)
}
this[_type] = type
if (!from)
throw new TypeError('must provide "from" node')
this[_setFrom](from)
this[_error] = this[_loadError]()
}
satisfiedBy (node) {
return depValid(node, this.spec, this.accept, this.from)
}
explain (seen = []) {
if (this[_explanation])
return this[_explanation]
return this[_explanation] = this[_explain](seen)
}
// return the edge data, and an explanation of how that edge came to be here
[_explain] (seen) {
const { error, from } = this
return {
type: this.type,
name: this.name,
spec: this.spec,
...(error ? { error } : {}),
...(from ? { from: from.explain(null, seen) } : {}),
}
}
get workspace () {
return this[_type] === 'workspace'
}
get prod () {
return this[_type] === 'prod'
}
get dev () {
return this[_type] === 'dev'
}
get optional () {
return this[_type] === 'optional' || this[_type] === 'peerOptional'
}
get peer () {
return this[_type] === 'peer' || this[_type] === 'peerOptional'
}
get type () {
return this[_type]
}
get name () {
return this[_name]
}
get spec () {
return this[_spec]
}
get accept () {
return this[_accept]
}
get valid () {
return !this.error
}
get missing () {
return this.error === 'MISSING'
}
get invalid () {
return this.error === 'INVALID'
}
get peerLocal () {
return this.error === 'PEER LOCAL'
}
get error () {
this[_error] = this[_error] || this[_loadError]()
return this[_error] === 'OK' ? null : this[_error]
}
[_loadError] () {
return !this[_to] ? (this.optional ? null : 'MISSING')
: this.peer && this.from === this.to.parent && !this.from.isTop ? 'PEER LOCAL'
: !depValid(this.to, this.spec, this.accept, this.from) ? 'INVALID'
: 'OK'
}
reload (hard = false) {
this[_explanation] = null
const newTo = this[_from].resolve(this.name)
if (newTo !== this[_to]) {
if (this[_to])
this[_to].edgesIn.delete(this)
this[_to] = newTo
this[_error] = this[_loadError]()
if (this[_to])
this[_to].addEdgeIn(this)
} else if (hard)
this[_error] = this[_loadError]()
}
detach () {
this[_explanation] = null
if (this[_to])
this[_to].edgesIn.delete(this)
this[_from].edgesOut.delete(this.name)
this[_to] = null
this[_error] = 'DETACHED'
this[_from] = null
}
[_setFrom] (node) {
this[_explanation] = null
this[_from] = node
if (node.edgesOut.has(this.name))
node.edgesOut.get(this.name).detach()
node.addEdgeOut(this)
this.reload()
}
get from () {
return this[_from]
}
get to () {
return this[_to]
}
toJSON () {
return printableEdge(this)
}
[util.inspect.custom] () {
return this.toJSON()
}
}
Edge.types = [...types]
Edge.errors = [
'DETACHED',
'MISSING',
'PEER LOCAL',
'INVALID',
]
module.exports = Edge