diff --git a/lib/util/identifier.js b/lib/util/identifier.js index be06d0fb..46a1255c 100644 --- a/lib/util/identifier.js +++ b/lib/util/identifier.js @@ -5,64 +5,72 @@ "use strict"; +const memorize = require("./memoize"); + +const getUrl = memorize(() => require("url")); + const PATH_QUERY_FRAGMENT_REGEXP = /^(#?(?:\0.|[^?#\0])*)(\?(?:\0.|[^#\0])*)?(#.*)?$/; const ZERO_ESCAPE_REGEXP = /\0(.)/g; +const FILE_REG_EXP = /file:/i; /** * @param {string} identifier identifier - * @returns {[string, string, string]|null} parsed identifier + * @returns {[string, string, string] | null} parsed identifier */ function parseIdentifier(identifier) { if (!identifier) { return null; } + if (FILE_REG_EXP.test(identifier)) { + identifier = getUrl().fileURLToPath(identifier); + } + const firstEscape = identifier.indexOf("\0"); - if (firstEscape < 0) { - // Fast path for inputs that don't use \0 escaping. - const queryStart = identifier.indexOf("?"); - // Start at index 1 to ignore a possible leading hash. - const fragmentStart = identifier.indexOf("#", 1); - - if (fragmentStart < 0) { - if (queryStart < 0) { - // No fragment, no query - return [identifier, "", ""]; - } - // Query, no fragment - return [ - identifier.slice(0, queryStart), - identifier.slice(queryStart), - "", - ]; - } - if (queryStart < 0 || fragmentStart < queryStart) { - // Fragment, no query - return [ - identifier.slice(0, fragmentStart), - "", - identifier.slice(fragmentStart), - ]; - } + // Handle `\0` + if (firstEscape !== -1) { + const match = PATH_QUERY_FRAGMENT_REGEXP.exec(identifier); + + if (!match) return null; - // Query and fragment return [ - identifier.slice(0, queryStart), - identifier.slice(queryStart, fragmentStart), - identifier.slice(fragmentStart), + match[1].replace(ZERO_ESCAPE_REGEXP, "$1"), + match[2] ? match[2].replace(ZERO_ESCAPE_REGEXP, "$1") : "", + match[3] || "", ]; } - const match = PATH_QUERY_FRAGMENT_REGEXP.exec(identifier); + // Fast path for inputs that don't use \0 escaping. + const queryStart = identifier.indexOf("?"); + // Start at index 1 to ignore a possible leading hash. + const fragmentStart = identifier.indexOf("#", 1); - if (!match) return null; + if (fragmentStart < 0) { + if (queryStart < 0) { + // No fragment, no query + return [identifier, "", ""]; + } + + // Query, no fragment + return [identifier.slice(0, queryStart), identifier.slice(queryStart), ""]; + } + + if (queryStart < 0 || fragmentStart < queryStart) { + // Fragment, no query + return [ + identifier.slice(0, fragmentStart), + "", + identifier.slice(fragmentStart), + ]; + } + // Query and fragment return [ - match[1].replace(ZERO_ESCAPE_REGEXP, "$1"), - match[2] ? match[2].replace(ZERO_ESCAPE_REGEXP, "$1") : "", - match[3] || "", + identifier.slice(0, queryStart), + identifier.slice(queryStart, fragmentStart), + identifier.slice(fragmentStart), ]; } diff --git a/test/resolve.test.js b/test/resolve.test.js index d7c86561..e2b3f445 100644 --- a/test/resolve.test.js +++ b/test/resolve.test.js @@ -1,6 +1,7 @@ "use strict"; const path = require("path"); +const url = require("url"); const resolve = require("../"); const fixtures = path.join(__dirname, "fixtures"); @@ -277,6 +278,40 @@ describe("resolve", () => { `${path.join(fixtures, "no\0#fragment/\0#", "\0#.js")}#fragment`, ); + testResolve( + "handle file URL", + fixtures, + url.pathToFileURL(path.resolve(fixtures, "./main1.js")).toString(), + path.join(fixtures, "main1.js"), + ); + + testResolve( + "handle file URL with query", + fixtures, + `${url + .pathToFileURL(path.resolve(fixtures, "./main1.js")) + .toString()}?query`, + path.join(fixtures, "main1.js"), + ); + + testResolve( + "handle file URL with fragment", + fixtures, + `${url + .pathToFileURL(path.resolve(fixtures, "./main1.js")) + .toString()}#fragment`, + path.join(fixtures, "main1.js"), + ); + + testResolve( + "handle file URL with query and fragment", + fixtures, + `${url + .pathToFileURL(path.resolve(fixtures, "./main1.js")) + .toString()}?query#fragment`, + path.join(fixtures, "main1.js"), + ); + it("should correctly resolve", (done) => { const issue238 = path.resolve(fixtures, "issue-238");