const os = require('bare-os') const { normalizeString } = require('./shared') const { CHAR_DOT, CHAR_FORWARD_SLASH } = require('./constants') function isPosixPathSeparator (code) { return code === CHAR_FORWARD_SLASH } exports.win32 = require('./win32') exports.posix = exports exports.sep = '/' exports.delimiter = ':' exports.resolve = function resolve (...args) { let resolvedPath = '' let resolvedAbsolute = false for (let i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) { const path = i >= 0 ? args[i] : os.cwd() if (path.length === 0) { continue } resolvedPath = `${path}/${resolvedPath}` resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH } resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/', isPosixPathSeparator) if (resolvedAbsolute) { return `/${resolvedPath}` } return resolvedPath.length > 0 ? resolvedPath : '.' } exports.normalize = function normalize (path) { if (path.length === 0) return '.' const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH const trailingSeparator = path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH path = normalizeString(path, !isAbsolute, '/', isPosixPathSeparator) if (path.length === 0) { if (isAbsolute) return '/' return trailingSeparator ? './' : '.' } if (trailingSeparator) path += '/' return isAbsolute ? `/${path}` : path } exports.isAbsolute = function isAbsolute (path) { return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH } exports.join = function join (...args) { if (args.length === 0) return '.' let joined for (let i = 0; i < args.length; ++i) { const arg = args[i] if (arg.length > 0) { if (joined === undefined) joined = arg else joined += `/${arg}` } } if (joined === undefined) return '.' return exports.normalize(joined) } exports.relative = function relative (from, to) { if (from === to) return '' from = exports.resolve(from) to = exports.resolve(to) if (from === to) return '' const fromStart = 1 const fromEnd = from.length const fromLen = fromEnd - fromStart const toStart = 1 const toLen = to.length - 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_FORWARD_SLASH) { lastCommonSep = i } } if (i === length) { if (toLen > length) { if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) { return to.substring(toStart + i + 1) } if (i === 0) { return to.substring(toStart + i) } } else if (fromLen > length) { if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) { lastCommonSep = i } else if (i === 0) { lastCommonSep = 0 } } } let out = '' for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) { out += out.length === 0 ? '..' : '/..' } } return `${out}${to.substring(toStart + lastCommonSep)}` } exports.toNamespacedPath = function toNamespacedPath (path) { return path } exports.dirname = function dirname (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 { matchedSlash = false } } if (end === -1) return hasRoot ? '/' : '.' if (hasRoot && end === 1) return '//' return path.substring(0, end) } exports.basename = function basename (path, suffix) { let start = 0 let end = -1 let matchedSlash = true 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 >= 0; --i) { const code = path.charCodeAt(i) if (code === CHAR_FORWARD_SLASH) { 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 >= 0; --i) { if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { 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 startDot = -1 let startPart = 0 let end = -1 let matchedSlash = true let preDotState = 0 for (let i = path.length - 1; i >= 0; --i) { const code = path.charCodeAt(i) if (code === CHAR_FORWARD_SLASH) { 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) }