AliasPlugin.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  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").ResolveStepHook} ResolveStepHook */
  11. /** @typedef {string | Array<string> | false} Alias */
  12. /** @typedef {{alias: Alias, name: string, onlyModule?: boolean}} AliasOption */
  13. module.exports = class AliasPlugin {
  14. /**
  15. * @param {string | ResolveStepHook} source source
  16. * @param {AliasOption | Array<AliasOption>} options options
  17. * @param {string | ResolveStepHook} target target
  18. */
  19. constructor(source, options, target) {
  20. this.source = source;
  21. this.options = Array.isArray(options) ? options : [options];
  22. this.target = target;
  23. }
  24. /**
  25. * @param {Resolver} resolver the resolver
  26. * @returns {void}
  27. */
  28. apply(resolver) {
  29. const target = resolver.ensureHook(this.target);
  30. /**
  31. * @param {string} maybeAbsolutePath path
  32. * @returns {null|string} absolute path with slash ending
  33. */
  34. const getAbsolutePathWithSlashEnding = maybeAbsolutePath => {
  35. const type = getType(maybeAbsolutePath);
  36. if (type === PathType.AbsolutePosix || type === PathType.AbsoluteWin) {
  37. return resolver.join(maybeAbsolutePath, "_").slice(0, -1);
  38. }
  39. return null;
  40. };
  41. /**
  42. * @param {string} path path
  43. * @param {string} maybeSubPath sub path
  44. * @returns {boolean} true, if path is sub path
  45. */
  46. const isSubPath = (path, maybeSubPath) => {
  47. const absolutePath = getAbsolutePathWithSlashEnding(maybeSubPath);
  48. if (!absolutePath) return false;
  49. return path.startsWith(absolutePath);
  50. };
  51. resolver
  52. .getHook(this.source)
  53. .tapAsync("AliasPlugin", (request, resolveContext, callback) => {
  54. const innerRequest = request.request || request.path;
  55. if (!innerRequest) return callback();
  56. forEachBail(
  57. this.options,
  58. (item, callback) => {
  59. /** @type {boolean} */
  60. let shouldStop = false;
  61. if (
  62. innerRequest === item.name ||
  63. (!item.onlyModule &&
  64. (request.request
  65. ? innerRequest.startsWith(`${item.name}/`)
  66. : isSubPath(innerRequest, item.name)))
  67. ) {
  68. /** @type {string} */
  69. const remainingRequest = innerRequest.slice(item.name.length);
  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. if (
  89. innerRequest !== alias &&
  90. !innerRequest.startsWith(alias + "/")
  91. ) {
  92. shouldStop = true;
  93. const newRequestStr = alias + remainingRequest;
  94. /** @type {ResolveRequest} */
  95. const obj = {
  96. ...request,
  97. request: newRequestStr,
  98. fullySpecified: false
  99. };
  100. return resolver.doResolve(
  101. target,
  102. obj,
  103. "aliased with mapping '" +
  104. item.name +
  105. "': '" +
  106. alias +
  107. "' to '" +
  108. newRequestStr +
  109. "'",
  110. resolveContext,
  111. (err, result) => {
  112. if (err) return callback(err);
  113. if (result) return callback(null, result);
  114. return callback();
  115. }
  116. );
  117. }
  118. return callback();
  119. };
  120. /**
  121. * @param {null|Error} [err] error
  122. * @param {null|ResolveRequest} [result] result
  123. * @returns {void}
  124. */
  125. const stoppingCallback = (err, result) => {
  126. if (err) return callback(err);
  127. if (result) return callback(null, result);
  128. // Don't allow other aliasing or raw request
  129. if (shouldStop) return callback(null, null);
  130. return callback();
  131. };
  132. if (Array.isArray(item.alias)) {
  133. return forEachBail(
  134. item.alias,
  135. resolveWithAlias,
  136. stoppingCallback
  137. );
  138. } else {
  139. return resolveWithAlias(item.alias, stoppingCallback);
  140. }
  141. }
  142. return callback();
  143. },
  144. callback
  145. );
  146. });
  147. }
  148. };