identifier.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. "use strict";
  2. const path = require("path");
  3. /**
  4. * @typedef {Object} MakeRelativePathsCache
  5. * @property {Map<string, Map<string, string>>=} relativePaths
  6. */
  7. /**
  8. *
  9. * @param {string} maybeAbsolutePath path to check
  10. * @returns {boolean} returns true if path is "Absolute Path"-like
  11. */
  12. const looksLikeAbsolutePath = maybeAbsolutePath => {
  13. if (/^\/.*\/$/.test(maybeAbsolutePath)) {
  14. // this 'path' is actually a regexp generated by dynamic requires.
  15. // Don't treat it as an absolute path.
  16. return false;
  17. }
  18. return /^(?:[a-z]:\\|\/)/i.test(maybeAbsolutePath);
  19. };
  20. /**
  21. *
  22. * @param {string} p path to normalize
  23. * @returns {string} normalized version of path
  24. */
  25. const normalizePathSeparator = p => p.replace(/\\/g, "/");
  26. /**
  27. *
  28. * @param {string} context context for relative path
  29. * @param {string} identifier identifier for path
  30. * @returns {string} a converted relative path
  31. */
  32. const _makePathsRelative = (context, identifier) => {
  33. return identifier
  34. .split(/([|! ])/)
  35. .map(
  36. str =>
  37. looksLikeAbsolutePath(str)
  38. ? normalizePathSeparator(path.relative(context, str))
  39. : str
  40. )
  41. .join("");
  42. };
  43. /**
  44. *
  45. * @param {string} context context used to create relative path
  46. * @param {string} identifier identifier used to create relative path
  47. * @param {MakeRelativePathsCache=} cache the cache object being set
  48. * @returns {string} the returned relative path
  49. */
  50. exports.makePathsRelative = (context, identifier, cache) => {
  51. if (!cache) return _makePathsRelative(context, identifier);
  52. const relativePaths =
  53. cache.relativePaths || (cache.relativePaths = new Map());
  54. let cachedResult;
  55. let contextCache = relativePaths.get(context);
  56. if (contextCache === undefined) {
  57. relativePaths.set(context, (contextCache = new Map()));
  58. } else {
  59. cachedResult = contextCache.get(identifier);
  60. }
  61. if (cachedResult !== undefined) {
  62. return cachedResult;
  63. } else {
  64. const relativePath = _makePathsRelative(context, identifier);
  65. contextCache.set(identifier, relativePath);
  66. return relativePath;
  67. }
  68. };
  69. /**
  70. * @param {string} context absolute context path
  71. * @param {string} request any request string may containing absolute paths, query string, etc.
  72. * @returns {string} a new request string avoiding absolute paths when possible
  73. */
  74. exports.contextify = (context, request) => {
  75. return request
  76. .split("!")
  77. .map(r => {
  78. const splitPath = r.split("?", 2);
  79. if (/^[a-zA-Z]:\\/.test(splitPath[0])) {
  80. splitPath[0] = path.win32.relative(context, splitPath[0]);
  81. if (!/^[a-zA-Z]:\\/.test(splitPath[0])) {
  82. splitPath[0] = splitPath[0].replace(/\\/g, "/");
  83. }
  84. }
  85. if (/^\//.test(splitPath[0])) {
  86. splitPath[0] = path.posix.relative(context, splitPath[0]);
  87. }
  88. if (!/^(\.\.\/|\/|[a-zA-Z]:\\)/.test(splitPath[0])) {
  89. splitPath[0] = "./" + splitPath[0];
  90. }
  91. return splitPath.join("?");
  92. })
  93. .join("!");
  94. };