Skip to content

Commit 4470605

Browse files
committed
fix: use URL instead of url.parse
1 parent 19c1bc7 commit 4470605

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, {
@@ -263,17 +263,17 @@ function fromFile (res, where) {
263263
// Remove when we want a breaking change to come into RFC compliance.
264264
if (resolvedUrl.host && resolvedUrl.host !== 'localhost') {
265265
const rawSpec = res.rawSpec.replace(/^file:\/\//, 'file:///')
266-
resolvedUrl = new url.URL(rawSpec, `file://${path.resolve(where)}/`)
267-
specUrl = new url.URL(rawSpec)
266+
resolvedUrl = new URL(rawSpec, `file://${path.resolve(where)}/`)
267+
specUrl = new URL(rawSpec)
268268
rawNoPrefix = rawSpec.replace(/^file:/, '')
269269
}
270270
// turn file:/../foo into file:../foo
271271
// for 1, 2 or 3 leading slashes since we attempted
272272
// in the previous step to make it a file protocol url with a leading slash
273273
if (/^\/{1,3}\.\.?(\/|$)/.test(rawNoPrefix)) {
274274
const rawSpec = res.rawSpec.replace(/^file:\/{1,3}/, 'file:')
275-
resolvedUrl = new url.URL(rawSpec, `file://${path.resolve(where)}/`)
276-
specUrl = new url.URL(rawSpec)
275+
resolvedUrl = new URL(rawSpec, `file://${path.resolve(where)}/`)
276+
specUrl = new URL(rawSpec)
277277
rawNoPrefix = rawSpec.replace(/^file:/, '')
278278
}
279279
// XXX end 8909 violation backwards compatibility section
@@ -329,28 +329,29 @@ function unsupportedURLType (protocol, spec) {
329329
return err
330330
}
331331

332-
function matchGitScp (spec) {
333-
// git ssh specifiers are overloaded to also use scp-style git
334-
// specifiers, so we have to parse those out and treat them special.
335-
// They are NOT true URIs, so we can't hand them to `url.parse`.
336-
//
337-
// This regex looks for things that look like:
338-
// git+ssh://[email protected]:username/project.git#deadbeef
339-
//
340-
// ...and various combinations. The username in the beginning is *required*.
341-
const matched = spec.match(/^git\+ssh:\/\/([^:#]+:[^#]+(?:\.git)?)(?:#(.*))?$/i)
342-
return matched && !matched[1].match(/:[0-9]+\/?.*$/i) && {
343-
fetchSpec: matched[1],
344-
gitCommittish: matched[2] == null ? null : matched[2],
345-
}
346-
}
347-
348332
function fromURL (res) {
333+
let rawSpec = res.rawSpec
334+
res.saveSpec = rawSpec
335+
if (rawSpec.startsWith('git+ssh:')) {
336+
// git ssh specifiers are overloaded to also use scp-style git
337+
// specifiers, so we have to parse those out and treat them special.
338+
// They are NOT true URIs, so we can't hand them to URL.
339+
const matched = rawSpec.match(/^git\+ssh:\/\/([^:#]+:[^#]+(?:\.git)?)(?:#(.*))?$/i)
340+
if (matched && !matched[1].match(/:[0-9]+\/?.*$/i)) {
341+
res.type = 'git'
342+
setGitCommittish(res, matched[2])
343+
res.fetchSpec = matched[1]
344+
return res
345+
}
346+
} else if (rawSpec.startsWith('git+file://')) {
347+
// URL can't handle windows paths
348+
const noProtocol = rawSpec.slice(11).replace(/\\/g, '/')
349+
rawSpec = `git+file://${noProtocol}`
350+
}
349351
// eslint-disable-next-line node/no-deprecated-api
350-
const urlparse = url.parse(res.rawSpec)
351-
res.saveSpec = res.rawSpec
352+
const parsedUrl = new URL(rawSpec)
352353
// check the protocol, and then see if it's git or not
353-
switch (urlparse.protocol) {
354+
switch (parsedUrl.protocol) {
354355
case 'git:':
355356
case 'git+http:':
356357
case 'git+https:':
@@ -359,21 +360,16 @@ function fromURL (res) {
359360
case 'git+file:':
360361
case 'git+ssh:': {
361362
res.type = 'git'
362-
const match = urlparse.protocol === 'git+ssh:' ? matchGitScp(res.rawSpec)
363-
: null
364-
if (match) {
365-
setGitCommittish(res, match.gitCommittish)
366-
res.fetchSpec = match.fetchSpec
363+
setGitCommittish(res, parsedUrl.hash.slice(1))
364+
if (parsedUrl.protocol === 'git+file:' && /^git\+file:\/\/[a-z]:/i.test(rawSpec)) {
365+
// URL can't handle drive letters on windows file paths, the host can't contain a :
366+
res.fetchSpec = `git+file://${parsedUrl.host.toLowerCase()}:${parsedUrl.pathname}`
367367
} else {
368-
setGitCommittish(res, urlparse.hash != null ? urlparse.hash.slice(1) : '')
369-
urlparse.protocol = urlparse.protocol.replace(/^git[+]/, '')
370-
if (urlparse.protocol === 'file:' && /^git\+file:\/\/[a-z]:/i.test(res.rawSpec)) {
371-
// keep the drive letter : on windows file paths
372-
urlparse.host += ':'
373-
urlparse.hostname += ':'
374-
}
375-
delete urlparse.hash
376-
res.fetchSpec = url.format(urlparse)
368+
parsedUrl.hash = ''
369+
res.fetchSpec = parsedUrl.toString()
370+
}
371+
if (res.fetchSpec.startsWith('git+')) {
372+
res.fetchSpec = res.fetchSpec.slice(4)
377373
}
378374
break
379375
}
@@ -384,7 +380,7 @@ function fromURL (res) {
384380
break
385381

386382
default:
387-
throw unsupportedURLType(urlparse.protocol, res.rawSpec)
383+
throw unsupportedURLType(parsedUrl.protocol, rawSpec)
388384
}
389385

390386
return res

0 commit comments

Comments
 (0)