Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dep-valid.js « lib « arborist « @npmcli « node_modules - github.com/npm/cli.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 198d34fa9ba7a1176bb245e15230c56d832b3572 (plain)
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
// 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 {resolve, 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))
    } 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.package.version, requested.fetchSpec, true)

    case 'directory':
      // directory must be a link to the specified folder
      return !!child.isLink &&
        relative(child.realpath, requested.fetchSpec) === ''

    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:<range> specifier, verify that the
      // version in the package is in the semver range
      const resRepo = npa(child.resolved || '')
      const resHost = resRepo.hosted
      const reqHost = requested.hosted
      const reqCommit = /^[a-fA-F0-9]{40}$/.test(requested.gitCommittish || '')
      const nc = { noCommittish: !reqCommit }
      const sameRepo =
        resHost ? reqHost && reqHost.ssh(nc) === resHost.ssh(nc)
        : resRepo.fetchSpec === requested.fetchSpec

      return !sameRepo ? false
        : requested.gitRange ?
          semver.satisfies(child.package.version, requested.gitRange, {
            loose: true,
          })
        : true

    default: // unpossible, just being cautious
      break
  }

  const er = new Error('Unsupported dependency type')
  er.dependency = child.name
  er.requested = requested
  requestor.errors.push(er)
  return false
}

const tarballValid = (child, requested, requestor) => {
  if (child.isLink)
    return false

  if (child.resolved)
    return child.resolved === `file:${requested.fetchSpec}`

  // if we have a legacy mutated package.json file.  we can't be 100%
  // sure that it resolved to the same file, but if it was the same
  // request, that's a pretty good indicator of sameness.
  if (child.package._requested)
    return child.package._requested.saveSpec === requested.saveSpec

  // ok, we're probably dealing with some legacy cruft here, not much
  // we can do at this point unfortunately.
  return false
}

module.exports = (child, requested, accept, requestor) =>
  depValid(child, requested, requestor) ||
  (typeof accept === 'string' ? depValid(child, accept, requestor) : false)