From e906e2be1b4c10b41efe63990dfae9be2a9b29a2 Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Sat, 8 Jul 2023 23:56:24 -0400 Subject: [PATCH 1/2] url: reduce `pathToFileURL` cpp calls --- lib/internal/url.js | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index 302d3fa08753a5..b59a1ff4dc21f2 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1414,6 +1414,8 @@ const backslashRegEx = /\\/g; const newlineRegEx = /\n/g; const carriageReturnRegEx = /\r/g; const tabRegEx = /\t/g; +const questionRegex = /\?/; +const hashRegex = /#/; function encodePathChars(filepath) { if (StringPrototypeIncludes(filepath, '%')) @@ -1431,8 +1433,8 @@ function encodePathChars(filepath) { } function pathToFileURL(filepath) { - const outURL = new URL('file://'); if (isWindows && StringPrototypeStartsWith(filepath, '\\\\')) { + const outURL = new URL('file://'); // UNC path format: \\server\share\resource const hostnameEndIndex = StringPrototypeIndexOf(filepath, '\\', 2); if (hostnameEndIndex === -1) { @@ -1453,18 +1455,29 @@ function pathToFileURL(filepath) { outURL.hostname = domainToASCII(hostname); outURL.pathname = encodePathChars( RegExpPrototypeSymbolReplace(backslashRegEx, StringPrototypeSlice(filepath, hostnameEndIndex), '/')); - } else { - let resolved = path.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)) && - resolved[resolved.length - 1] !== path.sep) - resolved += '/'; - outURL.pathname = encodePathChars(resolved); - } - return outURL; + return outURL; + } + let resolved = path.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)) && + resolved[resolved.length - 1] !== path.sep) + resolved += '/'; + + // Call encodePathChars first to avoid encoding % again for ? and #. + resolved = encodePathChars(resolved); + + // Question and hash character should be included in pathname. + // Therefore, encoding is required to eliminate parsing them in different states. + // This is done as an optimization to not creating a URL instance and + // later triggering pathname setter, which impacts performance + if (StringPrototypeIncludes(resolved, '?')) + resolved = RegExpPrototypeSymbolReplace(questionRegex, resolved, '%3F'); + if (StringPrototypeIncludes(resolved, '#')) + resolved = RegExpPrototypeSymbolReplace(hashRegex, resolved, '%23'); + return new URL(`file://${resolved}`); } function toPathIfFileURL(fileURLOrPath) { From 6d58fe41799e5bb3578e1a7467015a759cc6a597 Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Tue, 11 Jul 2023 15:59:15 -0400 Subject: [PATCH 2/2] fixup! url: reduce `pathToFileURL` cpp calls --- lib/internal/url.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index b59a1ff4dc21f2..ae506b57ce729a 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1414,20 +1414,20 @@ const backslashRegEx = /\\/g; const newlineRegEx = /\n/g; const carriageReturnRegEx = /\r/g; const tabRegEx = /\t/g; -const questionRegex = /\?/; -const hashRegex = /#/; +const questionRegex = /\?/g; +const hashRegex = /#/g; function encodePathChars(filepath) { - if (StringPrototypeIncludes(filepath, '%')) + if (StringPrototypeIndexOf(filepath, '%') !== -1) filepath = RegExpPrototypeSymbolReplace(percentRegEx, filepath, '%25'); // In posix, backslash is a valid character in paths: - if (!isWindows && StringPrototypeIncludes(filepath, '\\')) + if (!isWindows && StringPrototypeIndexOf(filepath, '\\') !== -1) filepath = RegExpPrototypeSymbolReplace(backslashRegEx, filepath, '%5C'); - if (StringPrototypeIncludes(filepath, '\n')) + if (StringPrototypeIndexOf(filepath, '\n') !== -1) filepath = RegExpPrototypeSymbolReplace(newlineRegEx, filepath, '%0A'); - if (StringPrototypeIncludes(filepath, '\r')) + if (StringPrototypeIndexOf(filepath, '\r') !== -1) filepath = RegExpPrototypeSymbolReplace(carriageReturnRegEx, filepath, '%0D'); - if (StringPrototypeIncludes(filepath, '\t')) + if (StringPrototypeIndexOf(filepath, '\t') !== -1) filepath = RegExpPrototypeSymbolReplace(tabRegEx, filepath, '%09'); return filepath; } @@ -1473,9 +1473,9 @@ function pathToFileURL(filepath) { // Therefore, encoding is required to eliminate parsing them in different states. // This is done as an optimization to not creating a URL instance and // later triggering pathname setter, which impacts performance - if (StringPrototypeIncludes(resolved, '?')) + if (StringPrototypeIndexOf(resolved, '?') !== -1) resolved = RegExpPrototypeSymbolReplace(questionRegex, resolved, '%3F'); - if (StringPrototypeIncludes(resolved, '#')) + if (StringPrototypeIndexOf(resolved, '#') !== -1) resolved = RegExpPrototypeSymbolReplace(hashRegex, resolved, '%23'); return new URL(`file://${resolved}`); }