path.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const path = require("path");
  7. const CHAR_HASH = "#".charCodeAt(0);
  8. const CHAR_SLASH = "/".charCodeAt(0);
  9. const CHAR_BACKSLASH = "\\".charCodeAt(0);
  10. const CHAR_A = "A".charCodeAt(0);
  11. const CHAR_Z = "Z".charCodeAt(0);
  12. const CHAR_LOWER_A = "a".charCodeAt(0);
  13. const CHAR_LOWER_Z = "z".charCodeAt(0);
  14. const CHAR_DOT = ".".charCodeAt(0);
  15. const CHAR_COLON = ":".charCodeAt(0);
  16. const posixNormalize = path.posix.normalize;
  17. const winNormalize = path.win32.normalize;
  18. /**
  19. * @enum {number}
  20. */
  21. const PathType = Object.freeze({
  22. Empty: 0,
  23. Normal: 1,
  24. Relative: 2,
  25. AbsoluteWin: 3,
  26. AbsolutePosix: 4,
  27. Internal: 5,
  28. });
  29. const deprecatedInvalidSegmentRegEx =
  30. /(^|\\|\/)((\.|%2e)(\.|%2e)?|(n|%6e|%4e)(o|%6f|%4f)(d|%64|%44)(e|%65|%45)(_|%5f)(m|%6d|%4d)(o|%6f|%4f)(d|%64|%44)(u|%75|%55)(l|%6c|%4c)(e|%65|%45)(s|%73|%53))(\\|\/|$)/i;
  31. const invalidSegmentRegEx =
  32. /(^|\\|\/)((\.|%2e)(\.|%2e)?|(n|%6e|%4e)(o|%6f|%4f)(d|%64|%44)(e|%65|%45)(_|%5f)(m|%6d|%4d)(o|%6f|%4f)(d|%64|%44)(u|%75|%55)(l|%6c|%4c)(e|%65|%45)(s|%73|%53))?(\\|\/|$)/i;
  33. /**
  34. * @param {string} maybePath a path
  35. * @returns {PathType} type of path
  36. */
  37. const getType = (maybePath) => {
  38. switch (maybePath.length) {
  39. case 0:
  40. return PathType.Empty;
  41. case 1: {
  42. const c0 = maybePath.charCodeAt(0);
  43. switch (c0) {
  44. case CHAR_DOT:
  45. return PathType.Relative;
  46. case CHAR_SLASH:
  47. return PathType.AbsolutePosix;
  48. case CHAR_HASH:
  49. return PathType.Internal;
  50. }
  51. return PathType.Normal;
  52. }
  53. case 2: {
  54. const c0 = maybePath.charCodeAt(0);
  55. switch (c0) {
  56. case CHAR_DOT: {
  57. const c1 = maybePath.charCodeAt(1);
  58. switch (c1) {
  59. case CHAR_DOT:
  60. case CHAR_SLASH:
  61. return PathType.Relative;
  62. }
  63. return PathType.Normal;
  64. }
  65. case CHAR_SLASH:
  66. return PathType.AbsolutePosix;
  67. case CHAR_HASH:
  68. return PathType.Internal;
  69. }
  70. const c1 = maybePath.charCodeAt(1);
  71. if (
  72. c1 === CHAR_COLON &&
  73. ((c0 >= CHAR_A && c0 <= CHAR_Z) ||
  74. (c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z))
  75. ) {
  76. return PathType.AbsoluteWin;
  77. }
  78. return PathType.Normal;
  79. }
  80. }
  81. const c0 = maybePath.charCodeAt(0);
  82. switch (c0) {
  83. case CHAR_DOT: {
  84. const c1 = maybePath.charCodeAt(1);
  85. switch (c1) {
  86. case CHAR_SLASH:
  87. return PathType.Relative;
  88. case CHAR_DOT: {
  89. const c2 = maybePath.charCodeAt(2);
  90. if (c2 === CHAR_SLASH) return PathType.Relative;
  91. return PathType.Normal;
  92. }
  93. }
  94. return PathType.Normal;
  95. }
  96. case CHAR_SLASH:
  97. return PathType.AbsolutePosix;
  98. case CHAR_HASH:
  99. return PathType.Internal;
  100. }
  101. const c1 = maybePath.charCodeAt(1);
  102. if (c1 === CHAR_COLON) {
  103. const c2 = maybePath.charCodeAt(2);
  104. if (
  105. (c2 === CHAR_BACKSLASH || c2 === CHAR_SLASH) &&
  106. ((c0 >= CHAR_A && c0 <= CHAR_Z) ||
  107. (c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z))
  108. ) {
  109. return PathType.AbsoluteWin;
  110. }
  111. }
  112. return PathType.Normal;
  113. };
  114. /**
  115. * @param {string} maybePath a path
  116. * @returns {string} the normalized path
  117. */
  118. const normalize = (maybePath) => {
  119. switch (getType(maybePath)) {
  120. case PathType.Empty:
  121. return maybePath;
  122. case PathType.AbsoluteWin:
  123. return winNormalize(maybePath);
  124. case PathType.Relative: {
  125. const r = posixNormalize(maybePath);
  126. return getType(r) === PathType.Relative ? r : `./${r}`;
  127. }
  128. }
  129. return posixNormalize(maybePath);
  130. };
  131. /**
  132. * @param {string} rootPath the root path
  133. * @param {string | undefined} request the request path
  134. * @returns {string} the joined path
  135. */
  136. const join = (rootPath, request) => {
  137. if (!request) return normalize(rootPath);
  138. const requestType = getType(request);
  139. switch (requestType) {
  140. case PathType.AbsolutePosix:
  141. return posixNormalize(request);
  142. case PathType.AbsoluteWin:
  143. return winNormalize(request);
  144. }
  145. switch (getType(rootPath)) {
  146. case PathType.Normal:
  147. case PathType.Relative:
  148. case PathType.AbsolutePosix:
  149. return posixNormalize(`${rootPath}/${request}`);
  150. case PathType.AbsoluteWin:
  151. return winNormalize(`${rootPath}\\${request}`);
  152. }
  153. switch (requestType) {
  154. case PathType.Empty:
  155. return rootPath;
  156. case PathType.Relative: {
  157. const r = posixNormalize(rootPath);
  158. return getType(r) === PathType.Relative ? r : `./${r}`;
  159. }
  160. }
  161. return posixNormalize(rootPath);
  162. };
  163. /** @type {Map<string, Map<string, string | undefined>>} */
  164. const joinCache = new Map();
  165. /**
  166. * @param {string} rootPath the root path
  167. * @param {string} request the request path
  168. * @returns {string} the joined path
  169. */
  170. const cachedJoin = (rootPath, request) => {
  171. /** @type {string | undefined} */
  172. let cacheEntry;
  173. let cache = joinCache.get(rootPath);
  174. if (cache === undefined) {
  175. joinCache.set(rootPath, (cache = new Map()));
  176. } else {
  177. cacheEntry = cache.get(request);
  178. if (cacheEntry !== undefined) return cacheEntry;
  179. }
  180. cacheEntry = join(rootPath, request);
  181. cache.set(request, cacheEntry);
  182. return cacheEntry;
  183. };
  184. module.exports.PathType = PathType;
  185. module.exports.cachedJoin = cachedJoin;
  186. module.exports.deprecatedInvalidSegmentRegEx = deprecatedInvalidSegmentRegEx;
  187. module.exports.getType = getType;
  188. module.exports.invalidSegmentRegEx = invalidSegmentRegEx;
  189. module.exports.join = join;
  190. module.exports.normalize = normalize;