path: allow calling platform specific methods

Add path.posix and path.win32 which have the specific methods like
resolve and normalize so you can specifically normalize or resolve
based on the target platform.

PR-URL: https://github.com/joyent/node/pull/5661
Reviewed-by: Chris Dickinson <christopher.s.dickinson@gmail.com>
This commit is contained in:
Timothy J Fontaine 2013-06-10 17:09:54 -07:00 committed by Chris Dickinson
parent 4dc8b26bbe
commit 6a90a06002
3 changed files with 535 additions and 502 deletions

View File

@ -201,3 +201,13 @@ An example on Windows:
process.env.PATH.split(path.delimiter)
// returns
['C:\Windows\system32', 'C:\Windows', 'C:\Program Files\nodejs\']
## path.posix
Provide access to aforementioned `path` methods but always interact in a posix
compatible way.
## path.win32
Provide access to aforementioned `path` methods but always interact in a win32
compatible way.

View File

@ -55,401 +55,283 @@ function normalizeArray(parts, allowAboveRoot) {
}
if (isWindows) {
// Regex to split a windows path into three parts: [*, device, slash,
// tail] windows-only
var splitDeviceRe =
/^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/;
// Regex to split a windows path into three parts: [*, device, slash,
// tail] windows-only
var splitDeviceRe =
/^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/;
// Regex to split the tail part of the above into [*, dir, basename, ext]
var splitTailRe =
/^([\s\S]*?)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/;
// Regex to split the tail part of the above into [*, dir, basename, ext]
var splitTailRe =
/^([\s\S]*?)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/;
// Function to split a filename into [root, dir, basename, ext]
// windows version
var splitPath = function(filename) {
// Separate device+slash from tail
var result = splitDeviceRe.exec(filename),
device = (result[1] || '') + (result[2] || ''),
tail = result[3] || '';
// Split the tail into dir, basename and extension
var result2 = splitTailRe.exec(tail),
dir = result2[1],
basename = result2[2],
ext = result2[3];
return [device, dir, basename, ext];
};
var win32 = {};
var normalizeUNCRoot = function(device) {
return '\\\\' + device.replace(/^[\\\/]+/, '').replace(/[\\\/]+/g, '\\');
};
// Function to split a filename into [root, dir, basename, ext]
win32.splitPath = function(filename) {
// Separate device+slash from tail
var result = splitDeviceRe.exec(filename),
device = (result[1] || '') + (result[2] || ''),
tail = result[3] || '';
// Split the tail into dir, basename and extension
var result2 = splitTailRe.exec(tail),
dir = result2[1],
basename = result2[2],
ext = result2[3];
return [device, dir, basename, ext];
};
// path.resolve([from ...], to)
// windows version
exports.resolve = function() {
var resolvedDevice = '',
resolvedTail = '',
resolvedAbsolute = false;
var normalizeUNCRoot = function(device) {
return '\\\\' + device.replace(/^[\\\/]+/, '').replace(/[\\\/]+/g, '\\');
};
for (var i = arguments.length - 1; i >= -1; i--) {
var path;
if (i >= 0) {
path = arguments[i];
} else if (!resolvedDevice) {
path = process.cwd();
} else {
// Windows has the concept of drive-specific current working
// directories. If we've resolved a drive letter but not yet an
// absolute path, get cwd for that drive. We're sure the device is not
// an unc path at this points, because unc paths are always absolute.
path = process.env['=' + resolvedDevice];
// Verify that a drive-local cwd was found and that it actually points
// to our drive. If not, default to the drive's root.
if (!path || path.substr(0, 3).toLowerCase() !==
resolvedDevice.toLowerCase() + '\\') {
path = resolvedDevice + '\\';
}
}
// path.resolve([from ...], to)
win32.resolve = function() {
var resolvedDevice = '',
resolvedTail = '',
resolvedAbsolute = false;
// Skip empty and invalid entries
if (!util.isString(path)) {
throw new TypeError('Arguments to path.resolve must be strings');
} else if (!path) {
continue;
}
var result = splitDeviceRe.exec(path),
device = result[1] || '',
isUnc = device && device.charAt(1) !== ':',
isAbsolute = exports.isAbsolute(path),
tail = result[3];
if (device &&
resolvedDevice &&
device.toLowerCase() !== resolvedDevice.toLowerCase()) {
// This path points to another device so it is not applicable
continue;
}
if (!resolvedDevice) {
resolvedDevice = device;
}
if (!resolvedAbsolute) {
resolvedTail = tail + '\\' + resolvedTail;
resolvedAbsolute = isAbsolute;
}
if (resolvedDevice && resolvedAbsolute) {
break;
for (var i = arguments.length - 1; i >= -1; i--) {
var path;
if (i >= 0) {
path = arguments[i];
} else if (!resolvedDevice) {
path = process.cwd();
} else {
// Windows has the concept of drive-specific current working
// directories. If we've resolved a drive letter but not yet an
// absolute path, get cwd for that drive. We're sure the device is not
// an unc path at this points, because unc paths are always absolute.
path = process.env['=' + resolvedDevice];
// Verify that a drive-local cwd was found and that it actually points
// to our drive. If not, default to the drive's root.
if (!path || path.substr(0, 3).toLowerCase() !==
resolvedDevice.toLowerCase() + '\\') {
path = resolvedDevice + '\\';
}
}
// 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);
// Skip empty and invalid entries
if (!util.isString(path)) {
throw new TypeError('Arguments to path.resolve must be strings');
} else if (!path) {
continue;
}
// 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()
// fails)
// Normalize the tail path
function f(p) {
return !!p;
}
resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/).filter(f),
!resolvedAbsolute).join('\\');
// If device is a drive letter, we'll normalize to lower case.
if (resolvedDevice && resolvedDevice.charAt(1) === ':') {
resolvedDevice = resolvedDevice[0].toLowerCase() +
resolvedDevice.substr(1);
}
return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) ||
'.';
};
// windows version
exports.normalize = function(path) {
var result = splitDeviceRe.exec(path),
device = result[1] || '',
isUnc = device && device.charAt(1) !== ':',
isAbsolute = exports.isAbsolute(path),
tail = result[3],
trailingSlash = /[\\\/]$/.test(tail);
isAbsolute = win32.isAbsolute(path),
tail = result[3];
// If device is a drive letter, we'll normalize to lower case.
if (device && device.charAt(1) === ':') {
device = device[0].toLowerCase() + device.substr(1);
if (device &&
resolvedDevice &&
device.toLowerCase() !== resolvedDevice.toLowerCase()) {
// This path points to another device so it is not applicable
continue;
}
// Normalize the tail path
tail = normalizeArray(tail.split(/[\\\/]+/).filter(function(p) {
return !!p;
}), !isAbsolute).join('\\');
if (!tail && !isAbsolute) {
tail = '.';
if (!resolvedDevice) {
resolvedDevice = device;
}
if (tail && trailingSlash) {
tail += '\\';
if (!resolvedAbsolute) {
resolvedTail = tail + '\\' + resolvedTail;
resolvedAbsolute = isAbsolute;
}
// Convert slashes to backslashes when `device` points to an UNC root.
// Also squash multiple slashes into a single one where appropriate.
if (isUnc) {
device = normalizeUNCRoot(device);
if (resolvedDevice && resolvedAbsolute) {
break;
}
}
// 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()
// fails)
// Normalize the tail path
function f(p) {
return !!p;
}
resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/).filter(f),
!resolvedAbsolute).join('\\');
// If device is a drive letter, we'll normalize to lower case.
if (resolvedDevice && resolvedDevice.charAt(1) === ':') {
resolvedDevice = resolvedDevice[0].toLowerCase() +
resolvedDevice.substr(1);
}
return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) ||
'.';
};
win32.normalize = function(path) {
var result = splitDeviceRe.exec(path),
device = result[1] || '',
isUnc = device && device.charAt(1) !== ':',
isAbsolute = win32.isAbsolute(path),
tail = result[3],
trailingSlash = /[\\\/]$/.test(tail);
// If device is a drive letter, we'll normalize to lower case.
if (device && device.charAt(1) === ':') {
device = device[0].toLowerCase() + device.substr(1);
}
// Normalize the tail path
tail = normalizeArray(tail.split(/[\\\/]+/).filter(function(p) {
return !!p;
}), !isAbsolute).join('\\');
if (!tail && !isAbsolute) {
tail = '.';
}
if (tail && trailingSlash) {
tail += '\\';
}
// Convert slashes to backslashes when `device` points to an UNC root.
// Also squash multiple slashes into a single one where appropriate.
if (isUnc) {
device = normalizeUNCRoot(device);
}
return device + (isAbsolute ? '\\' : '') + tail;
};
win32.isAbsolute = function(path) {
var result = splitDeviceRe.exec(path),
device = result[1] || '',
isUnc = !!device && device.charAt(1) !== ':';
// UNC paths are always absolute
return !!result[2] || isUnc;
};
win32.join = function() {
function f(p) {
if (!util.isString(p)) {
throw new TypeError('Arguments to path.join must be strings');
}
return p;
}
var paths = Array.prototype.filter.call(arguments, f);
var joined = paths.join('\\');
// 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 win32.normalize(joined);
};
// path.relative(from, to)
// it will solve the relative path from 'from' to 'to', for instance:
// from = 'C:\\orandea\\test\\aaa'
// to = 'C:\\orandea\\impl\\bbb'
// The output of the function should be: '..\\..\\impl\\bbb'
win32.relative = function(from, to) {
from = win32.resolve(from);
to = win32.resolve(to);
// windows is not case sensitive
var lowerFrom = from.toLowerCase();
var lowerTo = to.toLowerCase();
function trim(arr) {
var start = 0;
for (; start < arr.length; start++) {
if (arr[start] !== '') break;
}
return device + (isAbsolute ? '\\' : '') + tail;
};
// windows version
exports.isAbsolute = function(path) {
var result = splitDeviceRe.exec(path),
device = result[1] || '',
isUnc = !!device && device.charAt(1) !== ':';
// UNC paths are always absolute
return !!result[2] || isUnc;
};
// windows version
exports.join = function() {
function f(p) {
if (!util.isString(p)) {
throw new TypeError('Arguments to path.join must be strings');
}
return p;
var end = arr.length - 1;
for (; end >= 0; end--) {
if (arr[end] !== '') break;
}
var paths = Array.prototype.filter.call(arguments, f);
var joined = paths.join('\\');
if (start > end) return [];
return arr.slice(start, end + 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,}/, '\\');
var toParts = trim(to.split('\\'));
var lowerFromParts = trim(lowerFrom.split('\\'));
var lowerToParts = trim(lowerTo.split('\\'));
var length = Math.min(lowerFromParts.length, lowerToParts.length);
var samePartsLength = length;
for (var i = 0; i < length; i++) {
if (lowerFromParts[i] !== lowerToParts[i]) {
samePartsLength = i;
break;
}
}
return exports.normalize(joined);
};
if (samePartsLength == 0) {
return to;
}
// path.relative(from, to)
// it will solve the relative path from 'from' to 'to', for instance:
// from = 'C:\\orandea\\test\\aaa'
// to = 'C:\\orandea\\impl\\bbb'
// The output of the function should be: '..\\..\\impl\\bbb'
// windows version
exports.relative = function(from, to) {
from = exports.resolve(from);
to = exports.resolve(to);
var outputParts = [];
for (var i = samePartsLength; i < lowerFromParts.length; i++) {
outputParts.push('..');
}
// windows is not case sensitive
var lowerFrom = from.toLowerCase();
var lowerTo = to.toLowerCase();
outputParts = outputParts.concat(toParts.slice(samePartsLength));
function trim(arr) {
var start = 0;
for (; start < arr.length; start++) {
if (arr[start] !== '') break;
}
var end = arr.length - 1;
for (; end >= 0; end--) {
if (arr[end] !== '') break;
}
if (start > end) return [];
return arr.slice(start, end + 1);
}
var toParts = trim(to.split('\\'));
var lowerFromParts = trim(lowerFrom.split('\\'));
var lowerToParts = trim(lowerTo.split('\\'));
var length = Math.min(lowerFromParts.length, lowerToParts.length);
var samePartsLength = length;
for (var i = 0; i < length; i++) {
if (lowerFromParts[i] !== lowerToParts[i]) {
samePartsLength = i;
break;
}
}
if (samePartsLength == 0) {
return to;
}
var outputParts = [];
for (var i = samePartsLength; i < lowerFromParts.length; i++) {
outputParts.push('..');
}
outputParts = outputParts.concat(toParts.slice(samePartsLength));
return outputParts.join('\\');
};
exports.sep = '\\';
exports.delimiter = ';';
} else /* posix */ {
// Split a filename into [root, dir, basename, ext], unix version
// 'root' is just a slash, or nothing.
var splitPathRe =
/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
var splitPath = function(filename) {
return splitPathRe.exec(filename).slice(1);
};
// path.resolve([from ...], to)
// posix version
exports.resolve = function() {
var resolvedPath = '',
resolvedAbsolute = false;
for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
var path = (i >= 0) ? arguments[i] : process.cwd();
// Skip empty and invalid entries
if (!util.isString(path)) {
throw new TypeError('Arguments to path.resolve must be strings');
} else if (!path) {
continue;
}
resolvedPath = path + '/' + resolvedPath;
resolvedAbsolute = path.charAt(0) === '/';
}
// 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() fails)
// Normalize the path
resolvedPath = normalizeArray(resolvedPath.split('/').filter(function(p) {
return !!p;
}), !resolvedAbsolute).join('/');
return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
};
// path.normalize(path)
// posix version
exports.normalize = function(path) {
var isAbsolute = exports.isAbsolute(path),
trailingSlash = path[path.length - 1] === '/',
segments = path.split('/'),
nonEmptySegments = [];
// Normalize the path
for (var i = 0; i < segments.length; i++) {
if (segments[i]) {
nonEmptySegments.push(segments[i]);
}
}
path = normalizeArray(nonEmptySegments, !isAbsolute).join('/');
if (!path && !isAbsolute) {
path = '.';
}
if (path && trailingSlash) {
path += '/';
}
return (isAbsolute ? '/' : '') + path;
};
// posix version
exports.isAbsolute = function(path) {
return path.charAt(0) === '/';
};
// posix version
exports.join = function() {
var path = '';
for (var i = 0; i < arguments.length; i++) {
var segment = arguments[i];
if (!util.isString(segment)) {
throw new TypeError('Arguments to path.join must be strings');
}
if (segment) {
if (!path) {
path += segment;
} else {
path += '/' + segment;
}
}
}
return exports.normalize(path);
};
return outputParts.join('\\');
};
// path.relative(from, to)
// posix version
exports.relative = function(from, to) {
from = exports.resolve(from).substr(1);
to = exports.resolve(to).substr(1);
win32._makeLong = function(path) {
// Note: this will *probably* throw somewhere.
if (!util.isString(path))
return path;
function trim(arr) {
var start = 0;
for (; start < arr.length; start++) {
if (arr[start] !== '') break;
}
if (!path) {
return '';
}
var end = arr.length - 1;
for (; end >= 0; end--) {
if (arr[end] !== '') break;
}
var resolvedPath = win32.resolve(path);
if (start > end) return [];
return arr.slice(start, end + 1);
}
if (/^[a-zA-Z]\:\\/.test(resolvedPath)) {
// path is local filesystem path, which needs to be converted
// to long UNC path.
return '\\\\?\\' + resolvedPath;
} else if (/^\\\\[^?.]/.test(resolvedPath)) {
// path is network UNC path, which needs to be converted
// to long UNC path.
return '\\\\?\\UNC\\' + resolvedPath.substring(2);
}
var fromParts = trim(from.split('/'));
var toParts = trim(to.split('/'));
return path;
};
var length = Math.min(fromParts.length, toParts.length);
var samePartsLength = length;
for (var i = 0; i < length; i++) {
if (fromParts[i] !== toParts[i]) {
samePartsLength = i;
break;
}
}
var outputParts = [];
for (var i = samePartsLength; i < fromParts.length; i++) {
outputParts.push('..');
}
outputParts = outputParts.concat(toParts.slice(samePartsLength));
return outputParts.join('/');
};
exports.sep = '/';
exports.delimiter = ':';
}
exports.dirname = function(path) {
var result = splitPath(path),
win32.dirname = function(path) {
var result = win32.splitPath(path),
root = result[0],
dir = result[1];
@ -467,8 +349,8 @@ exports.dirname = function(path) {
};
exports.basename = function(path, ext) {
var f = splitPath(path)[2];
win32.basename = function(path, ext) {
var f = win32.splitPath(path)[2];
// TODO: make this comparison case-insensitive on windows?
if (ext && f.substr(-1 * ext.length) === ext) {
f = f.substr(0, f.length - ext.length);
@ -477,47 +359,200 @@ exports.basename = function(path, ext) {
};
exports.extname = function(path) {
return splitPath(path)[3];
win32.extname = function(path) {
return win32.splitPath(path)[3];
};
exports.exists = util.deprecate(function(path, callback) {
require('fs').exists(path, callback);
}, 'path.exists is now called `fs.exists`.');
win32.sep = '\\';
win32.delimiter = ';';
exports.existsSync = util.deprecate(function(path) {
return require('fs').existsSync(path);
}, 'path.existsSync is now called `fs.existsSync`.');
// Split a filename into [root, dir, basename, ext], unix version
// 'root' is just a slash, or nothing.
var splitPathRe =
/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
var posix = {};
if (isWindows) {
exports._makeLong = function(path) {
// Note: this will *probably* throw somewhere.
if (!util.isString(path))
return path;
posix.splitPath = function(filename) {
return splitPathRe.exec(filename).slice(1);
};
if (!path) {
return '';
// path.resolve([from ...], to)
// posix version
posix.resolve = function() {
var resolvedPath = '',
resolvedAbsolute = false;
for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
var path = (i >= 0) ? arguments[i] : process.cwd();
// Skip empty and invalid entries
if (!util.isString(path)) {
throw new TypeError('Arguments to path.resolve must be strings');
} else if (!path) {
continue;
}
var resolvedPath = exports.resolve(path);
resolvedPath = path + '/' + resolvedPath;
resolvedAbsolute = path.charAt(0) === '/';
}
if (/^[a-zA-Z]\:\\/.test(resolvedPath)) {
// path is local filesystem path, which needs to be converted
// to long UNC path.
return '\\\\?\\' + resolvedPath;
} else if (/^\\\\[^?.]/.test(resolvedPath)) {
// path is network UNC path, which needs to be converted
// to long UNC path.
return '\\\\?\\UNC\\' + resolvedPath.substring(2);
// 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() fails)
// Normalize the path
resolvedPath = normalizeArray(resolvedPath.split('/').filter(function(p) {
return !!p;
}), !resolvedAbsolute).join('/');
return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
};
// path.normalize(path)
// posix version
posix.normalize = function(path) {
var isAbsolute = posix.isAbsolute(path),
trailingSlash = path.substr(-1) === '/',
segments = path.split('/'),
nonEmptySegments = [];
// Normalize the path
for (var i = 0; i < segments.length; i++) {
if (segments[i]) {
nonEmptySegments.push(segments[i]);
}
}
path = normalizeArray(nonEmptySegments, !isAbsolute).join('/');
if (!path && !isAbsolute) {
path = '.';
}
if (path && trailingSlash) {
path += '/';
}
return (isAbsolute ? '/' : '') + path;
};
// posix version
posix.isAbsolute = function(path) {
return path.charAt(0) === '/';
};
// posix version
posix.join = function() {
var path = '';
for (var i = 0; i < arguments.length; i++) {
var segment = arguments[i];
if (!util.isString(segment)) {
throw new TypeError('Arguments to path.join must be strings');
}
if (segment) {
if (!path) {
path += segment;
} else {
path += '/' + segment;
}
}
}
return posix.normalize(path);
};
// path.relative(from, to)
// posix version
posix.relative = function(from, to) {
from = posix.resolve(from).substr(1);
to = posix.resolve(to).substr(1);
function trim(arr) {
var start = 0;
for (; start < arr.length; start++) {
if (arr[start] !== '') break;
}
return path;
};
} else {
exports._makeLong = function(path) {
return path;
};
}
var end = arr.length - 1;
for (; end >= 0; end--) {
if (arr[end] !== '') break;
}
if (start > end) return [];
return arr.slice(start, end + 1);
}
var fromParts = trim(from.split('/'));
var toParts = trim(to.split('/'));
var length = Math.min(fromParts.length, toParts.length);
var samePartsLength = length;
for (var i = 0; i < length; i++) {
if (fromParts[i] !== toParts[i]) {
samePartsLength = i;
break;
}
}
var outputParts = [];
for (var i = samePartsLength; i < fromParts.length; i++) {
outputParts.push('..');
}
outputParts = outputParts.concat(toParts.slice(samePartsLength));
return outputParts.join('/');
};
posix._makeLong = function(path) {
return path;
};
posix.dirname = function(path) {
var result = posix.splitPath(path),
root = result[0],
dir = result[1];
if (!root && !dir) {
// No dirname whatsoever
return '.';
}
if (dir) {
// It has a dirname, strip trailing slash
dir = dir.substr(0, dir.length - 1);
}
return root + dir;
};
posix.basename = function(path, ext) {
var f = posix.splitPath(path)[2];
// TODO: make this comparison case-insensitive on windows?
if (ext && f.substr(-1 * ext.length) === ext) {
f = f.substr(0, f.length - ext.length);
}
return f;
};
posix.extname = function(path) {
return posix.splitPath(path)[3];
};
posix.sep = '/';
posix.delimiter = ':';
if (isWindows)
module.exports = win32;
else /* posix */
module.exports = posix;
module.exports.posix = posix;
module.exports.win32 = win32;

View File

@ -37,22 +37,19 @@ assert.equal(path.basename('basename.ext'), 'basename.ext');
assert.equal(path.basename('basename.ext/'), 'basename.ext');
assert.equal(path.basename('basename.ext//'), 'basename.ext');
if (isWindows) {
// On Windows a backslash acts as a path separator.
assert.equal(path.basename('\\dir\\basename.ext'), 'basename.ext');
assert.equal(path.basename('\\basename.ext'), 'basename.ext');
assert.equal(path.basename('basename.ext'), 'basename.ext');
assert.equal(path.basename('basename.ext\\'), 'basename.ext');
assert.equal(path.basename('basename.ext\\\\'), 'basename.ext');
// On Windows a backslash acts as a path separator.
assert.equal(path.win32.basename('\\dir\\basename.ext'), 'basename.ext');
assert.equal(path.win32.basename('\\basename.ext'), 'basename.ext');
assert.equal(path.win32.basename('basename.ext'), 'basename.ext');
assert.equal(path.win32.basename('basename.ext\\'), 'basename.ext');
assert.equal(path.win32.basename('basename.ext\\\\'), 'basename.ext');
} else {
// On unix a backslash is just treated as any other character.
assert.equal(path.basename('\\dir\\basename.ext'), '\\dir\\basename.ext');
assert.equal(path.basename('\\basename.ext'), '\\basename.ext');
assert.equal(path.basename('basename.ext'), 'basename.ext');
assert.equal(path.basename('basename.ext\\'), 'basename.ext\\');
assert.equal(path.basename('basename.ext\\\\'), 'basename.ext\\\\');
}
// On unix a backslash is just treated as any other character.
assert.equal(path.posix.basename('\\dir\\basename.ext'), '\\dir\\basename.ext');
assert.equal(path.posix.basename('\\basename.ext'), '\\basename.ext');
assert.equal(path.posix.basename('basename.ext'), 'basename.ext');
assert.equal(path.posix.basename('basename.ext\\'), 'basename.ext\\');
assert.equal(path.posix.basename('basename.ext\\\\'), 'basename.ext\\\\');
// POSIX filenames may include control characters
// c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html
@ -73,35 +70,33 @@ assert.equal(path.dirname(''), '.');
assert.equal(path.dirname('/'), '/');
assert.equal(path.dirname('////'), '/');
if (isWindows) {
assert.equal(path.dirname('c:\\'), 'c:\\');
assert.equal(path.dirname('c:\\foo'), 'c:\\');
assert.equal(path.dirname('c:\\foo\\'), 'c:\\');
assert.equal(path.dirname('c:\\foo\\bar'), 'c:\\foo');
assert.equal(path.dirname('c:\\foo\\bar\\'), 'c:\\foo');
assert.equal(path.dirname('c:\\foo\\bar\\baz'), 'c:\\foo\\bar');
assert.equal(path.dirname('\\'), '\\');
assert.equal(path.dirname('\\foo'), '\\');
assert.equal(path.dirname('\\foo\\'), '\\');
assert.equal(path.dirname('\\foo\\bar'), '\\foo');
assert.equal(path.dirname('\\foo\\bar\\'), '\\foo');
assert.equal(path.dirname('\\foo\\bar\\baz'), '\\foo\\bar');
assert.equal(path.dirname('c:'), 'c:');
assert.equal(path.dirname('c:foo'), 'c:');
assert.equal(path.dirname('c:foo\\'), 'c:');
assert.equal(path.dirname('c:foo\\bar'), 'c:foo');
assert.equal(path.dirname('c:foo\\bar\\'), 'c:foo');
assert.equal(path.dirname('c:foo\\bar\\baz'), 'c:foo\\bar');
assert.equal(path.dirname('\\\\unc\\share'), '\\\\unc\\share');
assert.equal(path.dirname('\\\\unc\\share\\foo'), '\\\\unc\\share\\');
assert.equal(path.dirname('\\\\unc\\share\\foo\\'), '\\\\unc\\share\\');
assert.equal(path.dirname('\\\\unc\\share\\foo\\bar'),
'\\\\unc\\share\\foo');
assert.equal(path.dirname('\\\\unc\\share\\foo\\bar\\'),
'\\\\unc\\share\\foo');
assert.equal(path.dirname('\\\\unc\\share\\foo\\bar\\baz'),
'\\\\unc\\share\\foo\\bar');
}
assert.equal(path.win32.dirname('c:\\'), 'c:\\');
assert.equal(path.win32.dirname('c:\\foo'), 'c:\\');
assert.equal(path.win32.dirname('c:\\foo\\'), 'c:\\');
assert.equal(path.win32.dirname('c:\\foo\\bar'), 'c:\\foo');
assert.equal(path.win32.dirname('c:\\foo\\bar\\'), 'c:\\foo');
assert.equal(path.win32.dirname('c:\\foo\\bar\\baz'), 'c:\\foo\\bar');
assert.equal(path.win32.dirname('\\'), '\\');
assert.equal(path.win32.dirname('\\foo'), '\\');
assert.equal(path.win32.dirname('\\foo\\'), '\\');
assert.equal(path.win32.dirname('\\foo\\bar'), '\\foo');
assert.equal(path.win32.dirname('\\foo\\bar\\'), '\\foo');
assert.equal(path.win32.dirname('\\foo\\bar\\baz'), '\\foo\\bar');
assert.equal(path.win32.dirname('c:'), 'c:');
assert.equal(path.win32.dirname('c:foo'), 'c:');
assert.equal(path.win32.dirname('c:foo\\'), 'c:');
assert.equal(path.win32.dirname('c:foo\\bar'), 'c:foo');
assert.equal(path.win32.dirname('c:foo\\bar\\'), 'c:foo');
assert.equal(path.win32.dirname('c:foo\\bar\\baz'), 'c:foo\\bar');
assert.equal(path.win32.dirname('\\\\unc\\share'), '\\\\unc\\share');
assert.equal(path.win32.dirname('\\\\unc\\share\\foo'), '\\\\unc\\share\\');
assert.equal(path.win32.dirname('\\\\unc\\share\\foo\\'), '\\\\unc\\share\\');
assert.equal(path.win32.dirname('\\\\unc\\share\\foo\\bar'),
'\\\\unc\\share\\foo');
assert.equal(path.win32.dirname('\\\\unc\\share\\foo\\bar\\'),
'\\\\unc\\share\\foo');
assert.equal(path.win32.dirname('\\\\unc\\share\\foo\\bar\\baz'),
'\\\\unc\\share\\foo\\bar');
assert.equal(path.extname(''), '');
@ -146,28 +141,25 @@ assert.equal(path.extname('file//'), '');
assert.equal(path.extname('file./'), '.');
assert.equal(path.extname('file.//'), '.');
if (isWindows) {
// On windows, backspace is a path separator.
assert.equal(path.extname('.\\'), '');
assert.equal(path.extname('..\\'), '');
assert.equal(path.extname('file.ext\\'), '.ext');
assert.equal(path.extname('file.ext\\\\'), '.ext');
assert.equal(path.extname('file\\'), '');
assert.equal(path.extname('file\\\\'), '');
assert.equal(path.extname('file.\\'), '.');
assert.equal(path.extname('file.\\\\'), '.');
// On windows, backspace is a path separator.
assert.equal(path.win32.extname('.\\'), '');
assert.equal(path.win32.extname('..\\'), '');
assert.equal(path.win32.extname('file.ext\\'), '.ext');
assert.equal(path.win32.extname('file.ext\\\\'), '.ext');
assert.equal(path.win32.extname('file\\'), '');
assert.equal(path.win32.extname('file\\\\'), '');
assert.equal(path.win32.extname('file.\\'), '.');
assert.equal(path.win32.extname('file.\\\\'), '.');
} else {
// On unix, backspace is a valid name component like any other character.
assert.equal(path.extname('.\\'), '');
assert.equal(path.extname('..\\'), '.\\');
assert.equal(path.extname('file.ext\\'), '.ext\\');
assert.equal(path.extname('file.ext\\\\'), '.ext\\\\');
assert.equal(path.extname('file\\'), '');
assert.equal(path.extname('file\\\\'), '');
assert.equal(path.extname('file.\\'), '.\\');
assert.equal(path.extname('file.\\\\'), '.\\\\');
}
// On unix, backspace is a valid name component like any other character.
assert.equal(path.posix.extname('.\\'), '');
assert.equal(path.posix.extname('..\\'), '.\\');
assert.equal(path.posix.extname('file.ext\\'), '.ext\\');
assert.equal(path.posix.extname('file.ext\\\\'), '.ext\\\\');
assert.equal(path.posix.extname('file\\'), '');
assert.equal(path.posix.extname('file\\\\'), '');
assert.equal(path.posix.extname('file.\\'), '.\\');
assert.equal(path.posix.extname('file.\\\\'), '.\\\\');
// path.join tests
var failures = [];
@ -294,23 +286,21 @@ joinThrowTests.forEach(function(test) {
// path normalize tests
if (isWindows) {
assert.equal(path.normalize('./fixtures///b/../b/c.js'),
'fixtures\\b\\c.js');
assert.equal(path.normalize('/foo/../../../bar'), '\\bar');
assert.equal(path.normalize('a//b//../b'), 'a\\b');
assert.equal(path.normalize('a//b//./c'), 'a\\b\\c');
assert.equal(path.normalize('a//b//.'), 'a\\b');
assert.equal(path.normalize('//server/share/dir/file.ext'),
'\\\\server\\share\\dir\\file.ext');
} else {
assert.equal(path.normalize('./fixtures///b/../b/c.js'),
'fixtures/b/c.js');
assert.equal(path.normalize('/foo/../../../bar'), '/bar');
assert.equal(path.normalize('a//b//../b'), 'a/b');
assert.equal(path.normalize('a//b//./c'), 'a/b/c');
assert.equal(path.normalize('a//b//.'), 'a/b');
}
assert.equal(path.win32.normalize('./fixtures///b/../b/c.js'),
'fixtures\\b\\c.js');
assert.equal(path.win32.normalize('/foo/../../../bar'), '\\bar');
assert.equal(path.win32.normalize('a//b//../b'), 'a\\b');
assert.equal(path.win32.normalize('a//b//./c'), 'a\\b\\c');
assert.equal(path.win32.normalize('a//b//.'), 'a\\b');
assert.equal(path.win32.normalize('//server/share/dir/file.ext'),
'\\\\server\\share\\dir\\file.ext');
assert.equal(path.posix.normalize('./fixtures///b/../b/c.js'),
'fixtures/b/c.js');
assert.equal(path.posix.normalize('/foo/../../../bar'), '/bar');
assert.equal(path.posix.normalize('a//b//../b'), 'a/b');
assert.equal(path.posix.normalize('a//b//./c'), 'a/b/c');
assert.equal(path.posix.normalize('a//b//.'), 'a/b');
// path.resolve tests
if (isWindows) {
@ -352,21 +342,19 @@ resolveTests.forEach(function(test) {
assert.equal(failures.length, 0, failures.join(''));
// path.isAbsolute tests
if (isWindows) {
assert.equal(path.isAbsolute('//server/file'), true);
assert.equal(path.isAbsolute('\\\\server\\file'), true);
assert.equal(path.isAbsolute('C:/Users/'), true);
assert.equal(path.isAbsolute('C:\\Users\\'), true);
assert.equal(path.isAbsolute('C:cwd/another'), false);
assert.equal(path.isAbsolute('C:cwd\\another'), false);
assert.equal(path.isAbsolute('directory/directory'), false);
assert.equal(path.isAbsolute('directory\\directory'), false);
} else {
assert.equal(path.isAbsolute('/home/foo'), true);
assert.equal(path.isAbsolute('/home/foo/..'), true);
assert.equal(path.isAbsolute('bar/'), false);
assert.equal(path.isAbsolute('./baz'), false);
}
assert.equal(path.win32.isAbsolute('//server/file'), true);
assert.equal(path.win32.isAbsolute('\\\\server\\file'), true);
assert.equal(path.win32.isAbsolute('C:/Users/'), true);
assert.equal(path.win32.isAbsolute('C:\\Users\\'), true);
assert.equal(path.win32.isAbsolute('C:cwd/another'), false);
assert.equal(path.win32.isAbsolute('C:cwd\\another'), false);
assert.equal(path.win32.isAbsolute('directory/directory'), false);
assert.equal(path.win32.isAbsolute('directory\\directory'), false);
assert.equal(path.posix.isAbsolute('/home/foo'), true);
assert.equal(path.posix.isAbsolute('/home/foo/..'), true);
assert.equal(path.posix.isAbsolute('bar/'), false);
assert.equal(path.posix.isAbsolute('./baz'), false);
// path.relative tests
if (isWindows) {
@ -405,20 +393,20 @@ relativeTests.forEach(function(test) {
});
assert.equal(failures.length, 0, failures.join(''));
// path.sep tests
if (isWindows) {
// windows
assert.equal(path.sep, '\\');
} else {
// posix
assert.equal(path.sep, '/');
}
// windows
assert.equal(path.win32.sep, '\\');
// posix
assert.equal(path.posix.sep, '/');
// path.delimiter tests
if (isWindows) {
// windows
assert.equal(path.delimiter, ';');
} else {
// posix
assert.equal(path.delimiter, ':');
}
// windows
assert.equal(path.win32.delimiter, ';');
// posix
assert.equal(path.posix.delimiter, ':');
if (isWindows)
assert.deepEqual(path, path.win32, 'should be win32 path module');
else
assert.deepEqual(path, path.posix, 'should be posix path module');