source: trip-planner-front/node_modules/hosted-git-info/index.js@ 76712b2

Last change on this file since 76712b2 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 7.2 KB
Line 
1'use strict'
2const url = require('url')
3const gitHosts = require('./git-host-info.js')
4const GitHost = module.exports = require('./git-host.js')
5const LRU = require('lru-cache')
6const cache = new LRU({ max: 1000 })
7
8const protocolToRepresentationMap = {
9 'git+ssh:': 'sshurl',
10 'git+https:': 'https',
11 'ssh:': 'sshurl',
12 'git:': 'git'
13}
14
15function protocolToRepresentation (protocol) {
16 return protocolToRepresentationMap[protocol] || protocol.slice(0, -1)
17}
18
19const authProtocols = {
20 'git:': true,
21 'https:': true,
22 'git+https:': true,
23 'http:': true,
24 'git+http:': true
25}
26
27const knownProtocols = Object.keys(gitHosts.byShortcut).concat(['http:', 'https:', 'git:', 'git+ssh:', 'git+https:', 'ssh:'])
28
29module.exports.fromUrl = function (giturl, opts) {
30 if (typeof giturl !== 'string') {
31 return
32 }
33
34 const key = giturl + JSON.stringify(opts || {})
35
36 if (!cache.has(key)) {
37 cache.set(key, fromUrl(giturl, opts))
38 }
39
40 return cache.get(key)
41}
42
43function fromUrl (giturl, opts) {
44 if (!giturl) {
45 return
46 }
47
48 const url = isGitHubShorthand(giturl) ? 'github:' + giturl : correctProtocol(giturl)
49 const parsed = parseGitUrl(url)
50 if (!parsed) {
51 return parsed
52 }
53
54 const gitHostShortcut = gitHosts.byShortcut[parsed.protocol]
55 const gitHostDomain = gitHosts.byDomain[parsed.hostname.startsWith('www.') ? parsed.hostname.slice(4) : parsed.hostname]
56 const gitHostName = gitHostShortcut || gitHostDomain
57 if (!gitHostName) {
58 return
59 }
60
61 const gitHostInfo = gitHosts[gitHostShortcut || gitHostDomain]
62 let auth = null
63 if (authProtocols[parsed.protocol] && (parsed.username || parsed.password)) {
64 auth = `${parsed.username}${parsed.password ? ':' + parsed.password : ''}`
65 }
66
67 let committish = null
68 let user = null
69 let project = null
70 let defaultRepresentation = null
71
72 try {
73 if (gitHostShortcut) {
74 let pathname = parsed.pathname.startsWith('/') ? parsed.pathname.slice(1) : parsed.pathname
75 const firstAt = pathname.indexOf('@')
76 // we ignore auth for shortcuts, so just trim it out
77 if (firstAt > -1) {
78 pathname = pathname.slice(firstAt + 1)
79 }
80
81 const lastSlash = pathname.lastIndexOf('/')
82 if (lastSlash > -1) {
83 user = decodeURIComponent(pathname.slice(0, lastSlash))
84 // we want nulls only, never empty strings
85 if (!user) {
86 user = null
87 }
88 project = decodeURIComponent(pathname.slice(lastSlash + 1))
89 } else {
90 project = decodeURIComponent(pathname)
91 }
92
93 if (project.endsWith('.git')) {
94 project = project.slice(0, -4)
95 }
96
97 if (parsed.hash) {
98 committish = decodeURIComponent(parsed.hash.slice(1))
99 }
100
101 defaultRepresentation = 'shortcut'
102 } else {
103 if (!gitHostInfo.protocols.includes(parsed.protocol)) {
104 return
105 }
106
107 const segments = gitHostInfo.extract(parsed)
108 if (!segments) {
109 return
110 }
111
112 user = segments.user && decodeURIComponent(segments.user)
113 project = decodeURIComponent(segments.project)
114 committish = decodeURIComponent(segments.committish)
115 defaultRepresentation = protocolToRepresentation(parsed.protocol)
116 }
117 } catch (err) {
118 /* istanbul ignore else */
119 if (err instanceof URIError) {
120 return
121 } else {
122 throw err
123 }
124 }
125
126 return new GitHost(gitHostName, user, auth, project, committish, defaultRepresentation, opts)
127}
128
129// accepts input like git:github.com:user/repo and inserts the // after the first :
130const correctProtocol = (arg) => {
131 const firstColon = arg.indexOf(':')
132 const proto = arg.slice(0, firstColon + 1)
133 if (knownProtocols.includes(proto)) {
134 return arg
135 }
136
137 const firstAt = arg.indexOf('@')
138 if (firstAt > -1) {
139 if (firstAt > firstColon) {
140 return `git+ssh://${arg}`
141 } else {
142 return arg
143 }
144 }
145
146 const doubleSlash = arg.indexOf('//')
147 if (doubleSlash === firstColon + 1) {
148 return arg
149 }
150
151 return arg.slice(0, firstColon + 1) + '//' + arg.slice(firstColon + 1)
152}
153
154// look for github shorthand inputs, such as npm/cli
155const isGitHubShorthand = (arg) => {
156 // it cannot contain whitespace before the first #
157 // it cannot start with a / because that's probably an absolute file path
158 // but it must include a slash since repos are username/repository
159 // it cannot start with a . because that's probably a relative file path
160 // it cannot start with an @ because that's a scoped package if it passes the other tests
161 // it cannot contain a : before a # because that tells us that there's a protocol
162 // a second / may not exist before a #
163 const firstHash = arg.indexOf('#')
164 const firstSlash = arg.indexOf('/')
165 const secondSlash = arg.indexOf('/', firstSlash + 1)
166 const firstColon = arg.indexOf(':')
167 const firstSpace = /\s/.exec(arg)
168 const firstAt = arg.indexOf('@')
169
170 const spaceOnlyAfterHash = !firstSpace || (firstHash > -1 && firstSpace.index > firstHash)
171 const atOnlyAfterHash = firstAt === -1 || (firstHash > -1 && firstAt > firstHash)
172 const colonOnlyAfterHash = firstColon === -1 || (firstHash > -1 && firstColon > firstHash)
173 const secondSlashOnlyAfterHash = secondSlash === -1 || (firstHash > -1 && secondSlash > firstHash)
174 const hasSlash = firstSlash > 0
175 // if a # is found, what we really want to know is that the character immediately before # is not a /
176 const doesNotEndWithSlash = firstHash > -1 ? arg[firstHash - 1] !== '/' : !arg.endsWith('/')
177 const doesNotStartWithDot = !arg.startsWith('.')
178
179 return spaceOnlyAfterHash && hasSlash && doesNotEndWithSlash && doesNotStartWithDot && atOnlyAfterHash && colonOnlyAfterHash && secondSlashOnlyAfterHash
180}
181
182// attempt to correct an scp style url so that it will parse with `new URL()`
183const correctUrl = (giturl) => {
184 const firstAt = giturl.indexOf('@')
185 const lastHash = giturl.lastIndexOf('#')
186 let firstColon = giturl.indexOf(':')
187 let lastColon = giturl.lastIndexOf(':', lastHash > -1 ? lastHash : Infinity)
188
189 let corrected
190 if (lastColon > firstAt) {
191 // the last : comes after the first @ (or there is no @)
192 // like it would in:
193 // proto://hostname.com:user/repo
194 // username@hostname.com:user/repo
195 // :password@hostname.com:user/repo
196 // username:password@hostname.com:user/repo
197 // proto://username@hostname.com:user/repo
198 // proto://:password@hostname.com:user/repo
199 // proto://username:password@hostname.com:user/repo
200 // then we replace the last : with a / to create a valid path
201 corrected = giturl.slice(0, lastColon) + '/' + giturl.slice(lastColon + 1)
202 // // and we find our new : positions
203 firstColon = corrected.indexOf(':')
204 lastColon = corrected.lastIndexOf(':')
205 }
206
207 if (firstColon === -1 && giturl.indexOf('//') === -1) {
208 // we have no : at all
209 // as it would be in:
210 // username@hostname.com/user/repo
211 // then we prepend a protocol
212 corrected = `git+ssh://${corrected}`
213 }
214
215 return corrected
216}
217
218// try to parse the url as its given to us, if that throws
219// then we try to clean the url and parse that result instead
220// THIS FUNCTION SHOULD NEVER THROW
221const parseGitUrl = (giturl) => {
222 let result
223 try {
224 result = new url.URL(giturl)
225 } catch (err) {}
226
227 if (result) {
228 return result
229 }
230
231 const correctedUrl = correctUrl(giturl)
232 try {
233 result = new url.URL(correctedUrl)
234 } catch (err) {}
235
236 return result
237}
Note: See TracBrowser for help on using the repository browser.