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