path.js 4.9 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. exports.PathType = PathType;
  30. const invalidSegmentRegEx =
  31. /(^|\\|\/)((\.|%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;
  32. exports.invalidSegmentRegEx = invalidSegmentRegEx;
  33. const deprecatedInvalidSegmentRegEx =
  34. /(^|\\|\/)((\.|%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;
  35. exports.deprecatedInvalidSegmentRegEx = deprecatedInvalidSegmentRegEx;
  36. /**
  37. * @param {string} p a path
  38. * @returns {PathType} type of path
  39. */
  40. const getType = p => {
  41. switch (p.length) {
  42. case 0:
  43. return PathType.Empty;
  44. case 1: {
  45. const c0 = p.charCodeAt(0);
  46. switch (c0) {
  47. case CHAR_DOT:
  48. return PathType.Relative;
  49. case CHAR_SLASH:
  50. return PathType.AbsolutePosix;
  51. case CHAR_HASH:
  52. return PathType.Internal;
  53. }
  54. return PathType.Normal;
  55. }
  56. case 2: {
  57. const c0 = p.charCodeAt(0);
  58. switch (c0) {
  59. case CHAR_DOT: {
  60. const c1 = p.charCodeAt(1);
  61. switch (c1) {
  62. case CHAR_DOT:
  63. case CHAR_SLASH:
  64. return PathType.Relative;
  65. }
  66. return PathType.Normal;
  67. }
  68. case CHAR_SLASH:
  69. return PathType.AbsolutePosix;
  70. case CHAR_HASH:
  71. return PathType.Internal;
  72. }
  73. const c1 = p.charCodeAt(1);
  74. if (c1 === CHAR_COLON) {
  75. if (
  76. (c0 >= CHAR_A && c0 <= CHAR_Z) ||
  77. (c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z)
  78. ) {
  79. return PathType.AbsoluteWin;
  80. }
  81. }
  82. return PathType.Normal;
  83. }
  84. }
  85. const c0 = p.charCodeAt(0);
  86. switch (c0) {
  87. case CHAR_DOT: {
  88. const c1 = p.charCodeAt(1);
  89. switch (c1) {
  90. case CHAR_SLASH:
  91. return PathType.Relative;
  92. case CHAR_DOT: {
  93. const c2 = p.charCodeAt(2);
  94. if (c2 === CHAR_SLASH) return PathType.Relative;
  95. return PathType.Normal;
  96. }
  97. }
  98. return PathType.Normal;
  99. }
  100. case CHAR_SLASH:
  101. return PathType.AbsolutePosix;
  102. case CHAR_HASH:
  103. return PathType.Internal;
  104. }
  105. const c1 = p.charCodeAt(1);
  106. if (c1 === CHAR_COLON) {
  107. const c2 = p.charCodeAt(2);
  108. if (
  109. (c2 === CHAR_BACKSLASH || c2 === CHAR_SLASH) &&
  110. ((c0 >= CHAR_A && c0 <= CHAR_Z) ||
  111. (c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z))
  112. ) {
  113. return PathType.AbsoluteWin;
  114. }
  115. }
  116. return PathType.Normal;
  117. };
  118. exports.getType = getType;
  119. /**
  120. * @param {string} p a path
  121. * @returns {string} the normalized path
  122. */
  123. const normalize = p => {
  124. switch (getType(p)) {
  125. case PathType.Empty:
  126. return p;
  127. case PathType.AbsoluteWin:
  128. return winNormalize(p);
  129. case PathType.Relative: {
  130. const r = posixNormalize(p);
  131. return getType(r) === PathType.Relative ? r : `./${r}`;
  132. }
  133. }
  134. return posixNormalize(p);
  135. };
  136. exports.normalize = normalize;
  137. /**
  138. * @param {string} rootPath the root path
  139. * @param {string | undefined} request the request path
  140. * @returns {string} the joined path
  141. */
  142. const join = (rootPath, request) => {
  143. if (!request) return normalize(rootPath);
  144. const requestType = getType(request);
  145. switch (requestType) {
  146. case PathType.AbsolutePosix:
  147. return posixNormalize(request);
  148. case PathType.AbsoluteWin:
  149. return winNormalize(request);
  150. }
  151. switch (getType(rootPath)) {
  152. case PathType.Normal:
  153. case PathType.Relative:
  154. case PathType.AbsolutePosix:
  155. return posixNormalize(`${rootPath}/${request}`);
  156. case PathType.AbsoluteWin:
  157. return winNormalize(`${rootPath}\\${request}`);
  158. }
  159. switch (requestType) {
  160. case PathType.Empty:
  161. return rootPath;
  162. case PathType.Relative: {
  163. const r = posixNormalize(rootPath);
  164. return getType(r) === PathType.Relative ? r : `./${r}`;
  165. }
  166. }
  167. return posixNormalize(rootPath);
  168. };
  169. exports.join = join;
  170. /** @type {Map<string, Map<string, string | undefined>>} */
  171. const joinCache = new Map();
  172. /**
  173. * @param {string} rootPath the root path
  174. * @param {string} request the request path
  175. * @returns {string} the joined path
  176. */
  177. const cachedJoin = (rootPath, request) => {
  178. /** @type {string | undefined} */
  179. let cacheEntry;
  180. let cache = joinCache.get(rootPath);
  181. if (cache === undefined) {
  182. joinCache.set(rootPath, (cache = new Map()));
  183. } else {
  184. cacheEntry = cache.get(request);
  185. if (cacheEntry !== undefined) return cacheEntry;
  186. }
  187. cacheEntry = join(rootPath, request);
  188. cache.set(request, cacheEntry);
  189. return cacheEntry;
  190. };
  191. exports.cachedJoin = cachedJoin;