Skip to content

Commit

Permalink
Add fileURLToPath
Browse files Browse the repository at this point in the history
  • Loading branch information
mischnic committed Dec 7, 2021
1 parent 1834cd7 commit db4bb68
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 0 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,10 @@ an anchor tag. Examples:
url.resolve('/one/two/three', 'four') // '/one/two/four'
url.resolve('http://example.com/', '/one') // 'http://example.com/one'
url.resolve('http://example.com/one', '/two') // 'http://example.com/two'

### url.fileURLToPath(url)

Take a string or WHATWG `URL` object representing a filepath, and return the POSIX
filepath as a string.

url.fileURLToPath('file:///etc/hosts') // '/etc/hosts'
71 changes: 71 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2055,3 +2055,74 @@ relativeTests2.forEach(function (relativeTest) {
assert.equal(actual, expected, 'format(' + relativeTest[1] + ') == ' + expected + '\nactual:' + actual);
});
});

var fileURLToPathTestCases = [
// Lowercase ascii alpha
{ path: '/foo', fileURL: 'file:///foo' },
// Uppercase ascii alpha
{ path: '/FOO', fileURL: 'file:///FOO' },
// dir
{ path: '/dir/foo', fileURL: 'file:///dir/foo' },
// trailing separator
{ path: '/dir/', fileURL: 'file:///dir/' },
// dot
{ path: '/foo.mjs', fileURL: 'file:///foo.mjs' },
// space
{ path: '/foo bar', fileURL: 'file:///foo%20bar' },
// question mark
{ path: '/foo?bar', fileURL: 'file:///foo%3Fbar' },
// number sign
{ path: '/foo#bar', fileURL: 'file:///foo%23bar' },
// ampersand
{ path: '/foo&bar', fileURL: 'file:///foo&bar' },
// equals
{ path: '/foo=bar', fileURL: 'file:///foo=bar' },
// colon
{ path: '/foo:bar', fileURL: 'file:///foo:bar' },
// semicolon
{ path: '/foo;bar', fileURL: 'file:///foo;bar' },
// percent
{ path: '/foo%bar', fileURL: 'file:///foo%25bar' },
// backslash
{ path: '/foo\\bar', fileURL: 'file:///foo%5Cbar' },
// backspace
{ path: '/foo\bbar', fileURL: 'file:///foo%08bar' },
// tab
{ path: '/foo\tbar', fileURL: 'file:///foo%09bar' },
// newline
{ path: '/foo\nbar', fileURL: 'file:///foo%0Abar' },
// carriage return
{ path: '/foo\rbar', fileURL: 'file:///foo%0Dbar' },
// latin1
{ path: '/fóóbàr', fileURL: 'file:///f%C3%B3%C3%B3b%C3%A0r' },
// Euro sign (BMP code point)
{ path: '/€', fileURL: 'file:///%E2%82%AC' },
// Rocket emoji (non-BMP code point)
{ path: '/🚀', fileURL: 'file:///%F0%9F%9A%80' }
];

fileURLToPathTestCases.forEach(function (fileURLToPathTestCase) {
test('fileURLToPath(' + fileURLToPathTestCase.fileURL + ')', function () {
var fromString = url.fileURLToPath(fileURLToPathTestCase.fileURL);
assert.strictEqual(fromString, fileURLToPathTestCase.path);
var fromURL = url.fileURLToPath(new URL(fileURLToPathTestCase.fileURL));
assert.strictEqual(fromURL, fileURLToPathTestCase.path);
});
});

[
'https://host/y',
'file://host/a',
new URL('https://host/y'),
'file:///a%2F/',
'',
null,
undefined,
1,
{},
true
].forEach(function (val) {
test('fileURLToPath(' + val + ')', function () {
assert['throws'](function () { url.fileURLToPath(val); }, TypeError);
});
});
35 changes: 35 additions & 0 deletions url.js
Original file line number Diff line number Diff line change
Expand Up @@ -771,4 +771,39 @@ exports.resolve = urlResolve;
exports.resolveObject = urlResolveObject;
exports.format = urlFormat;

function isURLInstance(fileURLOrPath) {
return fileURLOrPath != null && fileURLOrPath.href && fileURLOrPath.origin;
}

function getPathFromURLPosix(url) {
if (url.hostname !== '') {
throw new TypeError('File URL host must be "localhost" or empty on darwin');
}
var pathname = url.pathname;
for (var n = 0; n < pathname.length; n++) {
if (pathname[n] === '%') {
var third = pathname.codePointAt(n + 2) | 0x20;
if (pathname[n + 1] === '2' && third === 102) {
throw new TypeError('File URL path must not include encoded / characters');
}
}
}
return decodeURIComponent(pathname);
}

function fileURLToPath(path) {
if (typeof path === 'string') {
path = new URL(path);
} else if (!isURLInstance(path)) {
throw new TypeError('The "path" argument must be of type string or an instance of URL. Received ' + path);
}
if (path.protocol !== 'file:') {
throw new TypeError('The URL must be of scheme file');
}
return getPathFromURLPosix(path);
}

exports.fileURLToPath = fileURLToPath;

exports.Url = Url;
exports.URL = URL;

0 comments on commit db4bb68

Please sign in to comment.