@@ -4,7 +4,7 @@ module.exports.resolve = resolve
44module . exports . toPurl = toPurl
55module . exports . Result = Result
66
7- const url = require ( 'url' )
7+ const { URL } = require ( 'url' )
88const HostedGit = require ( 'hosted-git-info' )
99const semver = require ( 'semver' )
1010const path = global . FAKE_WINDOWS ? require ( 'path' ) . win32 : require ( 'path' )
@@ -183,10 +183,11 @@ Result.prototype.toJSON = function () {
183183 return result
184184}
185185
186- function setGitCommittish ( res , committish ) {
186+ // sets res.gitCommittish, res.gitRange, and res.gitSubdir
187+ function setGitAttrs ( res , committish ) {
187188 if ( ! committish ) {
188189 res . gitCommittish = null
189- return res
190+ return
190191 }
191192
192193 // for each :: separated item:
@@ -224,8 +225,6 @@ function setGitCommittish (res, committish) {
224225 }
225226 log . warn ( 'npm-package-arg' , `ignoring unknown key "${ name } "` )
226227 }
227-
228- return res
229228}
230229
231230function fromFile ( res , where ) {
@@ -245,10 +244,10 @@ function fromFile (res, where) {
245244 const rawWithPrefix = prefix + res . rawSpec
246245 let rawNoPrefix = rawWithPrefix . replace ( / ^ f i l e : / , '' )
247246 try {
248- resolvedUrl = new url . URL ( rawWithPrefix , `file://${ path . resolve ( where ) } /` )
249- specUrl = new url . URL ( rawWithPrefix )
247+ resolvedUrl = new URL ( rawWithPrefix , `file://${ path . resolve ( where ) } /` )
248+ specUrl = new URL ( rawWithPrefix )
250249 } catch ( originalError ) {
251- const er = new Error ( 'Invalid file: URL, must comply with RFC 8909 ' )
250+ const er = new Error ( 'Invalid file: URL, must comply with RFC 8089 ' )
252251 throw Object . assign ( er , {
253252 raw : res . rawSpec ,
254253 spec : res ,
@@ -257,23 +256,23 @@ function fromFile (res, where) {
257256 } )
258257 }
259258
260- // XXX backwards compatibility lack of compliance with RFC 8909
259+ // XXX backwards compatibility lack of compliance with RFC 8089
261260 if ( resolvedUrl . host && resolvedUrl . host !== 'localhost' ) {
262261 const rawSpec = res . rawSpec . replace ( / ^ f i l e : \/ \/ / , 'file:///' )
263- resolvedUrl = new url . URL ( rawSpec , `file://${ path . resolve ( where ) } /` )
264- specUrl = new url . URL ( rawSpec )
262+ resolvedUrl = new URL ( rawSpec , `file://${ path . resolve ( where ) } /` )
263+ specUrl = new URL ( rawSpec )
265264 rawNoPrefix = rawSpec . replace ( / ^ f i l e : / , '' )
266265 }
267266 // turn file:/../foo into file:../foo
268267 // for 1, 2 or 3 leading slashes since we attempted
269268 // in the previous step to make it a file protocol url with a leading slash
270269 if ( / ^ \/ { 1 , 3 } \. \. ? ( \/ | $ ) / . test ( rawNoPrefix ) ) {
271270 const rawSpec = res . rawSpec . replace ( / ^ f i l e : \/ { 1 , 3 } / , 'file:' )
272- resolvedUrl = new url . URL ( rawSpec , `file://${ path . resolve ( where ) } /` )
273- specUrl = new url . URL ( rawSpec )
271+ resolvedUrl = new URL ( rawSpec , `file://${ path . resolve ( where ) } /` )
272+ specUrl = new URL ( rawSpec )
274273 rawNoPrefix = rawSpec . replace ( / ^ f i l e : / , '' )
275274 }
276- // XXX end RFC 8909 violation backwards compatibility section
275+ // XXX end RFC 8089 violation backwards compatibility section
277276
278277 // turn /C:/blah into just C:/blah on windows
279278 let specPath = decodeURIComponent ( specUrl . pathname )
@@ -303,7 +302,8 @@ function fromHostedGit (res, hosted) {
303302 res . hosted = hosted
304303 res . saveSpec = hosted . toString ( { noGitPlus : false , noCommittish : false } )
305304 res . fetchSpec = hosted . getDefaultRepresentation ( ) === 'shortcut' ? null : hosted . toString ( )
306- return setGitCommittish ( res , hosted . committish )
305+ setGitAttrs ( res , hosted . committish )
306+ return res
307307}
308308
309309function unsupportedURLType ( protocol , spec ) {
@@ -312,62 +312,59 @@ function unsupportedURLType (protocol, spec) {
312312 return err
313313}
314314
315- function matchGitScp ( spec ) {
316- // git ssh specifiers are overloaded to also use scp-style git
317- // specifiers, so we have to parse those out and treat them special.
318- // They are NOT true URIs, so we can't hand them to `url.parse`.
319- //
320- // This regex looks for things that look like:
321- // git+ssh://[email protected] :username/project.git#deadbeef 322- //
323- // ...and various combinations. The username in the beginning is *required*.
324- const matched = spec . match ( / ^ g i t \+ s s h : \/ \/ ( [ ^ : # ] + : [ ^ # ] + (?: \. g i t ) ? ) (?: # ( .* ) ) ? $ / i)
325- return matched && ! matched [ 1 ] . match ( / : [ 0 - 9 ] + \/ ? .* $ / i) && {
326- fetchSpec : matched [ 1 ] ,
327- gitCommittish : matched [ 2 ] == null ? null : matched [ 2 ] ,
328- }
329- }
330-
331315function fromURL ( res ) {
332- // eslint-disable-next-line node/no-deprecated-api
333- const urlparse = url . parse ( res . rawSpec )
334- res . saveSpec = res . rawSpec
316+ let rawSpec = res . rawSpec
317+ res . saveSpec = rawSpec
318+ if ( rawSpec . startsWith ( 'git+ssh:' ) ) {
319+ // git ssh specifiers are overloaded to also use scp-style git
320+ // specifiers, so we have to parse those out and treat them special.
321+ // They are NOT true URIs, so we can't hand them to URL.
322+
323+ // This regex looks for things that look like:
324+ // git+ssh://[email protected] :username/project.git#deadbeef 325+ // ...and various combinations. The username in the beginning is *required*.
326+ const matched = rawSpec . match ( / ^ g i t \+ s s h : \/ \/ ( [ ^ : # ] + : [ ^ # ] + (?: \. g i t ) ? ) (?: # ( .* ) ) ? $ / i)
327+ if ( matched && ! matched [ 1 ] . match ( / : [ 0 - 9 ] + \/ ? .* $ / i) ) {
328+ res . type = 'git'
329+ setGitAttrs ( res , matched [ 2 ] )
330+ res . fetchSpec = matched [ 1 ]
331+ return res
332+ }
333+ } else if ( rawSpec . startsWith ( 'git+file://' ) ) {
334+ // URL can't handle windows paths
335+ rawSpec = rawSpec . replace ( / \\ / g, '/' )
336+ }
337+ const parsedUrl = new URL ( rawSpec )
335338 // check the protocol, and then see if it's git or not
336- switch ( urlparse . protocol ) {
339+ switch ( parsedUrl . protocol ) {
337340 case 'git:' :
338341 case 'git+http:' :
339342 case 'git+https:' :
340343 case 'git+rsync:' :
341344 case 'git+ftp:' :
342345 case 'git+file:' :
343- case 'git+ssh:' : {
346+ case 'git+ssh:' :
344347 res . type = 'git'
345- const match = urlparse . protocol === 'git+ssh:' ? matchGitScp ( res . rawSpec )
346- : null
347- if ( match ) {
348- setGitCommittish ( res , match . gitCommittish )
349- res . fetchSpec = match . fetchSpec
348+ setGitAttrs ( res , parsedUrl . hash . slice ( 1 ) )
349+ if ( parsedUrl . protocol === 'git+file:' && / ^ g i t \+ f i l e : \/ \/ [ a - z ] : / i. test ( rawSpec ) ) {
350+ // URL can't handle drive letters on windows file paths, the host can't contain a :
351+ res . fetchSpec = `git+file://${ parsedUrl . host . toLowerCase ( ) } :${ parsedUrl . pathname } `
350352 } else {
351- setGitCommittish ( res , urlparse . hash != null ? urlparse . hash . slice ( 1 ) : '' )
352- urlparse . protocol = urlparse . protocol . replace ( / ^ g i t [ + ] / , '' )
353- if ( urlparse . protocol === 'file:' && / ^ g i t \+ f i l e : \/ \/ [ a - z ] : / i. test ( res . rawSpec ) ) {
354- // keep the drive letter : on windows file paths
355- urlparse . host += ':'
356- urlparse . hostname += ':'
357- }
358- delete urlparse . hash
359- res . fetchSpec = url . format ( urlparse )
353+ parsedUrl . hash = ''
354+ res . fetchSpec = parsedUrl . toString ( )
355+ }
356+ if ( res . fetchSpec . startsWith ( 'git+' ) ) {
357+ res . fetchSpec = res . fetchSpec . slice ( 4 )
360358 }
361359 break
362- }
363360 case 'http:' :
364361 case 'https:' :
365362 res . type = 'remote'
366363 res . fetchSpec = res . saveSpec
367364 break
368365
369366 default :
370- throw unsupportedURLType ( urlparse . protocol , res . rawSpec )
367+ throw unsupportedURLType ( parsedUrl . protocol , rawSpec )
371368 }
372369
373370 return res
0 commit comments