From 1b7c82a741ef6d5672200ee01a152011ca446397 Mon Sep 17 00:00:00 2001 From: Andrei Neculau Date: Sat, 18 Mar 2023 20:15:16 +0100 Subject: [PATCH] handle git+ssh with semver --- resolving/git-resolver/src/parsePref.ts | 67 ++++++++++++++----------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/resolving/git-resolver/src/parsePref.ts b/resolving/git-resolver/src/parsePref.ts index 8f66058a74b..30a979a8258 100644 --- a/resolving/git-resolver/src/parsePref.ts +++ b/resolving/git-resolver/src/parsePref.ts @@ -38,15 +38,8 @@ export async function parsePref (pref: string): Promise 1) ? decodeURIComponent(urlparse.hash.slice(1)) : null return { @@ -58,13 +51,6 @@ export async function parsePref (pref: string): Promise e.replace(/:([^/\d]|\d+[^:/\d])/, ':/$1')) - return [front, ...escapedBacks].join('@') -} - function urlToFetchSpec (urlparse: URL) { urlparse.hash = '' const fetchSpec = url.format(urlparse) @@ -151,18 +137,43 @@ function setGitCommittish (committish: string | null) { return { gitCommittish: committish } } -function matchGitScp (spec: string) { - // git ssh specifiers are overloaded to also use scp-style git - // specifiers, so we have to parse those out and treat them special. - // They are NOT true URIs, so we can't hand them to `url.parse`. - // - // This regex looks for things that look like: - // git+ssh://git@my.custom.git.com:username/project.git#deadbeef - // - // ...and various combinations. The username in the beginning is *required*. - const matched = spec.match(/^git\+ssh:\/\/([^:]+:[^#]+(?:\.git)?)(?:#(.*))$/i) - return (matched != null) && (matched[1].match(/:[0-9]+\/?.*$/i) == null) && { - fetchSpec: matched[1], - gitCommittish: matched[2], +// see https://github.com/npm/hosted-git-info/blob/f03bfbd3022c8f6283a991ff879ed97704ac35fa/lib/parse-url.js#L3 +function lastIndexOfBefore (str: string, char: string, beforeChar: string) { + const startPosition = str.indexOf(beforeChar) + return str.lastIndexOf(char, startPosition > -1 ? startPosition : Infinity) +} + +// see https://github.com/npm/hosted-git-info/blob/f03bfbd3022c8f6283a991ff879ed97704ac35fa/lib/parse-url.js#L41 +// attempt to correct an scp style url so that it will parse with `new URL()` +function correctUrl (giturl: string) { + // ignore @ that come after the first hash since the denotes the start + // of a committish which can contain @ characters + const firstAt = lastIndexOfBefore(giturl, '@', '#') + // ignore colons that come after the hash since that could include colons such as: + // git@github.com:user/package-2#semver:^1.0.0 + const lastColonBeforeHash = lastIndexOfBefore(giturl, ':', '#') + + if (lastColonBeforeHash > firstAt) { + // the last : comes after the first @ (or there is no @) + // like it would in: + // proto://hostname.com:user/repo + // username@hostname.com:user/repo + // :password@hostname.com:user/repo + // username:password@hostname.com:user/repo + // proto://username@hostname.com:user/repo + // proto://:password@hostname.com:user/repo + // proto://username:password@hostname.com:user/repo + // then we replace the last : with a / to create a valid path + giturl = giturl.slice(0, lastColonBeforeHash) + '/' + giturl.slice(lastColonBeforeHash + 1) } + + if (lastIndexOfBefore(giturl, ':', '#') === -1 && giturl.indexOf('//') === -1) { + // we have no : at all + // as it would be in: + // username@hostname.com/user/repo + // then we prepend a protocol + giturl = `git+ssh://${giturl}` + } + + return giturl }