codekingpro/portable-devtools
115k
1const url = require('url')
2
3const lastIndexOfBefore = (str, char, beforeChar) => {
4 const startPosition = str.indexOf(beforeChar)
5 return str.lastIndexOf(char, startPosition > -1 ? startPosition : Infinity)
6}
7
8const safeUrl = (u) => {
9 try {
10 return new url.URL(u)
11 } catch {
12 // this fn should never throw
13 }
14}
15
16// accepts input like git:github.com:user/repo and inserts the // after the first :
17const correctProtocol = (arg, protocols) => {
18 const firstColon = arg.indexOf(':')
19 const proto = arg.slice(0, firstColon + 1)
20 if (Object.prototype.hasOwnProperty.call(protocols, proto)) {
21 return arg
22 }
23
24 if (arg.substr(firstColon, 3) === '://') {
25 // If arg is given as <foo>://<bar>, then this is already a valid URL.
26 return arg
27 }
28
29 const firstAt = arg.indexOf('@')
30 if (firstAt > -1) {
31 if (firstAt > firstColon) {
32 // URL has the form of <foo>:<bar>@<baz>. Assume this is a git+ssh URL.
33 return `git+ssh://${arg}`
34 } else {
35 // URL has the form 'git@github.com:npm/hosted-git-info.git'.
36 return arg
37 }
38 }
39
40 // Correct <foo>:<bar> to <foo>://<bar>
41 return `${arg.slice(0, firstColon + 1)}//${arg.slice(firstColon + 1)}`
42}
43
44// attempt to correct an scp style url so that it will parse with `new URL()`
45const correctUrl = (giturl) => {
46 // ignore @ that come after the first hash since the denotes the start
47 // of a committish which can contain @ characters
48 const firstAt = lastIndexOfBefore(giturl, '@', '#')
49 // ignore colons that come after the hash since that could include colons such as:
50 // git@github.com:user/package-2#semver:^1.0.0
51 const lastColonBeforeHash = lastIndexOfBefore(giturl, ':', '#')
52
53 if (lastColonBeforeHash > firstAt) {
54 // the last : comes after the first @ (or there is no @)
55 // like it would in:
56 // proto://hostname.com:user/repo
57 // username@hostname.com:user/repo
58 // :password@hostname.com:user/repo
59 // username:password@hostname.com:user/repo
60 // proto://username@hostname.com:user/repo
61 // proto://:password@hostname.com:user/repo
62 // proto://username:password@hostname.com:user/repo
63 // then we replace the last : with a / to create a valid path
64 giturl = giturl.slice(0, lastColonBeforeHash) + '/' + giturl.slice(lastColonBeforeHash + 1)
65 }
66
67 if (lastIndexOfBefore(giturl, ':', '#') === -1 && giturl.indexOf('//') === -1) {
68 // we have no : at all
69 // as it would be in:
70 // username@hostname.com/user/repo
71 // then we prepend a protocol
72 giturl = `git+ssh://${giturl}`
73 }
74
75 return giturl
76}
77
78module.exports = (giturl, protocols) => {
79 const withProtocol = protocols ? correctProtocol(giturl, protocols) : giturl
80 return safeUrl(withProtocol) || safeUrl(correctUrl(withProtocol))
81}
82 