Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
index.js220 linesDownload Raw Back to lib
1'use strict'
2
3const npa = require('npm-package-arg')
4const semver = require('semver')
5const { checkEngine } = require('npm-install-checks')
6const normalizeBin = require('npm-normalize-package-bin')
7
8const engineOk = (manifest, npmVersion, nodeVersion) => {
9  try {
10    checkEngine(manifest, npmVersion, nodeVersion)
11    return true
12  } catch (_) {
13    return false
14  }
15}
16
17const isBefore = (verTimes, ver, time) =>
18  !verTimes || !verTimes[ver] || Date.parse(verTimes[ver]) <= time
19
20const avoidSemverOpt = { includePrerelease: true, loose: true }
21const shouldAvoid = (ver, avoid) =>
22  avoid && semver.satisfies(ver, avoid, avoidSemverOpt)
23
24const decorateAvoid = (result, avoid) =>
25  result && shouldAvoid(result.version, avoid)
26    ? { ...result, _shouldAvoid: true }
27    : result
28
29const pickManifest = (packument, wanted, opts) => {
30  const {
31    defaultTag = 'latest',
32    before = null,
33    nodeVersion = process.version,
34    npmVersion = null,
35    includeStaged = false,
36    avoid = null,
37    avoidStrict = false,
38  } = opts
39
40  const { name, time: verTimes } = packument
41  const versions = packument.versions || {}
42
43  if (avoidStrict) {
44    const looseOpts = {
45      ...opts,
46      avoidStrict: false,
47    }
48
49    const result = pickManifest(packument, wanted, looseOpts)
50    if (!result || !result._shouldAvoid) {
51      return result
52    }
53
54    const caret = pickManifest(packument, `^${result.version}`, looseOpts)
55    if (!caret || !caret._shouldAvoid) {
56      return {
57        ...caret,
58        _outsideDependencyRange: true,
59        _isSemVerMajor: false,
60      }
61    }
62
63    const star = pickManifest(packument, '*', looseOpts)
64    if (!star || !star._shouldAvoid) {
65      return {
66        ...star,
67        _outsideDependencyRange: true,
68        _isSemVerMajor: true,
69      }
70    }
71
72    throw Object.assign(new Error(`No avoidable versions for ${name}`), {
73      code: 'ETARGET',
74      name,
75      wanted,
76      avoid,
77      before,
78      versions: Object.keys(versions),
79    })
80  }
81
82  const staged = (includeStaged && packument.stagedVersions &&
83    packument.stagedVersions.versions) || {}
84  const restricted = (packument.policyRestrictions &&
85    packument.policyRestrictions.versions) || {}
86
87  const time = before && verTimes ? +(new Date(before)) : Infinity
88  const spec = npa.resolve(name, wanted || defaultTag)
89  const type = spec.type
90  const distTags = packument['dist-tags'] || {}
91
92  if (type !== 'tag' && type !== 'version' && type !== 'range') {
93    throw new Error('Only tag, version, and range are supported')
94  }
95
96  // if the type is 'tag', and not just the implicit default, then it must be that exactly, or nothing else will do.
97  if (wanted && type === 'tag') {
98    const ver = distTags[wanted]
99    // if the version in the dist-tags is before the before date, then we use that. Otherwise, we get the highest precedence version prior to the dist-tag.
100    if (isBefore(verTimes, ver, time)) {
101      return decorateAvoid(versions[ver] || staged[ver] || restricted[ver], avoid)
102    } else {
103      return pickManifest(packument, `<=${ver}`, opts)
104    }
105  }
106
107  // similarly, if a specific version, then only that version will do
108  if (wanted && type === 'version') {
109    const ver = semver.clean(wanted, { loose: true })
110    const mani = versions[ver] || staged[ver] || restricted[ver]
111    return isBefore(verTimes, ver, time) ? decorateAvoid(mani, avoid) : null
112  }
113
114  // ok, sort based on our heuristics, and pick the best fit
115  const range = type === 'range' ? wanted : '*'
116
117  // if the range is *, then we prefer the 'latest' if available but skip this if it should be avoided, in that case we have to try a little harder.
118  const defaultVer = distTags[defaultTag]
119  if (defaultVer &&
120      (range === '*' || semver.satisfies(defaultVer, range, { loose: true })) &&
121      !restricted[defaultVer] &&
122      !shouldAvoid(defaultVer, avoid)) {
123    const mani = versions[defaultVer]
124    const ok = mani &&
125      isBefore(verTimes, defaultVer, time) &&
126      engineOk(mani, npmVersion, nodeVersion) &&
127      !mani.deprecated &&
128      !staged[defaultVer]
129    if (ok) {
130      return mani
131    }
132  }
133
134  // ok, actually have to sort the list and take the winner
135  const allEntries = Object.entries(versions)
136    .concat(Object.entries(staged))
137    .concat(Object.entries(restricted))
138    .filter(([ver]) => isBefore(verTimes, ver, time))
139
140  if (!allEntries.length) {
141    throw Object.assign(new Error(`No versions available for ${name}`), {
142      code: 'ENOVERSIONS',
143      name,
144      type,
145      wanted,
146      before,
147      versions: Object.keys(versions),
148    })
149  }
150
151  const sortSemverOpt = { loose: true }
152  const entries = allEntries.filter(([ver]) =>
153    semver.satisfies(ver, range, { loose: true }))
154    .sort((a, b) => {
155      const [vera, mania] = a
156      const [verb, manib] = b
157      const notavoida = !shouldAvoid(vera, avoid)
158      const notavoidb = !shouldAvoid(verb, avoid)
159      const notrestra = !restricted[vera]
160      const notrestrb = !restricted[verb]
161      const notstagea = !staged[vera]
162      const notstageb = !staged[verb]
163      const notdepra = !mania.deprecated
164      const notdeprb = !manib.deprecated
165      const enginea = engineOk(mania, npmVersion, nodeVersion)
166      const engineb = engineOk(manib, npmVersion, nodeVersion)
167      // sort by:
168      // - not an avoided version
169      // - not restricted
170      // - not staged
171      // - not deprecated and engine ok
172      // - engine ok
173      // - not deprecated
174      // - semver
175      return (notavoidb - notavoida) ||
176        (notrestrb - notrestra) ||
177        (notstageb - notstagea) ||
178        ((notdeprb && engineb) - (notdepra && enginea)) ||
179        (engineb - enginea) ||
180        (notdeprb - notdepra) ||
181        semver.rcompare(vera, verb, sortSemverOpt)
182    })
183
184  return decorateAvoid(entries[0] && entries[0][1], avoid)
185}
186
187module.exports = (packument, wanted, opts = {}) => {
188  const mani = pickManifest(packument, wanted, opts)
189  const picked = mani && normalizeBin(mani)
190  const policyRestrictions = packument.policyRestrictions
191  const restricted = (policyRestrictions && policyRestrictions.versions) || {}
192
193  if (picked && !restricted[picked.version]) {
194    return picked
195  }
196
197  const { before = null, defaultTag = 'latest' } = opts
198  const bstr = before ? new Date(before).toLocaleString() : ''
199  const { name } = packument
200  const pckg = `${name}@${wanted}` +
201    (before ? ` with a date before ${bstr}` : '')
202
203  const isForbidden = picked && !!restricted[picked.version]
204  const polMsg = isForbidden ? policyRestrictions.message : ''
205
206  const msg = !isForbidden ? `No matching version found for ${pckg}.`
207    : `Could not download ${pckg} due to policy violations:\n${polMsg}`
208
209  const code = isForbidden ? 'E403' : 'ETARGET'
210  throw Object.assign(new Error(msg), {
211    code,
212    type: npa.resolve(packument.name, wanted).type,
213    wanted,
214    versions: Object.keys(packument.versions ?? {}),
215    name,
216    distTags: packument['dist-tags'],
217    defaultTag,
218  })
219}
220 
codekingpro/portable-devtools · Team Ai