1 | 'use strict'
|
2 |
|
3 | const figgyPudding = require('figgy-pudding')
|
4 | const npa = require('npm-package-arg')
|
5 | const semver = require('semver')
|
6 |
|
7 | const PickerOpts = figgyPudding({
|
8 | defaultTag: { default: 'latest' },
|
9 | enjoyBy: {},
|
10 | includeDeprecated: { default: false }
|
11 | })
|
12 |
|
13 | module.exports = pickManifest
|
14 | function pickManifest (packument, wanted, opts) {
|
15 | opts = PickerOpts(opts)
|
16 | const time = opts.enjoyBy && packument.time && +(new Date(opts.enjoyBy))
|
17 | const spec = npa.resolve(packument.name, wanted)
|
18 | const type = spec.type
|
19 | if (type === 'version' || type === 'range') {
|
20 | wanted = semver.clean(wanted, true) || wanted
|
21 | }
|
22 | const distTags = packument['dist-tags'] || {}
|
23 | const versions = Object.keys(packument.versions || {}).filter(v => {
|
24 | return semver.valid(v, true)
|
25 | })
|
26 | const policyRestrictions = packument.policyRestrictions
|
27 | const restrictedVersions = policyRestrictions
|
28 | ? Object.keys(policyRestrictions.versions) : []
|
29 |
|
30 | function enjoyableBy (v) {
|
31 | return !time || (
|
32 | packument.time[v] && time >= +(new Date(packument.time[v]))
|
33 | )
|
34 | }
|
35 |
|
36 | let err
|
37 |
|
38 | if (!versions.length && !restrictedVersions.length) {
|
39 | err = new Error(`No valid versions available for ${packument.name}`)
|
40 | err.code = 'ENOVERSIONS'
|
41 | err.name = packument.name
|
42 | err.type = type
|
43 | err.wanted = wanted
|
44 | throw err
|
45 | }
|
46 |
|
47 | let target
|
48 |
|
49 | if (type === 'tag' && enjoyableBy(distTags[wanted])) {
|
50 | target = distTags[wanted]
|
51 | } else if (type === 'version') {
|
52 | target = wanted
|
53 | } else if (type !== 'range' && enjoyableBy(distTags[wanted])) {
|
54 | throw new Error('Only tag, version, and range are supported')
|
55 | }
|
56 |
|
57 | const tagVersion = distTags[opts.defaultTag]
|
58 |
|
59 | if (
|
60 | !target &&
|
61 | tagVersion &&
|
62 | packument.versions[tagVersion] &&
|
63 | enjoyableBy(tagVersion) &&
|
64 | semver.satisfies(tagVersion, wanted, true)
|
65 | ) {
|
66 | target = tagVersion
|
67 | }
|
68 |
|
69 | if (!target && !opts.includeDeprecated) {
|
70 | const undeprecated = versions.filter(v => !packument.versions[v].deprecated && enjoyableBy(v)
|
71 | )
|
72 | target = semver.maxSatisfying(undeprecated, wanted, true)
|
73 | }
|
74 | if (!target) {
|
75 | const stillFresh = versions.filter(enjoyableBy)
|
76 | target = semver.maxSatisfying(stillFresh, wanted, true)
|
77 | }
|
78 |
|
79 | if (!target && wanted === '*' && enjoyableBy(tagVersion)) {
|
80 |
|
81 |
|
82 |
|
83 | target = tagVersion
|
84 | }
|
85 |
|
86 | if (
|
87 | !target &&
|
88 | time &&
|
89 | type === 'tag' &&
|
90 | distTags[wanted] &&
|
91 | !enjoyableBy(distTags[wanted])
|
92 | ) {
|
93 | const stillFresh = versions.filter(v =>
|
94 | enjoyableBy(v) && semver.lte(v, distTags[wanted], true)
|
95 | ).sort(semver.rcompare)
|
96 | target = stillFresh[0]
|
97 | }
|
98 |
|
99 | if (!target && restrictedVersions) {
|
100 | target = semver.maxSatisfying(restrictedVersions, wanted, true)
|
101 | }
|
102 |
|
103 | const manifest = (
|
104 | target &&
|
105 | packument.versions[target]
|
106 | )
|
107 | if (!manifest) {
|
108 |
|
109 | const isForbidden = target && policyRestrictions && policyRestrictions.versions[target]
|
110 | const pckg = `${packument.name}@${wanted}${
|
111 | opts.enjoyBy
|
112 | ? ` with an Enjoy By date of ${
|
113 | new Date(opts.enjoyBy).toLocaleString()
|
114 | }. Maybe try a different date?`
|
115 | : ''
|
116 | }`
|
117 |
|
118 | if (isForbidden) {
|
119 | err = new Error(`Could not download ${pckg} due to policy violations.\n${policyRestrictions.message}\n`)
|
120 | err.code = 'E403'
|
121 | } else {
|
122 | err = new Error(`No matching version found for ${pckg}.`)
|
123 | err.code = 'ETARGET'
|
124 | }
|
125 |
|
126 | err.name = packument.name
|
127 | err.type = type
|
128 | err.wanted = wanted
|
129 | err.versions = versions
|
130 | err.distTags = distTags
|
131 | err.defaultTag = opts.defaultTag
|
132 | throw err
|
133 | } else {
|
134 | return manifest
|
135 | }
|
136 | }
|