// Do not rely on package._fields, so that we don't throw
// false failures if a tree is generated by other clients.
// Only relies on child.resolved, which MAY come from
// client-specific package.json meta _fields, but most of
// the time will be pulled out of a lockfile
const semver = require('semver')
const npa = require('npm-package-arg')
const { relative } = require('path')
const fromPath = require('./from-path.js')
const depValid = (child, requested, requestor) => {
// NB: we don't do much to verify 'tag' type requests.
// Just verify that we got a remote resolution. Presumably, it
// came from a registry and was tagged at some point.
if (typeof requested === 'string') {
try {
// tarball/dir must have resolved to the same tgz on disk, but for
// file: deps that depend on other files/dirs, we must resolve the
// location based on the *requestor* file/dir, not where it ends up.
// '' is equivalent to '*'
requested = npa.resolve(child.name, requested || '*', fromPath(requestor, requestor.edgesOut.get(child.name)))
} catch (er) {
// Not invalid because the child doesn't match, but because
// the spec itself is not supported. Nothing would match,
// so the edge is definitely not valid and never can be.
er.dependency = child.name
er.requested = requested
requestor.errors.push(er)
return false
}
}
// if the lockfile is super old, or hand-modified,
// then it's possible to hit this state.
if (!requested) {
const er = new Error('Invalid dependency specifier')
er.dependency = child.name
er.requested = requested
requestor.errors.push(er)
return false
}
switch (requested.type) {
case 'range':
if (requested.fetchSpec === '*') {
return true
}
// fallthrough
case 'version':
// if it's a version or a range other than '*', semver it
return semver.satisfies(child.version, requested.fetchSpec, true)
case 'directory':
return linkValid(child, requested, requestor)
case 'file':
return tarballValid(child, requested, requestor)
case 'alias':
// check that the alias target is valid
return depValid(child, requested.subSpec, requestor)
case 'tag':
// if it's a tag, we just verify that it has a tarball resolution
// presumably, it came from the registry and was tagged at some point
return child.resolved && npa(child.resolved).type === 'remote'
case 'remote':
// verify that we got it from the desired location
return child.resolved === requested.fetchSpec
case 'git': {
// if it's a git type, verify that they're the same repo
//
// if it specifies a definite commit, then it must have the
// same commit to be considered the same repo
//
// if it has a #semver: