Skip to content

Commit b6dd55e

Browse files
committed
Completing error handling - showing what method to call & missing packages
1 parent 18c1594 commit b6dd55e

File tree

5 files changed

+130
-41
lines changed

5 files changed

+130
-41
lines changed

lib/config-generator.js

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const webpack = require('webpack');
22
const ExtractTextPlugin = require('extract-text-webpack-plugin');
33
const ManifestPlugin = require('./webpack-manifest-plugin');
44
const DeleteUnusedEntriesJSPlugin = require('./DeleteUnusedEntriesJSPlugin');
5-
const packageHelper = require('./package-helper');
5+
const loaderFeatures = require('./loader-features');
66
const CleanWebpackPlugin = require('clean-webpack-plugin');
77
const WebpackChunkHash = require('webpack-chunk-hash');
88
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
@@ -83,10 +83,7 @@ class ConfigGenerator
8383
},
8484
];
8585
if (this.webpackConfig.usePostCssLoader) {
86-
packageHelper.ensurePackagesExist(
87-
['postcss-loader'],
88-
'You must install the "postcss-loader" package to use enablePostCssLoader().'
89-
);
86+
loaderFeatures.ensureLoaderPackagesExist('postcss');
9087

9188
cssLoaders.push({
9289
loader: 'postcss-loader'+this.getSourceMapOption(),
@@ -114,10 +111,7 @@ class ConfigGenerator
114111
});
115112

116113
if (this.webpackConfig.useReact) {
117-
packageHelper.ensurePackagesExist(
118-
['babel-preset-react'],
119-
'You must install the "babel-preset-react" package to use enableReact().'
120-
);
114+
loaderFeatures.ensureLoaderPackagesExist('react');
121115

122116
babelConfig.presets.push('react');
123117
}
@@ -168,10 +162,7 @@ class ConfigGenerator
168162
]);
169163

170164
if (this.webpackConfig.useSassLoader) {
171-
packageHelper.ensurePackagesExist(
172-
['sass-loader', 'node-sass'],
173-
'You must install the "sass-loader" and "node-sass" packages to use enableLessLoader().'
174-
);
165+
loaderFeatures.ensureLoaderPackagesExist('sass');
175166

176167
rules.push({
177168
test: /\.s[ac]ss$/,
@@ -198,10 +189,7 @@ class ConfigGenerator
198189
}
199190

200191
if (this.webpackConfig.useLessLoader) {
201-
packageHelper.ensurePackagesExist(
202-
['less-loader'],
203-
'You must install the "less-loader" package to use enableLessLoader().'
204-
);
192+
loaderFeatures.ensureLoaderPackagesExist('less');
205193

206194
rules.push({
207195
test: /\.less/,

lib/friendly-errors/formatters/missing-loader.js

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
const chalk = require('chalk');
2+
const loaderFeatures = require('../../loader-features');
3+
const packageHelper = require('../../package-helper');
24

35
function formatErrors(errors) {
46
if (errors.length === 0) {
@@ -7,21 +9,41 @@ function formatErrors(errors) {
79

810
var messages = [];
911
for (let error of errors) {
10-
let neededCode = `Encore.${error.featureMethod}`;
1112
const fixes = [];
12-
fixes.push(`Add ${chalk.green(neededCode)} to your webpack.config.js file.`);
1313

14-
// TODO - I need some key that describes the "feature"
15-
// then, I need to be able to look up that dependency to
16-
// see what packages (if any) are missing
17-
// maybe even put the featureMethod into that file
14+
if (error.loaderName) {
15+
let neededCode = `Encore.${loaderFeatures.getLoaderFeatureMethod(error.loaderName)}`;
16+
fixes.push(`Add ${chalk.green(neededCode)} to your webpack.config.js file.`);
17+
18+
const loaderFeatureConfig = loaderFeatures.getLoaderFeatureConfig(error.loaderName);
19+
const packageRecommendations = packageHelper.getPackageRecommendations(
20+
loaderFeatureConfig.packages
21+
);
22+
23+
if (packageRecommendations) {
24+
fixes.push(`${packageRecommendations.message}\n ${packageRecommendations.yarnInstall}`);
25+
26+
}
27+
28+
// TODO - I need some key that describes the "feature"
29+
// then, I need to be able to look up that dependency to
30+
// see what packages (if any) are missing
31+
// maybe even put the featureMethod into that file
32+
} else {
33+
fixes.push('You may need to install and configure a special loader for this file type.');
34+
}
1835

1936
messages = messages.concat([
2037
chalk.red(`Error loading ${chalk.yellow(error.file)}`),
21-
'',
22-
`${chalk.bgGreen.black('', 'FIX', '')} To load ${error.loaderFileDescription}:`,
38+
''
2339
]);
2440

41+
if (error.loaderName) {
42+
messages.push(`${chalk.bgGreen.black('', 'FIX', '')} To ${loaderFeatures.getLoaderFeatureDescription(error.loaderName)}:`);
43+
} else {
44+
messages.push(`${chalk.bgGreen.black('', 'FIX', '')} To load ${error.file}:`);
45+
}
46+
2547
let index = 0;
2648
for (let fix of fixes) {
2749
messages.push(` ${++index}. ${fix}`)

lib/friendly-errors/transformers/missing-loader.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,17 @@ function transform(error) {
2525
}
2626

2727
const extension = getFileExtension(error.file);
28-
let message;
2928
switch (extension) {
3029
case 'sass':
3130
case 'scss':
32-
error.featureMethod = 'enableSassLoader()';
33-
error.loaderFileDescription = 'SASS files';
31+
error.loaderName = 'sass';
3432
break;
3533
case 'less':
36-
error.featureMethod = 'enableLessLoader()';
37-
error.loaderFileDescription = 'LESS files';
34+
error.loaderName = 'less';
3835
break;
39-
// todo - handle more
36+
case 'jsx':
37+
error.loaderName = 'react';
38+
// add more as needed
4039
default:
4140
return error;
4241
}

lib/loader-features.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
const packageHelper = require('./package-helper');
2+
3+
/**
4+
* An object that holds internal configuration about different
5+
* "loaders" that can be enabled.
6+
*/
7+
const loaderFeatures = {
8+
sass: {
9+
method: 'enableSassLoader()',
10+
packages: ['sass-loader', 'node-sass'],
11+
description: 'load SASS files'
12+
},
13+
less: {
14+
method: 'enableLessLoader()',
15+
packages: ['less-loader'],
16+
description: 'load LESS files'
17+
},
18+
postcss: {
19+
method: 'enablePostCssLoader()',
20+
packages: ['postcss-loader'],
21+
description: 'process through PostCSS'
22+
},
23+
react: {
24+
method: 'enableReact()',
25+
packages: ['babel-preset-react'],
26+
description: 'process React JS files'
27+
}
28+
};
29+
30+
function getLoaderFeatureConfig(loaderName) {
31+
if (!loaderFeatures[loaderName]) {
32+
throw new Error(`Unknown loader feature ${loaderName}`);
33+
}
34+
35+
return loaderFeatures[loaderName];
36+
}
37+
38+
module.exports = {
39+
getLoaderFeatureConfig,
40+
41+
// todo - probably remove this
42+
ensureLoaderPackagesExist: function(loaderName) {
43+
const config = getLoaderFeatureConfig(loaderName);
44+
45+
packageHelper.ensurePackagesExist(
46+
config.packages,
47+
config.method
48+
);
49+
},
50+
51+
getLoaderFeatureMethod: function(loaderName) {
52+
return getLoaderFeatureConfig(loaderName).method;
53+
},
54+
55+
getLoaderFeatureDescription: function(loaderName) {
56+
return getLoaderFeatureConfig(loaderName).description;
57+
}
58+
};

lib/package-helper.js

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
1-
function ensurePackagesExist(packageNames, error) {
2-
let missingPackageName = [];
1+
const chalk = require('chalk');
2+
3+
function ensurePackagesExist(packageNames, requestedFeature) {
4+
const recommendation = getPackageRecommendations(packageNames, requestedFeature);
5+
6+
if (!recommendation) {
7+
return;
8+
}
9+
10+
throw new Error(`
11+
${recommendation.message}
12+
${recommendation.yarnInstall}
13+
`);
14+
}
15+
16+
function getPackageRecommendations(packageNames, requestedFeature = null) {
17+
let missingPackageNames = [];
318

419
for (let packageName of packageNames) {
520
try {
@@ -13,17 +28,24 @@ function ensurePackagesExist(packageNames, error) {
1328
throw new Error(error);
1429
}
1530

16-
let message = `You must install the ${missingPackageNames.join(' and ')} package${missingPackageNames.length > 1 ? 's' : ''} to use ${requestedFeature}`;
17-
let yarnInstalls = missingPackageNames.map((packageName) => {
18-
return ` yarn add ${packageName} --dev`;
31+
const missingPackageNamesChalked = missingPackageNames.map(function(packageName) {
32+
return chalk.green(packageName);
1933
});
2034

21-
throw new Error(`
22-
${message}
23-
${yarnInstalls.join("\n")}
24-
`);
35+
let message = `Install ${missingPackageNamesChalked.join(' & ')}`;
36+
if (requestedFeature) {
37+
message += ` to use ${chalk.green(requestedFeature)}`;
38+
}
39+
40+
let yarnInstall = chalk.yellow(`yarn add ${missingPackageNames.join(' ')} --dev`);
41+
42+
return {
43+
message,
44+
yarnInstall
45+
}
2546
}
2647

2748
module.exports = {
28-
ensurePackagesExist
49+
ensurePackagesExist,
50+
getPackageRecommendations
2951
};

0 commit comments

Comments
 (0)