11cb0ef41Sopenharmony_ci'use strict' 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ciconst npa = require('npm-package-arg') 41cb0ef41Sopenharmony_ciconst semver = require('semver') 51cb0ef41Sopenharmony_ciconst { checkEngine } = require('npm-install-checks') 61cb0ef41Sopenharmony_ciconst normalizeBin = require('npm-normalize-package-bin') 71cb0ef41Sopenharmony_ci 81cb0ef41Sopenharmony_ciconst engineOk = (manifest, npmVersion, nodeVersion) => { 91cb0ef41Sopenharmony_ci try { 101cb0ef41Sopenharmony_ci checkEngine(manifest, npmVersion, nodeVersion) 111cb0ef41Sopenharmony_ci return true 121cb0ef41Sopenharmony_ci } catch (_) { 131cb0ef41Sopenharmony_ci return false 141cb0ef41Sopenharmony_ci } 151cb0ef41Sopenharmony_ci} 161cb0ef41Sopenharmony_ci 171cb0ef41Sopenharmony_ciconst isBefore = (verTimes, ver, time) => 181cb0ef41Sopenharmony_ci !verTimes || !verTimes[ver] || Date.parse(verTimes[ver]) <= time 191cb0ef41Sopenharmony_ci 201cb0ef41Sopenharmony_ciconst avoidSemverOpt = { includePrerelease: true, loose: true } 211cb0ef41Sopenharmony_ciconst shouldAvoid = (ver, avoid) => 221cb0ef41Sopenharmony_ci avoid && semver.satisfies(ver, avoid, avoidSemverOpt) 231cb0ef41Sopenharmony_ci 241cb0ef41Sopenharmony_ciconst decorateAvoid = (result, avoid) => 251cb0ef41Sopenharmony_ci result && shouldAvoid(result.version, avoid) 261cb0ef41Sopenharmony_ci ? { ...result, _shouldAvoid: true } 271cb0ef41Sopenharmony_ci : result 281cb0ef41Sopenharmony_ci 291cb0ef41Sopenharmony_ciconst pickManifest = (packument, wanted, opts) => { 301cb0ef41Sopenharmony_ci const { 311cb0ef41Sopenharmony_ci defaultTag = 'latest', 321cb0ef41Sopenharmony_ci before = null, 331cb0ef41Sopenharmony_ci nodeVersion = process.version, 341cb0ef41Sopenharmony_ci npmVersion = null, 351cb0ef41Sopenharmony_ci includeStaged = false, 361cb0ef41Sopenharmony_ci avoid = null, 371cb0ef41Sopenharmony_ci avoidStrict = false, 381cb0ef41Sopenharmony_ci } = opts 391cb0ef41Sopenharmony_ci 401cb0ef41Sopenharmony_ci const { name, time: verTimes } = packument 411cb0ef41Sopenharmony_ci const versions = packument.versions || {} 421cb0ef41Sopenharmony_ci 431cb0ef41Sopenharmony_ci if (avoidStrict) { 441cb0ef41Sopenharmony_ci const looseOpts = { 451cb0ef41Sopenharmony_ci ...opts, 461cb0ef41Sopenharmony_ci avoidStrict: false, 471cb0ef41Sopenharmony_ci } 481cb0ef41Sopenharmony_ci 491cb0ef41Sopenharmony_ci const result = pickManifest(packument, wanted, looseOpts) 501cb0ef41Sopenharmony_ci if (!result || !result._shouldAvoid) { 511cb0ef41Sopenharmony_ci return result 521cb0ef41Sopenharmony_ci } 531cb0ef41Sopenharmony_ci 541cb0ef41Sopenharmony_ci const caret = pickManifest(packument, `^${result.version}`, looseOpts) 551cb0ef41Sopenharmony_ci if (!caret || !caret._shouldAvoid) { 561cb0ef41Sopenharmony_ci return { 571cb0ef41Sopenharmony_ci ...caret, 581cb0ef41Sopenharmony_ci _outsideDependencyRange: true, 591cb0ef41Sopenharmony_ci _isSemVerMajor: false, 601cb0ef41Sopenharmony_ci } 611cb0ef41Sopenharmony_ci } 621cb0ef41Sopenharmony_ci 631cb0ef41Sopenharmony_ci const star = pickManifest(packument, '*', looseOpts) 641cb0ef41Sopenharmony_ci if (!star || !star._shouldAvoid) { 651cb0ef41Sopenharmony_ci return { 661cb0ef41Sopenharmony_ci ...star, 671cb0ef41Sopenharmony_ci _outsideDependencyRange: true, 681cb0ef41Sopenharmony_ci _isSemVerMajor: true, 691cb0ef41Sopenharmony_ci } 701cb0ef41Sopenharmony_ci } 711cb0ef41Sopenharmony_ci 721cb0ef41Sopenharmony_ci throw Object.assign(new Error(`No avoidable versions for ${name}`), { 731cb0ef41Sopenharmony_ci code: 'ETARGET', 741cb0ef41Sopenharmony_ci name, 751cb0ef41Sopenharmony_ci wanted, 761cb0ef41Sopenharmony_ci avoid, 771cb0ef41Sopenharmony_ci before, 781cb0ef41Sopenharmony_ci versions: Object.keys(versions), 791cb0ef41Sopenharmony_ci }) 801cb0ef41Sopenharmony_ci } 811cb0ef41Sopenharmony_ci 821cb0ef41Sopenharmony_ci const staged = (includeStaged && packument.stagedVersions && 831cb0ef41Sopenharmony_ci packument.stagedVersions.versions) || {} 841cb0ef41Sopenharmony_ci const restricted = (packument.policyRestrictions && 851cb0ef41Sopenharmony_ci packument.policyRestrictions.versions) || {} 861cb0ef41Sopenharmony_ci 871cb0ef41Sopenharmony_ci const time = before && verTimes ? +(new Date(before)) : Infinity 881cb0ef41Sopenharmony_ci const spec = npa.resolve(name, wanted || defaultTag) 891cb0ef41Sopenharmony_ci const type = spec.type 901cb0ef41Sopenharmony_ci const distTags = packument['dist-tags'] || {} 911cb0ef41Sopenharmony_ci 921cb0ef41Sopenharmony_ci if (type !== 'tag' && type !== 'version' && type !== 'range') { 931cb0ef41Sopenharmony_ci throw new Error('Only tag, version, and range are supported') 941cb0ef41Sopenharmony_ci } 951cb0ef41Sopenharmony_ci 961cb0ef41Sopenharmony_ci // if the type is 'tag', and not just the implicit default, then it must 971cb0ef41Sopenharmony_ci // be that exactly, or nothing else will do. 981cb0ef41Sopenharmony_ci if (wanted && type === 'tag') { 991cb0ef41Sopenharmony_ci const ver = distTags[wanted] 1001cb0ef41Sopenharmony_ci // if the version in the dist-tags is before the before date, then 1011cb0ef41Sopenharmony_ci // we use that. Otherwise, we get the highest precedence version 1021cb0ef41Sopenharmony_ci // prior to the dist-tag. 1031cb0ef41Sopenharmony_ci if (isBefore(verTimes, ver, time)) { 1041cb0ef41Sopenharmony_ci return decorateAvoid(versions[ver] || staged[ver] || restricted[ver], avoid) 1051cb0ef41Sopenharmony_ci } else { 1061cb0ef41Sopenharmony_ci return pickManifest(packument, `<=${ver}`, opts) 1071cb0ef41Sopenharmony_ci } 1081cb0ef41Sopenharmony_ci } 1091cb0ef41Sopenharmony_ci 1101cb0ef41Sopenharmony_ci // similarly, if a specific version, then only that version will do 1111cb0ef41Sopenharmony_ci if (wanted && type === 'version') { 1121cb0ef41Sopenharmony_ci const ver = semver.clean(wanted, { loose: true }) 1131cb0ef41Sopenharmony_ci const mani = versions[ver] || staged[ver] || restricted[ver] 1141cb0ef41Sopenharmony_ci return isBefore(verTimes, ver, time) ? decorateAvoid(mani, avoid) : null 1151cb0ef41Sopenharmony_ci } 1161cb0ef41Sopenharmony_ci 1171cb0ef41Sopenharmony_ci // ok, sort based on our heuristics, and pick the best fit 1181cb0ef41Sopenharmony_ci const range = type === 'range' ? wanted : '*' 1191cb0ef41Sopenharmony_ci 1201cb0ef41Sopenharmony_ci // if the range is *, then we prefer the 'latest' if available 1211cb0ef41Sopenharmony_ci // but skip this if it should be avoided, in that case we have 1221cb0ef41Sopenharmony_ci // to try a little harder. 1231cb0ef41Sopenharmony_ci const defaultVer = distTags[defaultTag] 1241cb0ef41Sopenharmony_ci if (defaultVer && 1251cb0ef41Sopenharmony_ci (range === '*' || semver.satisfies(defaultVer, range, { loose: true })) && 1261cb0ef41Sopenharmony_ci !shouldAvoid(defaultVer, avoid)) { 1271cb0ef41Sopenharmony_ci const mani = versions[defaultVer] 1281cb0ef41Sopenharmony_ci if (mani && isBefore(verTimes, defaultVer, time)) { 1291cb0ef41Sopenharmony_ci return mani 1301cb0ef41Sopenharmony_ci } 1311cb0ef41Sopenharmony_ci } 1321cb0ef41Sopenharmony_ci 1331cb0ef41Sopenharmony_ci // ok, actually have to sort the list and take the winner 1341cb0ef41Sopenharmony_ci const allEntries = Object.entries(versions) 1351cb0ef41Sopenharmony_ci .concat(Object.entries(staged)) 1361cb0ef41Sopenharmony_ci .concat(Object.entries(restricted)) 1371cb0ef41Sopenharmony_ci .filter(([ver, mani]) => isBefore(verTimes, ver, time)) 1381cb0ef41Sopenharmony_ci 1391cb0ef41Sopenharmony_ci if (!allEntries.length) { 1401cb0ef41Sopenharmony_ci throw Object.assign(new Error(`No versions available for ${name}`), { 1411cb0ef41Sopenharmony_ci code: 'ENOVERSIONS', 1421cb0ef41Sopenharmony_ci name, 1431cb0ef41Sopenharmony_ci type, 1441cb0ef41Sopenharmony_ci wanted, 1451cb0ef41Sopenharmony_ci before, 1461cb0ef41Sopenharmony_ci versions: Object.keys(versions), 1471cb0ef41Sopenharmony_ci }) 1481cb0ef41Sopenharmony_ci } 1491cb0ef41Sopenharmony_ci 1501cb0ef41Sopenharmony_ci const sortSemverOpt = { loose: true } 1511cb0ef41Sopenharmony_ci const entries = allEntries.filter(([ver, mani]) => 1521cb0ef41Sopenharmony_ci semver.satisfies(ver, range, { loose: true })) 1531cb0ef41Sopenharmony_ci .sort((a, b) => { 1541cb0ef41Sopenharmony_ci const [vera, mania] = a 1551cb0ef41Sopenharmony_ci const [verb, manib] = b 1561cb0ef41Sopenharmony_ci const notavoida = !shouldAvoid(vera, avoid) 1571cb0ef41Sopenharmony_ci const notavoidb = !shouldAvoid(verb, avoid) 1581cb0ef41Sopenharmony_ci const notrestra = !restricted[a] 1591cb0ef41Sopenharmony_ci const notrestrb = !restricted[b] 1601cb0ef41Sopenharmony_ci const notstagea = !staged[a] 1611cb0ef41Sopenharmony_ci const notstageb = !staged[b] 1621cb0ef41Sopenharmony_ci const notdepra = !mania.deprecated 1631cb0ef41Sopenharmony_ci const notdeprb = !manib.deprecated 1641cb0ef41Sopenharmony_ci const enginea = engineOk(mania, npmVersion, nodeVersion) 1651cb0ef41Sopenharmony_ci const engineb = engineOk(manib, npmVersion, nodeVersion) 1661cb0ef41Sopenharmony_ci // sort by: 1671cb0ef41Sopenharmony_ci // - not an avoided version 1681cb0ef41Sopenharmony_ci // - not restricted 1691cb0ef41Sopenharmony_ci // - not staged 1701cb0ef41Sopenharmony_ci // - not deprecated and engine ok 1711cb0ef41Sopenharmony_ci // - engine ok 1721cb0ef41Sopenharmony_ci // - not deprecated 1731cb0ef41Sopenharmony_ci // - semver 1741cb0ef41Sopenharmony_ci return (notavoidb - notavoida) || 1751cb0ef41Sopenharmony_ci (notrestrb - notrestra) || 1761cb0ef41Sopenharmony_ci (notstageb - notstagea) || 1771cb0ef41Sopenharmony_ci ((notdeprb && engineb) - (notdepra && enginea)) || 1781cb0ef41Sopenharmony_ci (engineb - enginea) || 1791cb0ef41Sopenharmony_ci (notdeprb - notdepra) || 1801cb0ef41Sopenharmony_ci semver.rcompare(vera, verb, sortSemverOpt) 1811cb0ef41Sopenharmony_ci }) 1821cb0ef41Sopenharmony_ci 1831cb0ef41Sopenharmony_ci return decorateAvoid(entries[0] && entries[0][1], avoid) 1841cb0ef41Sopenharmony_ci} 1851cb0ef41Sopenharmony_ci 1861cb0ef41Sopenharmony_cimodule.exports = (packument, wanted, opts = {}) => { 1871cb0ef41Sopenharmony_ci const mani = pickManifest(packument, wanted, opts) 1881cb0ef41Sopenharmony_ci const picked = mani && normalizeBin(mani) 1891cb0ef41Sopenharmony_ci const policyRestrictions = packument.policyRestrictions 1901cb0ef41Sopenharmony_ci const restricted = (policyRestrictions && policyRestrictions.versions) || {} 1911cb0ef41Sopenharmony_ci 1921cb0ef41Sopenharmony_ci if (picked && !restricted[picked.version]) { 1931cb0ef41Sopenharmony_ci return picked 1941cb0ef41Sopenharmony_ci } 1951cb0ef41Sopenharmony_ci 1961cb0ef41Sopenharmony_ci const { before = null, defaultTag = 'latest' } = opts 1971cb0ef41Sopenharmony_ci const bstr = before ? new Date(before).toLocaleString() : '' 1981cb0ef41Sopenharmony_ci const { name } = packument 1991cb0ef41Sopenharmony_ci const pckg = `${name}@${wanted}` + 2001cb0ef41Sopenharmony_ci (before ? ` with a date before ${bstr}` : '') 2011cb0ef41Sopenharmony_ci 2021cb0ef41Sopenharmony_ci const isForbidden = picked && !!restricted[picked.version] 2031cb0ef41Sopenharmony_ci const polMsg = isForbidden ? policyRestrictions.message : '' 2041cb0ef41Sopenharmony_ci 2051cb0ef41Sopenharmony_ci const msg = !isForbidden ? `No matching version found for ${pckg}.` 2061cb0ef41Sopenharmony_ci : `Could not download ${pckg} due to policy violations:\n${polMsg}` 2071cb0ef41Sopenharmony_ci 2081cb0ef41Sopenharmony_ci const code = isForbidden ? 'E403' : 'ETARGET' 2091cb0ef41Sopenharmony_ci throw Object.assign(new Error(msg), { 2101cb0ef41Sopenharmony_ci code, 2111cb0ef41Sopenharmony_ci type: npa.resolve(packument.name, wanted).type, 2121cb0ef41Sopenharmony_ci wanted, 2131cb0ef41Sopenharmony_ci versions: Object.keys(packument.versions ?? {}), 2141cb0ef41Sopenharmony_ci name, 2151cb0ef41Sopenharmony_ci distTags: packument['dist-tags'], 2161cb0ef41Sopenharmony_ci defaultTag, 2171cb0ef41Sopenharmony_ci }) 2181cb0ef41Sopenharmony_ci} 219