summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/path.js38
-rw-r--r--test/simple/test-path.js66
2 files changed, 94 insertions, 10 deletions
diff --git a/lib/path.js b/lib/path.js
index 7e521f20de..139d04aae8 100644
--- a/lib/path.js
+++ b/lib/path.js
@@ -59,7 +59,7 @@ if (isWindows) {
// Regex to split a windows path into three parts: [*, device, slash,
// tail] windows-only
var splitDeviceRe =
- /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?([\s\S]*?)$/;
+ /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/;
// Regex to split the tail part of the above into [*, dir, basename, ext]
var splitTailRe =
@@ -80,6 +80,10 @@ if (isWindows) {
return [device, dir, basename, ext];
};
+ var normalizeUNCRoot = function(device) {
+ return '\\\\' + device.replace(/^[\\\/]+/, '').replace(/[\\\/]+/g, '\\');
+ }
+
// path.resolve([from ...], to)
// windows version
exports.resolve = function() {
@@ -138,8 +142,11 @@ if (isWindows) {
}
}
- // Replace slashes (in UNC share name) by backslashes
- resolvedDevice = resolvedDevice.replace(/\//g, '\\');
+ // Convert slashes to backslashes when `resolvedDevice` points to an UNC
+ // root. Also squash multiple slashes into a single one where appropriate.
+ if (isUnc) {
+ resolvedDevice = normalizeUNCRoot(resolvedDevice);
+ }
// At this point the path should be resolved to a full absolute path,
// but handle relative paths to be safe (might happen when process.cwd()
@@ -180,7 +187,10 @@ if (isWindows) {
}
// Convert slashes to backslashes when `device` points to an UNC root.
- device = device.replace(/\//g, '\\');
+ // Also squash multiple slashes into a single one where appropriate.
+ if (isUnc) {
+ device = normalizeUNCRoot(device);
+ }
return device + (isAbsolute ? '\\' : '') + tail;
};
@@ -194,11 +204,21 @@ if (isWindows) {
var paths = Array.prototype.filter.call(arguments, f);
var joined = paths.join('\\');
- // Make sure that the joined path doesn't start with two slashes
- // - it will be mistaken for an unc path by normalize() -
- // unless the paths[0] also starts with two slashes
- if (/^[\\\/]{2}/.test(joined) && !/^[\\\/]{2}/.test(paths[0])) {
- joined = joined.substr(1);
+ // Make sure that the joined path doesn't start with two slashes, because
+ // normalize() will mistake it for an UNC path then.
+ //
+ // This step is skipped when it is very clear that the user actually
+ // intended to point at an UNC path. This is assumed when the first
+ // non-empty string arguments starts with exactly two slashes followed by
+ // at least one more non-slash character.
+ //
+ // Note that for normalize() to treat a path as an UNC path it needs to
+ // have at least 2 components, so we don't filter for that here.
+ // This means that the user can use join to construct UNC paths from
+ // a server name and a share name; for example:
+ // path.join('//server', 'share') -> '\\\\server\\share\')
+ if (!/^[\\\/]{2}[^\\\/]/.test(paths[0])) {
+ joined = joined.replace(/^[\\\/]{2,}/, '\\');
}
return exports.normalize(joined);
diff --git a/test/simple/test-path.js b/test/simple/test-path.js
index ca0afc26d5..1c03887e14 100644
--- a/test/simple/test-path.js
+++ b/test/simple/test-path.js
@@ -172,9 +172,67 @@ var joinTests =
[[' ', '.'], ' '],
[[' ', '/'], ' /'],
[[' ', ''], ' '],
+ [['/', 'foo'], '/foo'],
+ [['/', '/foo'], '/foo'],
+ [['/', '//foo'], '/foo'],
+ [['/', '', '/foo'], '/foo'],
+ [['', '/', 'foo'], '/foo'],
+ [['', '/', '/foo'], '/foo'],
// filtration of non-strings.
[['x', true, 7, 'y', null, {}], 'x/y']
];
+
+// Windows-specific join tests
+if (isWindows) {
+ joinTests = joinTests.concat(
+ [// UNC path expected
+ [['//foo/bar'], '//foo/bar/'],
+ [['\\/foo/bar'], '//foo/bar/'],
+ [['\\\\foo/bar'], '//foo/bar/'],
+ // UNC path expected - server and share separate
+ [['//foo', 'bar'], '//foo/bar/'],
+ [['//foo/', 'bar'], '//foo/bar/'],
+ [['//foo', '/bar'], '//foo/bar/'],
+ // UNC path expected - questionable
+ [['//foo', '', 'bar'], '//foo/bar/'],
+ [['//foo/', '', 'bar'], '//foo/bar/'],
+ [['//foo/', '', '/bar'], '//foo/bar/'],
+ // UNC path expected - even more questionable
+ [['', '//foo', 'bar'], '//foo/bar/'],
+ [['', '//foo/', 'bar'], '//foo/bar/'],
+ [['', '//foo/', '/bar'], '//foo/bar/'],
+ // No UNC path expected (no double slash in first component)
+ [['\\', 'foo/bar'], '/foo/bar'],
+ [['\\', '/foo/bar'], '/foo/bar'],
+ [['', '/', '/foo/bar'], '/foo/bar'],
+ // No UNC path expected (no non-slashes in first component - questionable)
+ [['//', 'foo/bar'], '/foo/bar'],
+ [['//', '/foo/bar'], '/foo/bar'],
+ [['\\\\', '/', '/foo/bar'], '/foo/bar'],
+ [['//'], '/'],
+ // No UNC path expected (share name missing - questionable).
+ [['//foo'], '/foo'],
+ [['//foo/'], '/foo/'],
+ [['//foo', '/'], '/foo/'],
+ [['//foo', '', '/'], '/foo/'],
+ // No UNC path expected (too many leading slashes - questionable)
+ [['///foo/bar'], '/foo/bar'],
+ [['////foo', 'bar'], '/foo/bar'],
+ [['\\\\\\/foo/bar'], '/foo/bar'],
+ // Drive-relative vs drive-absolute paths. This merely describes the
+ // status quo, rather than being obviously right
+ [['c:'], 'c:.'],
+ [['c:.'], 'c:.'],
+ [['c:', ''], 'c:.'],
+ [['', 'c:'], 'c:.'],
+ [['c:.', '/'], 'c:./'],
+ [['c:.', 'file'], 'c:file'],
+ [['c:', '/'], 'c:/'],
+ [['c:', 'file'], 'c:/file']
+ ]);
+}
+
+// Run the join tests.
joinTests.forEach(function(test) {
var actual = path.join.apply(path, test[0]);
var expected = isWindows ? test[1].replace(/\//g, '\\') : test[1];
@@ -215,7 +273,13 @@ if (isWindows) {
[['c:/ignore', 'c:/some/file'], 'c:\\some\\file'],
[['d:/ignore', 'd:some/dir//'], 'd:\\ignore\\some\\dir'],
[['.'], process.cwd()],
- [['//server/share', '..', 'relative\\'], '\\\\server\\share\\relative']];
+ [['//server/share', '..', 'relative\\'], '\\\\server\\share\\relative'],
+ [['c:/', '//'], 'c:\\'],
+ [['c:/', '//dir'], 'c:\\dir'],
+ [['c:/', '//server/share'], '\\\\server\\share\\'],
+ [['c:/', '//server//share'], '\\\\server\\share\\'],
+ [['c:/', '///some//dir'], 'c:\\some\\dir']
+ ];
} else {
// Posix
var resolveTests =