@file-services/path
Advanced tools
Comparing version 2.2.0 to 3.0.0
2179
browser-path.js
@@ -12,26 +12,26 @@ const CHAR_UPPERCASE_A = 65; /* A */ | ||
function valueType(value) { | ||
const valueType = typeof value; | ||
return valueType === 'object' && value === null ? 'null' : valueType; | ||
const valueType = typeof value; | ||
return valueType === 'object' && value === null ? 'null' : valueType; | ||
} | ||
function validateString(value, paramName) { | ||
if (typeof value !== 'string') { | ||
throw new TypeError( | ||
`[ERR_INVALID_ARG_TYPE]: The "${paramName}" argument must be of type string. Received ${valueType(value)}` | ||
); | ||
} | ||
if (typeof value !== 'string') { | ||
throw new TypeError( | ||
`[ERR_INVALID_ARG_TYPE]: The "${paramName}" argument must be of type string. Received ${valueType(value)}` | ||
); | ||
} | ||
} | ||
function isPathSeparator(code) { | ||
return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; | ||
return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; | ||
} | ||
function isPosixPathSeparator(code) { | ||
return code === CHAR_FORWARD_SLASH; | ||
return code === CHAR_FORWARD_SLASH; | ||
} | ||
function isWindowsDeviceRoot(code) { | ||
return ( | ||
(code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) || (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z) | ||
); | ||
return ( | ||
(code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) || (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z) | ||
); | ||
} | ||
@@ -41,1242 +41,1237 @@ | ||
function normalizeString(path, allowAboveRoot, separator, isPathSeparator) { | ||
let res = ''; | ||
let lastSegmentLength = 0; | ||
let lastSlash = -1; | ||
let dots = 0; | ||
let code = 0; | ||
for (let i = 0; i <= path.length; ++i) { | ||
if (i < path.length) code = path.charCodeAt(i); | ||
else if (isPathSeparator(code)) break; | ||
else code = CHAR_FORWARD_SLASH; | ||
let res = ''; | ||
let lastSegmentLength = 0; | ||
let lastSlash = -1; | ||
let dots = 0; | ||
let code = 0; | ||
for (let i = 0; i <= path.length; ++i) { | ||
if (i < path.length) code = path.charCodeAt(i); | ||
else if (isPathSeparator(code)) break; | ||
else code = CHAR_FORWARD_SLASH; | ||
if (isPathSeparator(code)) { | ||
if (lastSlash === i - 1 || dots === 1) { | ||
// NOOP | ||
} else if (dots === 2) { | ||
if ( | ||
res.length < 2 || | ||
lastSegmentLength !== 2 || | ||
res.charCodeAt(res.length - 1) !== CHAR_DOT || | ||
res.charCodeAt(res.length - 2) !== CHAR_DOT | ||
) { | ||
if (res.length > 2) { | ||
const lastSlashIndex = res.lastIndexOf(separator); | ||
if (lastSlashIndex === -1) { | ||
res = ''; | ||
lastSegmentLength = 0; | ||
} else { | ||
res = res.slice(0, lastSlashIndex); | ||
lastSegmentLength = res.length - 1 - res.lastIndexOf(separator); | ||
} | ||
lastSlash = i; | ||
dots = 0; | ||
continue; | ||
} else if (res.length !== 0) { | ||
res = ''; | ||
lastSegmentLength = 0; | ||
lastSlash = i; | ||
dots = 0; | ||
continue; | ||
} | ||
} | ||
if (allowAboveRoot) { | ||
res += res.length > 0 ? `${separator}..` : '..'; | ||
lastSegmentLength = 2; | ||
} | ||
if (isPathSeparator(code)) { | ||
if (lastSlash === i - 1 || dots === 1) { | ||
// NOOP | ||
} else if (dots === 2) { | ||
if ( | ||
res.length < 2 || | ||
lastSegmentLength !== 2 || | ||
res.charCodeAt(res.length - 1) !== CHAR_DOT || | ||
res.charCodeAt(res.length - 2) !== CHAR_DOT | ||
) { | ||
if (res.length > 2) { | ||
const lastSlashIndex = res.lastIndexOf(separator); | ||
if (lastSlashIndex === -1) { | ||
res = ''; | ||
lastSegmentLength = 0; | ||
} else { | ||
if (res.length > 0) res += `${separator}${path.slice(lastSlash + 1, i)}`; | ||
else res = path.slice(lastSlash + 1, i); | ||
lastSegmentLength = i - lastSlash - 1; | ||
res = res.slice(0, lastSlashIndex); | ||
lastSegmentLength = res.length - 1 - res.lastIndexOf(separator); | ||
} | ||
lastSlash = i; | ||
dots = 0; | ||
} else if (code === CHAR_DOT && dots !== -1) { | ||
++dots; | ||
} else { | ||
dots = -1; | ||
continue; | ||
} else if (res.length !== 0) { | ||
res = ''; | ||
lastSegmentLength = 0; | ||
lastSlash = i; | ||
dots = 0; | ||
continue; | ||
} | ||
} | ||
if (allowAboveRoot) { | ||
res += res.length > 0 ? `${separator}..` : '..'; | ||
lastSegmentLength = 2; | ||
} | ||
} else { | ||
if (res.length > 0) res += `${separator}${path.slice(lastSlash + 1, i)}`; | ||
else res = path.slice(lastSlash + 1, i); | ||
lastSegmentLength = i - lastSlash - 1; | ||
} | ||
lastSlash = i; | ||
dots = 0; | ||
} else if (code === CHAR_DOT && dots !== -1) { | ||
++dots; | ||
} else { | ||
dots = -1; | ||
} | ||
return res; | ||
} | ||
return res; | ||
} | ||
function _format(sep, pathObject) { | ||
if (pathObject === null || typeof pathObject !== 'object') { | ||
throw new TypeError( | ||
`[ERR_INVALID_ARG_TYPE]: The "pathObject" argument must be of type object. Received ${valueType( | ||
pathObject | ||
)}` | ||
); | ||
} | ||
const dir = pathObject.dir || pathObject.root; | ||
const base = pathObject.base || `${pathObject.name || ''}${pathObject.ext || ''}`; | ||
if (!dir) { | ||
return base; | ||
} | ||
return dir === pathObject.root ? `${dir}${base}` : `${dir}${sep}${base}`; | ||
if (pathObject === null || typeof pathObject !== 'object') { | ||
throw new TypeError( | ||
`[ERR_INVALID_ARG_TYPE]: The "pathObject" argument must be of type object. Received ${valueType(pathObject)}` | ||
); | ||
} | ||
const dir = pathObject.dir || pathObject.root; | ||
const base = pathObject.base || `${pathObject.name || ''}${pathObject.ext || ''}`; | ||
if (!dir) { | ||
return base; | ||
} | ||
return dir === pathObject.root ? `${dir}${base}` : `${dir}${sep}${base}`; | ||
} | ||
const win32 = { | ||
// path.resolve([from ...], to) | ||
resolve(...args) { | ||
let resolvedDevice = ''; | ||
let resolvedTail = ''; | ||
let resolvedAbsolute = false; | ||
// path.resolve([from ...], to) | ||
resolve(...args) { | ||
let resolvedDevice = ''; | ||
let resolvedTail = ''; | ||
let resolvedAbsolute = false; | ||
for (let i = args.length - 1; i >= -1; i--) { | ||
let path; | ||
if (i >= 0) { | ||
path = args[i]; | ||
validateString(path, 'path'); | ||
for (let i = args.length - 1; i >= -1; i--) { | ||
let path; | ||
if (i >= 0) { | ||
path = args[i]; | ||
validateString(path, 'path'); | ||
// Skip empty entries | ||
if (path.length === 0) { | ||
continue; | ||
} | ||
} else if (resolvedDevice.length === 0) { | ||
path = process.cwd(); | ||
} else { | ||
// Windows has the concept of drive-specific current working | ||
// directories. If we've resolved a drive letter but not yet an | ||
// absolute path, get cwd for that drive, or the process cwd if | ||
// the drive cwd is not available. We're sure the device is not | ||
// a UNC path at this points, because UNC paths are always absolute. | ||
path = process.env[`=${resolvedDevice}`] || process.cwd(); | ||
// Skip empty entries | ||
if (path.length === 0) { | ||
continue; | ||
} | ||
} else if (resolvedDevice.length === 0) { | ||
path = process.cwd(); | ||
} else { | ||
// Windows has the concept of drive-specific current working | ||
// directories. If we've resolved a drive letter but not yet an | ||
// absolute path, get cwd for that drive, or the process cwd if | ||
// the drive cwd is not available. We're sure the device is not | ||
// a UNC path at this points, because UNC paths are always absolute. | ||
path = process.env[`=${resolvedDevice}`] || process.cwd(); | ||
// Verify that a cwd was found and that it actually points | ||
// to our drive. If not, default to the drive's root. | ||
if ( | ||
path === undefined || | ||
(path.slice(0, 2).toLowerCase() !== resolvedDevice.toLowerCase() && | ||
path.charCodeAt(2) === CHAR_BACKWARD_SLASH) | ||
) { | ||
path = `${resolvedDevice}\\`; | ||
} | ||
} | ||
// Verify that a cwd was found and that it actually points | ||
// to our drive. If not, default to the drive's root. | ||
if ( | ||
path === undefined || | ||
(path.slice(0, 2).toLowerCase() !== resolvedDevice.toLowerCase() && | ||
path.charCodeAt(2) === CHAR_BACKWARD_SLASH) | ||
) { | ||
path = `${resolvedDevice}\\`; | ||
} | ||
} | ||
const len = path.length; | ||
let rootEnd = 0; | ||
let device = ''; | ||
let isAbsolute = false; | ||
const code = path.charCodeAt(0); | ||
const len = path.length; | ||
let rootEnd = 0; | ||
let device = ''; | ||
let isAbsolute = false; | ||
const code = path.charCodeAt(0); | ||
// Try to match a root | ||
if (len === 1) { | ||
if (isPathSeparator(code)) { | ||
// `path` contains just a path separator | ||
rootEnd = 1; | ||
isAbsolute = true; | ||
} | ||
} else if (isPathSeparator(code)) { | ||
// Possible UNC root | ||
// Try to match a root | ||
if (len === 1) { | ||
if (isPathSeparator(code)) { | ||
// `path` contains just a path separator | ||
rootEnd = 1; | ||
isAbsolute = true; | ||
} | ||
} else if (isPathSeparator(code)) { | ||
// Possible UNC root | ||
// If we started with a separator, we know we at least have an | ||
// absolute path of some kind (UNC or otherwise) | ||
isAbsolute = true; | ||
// If we started with a separator, we know we at least have an | ||
// absolute path of some kind (UNC or otherwise) | ||
isAbsolute = true; | ||
if (isPathSeparator(path.charCodeAt(1))) { | ||
// Matched double path separator at beginning | ||
let j = 2; | ||
let last = j; | ||
// Match 1 or more non-path separators | ||
while (j < len && !isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j < len && j !== last) { | ||
const firstPart = path.slice(last, j); | ||
// Matched! | ||
last = j; | ||
// Match 1 or more path separators | ||
while (j < len && isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j < len && j !== last) { | ||
// Matched! | ||
last = j; | ||
// Match 1 or more non-path separators | ||
while (j < len && !isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j === len || j !== last) { | ||
// We matched a UNC root | ||
device = `\\\\${firstPart}\\${path.slice(last, j)}`; | ||
rootEnd = j; | ||
} | ||
} | ||
} | ||
} else { | ||
rootEnd = 1; | ||
} | ||
} else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { | ||
// Possible device root | ||
device = path.slice(0, 2); | ||
rootEnd = 2; | ||
if (len > 2 && isPathSeparator(path.charCodeAt(2))) { | ||
// Treat separator following drive name as an absolute path | ||
// indicator | ||
isAbsolute = true; | ||
rootEnd = 3; | ||
} | ||
if (isPathSeparator(path.charCodeAt(1))) { | ||
// Matched double path separator at beginning | ||
let j = 2; | ||
let last = j; | ||
// Match 1 or more non-path separators | ||
while (j < len && !isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j < len && j !== last) { | ||
const firstPart = path.slice(last, j); | ||
// Matched! | ||
last = j; | ||
// Match 1 or more path separators | ||
while (j < len && isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (device.length > 0) { | ||
if (resolvedDevice.length > 0) { | ||
if (device.toLowerCase() !== resolvedDevice.toLowerCase()) | ||
// This path points to another device so it is not applicable | ||
continue; | ||
} else { | ||
resolvedDevice = device; | ||
} | ||
if (j < len && j !== last) { | ||
// Matched! | ||
last = j; | ||
// Match 1 or more non-path separators | ||
while (j < len && !isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j === len || j !== last) { | ||
// We matched a UNC root | ||
device = `\\\\${firstPart}\\${path.slice(last, j)}`; | ||
rootEnd = j; | ||
} | ||
} | ||
} | ||
} else { | ||
rootEnd = 1; | ||
} | ||
} else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { | ||
// Possible device root | ||
device = path.slice(0, 2); | ||
rootEnd = 2; | ||
if (len > 2 && isPathSeparator(path.charCodeAt(2))) { | ||
// Treat separator following drive name as an absolute path | ||
// indicator | ||
isAbsolute = true; | ||
rootEnd = 3; | ||
} | ||
} | ||
if (resolvedAbsolute) { | ||
if (resolvedDevice.length > 0) break; | ||
} else { | ||
resolvedTail = `${path.slice(rootEnd)}\\${resolvedTail}`; | ||
resolvedAbsolute = isAbsolute; | ||
if (isAbsolute && resolvedDevice.length > 0) { | ||
break; | ||
} | ||
} | ||
if (device.length > 0) { | ||
if (resolvedDevice.length > 0) { | ||
if (device.toLowerCase() !== resolvedDevice.toLowerCase()) | ||
// This path points to another device so it is not applicable | ||
continue; | ||
} else { | ||
resolvedDevice = device; | ||
} | ||
} | ||
// 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) | ||
if (resolvedAbsolute) { | ||
if (resolvedDevice.length > 0) break; | ||
} else { | ||
resolvedTail = `${path.slice(rootEnd)}\\${resolvedTail}`; | ||
resolvedAbsolute = isAbsolute; | ||
if (isAbsolute && resolvedDevice.length > 0) { | ||
break; | ||
} | ||
} | ||
} | ||
// Normalize the tail path | ||
resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, '\\', isPathSeparator); | ||
// 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) | ||
return resolvedAbsolute ? `${resolvedDevice}\\${resolvedTail}` : `${resolvedDevice}${resolvedTail}` || '.'; | ||
}, | ||
// Normalize the tail path | ||
resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, '\\', isPathSeparator); | ||
normalize(path) { | ||
validateString(path, 'path'); | ||
const len = path.length; | ||
if (len === 0) return '.'; | ||
let rootEnd = 0; | ||
let device; | ||
let isAbsolute = false; | ||
const code = path.charCodeAt(0); | ||
return resolvedAbsolute ? `${resolvedDevice}\\${resolvedTail}` : `${resolvedDevice}${resolvedTail}` || '.'; | ||
}, | ||
// Try to match a root | ||
if (len === 1) { | ||
// `path` contains just a single char, exit early to avoid | ||
// unnecessary work | ||
return isPosixPathSeparator(code) ? '\\' : path; | ||
} | ||
if (isPathSeparator(code)) { | ||
// Possible UNC root | ||
normalize(path) { | ||
validateString(path, 'path'); | ||
const len = path.length; | ||
if (len === 0) return '.'; | ||
let rootEnd = 0; | ||
let device; | ||
let isAbsolute = false; | ||
const code = path.charCodeAt(0); | ||
// If we started with a separator, we know we at least have an absolute | ||
// path of some kind (UNC or otherwise) | ||
isAbsolute = true; | ||
// Try to match a root | ||
if (len === 1) { | ||
// `path` contains just a single char, exit early to avoid | ||
// unnecessary work | ||
return isPosixPathSeparator(code) ? '\\' : path; | ||
} | ||
if (isPathSeparator(code)) { | ||
// Possible UNC root | ||
if (isPathSeparator(path.charCodeAt(1))) { | ||
// Matched double path separator at beginning | ||
let j = 2; | ||
let last = j; | ||
// Match 1 or more non-path separators | ||
while (j < len && !isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j < len && j !== last) { | ||
const firstPart = path.slice(last, j); | ||
// Matched! | ||
last = j; | ||
// Match 1 or more path separators | ||
while (j < len && isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j < len && j !== last) { | ||
// Matched! | ||
last = j; | ||
// Match 1 or more non-path separators | ||
while (j < len && !isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j === len) { | ||
// We matched a UNC root only | ||
// Return the normalized version of the UNC root since there | ||
// is nothing left to process | ||
return `\\\\${firstPart}\\${path.slice(last)}\\`; | ||
} | ||
if (j !== last) { | ||
// We matched a UNC root with leftovers | ||
device = `\\\\${firstPart}\\${path.slice(last, j)}`; | ||
rootEnd = j; | ||
} | ||
} | ||
} | ||
} else { | ||
rootEnd = 1; | ||
// If we started with a separator, we know we at least have an absolute | ||
// path of some kind (UNC or otherwise) | ||
isAbsolute = true; | ||
if (isPathSeparator(path.charCodeAt(1))) { | ||
// Matched double path separator at beginning | ||
let j = 2; | ||
let last = j; | ||
// Match 1 or more non-path separators | ||
while (j < len && !isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j < len && j !== last) { | ||
const firstPart = path.slice(last, j); | ||
// Matched! | ||
last = j; | ||
// Match 1 or more path separators | ||
while (j < len && isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j < len && j !== last) { | ||
// Matched! | ||
last = j; | ||
// Match 1 or more non-path separators | ||
while (j < len && !isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
} else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { | ||
// Possible device root | ||
device = path.slice(0, 2); | ||
rootEnd = 2; | ||
if (len > 2 && isPathSeparator(path.charCodeAt(2))) { | ||
// Treat separator following drive name as an absolute path | ||
// indicator | ||
isAbsolute = true; | ||
rootEnd = 3; | ||
if (j === len) { | ||
// We matched a UNC root only | ||
// Return the normalized version of the UNC root since there | ||
// is nothing left to process | ||
return `\\\\${firstPart}\\${path.slice(last)}\\`; | ||
} | ||
if (j !== last) { | ||
// We matched a UNC root with leftovers | ||
device = `\\\\${firstPart}\\${path.slice(last, j)}`; | ||
rootEnd = j; | ||
} | ||
} | ||
} | ||
} else { | ||
rootEnd = 1; | ||
} | ||
} else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { | ||
// Possible device root | ||
device = path.slice(0, 2); | ||
rootEnd = 2; | ||
if (len > 2 && isPathSeparator(path.charCodeAt(2))) { | ||
// Treat separator following drive name as an absolute path | ||
// indicator | ||
isAbsolute = true; | ||
rootEnd = 3; | ||
} | ||
} | ||
let tail = rootEnd < len ? normalizeString(path.slice(rootEnd), !isAbsolute, '\\', isPathSeparator) : ''; | ||
if (tail.length === 0 && !isAbsolute) tail = '.'; | ||
if (tail.length > 0 && isPathSeparator(path.charCodeAt(len - 1))) tail += '\\'; | ||
if (device === undefined) { | ||
return isAbsolute ? `\\${tail}` : tail; | ||
} | ||
return isAbsolute ? `${device}\\${tail}` : `${device}${tail}`; | ||
}, | ||
let tail = rootEnd < len ? normalizeString(path.slice(rootEnd), !isAbsolute, '\\', isPathSeparator) : ''; | ||
if (tail.length === 0 && !isAbsolute) tail = '.'; | ||
if (tail.length > 0 && isPathSeparator(path.charCodeAt(len - 1))) tail += '\\'; | ||
if (device === undefined) { | ||
return isAbsolute ? `\\${tail}` : tail; | ||
} | ||
return isAbsolute ? `${device}\\${tail}` : `${device}${tail}`; | ||
}, | ||
isAbsolute(path) { | ||
validateString(path, 'path'); | ||
const len = path.length; | ||
if (len === 0) return false; | ||
isAbsolute(path) { | ||
validateString(path, 'path'); | ||
const len = path.length; | ||
if (len === 0) return false; | ||
const code = path.charCodeAt(0); | ||
return ( | ||
isPathSeparator(code) || | ||
// Possible device root | ||
(len > 2 && | ||
isWindowsDeviceRoot(code) && | ||
path.charCodeAt(1) === CHAR_COLON && | ||
isPathSeparator(path.charCodeAt(2))) | ||
); | ||
}, | ||
const code = path.charCodeAt(0); | ||
return ( | ||
isPathSeparator(code) || | ||
// Possible device root | ||
(len > 2 && isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON && isPathSeparator(path.charCodeAt(2))) | ||
); | ||
}, | ||
join(...args) { | ||
if (args.length === 0) return '.'; | ||
join(...args) { | ||
if (args.length === 0) return '.'; | ||
let joined; | ||
let firstPart; | ||
for (let i = 0; i < args.length; ++i) { | ||
const arg = args[i]; | ||
validateString(arg, 'path'); | ||
if (arg.length > 0) { | ||
if (joined === undefined) joined = firstPart = arg; | ||
else joined += `\\${arg}`; | ||
} | ||
} | ||
let joined; | ||
let firstPart; | ||
for (let i = 0; i < args.length; ++i) { | ||
const arg = args[i]; | ||
validateString(arg, 'path'); | ||
if (arg.length > 0) { | ||
if (joined === undefined) joined = firstPart = arg; | ||
else joined += `\\${arg}`; | ||
} | ||
} | ||
if (joined === undefined) return '.'; | ||
if (joined === undefined) return '.'; | ||
// Make sure that the joined path doesn't start with two slashes, because | ||
// normalize() will mistake it for an UNC path then. | ||
// | ||
// This step is skipped when it is very clear that the user actually | ||
// intended to point at an UNC path. This is assumed when the first | ||
// non-empty string arguments starts with exactly two slashes followed by | ||
// at least one more non-slash character. | ||
// | ||
// Note that for normalize() to treat a path as an UNC path it needs to | ||
// have at least 2 components, so we don't filter for that here. | ||
// This means that the user can use join to construct UNC paths from | ||
// a server name and a share name; for example: | ||
// path.join('//server', 'share') -> '\\\\server\\share\\') | ||
let needsReplace = true; | ||
let slashCount = 0; | ||
if (isPathSeparator(firstPart.charCodeAt(0))) { | ||
++slashCount; | ||
const firstLen = firstPart.length; | ||
if (firstLen > 1 && isPathSeparator(firstPart.charCodeAt(1))) { | ||
++slashCount; | ||
if (firstLen > 2) { | ||
if (isPathSeparator(firstPart.charCodeAt(2))) ++slashCount; | ||
else { | ||
// We matched a UNC path in the first part | ||
needsReplace = false; | ||
} | ||
} | ||
} | ||
// Make sure that the joined path doesn't start with two slashes, because | ||
// normalize() will mistake it for an UNC path then. | ||
// | ||
// This step is skipped when it is very clear that the user actually | ||
// intended to point at an UNC path. This is assumed when the first | ||
// non-empty string arguments starts with exactly two slashes followed by | ||
// at least one more non-slash character. | ||
// | ||
// Note that for normalize() to treat a path as an UNC path it needs to | ||
// have at least 2 components, so we don't filter for that here. | ||
// This means that the user can use join to construct UNC paths from | ||
// a server name and a share name; for example: | ||
// path.join('//server', 'share') -> '\\\\server\\share\\') | ||
let needsReplace = true; | ||
let slashCount = 0; | ||
if (isPathSeparator(firstPart.charCodeAt(0))) { | ||
++slashCount; | ||
const firstLen = firstPart.length; | ||
if (firstLen > 1 && isPathSeparator(firstPart.charCodeAt(1))) { | ||
++slashCount; | ||
if (firstLen > 2) { | ||
if (isPathSeparator(firstPart.charCodeAt(2))) ++slashCount; | ||
else { | ||
// We matched a UNC path in the first part | ||
needsReplace = false; | ||
} | ||
} | ||
if (needsReplace) { | ||
// Find any more consecutive slashes we need to replace | ||
while (slashCount < joined.length && isPathSeparator(joined.charCodeAt(slashCount))) { | ||
slashCount++; | ||
} | ||
} | ||
} | ||
if (needsReplace) { | ||
// Find any more consecutive slashes we need to replace | ||
while (slashCount < joined.length && isPathSeparator(joined.charCodeAt(slashCount))) { | ||
slashCount++; | ||
} | ||
// Replace the slashes if needed | ||
if (slashCount >= 2) joined = `\\${joined.slice(slashCount)}`; | ||
} | ||
// Replace the slashes if needed | ||
if (slashCount >= 2) joined = `\\${joined.slice(slashCount)}`; | ||
} | ||
return win32.normalize(joined); | ||
}, | ||
return win32.normalize(joined); | ||
}, | ||
// 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' | ||
relative(from, to) { | ||
validateString(from, 'from'); | ||
validateString(to, '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' | ||
relative(from, to) { | ||
validateString(from, 'from'); | ||
validateString(to, 'to'); | ||
if (from === to) return ''; | ||
if (from === to) return ''; | ||
const fromOrig = win32.resolve(from); | ||
const toOrig = win32.resolve(to); | ||
const fromOrig = win32.resolve(from); | ||
const toOrig = win32.resolve(to); | ||
if (fromOrig === toOrig) return ''; | ||
if (fromOrig === toOrig) return ''; | ||
from = fromOrig.toLowerCase(); | ||
to = toOrig.toLowerCase(); | ||
from = fromOrig.toLowerCase(); | ||
to = toOrig.toLowerCase(); | ||
if (from === to) return ''; | ||
if (from === to) return ''; | ||
// Trim any leading backslashes | ||
let fromStart = 0; | ||
while (fromStart < from.length && from.charCodeAt(fromStart) === CHAR_BACKWARD_SLASH) { | ||
fromStart++; | ||
} | ||
// Trim trailing backslashes (applicable to UNC paths only) | ||
let fromEnd = from.length; | ||
while (fromEnd - 1 > fromStart && from.charCodeAt(fromEnd - 1) === CHAR_BACKWARD_SLASH) { | ||
fromEnd--; | ||
} | ||
const fromLen = fromEnd - fromStart; | ||
// Trim any leading backslashes | ||
let fromStart = 0; | ||
while (fromStart < from.length && from.charCodeAt(fromStart) === CHAR_BACKWARD_SLASH) { | ||
fromStart++; | ||
} | ||
// Trim trailing backslashes (applicable to UNC paths only) | ||
let fromEnd = from.length; | ||
while (fromEnd - 1 > fromStart && from.charCodeAt(fromEnd - 1) === CHAR_BACKWARD_SLASH) { | ||
fromEnd--; | ||
} | ||
const fromLen = fromEnd - fromStart; | ||
// Trim any leading backslashes | ||
let toStart = 0; | ||
while (toStart < to.length && to.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) { | ||
toStart++; | ||
// Trim any leading backslashes | ||
let toStart = 0; | ||
while (toStart < to.length && to.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) { | ||
toStart++; | ||
} | ||
// Trim trailing backslashes (applicable to UNC paths only) | ||
let toEnd = to.length; | ||
while (toEnd - 1 > toStart && to.charCodeAt(toEnd - 1) === CHAR_BACKWARD_SLASH) { | ||
toEnd--; | ||
} | ||
const toLen = toEnd - toStart; | ||
// Compare paths to find the longest common path from root | ||
const length = fromLen < toLen ? fromLen : toLen; | ||
let lastCommonSep = -1; | ||
let i = 0; | ||
for (; i < length; i++) { | ||
const fromCode = from.charCodeAt(fromStart + i); | ||
if (fromCode !== to.charCodeAt(toStart + i)) break; | ||
else if (fromCode === CHAR_BACKWARD_SLASH) lastCommonSep = i; | ||
} | ||
// We found a mismatch before the first common path separator was seen, so | ||
// return the original `to`. | ||
if (i !== length) { | ||
if (lastCommonSep === -1) return toOrig; | ||
} else { | ||
if (toLen > length) { | ||
if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) { | ||
// We get here if `from` is the exact base path for `to`. | ||
// For example: from='C:\\foo\\bar'; to='C:\\foo\\bar\\baz' | ||
return toOrig.slice(toStart + i + 1); | ||
} | ||
// Trim trailing backslashes (applicable to UNC paths only) | ||
let toEnd = to.length; | ||
while (toEnd - 1 > toStart && to.charCodeAt(toEnd - 1) === CHAR_BACKWARD_SLASH) { | ||
toEnd--; | ||
if (i === 2) { | ||
// We get here if `from` is the device root. | ||
// For example: from='C:\\'; to='C:\\foo' | ||
return toOrig.slice(toStart + i); | ||
} | ||
const toLen = toEnd - toStart; | ||
// Compare paths to find the longest common path from root | ||
const length = fromLen < toLen ? fromLen : toLen; | ||
let lastCommonSep = -1; | ||
let i = 0; | ||
for (; i < length; i++) { | ||
const fromCode = from.charCodeAt(fromStart + i); | ||
if (fromCode !== to.charCodeAt(toStart + i)) break; | ||
else if (fromCode === CHAR_BACKWARD_SLASH) lastCommonSep = i; | ||
} | ||
if (fromLen > length) { | ||
if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) { | ||
// We get here if `to` is the exact base path for `from`. | ||
// For example: from='C:\\foo\\bar'; to='C:\\foo' | ||
lastCommonSep = i; | ||
} else if (i === 2) { | ||
// We get here if `to` is the device root. | ||
// For example: from='C:\\foo\\bar'; to='C:\\' | ||
lastCommonSep = 3; | ||
} | ||
} | ||
if (lastCommonSep === -1) lastCommonSep = 0; | ||
} | ||
// We found a mismatch before the first common path separator was seen, so | ||
// return the original `to`. | ||
if (i !== length) { | ||
if (lastCommonSep === -1) return toOrig; | ||
} else { | ||
if (toLen > length) { | ||
if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) { | ||
// We get here if `from` is the exact base path for `to`. | ||
// For example: from='C:\\foo\\bar'; to='C:\\foo\\bar\\baz' | ||
return toOrig.slice(toStart + i + 1); | ||
} | ||
if (i === 2) { | ||
// We get here if `from` is the device root. | ||
// For example: from='C:\\'; to='C:\\foo' | ||
return toOrig.slice(toStart + i); | ||
} | ||
} | ||
if (fromLen > length) { | ||
if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) { | ||
// We get here if `to` is the exact base path for `from`. | ||
// For example: from='C:\\foo\\bar'; to='C:\\foo' | ||
lastCommonSep = i; | ||
} else if (i === 2) { | ||
// We get here if `to` is the device root. | ||
// For example: from='C:\\foo\\bar'; to='C:\\' | ||
lastCommonSep = 3; | ||
} | ||
} | ||
if (lastCommonSep === -1) lastCommonSep = 0; | ||
} | ||
let out = ''; | ||
// Generate the relative path based on the path difference between `to` and | ||
// `from` | ||
for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { | ||
if (i === fromEnd || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) { | ||
out += out.length === 0 ? '..' : '\\..'; | ||
} | ||
} | ||
let out = ''; | ||
// Generate the relative path based on the path difference between `to` and | ||
// `from` | ||
for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { | ||
if (i === fromEnd || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) { | ||
out += out.length === 0 ? '..' : '\\..'; | ||
} | ||
} | ||
toStart += lastCommonSep; | ||
toStart += lastCommonSep; | ||
// Lastly, append the rest of the destination (`to`) path that comes after | ||
// the common path parts | ||
if (out.length > 0) return `${out}${toOrig.slice(toStart, toEnd)}`; | ||
// Lastly, append the rest of the destination (`to`) path that comes after | ||
// the common path parts | ||
if (out.length > 0) return `${out}${toOrig.slice(toStart, toEnd)}`; | ||
if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) ++toStart; | ||
return toOrig.slice(toStart, toEnd); | ||
}, | ||
if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) ++toStart; | ||
return toOrig.slice(toStart, toEnd); | ||
}, | ||
toNamespacedPath(path) { | ||
// Note: this will *probably* throw somewhere. | ||
if (typeof path !== 'string') return path; | ||
toNamespacedPath(path) { | ||
// Note: this will *probably* throw somewhere. | ||
if (typeof path !== 'string') return path; | ||
if (path.length === 0) { | ||
return ''; | ||
} | ||
if (path.length === 0) { | ||
return ''; | ||
} | ||
const resolvedPath = win32.resolve(path); | ||
const resolvedPath = win32.resolve(path); | ||
if (resolvedPath.length <= 2) return path; | ||
if (resolvedPath.length <= 2) return path; | ||
if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) { | ||
// Possible UNC root | ||
if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) { | ||
const code = resolvedPath.charCodeAt(2); | ||
if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) { | ||
// Matched non-long UNC root, convert the path to a long UNC path | ||
return `\\\\?\\UNC\\${resolvedPath.slice(2)}`; | ||
} | ||
} | ||
} else if ( | ||
isWindowsDeviceRoot(resolvedPath.charCodeAt(0)) && | ||
resolvedPath.charCodeAt(1) === CHAR_COLON && | ||
resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH | ||
) { | ||
// Matched device root, convert the path to a long UNC path | ||
return `\\\\?\\${resolvedPath}`; | ||
if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) { | ||
// Possible UNC root | ||
if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) { | ||
const code = resolvedPath.charCodeAt(2); | ||
if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) { | ||
// Matched non-long UNC root, convert the path to a long UNC path | ||
return `\\\\?\\UNC\\${resolvedPath.slice(2)}`; | ||
} | ||
} | ||
} else if ( | ||
isWindowsDeviceRoot(resolvedPath.charCodeAt(0)) && | ||
resolvedPath.charCodeAt(1) === CHAR_COLON && | ||
resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH | ||
) { | ||
// Matched device root, convert the path to a long UNC path | ||
return `\\\\?\\${resolvedPath}`; | ||
} | ||
return path; | ||
}, | ||
return path; | ||
}, | ||
dirname(path) { | ||
validateString(path, 'path'); | ||
const len = path.length; | ||
if (len === 0) return '.'; | ||
let rootEnd = -1; | ||
let offset = 0; | ||
const code = path.charCodeAt(0); | ||
dirname(path) { | ||
validateString(path, 'path'); | ||
const len = path.length; | ||
if (len === 0) return '.'; | ||
let rootEnd = -1; | ||
let offset = 0; | ||
const code = path.charCodeAt(0); | ||
if (len === 1) { | ||
// `path` contains just a path separator, exit early to avoid | ||
// unnecessary work or a dot. | ||
return isPathSeparator(code) ? path : '.'; | ||
} | ||
if (len === 1) { | ||
// `path` contains just a path separator, exit early to avoid | ||
// unnecessary work or a dot. | ||
return isPathSeparator(code) ? path : '.'; | ||
} | ||
// Try to match a root | ||
if (isPathSeparator(code)) { | ||
// Possible UNC root | ||
// Try to match a root | ||
if (isPathSeparator(code)) { | ||
// Possible UNC root | ||
rootEnd = offset = 1; | ||
rootEnd = offset = 1; | ||
if (isPathSeparator(path.charCodeAt(1))) { | ||
// Matched double path separator at beginning | ||
let j = 2; | ||
let last = j; | ||
// Match 1 or more non-path separators | ||
while (j < len && !isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j < len && j !== last) { | ||
// Matched! | ||
last = j; | ||
// Match 1 or more path separators | ||
while (j < len && isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j < len && j !== last) { | ||
// Matched! | ||
last = j; | ||
// Match 1 or more non-path separators | ||
while (j < len && !isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j === len) { | ||
// We matched a UNC root only | ||
return path; | ||
} | ||
if (j !== last) { | ||
// We matched a UNC root with leftovers | ||
if (isPathSeparator(path.charCodeAt(1))) { | ||
// Matched double path separator at beginning | ||
let j = 2; | ||
let last = j; | ||
// Match 1 or more non-path separators | ||
while (j < len && !isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j < len && j !== last) { | ||
// Matched! | ||
last = j; | ||
// Match 1 or more path separators | ||
while (j < len && isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j < len && j !== last) { | ||
// Matched! | ||
last = j; | ||
// Match 1 or more non-path separators | ||
while (j < len && !isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j === len) { | ||
// We matched a UNC root only | ||
return path; | ||
} | ||
if (j !== last) { | ||
// We matched a UNC root with leftovers | ||
// Offset by 1 to include the separator after the UNC root to | ||
// treat it as a "normal root" on top of a (UNC) root | ||
rootEnd = offset = j + 1; | ||
} | ||
} | ||
} | ||
// Offset by 1 to include the separator after the UNC root to | ||
// treat it as a "normal root" on top of a (UNC) root | ||
rootEnd = offset = j + 1; | ||
} | ||
// Possible device root | ||
} else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { | ||
rootEnd = len > 2 && isPathSeparator(path.charCodeAt(2)) ? 3 : 2; | ||
offset = rootEnd; | ||
} | ||
} | ||
} | ||
// Possible device root | ||
} else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { | ||
rootEnd = len > 2 && isPathSeparator(path.charCodeAt(2)) ? 3 : 2; | ||
offset = rootEnd; | ||
} | ||
let end = -1; | ||
let matchedSlash = true; | ||
for (let i = len - 1; i >= offset; --i) { | ||
if (isPathSeparator(path.charCodeAt(i))) { | ||
if (!matchedSlash) { | ||
end = i; | ||
break; | ||
} | ||
} else { | ||
// We saw the first non-path separator | ||
matchedSlash = false; | ||
} | ||
let end = -1; | ||
let matchedSlash = true; | ||
for (let i = len - 1; i >= offset; --i) { | ||
if (isPathSeparator(path.charCodeAt(i))) { | ||
if (!matchedSlash) { | ||
end = i; | ||
break; | ||
} | ||
} else { | ||
// We saw the first non-path separator | ||
matchedSlash = false; | ||
} | ||
} | ||
if (end === -1) { | ||
if (rootEnd === -1) return '.'; | ||
if (end === -1) { | ||
if (rootEnd === -1) return '.'; | ||
end = rootEnd; | ||
} | ||
return path.slice(0, end); | ||
}, | ||
end = rootEnd; | ||
} | ||
return path.slice(0, end); | ||
}, | ||
basename(path, ext) { | ||
if (ext !== undefined) validateString(ext, 'ext'); | ||
validateString(path, 'path'); | ||
let start = 0; | ||
let end = -1; | ||
let matchedSlash = true; | ||
basename(path, ext) { | ||
if (ext !== undefined) validateString(ext, 'ext'); | ||
validateString(path, 'path'); | ||
let start = 0; | ||
let end = -1; | ||
let matchedSlash = true; | ||
// Check for a drive letter prefix so as not to mistake the following | ||
// path separator as an extra separator at the end of the path that can be | ||
// disregarded | ||
if (path.length >= 2 && isWindowsDeviceRoot(path.charCodeAt(0)) && path.charCodeAt(1) === CHAR_COLON) { | ||
start = 2; | ||
} | ||
// Check for a drive letter prefix so as not to mistake the following | ||
// path separator as an extra separator at the end of the path that can be | ||
// disregarded | ||
if (path.length >= 2 && isWindowsDeviceRoot(path.charCodeAt(0)) && path.charCodeAt(1) === CHAR_COLON) { | ||
start = 2; | ||
} | ||
if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { | ||
if (ext === path) return ''; | ||
let extIdx = ext.length - 1; | ||
let firstNonSlashEnd = -1; | ||
for (let i = path.length - 1; i >= start; --i) { | ||
const code = path.charCodeAt(i); | ||
if (isPathSeparator(code)) { | ||
// If we reached a path separator that was not part of a set of path | ||
// separators at the end of the string, stop now | ||
if (!matchedSlash) { | ||
start = i + 1; | ||
break; | ||
} | ||
} else { | ||
if (firstNonSlashEnd === -1) { | ||
// We saw the first non-path separator, remember this index in case | ||
// we need it if the extension ends up not matching | ||
matchedSlash = false; | ||
firstNonSlashEnd = i + 1; | ||
} | ||
if (extIdx >= 0) { | ||
// Try to match the explicit extension | ||
if (code === ext.charCodeAt(extIdx)) { | ||
if (--extIdx === -1) { | ||
// We matched the extension, so mark this as the end of our path | ||
// component | ||
end = i; | ||
} | ||
} else { | ||
// Extension does not match, so our result is the entire path | ||
// component | ||
extIdx = -1; | ||
end = firstNonSlashEnd; | ||
} | ||
} | ||
} | ||
if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { | ||
if (ext === path) return ''; | ||
let extIdx = ext.length - 1; | ||
let firstNonSlashEnd = -1; | ||
for (let i = path.length - 1; i >= start; --i) { | ||
const code = path.charCodeAt(i); | ||
if (isPathSeparator(code)) { | ||
// If we reached a path separator that was not part of a set of path | ||
// separators at the end of the string, stop now | ||
if (!matchedSlash) { | ||
start = i + 1; | ||
break; | ||
} | ||
} else { | ||
if (firstNonSlashEnd === -1) { | ||
// We saw the first non-path separator, remember this index in case | ||
// we need it if the extension ends up not matching | ||
matchedSlash = false; | ||
firstNonSlashEnd = i + 1; | ||
} | ||
if (extIdx >= 0) { | ||
// Try to match the explicit extension | ||
if (code === ext.charCodeAt(extIdx)) { | ||
if (--extIdx === -1) { | ||
// We matched the extension, so mark this as the end of our path | ||
// component | ||
end = i; | ||
} | ||
} else { | ||
// Extension does not match, so our result is the entire path | ||
// component | ||
extIdx = -1; | ||
end = firstNonSlashEnd; | ||
} | ||
} | ||
} | ||
} | ||
if (start === end) end = firstNonSlashEnd; | ||
else if (end === -1) end = path.length; | ||
return path.slice(start, end); | ||
if (start === end) end = firstNonSlashEnd; | ||
else if (end === -1) end = path.length; | ||
return path.slice(start, end); | ||
} | ||
for (let i = path.length - 1; i >= start; --i) { | ||
if (isPathSeparator(path.charCodeAt(i))) { | ||
// If we reached a path separator that was not part of a set of path | ||
// separators at the end of the string, stop now | ||
if (!matchedSlash) { | ||
start = i + 1; | ||
break; | ||
} | ||
for (let i = path.length - 1; i >= start; --i) { | ||
if (isPathSeparator(path.charCodeAt(i))) { | ||
// If we reached a path separator that was not part of a set of path | ||
// separators at the end of the string, stop now | ||
if (!matchedSlash) { | ||
start = i + 1; | ||
break; | ||
} | ||
} else if (end === -1) { | ||
// We saw the first non-path separator, mark this as the end of our | ||
// path component | ||
matchedSlash = false; | ||
end = i + 1; | ||
} | ||
} | ||
} else if (end === -1) { | ||
// We saw the first non-path separator, mark this as the end of our | ||
// path component | ||
matchedSlash = false; | ||
end = i + 1; | ||
} | ||
} | ||
if (end === -1) return ''; | ||
return path.slice(start, end); | ||
}, | ||
if (end === -1) return ''; | ||
return path.slice(start, end); | ||
}, | ||
extname(path) { | ||
validateString(path, 'path'); | ||
let start = 0; | ||
let startDot = -1; | ||
let startPart = 0; | ||
let end = -1; | ||
let matchedSlash = true; | ||
// Track the state of characters (if any) we see before our first dot and | ||
// after any path separator we find | ||
let preDotState = 0; | ||
extname(path) { | ||
validateString(path, 'path'); | ||
let start = 0; | ||
let startDot = -1; | ||
let startPart = 0; | ||
let end = -1; | ||
let matchedSlash = true; | ||
// Track the state of characters (if any) we see before our first dot and | ||
// after any path separator we find | ||
let preDotState = 0; | ||
// Check for a drive letter prefix so as not to mistake the following | ||
// path separator as an extra separator at the end of the path that can be | ||
// disregarded | ||
// Check for a drive letter prefix so as not to mistake the following | ||
// path separator as an extra separator at the end of the path that can be | ||
// disregarded | ||
if (path.length >= 2 && path.charCodeAt(1) === CHAR_COLON && isWindowsDeviceRoot(path.charCodeAt(0))) { | ||
start = startPart = 2; | ||
} | ||
if (path.length >= 2 && path.charCodeAt(1) === CHAR_COLON && isWindowsDeviceRoot(path.charCodeAt(0))) { | ||
start = startPart = 2; | ||
} | ||
for (let i = path.length - 1; i >= start; --i) { | ||
const code = path.charCodeAt(i); | ||
if (isPathSeparator(code)) { | ||
// If we reached a path separator that was not part of a set of path | ||
// separators at the end of the string, stop now | ||
if (!matchedSlash) { | ||
startPart = i + 1; | ||
break; | ||
} | ||
continue; | ||
} | ||
if (end === -1) { | ||
// We saw the first non-path separator, mark this as the end of our | ||
// extension | ||
matchedSlash = false; | ||
end = i + 1; | ||
} | ||
if (code === CHAR_DOT) { | ||
// If this is our first dot, mark it as the start of our extension | ||
if (startDot === -1) startDot = i; | ||
else if (preDotState !== 1) preDotState = 1; | ||
} else if (startDot !== -1) { | ||
// We saw a non-dot and non-path separator before our dot, so we should | ||
// have a good chance at having a non-empty extension | ||
preDotState = -1; | ||
} | ||
for (let i = path.length - 1; i >= start; --i) { | ||
const code = path.charCodeAt(i); | ||
if (isPathSeparator(code)) { | ||
// If we reached a path separator that was not part of a set of path | ||
// separators at the end of the string, stop now | ||
if (!matchedSlash) { | ||
startPart = i + 1; | ||
break; | ||
} | ||
continue; | ||
} | ||
if (end === -1) { | ||
// We saw the first non-path separator, mark this as the end of our | ||
// extension | ||
matchedSlash = false; | ||
end = i + 1; | ||
} | ||
if (code === CHAR_DOT) { | ||
// If this is our first dot, mark it as the start of our extension | ||
if (startDot === -1) startDot = i; | ||
else if (preDotState !== 1) preDotState = 1; | ||
} else if (startDot !== -1) { | ||
// We saw a non-dot and non-path separator before our dot, so we should | ||
// have a good chance at having a non-empty extension | ||
preDotState = -1; | ||
} | ||
} | ||
if ( | ||
startDot === -1 || | ||
end === -1 || | ||
// We saw a non-dot character immediately before the dot | ||
preDotState === 0 || | ||
// The (right-most) trimmed path component is exactly '..' | ||
(preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) | ||
) { | ||
return ''; | ||
} | ||
return path.slice(startDot, end); | ||
}, | ||
if ( | ||
startDot === -1 || | ||
end === -1 || | ||
// We saw a non-dot character immediately before the dot | ||
preDotState === 0 || | ||
// The (right-most) trimmed path component is exactly '..' | ||
(preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) | ||
) { | ||
return ''; | ||
} | ||
return path.slice(startDot, end); | ||
}, | ||
format: _format.bind(null, '\\'), | ||
format: _format.bind(null, '\\'), | ||
parse(path) { | ||
validateString(path, 'path'); | ||
parse(path) { | ||
validateString(path, 'path'); | ||
const ret = { root: '', dir: '', base: '', ext: '', name: '' }; | ||
if (path.length === 0) return ret; | ||
const ret = { root: '', dir: '', base: '', ext: '', name: '' }; | ||
if (path.length === 0) return ret; | ||
const len = path.length; | ||
let rootEnd = 0; | ||
let code = path.charCodeAt(0); | ||
const len = path.length; | ||
let rootEnd = 0; | ||
let code = path.charCodeAt(0); | ||
if (len === 1) { | ||
if (isPathSeparator(code)) { | ||
// `path` contains just a path separator, exit early to avoid | ||
// unnecessary work | ||
ret.root = ret.dir = path; | ||
return ret; | ||
} | ||
ret.base = ret.name = path; | ||
return ret; | ||
if (len === 1) { | ||
if (isPathSeparator(code)) { | ||
// `path` contains just a path separator, exit early to avoid | ||
// unnecessary work | ||
ret.root = ret.dir = path; | ||
return ret; | ||
} | ||
ret.base = ret.name = path; | ||
return ret; | ||
} | ||
// Try to match a root | ||
if (isPathSeparator(code)) { | ||
// Possible UNC root | ||
rootEnd = 1; | ||
if (isPathSeparator(path.charCodeAt(1))) { | ||
// Matched double path separator at beginning | ||
let j = 2; | ||
let last = j; | ||
// Match 1 or more non-path separators | ||
while (j < len && !isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
// Try to match a root | ||
if (isPathSeparator(code)) { | ||
// Possible UNC root | ||
rootEnd = 1; | ||
if (isPathSeparator(path.charCodeAt(1))) { | ||
// Matched double path separator at beginning | ||
let j = 2; | ||
let last = j; | ||
// Match 1 or more non-path separators | ||
while (j < len && !isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j < len && j !== last) { | ||
// Matched! | ||
last = j; | ||
// Match 1 or more path separators | ||
while (j < len && isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j < len && j !== last) { | ||
// Matched! | ||
last = j; | ||
// Match 1 or more non-path separators | ||
while (j < len && !isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j === len) { | ||
// We matched a UNC root only | ||
rootEnd = j; | ||
} else if (j !== last) { | ||
// We matched a UNC root with leftovers | ||
rootEnd = j + 1; | ||
} | ||
} | ||
} | ||
if (j < len && j !== last) { | ||
// Matched! | ||
last = j; | ||
// Match 1 or more path separators | ||
while (j < len && isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
if (j < len && j !== last) { | ||
// Matched! | ||
last = j; | ||
// Match 1 or more non-path separators | ||
while (j < len && !isPathSeparator(path.charCodeAt(j))) { | ||
j++; | ||
} | ||
} else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { | ||
// Possible device root | ||
if (len <= 2) { | ||
// `path` contains just a drive root, exit early to avoid | ||
// unnecessary work | ||
ret.root = ret.dir = path; | ||
return ret; | ||
if (j === len) { | ||
// We matched a UNC root only | ||
rootEnd = j; | ||
} else if (j !== last) { | ||
// We matched a UNC root with leftovers | ||
rootEnd = j + 1; | ||
} | ||
rootEnd = 2; | ||
if (isPathSeparator(path.charCodeAt(2))) { | ||
if (len === 3) { | ||
// `path` contains just a drive root, exit early to avoid | ||
// unnecessary work | ||
ret.root = ret.dir = path; | ||
return ret; | ||
} | ||
rootEnd = 3; | ||
} | ||
} | ||
} | ||
if (rootEnd > 0) ret.root = path.slice(0, rootEnd); | ||
} | ||
} else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { | ||
// Possible device root | ||
if (len <= 2) { | ||
// `path` contains just a drive root, exit early to avoid | ||
// unnecessary work | ||
ret.root = ret.dir = path; | ||
return ret; | ||
} | ||
rootEnd = 2; | ||
if (isPathSeparator(path.charCodeAt(2))) { | ||
if (len === 3) { | ||
// `path` contains just a drive root, exit early to avoid | ||
// unnecessary work | ||
ret.root = ret.dir = path; | ||
return ret; | ||
} | ||
rootEnd = 3; | ||
} | ||
} | ||
if (rootEnd > 0) ret.root = path.slice(0, rootEnd); | ||
let startDot = -1; | ||
let startPart = rootEnd; | ||
let end = -1; | ||
let matchedSlash = true; | ||
let i = path.length - 1; | ||
let startDot = -1; | ||
let startPart = rootEnd; | ||
let end = -1; | ||
let matchedSlash = true; | ||
let i = path.length - 1; | ||
// Track the state of characters (if any) we see before our first dot and | ||
// after any path separator we find | ||
let preDotState = 0; | ||
// Track the state of characters (if any) we see before our first dot and | ||
// after any path separator we find | ||
let preDotState = 0; | ||
// Get non-dir info | ||
for (; i >= rootEnd; --i) { | ||
code = path.charCodeAt(i); | ||
if (isPathSeparator(code)) { | ||
// If we reached a path separator that was not part of a set of path | ||
// separators at the end of the string, stop now | ||
if (!matchedSlash) { | ||
startPart = i + 1; | ||
break; | ||
} | ||
continue; | ||
} | ||
if (end === -1) { | ||
// We saw the first non-path separator, mark this as the end of our | ||
// extension | ||
matchedSlash = false; | ||
end = i + 1; | ||
} | ||
if (code === CHAR_DOT) { | ||
// If this is our first dot, mark it as the start of our extension | ||
if (startDot === -1) startDot = i; | ||
else if (preDotState !== 1) preDotState = 1; | ||
} else if (startDot !== -1) { | ||
// We saw a non-dot and non-path separator before our dot, so we should | ||
// have a good chance at having a non-empty extension | ||
preDotState = -1; | ||
} | ||
// Get non-dir info | ||
for (; i >= rootEnd; --i) { | ||
code = path.charCodeAt(i); | ||
if (isPathSeparator(code)) { | ||
// If we reached a path separator that was not part of a set of path | ||
// separators at the end of the string, stop now | ||
if (!matchedSlash) { | ||
startPart = i + 1; | ||
break; | ||
} | ||
continue; | ||
} | ||
if (end === -1) { | ||
// We saw the first non-path separator, mark this as the end of our | ||
// extension | ||
matchedSlash = false; | ||
end = i + 1; | ||
} | ||
if (code === CHAR_DOT) { | ||
// If this is our first dot, mark it as the start of our extension | ||
if (startDot === -1) startDot = i; | ||
else if (preDotState !== 1) preDotState = 1; | ||
} else if (startDot !== -1) { | ||
// We saw a non-dot and non-path separator before our dot, so we should | ||
// have a good chance at having a non-empty extension | ||
preDotState = -1; | ||
} | ||
} | ||
if (end !== -1) { | ||
if ( | ||
startDot === -1 || | ||
// We saw a non-dot character immediately before the dot | ||
preDotState === 0 || | ||
// The (right-most) trimmed path component is exactly '..' | ||
(preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) | ||
) { | ||
ret.base = ret.name = path.slice(startPart, end); | ||
} else { | ||
ret.name = path.slice(startPart, startDot); | ||
ret.base = path.slice(startPart, end); | ||
ret.ext = path.slice(startDot, end); | ||
} | ||
} | ||
if (end !== -1) { | ||
if ( | ||
startDot === -1 || | ||
// We saw a non-dot character immediately before the dot | ||
preDotState === 0 || | ||
// The (right-most) trimmed path component is exactly '..' | ||
(preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) | ||
) { | ||
ret.base = ret.name = path.slice(startPart, end); | ||
} else { | ||
ret.name = path.slice(startPart, startDot); | ||
ret.base = path.slice(startPart, end); | ||
ret.ext = path.slice(startDot, end); | ||
} | ||
} | ||
// If the directory is the root, use the entire root as the `dir` including | ||
// the trailing slash if any (`C:\abc` -> `C:\`). Otherwise, strip out the | ||
// trailing slash (`C:\abc\def` -> `C:\abc`). | ||
if (startPart > 0 && startPart !== rootEnd) ret.dir = path.slice(0, startPart - 1); | ||
else ret.dir = ret.root; | ||
// If the directory is the root, use the entire root as the `dir` including | ||
// the trailing slash if any (`C:\abc` -> `C:\`). Otherwise, strip out the | ||
// trailing slash (`C:\abc\def` -> `C:\abc`). | ||
if (startPart > 0 && startPart !== rootEnd) ret.dir = path.slice(0, startPart - 1); | ||
else ret.dir = ret.root; | ||
return ret; | ||
}, | ||
return ret; | ||
}, | ||
sep: '\\', | ||
delimiter: ';', | ||
win32: null, | ||
posix: null | ||
sep: '\\', | ||
delimiter: ';', | ||
win32: null, | ||
posix: null, | ||
}; | ||
const posix = { | ||
// path.resolve([from ...], to) | ||
resolve(...args) { | ||
let resolvedPath = ''; | ||
let resolvedAbsolute = false; | ||
// path.resolve([from ...], to) | ||
resolve(...args) { | ||
let resolvedPath = ''; | ||
let resolvedAbsolute = false; | ||
for (let i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) { | ||
const path = i >= 0 ? args[i] : process.cwd(); | ||
for (let i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) { | ||
const path = i >= 0 ? args[i] : process.cwd(); | ||
validateString(path, 'path'); | ||
validateString(path, 'path'); | ||
// Skip empty entries | ||
if (path.length === 0) { | ||
continue; | ||
} | ||
// Skip empty entries | ||
if (path.length === 0) { | ||
continue; | ||
} | ||
resolvedPath = `${path}/${resolvedPath}`; | ||
resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; | ||
} | ||
resolvedPath = `${path}/${resolvedPath}`; | ||
resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; | ||
} | ||
// 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) | ||
// 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 = normalizeString(resolvedPath, !resolvedAbsolute, '/', isPosixPathSeparator); | ||
// Normalize the path | ||
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/', isPosixPathSeparator); | ||
if (resolvedAbsolute) { | ||
return `/${resolvedPath}`; | ||
} | ||
return resolvedPath.length > 0 ? resolvedPath : '.'; | ||
}, | ||
if (resolvedAbsolute) { | ||
return `/${resolvedPath}`; | ||
} | ||
return resolvedPath.length > 0 ? resolvedPath : '.'; | ||
}, | ||
normalize(path) { | ||
validateString(path, 'path'); | ||
normalize(path) { | ||
validateString(path, 'path'); | ||
if (path.length === 0) return '.'; | ||
if (path.length === 0) return '.'; | ||
const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; | ||
const trailingSeparator = path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH; | ||
const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; | ||
const trailingSeparator = path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH; | ||
// Normalize the path | ||
path = normalizeString(path, !isAbsolute, '/', isPosixPathSeparator); | ||
// Normalize the path | ||
path = normalizeString(path, !isAbsolute, '/', isPosixPathSeparator); | ||
if (path.length === 0) { | ||
if (isAbsolute) return '/'; | ||
return trailingSeparator ? './' : '.'; | ||
} | ||
if (trailingSeparator) path += '/'; | ||
if (path.length === 0) { | ||
if (isAbsolute) return '/'; | ||
return trailingSeparator ? './' : '.'; | ||
} | ||
if (trailingSeparator) path += '/'; | ||
return isAbsolute ? `/${path}` : path; | ||
}, | ||
return isAbsolute ? `/${path}` : path; | ||
}, | ||
isAbsolute(path) { | ||
validateString(path, 'path'); | ||
return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH; | ||
}, | ||
isAbsolute(path) { | ||
validateString(path, 'path'); | ||
return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH; | ||
}, | ||
join(...args) { | ||
if (args.length === 0) return '.'; | ||
let joined; | ||
for (let i = 0; i < args.length; ++i) { | ||
const arg = args[i]; | ||
validateString(arg, 'path'); | ||
if (arg.length > 0) { | ||
if (joined === undefined) joined = arg; | ||
else joined += `/${arg}`; | ||
} | ||
} | ||
if (joined === undefined) return '.'; | ||
return posix.normalize(joined); | ||
}, | ||
join(...args) { | ||
if (args.length === 0) return '.'; | ||
let joined; | ||
for (let i = 0; i < args.length; ++i) { | ||
const arg = args[i]; | ||
validateString(arg, 'path'); | ||
if (arg.length > 0) { | ||
if (joined === undefined) joined = arg; | ||
else joined += `/${arg}`; | ||
} | ||
} | ||
if (joined === undefined) return '.'; | ||
return posix.normalize(joined); | ||
}, | ||
relative(from, to) { | ||
validateString(from, 'from'); | ||
validateString(to, 'to'); | ||
relative(from, to) { | ||
validateString(from, 'from'); | ||
validateString(to, 'to'); | ||
if (from === to) return ''; | ||
if (from === to) return ''; | ||
// Trim leading forward slashes. | ||
from = posix.resolve(from); | ||
to = posix.resolve(to); | ||
// Trim leading forward slashes. | ||
from = posix.resolve(from); | ||
to = posix.resolve(to); | ||
if (from === to) return ''; | ||
if (from === to) return ''; | ||
const fromStart = 1; | ||
const fromEnd = from.length; | ||
const fromLen = fromEnd - fromStart; | ||
const toStart = 1; | ||
const toLen = to.length - toStart; | ||
const fromStart = 1; | ||
const fromEnd = from.length; | ||
const fromLen = fromEnd - fromStart; | ||
const toStart = 1; | ||
const toLen = to.length - toStart; | ||
// Compare paths to find the longest common path from root | ||
const length = fromLen < toLen ? fromLen : toLen; | ||
let lastCommonSep = -1; | ||
let i = 0; | ||
for (; i < length; i++) { | ||
const fromCode = from.charCodeAt(fromStart + i); | ||
if (fromCode !== to.charCodeAt(toStart + i)) break; | ||
else if (fromCode === CHAR_FORWARD_SLASH) lastCommonSep = i; | ||
// Compare paths to find the longest common path from root | ||
const length = fromLen < toLen ? fromLen : toLen; | ||
let lastCommonSep = -1; | ||
let i = 0; | ||
for (; i < length; i++) { | ||
const fromCode = from.charCodeAt(fromStart + i); | ||
if (fromCode !== to.charCodeAt(toStart + i)) break; | ||
else if (fromCode === CHAR_FORWARD_SLASH) lastCommonSep = i; | ||
} | ||
if (i === length) { | ||
if (toLen > length) { | ||
if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) { | ||
// We get here if `from` is the exact base path for `to`. | ||
// For example: from='/foo/bar'; to='/foo/bar/baz' | ||
return to.slice(toStart + i + 1); | ||
} | ||
if (i === length) { | ||
if (toLen > length) { | ||
if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) { | ||
// We get here if `from` is the exact base path for `to`. | ||
// For example: from='/foo/bar'; to='/foo/bar/baz' | ||
return to.slice(toStart + i + 1); | ||
} | ||
if (i === 0) { | ||
// We get here if `from` is the root | ||
// For example: from='/'; to='/foo' | ||
return to.slice(toStart + i); | ||
} | ||
} else if (fromLen > length) { | ||
if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) { | ||
// We get here if `to` is the exact base path for `from`. | ||
// For example: from='/foo/bar/baz'; to='/foo/bar' | ||
lastCommonSep = i; | ||
} else if (i === 0) { | ||
// We get here if `to` is the root. | ||
// For example: from='/foo/bar'; to='/' | ||
lastCommonSep = 0; | ||
} | ||
} | ||
if (i === 0) { | ||
// We get here if `from` is the root | ||
// For example: from='/'; to='/foo' | ||
return to.slice(toStart + i); | ||
} | ||
let out = ''; | ||
// Generate the relative path based on the path difference between `to` | ||
// and `from`. | ||
for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { | ||
if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) { | ||
out += out.length === 0 ? '..' : '/..'; | ||
} | ||
} else if (fromLen > length) { | ||
if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) { | ||
// We get here if `to` is the exact base path for `from`. | ||
// For example: from='/foo/bar/baz'; to='/foo/bar' | ||
lastCommonSep = i; | ||
} else if (i === 0) { | ||
// We get here if `to` is the root. | ||
// For example: from='/foo/bar'; to='/' | ||
lastCommonSep = 0; | ||
} | ||
} | ||
} | ||
// Lastly, append the rest of the destination (`to`) path that comes after | ||
// the common path parts. | ||
return `${out}${to.slice(toStart + lastCommonSep)}`; | ||
}, | ||
let out = ''; | ||
// Generate the relative path based on the path difference between `to` | ||
// and `from`. | ||
for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { | ||
if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) { | ||
out += out.length === 0 ? '..' : '/..'; | ||
} | ||
} | ||
toNamespacedPath(path) { | ||
// Non-op on posix systems | ||
return path; | ||
}, | ||
// Lastly, append the rest of the destination (`to`) path that comes after | ||
// the common path parts. | ||
return `${out}${to.slice(toStart + lastCommonSep)}`; | ||
}, | ||
dirname(path) { | ||
validateString(path, 'path'); | ||
if (path.length === 0) return '.'; | ||
const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH; | ||
let end = -1; | ||
let matchedSlash = true; | ||
for (let i = path.length - 1; i >= 1; --i) { | ||
if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { | ||
if (!matchedSlash) { | ||
end = i; | ||
break; | ||
} | ||
} else { | ||
// We saw the first non-path separator | ||
matchedSlash = false; | ||
} | ||
toNamespacedPath(path) { | ||
// Non-op on posix systems | ||
return path; | ||
}, | ||
dirname(path) { | ||
validateString(path, 'path'); | ||
if (path.length === 0) return '.'; | ||
const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH; | ||
let end = -1; | ||
let matchedSlash = true; | ||
for (let i = path.length - 1; i >= 1; --i) { | ||
if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { | ||
if (!matchedSlash) { | ||
end = i; | ||
break; | ||
} | ||
} else { | ||
// We saw the first non-path separator | ||
matchedSlash = false; | ||
} | ||
} | ||
if (end === -1) return hasRoot ? '/' : '.'; | ||
if (hasRoot && end === 1) return '//'; | ||
return path.slice(0, end); | ||
}, | ||
if (end === -1) return hasRoot ? '/' : '.'; | ||
if (hasRoot && end === 1) return '//'; | ||
return path.slice(0, end); | ||
}, | ||
basename(path, ext) { | ||
if (ext !== undefined) validateString(ext, 'ext'); | ||
validateString(path, 'path'); | ||
basename(path, ext) { | ||
if (ext !== undefined) validateString(ext, 'ext'); | ||
validateString(path, 'path'); | ||
let start = 0; | ||
let end = -1; | ||
let matchedSlash = true; | ||
let start = 0; | ||
let end = -1; | ||
let matchedSlash = true; | ||
if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { | ||
if (ext === path) return ''; | ||
let extIdx = ext.length - 1; | ||
let firstNonSlashEnd = -1; | ||
for (let i = path.length - 1; i >= 0; --i) { | ||
const code = path.charCodeAt(i); | ||
if (code === CHAR_FORWARD_SLASH) { | ||
// If we reached a path separator that was not part of a set of path | ||
// separators at the end of the string, stop now | ||
if (!matchedSlash) { | ||
start = i + 1; | ||
break; | ||
} | ||
} else { | ||
if (firstNonSlashEnd === -1) { | ||
// We saw the first non-path separator, remember this index in case | ||
// we need it if the extension ends up not matching | ||
matchedSlash = false; | ||
firstNonSlashEnd = i + 1; | ||
} | ||
if (extIdx >= 0) { | ||
// Try to match the explicit extension | ||
if (code === ext.charCodeAt(extIdx)) { | ||
if (--extIdx === -1) { | ||
// We matched the extension, so mark this as the end of our path | ||
// component | ||
end = i; | ||
} | ||
} else { | ||
// Extension does not match, so our result is the entire path | ||
// component | ||
extIdx = -1; | ||
end = firstNonSlashEnd; | ||
} | ||
} | ||
} | ||
if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { | ||
if (ext === path) return ''; | ||
let extIdx = ext.length - 1; | ||
let firstNonSlashEnd = -1; | ||
for (let i = path.length - 1; i >= 0; --i) { | ||
const code = path.charCodeAt(i); | ||
if (code === CHAR_FORWARD_SLASH) { | ||
// If we reached a path separator that was not part of a set of path | ||
// separators at the end of the string, stop now | ||
if (!matchedSlash) { | ||
start = i + 1; | ||
break; | ||
} | ||
} else { | ||
if (firstNonSlashEnd === -1) { | ||
// We saw the first non-path separator, remember this index in case | ||
// we need it if the extension ends up not matching | ||
matchedSlash = false; | ||
firstNonSlashEnd = i + 1; | ||
} | ||
if (extIdx >= 0) { | ||
// Try to match the explicit extension | ||
if (code === ext.charCodeAt(extIdx)) { | ||
if (--extIdx === -1) { | ||
// We matched the extension, so mark this as the end of our path | ||
// component | ||
end = i; | ||
} | ||
} else { | ||
// Extension does not match, so our result is the entire path | ||
// component | ||
extIdx = -1; | ||
end = firstNonSlashEnd; | ||
} | ||
} | ||
} | ||
} | ||
if (start === end) end = firstNonSlashEnd; | ||
else if (end === -1) end = path.length; | ||
return path.slice(start, end); | ||
if (start === end) end = firstNonSlashEnd; | ||
else if (end === -1) end = path.length; | ||
return path.slice(start, end); | ||
} | ||
for (let i = path.length - 1; i >= 0; --i) { | ||
if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { | ||
// If we reached a path separator that was not part of a set of path | ||
// separators at the end of the string, stop now | ||
if (!matchedSlash) { | ||
start = i + 1; | ||
break; | ||
} | ||
for (let i = path.length - 1; i >= 0; --i) { | ||
if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { | ||
// If we reached a path separator that was not part of a set of path | ||
// separators at the end of the string, stop now | ||
if (!matchedSlash) { | ||
start = i + 1; | ||
break; | ||
} | ||
} else if (end === -1) { | ||
// We saw the first non-path separator, mark this as the end of our | ||
// path component | ||
matchedSlash = false; | ||
end = i + 1; | ||
} | ||
} | ||
} else if (end === -1) { | ||
// We saw the first non-path separator, mark this as the end of our | ||
// path component | ||
matchedSlash = false; | ||
end = i + 1; | ||
} | ||
} | ||
if (end === -1) return ''; | ||
return path.slice(start, end); | ||
}, | ||
if (end === -1) return ''; | ||
return path.slice(start, end); | ||
}, | ||
extname(path) { | ||
validateString(path, 'path'); | ||
let startDot = -1; | ||
let startPart = 0; | ||
let end = -1; | ||
let matchedSlash = true; | ||
// Track the state of characters (if any) we see before our first dot and | ||
// after any path separator we find | ||
let preDotState = 0; | ||
for (let i = path.length - 1; i >= 0; --i) { | ||
const code = path.charCodeAt(i); | ||
if (code === CHAR_FORWARD_SLASH) { | ||
// If we reached a path separator that was not part of a set of path | ||
// separators at the end of the string, stop now | ||
if (!matchedSlash) { | ||
startPart = i + 1; | ||
break; | ||
} | ||
continue; | ||
} | ||
if (end === -1) { | ||
// We saw the first non-path separator, mark this as the end of our | ||
// extension | ||
matchedSlash = false; | ||
end = i + 1; | ||
} | ||
if (code === CHAR_DOT) { | ||
// If this is our first dot, mark it as the start of our extension | ||
if (startDot === -1) startDot = i; | ||
else if (preDotState !== 1) preDotState = 1; | ||
} else if (startDot !== -1) { | ||
// We saw a non-dot and non-path separator before our dot, so we should | ||
// have a good chance at having a non-empty extension | ||
preDotState = -1; | ||
} | ||
extname(path) { | ||
validateString(path, 'path'); | ||
let startDot = -1; | ||
let startPart = 0; | ||
let end = -1; | ||
let matchedSlash = true; | ||
// Track the state of characters (if any) we see before our first dot and | ||
// after any path separator we find | ||
let preDotState = 0; | ||
for (let i = path.length - 1; i >= 0; --i) { | ||
const code = path.charCodeAt(i); | ||
if (code === CHAR_FORWARD_SLASH) { | ||
// If we reached a path separator that was not part of a set of path | ||
// separators at the end of the string, stop now | ||
if (!matchedSlash) { | ||
startPart = i + 1; | ||
break; | ||
} | ||
continue; | ||
} | ||
if (end === -1) { | ||
// We saw the first non-path separator, mark this as the end of our | ||
// extension | ||
matchedSlash = false; | ||
end = i + 1; | ||
} | ||
if (code === CHAR_DOT) { | ||
// If this is our first dot, mark it as the start of our extension | ||
if (startDot === -1) startDot = i; | ||
else if (preDotState !== 1) preDotState = 1; | ||
} else if (startDot !== -1) { | ||
// We saw a non-dot and non-path separator before our dot, so we should | ||
// have a good chance at having a non-empty extension | ||
preDotState = -1; | ||
} | ||
} | ||
if ( | ||
startDot === -1 || | ||
end === -1 || | ||
// We saw a non-dot character immediately before the dot | ||
preDotState === 0 || | ||
// The (right-most) trimmed path component is exactly '..' | ||
(preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) | ||
) { | ||
return ''; | ||
} | ||
return path.slice(startDot, end); | ||
}, | ||
if ( | ||
startDot === -1 || | ||
end === -1 || | ||
// We saw a non-dot character immediately before the dot | ||
preDotState === 0 || | ||
// The (right-most) trimmed path component is exactly '..' | ||
(preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) | ||
) { | ||
return ''; | ||
} | ||
return path.slice(startDot, end); | ||
}, | ||
format: _format.bind(null, '/'), | ||
format: _format.bind(null, '/'), | ||
parse(path) { | ||
validateString(path, 'path'); | ||
parse(path) { | ||
validateString(path, 'path'); | ||
const ret = { root: '', dir: '', base: '', ext: '', name: '' }; | ||
if (path.length === 0) return ret; | ||
const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; | ||
let start; | ||
if (isAbsolute) { | ||
ret.root = '/'; | ||
start = 1; | ||
} else { | ||
start = 0; | ||
} | ||
let startDot = -1; | ||
let startPart = 0; | ||
let end = -1; | ||
let matchedSlash = true; | ||
let i = path.length - 1; | ||
const ret = { root: '', dir: '', base: '', ext: '', name: '' }; | ||
if (path.length === 0) return ret; | ||
const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; | ||
let start; | ||
if (isAbsolute) { | ||
ret.root = '/'; | ||
start = 1; | ||
} else { | ||
start = 0; | ||
} | ||
let startDot = -1; | ||
let startPart = 0; | ||
let end = -1; | ||
let matchedSlash = true; | ||
let i = path.length - 1; | ||
// Track the state of characters (if any) we see before our first dot and | ||
// after any path separator we find | ||
let preDotState = 0; | ||
// Track the state of characters (if any) we see before our first dot and | ||
// after any path separator we find | ||
let preDotState = 0; | ||
// Get non-dir info | ||
for (; i >= start; --i) { | ||
const code = path.charCodeAt(i); | ||
if (code === CHAR_FORWARD_SLASH) { | ||
// If we reached a path separator that was not part of a set of path | ||
// separators at the end of the string, stop now | ||
if (!matchedSlash) { | ||
startPart = i + 1; | ||
break; | ||
} | ||
continue; | ||
} | ||
if (end === -1) { | ||
// We saw the first non-path separator, mark this as the end of our | ||
// extension | ||
matchedSlash = false; | ||
end = i + 1; | ||
} | ||
if (code === CHAR_DOT) { | ||
// If this is our first dot, mark it as the start of our extension | ||
if (startDot === -1) startDot = i; | ||
else if (preDotState !== 1) preDotState = 1; | ||
} else if (startDot !== -1) { | ||
// We saw a non-dot and non-path separator before our dot, so we should | ||
// have a good chance at having a non-empty extension | ||
preDotState = -1; | ||
} | ||
// Get non-dir info | ||
for (; i >= start; --i) { | ||
const code = path.charCodeAt(i); | ||
if (code === CHAR_FORWARD_SLASH) { | ||
// If we reached a path separator that was not part of a set of path | ||
// separators at the end of the string, stop now | ||
if (!matchedSlash) { | ||
startPart = i + 1; | ||
break; | ||
} | ||
continue; | ||
} | ||
if (end === -1) { | ||
// We saw the first non-path separator, mark this as the end of our | ||
// extension | ||
matchedSlash = false; | ||
end = i + 1; | ||
} | ||
if (code === CHAR_DOT) { | ||
// If this is our first dot, mark it as the start of our extension | ||
if (startDot === -1) startDot = i; | ||
else if (preDotState !== 1) preDotState = 1; | ||
} else if (startDot !== -1) { | ||
// We saw a non-dot and non-path separator before our dot, so we should | ||
// have a good chance at having a non-empty extension | ||
preDotState = -1; | ||
} | ||
} | ||
if (end !== -1) { | ||
const start = startPart === 0 && isAbsolute ? 1 : startPart; | ||
if ( | ||
startDot === -1 || | ||
// We saw a non-dot character immediately before the dot | ||
preDotState === 0 || | ||
// The (right-most) trimmed path component is exactly '..' | ||
(preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) | ||
) { | ||
ret.base = ret.name = path.slice(start, end); | ||
} else { | ||
ret.name = path.slice(start, startDot); | ||
ret.base = path.slice(start, end); | ||
ret.ext = path.slice(startDot, end); | ||
} | ||
} | ||
if (end !== -1) { | ||
const start = startPart === 0 && isAbsolute ? 1 : startPart; | ||
if ( | ||
startDot === -1 || | ||
// We saw a non-dot character immediately before the dot | ||
preDotState === 0 || | ||
// The (right-most) trimmed path component is exactly '..' | ||
(preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) | ||
) { | ||
ret.base = ret.name = path.slice(start, end); | ||
} else { | ||
ret.name = path.slice(start, startDot); | ||
ret.base = path.slice(start, end); | ||
ret.ext = path.slice(startDot, end); | ||
} | ||
} | ||
if (startPart > 0) ret.dir = path.slice(0, startPart - 1); | ||
else if (isAbsolute) ret.dir = '/'; | ||
if (startPart > 0) ret.dir = path.slice(0, startPart - 1); | ||
else if (isAbsolute) ret.dir = '/'; | ||
return ret; | ||
}, | ||
return ret; | ||
}, | ||
sep: '/', | ||
delimiter: ':', | ||
win32: null, | ||
posix: null | ||
sep: '/', | ||
delimiter: ':', | ||
win32: null, | ||
posix: null, | ||
}; | ||
@@ -1283,0 +1278,0 @@ |
{ | ||
"name": "@file-services/path", | ||
"description": "Node's `path` implementation, ready for the web.", | ||
"version": "2.2.0", | ||
"version": "3.0.0", | ||
"main": "index.js", | ||
@@ -9,3 +9,3 @@ "types": "index.d.ts", | ||
"dependencies": { | ||
"@file-services/types": "^2.2.0" | ||
"@file-services/types": "^3.0.0" | ||
}, | ||
@@ -12,0 +12,0 @@ "files": [ |
# @file-services/path | ||
[![npm version](https://img.shields.io/npm/v/@file-services/path.svg)](https://www.npmjs.com/package/@file-services/path) | ||
@@ -3,0 +4,0 @@ [![package size](https://badgen.net/bundlephobia/minzip/@file-services/path)](https://bundlephobia.com/result?p=@file-services/path) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
13
0
43592
1155
+ Added@file-services/types@3.1.1(transitive)
- Removed@file-services/types@2.2.0(transitive)
Updated@file-services/types@^3.0.0