| const os = require('bare-os') | |
| const { normalizeString } = require('./shared') | |
| const { | |
| CHAR_UPPERCASE_A, | |
| CHAR_LOWERCASE_A, | |
| CHAR_UPPERCASE_Z, | |
| CHAR_LOWERCASE_Z, | |
| CHAR_DOT, | |
| CHAR_FORWARD_SLASH, | |
| CHAR_BACKWARD_SLASH, | |
| CHAR_COLON, | |
| CHAR_QUESTION_MARK | |
| } = require('./constants') | |
| function isWindowsPathSeparator (code) { | |
| return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH | |
| } | |
| function isWindowsDeviceRoot (code) { | |
| return (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) || | |
| (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z) | |
| } | |
| exports.posix = require('./posix') | |
| exports.win32 = exports | |
| exports.sep = '\\' | |
| exports.delimiter = ';' | |
| exports.resolve = function 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] | |
| if (path.length === 0) continue | |
| } else if (resolvedDevice.length === 0) { | |
| path = os.cwd() | |
| } else { | |
| path = os.getEnv(`=${resolvedDevice}`) || os.cwd() | |
| if (path === undefined || (path.substring(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) | |
| if (len === 1) { | |
| if (isWindowsPathSeparator(code)) { | |
| rootEnd = 1 | |
| isAbsolute = true | |
| } | |
| } else if (isWindowsPathSeparator(code)) { | |
| isAbsolute = true | |
| if (isWindowsPathSeparator(path.charCodeAt(1))) { | |
| let j = 2 | |
| let last = j | |
| while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) { | |
| j++ | |
| } | |
| if (j < len && j !== last) { | |
| const firstPart = path.substring(last, j) | |
| last = j | |
| while (j < len && isWindowsPathSeparator(path.charCodeAt(j))) { | |
| j++ | |
| } | |
| if (j < len && j !== last) { | |
| last = j | |
| while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) { | |
| j++ | |
| } | |
| if (j === len || j !== last) { | |
| device = `\\\\${firstPart}\\${path.substring(last, j)}` | |
| rootEnd = j | |
| } | |
| } | |
| } | |
| } else { | |
| rootEnd = 1 | |
| } | |
| } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { | |
| device = path.substring(0, 2) | |
| rootEnd = 2 | |
| if (len > 2 && isWindowsPathSeparator(path.charCodeAt(2))) { | |
| isAbsolute = true | |
| rootEnd = 3 | |
| } | |
| } | |
| if (device.length > 0) { | |
| if (resolvedDevice.length > 0) { | |
| if (device.toLowerCase() !== resolvedDevice.toLowerCase()) { continue } | |
| } else { | |
| resolvedDevice = device | |
| } | |
| } | |
| if (resolvedAbsolute) { | |
| if (resolvedDevice.length > 0) { break } | |
| } else { | |
| resolvedTail = `${path.substring(rootEnd)}\\${resolvedTail}` | |
| resolvedAbsolute = isAbsolute | |
| if (isAbsolute && resolvedDevice.length > 0) { | |
| break | |
| } | |
| } | |
| } | |
| resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, '\\', isWindowsPathSeparator) | |
| return resolvedAbsolute ? `${resolvedDevice}\\${resolvedTail}` : `${resolvedDevice}${resolvedTail}` || '.' | |
| } | |
| exports.normalize = function normalize (path) { | |
| const len = path.length | |
| if (len === 0) return '.' | |
| let rootEnd = 0 | |
| let device | |
| let isAbsolute = false | |
| const code = path.charCodeAt(0) | |
| if (len === 1) { | |
| return code === CHAR_FORWARD_SLASH ? '\\' : path | |
| } | |
| if (isWindowsPathSeparator(code)) { | |
| isAbsolute = true | |
| if (isWindowsPathSeparator(path.charCodeAt(1))) { | |
| let j = 2 | |
| let last = j | |
| while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) { | |
| j++ | |
| } | |
| if (j < len && j !== last) { | |
| const firstPart = path.substring(last, j) | |
| last = j | |
| while (j < len && isWindowsPathSeparator(path.charCodeAt(j))) { | |
| j++ | |
| } | |
| if (j < len && j !== last) { | |
| last = j | |
| while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) { | |
| j++ | |
| } | |
| if (j === len) { | |
| return `\\\\${firstPart}\\${path.substring(last)}\\` | |
| } | |
| if (j !== last) { | |
| device = `\\\\${firstPart}\\${path.substring(last, j)}` | |
| rootEnd = j | |
| } | |
| } | |
| } | |
| } else { | |
| rootEnd = 1 | |
| } | |
| } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { | |
| device = path.substring(0, 2) | |
| rootEnd = 2 | |
| if (len > 2 && isWindowsPathSeparator(path.charCodeAt(2))) { | |
| isAbsolute = true | |
| rootEnd = 3 | |
| } | |
| } | |
| let tail = rootEnd < len ? normalizeString(path.substring(rootEnd), !isAbsolute, '\\', isWindowsPathSeparator) : '' | |
| if (tail.length === 0 && !isAbsolute) { | |
| tail = '.' | |
| } | |
| if (tail.length > 0 && isWindowsPathSeparator(path.charCodeAt(len - 1))) { | |
| tail += '\\' | |
| } | |
| if (device === undefined) { | |
| return isAbsolute ? `\\${tail}` : tail | |
| } | |
| return isAbsolute ? `${device}\\${tail}` : `${device}${tail}` | |
| } | |
| exports.isAbsolute = function isAbsolute (path) { | |
| const len = path.length | |
| if (len === 0) return false | |
| const code = path.charCodeAt(0) | |
| return isWindowsPathSeparator(code) || (len > 2 && isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON && isWindowsPathSeparator(path.charCodeAt(2))) | |
| } | |
| exports.join = function join (...args) { | |
| if (args.length === 0) return '.' | |
| let joined | |
| let firstPart | |
| for (let i = 0; i < args.length; ++i) { | |
| const arg = args[i] | |
| if (arg.length > 0) { | |
| if (joined === undefined) joined = firstPart = arg | |
| else joined += `\\${arg}` | |
| } | |
| } | |
| if (joined === undefined) return '.' | |
| let needsReplace = true | |
| let slashCount = 0 | |
| if (isWindowsPathSeparator(firstPart.charCodeAt(0))) { | |
| ++slashCount | |
| const firstLen = firstPart.length | |
| if (firstLen > 1 && isWindowsPathSeparator(firstPart.charCodeAt(1))) { | |
| ++slashCount | |
| if (firstLen > 2) { | |
| if (isWindowsPathSeparator(firstPart.charCodeAt(2))) { | |
| ++slashCount | |
| } else { | |
| needsReplace = false | |
| } | |
| } | |
| } | |
| } | |
| if (needsReplace) { | |
| while (slashCount < joined.length && isWindowsPathSeparator(joined.charCodeAt(slashCount))) { | |
| slashCount++ | |
| } | |
| if (slashCount >= 2) { | |
| joined = `\\${joined.substring(slashCount)}` | |
| } | |
| } | |
| return exports.normalize(joined) | |
| } | |
| exports.relative = function relative (from, to) { | |
| if (from === to) return '' | |
| const fromOrig = exports.resolve(from) | |
| const toOrig = exports.resolve(to) | |
| if (fromOrig === toOrig) return '' | |
| from = fromOrig.toLowerCase() | |
| to = toOrig.toLowerCase() | |
| if (from === to) return '' | |
| let fromStart = 0 | |
| while (fromStart < from.length && from.charCodeAt(fromStart) === CHAR_BACKWARD_SLASH) { | |
| fromStart++ | |
| } | |
| let fromEnd = from.length | |
| while (fromEnd - 1 > fromStart && from.charCodeAt(fromEnd - 1) === CHAR_BACKWARD_SLASH) { | |
| fromEnd-- | |
| } | |
| const fromLen = fromEnd - fromStart | |
| let toStart = 0 | |
| while (toStart < to.length && to.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) { | |
| toStart++ | |
| } | |
| let toEnd = to.length | |
| while (toEnd - 1 > toStart && to.charCodeAt(toEnd - 1) === CHAR_BACKWARD_SLASH) { | |
| toEnd-- | |
| } | |
| const toLen = toEnd - toStart | |
| 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 (i !== length) { | |
| if (lastCommonSep === -1) return toOrig | |
| } else { | |
| if (toLen > length) { | |
| if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) { | |
| return toOrig.substring(toStart + i + 1) | |
| } | |
| if (i === 2) { | |
| return toOrig.substring(toStart + i) | |
| } | |
| } | |
| if (fromLen > length) { | |
| if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) { | |
| lastCommonSep = i | |
| } else if (i === 2) { | |
| lastCommonSep = 3 | |
| } | |
| } | |
| if (lastCommonSep === -1) lastCommonSep = 0 | |
| } | |
| let out = '' | |
| for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { | |
| if (i === fromEnd || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) { | |
| out += out.length === 0 ? '..' : '\\..' | |
| } | |
| } | |
| toStart += lastCommonSep | |
| if (out.length > 0) { | |
| return `${out}${toOrig.substring(toStart, toEnd)}` | |
| } | |
| if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) { | |
| ++toStart | |
| } | |
| return toOrig.substring(toStart, toEnd) | |
| } | |
| exports.toNamespacedPath = function toNamespacedPath (path) { | |
| if (path.length === 0) return path | |
| const resolvedPath = exports.resolve(path) | |
| if (resolvedPath.length <= 2) return path | |
| if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) { | |
| if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) { | |
| const code = resolvedPath.charCodeAt(2) | |
| if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) { | |
| return `\\\\?\\UNC\\${resolvedPath.substring(2)}` | |
| } | |
| } | |
| } else if ( | |
| isWindowsDeviceRoot(resolvedPath.charCodeAt(0)) && | |
| resolvedPath.charCodeAt(1) === CHAR_COLON && | |
| resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH | |
| ) { | |
| return `\\\\?\\${resolvedPath}` | |
| } | |
| return path | |
| } | |
| exports.dirname = function dirname (path) { | |
| const len = path.length | |
| if (len === 0) return '.' | |
| let rootEnd = -1 | |
| let offset = 0 | |
| const code = path.charCodeAt(0) | |
| if (len === 1) { | |
| return isWindowsPathSeparator(code) ? path : '.' | |
| } | |
| if (isWindowsPathSeparator(code)) { | |
| rootEnd = offset = 1 | |
| if (isWindowsPathSeparator(path.charCodeAt(1))) { | |
| let j = 2 | |
| let last = j | |
| while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) { | |
| j++ | |
| } | |
| if (j < len && j !== last) { | |
| last = j | |
| while (j < len && isWindowsPathSeparator(path.charCodeAt(j))) { | |
| j++ | |
| } | |
| if (j < len && j !== last) { | |
| last = j | |
| while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) { | |
| j++ | |
| } | |
| if (j === len) { | |
| return path | |
| } | |
| if (j !== last) { | |
| rootEnd = offset = j + 1 | |
| } | |
| } | |
| } | |
| } | |
| } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { | |
| rootEnd = len > 2 && isWindowsPathSeparator(path.charCodeAt(2)) ? 3 : 2 | |
| offset = rootEnd | |
| } | |
| let end = -1 | |
| let matchedSlash = true | |
| for (let i = len - 1; i >= offset; --i) { | |
| if (isWindowsPathSeparator(path.charCodeAt(i))) { | |
| if (!matchedSlash) { | |
| end = i | |
| break | |
| } | |
| } else { | |
| matchedSlash = false | |
| } | |
| } | |
| if (end === -1) { | |
| if (rootEnd === -1) return '.' | |
| end = rootEnd | |
| } | |
| return path.substring(0, end) | |
| } | |
| exports.basename = function basename (path, suffix) { | |
| let start = 0 | |
| let end = -1 | |
| let matchedSlash = true | |
| if (path.length >= 2 && isWindowsDeviceRoot(path.charCodeAt(0)) && path.charCodeAt(1) === CHAR_COLON) { | |
| start = 2 | |
| } | |
| if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) { | |
| if (suffix === path) return '' | |
| let extIdx = suffix.length - 1 | |
| let firstNonSlashEnd = -1 | |
| for (let i = path.length - 1; i >= start; --i) { | |
| const code = path.charCodeAt(i) | |
| if (isWindowsPathSeparator(code)) { | |
| if (!matchedSlash) { | |
| start = i + 1 | |
| break | |
| } | |
| } else { | |
| if (firstNonSlashEnd === -1) { | |
| matchedSlash = false | |
| firstNonSlashEnd = i + 1 | |
| } | |
| if (extIdx >= 0) { | |
| if (code === suffix.charCodeAt(extIdx)) { | |
| if (--extIdx === -1) { | |
| end = i | |
| } | |
| } else { | |
| extIdx = -1 | |
| end = firstNonSlashEnd | |
| } | |
| } | |
| } | |
| } | |
| if (start === end) end = firstNonSlashEnd | |
| else if (end === -1) end = path.length | |
| return path.substring(start, end) | |
| } | |
| for (let i = path.length - 1; i >= start; --i) { | |
| if (isWindowsPathSeparator(path.charCodeAt(i))) { | |
| if (!matchedSlash) { | |
| start = i + 1 | |
| break | |
| } | |
| } else if (end === -1) { | |
| matchedSlash = false | |
| end = i + 1 | |
| } | |
| } | |
| if (end === -1) return '' | |
| return path.substring(start, end) | |
| } | |
| exports.extname = function extname (path) { | |
| let start = 0 | |
| let startDot = -1 | |
| let startPart = 0 | |
| let end = -1 | |
| let matchedSlash = true | |
| let preDotState = 0 | |
| 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 (isWindowsPathSeparator(code)) { | |
| if (!matchedSlash) { | |
| startPart = i + 1 | |
| break | |
| } | |
| continue | |
| } | |
| if (end === -1) { | |
| matchedSlash = false | |
| end = i + 1 | |
| } | |
| if (code === CHAR_DOT) { | |
| if (startDot === -1) startDot = i | |
| else if (preDotState !== 1) preDotState = 1 | |
| } else if (startDot !== -1) { | |
| preDotState = -1 | |
| } | |
| } | |
| if (startDot === -1 || end === -1 || preDotState === 0 || (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)) { | |
| return '' | |
| } | |
| return path.substring(startDot, end) | |
| } | |