Skip to content

Commit f21d293

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 better integrate the existing UNC support, 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. Note that this almost certainly still isn't perfect, as URLs, UNC paths, and even Windows drive letters are all quite complex. But I don't have the time or project context to make a major change here.
1 parent 00d5f1a commit f21d293

File tree

5 files changed

+69
-12
lines changed

5 files changed

+69
-12
lines changed

index.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2423,6 +2423,13 @@ export namespace util {
24232423
/** A minimal path module to resolve Unix, Windows and URL paths alike. */
24242424
namespace path {
24252425

2426+
/**
2427+
* Gets the prefix of an absolute UNC path, Unix path, or URL that makes it absolute, if present.
2428+
* @param path Path to test
2429+
* @returns the prefix, or null if path is relative
2430+
*/
2431+
function absolutePrefix(path: string): (string|null);
2432+
24262433
/**
24272434
* Tests if the specified path is absolute.
24282435
* @param path Path to test

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)