|
@ -1,127 +1,247 @@ |
|
|
function validPathPart(p) { |
|
|
|
|
|
return typeof p === 'string' && p; |
|
|
var isWindows = process.platform === 'win32'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// resolves . and .. elements in a path array with directory names
|
|
|
|
|
|
// there must be no slashes, empty elements, or device names (c:\) in the array
|
|
|
|
|
|
// (so also no leading and trailing slashes - it does not distinguish relative and absolute paths)
|
|
|
|
|
|
function normalizeArray(parts, allowAboveRoot) { |
|
|
|
|
|
// if the path tries to go above the root, `up` ends up > 0
|
|
|
|
|
|
var up = 0; |
|
|
|
|
|
for (var i = parts.length; i >= 0; i--) { |
|
|
|
|
|
var last = parts[i]; |
|
|
|
|
|
if (last == '.') { |
|
|
|
|
|
parts.splice(i, 1); |
|
|
|
|
|
} else if (last === '..') { |
|
|
|
|
|
parts.splice(i, 1); |
|
|
|
|
|
up++; |
|
|
|
|
|
} else if (up) { |
|
|
|
|
|
parts.splice(i, 1); |
|
|
|
|
|
up--; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// if the path is allowed to go above the root, restore leading ..s
|
|
|
|
|
|
if (allowAboveRoot) { |
|
|
|
|
|
for ( ; up--; up) { |
|
|
|
|
|
parts.unshift('..'); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return parts; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exports.join = function() { |
|
|
if (isWindows) { |
|
|
var args = Array.prototype.slice.call(arguments); |
|
|
|
|
|
return exports.normalizeArray(args).join('/'); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Regex to split a filename into [*, dir, basename, ext]
|
|
|
|
|
|
// windows version
|
|
|
|
|
|
var splitPathRe = /^(.+(?:[\\\/](?!$)|:)|[\\\/])?((?:.+?)?(\.[^.]*)?)$/; |
|
|
|
|
|
|
|
|
exports.split = function(path) { |
|
|
// Regex to split a windows path into three parts: [*, device, slash, tail]
|
|
|
// split based on / and \, but only if that / is not at the start or end.
|
|
|
// windows-only
|
|
|
return exports.normalizeArray(path.split(/^|[\\\/](?!$)/)); |
|
|
var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?(.*?)$/; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// path.resolve([from ...], to)
|
|
|
|
|
|
// windows version
|
|
|
|
|
|
exports.resolve = function() { |
|
|
|
|
|
// Prepend cwd to provided paths
|
|
|
|
|
|
var paths = [process.cwd()].concat(Array.prototype.slice.call(arguments, 0)); |
|
|
|
|
|
|
|
|
function cleanArray(parts) { |
|
|
var resolvedDevice = "", |
|
|
var i = 0; |
|
|
resolvedTail = "", |
|
|
var l = parts.length - 1; |
|
|
resolvedAbsolute = false; |
|
|
var stripped = false; |
|
|
|
|
|
|
|
|
|
|
|
// strip leading empty args
|
|
|
for (var i = paths.length; i >= 0; i--) { |
|
|
while (i < l && !validPathPart(parts[i])) { |
|
|
var path = paths[i]; |
|
|
stripped = true; |
|
|
|
|
|
i++; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// strip tailing empty args
|
|
|
// Skip empty and invalid entries
|
|
|
while (l >= i && !validPathPart(parts[l])) { |
|
|
if (typeof path !== 'string' || !path) { |
|
|
stripped = true; |
|
|
continue; |
|
|
l--; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (stripped) { |
|
|
var result = splitDeviceRe.exec(path), |
|
|
// if l chopped all the way back to i, then this is empty
|
|
|
device = result[1] || '', |
|
|
parts = Array.prototype.slice.call(parts, i, l + 1); |
|
|
isUnc = device && device.charAt(1) !== ':', |
|
|
|
|
|
isAbsolute = !!result[2] || isUnc, // UNC paths are always absolute
|
|
|
|
|
|
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; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!resolvedAbsolute && resolvedDevice) { |
|
|
|
|
|
// If we still don't have an absolute path,
|
|
|
|
|
|
// prepend the current path for the device found.
|
|
|
|
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
|
|
// Windows stores the current directories for 'other' drives
|
|
|
|
|
|
// as hidden environment variables like =C:=c:\windows (literally)
|
|
|
|
|
|
// var deviceCwd = os.getCwdForDrive(resolvedDevice);
|
|
|
|
|
|
var deviceCwd = ""; |
|
|
|
|
|
|
|
|
|
|
|
// If there is no cwd set for the drive, it is at root
|
|
|
|
|
|
resolvedTail = deviceCwd + '\\' + resolvedTail; |
|
|
|
|
|
resolvedAbsolute = true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Replace slashes (in UNC share name) by backslashes
|
|
|
|
|
|
resolvedDevice = resolvedDevice.replace(/\//g, '\\'); |
|
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/).filter(function(p) { |
|
|
|
|
|
return !!p; |
|
|
|
|
|
}), !resolvedAbsolute).join('\\'); |
|
|
|
|
|
|
|
|
|
|
|
return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) || '.'; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return parts.filter(function(p) { return validPathPart(p) }) |
|
|
// windows version
|
|
|
.join('/') |
|
|
exports.normalize = function(path) { |
|
|
.split(/^|[\\\/](?!$)/); |
|
|
var result = splitDeviceRe.exec(path), |
|
|
} |
|
|
device = result[1] || '', |
|
|
|
|
|
isUnc = device && device.charAt(1) !== ':', |
|
|
|
|
|
isAbsolute = !!result[2] || isUnc, // UNC paths are always absolute
|
|
|
|
|
|
tail = result[3], |
|
|
|
|
|
trailingSlash = /[\\\/]$/.test(tail); |
|
|
|
|
|
|
|
|
|
|
|
// Normalize the tail path
|
|
|
|
|
|
tail = normalizeArray(tail.split(/[\\\/]+/).filter(function(p) { |
|
|
|
|
|
return !!p; |
|
|
|
|
|
}), !isAbsolute).join('\\'); |
|
|
|
|
|
|
|
|
exports.normalizeArray = function(original) { |
|
|
if (!tail && !isAbsolute) { |
|
|
var parts = cleanArray(original); |
|
|
tail = '.' |
|
|
if (!parts.length || (parts.length === 1 && !parts[0])) return ['.']; |
|
|
|
|
|
|
|
|
|
|
|
// now we're fully ready to rock.
|
|
|
|
|
|
// leading/trailing invalids have been stripped off.
|
|
|
|
|
|
// if it comes in starting with a slash, or ending with a slash,
|
|
|
|
|
|
var leadingSlash = (parts[0].charAt(0) === '/'); |
|
|
|
|
|
|
|
|
|
|
|
if (leadingSlash) parts[0] = parts[0].substr(1); |
|
|
|
|
|
var last = parts.slice(-1)[0]; |
|
|
|
|
|
var tailingSlash = (last.substr(-1) === '/'); |
|
|
|
|
|
if (tailingSlash) parts[parts.length - 1] = last.slice(0, -1); |
|
|
|
|
|
var directories = []; |
|
|
|
|
|
var prev; |
|
|
|
|
|
for (var i = 0, l = parts.length - 1; i <= l; i++) { |
|
|
|
|
|
var directory = parts[i]; |
|
|
|
|
|
|
|
|
|
|
|
// if it's blank, and we're not keeping blanks, then skip it.
|
|
|
|
|
|
if (directory === '') continue; |
|
|
|
|
|
|
|
|
|
|
|
// if it's a dot, then skip it
|
|
|
|
|
|
if (directory === '.' && (directories.length || |
|
|
|
|
|
(i === 0 && !(tailingSlash && i === l)) || |
|
|
|
|
|
(i === 0 && leadingSlash))) continue; |
|
|
|
|
|
|
|
|
|
|
|
// if we're dealing with an absolute path, then discard ..s that go
|
|
|
|
|
|
// above that the base.
|
|
|
|
|
|
if (leadingSlash && directories.length === 0 && directory === '..') { |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
} |
|
|
// trying to go up a dir
|
|
|
if (tail && trailingSlash) { |
|
|
if (directory === '..' && directories.length && prev !== '..' && |
|
|
tail += '\\' |
|
|
prev !== undefined) { |
|
|
|
|
|
directories.pop(); |
|
|
|
|
|
prev = directories.slice(-1)[0]; |
|
|
|
|
|
} else { |
|
|
|
|
|
directories.push(directory); |
|
|
|
|
|
prev = directory; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return device + (isAbsolute ? '\\' : '') + tail; |
|
|
} |
|
|
} |
|
|
if (!directories.length) { |
|
|
|
|
|
directories = [leadingSlash || tailingSlash ? '' : '.']; |
|
|
// windows version
|
|
|
|
|
|
exports.join = function() { |
|
|
|
|
|
var paths = Array.prototype.slice.call(arguments, 0).filter(function(p) { |
|
|
|
|
|
return p && typeof p === 'string'; |
|
|
|
|
|
}), |
|
|
|
|
|
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.slice(1); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return exports.normalize(joined); |
|
|
} |
|
|
} |
|
|
var last = directories.slice(-1)[0]; |
|
|
|
|
|
if (tailingSlash && last.substr(-1) !== '/') { |
|
|
|
|
|
directories[directories.length - 1] += '/'; |
|
|
} else /* posix */ { |
|
|
|
|
|
|
|
|
|
|
|
// Regex to split a filename into [*, dir, basename, ext]
|
|
|
|
|
|
// posix version
|
|
|
|
|
|
var splitPathRe = /^(.+\/(?!$)|\/)?((?:.+?)?(\.[^.]*)?)$/; |
|
|
|
|
|
|
|
|
|
|
|
// path.resolve([from ...], to)
|
|
|
|
|
|
// posix version
|
|
|
|
|
|
exports.resolve = function() { |
|
|
|
|
|
// Prepend cwd to provided paths
|
|
|
|
|
|
var paths = [process.cwd()].concat(Array.prototype.slice.call(arguments, 0)); |
|
|
|
|
|
|
|
|
|
|
|
var resolvedPath = "", |
|
|
|
|
|
resolvedAbsolute = false; |
|
|
|
|
|
|
|
|
|
|
|
for (var i = paths.length; i >= 0 && !resolvedAbsolute; i--) { |
|
|
|
|
|
var path = paths[i]; |
|
|
|
|
|
// Skip empty and invalid entries
|
|
|
|
|
|
if (typeof path !== 'string' || !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) || '.'; |
|
|
} |
|
|
} |
|
|
if (leadingSlash && directories[0].charAt(0) !== '/') { |
|
|
|
|
|
if (directories[0] === '.') directories[0] = ''; |
|
|
// path.normalize(path)
|
|
|
directories[0] = '/' + directories[0]; |
|
|
// posix version
|
|
|
|
|
|
exports.normalize = function(path) { |
|
|
|
|
|
var isAbsolute = path.charAt(0) === '/', |
|
|
|
|
|
trailingSlash = path.slice(-1) === '/'; |
|
|
|
|
|
|
|
|
|
|
|
// Normalize the path
|
|
|
|
|
|
path = normalizeArray(path.split('/').filter(function(p) { |
|
|
|
|
|
return !!p; |
|
|
|
|
|
}), !isAbsolute).join('/'); |
|
|
|
|
|
|
|
|
|
|
|
if (!path && !isAbsolute) { |
|
|
|
|
|
path = '.' |
|
|
|
|
|
} |
|
|
|
|
|
if (path && trailingSlash) { |
|
|
|
|
|
path += '/'; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return (isAbsolute ? '/' : '') + path; |
|
|
} |
|
|
} |
|
|
return directories; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exports.normalize = function(path) { |
|
|
// posix version
|
|
|
return exports.join(path); |
|
|
exports.join = function() { |
|
|
}; |
|
|
var paths = Array.prototype.slice.call(arguments, 0); |
|
|
|
|
|
return exports.normalize(paths.filter(function(p, index) { |
|
|
|
|
|
return p && typeof p === 'string' |
|
|
|
|
|
}).join('/')); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exports.dirname = function(path) { |
|
|
exports.dirname = function(path) { |
|
|
if (path.length > 1 && '\\/'.indexOf(path[path.length-1]) != -1) { |
|
|
var dir = splitPathRe.exec(path)[1] || ''; |
|
|
path = path.replace(/\/+$/, ''); |
|
|
if (!dir) { |
|
|
} |
|
|
// No dirname
|
|
|
var lastSlash = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')); |
|
|
return '.' |
|
|
switch (lastSlash) { |
|
|
} else if (dir.length === 1 || |
|
|
case -1: |
|
|
(isWindows && dir.length <= 3 && dir.charAt(1) === ':')) { |
|
|
return '.'; |
|
|
// It is just a slash or a drive letter with a slash
|
|
|
case 0: |
|
|
return dir; |
|
|
return '/'; |
|
|
} else { |
|
|
default: |
|
|
// It is a full dirname, strip trailing slash
|
|
|
return path.substring(0, lastSlash); |
|
|
return dir.substring(0, dir.length - 1); |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exports.basename = function(path, ext) { |
|
|
exports.basename = function(path, ext) { |
|
|
var f = path.substr(path.lastIndexOf('/') + 1); |
|
|
var f = splitPathRe.exec(path)[2] || ''; |
|
|
|
|
|
// TODO: make this comparison case-insensitive on windows?
|
|
|
if (ext && f.substr(-1 * ext.length) === ext) { |
|
|
if (ext && f.substr(-1 * ext.length) === ext) { |
|
|
f = f.substr(0, f.length - ext.length); |
|
|
f = f.substr(0, f.length - ext.length); |
|
|
} |
|
|
} |
|
@ -130,12 +250,7 @@ exports.basename = function(path, ext) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exports.extname = function(path) { |
|
|
exports.extname = function(path) { |
|
|
var dot = path.lastIndexOf('.'), |
|
|
return splitPathRe.exec(path)[3] || ''; |
|
|
slash = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')); |
|
|
|
|
|
// The last dot must be in the last path component, and it (the last dot) must
|
|
|
|
|
|
// not start the last path component (i.e. be a dot that signifies a hidden
|
|
|
|
|
|
// file in UNIX).
|
|
|
|
|
|
return dot <= slash + 1 ? '' : path.substring(dot); |
|
|
|
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|