Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Teach update-dependency about more range specifiers and make it adopt the current range for any tag #7709

Merged
merged 1 commit into from Jan 2, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
65 changes: 44 additions & 21 deletions buildutils/src/update-dependency.ts
Expand Up @@ -12,32 +12,55 @@ import commander from 'commander';
import semver from 'semver';

let versionCache = new Map();
const tags = /^([~^]?)([\w\-.]*)$/;

/**
* Matches a simple semver range, where the version number could be an npm tag.
*/
const SEMVER_RANGE = /^(~|\^|=|<|>|<=|>=)?([\w\-.]*)$/;

/**
* Get the specifier we should use
*
* @param currentSpecifier - The current package version.
* @param suggestedSpecifier - The package version we would like to use.
*
* #### Notes
* If the suggested specifier is not a valid range, we assume it is of the
* form ${RANGE}${TAG}, where TAG is an npm tag (such as 'latest') and RANGE
* is either a semver range indicator (one of `~, ^, >, <, =, >=, <=`), or is
* not given (in which case the current specifier range prefix is used).
*/
async function getSpecifier(
currentSpecifier: string,
suggestedSpecifier: string
) {
// we have an special case where `latest` will use the same current specifier
// e.g. when using latest:
// ^current should result in ^new
// ~current should result in ~new
if (semver.validRange(suggestedSpecifier)) {
return suggestedSpecifier;
}

if (semver.validRange(suggestedSpecifier) === null) {
// We have a tag, with possibly a range specifier, such as ^latest
let specifier = suggestedSpecifier;
if (specifier === 'latest') {
specifier = currentSpecifier;
}
let match = specifier.match(tags);
// The suggested specifier is not a valid range, so we assume it
// references a tag
let [, suggestedSigil, suggestedTag] =
suggestedSpecifier.match(SEMVER_RANGE) ?? [];

if (!suggestedTag) {
throw Error(`Invalid version specifier: ${suggestedSpecifier}`);
}

// A tag with no sigil adopts the sigil from the current specification
if (!suggestedSigil) {
let match = currentSpecifier.match(SEMVER_RANGE);
if (match === null) {
throw Error(`Invalid version specifier: ${specifier}`);
throw Error(
`Current version range is not recognized: ${currentSpecifier}`
);
}
let [, currentSigil] = match;
if (currentSigil) {
suggestedSigil = currentSigil;
}
specifier = match[1] + suggestedSpecifier;
return specifier;
} else {
return suggestedSpecifier;
}
return `${suggestedSigil}${suggestedTag}`;
}

async function getVersion(pkg: string, specifier: string) {
Expand All @@ -47,7 +70,7 @@ async function getVersion(pkg: string, specifier: string) {
}
if (semver.validRange(specifier) === null) {
// We have a tag, with possibly a range specifier, such as ^latest
let match = specifier.match(tags);
let match = specifier.match(SEMVER_RANGE);
if (match === null) {
throw Error(`Invalid version specifier: ${specifier}`);
}
Expand All @@ -70,10 +93,10 @@ async function getVersion(pkg: string, specifier: string) {
*/
function subset(range1: string, range2: string): boolean {
try {
const [, r1, version1] = range1.match(tags)!;
const [, r2] = range2.match(tags)!;
const [, r1, version1] = range1.match(SEMVER_RANGE) ?? [];
const [, r2] = range2.match(SEMVER_RANGE) ?? [];
return (
['', '~', '^'].indexOf(r1) >= 0 &&
['', '>=', '=', '~', '^'].includes(r1) &&
r1 === r2 &&
!!semver.valid(version1) &&
semver.satisfies(version1, range2)
Expand Down