ModuleNotFoundPlugin.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /**
  2. * Copyright (c) 2015-present, Facebook, Inc.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. 'use strict';
  8. const chalk = require('chalk');
  9. const findUp = require('find-up');
  10. const path = require('path');
  11. class ModuleNotFoundPlugin {
  12. constructor(appPath, yarnLockFile) {
  13. this.appPath = appPath;
  14. this.yarnLockFile = yarnLockFile;
  15. this.useYarnCommand = this.useYarnCommand.bind(this);
  16. this.getRelativePath = this.getRelativePath.bind(this);
  17. this.prettierError = this.prettierError.bind(this);
  18. }
  19. useYarnCommand() {
  20. try {
  21. return findUp.sync('yarn.lock', { cwd: this.appPath }) != null;
  22. } catch (_) {
  23. return false;
  24. }
  25. }
  26. getRelativePath(_file) {
  27. let file = path.relative(this.appPath, _file);
  28. if (file.startsWith('..')) {
  29. file = _file;
  30. } else if (!file.startsWith('.')) {
  31. file = '.' + path.sep + file;
  32. }
  33. return file;
  34. }
  35. prettierError(err) {
  36. let { details: _details = '', origin } = err;
  37. if (origin == null) {
  38. const caseSensitivity =
  39. err.message &&
  40. /\[CaseSensitivePathsPlugin\] `(.*?)` .* `(.*?)`/.exec(err.message);
  41. if (caseSensitivity) {
  42. const [, incorrectPath, actualName] = caseSensitivity;
  43. const actualFile = this.getRelativePath(
  44. path.join(path.dirname(incorrectPath), actualName)
  45. );
  46. const incorrectName = path.basename(incorrectPath);
  47. err.message = `Cannot find file: '${incorrectName}' does not match the corresponding name on disk: '${actualFile}'.`;
  48. }
  49. return err;
  50. }
  51. const file = this.getRelativePath(origin.resource);
  52. let details = _details.split('\n');
  53. const request = /resolve '(.*?)' in '(.*?)'/.exec(details);
  54. if (request) {
  55. const isModule = details[1] && details[1].includes('module');
  56. const isFile = details[1] && details[1].includes('file');
  57. let [, target, context] = request;
  58. context = this.getRelativePath(context);
  59. if (isModule) {
  60. const isYarn = this.useYarnCommand();
  61. details = [
  62. `Cannot find module: '${target}'. Make sure this package is installed.`,
  63. '',
  64. 'You can install this package by running: ' +
  65. (isYarn
  66. ? chalk.bold(`yarn add ${target}`)
  67. : chalk.bold(`npm install ${target}`)) +
  68. '.',
  69. ];
  70. } else if (isFile) {
  71. details = [`Cannot find file '${target}' in '${context}'.`];
  72. } else {
  73. details = [err.message];
  74. }
  75. } else {
  76. details = [err.message];
  77. }
  78. err.message = [file, ...details].join('\n').replace('Error: ', '');
  79. const isModuleScopePluginError =
  80. err.error && err.error.__module_scope_plugin;
  81. if (isModuleScopePluginError) {
  82. err.message = err.message.replace('Module not found: ', '');
  83. }
  84. return err;
  85. }
  86. apply(compiler) {
  87. const { prettierError } = this;
  88. compiler.hooks.make.intercept({
  89. register(tap) {
  90. if (
  91. !(tap.name === 'MultiEntryPlugin' || tap.name === 'SingleEntryPlugin')
  92. ) {
  93. return tap;
  94. }
  95. return Object.assign({}, tap, {
  96. fn: (compilation, callback) => {
  97. tap.fn(compilation, (err, ...args) => {
  98. if (err && err.name === 'ModuleNotFoundError') {
  99. err = prettierError(err);
  100. }
  101. callback(err, ...args);
  102. });
  103. },
  104. });
  105. },
  106. });
  107. compiler.hooks.normalModuleFactory.tap('ModuleNotFoundPlugin', nmf => {
  108. nmf.hooks.afterResolve.intercept({
  109. register(tap) {
  110. if (tap.name !== 'CaseSensitivePathsPlugin') {
  111. return tap;
  112. }
  113. return Object.assign({}, tap, {
  114. fn: (compilation, callback) => {
  115. tap.fn(compilation, (err, ...args) => {
  116. if (
  117. err &&
  118. err.message &&
  119. err.message.includes('CaseSensitivePathsPlugin')
  120. ) {
  121. err = prettierError(err);
  122. }
  123. callback(err, ...args);
  124. });
  125. },
  126. });
  127. },
  128. });
  129. });
  130. }
  131. }
  132. module.exports = ModuleNotFoundPlugin;