Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

'use strict';

var url = require('url');
var parseURL = require('./parse-url');
var cache = { __proto__: null };

function isChecksum(str) {
Expand Down Expand Up @@ -45,6 +45,14 @@ function owner(str) {
return str;
}

/**
* Extract the host from a git@ URL using the WHATWG URL API.
*/
function getGitAtHost(str) {
var transformed = 'http://' + str.replace(/git@([^:]+):/, '$1/');
return parseURL(transformed).host || null;
}

function parse(str) {
if (typeof str !== 'string' || !str.length) {
return null;
Expand All @@ -55,14 +63,14 @@ function parse(str) {
}

// parse the URL
var obj = url.parse(str);
var obj = parseURL(str);
if (typeof obj.path !== 'string' || !obj.path.length || typeof obj.pathname !== 'string' || !obj.pathname.length) {
return null;
}

if (!obj.host && (/^git@/).test(str) === true) {
// return the correct host for git@ URLs
obj.host = url.parse('http://' + str.replace(/git@([^:]+):/, '$1/')).host;
obj.host = getGitAtHost(str);
}

obj.path = trimSlash(obj.path);
Expand Down
78 changes: 78 additions & 0 deletions parse-url.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
'use strict';

var urlModule = require('url');
var URLCtor = typeof URL === 'undefined' ? urlModule.URL || null : URL;
var legacyURLParse = URLCtor ? null : urlModule.parse;

function parseWHATWG(str) {
try {
var u = new URLCtor(str);
var auth = null;
if (u.username) {
auth = u.password ? u.username + ':' + u.password : u.username;
}
var host = u.host || null;
var hostname = u.hostname || null;
var pathname = u.pathname || null;
var path = u.pathname + (u.search || '') || null;

// For non-special schemes without '//' (e.g. 'github:user/repo', 'foo:bar'),
// the WHATWG URL API produces an opaque path (host is empty). Replicate the
// legacy url.parse() behavior: treat the first path segment as the host.
if (!host && pathname && str.indexOf('//') === -1) {
var slashIdx = pathname.indexOf('/');
if (slashIdx === -1) {
// e.g. 'foo:bar' — no path segment, only a host-like token → null path
host = pathname;
hostname = pathname;
pathname = null;
path = null;
} else {
// e.g. 'github:user/repo' — first segment is host, rest is path
host = pathname.slice(0, slashIdx);
hostname = host;
pathname = pathname.slice(slashIdx);
path = pathname + (u.search || '');
}
}

return {
auth: auth,
hash: u.hash || null,
host: host,
hostname: hostname,
href: u.href,
path: path,
pathname: pathname,
port: u.port || null,
protocol: u.protocol || null,
query: u.search ? u.search.slice(1) : null,
search: u.search || null,
slashes: str.indexOf('//') === -1 ? null : true
};
} catch (_) {
// Fall back for non-standard strings (bare paths, git@ URLs, etc.)
var hashIdx = str.indexOf('#');
var hash = hashIdx === -1 ? null : str.slice(hashIdx);
var pathPart = hashIdx === -1 ? str : str.slice(0, hashIdx);
var queryIdx = pathPart.indexOf('?');
var search = queryIdx === -1 ? null : pathPart.slice(queryIdx);
var pathnamePart = queryIdx === -1 ? pathPart : pathPart.slice(0, queryIdx);
return {
auth: null,
hash: hash,
host: null,
hostname: null,
href: str,
path: pathPart || null,
pathname: pathnamePart || null,
port: null,
protocol: null,
query: search ? search.slice(1) : null,
search: search,
slashes: null
};
}
}

module.exports = URLCtor ? parseWHATWG : legacyURLParse;
8 changes: 6 additions & 2 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ var test = require('tape');
var gh = require('../');

test('parse-github-url', function (t) {
process.on('warning', function (e) {
function failOnWarn(e) {
t.fail(e, 'no deprecation warnings are issued');
});
}

process.on('warning', failOnWarn);
t.teardown(function () { process.removeListener('warning', failOnWarn); });

t.equal(gh('toString').href, 'toString');

Expand Down Expand Up @@ -247,4 +250,5 @@ test('parse-github-url', function (t) {

assert.end();
});

});
32 changes: 32 additions & 0 deletions test/parse-url.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict';

var test = require('tape');
var urlParse = require('url').parse;
var parseURL = require('../parse-url');

test('parse-url', function (t) {
t.test('matches url.parse() for key properties:', function (st) {
var props = ['auth', 'hash', 'host', 'hostname', 'path', 'pathname', 'protocol', 'slashes'];
var urls = [
'https://github.com/assemble/verb.git',
'git://github.com/assemble/verb.git',
'git@github.com:assemble/verb.git',
'github:user/repo',
'assemble/verb',
'foo:bar',
'assemble/verb?tab=readme',
'assemble/verb?tab=readme#section',
'git@github.com:assemble/verb.git?foo=bar'
];

urls.forEach(function (url) {
var expected = urlParse(url);
var actual = parseURL(url);
props.forEach(function (prop) {
st.equal(actual[prop], expected[prop], url + ' — ' + prop);
});
});

st.end();
});
});
Loading