ExtensionAliasPlugin.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Ivan Kopeykin @vankop
  4. */
  5. "use strict";
  6. const forEachBail = require("./forEachBail");
  7. /** @typedef {import("./Resolver")} Resolver */
  8. /** @typedef {import("./Resolver").ResolveRequest} ResolveRequest */
  9. /** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */
  10. /** @typedef {{ alias: string|string[], extension: string }} ExtensionAliasOption */
  11. module.exports = class ExtensionAliasPlugin {
  12. /**
  13. * @param {string | ResolveStepHook} source source
  14. * @param {ExtensionAliasOption} options options
  15. * @param {string | ResolveStepHook} target target
  16. */
  17. constructor(source, options, target) {
  18. this.source = source;
  19. this.options = options;
  20. this.target = target;
  21. }
  22. /**
  23. * @param {Resolver} resolver the resolver
  24. * @returns {void}
  25. */
  26. apply(resolver) {
  27. const target = resolver.ensureHook(this.target);
  28. const { extension, alias } = this.options;
  29. resolver
  30. .getHook(this.source)
  31. .tapAsync("ExtensionAliasPlugin", (request, resolveContext, callback) => {
  32. const requestPath = request.request;
  33. if (!requestPath || !requestPath.endsWith(extension)) return callback();
  34. const isAliasString = typeof alias === "string";
  35. /**
  36. * @param {string} alias extension alias
  37. * @param {(err?: null|Error, result?: null|ResolveRequest) => void} callback callback
  38. * @param {number} [index] index
  39. * @returns {void}
  40. */
  41. const resolve = (alias, callback, index) => {
  42. const newRequest = `${requestPath.slice(
  43. 0,
  44. -extension.length
  45. )}${alias}`;
  46. return resolver.doResolve(
  47. target,
  48. {
  49. ...request,
  50. request: newRequest,
  51. fullySpecified: true
  52. },
  53. `aliased from extension alias with mapping '${extension}' to '${alias}'`,
  54. resolveContext,
  55. (err, result) => {
  56. // Throw error if we are on the last alias (for multiple aliases) and it failed, always throw if we are not an array or we have only one alias
  57. if (!isAliasString && index) {
  58. if (index !== this.options.alias.length) {
  59. if (resolveContext.log) {
  60. resolveContext.log(
  61. `Failed to alias from extension alias with mapping '${extension}' to '${alias}' for '${newRequest}': ${err}`
  62. );
  63. }
  64. return callback(null, result);
  65. }
  66. return callback(err, result);
  67. } else {
  68. callback(err, result);
  69. }
  70. }
  71. );
  72. };
  73. /**
  74. * @param {null|Error} [err] error
  75. * @param {null|ResolveRequest} [result] result
  76. * @returns {void}
  77. */
  78. const stoppingCallback = (err, result) => {
  79. if (err) return callback(err);
  80. if (result) return callback(null, result);
  81. // Don't allow other aliasing or raw request
  82. return callback(null, null);
  83. };
  84. if (isAliasString) {
  85. resolve(alias, stoppingCallback);
  86. } else if (alias.length > 1) {
  87. forEachBail(alias, resolve, stoppingCallback);
  88. } else {
  89. resolve(alias[0], stoppingCallback);
  90. }
  91. });
  92. }
  93. };