Skip to content

Commit b953768

Browse files
fix: support file: schema (#467)
1 parent 6ff935b commit b953768

File tree

2 files changed

+79
-36
lines changed

2 files changed

+79
-36
lines changed

lib/util/identifier.js

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,64 +5,72 @@
55

66
"use strict";
77

8+
const memorize = require("./memoize");
9+
10+
const getUrl = memorize(() => require("url"));
11+
812
const PATH_QUERY_FRAGMENT_REGEXP =
913
/^(#?(?:\0.|[^?#\0])*)(\?(?:\0.|[^#\0])*)?(#.*)?$/;
1014
const ZERO_ESCAPE_REGEXP = /\0(.)/g;
15+
const FILE_REG_EXP = /file:/i;
1116

1217
/**
1318
* @param {string} identifier identifier
14-
* @returns {[string, string, string]|null} parsed identifier
19+
* @returns {[string, string, string] | null} parsed identifier
1520
*/
1621
function parseIdentifier(identifier) {
1722
if (!identifier) {
1823
return null;
1924
}
2025

26+
if (FILE_REG_EXP.test(identifier)) {
27+
identifier = getUrl().fileURLToPath(identifier);
28+
}
29+
2130
const firstEscape = identifier.indexOf("\0");
22-
if (firstEscape < 0) {
23-
// Fast path for inputs that don't use \0 escaping.
24-
const queryStart = identifier.indexOf("?");
25-
// Start at index 1 to ignore a possible leading hash.
26-
const fragmentStart = identifier.indexOf("#", 1);
27-
28-
if (fragmentStart < 0) {
29-
if (queryStart < 0) {
30-
// No fragment, no query
31-
return [identifier, "", ""];
32-
}
33-
// Query, no fragment
34-
return [
35-
identifier.slice(0, queryStart),
36-
identifier.slice(queryStart),
37-
"",
38-
];
39-
}
4031

41-
if (queryStart < 0 || fragmentStart < queryStart) {
42-
// Fragment, no query
43-
return [
44-
identifier.slice(0, fragmentStart),
45-
"",
46-
identifier.slice(fragmentStart),
47-
];
48-
}
32+
// Handle `\0`
33+
if (firstEscape !== -1) {
34+
const match = PATH_QUERY_FRAGMENT_REGEXP.exec(identifier);
35+
36+
if (!match) return null;
4937

50-
// Query and fragment
5138
return [
52-
identifier.slice(0, queryStart),
53-
identifier.slice(queryStart, fragmentStart),
54-
identifier.slice(fragmentStart),
39+
match[1].replace(ZERO_ESCAPE_REGEXP, "$1"),
40+
match[2] ? match[2].replace(ZERO_ESCAPE_REGEXP, "$1") : "",
41+
match[3] || "",
5542
];
5643
}
5744

58-
const match = PATH_QUERY_FRAGMENT_REGEXP.exec(identifier);
45+
// Fast path for inputs that don't use \0 escaping.
46+
const queryStart = identifier.indexOf("?");
47+
// Start at index 1 to ignore a possible leading hash.
48+
const fragmentStart = identifier.indexOf("#", 1);
5949

60-
if (!match) return null;
50+
if (fragmentStart < 0) {
51+
if (queryStart < 0) {
52+
// No fragment, no query
53+
return [identifier, "", ""];
54+
}
55+
56+
// Query, no fragment
57+
return [identifier.slice(0, queryStart), identifier.slice(queryStart), ""];
58+
}
59+
60+
if (queryStart < 0 || fragmentStart < queryStart) {
61+
// Fragment, no query
62+
return [
63+
identifier.slice(0, fragmentStart),
64+
"",
65+
identifier.slice(fragmentStart),
66+
];
67+
}
6168

69+
// Query and fragment
6270
return [
63-
match[1].replace(ZERO_ESCAPE_REGEXP, "$1"),
64-
match[2] ? match[2].replace(ZERO_ESCAPE_REGEXP, "$1") : "",
65-
match[3] || "",
71+
identifier.slice(0, queryStart),
72+
identifier.slice(queryStart, fragmentStart),
73+
identifier.slice(fragmentStart),
6674
];
6775
}
6876

test/resolve.test.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use strict";
22

33
const path = require("path");
4+
const url = require("url");
45
const resolve = require("../");
56

67
const fixtures = path.join(__dirname, "fixtures");
@@ -277,6 +278,40 @@ describe("resolve", () => {
277278
`${path.join(fixtures, "no\0#fragment/\0#", "\0#.js")}#fragment`,
278279
);
279280

281+
testResolve(
282+
"handle file URL",
283+
fixtures,
284+
url.pathToFileURL(path.resolve(fixtures, "./main1.js")).toString(),
285+
path.join(fixtures, "main1.js"),
286+
);
287+
288+
testResolve(
289+
"handle file URL with query",
290+
fixtures,
291+
`${url
292+
.pathToFileURL(path.resolve(fixtures, "./main1.js"))
293+
.toString()}?query`,
294+
path.join(fixtures, "main1.js"),
295+
);
296+
297+
testResolve(
298+
"handle file URL with fragment",
299+
fixtures,
300+
`${url
301+
.pathToFileURL(path.resolve(fixtures, "./main1.js"))
302+
.toString()}#fragment`,
303+
path.join(fixtures, "main1.js"),
304+
);
305+
306+
testResolve(
307+
"handle file URL with query and fragment",
308+
fixtures,
309+
`${url
310+
.pathToFileURL(path.resolve(fixtures, "./main1.js"))
311+
.toString()}?query#fragment`,
312+
path.join(fixtures, "main1.js"),
313+
);
314+
280315
it("should correctly resolve", (done) => {
281316
const issue238 = path.resolve(fixtures, "issue-238");
282317

0 commit comments

Comments
 (0)