path.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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. /**
  31. * @param {string} p a path
  32. * @returns {PathType} type of path
  33. */
  34. const getType = p => {
  35. switch (p.length) {
  36. case 0:
  37. return PathType.Empty;
  38. case 1: {
  39. const c0 = p.charCodeAt(0);
  40. switch (c0) {
  41. case CHAR_DOT:
  42. return PathType.Relative;
  43. case CHAR_SLASH:
  44. return PathType.AbsolutePosix;
  45. case CHAR_HASH:
  46. return PathType.Internal;
  47. }
  48. return PathType.Normal;
  49. }
  50. case 2: {
  51. const c0 = p.charCodeAt(0);
  52. switch (c0) {
  53. case CHAR_DOT: {
  54. const c1 = p.charCodeAt(1);
  55. switch (c1) {
  56. case CHAR_DOT:
  57. case CHAR_SLASH:
  58. return PathType.Relative;
  59. }
  60. return PathType.Normal;
  61. }
  62. case CHAR_SLASH:
  63. return PathType.AbsolutePosix;
  64. case CHAR_HASH:
  65. return PathType.Internal;
  66. }
  67. const c1 = p.charCodeAt(1);
  68. if (c1 === CHAR_COLON) {
  69. if (
  70. (c0 >= CHAR_A && c0 <= CHAR_Z) ||
  71. (c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z)
  72. ) {
  73. return PathType.AbsoluteWin;
  74. }
  75. }
  76. return PathType.Normal;
  77. }
  78. }
  79. const c0 = p.charCodeAt(0);
  80. switch (c0) {
  81. case CHAR_DOT: {
  82. const c1 = p.charCodeAt(1);
  83. switch (c1) {
  84. case CHAR_SLASH:
  85. return PathType.Relative;
  86. case CHAR_DOT: {
  87. const c2 = p.charCodeAt(2);
  88. if (c2 === CHAR_SLASH) return PathType.Relative;
  89. return PathType.Normal;
  90. }
  91. }
  92. return PathType.Normal;
  93. }
  94. case CHAR_SLASH:
  95. return PathType.AbsolutePosix;
  96. case CHAR_HASH:
  97. return PathType.Internal;
  98. }
  99. const c1 = p.charCodeAt(1);
  100. if (c1 === CHAR_COLON) {
  101. const c2 = p.charCodeAt(2);
  102. if (
  103. (c2 === CHAR_BACKSLASH || c2 === CHAR_SLASH) &&
  104. ((c0 >= CHAR_A && c0 <= CHAR_Z) ||
  105. (c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z))
  106. ) {
  107. return PathType.AbsoluteWin;
  108. }
  109. }
  110. return PathType.Normal;
  111. };
  112. exports.getType = getType;
  113. /**
  114. * @param {string} p a path
  115. * @returns {string} the normalized path
  116. */
  117. const normalize = p => {
  118. switch (getType(p)) {
  119. case PathType.Empty:
  120. return p;
  121. case PathType.AbsoluteWin:
  122. return winNormalize(p);
  123. case PathType.Relative: {
  124. const r = posixNormalize(p);
  125. return getType(r) === PathType.Relative ? r : `./${r}`;
  126. }
  127. }
  128. return posixNormalize(p);
  129. };
  130. exports.normalize = normalize;
  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. exports.join = join;
  164. const joinCache = new Map();
  165. /**
  166. * @param {string} rootPath the root path
  167. * @param {string | undefined} request the request path
  168. * @returns {string} the joined path
  169. */
  170. const cachedJoin = (rootPath, request) => {
  171. let cacheEntry;
  172. let cache = joinCache.get(rootPath);
  173. if (cache === undefined) {
  174. joinCache.set(rootPath, (cache = new Map()));
  175. } else {
  176. cacheEntry = cache.get(request);
  177. if (cacheEntry !== undefined) return cacheEntry;
  178. }
  179. cacheEntry = join(rootPath, request);
  180. cache.set(request, cacheEntry);
  181. return cacheEntry;
  182. };
  183. exports.cachedJoin = cachedJoin;
  184. const checkImportsExportsFieldTarget = relativePath => {
  185. let lastNonSlashIndex = 0;
  186. let slashIndex = relativePath.indexOf("/", 1);
  187. let cd = 0;
  188. while (slashIndex !== -1) {
  189. const folder = relativePath.slice(lastNonSlashIndex, slashIndex);
  190. switch (folder) {
  191. case "..": {
  192. cd--;
  193. if (cd < 0)
  194. return new Error(
  195. `Trying to access out of package scope. Requesting ${relativePath}`
  196. );
  197. break;
  198. }
  199. case ".":
  200. break;
  201. default:
  202. cd++;
  203. break;
  204. }
  205. lastNonSlashIndex = slashIndex + 1;
  206. slashIndex = relativePath.indexOf("/", lastNonSlashIndex);
  207. }
  208. };
  209. exports.checkImportsExportsFieldTarget = checkImportsExportsFieldTarget;