Skip to content

Commit ea724ae

Browse files
committed
fix: use URL instead of url.parse
1 parent 9576bde commit ea724ae

File tree

1 file changed

+37
-41
lines changed

1 file changed

+37
-41
lines changed

lib/npa.js

Lines changed: 37 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module.exports.resolve = resolve
44
module.exports.toPurl = toPurl
55
module.exports.Result = Result
66

7-
const url = require('url')
7+
const { URL } = require('url')
88
const HostedGit = require('hosted-git-info')
99
const semver = require('semver')
1010
const path = global.FAKE_WINDOWS ? require('path').win32 : require('path')
@@ -245,8 +245,8 @@ function fromFile (res, where) {
245245
const rawWithPrefix = prefix + res.rawSpec
246246
let rawNoPrefix = rawWithPrefix.replace(/^file:/, '')
247247
try {
248-
resolvedUrl = new url.URL(rawWithPrefix, `file://${path.resolve(where)}/`)
249-
specUrl = new url.URL(rawWithPrefix)
248+
resolvedUrl = new URL(rawWithPrefix, `file://${path.resolve(where)}/`)
249+
specUrl = new URL(rawWithPrefix)
250250
} catch (originalError) {
251251
const er = new Error('Invalid file: URL, must comply with RFC 8909')
252252
throw Object.assign(er, {
@@ -260,17 +260,17 @@ function fromFile (res, where) {
260260
// XXX backwards compatibility lack of compliance with RFC 8909
261261
if (resolvedUrl.host && resolvedUrl.host !== 'localhost') {
262262
const rawSpec = res.rawSpec.replace(/^file:\/\//, 'file:///')
263-
resolvedUrl = new url.URL(rawSpec, `file://${path.resolve(where)}/`)
264-
specUrl = new url.URL(rawSpec)
263+
resolvedUrl = new URL(rawSpec, `file://${path.resolve(where)}/`)
264+
specUrl = new URL(rawSpec)
265265
rawNoPrefix = rawSpec.replace(/^file:/, '')
266266
}
267267
// turn file:/../foo into file:../foo
268268
// for 1, 2 or 3 leading slashes since we attempted
269269
// in the previous step to make it a file protocol url with a leading slash
270270
if (/^\/{1,3}\.\.?(\/|$)/.test(rawNoPrefix)) {
271271
const rawSpec = res.rawSpec.replace(/^file:\/{1,3}/, 'file:')
272-
resolvedUrl = new url.URL(rawSpec, `file://${path.resolve(where)}/`)
273-
specUrl = new url.URL(rawSpec)
272+
resolvedUrl = new URL(rawSpec, `file://${path.resolve(where)}/`)
273+
specUrl = new URL(rawSpec)
274274
rawNoPrefix = rawSpec.replace(/^file:/, '')
275275
}
276276
// XXX end RFC 8909 violation backwards compatibility section
@@ -312,28 +312,29 @@ 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(/^git\+ssh:\/\/([^:#]+:[^#]+(?:\.git)?)(?:#(.*))?$/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-
331315
function fromURL (res) {
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+
const matched = rawSpec.match(/^git\+ssh:\/\/([^:#]+:[^#]+(?:\.git)?)(?:#(.*))?$/i)
323+
if (matched && !matched[1].match(/:[0-9]+\/?.*$/i)) {
324+
res.type = 'git'
325+
setGitCommittish(res, matched[2])
326+
res.fetchSpec = matched[1]
327+
return res
328+
}
329+
} else if (rawSpec.startsWith('git+file://')) {
330+
// URL can't handle windows paths
331+
const noProtocol = rawSpec.slice(11).replace(/\\/g, '/')
332+
rawSpec = `git+file://${noProtocol}`
333+
}
332334
// eslint-disable-next-line node/no-deprecated-api
333-
const urlparse = url.parse(res.rawSpec)
334-
res.saveSpec = res.rawSpec
335+
const parsedUrl = new URL(rawSpec)
335336
// check the protocol, and then see if it's git or not
336-
switch (urlparse.protocol) {
337+
switch (parsedUrl.protocol) {
337338
case 'git:':
338339
case 'git+http:':
339340
case 'git+https:':
@@ -342,21 +343,16 @@ function fromURL (res) {
342343
case 'git+file:':
343344
case 'git+ssh:': {
344345
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
346+
setGitCommittish(res, parsedUrl.hash.slice(1))
347+
if (parsedUrl.protocol === 'git+file:' && /^git\+file:\/\/[a-z]:/i.test(rawSpec)) {
348+
// URL can't handle drive letters on windows file paths, the host can't contain a :
349+
res.fetchSpec = `git+file://${parsedUrl.host.toLowerCase()}:${parsedUrl.pathname}`
350350
} else {
351-
setGitCommittish(res, urlparse.hash != null ? urlparse.hash.slice(1) : '')
352-
urlparse.protocol = urlparse.protocol.replace(/^git[+]/, '')
353-
if (urlparse.protocol === 'file:' && /^git\+file:\/\/[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)
351+
parsedUrl.hash = ''
352+
res.fetchSpec = parsedUrl.toString()
353+
}
354+
if (res.fetchSpec.startsWith('git+')) {
355+
res.fetchSpec = res.fetchSpec.slice(4)
360356
}
361357
break
362358
}
@@ -367,7 +363,7 @@ function fromURL (res) {
367363
break
368364

369365
default:
370-
throw unsupportedURLType(urlparse.protocol, res.rawSpec)
366+
throw unsupportedURLType(parsedUrl.protocol, rawSpec)
371367
}
372368

373369
return res

0 commit comments

Comments
 (0)