MainFieldPlugin.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const path = require("path");
  7. const DescriptionFileUtils = require("./DescriptionFileUtils");
  8. /** @typedef {import("./Resolver")} Resolver */
  9. /** @typedef {import("./Resolver").JsonObject} JsonObject */
  10. /** @typedef {import("./Resolver").ResolveRequest} ResolveRequest */
  11. /** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */
  12. /** @typedef {{name: string|Array<string>, forceRelative: boolean}} MainFieldOptions */
  13. const alreadyTriedMainField = Symbol("alreadyTriedMainField");
  14. module.exports = class MainFieldPlugin {
  15. /**
  16. * @param {string | ResolveStepHook} source source
  17. * @param {MainFieldOptions} options options
  18. * @param {string | ResolveStepHook} target target
  19. */
  20. constructor(source, options, target) {
  21. this.source = source;
  22. this.options = options;
  23. this.target = target;
  24. }
  25. /**
  26. * @param {Resolver} resolver the resolver
  27. * @returns {void}
  28. */
  29. apply(resolver) {
  30. const target = resolver.ensureHook(this.target);
  31. resolver
  32. .getHook(this.source)
  33. .tapAsync("MainFieldPlugin", (request, resolveContext, callback) => {
  34. if (
  35. request.path !== request.descriptionFileRoot ||
  36. /** @type {ResolveRequest & { [alreadyTriedMainField]?: string }} */
  37. (request)[alreadyTriedMainField] === request.descriptionFilePath ||
  38. !request.descriptionFilePath
  39. ) {
  40. return callback();
  41. }
  42. const filename = path.basename(request.descriptionFilePath);
  43. let mainModule =
  44. /** @type {string|null|undefined} */
  45. (
  46. DescriptionFileUtils.getField(
  47. /** @type {JsonObject} */ (request.descriptionFileData),
  48. this.options.name,
  49. )
  50. );
  51. if (
  52. !mainModule ||
  53. typeof mainModule !== "string" ||
  54. mainModule === "." ||
  55. mainModule === "./"
  56. ) {
  57. return callback();
  58. }
  59. if (this.options.forceRelative && !/^\.\.?\//.test(mainModule)) {
  60. mainModule = `./${mainModule}`;
  61. }
  62. /** @type {ResolveRequest & { [alreadyTriedMainField]?: string }} */
  63. const obj = {
  64. ...request,
  65. request: mainModule,
  66. module: false,
  67. directory: mainModule.endsWith("/"),
  68. [alreadyTriedMainField]: request.descriptionFilePath,
  69. };
  70. return resolver.doResolve(
  71. target,
  72. obj,
  73. `use ${mainModule} from ${this.options.name} in ${filename}`,
  74. resolveContext,
  75. callback,
  76. );
  77. });
  78. }
  79. };