|
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
|
|
//
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
// copy of this software and associated documentation files (the
|
|
|
|
// "Software"), to deal in the Software without restriction, including
|
|
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
|
|
// persons to whom the Software is furnished to do so, subject to the
|
|
|
|
// following conditions:
|
|
|
|
//
|
|
|
|
// The above copyright notice and this permission notice shall be included
|
|
|
|
// in all copies or substantial portions of the Software.
|
|
|
|
//
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isWindows) {
|
|
|
|
|
|
|
|
// Regex to split a filename into [*, dir, basename, ext]
|
|
|
|
// windows version
|
|
|
|
var splitPathRe = /^(.+(?:[\\\/](?!$)|:)|[\\\/])?((?:.+?)?(\.[^.]*)?)$/;
|
|
|
|
|
|
|
|
// Regex to split a windows path into three parts: [*, device, slash,
|
|
|
|
// tail] windows-only
|
|
|
|
var splitDeviceRe =
|
|
|
|
/^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?(.*?)$/;
|
|
|
|
|
|
|
|
// path.resolve([from ...], to)
|
|
|
|
// windows version
|
|
|
|
exports.resolve = function() {
|
|
|
|
var resolvedDevice = '',
|
|
|
|
resolvedTail = '',
|
|
|
|
resolvedAbsolute = false;
|
|
|
|
|
|
|
|
for (var i = arguments.length; i >= -1; i--) {
|
|
|
|
var path = (i >= 0) ? arguments[i] : process.cwd();
|
|
|
|
|
|
|
|
// Skip empty and invalid entries
|
|
|
|
if (typeof path !== 'string' || !path) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
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];
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
function f(p) {
|
|
|
|
return !!p;
|
|
|
|
}
|
|
|
|
|
|
|
|
resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/).filter(f),
|
|
|
|
!resolvedAbsolute).join('\\');
|
|
|
|
|
|
|
|
return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) ||
|
|
|
|
'.';
|
|
|
|
};
|
|
|
|
|
|
|
|
// windows version
|
|
|
|
exports.normalize = function(path) {
|
|
|
|
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('\\');
|
|
|
|
|
|
|
|
if (!tail && !isAbsolute) {
|
|
|
|
tail = '.';
|
|
|
|
}
|
|
|
|
if (tail && trailingSlash) {
|
|
|
|
tail += '\\';
|
|
|
|
}
|
|
|
|
|
|
|
|
return device + (isAbsolute ? '\\' : '') + tail;
|
|
|
|
};
|
|
|
|
|
|
|
|
// windows version
|
|
|
|
exports.join = function() {
|
|
|
|
function f(p) {
|
|
|
|
return p && typeof p === 'string';
|
|
|
|
}
|
|
|
|
|
|
|
|
var paths = Array.prototype.slice.call(arguments, 0).filter(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.slice(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return exports.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'
|
|
|
|
// windows version
|
|
|
|
exports.relative = function(from, to) {
|
|
|
|
from = exports.resolve(from);
|
|
|
|
to = exports.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;
|
|
|
|
}
|
|
|
|
|
|
|
|
var end = arr.length - 1;
|
|
|
|
for (; end >= 0; end--) {
|
|
|
|
if (arr[end] !== '') break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (start > end) return [];
|
|
|
|
return arr.slice(start, end - start + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
var fromParts = trim(from.split('\\'));
|
|
|
|
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('\\');
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
} else /* posix */ {
|
|
|
|
|
|
|
|
// Regex to split a filename into [*, dir, basename, ext]
|
|
|
|
// posix version
|
|
|
|
var splitPathRe = /^([\s\S]+\/(?!$)|\/)?((?:[\s\S]+?)?(\.[^.]*)?)$/;
|
|
|
|
|
|
|
|
// path.resolve([from ...], to)
|
|
|
|
// posix version
|
|
|
|
exports.resolve = function() {
|
|
|
|
var resolvedPath = '',
|
|
|
|
resolvedAbsolute = false;
|
|
|
|
|
|
|
|
for (var i = arguments.length; i >= -1 && !resolvedAbsolute; i--) {
|
|
|
|
var path = (i >= 0) ? arguments[i] : process.cwd();
|
|
|
|
|
|
|
|
// 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) || '.';
|
|
|
|
};
|
|
|
|
|
|
|
|
// path.normalize(path)
|
|
|
|
// 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;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// posix version
|
|
|
|
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('/'));
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// path.relative(from, to)
|
|
|
|
// posix version
|
|
|
|
exports.relative = function(from, to) {
|
|
|
|
from = exports.resolve(from).substr(1);
|
|
|
|
to = exports.resolve(to).substr(1);
|
|
|
|
|
|
|
|
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 - start + 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('/');
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
exports.dirname = function(path) {
|
|
|
|
var dir = splitPathRe.exec(path)[1] || '';
|
|
|
|
if (!dir) {
|
|
|
|
// No dirname
|
|
|
|
return '.';
|
|
|
|
} else if (dir.length === 1 ||
|
|
|
|
(isWindows && dir.length <= 3 && dir.charAt(1) === ':')) {
|
|
|
|
// It is just a slash or a drive letter with a slash
|
|
|
|
return dir;
|
|
|
|
} else {
|
|
|
|
// It is a full dirname, strip trailing slash
|
|
|
|
return dir.substring(0, dir.length - 1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
exports.basename = function(path, ext) {
|
|
|
|
var f = splitPathRe.exec(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;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
exports.extname = function(path) {
|
|
|
|
return splitPathRe.exec(path)[3] || '';
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
exports.exists = function(path, callback) {
|
|
|
|
process.binding('fs').stat(path, function(err, stats) {
|
|
|
|
if (callback) callback(err ? false : true);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
exports.existsSync = function(path) {
|
|
|
|
try {
|
|
|
|
process.binding('fs').stat(path);
|
|
|
|
return true;
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|