From 5bbc3fe69e3e03e4dd61a693a75bdc7c6ecf01f7 Mon Sep 17 00:00:00 2001 From: Yosuke Ota Date: Sat, 17 Jul 2021 16:02:18 +0900 Subject: [PATCH] Downgrade to `semver` compatible with Node v8 (#1575) * Downgrade to `semver` compatible with Node v8 * update --- lib/rules/no-unsupported-features.js | 207 ++++++++++++++++++++++++++- package.json | 2 +- 2 files changed, 207 insertions(+), 2 deletions(-) diff --git a/lib/rules/no-unsupported-features.js b/lib/rules/no-unsupported-features.js index 821a82373..821a23ae5 100644 --- a/lib/rules/no-unsupported-features.js +++ b/lib/rules/no-unsupported-features.js @@ -129,7 +129,7 @@ module.exports = { * @returns {boolean} `true` if it's supporting. */ function isNotSupportingVersion(aCase) { - return !semver.subset(versionRange, getSemverRange(aCase.supported)) + return !semverSubset(versionRange, getSemverRange(aCase.supported)) } /** @type {TemplateListener} */ @@ -163,3 +163,208 @@ module.exports = { ) } } + +// TODO replace semver.subset() in the major version. +/** + * semver.subset() + * + * We need to use a copy of the semver source code until a major version upgrade. + * + * @see https://github.com/npm/node-semver/blob/e79ac3a450e8bb504e78b8159e3efc70895699b8/ranges/subset.js#L43 + * @license ISC at Isaac Z. Schlueter and Contributors + * https://github.com/npm/node-semver/blob/master/LICENSE + * + * @param {semver.Range} sub + * @param {semver.Range} dom + */ +function semverSubset(sub, dom) { + if (sub === dom) return true + + sub = new semver.Range(sub) + dom = new semver.Range(dom) + let sawNonNull = false + + // eslint-disable-next-line no-labels + OUTER: for (const simpleSub of sub.set) { + for (const simpleDom of dom.set) { + const isSub = simpleSubset(simpleSub, simpleDom) + sawNonNull = sawNonNull || isSub !== null + // eslint-disable-next-line no-labels + if (isSub) continue OUTER + } + if (sawNonNull) return false + } + return true +} + +/** + * @license ISC at Isaac Z. Schlueter and Contributors + * https://github.com/npm/node-semver/blob/master/LICENSE + * @param {readonly semver.Comparator[]} sub + * @param {readonly semver.Comparator[]} dom + */ +function simpleSubset(sub, dom) { + if (sub === dom) return true + + /** + * @param {semver.Comparator} c + */ + function isAny(c) { + return Object.keys(c.semver).length === 0 + } + + if (sub.length === 1 && isAny(sub[0])) { + if (dom.length === 1 && isAny(dom[0])) return true + else sub = [new semver.Comparator('>=0.0.0')] + } + + if (dom.length === 1 && isAny(dom[0])) { + dom = [new semver.Comparator('>=0.0.0')] + } + + const eqSet = new Set() + let gt, lt + for (const c of sub) { + if (c.operator === '>' || c.operator === '>=') gt = higherGT(gt, c) + else if (c.operator === '<' || c.operator === '<=') lt = lowerLT(lt, c) + else eqSet.add(c.semver) + } + + if (eqSet.size > 1) return null + + let gtltComp + if (gt && lt) { + gtltComp = semver.compare(gt.semver, lt.semver) + if (gtltComp > 0) return null + else if (gtltComp === 0 && (gt.operator !== '>=' || lt.operator !== '<=')) + return null + } + + // will iterate one or zero times + for (const eq of eqSet) { + if (gt && !semver.satisfies(eq, String(gt))) return null + + if (lt && !semver.satisfies(eq, String(lt))) return null + + for (const c of dom) { + if (!semver.satisfies(eq, String(c))) return false + } + + return true + } + + let higher, lower + let hasDomLT, hasDomGT + // if the subset has a prerelease, we need a comparator in the superset + // with the same tuple and a prerelease, or it's not a subset + let needDomLTPre = lt && lt.semver.prerelease.length ? lt.semver : false + let needDomGTPre = gt && gt.semver.prerelease.length ? gt.semver : false + // exception: <1.2.3-0 is the same as <1.2.3 + if ( + needDomLTPre && + needDomLTPre.prerelease.length === 1 && + lt && + lt.operator === '<' && + needDomLTPre.prerelease[0] === 0 + ) { + needDomLTPre = false + } + + for (const c of dom) { + hasDomGT = hasDomGT || c.operator === '>' || c.operator === '>=' + hasDomLT = hasDomLT || c.operator === '<' || c.operator === '<=' + if (gt) { + if (needDomGTPre) { + if ( + c.semver.prerelease && + c.semver.prerelease.length && + c.semver.major === needDomGTPre.major && + c.semver.minor === needDomGTPre.minor && + c.semver.patch === needDomGTPre.patch + ) { + needDomGTPre = false + } + } + if (c.operator === '>' || c.operator === '>=') { + higher = higherGT(gt, c) + if (higher === c && higher !== gt) return false + } else if ( + gt.operator === '>=' && + !semver.satisfies(gt.semver, String(c)) + ) + return false + } + if (lt) { + if (needDomLTPre) { + if ( + c.semver.prerelease && + c.semver.prerelease.length && + c.semver.major === needDomLTPre.major && + c.semver.minor === needDomLTPre.minor && + c.semver.patch === needDomLTPre.patch + ) { + needDomLTPre = false + } + } + if (c.operator === '<' || c.operator === '<=') { + lower = lowerLT(lt, c) + if (lower === c && lower !== lt) return false + } else if ( + lt.operator === '<=' && + !semver.satisfies(lt.semver, String(c)) + ) + return false + } + if (!c.operator && (lt || gt) && gtltComp !== 0) return false + } + + // if there was a < or >, and nothing in the dom, then must be false + // UNLESS it was limited by another range in the other direction. + // Eg, >1.0.0 <1.0.1 is still a subset of <2.0.0 + if (gt && hasDomLT && !lt && gtltComp !== 0) return false + + if (lt && hasDomGT && !gt && gtltComp !== 0) return false + + // we needed a prerelease range in a specific tuple, but didn't get one + // then this isn't a subset. eg >=1.2.3-pre is not a subset of >=1.0.0, + // because it includes prereleases in the 1.2.3 tuple + if (needDomGTPre || needDomLTPre) return false + + return true +} + +/** + * @license ISC at Isaac Z. Schlueter and Contributors + * https://github.com/npm/node-semver/blob/master/LICENSE + * @param {semver.Comparator | void} a + * @param {semver.Comparator} b + */ +const higherGT = (a, b) => { + if (!a) return b + const comp = semver.compare(a.semver, b.semver) + return comp > 0 + ? a + : comp < 0 + ? b + : b.operator === '>' && a.operator === '>=' + ? b + : a +} + +/** + * @license ISC at Isaac Z. Schlueter and Contributors + * https://github.com/npm/node-semver/blob/master/LICENSE + * @param {semver.Comparator | void} a + * @param {semver.Comparator} b + */ +const lowerLT = (a, b) => { + if (!a) return b + const comp = semver.compare(a.semver, b.semver) + return comp < 0 + ? a + : comp > 0 + ? b + : b.operator === '<' && a.operator === '<=' + ? b + : a +} diff --git a/package.json b/package.json index f6feb58d4..5821b2500 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "dependencies": { "eslint-utils": "^2.1.0", "natural-compare": "^1.4.0", - "semver": "^7.3.2", + "semver": "^6.3.0", "vue-eslint-parser": "^7.8.0" }, "devDependencies": {