3 const util = require('util');
4 const isWindows = process.platform === 'win32';
6 function assertPath(path) {
7 if (typeof path !== 'string') {
8 throw new TypeError('Path must be a string. Received ' +
13 // resolves . and .. elements in a path array with directory names there
14 // must be no slashes or device names (c:\) in the array
15 // (so also no leading and trailing slashes - it does not distinguish
16 // relative and absolute paths)
17 function normalizeArray(parts, allowAboveRoot) {
19 for (var i = 0; i < parts.length; i++) {
27 if (res.length && res[res.length - 1] !== '..') {
29 } else if (allowAboveRoot) {
40 // Returns an array with empty elements removed from either end of the input
41 // array or the original array if no elements need to be removed
42 function trimArray(arr) {
43 var lastIndex = arr.length - 1;
45 for (; start <= lastIndex; start++) {
51 for (; end >= 0; end--) {
56 if (start === 0 && end === lastIndex)
60 return arr.slice(start, end + 1);
63 // Regex to split a windows path into three parts: [*, device, slash,
66 /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/;
68 // Regex to split the tail part of the above into [*, dir, basename, ext]
70 /^([\s\S]*?)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/;
74 // Function to split a filename into [root, dir, basename, ext]
75 function win32SplitPath(filename) {
76 // Separate device+slash from tail
77 var result = splitDeviceRe.exec(filename),
78 device = (result[1] || '') + (result[2] || ''),
80 // Split the tail into dir, basename and extension
81 var result2 = splitTailRe.exec(tail),
83 basename = result2[2],
85 return [device, dir, basename, ext];
88 function win32StatPath(path) {
89 var result = splitDeviceRe.exec(path),
90 device = result[1] || '',
91 isUnc = !!device && device[1] !== ':';
95 isAbsolute: isUnc || !!result[2], // UNC paths are always absolute
100 function normalizeUNCRoot(device) {
101 return '\\\\' + device.replace(/^[\\\/]+/, '').replace(/[\\\/]+/g, '\\');
104 // path.resolve([from ...], to)
105 win32.resolve = function() {
106 var resolvedDevice = '',
108 resolvedAbsolute = false;
110 for (var i = arguments.length - 1; i >= -1; i--) {
114 } else if (!resolvedDevice) {
115 path = process.cwd();
117 // Windows has the concept of drive-specific current working
118 // directories. If we've resolved a drive letter but not yet an
119 // absolute path, get cwd for that drive. We're sure the device is not
120 // an unc path at this points, because unc paths are always absolute.
121 path = process.env['=' + resolvedDevice];
122 // Verify that a drive-local cwd was found and that it actually points
123 // to our drive. If not, default to the drive's root.
124 if (!path || path.substr(0, 3).toLowerCase() !==
125 resolvedDevice.toLowerCase() + '\\') {
126 path = resolvedDevice + '\\';
132 // Skip empty entries
137 var result = win32StatPath(path),
138 device = result.device,
139 isUnc = result.isUnc,
140 isAbsolute = result.isAbsolute,
145 device.toLowerCase() !== resolvedDevice.toLowerCase()) {
146 // This path points to another device so it is not applicable
150 if (!resolvedDevice) {
151 resolvedDevice = device;
153 if (!resolvedAbsolute) {
154 resolvedTail = tail + '\\' + resolvedTail;
155 resolvedAbsolute = isAbsolute;
158 if (resolvedDevice && resolvedAbsolute) {
163 // Convert slashes to backslashes when `resolvedDevice` points to an UNC
164 // root. Also squash multiple slashes into a single one where appropriate.
166 resolvedDevice = normalizeUNCRoot(resolvedDevice);
169 // At this point the path should be resolved to a full absolute path,
170 // but handle relative paths to be safe (might happen when process.cwd()
173 // Normalize the tail path
174 resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/),
175 !resolvedAbsolute).join('\\');
177 return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) ||
182 win32.normalize = function(path) {
185 var result = win32StatPath(path),
186 device = result.device,
187 isUnc = result.isUnc,
188 isAbsolute = result.isAbsolute,
190 trailingSlash = /[\\\/]$/.test(tail);
192 // Normalize the tail path
193 tail = normalizeArray(tail.split(/[\\\/]+/), !isAbsolute).join('\\');
195 if (!tail && !isAbsolute) {
198 if (tail && trailingSlash) {
202 // Convert slashes to backslashes when `device` points to an UNC root.
203 // Also squash multiple slashes into a single one where appropriate.
205 device = normalizeUNCRoot(device);
208 return device + (isAbsolute ? '\\' : '') + tail;
212 win32.isAbsolute = function(path) {
214 return win32StatPath(path).isAbsolute;
217 win32.join = function() {
219 for (var i = 0; i < arguments.length; i++) {
220 var arg = arguments[i];
227 var joined = paths.join('\\');
229 // Make sure that the joined path doesn't start with two slashes, because
230 // normalize() will mistake it for an UNC path then.
232 // This step is skipped when it is very clear that the user actually
233 // intended to point at an UNC path. This is assumed when the first
234 // non-empty string arguments starts with exactly two slashes followed by
235 // at least one more non-slash character.
237 // Note that for normalize() to treat a path as an UNC path it needs to
238 // have at least 2 components, so we don't filter for that here.
239 // This means that the user can use join to construct UNC paths from
240 // a server name and a share name; for example:
241 // path.join('//server', 'share') -> '\\\\server\\share\')
242 if (!/^[\\\/]{2}[^\\\/]/.test(paths[0])) {
243 joined = joined.replace(/^[\\\/]{2,}/, '\\');
246 return win32.normalize(joined);
250 // path.relative(from, to)
251 // it will solve the relative path from 'from' to 'to', for instance:
252 // from = 'C:\\orandea\\test\\aaa'
253 // to = 'C:\\orandea\\impl\\bbb'
254 // The output of the function should be: '..\\..\\impl\\bbb'
255 win32.relative = function(from, to) {
259 from = win32.resolve(from);
260 to = win32.resolve(to);
262 // windows is not case sensitive
263 var lowerFrom = from.toLowerCase();
264 var lowerTo = to.toLowerCase();
266 var toParts = trimArray(to.split('\\'));
268 var lowerFromParts = trimArray(lowerFrom.split('\\'));
269 var lowerToParts = trimArray(lowerTo.split('\\'));
271 var length = Math.min(lowerFromParts.length, lowerToParts.length);
272 var samePartsLength = length;
273 for (var i = 0; i < length; i++) {
274 if (lowerFromParts[i] !== lowerToParts[i]) {
280 if (samePartsLength === 0) {
284 var outputParts = [];
285 for (var i = samePartsLength; i < lowerFromParts.length; i++) {
286 outputParts.push('..');
289 outputParts = outputParts.concat(toParts.slice(samePartsLength));
291 return outputParts.join('\\');
295 win32._makeLong = function(path) {
296 // Note: this will *probably* throw somewhere.
297 if (typeof path !== 'string')
304 var resolvedPath = win32.resolve(path);
306 if (/^[a-zA-Z]\:\\/.test(resolvedPath)) {
307 // path is local filesystem path, which needs to be converted
309 return '\\\\?\\' + resolvedPath;
310 } else if (/^\\\\[^?.]/.test(resolvedPath)) {
311 // path is network UNC path, which needs to be converted
313 return '\\\\?\\UNC\\' + resolvedPath.substring(2);
320 win32.dirname = function(path) {
321 var result = win32SplitPath(path),
326 // No dirname whatsoever
331 // It has a dirname, strip trailing slash
332 dir = dir.substr(0, dir.length - 1);
339 win32.basename = function(path, ext) {
340 if (ext !== undefined && typeof ext !== 'string')
341 throw new TypeError('ext must be a string');
343 var f = win32SplitPath(path)[2];
344 // TODO: make this comparison case-insensitive on windows?
345 if (ext && f.substr(-1 * ext.length) === ext) {
346 f = f.substr(0, f.length - ext.length);
352 win32.extname = function(path) {
353 return win32SplitPath(path)[3];
357 win32.format = function(pathObject) {
358 if (pathObject === null || typeof pathObject !== 'object') {
360 "Parameter 'pathObject' must be an object, not " + typeof pathObject
364 var root = pathObject.root || '';
366 if (typeof root !== 'string') {
368 "'pathObject.root' must be a string or undefined, not " +
369 typeof pathObject.root
373 var dir = pathObject.dir;
374 var base = pathObject.base || '';
378 if (dir[dir.length - 1] === win32.sep) {
381 return dir + win32.sep + base;
385 win32.parse = function(pathString) {
386 assertPath(pathString);
388 var allParts = win32SplitPath(pathString);
391 dir: allParts[0] + allParts[1].slice(0, -1),
394 name: allParts[2].slice(0, allParts[2].length - allParts[3].length)
400 win32.delimiter = ';';
403 // Split a filename into [root, dir, basename, ext], unix version
404 // 'root' is just a slash, or nothing.
406 /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
410 function posixSplitPath(filename) {
411 const out = splitPathRe.exec(filename);
417 // path.resolve([from ...], to)
419 posix.resolve = function() {
420 var resolvedPath = '',
421 resolvedAbsolute = false;
423 for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
424 var path = (i >= 0) ? arguments[i] : process.cwd();
428 // Skip empty entries
433 resolvedPath = path + '/' + resolvedPath;
434 resolvedAbsolute = path[0] === '/';
437 // At this point the path should be resolved to a full absolute path, but
438 // handle relative paths to be safe (might happen when process.cwd() fails)
440 // Normalize the path
441 resolvedPath = normalizeArray(resolvedPath.split('/'),
442 !resolvedAbsolute).join('/');
444 return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
447 // path.normalize(path)
449 posix.normalize = function(path) {
452 var isAbsolute = posix.isAbsolute(path),
453 trailingSlash = path && path[path.length - 1] === '/';
455 // Normalize the path
456 path = normalizeArray(path.split('/'), !isAbsolute).join('/');
458 if (!path && !isAbsolute) {
461 if (path && trailingSlash) {
465 return (isAbsolute ? '/' : '') + path;
469 posix.isAbsolute = function(path) {
471 return !!path && path[0] === '/';
475 posix.join = function() {
477 for (var i = 0; i < arguments.length; i++) {
478 var segment = arguments[i];
484 path += '/' + segment;
488 return posix.normalize(path);
492 // path.relative(from, to)
494 posix.relative = function(from, to) {
498 from = posix.resolve(from).substr(1);
499 to = posix.resolve(to).substr(1);
501 var fromParts = trimArray(from.split('/'));
502 var toParts = trimArray(to.split('/'));
504 var length = Math.min(fromParts.length, toParts.length);
505 var samePartsLength = length;
506 for (var i = 0; i < length; i++) {
507 if (fromParts[i] !== toParts[i]) {
513 var outputParts = [];
514 for (var i = samePartsLength; i < fromParts.length; i++) {
515 outputParts.push('..');
518 outputParts = outputParts.concat(toParts.slice(samePartsLength));
520 return outputParts.join('/');
524 posix._makeLong = function(path) {
529 posix.dirname = function(path) {
530 var result = posixSplitPath(path),
535 // No dirname whatsoever
540 // It has a dirname, strip trailing slash
541 dir = dir.substr(0, dir.length - 1);
548 posix.basename = function(path, ext) {
549 if (ext !== undefined && typeof ext !== 'string')
550 throw new TypeError('ext must be a string');
552 var f = posixSplitPath(path)[2];
554 if (ext && f.substr(-1 * ext.length) === ext) {
555 f = f.substr(0, f.length - ext.length);
561 posix.extname = function(path) {
562 return posixSplitPath(path)[3];
566 posix.format = function(pathObject) {
567 if (pathObject === null || typeof pathObject !== 'object') {
569 "Parameter 'pathObject' must be an object, not " + typeof pathObject
573 var root = pathObject.root || '';
575 if (typeof root !== 'string') {
577 "'pathObject.root' must be a string or undefined, not " +
578 typeof pathObject.root
582 var dir = pathObject.dir ? pathObject.dir + posix.sep : '';
583 var base = pathObject.base || '';
588 posix.parse = function(pathString) {
589 assertPath(pathString);
591 var allParts = posixSplitPath(pathString);
594 dir: allParts[0] + allParts[1].slice(0, -1),
597 name: allParts[2].slice(0, allParts[2].length - allParts[3].length)
603 posix.delimiter = ':';
607 module.exports = win32;
609 module.exports = posix;
611 module.exports.posix = posix;
612 module.exports.win32 = win32;