index.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. const path = require(`path`);
  2. const {resolveModuleName} = require(`ts-pnp`);
  3. function nothing() {
  4. // ¯\_(ツ)_/¯
  5. }
  6. function getModuleLocator(module) {
  7. const pnp = require(`pnpapi`);
  8. const moduleLocation = typeof module === `string`
  9. ? module
  10. : module.filename;
  11. if (!moduleLocation)
  12. throw new Error(`The specified module doesn't seem to exist on the filesystem`);
  13. const moduleLocator = pnp.findPackageLocator(moduleLocation);
  14. if (!moduleLocator)
  15. throw new Error(`the specified module doesn't seem to be part of the dependency tree`);
  16. return moduleLocator;
  17. }
  18. function getDependencyLocator(sourceLocator, name) {
  19. const pnp = require(`pnpapi`);
  20. const {packageDependencies} = pnp.getPackageInformation(sourceLocator);
  21. const reference = packageDependencies.get(name);
  22. return {name, reference};
  23. }
  24. function getSourceLocation(sourceLocator) {
  25. if (!sourceLocator)
  26. return null;
  27. const pnp = require(`pnpapi`);
  28. const sourceInformation = pnp.getPackageInformation(sourceLocator);
  29. if (!sourceInformation)
  30. throw new Error(`Couldn't find the package to use as resolution source`);
  31. if (!sourceInformation.packageLocation)
  32. 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?`);
  33. return sourceInformation.packageLocation.replace(/\/?$/, `/`);
  34. }
  35. function makeResolver(sourceLocator, filter) {
  36. const sourceLocation = getSourceLocation(sourceLocator);
  37. return resolver => {
  38. const BACKWARD_PATH = /^\.\.([\\\/]|$)/;
  39. const resolvedHook = resolver.ensureHook(`resolve`);
  40. // Prevents the SymlinkPlugin from kicking in. We need the symlinks to be preserved because that's how we deal with peer dependencies ambiguities.
  41. resolver.getHook(`file`).intercept({
  42. register: tapInfo => {
  43. return tapInfo.name !== `SymlinkPlugin` ? tapInfo : Object.assign({}, tapInfo, {fn: (request, resolveContext, callback) => {
  44. callback();
  45. }});
  46. }
  47. });
  48. resolver.getHook(`after-module`).tapAsync(`PnpResolver`, (request, resolveContext, callback) => {
  49. // rethrow pnp errors if we have any for this request
  50. return callback(resolveContext.pnpErrors && resolveContext.pnpErrors.get(request.context.issuer));
  51. });
  52. // Register a plugin that will resolve bare imports into the package location on the filesystem before leaving the rest of the resolution to Webpack
  53. resolver.getHook(`before-module`).tapAsync(`PnpResolver`, (requestContext, resolveContext, callback) => {
  54. const pnp = require(`pnpapi`);
  55. let request = requestContext.request;
  56. let issuer = requestContext.context.issuer;
  57. // 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)
  58. if (!issuer) {
  59. issuer = `${requestContext.path}/`;
  60. // We only support issuer when they're absolute paths. I'm not sure the opposite can ever happen, but better check here.
  61. } else if (!path.isAbsolute(issuer)) {
  62. throw new Error(`Cannot successfully resolve this dependency - issuer not supported (${issuer})`);
  63. }
  64. if (filter) {
  65. const relative = path.relative(filter, issuer);
  66. if (path.isAbsolute(relative) || BACKWARD_PATH.test(relative)) {
  67. return callback(null);
  68. }
  69. }
  70. let resolutionIssuer = sourceLocation || issuer;
  71. let resolution;
  72. try {
  73. resolution = pnp.resolveToUnqualified(request, resolutionIssuer, {considerBuiltins: false});
  74. } catch (error) {
  75. if (resolveContext.missingDependencies)
  76. resolveContext.missingDependencies.add(requestContext.path);
  77. if (resolveContext.log)
  78. resolveContext.log(error.message);
  79. resolveContext.pnpErrors = resolveContext.pnpErrors || new Map();
  80. resolveContext.pnpErrors.set(issuer, error);
  81. return callback();
  82. }
  83. resolver.doResolve(
  84. resolvedHook,
  85. Object.assign({}, requestContext, {
  86. request: resolution,
  87. }),
  88. null,
  89. resolveContext,
  90. callback
  91. );
  92. });
  93. };
  94. }
  95. module.exports = process.versions.pnp ? {
  96. apply: makeResolver(null),
  97. } : {
  98. apply: nothing,
  99. };
  100. module.exports.makePlugin = (locator, filter) => process.versions.pnp ? {
  101. apply: makeResolver(locator, filter),
  102. } : {
  103. apply: nothing,
  104. };
  105. module.exports.moduleLoader = module => process.versions.pnp ? {
  106. apply: makeResolver(getModuleLocator(module)),
  107. } : {
  108. apply: nothing,
  109. };
  110. module.exports.topLevelLoader = process.versions.pnp ? {
  111. apply: makeResolver({name: null, reference: null}),
  112. } : {
  113. apply: nothing,
  114. };
  115. module.exports.bind = (filter, module, dependency) => process.versions.pnp ? {
  116. apply: makeResolver(dependency ? getDependencyLocator(getModuleLocator(module), dependency) : getModuleLocator(module), filter),
  117. } : {
  118. apply: nothing,
  119. };
  120. module.exports.tsLoaderOptions = (options = {}) => process.versions.pnp ? Object.assign({}, options, {
  121. resolveModuleName: resolveModuleName,
  122. resolveTypeReferenceDirective: resolveModuleName,
  123. }) : options;
  124. module.exports.forkTsCheckerOptions = (options = {}) => process.versions.pnp ? Object.assign({}, options, {
  125. resolveModuleNameModule: require.resolve(`./ts`),
  126. resolveTypeReferenceDirectiveModule: require.resolve(`./ts`),
  127. }) : options;