You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

110 lines
3.2 KiB

  1. let pnp;
  2. try {
  3. pnp = require(`pnpapi`);
  4. } catch (error) {
  5. // not in PnP; not a problem
  6. }
  7. function nothing() {
  8. // ¯\_(ツ)_/¯
  9. }
  10. function getModuleLocator(module) {
  11. const moduleLocation = module.filename;
  12. if (!moduleLocation)
  13. throw new Error(`The specified module doesn't seem to exist on the filesystem`);
  14. const moduleLocator = pnp.findPackageLocator(moduleLocation);
  15. if (!moduleLocator)
  16. throw new Error(`the specified module doesn't seem to be part of the dependency tree`);
  17. return moduleLocator;
  18. }
  19. function getSourceLocation(sourceLocator) {
  20. if (!sourceLocator)
  21. return null;
  22. const sourceInformation = pnp.getPackageInformation(sourceLocator);
  23. if (!sourceInformation)
  24. throw new Error(`Couldn't find the package to use as resolution source`);
  25. if (!sourceInformation.packageLocation)
  26. throw new Error(`The package to use as resolution source seem to not have been installed - maybe it's a devDependency not installed in prod?`);
  27. return sourceInformation.packageLocation.replace(/\/?$/, `/`);
  28. }
  29. function makeResolver(sourceLocator) {
  30. const sourceLocation = getSourceLocation(sourceLocator);
  31. return resolver => {
  32. const MAYBE_BUILTIN = /^[^\/]$/;
  33. const resolvedHook = resolver.ensureHook(`resolve`);
  34. // Prevents the SymlinkPlugin from kicking in. We need the symlinks to be preserved because that's how we deal with peer dependencies ambiguities.
  35. resolver.getHook(`file`).intercept({
  36. register: tapInfo => {
  37. return tapInfo.name !== `SymlinkPlugin` ? tapInfo : Object.assign({}, tapInfo, {fn: (request, resolveContext, callback) => {
  38. callback();
  39. }});
  40. }
  41. });
  42. // Register a plugin that will resolve bare imports into the package location on the filesystem before leaving the rest of the resolution to Webpack
  43. resolver.getHook(`before-module`).tapAsync(`PnpResolver`, (requestContext, resolveContext, callback) => {
  44. let request = requestContext.request;
  45. let issuer = sourceLocation || requestContext.context.issuer;
  46. // When using require.context, issuer seems to be false (cf https://github.com/webpack/webpack-dev-server/blob/d0725c98fb752d8c0b1e8c9067e526e22b5f5134/client-src/default/index.js#L94)
  47. if (!issuer) {
  48. issuer = `${requestContext.path}/`;
  49. // We only support issuer when they're absolute paths. I'm not sure the opposite can ever happen, but better check here.
  50. } else if (!issuer.startsWith(`/`)) {
  51. throw new Error(`Cannot successfully resolve this dependency - issuer not supported (${issuer})`);
  52. }
  53. let resolution;
  54. try {
  55. resolution = pnp.resolveToUnqualified(request, issuer, {considerBuiltins: false});
  56. } catch (error) {
  57. return callback(error);
  58. }
  59. resolver.doResolve(
  60. resolvedHook,
  61. Object.assign({}, requestContext, {
  62. request: resolution,
  63. }),
  64. null,
  65. resolveContext,
  66. callback
  67. );
  68. });
  69. };
  70. }
  71. module.exports = pnp ? {
  72. apply: makeResolver(null),
  73. } : {
  74. apply: nothing,
  75. };
  76. module.exports.moduleLoader = module => pnp ? {
  77. apply: makeResolver(getModuleLocator(module)),
  78. } : {
  79. apply: nothing,
  80. };
  81. module.exports.topLevelLoader = pnp ? {
  82. apply: makeResolver(pnp.topLevel),
  83. } : {
  84. apply: nothing,
  85. };