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() { // Prepend cwd to provided paths var paths = [process.cwd()].concat( Array.prototype.slice.call(arguments, 0)); var resolvedDevice = '', resolvedTail = '', resolvedAbsolute = false; for (var i = paths.length; i >= 0; i--) { var path = paths[i]; // 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); }; } 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) || '.'; }; // 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('/')); }; } 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; } };