Skip to content

Commit 862eb9f

Browse files
committed
Fix: Allow absolute URLs as paths, e.g. for load()
When using protobuf.js in a browser, it's useful to be able to load proto files from other origins. But currently, path.normalize() mangles URLs by removing the second slash after the scheme (e.g. "https://example.com/" becomes "https:/example.com"). Fix that issue, and also make the bolted-on UNC support cleaner, by introducing a new path.absolutePrefix() function that checks for all the various prefixes that indicate a path is absolute and returns the one it finds (if any). By including both slashes in the return value for URLs, we exclude them from the normalization logic.
1 parent 00d5f1a commit 862eb9f

File tree

4 files changed

+62
-12
lines changed

4 files changed

+62
-12
lines changed

lib/path/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,7 @@ API
1616
* **path.resolve(originPath: `string`, includePath: `string`, [alreadyNormalized=false: `boolean`]): `string`**<br />
1717
Resolves the specified include path against the specified origin path.
1818

19+
* **path.absolutePrefix(path: `string`): `string|null`**<br />
20+
Gets the prefix of an absolute Windows, UNC, or Unix path or a URL that indicates it's absolute.
21+
1922
**License:** [BSD 3-Clause License](https://opensource.org/licenses/BSD-3-Clause)

lib/path/index.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
/**
2+
* Gets the prefix of an absolute Windows, UNC, or Unix path or a URL that indicates it's absolute.
3+
* @param {string} path Path to test
4+
* @returns {string|null} the prefix, or null if path is relative
5+
*/
6+
export function absolutePrefix(path: string): (string|null);
7+
18
/**
29
* Tests if the specified path is absolute.
310
* @param {string} path Path to test

lib/path/index.js

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,39 @@
77
*/
88
var path = exports;
99

10+
var absolutePrefix =
11+
/**
12+
* Gets the prefix of an absolute Windows, UNC, or Unix path or a URL that indicates it's absolute.
13+
* @param {string} path Path to test
14+
* @returns {string|null} the prefix, or null if path is relative
15+
*/
16+
path.absolutePrefix = function absolutePrefix(path) {
17+
var match;
18+
if (path.startsWith("\\\\")) {
19+
// UNC path
20+
return "\\\\";
21+
} else if ((match = /^\w*:\/\//.exec(path)) !== null) {
22+
// URL
23+
return match[0];
24+
} else if ((match = /^\w:/.exec(path)) !== null) {
25+
// Windows path
26+
return match[0];
27+
} else if (path.startsWith("/")) {
28+
// Unix path
29+
return "/";
30+
} else {
31+
return null;
32+
}
33+
}
34+
1035
var isAbsolute =
1136
/**
1237
* Tests if the specified path is absolute.
1338
* @param {string} path Path to test
1439
* @returns {boolean} `true` if path is absolute
1540
*/
1641
path.isAbsolute = function isAbsolute(path) {
17-
return /^(?:\/|\w+:|\\\\\w+)/.test(path);
42+
return absolutePrefix(path) !== null;
1843
};
1944

2045
var normalize =
@@ -24,20 +49,18 @@ var normalize =
2449
* @returns {string} Normalized path
2550
*/
2651
path.normalize = function normalize(path) {
27-
var firstTwoCharacters = path.substring(0,2);
28-
var uncPrefix = "";
29-
if (firstTwoCharacters === "\\\\") {
30-
uncPrefix = firstTwoCharacters;
31-
path = path.substring(2);
52+
var absolute = false;
53+
var prefix = absolutePrefix(path);
54+
if (prefix !== null) {
55+
absolute = true;
56+
path = path.substring(prefix.length);
57+
} else {
58+
prefix = "";
3259
}
3360

3461
path = path.replace(/\\/g, "/")
3562
.replace(/\/{2,}/g, "/");
36-
var parts = path.split("/"),
37-
absolute = isAbsolute(path),
38-
prefix = "";
39-
if (absolute)
40-
prefix = parts.shift() + "/";
63+
var parts = path.split("/");
4164
for (var i = 0; i < parts.length;) {
4265
if (parts[i] === "..") {
4366
if (i > 0 && parts[i - 1] !== "..")
@@ -51,7 +74,7 @@ path.normalize = function normalize(path) {
5174
else
5275
++i;
5376
}
54-
return uncPrefix + prefix + parts.join("/");
77+
return prefix + parts.join("/");
5578
};
5679

5780
/**

lib/path/tests/index.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ tape.test("path", function(test) {
1212

1313
test.ok(path.isAbsolute("\\\\some-unc\\path\\file.js"), "should identify windows unc paths");
1414

15+
test.ok(path.isAbsolute("https://example.com/path/file.js"), "should identify absolute URLs");
16+
test.ok(path.isAbsolute("://example.com/path/file.js"), "should identify protocol-relative URLs");
17+
1518
var paths = [
1619
{
1720
actual: "X:\\some\\..\\.\\path\\\\file.js",
@@ -61,6 +64,20 @@ tape.test("path", function(test) {
6164
origin: "\\\\some-unc\\path\\..\\origin.js",
6265
expected: "\\\\some-unc/file.js"
6366
}
67+
}, {
68+
actual: "https://example.com/path/../file.js",
69+
normal: "https://example.com/file.js",
70+
resolve: {
71+
origin: "/some/local/path",
72+
expected: "https://example.com/file.js"
73+
}
74+
}, {
75+
actual: "path/file2.js",
76+
normal: "path/file2.js",
77+
resolve: {
78+
origin: "https://example.com/file1.js",
79+
expected: "https://example.com/path/file2.js"
80+
}
6481
}
6582
];
6683

0 commit comments

Comments
 (0)