diff --git a/lib/index.js b/lib/index.js index 8280797..a724e76 100644 --- a/lib/index.js +++ b/lib/index.js @@ -35,6 +35,7 @@ const pickManifest = (packument, wanted, opts) => { includeStaged = false, avoid = null, avoidStrict = false, + engineStrict = false, } = opts const { name, time: verTimes } = packument @@ -101,7 +102,18 @@ const pickManifest = (packument, wanted, opts) => { // we use that. Otherwise, we get the highest precedence version // prior to the dist-tag. if (isBefore(verTimes, ver, time)) { - return decorateAvoid(versions[ver] || staged[ver] || restricted[ver], avoid) + const mani = versions[ver] || staged[ver] || restricted[ver] + if (engineStrict && mani && !engineOk(mani, npmVersion, nodeVersion)) { + throw Object.assign(new Error(`No compatible version found: ${name}@${ver}`), { + code: 'ENOTENGINECOMPLIANT', + type, + wanted, + versions: Object.keys(versions), + name, + distTags, + }) + } + return decorateAvoid(mani, avoid) } else { return pickManifest(packument, `<=${ver}`, opts) } @@ -111,7 +123,21 @@ const pickManifest = (packument, wanted, opts) => { if (wanted && type === 'version') { const ver = semver.clean(wanted, { loose: true }) const mani = versions[ver] || staged[ver] || restricted[ver] - return isBefore(verTimes, ver, time) ? decorateAvoid(mani, avoid) : null + if (isBefore(verTimes, ver, time)) { + if (engineStrict && mani && !engineOk(mani, npmVersion, nodeVersion)) { + throw Object.assign(new Error(`No compatible version found: ${name}@${ver}`), { + code: 'ENOTENGINECOMPLIANT', + type, + wanted, + versions: Object.keys(versions), + name, + distTags, + }) + } + return decorateAvoid(mani, avoid) + } else { + return null + } } // ok, sort based on our heuristics, and pick the best fit @@ -153,38 +179,58 @@ const pickManifest = (packument, wanted, opts) => { }) } - const sortSemverOpt = { loose: true } - const entries = allEntries.filter(([ver]) => + let entries = allEntries.filter(([ver]) => semver.satisfies(ver, range, { loose: true })) - .sort((a, b) => { - const [vera, mania] = a - const [verb, manib] = b - const notavoida = !shouldAvoid(vera, avoid) - const notavoidb = !shouldAvoid(verb, avoid) - const notrestra = !restricted[vera] - const notrestrb = !restricted[verb] - const notstagea = !staged[vera] - const notstageb = !staged[verb] - const notdepra = !mania.deprecated - const notdeprb = !manib.deprecated - const enginea = engineOk(mania, npmVersion, nodeVersion) - const engineb = engineOk(manib, npmVersion, nodeVersion) - // sort by: - // - not an avoided version - // - not restricted - // - not staged - // - not deprecated and engine ok - // - engine ok - // - not deprecated - // - semver - return (notavoidb - notavoida) || - (notrestrb - notrestra) || - (notstageb - notstagea) || - ((notdeprb && engineb) - (notdepra && enginea)) || - (engineb - enginea) || - (notdeprb - notdepra) || - semver.rcompare(vera, verb, sortSemverOpt) + + // **Engine-Strict Filtering** + if (engineStrict) { + entries = entries.filter(([, mani]) => + engineOk(mani, npmVersion, nodeVersion)) + } + + if (!entries.length) { + throw Object.assign(new Error(`No compatible version found: ${name}@${wanted}`), { + code: 'ENOTENGINECOMPLIANT', + name, + type, + wanted, + versions: Object.keys(versions), + nodeVersion, + npmVersion, }) + } + + // Sort the entries to pick the best version + const sortSemverOpt = { loose: true } + entries = entries.sort((a, b) => { + const [vera, mania] = a + const [verb, manib] = b + const notavoida = !shouldAvoid(vera, avoid) + const notavoidb = !shouldAvoid(verb, avoid) + const notrestra = !restricted[vera] + const notrestrb = !restricted[verb] + const notstagea = !staged[vera] + const notstageb = !staged[verb] + const notdepra = !mania.deprecated + const notdeprb = !manib.deprecated + const enginea = engineOk(mania, npmVersion, nodeVersion) + const engineb = engineOk(manib, npmVersion, nodeVersion) + // Sort by: + // - Not an avoided version + // - Not restricted + // - Not staged + // - Not deprecated and engine ok + // - Engine ok + // - Not deprecated + // - Semver + return (notavoidb - notavoida) || + (notrestrb - notrestra) || + (notstageb - notstagea) || + ((notdeprb && engineb) - (notdepra && enginea)) || + (engineb - enginea) || + (notdeprb - notdepra) || + semver.rcompare(vera, verb, sortSemverOpt) + }) return decorateAvoid(entries[0] && entries[0][1], avoid) }