PnpPlugin.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Maël Nison @arcanis
  4. */
  5. "use strict";
  6. /** @typedef {import("./Resolver")} Resolver */
  7. /** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */
  8. /**
  9. * @typedef {Object} PnpApiImpl
  10. * @property {function(string, string, object): string} resolveToUnqualified
  11. */
  12. module.exports = class PnpPlugin {
  13. /**
  14. * @param {string | ResolveStepHook} source source
  15. * @param {PnpApiImpl} pnpApi pnpApi
  16. * @param {string | ResolveStepHook} target target
  17. */
  18. constructor(source, pnpApi, target) {
  19. this.source = source;
  20. this.pnpApi = pnpApi;
  21. this.target = target;
  22. }
  23. /**
  24. * @param {Resolver} resolver the resolver
  25. * @returns {void}
  26. */
  27. apply(resolver) {
  28. const target = resolver.ensureHook(this.target);
  29. resolver
  30. .getHook(this.source)
  31. .tapAsync("PnpPlugin", (request, resolveContext, callback) => {
  32. const req = request.request;
  33. if (!req) return callback();
  34. // The trailing slash indicates to PnP that this value is a folder rather than a file
  35. const issuer = `${request.path}/`;
  36. const packageMatch = /^(@[^/]+\/)?[^/]+/.exec(req);
  37. if (!packageMatch) return callback();
  38. const packageName = packageMatch[0];
  39. const innerRequest = `.${req.slice(packageName.length)}`;
  40. let resolution;
  41. let apiResolution;
  42. try {
  43. resolution = this.pnpApi.resolveToUnqualified(packageName, issuer, {
  44. considerBuiltins: false
  45. });
  46. if (resolveContext.fileDependencies) {
  47. apiResolution = this.pnpApi.resolveToUnqualified("pnpapi", issuer, {
  48. considerBuiltins: false
  49. });
  50. }
  51. } catch (error) {
  52. if (
  53. error.code === "MODULE_NOT_FOUND" &&
  54. error.pnpCode === "UNDECLARED_DEPENDENCY"
  55. ) {
  56. // This is not a PnP managed dependency.
  57. // Try to continue resolving with our alternatives
  58. if (resolveContext.log) {
  59. resolveContext.log(`request is not managed by the pnpapi`);
  60. for (const line of error.message.split("\n").filter(Boolean))
  61. resolveContext.log(` ${line}`);
  62. }
  63. return callback();
  64. }
  65. return callback(error);
  66. }
  67. if (resolution === packageName) return callback();
  68. if (apiResolution && resolveContext.fileDependencies) {
  69. resolveContext.fileDependencies.add(apiResolution);
  70. }
  71. const obj = {
  72. ...request,
  73. path: resolution,
  74. request: innerRequest,
  75. ignoreSymlinks: true,
  76. fullySpecified: request.fullySpecified && innerRequest !== "."
  77. };
  78. resolver.doResolve(
  79. target,
  80. obj,
  81. `resolved by pnp to ${resolution}`,
  82. resolveContext,
  83. (err, result) => {
  84. if (err) return callback(err);
  85. if (result) return callback(null, result);
  86. // Skip alternatives
  87. return callback(null, null);
  88. }
  89. );
  90. });
  91. }
  92. };