Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80538 views
1
// Copyright Joyent, Inc. and other Node contributors.
2
//
3
// Permission is hereby granted, free of charge, to any person obtaining a
4
// copy of this software and associated documentation files (the
5
// "Software"), to deal in the Software without restriction, including
6
// without limitation the rights to use, copy, modify, merge, publish,
7
// distribute, sublicense, and/or sell copies of the Software, and to permit
8
// persons to whom the Software is furnished to do so, subject to the
9
// following conditions:
10
//
11
// The above copyright notice and this permission notice shall be included
12
// in all copies or substantial portions of the Software.
13
//
14
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22
23
var isWindows = process.platform === 'win32';
24
var util = require('util');
25
26
var _path = require('path');
27
28
// we are new enough we already have this from the system, just export the
29
// system then
30
if (_path.posix) {
31
module.exports = _path;
32
return;
33
}
34
35
// resolves . and .. elements in a path array with directory names there
36
// must be no slashes or device names (c:\) in the array
37
// (so also no leading and trailing slashes - it does not distinguish
38
// relative and absolute paths)
39
function normalizeArray(parts, allowAboveRoot) {
40
var res = [];
41
for (var i = 0; i < parts.length; i++) {
42
var p = parts[i];
43
44
// ignore empty parts
45
if (!p || p === '.')
46
continue;
47
48
if (p === '..') {
49
if (res.length && res[res.length - 1] !== '..') {
50
res.pop();
51
} else if (allowAboveRoot) {
52
res.push('..');
53
}
54
} else {
55
res.push(p);
56
}
57
}
58
59
return res;
60
}
61
62
// Regex to split a windows path into three parts: [*, device, slash,
63
// tail] windows-only
64
var splitDeviceRe =
65
/^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/;
66
67
// Regex to split the tail part of the above into [*, dir, basename, ext]
68
var splitTailRe =
69
/^([\s\S]*?)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/;
70
71
var win32 = {};
72
73
// Function to split a filename into [root, dir, basename, ext]
74
function win32SplitPath(filename) {
75
// Separate device+slash from tail
76
var result = splitDeviceRe.exec(filename),
77
device = (result[1] || '') + (result[2] || ''),
78
tail = result[3] || '';
79
// Split the tail into dir, basename and extension
80
var result2 = splitTailRe.exec(tail),
81
dir = result2[1],
82
basename = result2[2],
83
ext = result2[3];
84
return [device, dir, basename, ext];
85
}
86
87
var normalizeUNCRoot = function(device) {
88
return '\\\\' + device.replace(/^[\\\/]+/, '').replace(/[\\\/]+/g, '\\');
89
};
90
91
// path.resolve([from ...], to)
92
win32.resolve = function() {
93
var resolvedDevice = '',
94
resolvedTail = '',
95
resolvedAbsolute = false;
96
97
for (var i = arguments.length - 1; i >= -1; i--) {
98
var path;
99
if (i >= 0) {
100
path = arguments[i];
101
} else if (!resolvedDevice) {
102
path = process.cwd();
103
} else {
104
// Windows has the concept of drive-specific current working
105
// directories. If we've resolved a drive letter but not yet an
106
// absolute path, get cwd for that drive. We're sure the device is not
107
// an unc path at this points, because unc paths are always absolute.
108
path = process.env['=' + resolvedDevice];
109
// Verify that a drive-local cwd was found and that it actually points
110
// to our drive. If not, default to the drive's root.
111
if (!path || path.substr(0, 3).toLowerCase() !==
112
resolvedDevice.toLowerCase() + '\\') {
113
path = resolvedDevice + '\\';
114
}
115
}
116
117
// Skip empty and invalid entries
118
if (!util.isString(path)) {
119
throw new TypeError('Arguments to path.resolve must be strings');
120
} else if (!path) {
121
continue;
122
}
123
124
var result = splitDeviceRe.exec(path),
125
device = result[1] || '',
126
isUnc = device && device.charAt(1) !== ':',
127
isAbsolute = win32.isAbsolute(path),
128
tail = result[3];
129
130
if (device &&
131
resolvedDevice &&
132
device.toLowerCase() !== resolvedDevice.toLowerCase()) {
133
// This path points to another device so it is not applicable
134
continue;
135
}
136
137
if (!resolvedDevice) {
138
resolvedDevice = device;
139
}
140
if (!resolvedAbsolute) {
141
resolvedTail = tail + '\\' + resolvedTail;
142
resolvedAbsolute = isAbsolute;
143
}
144
145
if (resolvedDevice && resolvedAbsolute) {
146
break;
147
}
148
}
149
150
// Convert slashes to backslashes when `resolvedDevice` points to an UNC
151
// root. Also squash multiple slashes into a single one where appropriate.
152
if (isUnc) {
153
resolvedDevice = normalizeUNCRoot(resolvedDevice);
154
}
155
156
// At this point the path should be resolved to a full absolute path,
157
// but handle relative paths to be safe (might happen when process.cwd()
158
// fails)
159
160
// Normalize the tail path
161
resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/),
162
!resolvedAbsolute).join('\\');
163
164
// If device is a drive letter, we'll normalize to lower case.
165
if (resolvedDevice && resolvedDevice.charAt(1) === ':') {
166
resolvedDevice = resolvedDevice[0].toLowerCase() +
167
resolvedDevice.substr(1);
168
}
169
170
return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) ||
171
'.';
172
};
173
174
175
win32.normalize = function(path) {
176
var result = splitDeviceRe.exec(path),
177
device = result[1] || '',
178
isUnc = device && device.charAt(1) !== ':',
179
isAbsolute = win32.isAbsolute(path),
180
tail = result[3],
181
trailingSlash = /[\\\/]$/.test(tail);
182
183
// If device is a drive letter, we'll normalize to lower case.
184
if (device && device.charAt(1) === ':') {
185
device = device[0].toLowerCase() + device.substr(1);
186
}
187
188
// Normalize the tail path
189
tail = normalizeArray(tail.split(/[\\\/]+/), !isAbsolute).join('\\');
190
191
if (!tail && !isAbsolute) {
192
tail = '.';
193
}
194
if (tail && trailingSlash) {
195
tail += '\\';
196
}
197
198
// Convert slashes to backslashes when `device` points to an UNC root.
199
// Also squash multiple slashes into a single one where appropriate.
200
if (isUnc) {
201
device = normalizeUNCRoot(device);
202
}
203
204
return device + (isAbsolute ? '\\' : '') + tail;
205
};
206
207
208
win32.isAbsolute = function(path) {
209
var result = splitDeviceRe.exec(path),
210
device = result[1] || '',
211
isUnc = !!device && device.charAt(1) !== ':';
212
// UNC paths are always absolute
213
return !!result[2] || isUnc;
214
};
215
216
win32.join = function() {
217
function f(p) {
218
if (!util.isString(p)) {
219
throw new TypeError('Arguments to path.join must be strings');
220
}
221
return p;
222
}
223
224
var paths = Array.prototype.filter.call(arguments, f);
225
var joined = paths.join('\\');
226
227
// Make sure that the joined path doesn't start with two slashes, because
228
// normalize() will mistake it for an UNC path then.
229
//
230
// This step is skipped when it is very clear that the user actually
231
// intended to point at an UNC path. This is assumed when the first
232
// non-empty string arguments starts with exactly two slashes followed by
233
// at least one more non-slash character.
234
//
235
// Note that for normalize() to treat a path as an UNC path it needs to
236
// have at least 2 components, so we don't filter for that here.
237
// This means that the user can use join to construct UNC paths from
238
// a server name and a share name; for example:
239
// path.join('//server', 'share') -> '\\\\server\\share\')
240
if (!/^[\\\/]{2}[^\\\/]/.test(paths[0])) {
241
joined = joined.replace(/^[\\\/]{2,}/, '\\');
242
}
243
244
return win32.normalize(joined);
245
};
246
247
248
// path.relative(from, to)
249
// it will solve the relative path from 'from' to 'to', for instance:
250
// from = 'C:\\orandea\\test\\aaa'
251
// to = 'C:\\orandea\\impl\\bbb'
252
// The output of the function should be: '..\\..\\impl\\bbb'
253
win32.relative = function(from, to) {
254
from = win32.resolve(from);
255
to = win32.resolve(to);
256
257
// windows is not case sensitive
258
var lowerFrom = from.toLowerCase();
259
var lowerTo = to.toLowerCase();
260
261
function trim(arr) {
262
var start = 0;
263
for (; start < arr.length; start++) {
264
if (arr[start] !== '') break;
265
}
266
267
var end = arr.length - 1;
268
for (; end >= 0; end--) {
269
if (arr[end] !== '') break;
270
}
271
272
if (start > end) return [];
273
return arr.slice(start, end + 1);
274
}
275
276
var toParts = trim(to.split('\\'));
277
278
var lowerFromParts = trim(lowerFrom.split('\\'));
279
var lowerToParts = trim(lowerTo.split('\\'));
280
281
var length = Math.min(lowerFromParts.length, lowerToParts.length);
282
var samePartsLength = length;
283
for (var i = 0; i < length; i++) {
284
if (lowerFromParts[i] !== lowerToParts[i]) {
285
samePartsLength = i;
286
break;
287
}
288
}
289
290
if (samePartsLength == 0) {
291
return to;
292
}
293
294
var outputParts = [];
295
for (var i = samePartsLength; i < lowerFromParts.length; i++) {
296
outputParts.push('..');
297
}
298
299
outputParts = outputParts.concat(toParts.slice(samePartsLength));
300
301
return outputParts.join('\\');
302
};
303
304
305
win32._makeLong = function(path) {
306
// Note: this will *probably* throw somewhere.
307
if (!util.isString(path))
308
return path;
309
310
if (!path) {
311
return '';
312
}
313
314
var resolvedPath = win32.resolve(path);
315
316
if (/^[a-zA-Z]\:\\/.test(resolvedPath)) {
317
// path is local filesystem path, which needs to be converted
318
// to long UNC path.
319
return '\\\\?\\' + resolvedPath;
320
} else if (/^\\\\[^?.]/.test(resolvedPath)) {
321
// path is network UNC path, which needs to be converted
322
// to long UNC path.
323
return '\\\\?\\UNC\\' + resolvedPath.substring(2);
324
}
325
326
return path;
327
};
328
329
330
win32.dirname = function(path) {
331
var result = win32SplitPath(path),
332
root = result[0],
333
dir = result[1];
334
335
if (!root && !dir) {
336
// No dirname whatsoever
337
return '.';
338
}
339
340
if (dir) {
341
// It has a dirname, strip trailing slash
342
dir = dir.substr(0, dir.length - 1);
343
}
344
345
return root + dir;
346
};
347
348
349
win32.basename = function(path, ext) {
350
var f = win32SplitPath(path)[2];
351
// TODO: make this comparison case-insensitive on windows?
352
if (ext && f.substr(-1 * ext.length) === ext) {
353
f = f.substr(0, f.length - ext.length);
354
}
355
return f;
356
};
357
358
359
win32.extname = function(path) {
360
return win32SplitPath(path)[3];
361
};
362
363
364
win32.format = function(pathObject) {
365
if (!util.isObject(pathObject)) {
366
throw new TypeError(
367
"Parameter 'pathObject' must be an object, not " + typeof pathObject
368
);
369
}
370
371
var root = pathObject.root || '';
372
373
if (!util.isString(root)) {
374
throw new TypeError(
375
"'pathObject.root' must be a string or undefined, not " +
376
typeof pathObject.root
377
);
378
}
379
380
var dir = pathObject.dir;
381
var base = pathObject.base || '';
382
if (dir.slice(dir.length - 1, dir.length) === win32.sep) {
383
return dir + base;
384
}
385
386
if (dir) {
387
return dir + win32.sep + base;
388
}
389
390
return base;
391
};
392
393
394
win32.parse = function(pathString) {
395
if (!util.isString(pathString)) {
396
throw new TypeError(
397
"Parameter 'pathString' must be a string, not " + typeof pathString
398
);
399
}
400
var allParts = win32SplitPath(pathString);
401
if (!allParts || allParts.length !== 4) {
402
throw new TypeError("Invalid path '" + pathString + "'");
403
}
404
return {
405
root: allParts[0],
406
dir: allParts[0] + allParts[1].slice(0, allParts[1].length - 1),
407
base: allParts[2],
408
ext: allParts[3],
409
name: allParts[2].slice(0, allParts[2].length - allParts[3].length)
410
};
411
};
412
413
414
win32.sep = '\\';
415
win32.delimiter = ';';
416
417
418
// Split a filename into [root, dir, basename, ext], unix version
419
// 'root' is just a slash, or nothing.
420
var splitPathRe =
421
/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
422
var posix = {};
423
424
425
function posixSplitPath(filename) {
426
return splitPathRe.exec(filename).slice(1);
427
}
428
429
430
// path.resolve([from ...], to)
431
// posix version
432
posix.resolve = function() {
433
var resolvedPath = '',
434
resolvedAbsolute = false;
435
436
for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
437
var path = (i >= 0) ? arguments[i] : process.cwd();
438
439
// Skip empty and invalid entries
440
if (!util.isString(path)) {
441
throw new TypeError('Arguments to path.resolve must be strings');
442
} else if (!path) {
443
continue;
444
}
445
446
resolvedPath = path + '/' + resolvedPath;
447
resolvedAbsolute = path.charAt(0) === '/';
448
}
449
450
// At this point the path should be resolved to a full absolute path, but
451
// handle relative paths to be safe (might happen when process.cwd() fails)
452
453
// Normalize the path
454
resolvedPath = normalizeArray(resolvedPath.split('/'),
455
!resolvedAbsolute).join('/');
456
457
return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
458
};
459
460
// path.normalize(path)
461
// posix version
462
posix.normalize = function(path) {
463
var isAbsolute = posix.isAbsolute(path),
464
trailingSlash = path.substr(-1) === '/';
465
466
// Normalize the path
467
path = normalizeArray(path.split('/'), !isAbsolute).join('/');
468
469
if (!path && !isAbsolute) {
470
path = '.';
471
}
472
if (path && trailingSlash) {
473
path += '/';
474
}
475
476
return (isAbsolute ? '/' : '') + path;
477
};
478
479
// posix version
480
posix.isAbsolute = function(path) {
481
return path.charAt(0) === '/';
482
};
483
484
// posix version
485
posix.join = function() {
486
var path = '';
487
for (var i = 0; i < arguments.length; i++) {
488
var segment = arguments[i];
489
if (!util.isString(segment)) {
490
throw new TypeError('Arguments to path.join must be strings');
491
}
492
if (segment) {
493
if (!path) {
494
path += segment;
495
} else {
496
path += '/' + segment;
497
}
498
}
499
}
500
return posix.normalize(path);
501
};
502
503
504
// path.relative(from, to)
505
// posix version
506
posix.relative = function(from, to) {
507
from = posix.resolve(from).substr(1);
508
to = posix.resolve(to).substr(1);
509
510
function trim(arr) {
511
var start = 0;
512
for (; start < arr.length; start++) {
513
if (arr[start] !== '') break;
514
}
515
516
var end = arr.length - 1;
517
for (; end >= 0; end--) {
518
if (arr[end] !== '') break;
519
}
520
521
if (start > end) return [];
522
return arr.slice(start, end + 1);
523
}
524
525
var fromParts = trim(from.split('/'));
526
var toParts = trim(to.split('/'));
527
528
var length = Math.min(fromParts.length, toParts.length);
529
var samePartsLength = length;
530
for (var i = 0; i < length; i++) {
531
if (fromParts[i] !== toParts[i]) {
532
samePartsLength = i;
533
break;
534
}
535
}
536
537
var outputParts = [];
538
for (var i = samePartsLength; i < fromParts.length; i++) {
539
outputParts.push('..');
540
}
541
542
outputParts = outputParts.concat(toParts.slice(samePartsLength));
543
544
return outputParts.join('/');
545
};
546
547
548
posix._makeLong = function(path) {
549
return path;
550
};
551
552
553
posix.dirname = function(path) {
554
var result = posixSplitPath(path),
555
root = result[0],
556
dir = result[1];
557
558
if (!root && !dir) {
559
// No dirname whatsoever
560
return '.';
561
}
562
563
if (dir) {
564
// It has a dirname, strip trailing slash
565
dir = dir.substr(0, dir.length - 1);
566
}
567
568
return root + dir;
569
};
570
571
572
posix.basename = function(path, ext) {
573
var f = posixSplitPath(path)[2];
574
// TODO: make this comparison case-insensitive on windows?
575
if (ext && f.substr(-1 * ext.length) === ext) {
576
f = f.substr(0, f.length - ext.length);
577
}
578
return f;
579
};
580
581
582
posix.extname = function(path) {
583
return posixSplitPath(path)[3];
584
};
585
586
587
posix.format = function(pathObject) {
588
if (!util.isObject(pathObject)) {
589
throw new TypeError(
590
"Parameter 'pathObject' must be an object, not " + typeof pathObject
591
);
592
}
593
594
var root = pathObject.root || '';
595
596
if (!util.isString(root)) {
597
throw new TypeError(
598
"'pathObject.root' must be a string or undefined, not " +
599
typeof pathObject.root
600
);
601
}
602
603
var dir = pathObject.dir ? pathObject.dir + posix.sep : '';
604
var base = pathObject.base || '';
605
return dir + base;
606
};
607
608
609
posix.parse = function(pathString) {
610
if (!util.isString(pathString)) {
611
throw new TypeError(
612
"Parameter 'pathString' must be a string, not " + typeof pathString
613
);
614
}
615
var allParts = posixSplitPath(pathString);
616
if (!allParts || allParts.length !== 4) {
617
throw new TypeError("Invalid path '" + pathString + "'");
618
}
619
allParts[1] = allParts[1] || '';
620
allParts[2] = allParts[2] || '';
621
allParts[3] = allParts[3] || '';
622
623
return {
624
root: allParts[0],
625
dir: allParts[0] + allParts[1].slice(0, allParts[1].length - 1),
626
base: allParts[2],
627
ext: allParts[3],
628
name: allParts[2].slice(0, allParts[2].length - allParts[3].length)
629
};
630
};
631
632
633
posix.sep = '/';
634
posix.delimiter = ':';
635
636
637
if (isWindows)
638
module.exports = win32;
639
else /* posix */
640
module.exports = posix;
641
642
module.exports.posix = posix;
643
module.exports.win32 = win32;
644
645