getPaths.js 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. /** @typedef {import("./Resolver").FileSystem} FileSystem */
  7. /** @typedef {{ paths: string[], segments: string[] }} GetPathsResult */
  8. /**
  9. * Walk `path` from tip to root, returning every ancestor directory (plus the
  10. * input itself) in `paths`, and each corresponding segment name in `segments`.
  11. *
  12. * The return value may be shared across callers via `getPathsCached` — treat
  13. * it as read-only. Callers that need to mutate (currently only
  14. * `SymlinkPlugin`) should `slice()` the arrays locally before writing.
  15. * @param {string} path path
  16. * @returns {GetPathsResult} paths and segments
  17. */
  18. function getPaths(path) {
  19. if (path === "/") return { paths: ["/"], segments: [""] };
  20. const parts = path.split(/(.*?[\\/]+)/);
  21. const paths = [path];
  22. const segments = [parts[parts.length - 1]];
  23. let part = parts[parts.length - 1];
  24. path = path.slice(0, Math.max(0, path.length - part.length - 1));
  25. for (let i = parts.length - 2; i > 2; i -= 2) {
  26. paths.push(path);
  27. part = parts[i];
  28. path = path.slice(0, Math.max(0, path.length - part.length)) || "/";
  29. segments.push(part.slice(0, -1));
  30. }
  31. [, part] = parts;
  32. segments.push(part);
  33. paths.push(part);
  34. return {
  35. paths,
  36. segments,
  37. };
  38. }
  39. /**
  40. * Per-filesystem memoization of `getPaths`. Kept in a standalone WeakMap
  41. * rather than being hung off `resolver.pathCache` so that adding this cache
  42. * does not change the hidden-class shape of `pathCache` — which is accessed
  43. * on the hot path of every resolve as `resolver.pathCache.{join,dirname,
  44. * basename}.fn(...)`. CodSpeed caught that shape change as a ~1–2%
  45. * instruction-count regression on `cache-predicate`, so we keep pathCache
  46. * shape-stable by owning this cache here instead.
  47. *
  48. * The cache lifetime is tied to the filesystem object (same invariant as
  49. * `_pathCacheByFs` in `Resolver.js`): when the user swaps filesystems the
  50. * entries become unreachable and get collected.
  51. * @type {WeakMap<FileSystem, Map<string, GetPathsResult>>}
  52. */
  53. const _getPathsCacheByFs = new WeakMap();
  54. /**
  55. * Memoized `getPaths`. The returned object is shared across callers — do
  56. * not mutate the `paths` or `segments` arrays in-place; `slice()` first if
  57. * you need a mutable copy.
  58. * @param {FileSystem} fileSystem filesystem used as the cache namespace
  59. * @param {string} path path
  60. * @returns {GetPathsResult} paths and segments
  61. */
  62. function getPathsCached(fileSystem, path) {
  63. let cache = _getPathsCacheByFs.get(fileSystem);
  64. if (cache === undefined) {
  65. cache = new Map();
  66. _getPathsCacheByFs.set(fileSystem, cache);
  67. } else {
  68. const cached = cache.get(path);
  69. if (cached !== undefined) return cached;
  70. }
  71. const result = getPaths(path);
  72. cache.set(path, result);
  73. return result;
  74. }
  75. module.exports = getPaths;
  76. module.exports.getPathsCached = getPathsCached;