path: refactor for performance and consistency
Improve performance by: + Not leaking the `arguments` object! + Getting the last character of a string by index, instead of with `.substr()` or `.slice()` Improve code consistency by: + Using `[]` instead of `.charAt()` where possible + Using a function declaration instead of a var declaration + Using `.slice()` with clearer arguments + Checking if `dir` is truthy in `win32.format` (added tests for this) Improve both by: + Making the reusable `trimArray()` function + Standardizing getting certain path statistics with the new `win32StatPath()` function PR-URL: https://github.com/nodejs/io.js/pull/1778 Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com> Reviewed-By: Roman Reiss <me@silverwind.io>
This commit is contained in:
parent
46140334cd
commit
bca53dce76
139
lib/path.js
139
lib/path.js
@ -37,6 +37,29 @@ function normalizeArray(parts, allowAboveRoot) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns an array with empty elements removed from either end of the input
|
||||||
|
// array or the original array if no elements need to be removed
|
||||||
|
function trimArray(arr) {
|
||||||
|
var lastIndex = arr.length - 1;
|
||||||
|
var start = 0;
|
||||||
|
for (; start <= lastIndex; start++) {
|
||||||
|
if (arr[start])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var end = lastIndex;
|
||||||
|
for (; end >= 0; end--) {
|
||||||
|
if (arr[end])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start === 0 && end === lastIndex)
|
||||||
|
return arr;
|
||||||
|
if (start > end)
|
||||||
|
return [];
|
||||||
|
return arr.slice(start, end + 1);
|
||||||
|
}
|
||||||
|
|
||||||
// Regex to split a windows path into three parts: [*, device, slash,
|
// Regex to split a windows path into three parts: [*, device, slash,
|
||||||
// tail] windows-only
|
// tail] windows-only
|
||||||
const splitDeviceRe =
|
const splitDeviceRe =
|
||||||
@ -62,9 +85,21 @@ function win32SplitPath(filename) {
|
|||||||
return [device, dir, basename, ext];
|
return [device, dir, basename, ext];
|
||||||
}
|
}
|
||||||
|
|
||||||
var normalizeUNCRoot = function(device) {
|
function win32StatPath(path) {
|
||||||
|
var result = splitDeviceRe.exec(path),
|
||||||
|
device = result[1] || '',
|
||||||
|
isUnc = !!device && device[1] !== ':';
|
||||||
|
return {
|
||||||
|
device,
|
||||||
|
isUnc,
|
||||||
|
isAbsolute: isUnc || !!result[2], // UNC paths are always absolute
|
||||||
|
tail: result[3]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeUNCRoot(device) {
|
||||||
return '\\\\' + device.replace(/^[\\\/]+/, '').replace(/[\\\/]+/g, '\\');
|
return '\\\\' + device.replace(/^[\\\/]+/, '').replace(/[\\\/]+/g, '\\');
|
||||||
};
|
}
|
||||||
|
|
||||||
// path.resolve([from ...], to)
|
// path.resolve([from ...], to)
|
||||||
win32.resolve = function() {
|
win32.resolve = function() {
|
||||||
@ -99,11 +134,11 @@ win32.resolve = function() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = splitDeviceRe.exec(path),
|
var result = win32StatPath(path),
|
||||||
device = result[1] || '',
|
device = result.device,
|
||||||
isUnc = device && device.charAt(1) !== ':',
|
isUnc = result.isUnc,
|
||||||
isAbsolute = win32.isAbsolute(path),
|
isAbsolute = result.isAbsolute,
|
||||||
tail = result[3];
|
tail = result.tail;
|
||||||
|
|
||||||
if (device &&
|
if (device &&
|
||||||
resolvedDevice &&
|
resolvedDevice &&
|
||||||
@ -147,11 +182,11 @@ win32.resolve = function() {
|
|||||||
win32.normalize = function(path) {
|
win32.normalize = function(path) {
|
||||||
assertPath(path);
|
assertPath(path);
|
||||||
|
|
||||||
var result = splitDeviceRe.exec(path),
|
var result = win32StatPath(path),
|
||||||
device = result[1] || '',
|
device = result.device,
|
||||||
isUnc = device && device.charAt(1) !== ':',
|
isUnc = result.isUnc,
|
||||||
isAbsolute = win32.isAbsolute(path),
|
isAbsolute = result.isAbsolute,
|
||||||
tail = result[3],
|
tail = result.tail,
|
||||||
trailingSlash = /[\\\/]$/.test(tail);
|
trailingSlash = /[\\\/]$/.test(tail);
|
||||||
|
|
||||||
// Normalize the tail path
|
// Normalize the tail path
|
||||||
@ -176,23 +211,19 @@ win32.normalize = function(path) {
|
|||||||
|
|
||||||
win32.isAbsolute = function(path) {
|
win32.isAbsolute = function(path) {
|
||||||
assertPath(path);
|
assertPath(path);
|
||||||
|
return win32StatPath(path).isAbsolute;
|
||||||
var result = splitDeviceRe.exec(path),
|
|
||||||
device = result[1] || '',
|
|
||||||
isUnc = !!device && device.charAt(1) !== ':';
|
|
||||||
// UNC paths are always absolute
|
|
||||||
return !!result[2] || isUnc;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
win32.join = function() {
|
win32.join = function() {
|
||||||
function f(p) {
|
var paths = [];
|
||||||
if (typeof p !== 'string') {
|
for (var i = 0; i < arguments.length; i++) {
|
||||||
throw new TypeError('Arguments to path.join must be strings');
|
var arg = arguments[i];
|
||||||
|
assertPath(arg);
|
||||||
|
if (arg) {
|
||||||
|
paths.push(arg);
|
||||||
}
|
}
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var paths = Array.prototype.filter.call(arguments, f);
|
|
||||||
var joined = paths.join('\\');
|
var joined = paths.join('\\');
|
||||||
|
|
||||||
// Make sure that the joined path doesn't start with two slashes, because
|
// Make sure that the joined path doesn't start with two slashes, because
|
||||||
@ -232,25 +263,10 @@ win32.relative = function(from, to) {
|
|||||||
var lowerFrom = from.toLowerCase();
|
var lowerFrom = from.toLowerCase();
|
||||||
var lowerTo = to.toLowerCase();
|
var lowerTo = to.toLowerCase();
|
||||||
|
|
||||||
function trim(arr) {
|
var toParts = trimArray(to.split('\\'));
|
||||||
var start = 0;
|
|
||||||
for (; start < arr.length; start++) {
|
|
||||||
if (arr[start] !== '') break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var end = arr.length - 1;
|
var lowerFromParts = trimArray(lowerFrom.split('\\'));
|
||||||
for (; end >= 0; end--) {
|
var lowerToParts = trimArray(lowerTo.split('\\'));
|
||||||
if (arr[end] !== '') break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start > end) return [];
|
|
||||||
return arr.slice(start, end + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
var toParts = trim(to.split('\\'));
|
|
||||||
|
|
||||||
var lowerFromParts = trim(lowerFrom.split('\\'));
|
|
||||||
var lowerToParts = trim(lowerTo.split('\\'));
|
|
||||||
|
|
||||||
var length = Math.min(lowerFromParts.length, lowerToParts.length);
|
var length = Math.min(lowerFromParts.length, lowerToParts.length);
|
||||||
var samePartsLength = length;
|
var samePartsLength = length;
|
||||||
@ -356,15 +372,13 @@ win32.format = function(pathObject) {
|
|||||||
|
|
||||||
var dir = pathObject.dir;
|
var dir = pathObject.dir;
|
||||||
var base = pathObject.base || '';
|
var base = pathObject.base || '';
|
||||||
if (dir.slice(dir.length - 1, dir.length) === win32.sep) {
|
if (!dir) {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
if (dir[dir.length - 1] === win32.sep) {
|
||||||
return dir + base;
|
return dir + base;
|
||||||
}
|
}
|
||||||
|
return dir + win32.sep + base;
|
||||||
if (dir) {
|
|
||||||
return dir + win32.sep + base;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -377,7 +391,7 @@ win32.parse = function(pathString) {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
root: allParts[0],
|
root: allParts[0],
|
||||||
dir: allParts[0] + allParts[1].slice(0, allParts[1].length - 1),
|
dir: allParts[0] + allParts[1].slice(0, -1),
|
||||||
base: allParts[2],
|
base: allParts[2],
|
||||||
ext: allParts[3],
|
ext: allParts[3],
|
||||||
name: allParts[2].slice(0, allParts[2].length - allParts[3].length)
|
name: allParts[2].slice(0, allParts[2].length - allParts[3].length)
|
||||||
@ -418,7 +432,7 @@ posix.resolve = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resolvedPath = path + '/' + resolvedPath;
|
resolvedPath = path + '/' + resolvedPath;
|
||||||
resolvedAbsolute = path.charAt(0) === '/';
|
resolvedAbsolute = path[0] === '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point the path should be resolved to a full absolute path, but
|
// At this point the path should be resolved to a full absolute path, but
|
||||||
@ -437,7 +451,7 @@ posix.normalize = function(path) {
|
|||||||
assertPath(path);
|
assertPath(path);
|
||||||
|
|
||||||
var isAbsolute = posix.isAbsolute(path),
|
var isAbsolute = posix.isAbsolute(path),
|
||||||
trailingSlash = path.substr(-1) === '/';
|
trailingSlash = path && path[path.length - 1] === '/';
|
||||||
|
|
||||||
// Normalize the path
|
// Normalize the path
|
||||||
path = normalizeArray(path.split('/'), !isAbsolute).join('/');
|
path = normalizeArray(path.split('/'), !isAbsolute).join('/');
|
||||||
@ -455,7 +469,7 @@ posix.normalize = function(path) {
|
|||||||
// posix version
|
// posix version
|
||||||
posix.isAbsolute = function(path) {
|
posix.isAbsolute = function(path) {
|
||||||
assertPath(path);
|
assertPath(path);
|
||||||
return path.charAt(0) === '/';
|
return !!path && path[0] === '/';
|
||||||
};
|
};
|
||||||
|
|
||||||
// posix version
|
// posix version
|
||||||
@ -487,23 +501,8 @@ posix.relative = function(from, to) {
|
|||||||
from = posix.resolve(from).substr(1);
|
from = posix.resolve(from).substr(1);
|
||||||
to = posix.resolve(to).substr(1);
|
to = posix.resolve(to).substr(1);
|
||||||
|
|
||||||
function trim(arr) {
|
var fromParts = trimArray(from.split('/'));
|
||||||
var start = 0;
|
var toParts = trimArray(to.split('/'));
|
||||||
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 + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
var fromParts = trim(from.split('/'));
|
|
||||||
var toParts = trim(to.split('/'));
|
|
||||||
|
|
||||||
var length = Math.min(fromParts.length, toParts.length);
|
var length = Math.min(fromParts.length, toParts.length);
|
||||||
var samePartsLength = length;
|
var samePartsLength = length;
|
||||||
@ -602,7 +601,7 @@ posix.parse = function(pathString) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
root: allParts[0],
|
root: allParts[0],
|
||||||
dir: allParts[0] + allParts[1].slice(0, allParts[1].length - 1),
|
dir: allParts[0] + allParts[1].slice(0, -1),
|
||||||
base: allParts[2],
|
base: allParts[2],
|
||||||
ext: allParts[3],
|
ext: allParts[3],
|
||||||
name: allParts[2].slice(0, allParts[2].length - allParts[3].length)
|
name: allParts[2].slice(0, allParts[2].length - allParts[3].length)
|
||||||
|
@ -15,7 +15,12 @@ var winPaths = [
|
|||||||
'\\\\server two\\shared folder\\file path.zip',
|
'\\\\server two\\shared folder\\file path.zip',
|
||||||
'\\\\teela\\admin$\\system32',
|
'\\\\teela\\admin$\\system32',
|
||||||
'\\\\?\\UNC\\server\\share'
|
'\\\\?\\UNC\\server\\share'
|
||||||
|
];
|
||||||
|
|
||||||
|
var winSpecialCaseFormatTests = [
|
||||||
|
[{dir: 'some\\dir'}, 'some\\dir\\'],
|
||||||
|
[{base: 'index.html'}, 'index.html'],
|
||||||
|
[{}, '']
|
||||||
];
|
];
|
||||||
|
|
||||||
var unixPaths = [
|
var unixPaths = [
|
||||||
@ -30,6 +35,12 @@ var unixPaths = [
|
|||||||
'C:\\foo'
|
'C:\\foo'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
var unixSpecialCaseFormatTests = [
|
||||||
|
[{dir: 'some/dir'}, 'some/dir/'],
|
||||||
|
[{base: 'index.html'}, 'index.html'],
|
||||||
|
[{}, '']
|
||||||
|
];
|
||||||
|
|
||||||
var errors = [
|
var errors = [
|
||||||
{method: 'parse', input: [null],
|
{method: 'parse', input: [null],
|
||||||
message: /Path must be a string. Received null/},
|
message: /Path must be a string. Received null/},
|
||||||
@ -57,10 +68,12 @@ var errors = [
|
|||||||
message: /'pathObject.root' must be a string or undefined, not number/},
|
message: /'pathObject.root' must be a string or undefined, not number/},
|
||||||
];
|
];
|
||||||
|
|
||||||
check(path.win32, winPaths);
|
checkParseFormat(path.win32, winPaths);
|
||||||
check(path.posix, unixPaths);
|
checkParseFormat(path.posix, unixPaths);
|
||||||
checkErrors(path.win32);
|
checkErrors(path.win32);
|
||||||
checkErrors(path.posix);
|
checkErrors(path.posix);
|
||||||
|
checkFormat(path.win32, winSpecialCaseFormatTests);
|
||||||
|
checkFormat(path.posix, unixSpecialCaseFormatTests);
|
||||||
|
|
||||||
function checkErrors(path) {
|
function checkErrors(path) {
|
||||||
errors.forEach(function(errorCase) {
|
errors.forEach(function(errorCase) {
|
||||||
@ -79,8 +92,7 @@ function checkErrors(path) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkParseFormat(path, paths) {
|
||||||
function check(path, paths) {
|
|
||||||
paths.forEach(function(element, index, array) {
|
paths.forEach(function(element, index, array) {
|
||||||
var output = path.parse(element);
|
var output = path.parse(element);
|
||||||
assert.strictEqual(path.format(output), element);
|
assert.strictEqual(path.format(output), element);
|
||||||
@ -89,3 +101,9 @@ function check(path, paths) {
|
|||||||
assert.strictEqual(output.ext, path.extname(element));
|
assert.strictEqual(output.ext, path.extname(element));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkFormat(path, testCases) {
|
||||||
|
testCases.forEach(function(testCase) {
|
||||||
|
assert.strictEqual(path.format(testCase[0]), testCase[1]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user