From 7a98ac5844e613a866338bcd3b61466b4f5d6440 Mon Sep 17 00:00:00 2001 From: RedYetiDev <38299977+RedYetiDev@users.noreply.github.com> Date: Wed, 24 Apr 2024 16:27:31 -0400 Subject: [PATCH 1/3] path: support file urls --- doc/api/path.md | 83 +++++++++++++++------ lib/internal/url.js | 11 +-- lib/path.js | 87 +++++++++++++++------- test/parallel/test-path-basename.js | 5 ++ test/parallel/test-path-dirname.js | 5 ++ test/parallel/test-path-extname.js | 6 ++ test/parallel/test-path-isabsolute.js | 5 ++ test/parallel/test-path-join.js | 103 +++++++++++++------------- test/parallel/test-path-makelong.js | 5 ++ test/parallel/test-path-normalize.js | 5 ++ test/parallel/test-path-relative.js | 6 ++ test/parallel/test-path-resolve.js | 6 ++ test/parallel/test-path.js | 6 ++ 13 files changed, 227 insertions(+), 106 deletions(-) diff --git a/doc/api/path.md b/doc/api/path.md index c7a999cbb1ec22..8a5448a83c6443 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)` @@ -283,9 +292,13 @@ path.format({ -* `path` {string} +* `path` {string|URL} * Returns: {boolean} The `path.isAbsolute()` method determines if `path` is an absolute path. @@ -313,15 +326,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 @@ -339,15 +356,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 @@ -391,15 +412,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 @@ -458,7 +483,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` @@ -482,14 +507,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 @@ -513,15 +541,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 @@ -556,7 +588,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` @@ -593,14 +625,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. @@ -624,6 +660,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 9c18aab07ff19d..6f054943a15fd3 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 1a2b3e38eca03f..87092647584e44 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 platformIsWin32 = (process.platform === 'win32'); @@ -62,6 +70,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 = ''; @@ -153,10 +164,15 @@ function _format(sep, pathObject) { return dir === pathObject.root ? `${dir}${base}` : `${dir}${sep}${base}`; } +/** + * @typedef PathLike + * @type {string|URL} + */ + const win32 = { /** * path.resolve([from ...], to) - * @param {...string} args + * @param {...(PathLike)} args * @returns {string} */ resolve(...args) { @@ -167,7 +183,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 @@ -302,10 +318,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) @@ -400,10 +417,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) @@ -419,7 +437,7 @@ const win32 = { }, /** - * @param {...string} args + * @param {...(PathLike)} args * @returns {string} */ join(...args) { @@ -429,7 +447,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) @@ -493,11 +511,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'); @@ -616,10 +636,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; @@ -651,10 +672,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) @@ -746,13 +768,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; @@ -834,10 +857,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; @@ -904,7 +928,7 @@ const win32 = { format: FunctionPrototypeBind(_format, null, '\\'), /** - * @param {string} path + * @param {PathLike} path * @returns {{ * dir: string; * root: string; @@ -914,6 +938,7 @@ const win32 = { * }} */ parse(path) { + path = toPathIfFileURL(path, kWindowsURLOptions); validateString(path, 'path'); const ret = { root: '', dir: '', base: '', ext: '', name: '' }; @@ -1089,7 +1114,7 @@ const posixCwd = (() => { const posix = { /** * path.resolve([from ...], to) - * @param {...string} args + * @param {...PathLike} args * @returns {string} */ resolve(...args) { @@ -1097,7 +1122,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 @@ -1124,10 +1149,11 @@ const posix = { }, /** - * @param {string} path + * @param {PathLike} path * @returns {string} */ normalize(path) { + path = toPathIfFileURL(path, kNixURLOptions); validateString(path, 'path'); if (path.length === 0) @@ -1153,17 +1179,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) { @@ -1171,7 +1198,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) @@ -1186,11 +1213,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'); @@ -1263,19 +1292,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 '.'; @@ -1302,13 +1332,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; @@ -1382,10 +1413,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; @@ -1440,7 +1472,7 @@ const posix = { format: FunctionPrototypeBind(_format, null, '/'), /** - * @param {string} path + * @param {PathLike} path * @returns {{ * dir: string; * root: string; @@ -1450,6 +1482,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 d085cecb24f069..16e046796bd991 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..0f87e10f7d5cee 100644 --- a/test/parallel/test-path-join.js +++ b/test/parallel/test-path-join.js @@ -8,55 +8,55 @@ const backslashRE = /\\/g; const joinTests = [ [ [path.posix.join, path.win32.join], - // Arguments result - [[['.', 'x/b', '..', '/b/c.js'], 'x/b/c.js'], - [[], '.'], - [['/.', 'x/b', '..', '/b/c.js'], '/x/b/c.js'], - [['/foo', '../../../bar'], '/bar'], - [['foo', '../../../bar'], '../../bar'], - [['foo/', '../../../bar'], '../../bar'], - [['foo/x', '../../../bar'], '../bar'], - [['foo/x', './bar'], 'foo/x/bar'], - [['foo/x/', './bar'], 'foo/x/bar'], - [['foo/x/', '.', 'bar'], 'foo/x/bar'], - [['./'], './'], - [['.', './'], './'], - [['.', '.', '.'], '.'], - [['.', './', '.'], '.'], - [['.', '/./', '.'], '.'], - [['.', '/////./', '.'], '.'], - [['.'], '.'], - [['', '.'], '.'], - [['', 'foo'], 'foo'], - [['foo', '/bar'], 'foo/bar'], - [['', '/foo'], '/foo'], - [['', '', '/foo'], '/foo'], - [['', '', 'foo'], 'foo'], - [['foo', ''], 'foo'], - [['foo/', ''], 'foo/'], - [['foo', '', '/bar'], 'foo/bar'], - [['./', '..', '/foo'], '../foo'], - [['./', '..', '..', '/foo'], '../../foo'], - [['.', '..', '..', '/foo'], '../../foo'], - [['', '..', '..', '/foo'], '../../foo'], - [['/'], '/'], - [['/', '.'], '/'], - [['/', '..'], '/'], - [['/', '..', '..'], '/'], - [[''], '.'], - [['', ''], '.'], - [[' /foo'], ' /foo'], - [[' ', 'foo'], ' /foo'], - [[' ', '.'], ' '], - [[' ', '/'], ' /'], - [[' ', ''], ' '], - [['/', 'foo'], '/foo'], - [['/', '/foo'], '/foo'], - [['/', '//foo'], '/foo'], - [['/', '', '/foo'], '/foo'], - [['', '/', 'foo'], '/foo'], - [['', '/', '/foo'], '/foo'], - ], + // Arguments result + [[['.', 'x/b', '..', '/b/c.js'], 'x/b/c.js'], + [[], '.'], + [['/.', 'x/b', '..', '/b/c.js'], '/x/b/c.js'], + [['/foo', '../../../bar'], '/bar'], + [['foo', '../../../bar'], '../../bar'], + [['foo/', '../../../bar'], '../../bar'], + [['foo/x', '../../../bar'], '../bar'], + [['foo/x', './bar'], 'foo/x/bar'], + [['foo/x/', './bar'], 'foo/x/bar'], + [['foo/x/', '.', 'bar'], 'foo/x/bar'], + [['./'], './'], + [['.', './'], './'], + [['.', '.', '.'], '.'], + [['.', './', '.'], '.'], + [['.', '/./', '.'], '.'], + [['.', '/////./', '.'], '.'], + [['.'], '.'], + [['', '.'], '.'], + [['', 'foo'], 'foo'], + [['foo', '/bar'], 'foo/bar'], + [['', '/foo'], '/foo'], + [['', '', '/foo'], '/foo'], + [['', '', 'foo'], 'foo'], + [['foo', ''], 'foo'], + [['foo/', ''], 'foo/'], + [['foo', '', '/bar'], 'foo/bar'], + [['./', '..', '/foo'], '../foo'], + [['./', '..', '..', '/foo'], '../../foo'], + [['.', '..', '..', '/foo'], '../../foo'], + [['', '..', '..', '/foo'], '../../foo'], + [['/'], '/'], + [['/', '.'], '/'], + [['/', '..'], '/'], + [['/', '..', '..'], '/'], + [[''], '.'], + [['', ''], '.'], + [[' /foo'], ' /foo'], + [[' ', 'foo'], ' /foo'], + [[' ', '.'], ' '], + [[' ', '/'], ' /'], + [[' ', ''], ' '], + [['/', 'foo'], '/foo'], + [['/', '/foo'], '/foo'], + [['/', '//foo'], '/foo'], + [['/', '', '/foo'], '/foo'], + [['', '/', 'foo'], '/foo'], + [['', '/', '/foo'], '/foo'], + ], ], ]; @@ -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 b0a4ebc6b30b62..14cf4bc6947419 100644 --- a/test/parallel/test-path-makelong.js +++ b/test/parallel/test-path-makelong.js @@ -85,3 +85,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' }); From 02623b9c20655201528b6dfae2759ed833d27e7b Mon Sep 17 00:00:00 2001 From: RedYetiDev <38299977+RedYetiDev@users.noreply.github.com> Date: Sat, 4 May 2024 11:27:13 -0400 Subject: [PATCH 2/3] fix formatting --- test/parallel/test-path-join.js | 98 ++++++++++++++++----------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/test/parallel/test-path-join.js b/test/parallel/test-path-join.js index 0f87e10f7d5cee..f8028b81ace016 100644 --- a/test/parallel/test-path-join.js +++ b/test/parallel/test-path-join.js @@ -8,55 +8,55 @@ const backslashRE = /\\/g; const joinTests = [ [ [path.posix.join, path.win32.join], - // Arguments result - [[['.', 'x/b', '..', '/b/c.js'], 'x/b/c.js'], - [[], '.'], - [['/.', 'x/b', '..', '/b/c.js'], '/x/b/c.js'], - [['/foo', '../../../bar'], '/bar'], - [['foo', '../../../bar'], '../../bar'], - [['foo/', '../../../bar'], '../../bar'], - [['foo/x', '../../../bar'], '../bar'], - [['foo/x', './bar'], 'foo/x/bar'], - [['foo/x/', './bar'], 'foo/x/bar'], - [['foo/x/', '.', 'bar'], 'foo/x/bar'], - [['./'], './'], - [['.', './'], './'], - [['.', '.', '.'], '.'], - [['.', './', '.'], '.'], - [['.', '/./', '.'], '.'], - [['.', '/////./', '.'], '.'], - [['.'], '.'], - [['', '.'], '.'], - [['', 'foo'], 'foo'], - [['foo', '/bar'], 'foo/bar'], - [['', '/foo'], '/foo'], - [['', '', '/foo'], '/foo'], - [['', '', 'foo'], 'foo'], - [['foo', ''], 'foo'], - [['foo/', ''], 'foo/'], - [['foo', '', '/bar'], 'foo/bar'], - [['./', '..', '/foo'], '../foo'], - [['./', '..', '..', '/foo'], '../../foo'], - [['.', '..', '..', '/foo'], '../../foo'], - [['', '..', '..', '/foo'], '../../foo'], - [['/'], '/'], - [['/', '.'], '/'], - [['/', '..'], '/'], - [['/', '..', '..'], '/'], - [[''], '.'], - [['', ''], '.'], - [[' /foo'], ' /foo'], - [[' ', 'foo'], ' /foo'], - [[' ', '.'], ' '], - [[' ', '/'], ' /'], - [[' ', ''], ' '], - [['/', 'foo'], '/foo'], - [['/', '/foo'], '/foo'], - [['/', '//foo'], '/foo'], - [['/', '', '/foo'], '/foo'], - [['', '/', 'foo'], '/foo'], - [['', '/', '/foo'], '/foo'], - ], + // Arguments result + [[['.', 'x/b', '..', '/b/c.js'], 'x/b/c.js'], + [[], '.'], + [['/.', 'x/b', '..', '/b/c.js'], '/x/b/c.js'], + [['/foo', '../../../bar'], '/bar'], + [['foo', '../../../bar'], '../../bar'], + [['foo/', '../../../bar'], '../../bar'], + [['foo/x', '../../../bar'], '../bar'], + [['foo/x', './bar'], 'foo/x/bar'], + [['foo/x/', './bar'], 'foo/x/bar'], + [['foo/x/', '.', 'bar'], 'foo/x/bar'], + [['./'], './'], + [['.', './'], './'], + [['.', '.', '.'], '.'], + [['.', './', '.'], '.'], + [['.', '/./', '.'], '.'], + [['.', '/////./', '.'], '.'], + [['.'], '.'], + [['', '.'], '.'], + [['', 'foo'], 'foo'], + [['foo', '/bar'], 'foo/bar'], + [['', '/foo'], '/foo'], + [['', '', '/foo'], '/foo'], + [['', '', 'foo'], 'foo'], + [['foo', ''], 'foo'], + [['foo/', ''], 'foo/'], + [['foo', '', '/bar'], 'foo/bar'], + [['./', '..', '/foo'], '../foo'], + [['./', '..', '..', '/foo'], '../../foo'], + [['.', '..', '..', '/foo'], '../../foo'], + [['', '..', '..', '/foo'], '../../foo'], + [['/'], '/'], + [['/', '.'], '/'], + [['/', '..'], '/'], + [['/', '..', '..'], '/'], + [[''], '.'], + [['', ''], '.'], + [[' /foo'], ' /foo'], + [[' ', 'foo'], ' /foo'], + [[' ', '.'], ' '], + [[' ', '/'], ' /'], + [[' ', ''], ' '], + [['/', 'foo'], '/foo'], + [['/', '/foo'], '/foo'], + [['/', '//foo'], '/foo'], + [['/', '', '/foo'], '/foo'], + [['', '/', 'foo'], '/foo'], + [['', '/', '/foo'], '/foo'], + ], ], ]; From 21c7534b8e2c2d3f5874c1ef4d88a5e45c270a02 Mon Sep 17 00:00:00 2001 From: Aviv Keller <38299977+RedYetiDev@users.noreply.github.com> Date: Tue, 25 Jun 2024 17:07:52 -0400 Subject: [PATCH 3/3] Update path.js --- lib/path.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/path.js b/lib/path.js index 774bb9fce3c822..a959acc82eb8de 100644 --- a/lib/path.js +++ b/lib/path.js @@ -172,6 +172,11 @@ 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');