diff --git a/doc/api/path.md b/doc/api/path.md index ef710106efd23b..6f5b91df570088 100644 --- a/doc/api/path.md +++ b/doc/api/path.md @@ -67,12 +67,15 @@ example, `path.resolve('C:\\')` can potentially return a different result than -* `path` {string} +* `path` {string|URL} * `suffix` {string} An optional suffix to remove * Returns: {string} @@ -101,8 +104,8 @@ path.win32.basename('C:\\foo.HTML', '.html'); // Returns: 'foo.HTML' ``` -A [`TypeError`][] is thrown if `path` is not a string or if `suffix` is given -and is not a string. +A [`TypeError`][] is thrown if `path` is not a string or an instance of [`URL`][], +or if `suffix` is given and is not a string. ## `path.delimiter` @@ -142,12 +145,15 @@ process.env.PATH.split(path.delimiter); -* `path` {string} +* `path` {string|URL} * Returns: {string} The `path.dirname()` method returns the directory name of a `path`, similar to @@ -159,19 +165,22 @@ path.dirname('/foo/bar/baz/asdf/quux'); // Returns: '/foo/bar/baz/asdf' ``` -A [`TypeError`][] is thrown if `path` is not a string. +A [`TypeError`][] is thrown if `path` is not a string or an instance of [`URL`][] ## `path.extname(path)` -* `path` {string} +* `path` {string|URL} * Returns: {string} The `path.extname()` method returns the extension of the `path`, from the last @@ -200,7 +209,7 @@ path.extname('.index.md'); // Returns: '.md' ``` -A [`TypeError`][] is thrown if `path` is not a string. +A [`TypeError`][] is thrown if `path` is not a string or an instance of [`URL`][]. ## `path.format(pathObject)` @@ -306,9 +315,13 @@ A [`TypeError`][] is thrown if `path` or `pattern` are not strings. -* `path` {string} +* `path` {string|URL} * Returns: {boolean} The `path.isAbsolute()` method determines if `path` is an absolute path. @@ -336,15 +349,19 @@ path.isAbsolute('bar/baz'); // false path.isAbsolute('.'); // false ``` -A [`TypeError`][] is thrown if `path` is not a string. +A [`TypeError`][] is thrown if `path` is not a string or an instance of [`URL`][]. ## `path.join([...paths])` -* `...paths` {string} A sequence of path segments +* `...paths` {string|URL} A sequence of path segments * Returns: {string} The `path.join()` method joins all given `path` segments together using the @@ -362,15 +379,19 @@ path.join('foo', {}, 'bar'); // Throws 'TypeError: Path must be a string. Received {}' ``` -A [`TypeError`][] is thrown if any of the path segments is not a string. +A [`TypeError`][] is thrown if any of the path segments is not a string or an instance of [`URL`][]. ## `path.normalize(path)` -* `path` {string} +* `path` {string|URL} * Returns: {string} The `path.normalize()` method normalizes the given `path`, resolving `'..'` and @@ -414,15 +435,19 @@ path.win32.normalize('C:////temp\\\\/\\/\\/foo/bar'); // Returns: 'C:\\temp\\foo\\bar' ``` -A [`TypeError`][] is thrown if `path` is not a string. +A [`TypeError`][] is thrown if `path` is not a string or an instance of [`URL`][]. ## `path.parse(path)` -* `path` {string} +* `path` {string|URL} * Returns: {Object} The `path.parse()` method returns an object whose properties represent @@ -481,7 +506,7 @@ path.parse('C:\\path\\dir\\file.txt'); (All spaces in the "" line should be ignored. They are purely for formatting.) ``` -A [`TypeError`][] is thrown if `path` is not a string. +A [`TypeError`][] is thrown if `path` is not a string or an instance of [`URL`][]. ## `path.posix` @@ -505,14 +530,17 @@ The API is accessible via `require('node:path').posix` or `require('node:path/po -* `from` {string} -* `to` {string} +* `from` {string|URL} +* `to` {string|URL} * Returns: {string} The `path.relative()` method returns the relative path from `from` to `to` based @@ -536,15 +564,19 @@ path.relative('C:\\orandea\\test\\aaa', 'C:\\orandea\\impl\\bbb'); // Returns: '..\\..\\impl\\bbb' ``` -A [`TypeError`][] is thrown if either `from` or `to` is not a string. +A [`TypeError`][] is thrown if either `from` or `to` is not a string or an instance of [`URL`][]. ## `path.resolve([...paths])` -* `...paths` {string} A sequence of paths or path segments +* `...paths` {string|URL} A sequence of paths or path segments * Returns: {string} The `path.resolve()` method resolves a sequence of paths or path segments into @@ -579,7 +611,7 @@ path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif'); // this returns '/home/myself/node/wwwroot/static_files/gif/image.gif' ``` -A [`TypeError`][] is thrown if any of the arguments is not a string. +A [`TypeError`][] is thrown if any of the arguments is not a string or an instance of [`URL`][]. ## `path.sep` @@ -616,14 +648,18 @@ slashes (`\`). -* `path` {string} +* `path` {string|URL} * Returns: {string} On Windows systems only, returns an equivalent [namespace-prefixed path][] for -the given `path`. If `path` is not a string, `path` will be returned without -modifications. +the given `path`. If `path` is not a string or URL, `path` will be returned +without modifications. This method is meaningful only on Windows systems. On POSIX systems, the method is non-operational and always returns `path` without modifications. @@ -647,6 +683,7 @@ The API is accessible via `require('node:path').win32` or `require('node:path/wi [MSDN-Rel-Path]: https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#fully-qualified-vs-relative-paths [`TypeError`]: errors.md#class-typeerror +[`URL`]: url.md#the-whatwg-url-api [`path.parse()`]: #pathparsepath [`path.posix`]: #pathposix [`path.sep`]: #pathsep diff --git a/lib/internal/url.js b/lib/internal/url.js index 0615e38c8a761f..2ce2a554b822bc 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -46,6 +46,7 @@ const { kEnumerableProperty, kEmptyObject, SideEffectFreeRegExpPrototypeSymbolReplace, + getLazy, } = require('internal/util'); const { @@ -77,7 +78,7 @@ const { CHAR_PERCENT, CHAR_PLUS, } = require('internal/constants'); -const path = require('path'); +const lazyPath = getLazy(() => require('path')); const { validateFunction, @@ -1555,13 +1556,13 @@ function pathToFileURL(filepath, options = kEmptyObject) { ); return outURL; } - let resolved = (windows ?? isWindows) ? path.win32.resolve(filepath) : path.posix.resolve(filepath); + let resolved = (windows ?? isWindows) ? lazyPath().win32.resolve(filepath) : lazyPath().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 || ((windows ?? isWindows) && filePathLast === CHAR_BACKWARD_SLASH)) && - resolved[resolved.length - 1] !== path.sep) + resolved[resolved.length - 1] !== lazyPath().sep) resolved += '/'; // Call encodePathChars first to avoid encoding % again for ? and #. @@ -1578,10 +1579,10 @@ function pathToFileURL(filepath, options = kEmptyObject) { return new URL(`file://${resolved}`); } -function toPathIfFileURL(fileURLOrPath) { +function toPathIfFileURL(fileURLOrPath, options = kEmptyObject) { if (!isURL(fileURLOrPath)) return fileURLOrPath; - return fileURLToPath(fileURLOrPath); + return fileURLToPath(fileURLOrPath, options); } /** diff --git a/lib/path.js b/lib/path.js index e9dec3f7d361d3..a959acc82eb8de 100644 --- a/lib/path.js +++ b/lib/path.js @@ -46,6 +46,14 @@ const { validateObject, validateString, } = require('internal/validators'); +const { + toPathIfFileURL: URLToPathIfFileURL, +} = require('internal/url'); + +function toPathIfFileURL(url, options) { + if (typeof url === 'string') return url; + return URLToPathIfFileURL(url, options); +} const { getLazy, @@ -70,6 +78,9 @@ function isWindowsDeviceRoot(code) { (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z); } +const kWindowsURLOptions = { windows: true }; +const kNixURLOptions = { windows: false }; + // Resolves . and .. elements in a path with directory names function normalizeString(path, allowAboveRoot, separator, isPathSeparator) { let res = ''; @@ -161,7 +172,13 @@ function _format(sep, pathObject) { return dir === pathObject.root ? `${dir}${base}` : `${dir}${sep}${base}`; } +/** +* @typedef {PathLike} +* @type {URL | string} +*/ + function glob(path, pattern, windows) { + path = toPathIfFileURL(args[i], kWindowsURLOptions); emitExperimentalWarning('glob'); validateString(path, 'path'); validateString(pattern, 'pattern'); @@ -180,7 +197,7 @@ function glob(path, pattern, windows) { const win32 = { /** * path.resolve([from ...], to) - * @param {...string} args + * @param {...(PathLike)} args * @returns {string} */ resolve(...args) { @@ -191,7 +208,7 @@ const win32 = { for (let i = args.length - 1; i >= -1; i--) { let path; if (i >= 0) { - path = args[i]; + path = toPathIfFileURL(args[i], kWindowsURLOptions); validateString(path, `paths[${i}]`); // Skip empty entries @@ -326,10 +343,11 @@ const win32 = { }, /** - * @param {string} path + * @param {PathLike} path * @returns {string} */ normalize(path) { + path = toPathIfFileURL(path, kWindowsURLOptions); validateString(path, 'path'); const len = path.length; if (len === 0) @@ -424,10 +442,11 @@ const win32 = { }, /** - * @param {string} path + * @param {PathLike} path * @returns {boolean} */ isAbsolute(path) { + path = toPathIfFileURL(path, kWindowsURLOptions); validateString(path, 'path'); const len = path.length; if (len === 0) @@ -443,7 +462,7 @@ const win32 = { }, /** - * @param {...string} args + * @param {...(PathLike)} args * @returns {string} */ join(...args) { @@ -453,7 +472,7 @@ const win32 = { let joined; let firstPart; for (let i = 0; i < args.length; ++i) { - const arg = args[i]; + const arg = toPathIfFileURL(args[i], kWindowsURLOptions); validateString(arg, 'path'); if (arg.length > 0) { if (joined === undefined) @@ -517,11 +536,13 @@ const win32 = { * from = 'C:\\orandea\\test\\aaa' * to = 'C:\\orandea\\impl\\bbb' * The output of the function should be: '..\\..\\impl\\bbb' - * @param {string} from - * @param {string} to + * @param {PathLike} from + * @param {PathLike} to * @returns {string} */ relative(from, to) { + from = toPathIfFileURL(from, kWindowsURLOptions); + to = toPathIfFileURL(to, kWindowsURLOptions); validateString(from, 'from'); validateString(to, 'to'); @@ -640,10 +661,11 @@ const win32 = { }, /** - * @param {string} path + * @param {PathLike} path * @returns {string} */ toNamespacedPath(path) { + path = toPathIfFileURL(path, kWindowsURLOptions); // Note: this will *probably* throw somewhere. if (typeof path !== 'string' || path.length === 0) return path; @@ -675,10 +697,11 @@ const win32 = { }, /** - * @param {string} path + * @param {PathLike} path * @returns {string} */ dirname(path) { + path = toPathIfFileURL(path, kWindowsURLOptions); validateString(path, 'path'); const len = path.length; if (len === 0) @@ -770,13 +793,14 @@ const win32 = { }, /** - * @param {string} path + * @param {PathLike} path * @param {string} [suffix] * @returns {string} */ basename(path, suffix) { if (suffix !== undefined) validateString(suffix, 'ext'); + path = toPathIfFileURL(path, kWindowsURLOptions); validateString(path, 'path'); let start = 0; let end = -1; @@ -858,10 +882,11 @@ const win32 = { }, /** - * @param {string} path + * @param {PathLike} path * @returns {string} */ extname(path) { + path = toPathIfFileURL(path, kWindowsURLOptions); validateString(path, 'path'); let start = 0; let startDot = -1; @@ -928,7 +953,7 @@ const win32 = { format: FunctionPrototypeBind(_format, null, '\\'), /** - * @param {string} path + * @param {PathLike} path * @returns {{ * dir: string; * root: string; @@ -938,6 +963,7 @@ const win32 = { * }} */ parse(path) { + path = toPathIfFileURL(path, kWindowsURLOptions); validateString(path, 'path'); const ret = { root: '', dir: '', base: '', ext: '', name: '' }; @@ -1117,7 +1143,7 @@ const posixCwd = (() => { const posix = { /** * path.resolve([from ...], to) - * @param {...string} args + * @param {...PathLike} args * @returns {string} */ resolve(...args) { @@ -1125,7 +1151,7 @@ const posix = { let resolvedAbsolute = false; for (let i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) { - const path = i >= 0 ? args[i] : posixCwd(); + const path = i >= 0 ? toPathIfFileURL(args[i], kNixURLOptions) : posixCwd(); validateString(path, `paths[${i}]`); // Skip empty entries @@ -1152,10 +1178,11 @@ const posix = { }, /** - * @param {string} path + * @param {PathLike} path * @returns {string} */ normalize(path) { + path = toPathIfFileURL(path, kNixURLOptions); validateString(path, 'path'); if (path.length === 0) @@ -1181,17 +1208,18 @@ const posix = { }, /** - * @param {string} path + * @param {PathLike} path * @returns {boolean} */ isAbsolute(path) { + path = toPathIfFileURL(path, kNixURLOptions); validateString(path, 'path'); return path.length > 0 && StringPrototypeCharCodeAt(path, 0) === CHAR_FORWARD_SLASH; }, /** - * @param {...string} args + * @param {...PathLike} args * @returns {string} */ join(...args) { @@ -1199,7 +1227,7 @@ const posix = { return '.'; let joined; for (let i = 0; i < args.length; ++i) { - const arg = args[i]; + const arg = toPathIfFileURL(args[i], kNixURLOptions); validateString(arg, 'path'); if (arg.length > 0) { if (joined === undefined) @@ -1214,11 +1242,13 @@ const posix = { }, /** - * @param {string} from - * @param {string} to + * @param {PathLike} from + * @param {PathLike} to * @returns {string} */ relative(from, to) { + from = toPathIfFileURL(from, kNixURLOptions); + to = toPathIfFileURL(to, kNixURLOptions); validateString(from, 'from'); validateString(to, 'to'); @@ -1291,19 +1321,20 @@ const posix = { }, /** - * @param {string} path + * @param {PathLike} path * @returns {string} */ toNamespacedPath(path) { // Non-op on posix systems - return path; + return toPathIfFileURL(path, kNixURLOptions); }, /** - * @param {string} path + * @param {PathLike} path * @returns {string} */ dirname(path) { + path = toPathIfFileURL(path, kNixURLOptions); validateString(path, 'path'); if (path.length === 0) return '.'; @@ -1330,13 +1361,14 @@ const posix = { }, /** - * @param {string} path + * @param {PathLike} path * @param {string} [suffix] * @returns {string} */ basename(path, suffix) { if (suffix !== undefined) validateString(suffix, 'ext'); + path = toPathIfFileURL(path, kNixURLOptions); validateString(path, 'path'); let start = 0; @@ -1410,10 +1442,11 @@ const posix = { }, /** - * @param {string} path + * @param {PathLike} path * @returns {string} */ extname(path) { + path = toPathIfFileURL(path, kNixURLOptions); validateString(path, 'path'); let startDot = -1; let startPart = 0; @@ -1468,7 +1501,7 @@ const posix = { format: FunctionPrototypeBind(_format, null, '/'), /** - * @param {string} path + * @param {PathLike} path * @returns {{ * dir: string; * root: string; @@ -1478,6 +1511,7 @@ const posix = { * }} */ parse(path) { + path = toPathIfFileURL(path, kNixURLOptions); validateString(path, 'path'); const ret = { root: '', dir: '', base: '', ext: '', name: '' }; diff --git a/test/parallel/test-path-basename.js b/test/parallel/test-path-basename.js index b16f9e5d63a94b..bf0aaea66d64c8 100644 --- a/test/parallel/test-path-basename.js +++ b/test/parallel/test-path-basename.js @@ -69,6 +69,11 @@ assert.strictEqual(path.posix.basename('basename.ext\\'), 'basename.ext\\'); assert.strictEqual(path.posix.basename('basename.ext\\\\'), 'basename.ext\\\\'); assert.strictEqual(path.posix.basename('foo'), 'foo'); +assert.strictEqual(path.posix.basename(new URL('file:///basename.ext')), 'basename.ext'); +assert.strictEqual(path.posix.basename(new URL('file:///basename.ext'), '.ext'), 'basename'); +assert.strictEqual(path.win32.basename(new URL('file://C:\\basename.ext')), 'basename.ext'); +assert.strictEqual(path.win32.basename(new URL('file://C:\\basename.ext'), '.ext'), 'basename'); + // POSIX filenames may include control characters // c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html const controlCharFilename = `Icon${String.fromCharCode(13)}`; diff --git a/test/parallel/test-path-dirname.js b/test/parallel/test-path-dirname.js index 0d4a182884ad0c..96cf4bb6b0a591 100644 --- a/test/parallel/test-path-dirname.js +++ b/test/parallel/test-path-dirname.js @@ -57,3 +57,8 @@ assert.strictEqual(path.win32.dirname(''), '.'); assert.strictEqual(path.win32.dirname('/'), '/'); assert.strictEqual(path.win32.dirname('////'), '/'); assert.strictEqual(path.win32.dirname('foo'), '.'); + +assert.strictEqual(path.posix.dirname(new URL('file:///a/b/')), '/a'); +assert.strictEqual(path.posix.dirname(new URL('file:///a/b')), '/a'); +assert.strictEqual(path.win32.dirname(new URL('file://C:\\a\\b\\')), 'C:\\a'); +assert.strictEqual(path.win32.dirname(new URL('file://C:\\a\\b')), 'C:\\a'); diff --git a/test/parallel/test-path-extname.js b/test/parallel/test-path-extname.js index be5a6316b0c7c3..64b31a91c99f97 100644 --- a/test/parallel/test-path-extname.js +++ b/test/parallel/test-path-extname.js @@ -98,3 +98,9 @@ assert.strictEqual(path.posix.extname('file\\'), ''); assert.strictEqual(path.posix.extname('file\\\\'), ''); assert.strictEqual(path.posix.extname('file.\\'), '.\\'); assert.strictEqual(path.posix.extname('file.\\\\'), '.\\\\'); + +assert.strictEqual(path.posix.extname(new URL('file:///path/to/file.ext')), '.ext'); +assert.strictEqual(path.posix.extname(new URL('file:///path.to/file.ext')), '.ext'); + +assert.strictEqual(path.win32.extname(new URL('file://C:/path/to/file.ext')), '.ext'); +assert.strictEqual(path.win32.extname(new URL('file://C:/path.to/file.ext')), '.ext'); diff --git a/test/parallel/test-path-isabsolute.js b/test/parallel/test-path-isabsolute.js index 66b4f1ee51103a..1660e0b0b76b59 100644 --- a/test/parallel/test-path-isabsolute.js +++ b/test/parallel/test-path-isabsolute.js @@ -26,3 +26,8 @@ assert.strictEqual(path.posix.isAbsolute('/home/foo'), true); assert.strictEqual(path.posix.isAbsolute('/home/foo/..'), true); assert.strictEqual(path.posix.isAbsolute('bar/'), false); assert.strictEqual(path.posix.isAbsolute('./baz'), false); + +assert.strictEqual(path.posix.isAbsolute(new URL('file:///home/foo')), true); +assert.strictEqual(path.posix.isAbsolute(new URL('file:///home/foo/..')), true); +assert.strictEqual(path.win32.isAbsolute(new URL('file://C:\\Users\\')), true); +assert.strictEqual(path.win32.isAbsolute(new URL('file://C:/Users/')), true); diff --git a/test/parallel/test-path-join.js b/test/parallel/test-path-join.js index d6d18399960d0b..f8028b81ace016 100644 --- a/test/parallel/test-path-join.js +++ b/test/parallel/test-path-join.js @@ -110,9 +110,11 @@ joinTests.push([ [['c:.', 'file'], 'c:file'], [['c:', '/'], 'c:\\'], [['c:', 'file'], 'c:\\file'], + [[new URL('file:///C:/hello'), 'foo'], 'C:\\hello\\foo'], ] ), ]); + joinTests.forEach((test) => { if (!Array.isArray(test[0])) test[0] = [test[0]]; @@ -133,8 +135,7 @@ joinTests.forEach((test) => { } if (actual !== expected && actualAlt !== expected) { const delimiter = test[0].map(JSON.stringify).join(','); - const message = `path.${os}.join(${delimiter})\n expect=${ - JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; + const message = `path.${os}.join(${delimiter})\n expect=${JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`; failures.push(`\n${message}`); } }); diff --git a/test/parallel/test-path-makelong.js b/test/parallel/test-path-makelong.js index 7a4783953c8fde..dd43aacc9574c8 100644 --- a/test/parallel/test-path-makelong.js +++ b/test/parallel/test-path-makelong.js @@ -86,3 +86,8 @@ assert.strictEqual(path.win32.toNamespacedPath(true), true); assert.strictEqual(path.win32.toNamespacedPath(1), 1); assert.strictEqual(path.win32.toNamespacedPath(), undefined); assert.strictEqual(path.win32.toNamespacedPath(emptyObj), emptyObj); + +assert.strictEqual(path.posix.toNamespacedPath(new URL('file:///a/b/')), '/a/b/'); +assert.strictEqual(path.posix.toNamespacedPath(new URL('file:///a/b')), '/a/b'); +assert.strictEqual(path.win32.toNamespacedPath(new URL('file://C:\\a\\b\\')), '\\\\?\\C:\\a\\b'); +assert.strictEqual(path.win32.toNamespacedPath(new URL('file://C:\\a\\b')), '\\\\?\\C:\\a\\b'); diff --git a/test/parallel/test-path-normalize.js b/test/parallel/test-path-normalize.js index e1d3b9ce1e6c02..75d9fa51962bfe 100644 --- a/test/parallel/test-path-normalize.js +++ b/test/parallel/test-path-normalize.js @@ -70,3 +70,8 @@ assert.strictEqual( '../../../../baz' ); assert.strictEqual(path.posix.normalize('foo/bar\\baz'), 'foo/bar\\baz'); + +assert.strictEqual(path.posix.normalize(new URL('file:///foo/../../../bar')), '/bar'); +assert.strictEqual(path.posix.normalize(new URL('file:///a/b/c/../../../x/y/z')), '/x/y/z'); +assert.strictEqual(path.win32.normalize(new URL('file://C:/foo/../../../bar')), 'C:\\bar'); +assert.strictEqual(path.win32.normalize(new URL('file://C:/a/b/c/../../../x/y/z')), 'C:\\x\\y\\z'); diff --git a/test/parallel/test-path-relative.js b/test/parallel/test-path-relative.js index f6a9f5662a6c24..4a237557cf7c3a 100644 --- a/test/parallel/test-path-relative.js +++ b/test/parallel/test-path-relative.js @@ -32,6 +32,9 @@ const relativeTests = [ ['\\\\foo\\baz', '\\\\foo\\baz-quux', '..\\baz-quux'], ['C:\\baz', '\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz'], ['\\\\foo\\bar\\baz', 'C:\\baz', 'C:\\baz'], + + [new URL('file://C:\\foo\\bar\\baz\\quux'), 'C:\\', '..\\..\\..\\..'], + [new URL('file://C:\\foo\\test'), 'C:\\foo\\test\\bar\\package.json', 'bar\\package.json'], ], ], [ path.posix.relative, @@ -49,6 +52,9 @@ const relativeTests = [ ['/baz-quux', '/baz', '../baz'], ['/baz', '/baz-quux', '../baz-quux'], ['/page1/page2/foo', '/', '../../..'], + + [new URL('file:///var/lib'), '/var', '..'], + [new URL('file:///var/lib'), '/bin', '../../bin'], ], ], ]; diff --git a/test/parallel/test-path-resolve.js b/test/parallel/test-path-resolve.js index 3fc9b2e3abd90a..1a36916c01bd0a 100644 --- a/test/parallel/test-path-resolve.js +++ b/test/parallel/test-path-resolve.js @@ -33,6 +33,9 @@ const resolveTests = [ [['c:/', '///some//dir'], 'c:\\some\\dir'], [['C:\\foo\\tmp.3\\', '..\\tmp.3\\cycles\\root.js'], 'C:\\foo\\tmp.3\\cycles\\root.js'], + + [[new URL('file:///C:\\foo\\bar\\baz\\quux'), 'bar'], 'C:\\foo\\bar\\baz\\quux\\bar'], + [[new URL('file:///C:\\foo\\test'), 'bar'], 'C:\\foo\\test\\bar'], ], ], [ path.posix.resolve, @@ -43,9 +46,12 @@ const resolveTests = [ [['.'], posixyCwd], [['/some/dir', '.', '/absolute/'], '/absolute'], [['/foo/tmp.3/', '../tmp.3/cycles/root.js'], '/foo/tmp.3/cycles/root.js'], + + [[new URL('file:///var/lib'), '../', 'file/'], '/var/file'], ], ], ]; + resolveTests.forEach(([resolve, tests]) => { tests.forEach(([test, expected]) => { const actual = resolve.apply(null, test); diff --git a/test/parallel/test-path.js b/test/parallel/test-path.js index 0cb55d42aa2999..7993d0b978216e 100644 --- a/test/parallel/test-path.js +++ b/test/parallel/test-path.js @@ -71,3 +71,9 @@ if (common.isWindows) assert.strictEqual(path, path.win32); else assert.strictEqual(path, path.posix); + + +// invalid URL tests +assert.throws(() => { + path.posix.isAbsolute(new URL('http://example.com')); +}, { code: 'ERR_INVALID_URL_SCHEME', name: 'TypeError' });