posix.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. const os = require('bare-os')
  2. const { normalizeString } = require('./shared')
  3. const {
  4. CHAR_DOT,
  5. CHAR_FORWARD_SLASH
  6. } = require('./constants')
  7. function isPosixPathSeparator (code) {
  8. return code === CHAR_FORWARD_SLASH
  9. }
  10. exports.win32 = require('./win32')
  11. exports.posix = exports
  12. exports.sep = '/'
  13. exports.delimiter = ':'
  14. exports.resolve = function resolve (...args) {
  15. let resolvedPath = ''
  16. let resolvedAbsolute = false
  17. for (let i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) {
  18. const path = i >= 0 ? args[i] : os.cwd()
  19. if (path.length === 0) {
  20. continue
  21. }
  22. resolvedPath = `${path}/${resolvedPath}`
  23. resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH
  24. }
  25. resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/', isPosixPathSeparator)
  26. if (resolvedAbsolute) {
  27. return `/${resolvedPath}`
  28. }
  29. return resolvedPath.length > 0 ? resolvedPath : '.'
  30. }
  31. exports.normalize = function normalize (path) {
  32. if (path.length === 0) return '.'
  33. const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH
  34. const trailingSeparator = path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH
  35. path = normalizeString(path, !isAbsolute, '/', isPosixPathSeparator)
  36. if (path.length === 0) {
  37. if (isAbsolute) return '/'
  38. return trailingSeparator ? './' : '.'
  39. }
  40. if (trailingSeparator) path += '/'
  41. return isAbsolute ? `/${path}` : path
  42. }
  43. exports.isAbsolute = function isAbsolute (path) {
  44. return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH
  45. }
  46. exports.join = function join (...args) {
  47. if (args.length === 0) return '.'
  48. let joined
  49. for (let i = 0; i < args.length; ++i) {
  50. const arg = args[i]
  51. if (arg.length > 0) {
  52. if (joined === undefined) joined = arg
  53. else joined += `/${arg}`
  54. }
  55. }
  56. if (joined === undefined) return '.'
  57. return exports.normalize(joined)
  58. }
  59. exports.relative = function relative (from, to) {
  60. if (from === to) return ''
  61. from = exports.resolve(from)
  62. to = exports.resolve(to)
  63. if (from === to) return ''
  64. const fromStart = 1
  65. const fromEnd = from.length
  66. const fromLen = fromEnd - fromStart
  67. const toStart = 1
  68. const toLen = to.length - toStart
  69. const length = (fromLen < toLen ? fromLen : toLen)
  70. let lastCommonSep = -1
  71. let i = 0
  72. for (; i < length; i++) {
  73. const fromCode = from.charCodeAt(fromStart + i)
  74. if (fromCode !== to.charCodeAt(toStart + i)) {
  75. break
  76. } else if (fromCode === CHAR_FORWARD_SLASH) {
  77. lastCommonSep = i
  78. }
  79. }
  80. if (i === length) {
  81. if (toLen > length) {
  82. if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) {
  83. return to.substring(toStart + i + 1)
  84. }
  85. if (i === 0) {
  86. return to.substring(toStart + i)
  87. }
  88. } else if (fromLen > length) {
  89. if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) {
  90. lastCommonSep = i
  91. } else if (i === 0) {
  92. lastCommonSep = 0
  93. }
  94. }
  95. }
  96. let out = ''
  97. for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {
  98. if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) {
  99. out += out.length === 0 ? '..' : '/..'
  100. }
  101. }
  102. return `${out}${to.substring(toStart + lastCommonSep)}`
  103. }
  104. exports.toNamespacedPath = function toNamespacedPath (path) {
  105. return path
  106. }
  107. exports.dirname = function dirname (path) {
  108. if (path.length === 0) return '.'
  109. const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH
  110. let end = -1
  111. let matchedSlash = true
  112. for (let i = path.length - 1; i >= 1; --i) {
  113. if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) {
  114. if (!matchedSlash) {
  115. end = i
  116. break
  117. }
  118. } else {
  119. matchedSlash = false
  120. }
  121. }
  122. if (end === -1) return hasRoot ? '/' : '.'
  123. if (hasRoot && end === 1) return '//'
  124. return path.substring(0, end)
  125. }
  126. exports.basename = function basename (path, suffix) {
  127. let start = 0
  128. let end = -1
  129. let matchedSlash = true
  130. if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) {
  131. if (suffix === path) { return '' }
  132. let extIdx = suffix.length - 1
  133. let firstNonSlashEnd = -1
  134. for (let i = path.length - 1; i >= 0; --i) {
  135. const code = path.charCodeAt(i)
  136. if (code === CHAR_FORWARD_SLASH) {
  137. if (!matchedSlash) {
  138. start = i + 1
  139. break
  140. }
  141. } else {
  142. if (firstNonSlashEnd === -1) {
  143. matchedSlash = false
  144. firstNonSlashEnd = i + 1
  145. }
  146. if (extIdx >= 0) {
  147. if (code === suffix.charCodeAt(extIdx)) {
  148. if (--extIdx === -1) {
  149. end = i
  150. }
  151. } else {
  152. extIdx = -1
  153. end = firstNonSlashEnd
  154. }
  155. }
  156. }
  157. }
  158. if (start === end) end = firstNonSlashEnd
  159. else if (end === -1) end = path.length
  160. return path.substring(start, end)
  161. }
  162. for (let i = path.length - 1; i >= 0; --i) {
  163. if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) {
  164. if (!matchedSlash) {
  165. start = i + 1
  166. break
  167. }
  168. } else if (end === -1) {
  169. matchedSlash = false
  170. end = i + 1
  171. }
  172. }
  173. if (end === -1) return ''
  174. return path.substring(start, end)
  175. }
  176. exports.extname = function extname (path) {
  177. let startDot = -1
  178. let startPart = 0
  179. let end = -1
  180. let matchedSlash = true
  181. let preDotState = 0
  182. for (let i = path.length - 1; i >= 0; --i) {
  183. const code = path.charCodeAt(i)
  184. if (code === CHAR_FORWARD_SLASH) {
  185. if (!matchedSlash) {
  186. startPart = i + 1
  187. break
  188. }
  189. continue
  190. }
  191. if (end === -1) {
  192. matchedSlash = false
  193. end = i + 1
  194. }
  195. if (code === CHAR_DOT) {
  196. if (startDot === -1) startDot = i
  197. else if (preDotState !== 1) preDotState = 1
  198. } else if (startDot !== -1) {
  199. preDotState = -1
  200. }
  201. }
  202. if (startDot === -1 || end === -1 || preDotState === 0 || (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)) {
  203. return ''
  204. }
  205. return path.substring(startDot, end)
  206. }