AliasUtils.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const forEachBail = require("./forEachBail");
  7. const { PathType, getType } = require("./util/path");
  8. /** @typedef {import("./Resolver")} Resolver */
  9. /** @typedef {import("./Resolver").ResolveRequest} ResolveRequest */
  10. /** @typedef {import("./Resolver").ResolveContext} ResolveContext */
  11. /** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */
  12. /** @typedef {import("./Resolver").ResolveCallback} ResolveCallback */
  13. /** @typedef {string | string[] | false} Alias */
  14. /** @typedef {{ alias: Alias, name: string, onlyModule?: boolean }} AliasOption */
  15. /** @typedef {(err?: null | Error, result?: null | ResolveRequest) => void} InnerCallback */
  16. /**
  17. * @param {Resolver} resolver resolver
  18. * @param {AliasOption[]} options options
  19. * @param {ResolveStepHook} target target
  20. * @param {ResolveRequest} request request
  21. * @param {ResolveContext} resolveContext resolve context
  22. * @param {InnerCallback} callback callback
  23. * @returns {void}
  24. */
  25. function aliasResolveHandler(
  26. resolver,
  27. options,
  28. target,
  29. request,
  30. resolveContext,
  31. callback,
  32. ) {
  33. const innerRequest = request.request || request.path;
  34. if (!innerRequest) return callback();
  35. /**
  36. * @param {string} maybeAbsolutePath path
  37. * @returns {null | string} absolute path with slash ending
  38. */
  39. const getAbsolutePathWithSlashEnding = (maybeAbsolutePath) => {
  40. const type = getType(maybeAbsolutePath);
  41. if (type === PathType.AbsolutePosix || type === PathType.AbsoluteWin) {
  42. return resolver.join(maybeAbsolutePath, "_").slice(0, -1);
  43. }
  44. return null;
  45. };
  46. /**
  47. * @param {string} path path
  48. * @param {string} maybeSubPath sub path
  49. * @returns {boolean} true, if path is sub path
  50. */
  51. const isSubPath = (path, maybeSubPath) => {
  52. const absolutePath = getAbsolutePathWithSlashEnding(maybeSubPath);
  53. if (!absolutePath) return false;
  54. return path.startsWith(absolutePath);
  55. };
  56. forEachBail(
  57. options,
  58. (item, callback) => {
  59. /** @type {boolean} */
  60. let shouldStop = false;
  61. const matchRequest =
  62. innerRequest === item.name ||
  63. (!item.onlyModule &&
  64. (request.request
  65. ? innerRequest.startsWith(`${item.name}/`)
  66. : isSubPath(innerRequest, item.name)));
  67. const splitName = item.name.split("*");
  68. const matchWildcard = !item.onlyModule && splitName.length === 2;
  69. if (matchRequest || matchWildcard) {
  70. /**
  71. * @param {Alias} alias alias
  72. * @param {(err?: null | Error, result?: null | ResolveRequest) => void} callback callback
  73. * @returns {void}
  74. */
  75. const resolveWithAlias = (alias, callback) => {
  76. if (alias === false) {
  77. /** @type {ResolveRequest} */
  78. const ignoreObj = {
  79. ...request,
  80. path: false,
  81. };
  82. if (typeof resolveContext.yield === "function") {
  83. resolveContext.yield(ignoreObj);
  84. return callback(null, null);
  85. }
  86. return callback(null, ignoreObj);
  87. }
  88. let newRequestStr;
  89. const [prefix, suffix] = splitName;
  90. if (
  91. matchWildcard &&
  92. innerRequest.startsWith(prefix) &&
  93. innerRequest.endsWith(suffix)
  94. ) {
  95. const match = innerRequest.slice(
  96. prefix.length,
  97. innerRequest.length - suffix.length,
  98. );
  99. newRequestStr = alias.toString().replace("*", match);
  100. }
  101. if (
  102. matchRequest &&
  103. innerRequest !== alias &&
  104. !innerRequest.startsWith(`${alias}/`)
  105. ) {
  106. /** @type {string} */
  107. const remainingRequest = innerRequest.slice(item.name.length);
  108. newRequestStr = alias + remainingRequest;
  109. }
  110. if (newRequestStr !== undefined) {
  111. shouldStop = true;
  112. /** @type {ResolveRequest} */
  113. const obj = {
  114. ...request,
  115. request: newRequestStr,
  116. fullySpecified: false,
  117. };
  118. return resolver.doResolve(
  119. target,
  120. obj,
  121. `aliased with mapping '${item.name}': '${alias}' to '${newRequestStr}'`,
  122. resolveContext,
  123. (err, result) => {
  124. if (err) return callback(err);
  125. if (result) return callback(null, result);
  126. return callback();
  127. },
  128. );
  129. }
  130. return callback();
  131. };
  132. /**
  133. * @param {(null | Error)=} err error
  134. * @param {(null | ResolveRequest)=} result result
  135. * @returns {void}
  136. */
  137. const stoppingCallback = (err, result) => {
  138. if (err) return callback(err);
  139. if (result) return callback(null, result);
  140. // Don't allow other aliasing or raw request
  141. if (shouldStop) return callback(null, null);
  142. return callback();
  143. };
  144. if (Array.isArray(item.alias)) {
  145. return forEachBail(item.alias, resolveWithAlias, stoppingCallback);
  146. }
  147. return resolveWithAlias(item.alias, stoppingCallback);
  148. }
  149. return callback();
  150. },
  151. callback,
  152. );
  153. }
  154. module.exports.aliasResolveHandler = aliasResolveHandler;