mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 12:20:27 +01:00
lib, url: add a windows option to path parsing
PR-URL: https://github.com/nodejs/node/pull/52509 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Zeyu "Alex" Yang <himself65@outlook.com> Reviewed-By: LiviaMedeiros <livia@cirno.name>
This commit is contained in:
parent
91dc8c93a3
commit
ac9aa37bcb
|
|
@ -1175,7 +1175,7 @@ resolution for ESM specifiers is [commonjs-extension-resolution-loader][].
|
|||
[`package.json`]: packages.md#nodejs-packagejson-field-definitions
|
||||
[`path.dirname()`]: path.md#pathdirnamepath
|
||||
[`process.dlopen`]: process.md#processdlopenmodule-filename-flags
|
||||
[`url.fileURLToPath()`]: url.md#urlfileurltopathurl
|
||||
[`url.fileURLToPath()`]: url.md#urlfileurltopathurl-options
|
||||
[cjs-module-lexer]: https://github.com/nodejs/cjs-module-lexer/tree/1.2.2
|
||||
[commonjs-extension-resolution-loader]: https://github.com/nodejs/loaders-test/tree/main/commonjs-extension-resolution-loader
|
||||
[custom https loader]: module.md#import-from-https
|
||||
|
|
@ -1184,4 +1184,4 @@ resolution for ESM specifiers is [commonjs-extension-resolution-loader][].
|
|||
[special scheme]: https://url.spec.whatwg.org/#special-scheme
|
||||
[status code]: process.md#exit-codes
|
||||
[the official standard format]: https://tc39.github.io/ecma262/#sec-modules
|
||||
[url.pathToFileURL]: url.md#urlpathtofileurlpath
|
||||
[url.pathToFileURL]: url.md#urlpathtofileurlpath-options
|
||||
|
|
|
|||
|
|
@ -1151,13 +1151,23 @@ console.log(url.domainToUnicode('xn--iñvalid.com'));
|
|||
// Prints an empty string
|
||||
```
|
||||
|
||||
### `url.fileURLToPath(url)`
|
||||
### `url.fileURLToPath(url[, options])`
|
||||
|
||||
<!-- YAML
|
||||
added: v10.12.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/52509
|
||||
description: The `options` argument can now be used to
|
||||
determine how to parse the `path` argument.
|
||||
-->
|
||||
|
||||
* `url` {URL | string} The file URL string or URL object to convert to a path.
|
||||
* `options` {Object}
|
||||
* `windows` {boolean|undefined} `true` if the `path` should be
|
||||
return as a windows filepath, `false` for posix, and
|
||||
`undefined` for the system default.
|
||||
**Default:** `undefined`.
|
||||
* Returns: {string} The fully-resolved platform-specific Node.js file path.
|
||||
|
||||
This function ensures the correct decodings of percent-encoded characters as
|
||||
|
|
@ -1251,13 +1261,23 @@ console.log(url.format(myURL, { fragment: false, unicode: true, auth: false }));
|
|||
// Prints 'https://測試/?abc'
|
||||
```
|
||||
|
||||
### `url.pathToFileURL(path)`
|
||||
### `url.pathToFileURL(path[, options])`
|
||||
|
||||
<!-- YAML
|
||||
added: v10.12.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/52509
|
||||
description: The `options` argument can now be used to
|
||||
determine how to return the `path` value.
|
||||
-->
|
||||
|
||||
* `path` {string} The path to convert to a File URL.
|
||||
* `options` {Object}
|
||||
* `windows` {boolean|undefined} `true` if the `path` should be
|
||||
treated as a windows filepath, `false` for posix, and
|
||||
`undefined` for the system default.
|
||||
**Default:** `undefined`.
|
||||
* Returns: {URL} The file URL object.
|
||||
|
||||
This function ensures that `path` is resolved absolutely, and that the URL
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ const {
|
|||
getConstructorOf,
|
||||
removeColors,
|
||||
kEnumerableProperty,
|
||||
kEmptyObject,
|
||||
SideEffectFreeRegExpPrototypeSymbolReplace,
|
||||
} = require('internal/util');
|
||||
|
||||
|
|
@ -1480,14 +1481,15 @@ function getPathFromURLPosix(url) {
|
|||
return decodeURIComponent(pathname);
|
||||
}
|
||||
|
||||
function fileURLToPath(path) {
|
||||
function fileURLToPath(path, options = kEmptyObject) {
|
||||
const windows = options?.windows;
|
||||
if (typeof path === 'string')
|
||||
path = new URL(path);
|
||||
else if (!isURL(path))
|
||||
throw new ERR_INVALID_ARG_TYPE('path', ['string', 'URL'], path);
|
||||
if (path.protocol !== 'file:')
|
||||
throw new ERR_INVALID_URL_SCHEME('file');
|
||||
return isWindows ? getPathFromURLWin32(path) : getPathFromURLPosix(path);
|
||||
return (windows ?? isWindows) ? getPathFromURLWin32(path) : getPathFromURLPosix(path);
|
||||
}
|
||||
|
||||
// The following characters are percent-encoded when converting from file path
|
||||
|
|
@ -1509,11 +1511,12 @@ const tabRegEx = /\t/g;
|
|||
const questionRegex = /\?/g;
|
||||
const hashRegex = /#/g;
|
||||
|
||||
function encodePathChars(filepath) {
|
||||
function encodePathChars(filepath, options = kEmptyObject) {
|
||||
const windows = options?.windows;
|
||||
if (StringPrototypeIndexOf(filepath, '%') !== -1)
|
||||
filepath = RegExpPrototypeSymbolReplace(percentRegEx, filepath, '%25');
|
||||
// In posix, backslash is a valid character in paths:
|
||||
if (!isWindows && StringPrototypeIndexOf(filepath, '\\') !== -1)
|
||||
if (!(windows ?? isWindows) && StringPrototypeIndexOf(filepath, '\\') !== -1)
|
||||
filepath = RegExpPrototypeSymbolReplace(backslashRegEx, filepath, '%5C');
|
||||
if (StringPrototypeIndexOf(filepath, '\n') !== -1)
|
||||
filepath = RegExpPrototypeSymbolReplace(newlineRegEx, filepath, '%0A');
|
||||
|
|
@ -1524,8 +1527,9 @@ function encodePathChars(filepath) {
|
|||
return filepath;
|
||||
}
|
||||
|
||||
function pathToFileURL(filepath) {
|
||||
if (isWindows && StringPrototypeStartsWith(filepath, '\\\\')) {
|
||||
function pathToFileURL(filepath, options = kEmptyObject) {
|
||||
const windows = options?.windows;
|
||||
if ((windows ?? isWindows) && StringPrototypeStartsWith(filepath, '\\\\')) {
|
||||
const outURL = new URL('file://');
|
||||
// UNC path format: \\server\share\resource
|
||||
const hostnameEndIndex = StringPrototypeIndexOf(filepath, '\\', 2);
|
||||
|
|
@ -1546,20 +1550,22 @@ function pathToFileURL(filepath) {
|
|||
const hostname = StringPrototypeSlice(filepath, 2, hostnameEndIndex);
|
||||
outURL.hostname = domainToASCII(hostname);
|
||||
outURL.pathname = encodePathChars(
|
||||
RegExpPrototypeSymbolReplace(backslashRegEx, StringPrototypeSlice(filepath, hostnameEndIndex), '/'));
|
||||
RegExpPrototypeSymbolReplace(backslashRegEx, StringPrototypeSlice(filepath, hostnameEndIndex), '/'),
|
||||
{ windows },
|
||||
);
|
||||
return outURL;
|
||||
}
|
||||
let resolved = path.resolve(filepath);
|
||||
let resolved = (windows ?? isWindows) ? path.win32.resolve(filepath) : path.posix.resolve(filepath);
|
||||
// path.resolve strips trailing slashes so we must add them back
|
||||
const filePathLast = StringPrototypeCharCodeAt(filepath,
|
||||
filepath.length - 1);
|
||||
if ((filePathLast === CHAR_FORWARD_SLASH ||
|
||||
(isWindows && filePathLast === CHAR_BACKWARD_SLASH)) &&
|
||||
((windows ?? isWindows) && filePathLast === CHAR_BACKWARD_SLASH)) &&
|
||||
resolved[resolved.length - 1] !== path.sep)
|
||||
resolved += '/';
|
||||
|
||||
// Call encodePathChars first to avoid encoding % again for ? and #.
|
||||
resolved = encodePathChars(resolved);
|
||||
resolved = encodePathChars(resolved, { windows });
|
||||
|
||||
// Question and hash character should be included in pathname.
|
||||
// Therefore, encoding is required to eliminate parsing them in different states.
|
||||
|
|
|
|||
|
|
@ -1020,10 +1020,10 @@ Url.prototype.parseHost = function parseHost() {
|
|||
// When used internally, we are not obligated to associate TypeError with
|
||||
// this function, so non-strings can be rejected by underlying implementation.
|
||||
// Public API has to validate input and throw appropriate error.
|
||||
function pathToFileURL(path) {
|
||||
function pathToFileURL(path, options) {
|
||||
validateString(path, 'path');
|
||||
|
||||
return _pathToFileURL(path);
|
||||
return _pathToFileURL(path, options);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
|
|||
|
|
@ -49,106 +49,120 @@ assert.throws(() => url.fileURLToPath('https://a/b/c'), {
|
|||
}
|
||||
}
|
||||
|
||||
{
|
||||
let testCases;
|
||||
if (isWindows) {
|
||||
testCases = [
|
||||
// Lowercase ascii alpha
|
||||
{ path: 'C:\\foo', fileURL: 'file:///C:/foo' },
|
||||
// Uppercase ascii alpha
|
||||
{ path: 'C:\\FOO', fileURL: 'file:///C:/FOO' },
|
||||
// dir
|
||||
{ path: 'C:\\dir\\foo', fileURL: 'file:///C:/dir/foo' },
|
||||
// trailing separator
|
||||
{ path: 'C:\\dir\\', fileURL: 'file:///C:/dir/' },
|
||||
// dot
|
||||
{ path: 'C:\\foo.mjs', fileURL: 'file:///C:/foo.mjs' },
|
||||
// space
|
||||
{ path: 'C:\\foo bar', fileURL: 'file:///C:/foo%20bar' },
|
||||
// question mark
|
||||
{ path: 'C:\\foo?bar', fileURL: 'file:///C:/foo%3Fbar' },
|
||||
// number sign
|
||||
{ path: 'C:\\foo#bar', fileURL: 'file:///C:/foo%23bar' },
|
||||
// ampersand
|
||||
{ path: 'C:\\foo&bar', fileURL: 'file:///C:/foo&bar' },
|
||||
// equals
|
||||
{ path: 'C:\\foo=bar', fileURL: 'file:///C:/foo=bar' },
|
||||
// colon
|
||||
{ path: 'C:\\foo:bar', fileURL: 'file:///C:/foo:bar' },
|
||||
// semicolon
|
||||
{ path: 'C:\\foo;bar', fileURL: 'file:///C:/foo;bar' },
|
||||
// percent
|
||||
{ path: 'C:\\foo%bar', fileURL: 'file:///C:/foo%25bar' },
|
||||
// backslash
|
||||
{ path: 'C:\\foo\\bar', fileURL: 'file:///C:/foo/bar' },
|
||||
// backspace
|
||||
{ path: 'C:\\foo\bbar', fileURL: 'file:///C:/foo%08bar' },
|
||||
// tab
|
||||
{ path: 'C:\\foo\tbar', fileURL: 'file:///C:/foo%09bar' },
|
||||
// newline
|
||||
{ path: 'C:\\foo\nbar', fileURL: 'file:///C:/foo%0Abar' },
|
||||
// carriage return
|
||||
{ path: 'C:\\foo\rbar', fileURL: 'file:///C:/foo%0Dbar' },
|
||||
// latin1
|
||||
{ path: 'C:\\fóóbàr', fileURL: 'file:///C:/f%C3%B3%C3%B3b%C3%A0r' },
|
||||
// Euro sign (BMP code point)
|
||||
{ path: 'C:\\€', fileURL: 'file:///C:/%E2%82%AC' },
|
||||
// Rocket emoji (non-BMP code point)
|
||||
{ path: 'C:\\🚀', fileURL: 'file:///C:/%F0%9F%9A%80' },
|
||||
// UNC path (see https://docs.microsoft.com/en-us/archive/blogs/ie/file-uris-in-windows)
|
||||
{ path: '\\\\nas\\My Docs\\File.doc', fileURL: 'file://nas/My%20Docs/File.doc' },
|
||||
];
|
||||
} else {
|
||||
testCases = [
|
||||
// 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' },
|
||||
];
|
||||
}
|
||||
const windowsTestCases = [
|
||||
// Lowercase ascii alpha
|
||||
{ path: 'C:\\foo', fileURL: 'file:///C:/foo' },
|
||||
// Uppercase ascii alpha
|
||||
{ path: 'C:\\FOO', fileURL: 'file:///C:/FOO' },
|
||||
// dir
|
||||
{ path: 'C:\\dir\\foo', fileURL: 'file:///C:/dir/foo' },
|
||||
// trailing separator
|
||||
{ path: 'C:\\dir\\', fileURL: 'file:///C:/dir/' },
|
||||
// dot
|
||||
{ path: 'C:\\foo.mjs', fileURL: 'file:///C:/foo.mjs' },
|
||||
// space
|
||||
{ path: 'C:\\foo bar', fileURL: 'file:///C:/foo%20bar' },
|
||||
// question mark
|
||||
{ path: 'C:\\foo?bar', fileURL: 'file:///C:/foo%3Fbar' },
|
||||
// number sign
|
||||
{ path: 'C:\\foo#bar', fileURL: 'file:///C:/foo%23bar' },
|
||||
// ampersand
|
||||
{ path: 'C:\\foo&bar', fileURL: 'file:///C:/foo&bar' },
|
||||
// equals
|
||||
{ path: 'C:\\foo=bar', fileURL: 'file:///C:/foo=bar' },
|
||||
// colon
|
||||
{ path: 'C:\\foo:bar', fileURL: 'file:///C:/foo:bar' },
|
||||
// semicolon
|
||||
{ path: 'C:\\foo;bar', fileURL: 'file:///C:/foo;bar' },
|
||||
// percent
|
||||
{ path: 'C:\\foo%bar', fileURL: 'file:///C:/foo%25bar' },
|
||||
// backslash
|
||||
{ path: 'C:\\foo\\bar', fileURL: 'file:///C:/foo/bar' },
|
||||
// backspace
|
||||
{ path: 'C:\\foo\bbar', fileURL: 'file:///C:/foo%08bar' },
|
||||
// tab
|
||||
{ path: 'C:\\foo\tbar', fileURL: 'file:///C:/foo%09bar' },
|
||||
// newline
|
||||
{ path: 'C:\\foo\nbar', fileURL: 'file:///C:/foo%0Abar' },
|
||||
// carriage return
|
||||
{ path: 'C:\\foo\rbar', fileURL: 'file:///C:/foo%0Dbar' },
|
||||
// latin1
|
||||
{ path: 'C:\\fóóbàr', fileURL: 'file:///C:/f%C3%B3%C3%B3b%C3%A0r' },
|
||||
// Euro sign (BMP code point)
|
||||
{ path: 'C:\\€', fileURL: 'file:///C:/%E2%82%AC' },
|
||||
// Rocket emoji (non-BMP code point)
|
||||
{ path: 'C:\\🚀', fileURL: 'file:///C:/%F0%9F%9A%80' },
|
||||
// UNC path (see https://docs.microsoft.com/en-us/archive/blogs/ie/file-uris-in-windows)
|
||||
{ path: '\\\\nas\\My Docs\\File.doc', fileURL: 'file://nas/My%20Docs/File.doc' },
|
||||
];
|
||||
const posixTestCases = [
|
||||
// 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' },
|
||||
];
|
||||
|
||||
for (const { path, fileURL } of testCases) {
|
||||
const fromString = url.fileURLToPath(fileURL);
|
||||
assert.strictEqual(fromString, path);
|
||||
const fromURL = url.fileURLToPath(new URL(fileURL));
|
||||
assert.strictEqual(fromURL, path);
|
||||
}
|
||||
for (const { path, fileURL } of windowsTestCases) {
|
||||
const fromString = url.fileURLToPath(fileURL, { windows: true });
|
||||
assert.strictEqual(fromString, path);
|
||||
const fromURL = url.fileURLToPath(new URL(fileURL), { windows: true });
|
||||
assert.strictEqual(fromURL, path);
|
||||
}
|
||||
|
||||
for (const { path, fileURL } of posixTestCases) {
|
||||
const fromString = url.fileURLToPath(fileURL, { windows: false });
|
||||
assert.strictEqual(fromString, path);
|
||||
const fromURL = url.fileURLToPath(new URL(fileURL), { windows: false });
|
||||
assert.strictEqual(fromURL, path);
|
||||
}
|
||||
|
||||
const defaultTestCases = isWindows ? windowsTestCases : posixTestCases;
|
||||
|
||||
// Test when `options` is null
|
||||
const whenNullActual = url.fileURLToPath(new URL(defaultTestCases[0].fileURL), null);
|
||||
assert.strictEqual(whenNullActual, defaultTestCases[0].path);
|
||||
|
||||
for (const { path, fileURL } of defaultTestCases) {
|
||||
const fromString = url.fileURLToPath(fileURL);
|
||||
assert.strictEqual(fromString, path);
|
||||
const fromURL = url.fileURLToPath(new URL(fileURL));
|
||||
assert.strictEqual(fromURL, path);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
const { isWindows } = require('../common');
|
||||
const assert = require('assert');
|
||||
const url = require('url');
|
||||
|
|
@ -60,106 +61,116 @@ const url = require('url');
|
|||
}
|
||||
}
|
||||
|
||||
{
|
||||
let testCases;
|
||||
if (isWindows) {
|
||||
testCases = [
|
||||
// Lowercase ascii alpha
|
||||
{ path: 'C:\\foo', expected: 'file:///C:/foo' },
|
||||
// Uppercase ascii alpha
|
||||
{ path: 'C:\\FOO', expected: 'file:///C:/FOO' },
|
||||
// dir
|
||||
{ path: 'C:\\dir\\foo', expected: 'file:///C:/dir/foo' },
|
||||
// trailing separator
|
||||
{ path: 'C:\\dir\\', expected: 'file:///C:/dir/' },
|
||||
// dot
|
||||
{ path: 'C:\\foo.mjs', expected: 'file:///C:/foo.mjs' },
|
||||
// space
|
||||
{ path: 'C:\\foo bar', expected: 'file:///C:/foo%20bar' },
|
||||
// question mark
|
||||
{ path: 'C:\\foo?bar', expected: 'file:///C:/foo%3Fbar' },
|
||||
// number sign
|
||||
{ path: 'C:\\foo#bar', expected: 'file:///C:/foo%23bar' },
|
||||
// ampersand
|
||||
{ path: 'C:\\foo&bar', expected: 'file:///C:/foo&bar' },
|
||||
// equals
|
||||
{ path: 'C:\\foo=bar', expected: 'file:///C:/foo=bar' },
|
||||
// colon
|
||||
{ path: 'C:\\foo:bar', expected: 'file:///C:/foo:bar' },
|
||||
// semicolon
|
||||
{ path: 'C:\\foo;bar', expected: 'file:///C:/foo;bar' },
|
||||
// percent
|
||||
{ path: 'C:\\foo%bar', expected: 'file:///C:/foo%25bar' },
|
||||
// backslash
|
||||
{ path: 'C:\\foo\\bar', expected: 'file:///C:/foo/bar' },
|
||||
// backspace
|
||||
{ path: 'C:\\foo\bbar', expected: 'file:///C:/foo%08bar' },
|
||||
// tab
|
||||
{ path: 'C:\\foo\tbar', expected: 'file:///C:/foo%09bar' },
|
||||
// newline
|
||||
{ path: 'C:\\foo\nbar', expected: 'file:///C:/foo%0Abar' },
|
||||
// carriage return
|
||||
{ path: 'C:\\foo\rbar', expected: 'file:///C:/foo%0Dbar' },
|
||||
// latin1
|
||||
{ path: 'C:\\fóóbàr', expected: 'file:///C:/f%C3%B3%C3%B3b%C3%A0r' },
|
||||
// Euro sign (BMP code point)
|
||||
{ path: 'C:\\€', expected: 'file:///C:/%E2%82%AC' },
|
||||
// Rocket emoji (non-BMP code point)
|
||||
{ path: 'C:\\🚀', expected: 'file:///C:/%F0%9F%9A%80' },
|
||||
// UNC path (see https://docs.microsoft.com/en-us/archive/blogs/ie/file-uris-in-windows)
|
||||
{ path: '\\\\nas\\My Docs\\File.doc', expected: 'file://nas/My%20Docs/File.doc' },
|
||||
];
|
||||
} else {
|
||||
testCases = [
|
||||
// Lowercase ascii alpha
|
||||
{ path: '/foo', expected: 'file:///foo' },
|
||||
// Uppercase ascii alpha
|
||||
{ path: '/FOO', expected: 'file:///FOO' },
|
||||
// dir
|
||||
{ path: '/dir/foo', expected: 'file:///dir/foo' },
|
||||
// trailing separator
|
||||
{ path: '/dir/', expected: 'file:///dir/' },
|
||||
// dot
|
||||
{ path: '/foo.mjs', expected: 'file:///foo.mjs' },
|
||||
// space
|
||||
{ path: '/foo bar', expected: 'file:///foo%20bar' },
|
||||
// question mark
|
||||
{ path: '/foo?bar', expected: 'file:///foo%3Fbar' },
|
||||
// number sign
|
||||
{ path: '/foo#bar', expected: 'file:///foo%23bar' },
|
||||
// ampersand
|
||||
{ path: '/foo&bar', expected: 'file:///foo&bar' },
|
||||
// equals
|
||||
{ path: '/foo=bar', expected: 'file:///foo=bar' },
|
||||
// colon
|
||||
{ path: '/foo:bar', expected: 'file:///foo:bar' },
|
||||
// semicolon
|
||||
{ path: '/foo;bar', expected: 'file:///foo;bar' },
|
||||
// percent
|
||||
{ path: '/foo%bar', expected: 'file:///foo%25bar' },
|
||||
// backslash
|
||||
{ path: '/foo\\bar', expected: 'file:///foo%5Cbar' },
|
||||
// backspace
|
||||
{ path: '/foo\bbar', expected: 'file:///foo%08bar' },
|
||||
// tab
|
||||
{ path: '/foo\tbar', expected: 'file:///foo%09bar' },
|
||||
// newline
|
||||
{ path: '/foo\nbar', expected: 'file:///foo%0Abar' },
|
||||
// carriage return
|
||||
{ path: '/foo\rbar', expected: 'file:///foo%0Dbar' },
|
||||
// latin1
|
||||
{ path: '/fóóbàr', expected: 'file:///f%C3%B3%C3%B3b%C3%A0r' },
|
||||
// Euro sign (BMP code point)
|
||||
{ path: '/€', expected: 'file:///%E2%82%AC' },
|
||||
// Rocket emoji (non-BMP code point)
|
||||
{ path: '/🚀', expected: 'file:///%F0%9F%9A%80' },
|
||||
];
|
||||
}
|
||||
const windowsTestCases = [
|
||||
// Lowercase ascii alpha
|
||||
{ path: 'C:\\foo', expected: 'file:///C:/foo' },
|
||||
// Uppercase ascii alpha
|
||||
{ path: 'C:\\FOO', expected: 'file:///C:/FOO' },
|
||||
// dir
|
||||
{ path: 'C:\\dir\\foo', expected: 'file:///C:/dir/foo' },
|
||||
// trailing separator
|
||||
{ path: 'C:\\dir\\', expected: 'file:///C:/dir/' },
|
||||
// dot
|
||||
{ path: 'C:\\foo.mjs', expected: 'file:///C:/foo.mjs' },
|
||||
// space
|
||||
{ path: 'C:\\foo bar', expected: 'file:///C:/foo%20bar' },
|
||||
// question mark
|
||||
{ path: 'C:\\foo?bar', expected: 'file:///C:/foo%3Fbar' },
|
||||
// number sign
|
||||
{ path: 'C:\\foo#bar', expected: 'file:///C:/foo%23bar' },
|
||||
// ampersand
|
||||
{ path: 'C:\\foo&bar', expected: 'file:///C:/foo&bar' },
|
||||
// equals
|
||||
{ path: 'C:\\foo=bar', expected: 'file:///C:/foo=bar' },
|
||||
// colon
|
||||
{ path: 'C:\\foo:bar', expected: 'file:///C:/foo:bar' },
|
||||
// semicolon
|
||||
{ path: 'C:\\foo;bar', expected: 'file:///C:/foo;bar' },
|
||||
// percent
|
||||
{ path: 'C:\\foo%bar', expected: 'file:///C:/foo%25bar' },
|
||||
// backslash
|
||||
{ path: 'C:\\foo\\bar', expected: 'file:///C:/foo/bar' },
|
||||
// backspace
|
||||
{ path: 'C:\\foo\bbar', expected: 'file:///C:/foo%08bar' },
|
||||
// tab
|
||||
{ path: 'C:\\foo\tbar', expected: 'file:///C:/foo%09bar' },
|
||||
// newline
|
||||
{ path: 'C:\\foo\nbar', expected: 'file:///C:/foo%0Abar' },
|
||||
// carriage return
|
||||
{ path: 'C:\\foo\rbar', expected: 'file:///C:/foo%0Dbar' },
|
||||
// latin1
|
||||
{ path: 'C:\\fóóbàr', expected: 'file:///C:/f%C3%B3%C3%B3b%C3%A0r' },
|
||||
// Euro sign (BMP code point)
|
||||
{ path: 'C:\\€', expected: 'file:///C:/%E2%82%AC' },
|
||||
// Rocket emoji (non-BMP code point)
|
||||
{ path: 'C:\\🚀', expected: 'file:///C:/%F0%9F%9A%80' },
|
||||
// UNC path (see https://docs.microsoft.com/en-us/archive/blogs/ie/file-uris-in-windows)
|
||||
{ path: '\\\\nas\\My Docs\\File.doc', expected: 'file://nas/My%20Docs/File.doc' },
|
||||
];
|
||||
const posixTestCases = [
|
||||
// Lowercase ascii alpha
|
||||
{ path: '/foo', expected: 'file:///foo' },
|
||||
// Uppercase ascii alpha
|
||||
{ path: '/FOO', expected: 'file:///FOO' },
|
||||
// dir
|
||||
{ path: '/dir/foo', expected: 'file:///dir/foo' },
|
||||
// trailing separator
|
||||
{ path: '/dir/', expected: 'file:///dir/' },
|
||||
// dot
|
||||
{ path: '/foo.mjs', expected: 'file:///foo.mjs' },
|
||||
// space
|
||||
{ path: '/foo bar', expected: 'file:///foo%20bar' },
|
||||
// question mark
|
||||
{ path: '/foo?bar', expected: 'file:///foo%3Fbar' },
|
||||
// number sign
|
||||
{ path: '/foo#bar', expected: 'file:///foo%23bar' },
|
||||
// ampersand
|
||||
{ path: '/foo&bar', expected: 'file:///foo&bar' },
|
||||
// equals
|
||||
{ path: '/foo=bar', expected: 'file:///foo=bar' },
|
||||
// colon
|
||||
{ path: '/foo:bar', expected: 'file:///foo:bar' },
|
||||
// semicolon
|
||||
{ path: '/foo;bar', expected: 'file:///foo;bar' },
|
||||
// percent
|
||||
{ path: '/foo%bar', expected: 'file:///foo%25bar' },
|
||||
// backslash
|
||||
{ path: '/foo\\bar', expected: 'file:///foo%5Cbar' },
|
||||
// backspace
|
||||
{ path: '/foo\bbar', expected: 'file:///foo%08bar' },
|
||||
// tab
|
||||
{ path: '/foo\tbar', expected: 'file:///foo%09bar' },
|
||||
// newline
|
||||
{ path: '/foo\nbar', expected: 'file:///foo%0Abar' },
|
||||
// carriage return
|
||||
{ path: '/foo\rbar', expected: 'file:///foo%0Dbar' },
|
||||
// latin1
|
||||
{ path: '/fóóbàr', expected: 'file:///f%C3%B3%C3%B3b%C3%A0r' },
|
||||
// Euro sign (BMP code point)
|
||||
{ path: '/€', expected: 'file:///%E2%82%AC' },
|
||||
// Rocket emoji (non-BMP code point)
|
||||
{ path: '/🚀', expected: 'file:///%F0%9F%9A%80' },
|
||||
];
|
||||
|
||||
for (const { path, expected } of testCases) {
|
||||
const actual = url.pathToFileURL(path).href;
|
||||
assert.strictEqual(actual, expected);
|
||||
}
|
||||
for (const { path, expected } of windowsTestCases) {
|
||||
const actual = url.pathToFileURL(path, { windows: true }).href;
|
||||
assert.strictEqual(actual, expected);
|
||||
}
|
||||
|
||||
for (const { path, expected } of posixTestCases) {
|
||||
const actual = url.pathToFileURL(path, { windows: false }).href;
|
||||
assert.strictEqual(actual, expected);
|
||||
}
|
||||
|
||||
const testCases = isWindows ? windowsTestCases : posixTestCases;
|
||||
|
||||
// Test when `options` is null
|
||||
const whenNullActual = url.pathToFileURL(testCases[0].path, null);
|
||||
assert.strictEqual(whenNullActual.href, testCases[0].expected);
|
||||
|
||||
for (const { path, expected } of testCases) {
|
||||
const actual = url.pathToFileURL(path).href;
|
||||
assert.strictEqual(actual, expected);
|
||||
}
|
||||
|
||||
// Test for non-string parameter
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user