Skip to content

Commit

Permalink
Use friendly-errors-webpack-plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
geowarin committed Aug 28, 2016
1 parent 8168a3b commit a6dc629
Show file tree
Hide file tree
Showing 15 changed files with 112 additions and 31 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Tarec takes all the best practices in the React community and makes them availab
* Babel 6 stage 0
* Tree-shaking with webpack 2
* Hot reloading with react-hmr
* Great DX experience with build notifications and clean error messages
* Pre-configured loaders for all resources (images, fonts, json, ...)
* Separate bundles for vendors and your code (css and js)
* Cache-busting
Expand Down Expand Up @@ -90,6 +91,20 @@ happypack:
Happypack is only used in development mode.
### Notifications
By default, tarec will display an os notification on build errors.
This can be very handy because you don't always have the console visible when coding.
But if you are annoyed by this feature, you can disable it:
```yml
build:
# show notifications on build failure. Default, true
showNotification: true
```
![Build error notifications](http://i.imgur.com/UN7hhJF.gif)
### Babel aliases
Create a `tarec.yml` file and configure [aliases](https://github.com/tleunen/babel-plugin-module-alias) like this:
Expand Down
1 change: 1 addition & 0 deletions lib/commands/addBuiltInCommands.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ module.exports = function addBuiltInCommands (commands) {
.option('port', { alias: 'p', type: 'number', default: 3000, describe: 'change the server port'})
.option('open', { alias: 'o', type: 'boolean', default: false, describe: 'open in your browser'})
.option('happy', { type: 'boolean', default: false, describe: 'attempts to speed up you build with happypack (multithreaded compilation)'})
.option('quiet', { type: 'boolean', default: false, describe: 'Displays no message during compilation'})
.before((context, args) => {
process.env['NODE_ENV'] = 'development';
context.serverPort = args.port;
Expand Down
13 changes: 9 additions & 4 deletions lib/commands/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,25 @@ module.exports = function start (context, args) {
app.use(require('webpack-dev-middleware')(compiler, {
noInfo: true,
publicPath: context.webpackConfig.output.publicPath,
stats: {
colors: true
quiet: true,
watchOptions: {
ignored: /node_modules/
}
}));

app.use(require('webpack-hot-middleware')(compiler));
app.use(require('webpack-hot-middleware')(compiler, {
quiet: true,
reload: true,
log: () => {}
}));

app.listen(context.serverPort, '0.0.0.0', (err) => {
if (err) {
debug.log(err);
return;
}

debug.log(`Listening at ${chalk.blue(url)}`);
debug.log(`${chalk.green('Compiling...')}`);
if (args.open) {
open(url);
}
Expand Down
3 changes: 3 additions & 0 deletions lib/config/loadUserConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ module.exports = function loadUserConfig(configFile) {
enabled: false,
cache: true,
cpus: os.cpus().length
},
build: {
showNotifications: true
}
};

Expand Down
28 changes: 17 additions & 11 deletions lib/utils/debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,23 @@ class Debugger {
this.enabled = true;
}

capture () {
this.enabled = true;
this.capturing = true;
}

endCapture () {
this.enabled = false;
this.capturing = false;
this.capturedMessages = [];
}

log (...args) {
if (this.enabled) {
this.captureConsole(args, console.log);
}
}

info (...args) {
if (this.enabled) {
this.captureConsole(args, console.info, 'green');
Expand All @@ -36,9 +47,10 @@ class Debugger {
}
}

capture () {
this.enabled = true;
this.capturing = true;
clearConsole () {
if (!this.capturing && this.enabled) {
process.stdout.write('\x1bc');
}
}

captureConsole (args, method, color) {
Expand All @@ -55,17 +67,11 @@ class Debugger {
}
return array.map(e => {
if (typeof e === 'string') {
return chalk[color](e);
return chalk[color](e);
}
return e;
})
}

endCapture () {
this.enabled = false;
this.capturing = false;
this.capturedMessages = [];
}
}

module.exports = new Debugger();
15 changes: 15 additions & 0 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

/**
* Concat and flattens non-null values.
* Ex: concat(1, undefined, 2, [3, 4]) = [1, 2, 3, 4]
*/
function concat() {
const args = Array.from(arguments).filter(e => e != null);
const baseArray = Array.isArray(args[0]) ? args[0] : [args[0]];
return Array.prototype.concat.apply(baseArray, args.slice(1));
}

module.exports = {
concat: concat
};
Binary file added lib/webpack/plugins/tarec_logo_ico.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 28 additions & 5 deletions lib/webpack/webpack.dev.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
const allLoaders = Object.assign(requireDir('../loaders'), requireDir('../loaders/dev'));
const debug = require('../utils/debug');
const chalk = require('chalk');
const NotifierPlugin = require('friendly-errors-webpack-plugin');
const notifier = require('node-notifier');
const LOGO = path.join(__dirname, 'plugins/tarec_logo_ico.png');

const {concat} = require('../utils');

const env = 'development';

Expand Down Expand Up @@ -38,18 +43,22 @@ module.exports = function devConfig(context, args) {
filename: '[name].js',
publicPath: '/'
},
plugins: [
plugins: concat(
new webpack.DefinePlugin(definitions),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new HtmlWebpackPlugin({
title: context.pkg.name,
template: context.indexPath
}),
new AddAssetHtmlPlugin(makeDlls(context))
]
.concat(...makeHappy(args, context, loaders))
.concat(...makeDllPlugins(context, args)),
new AddAssetHtmlPlugin(makeDlls(context)),
args.quiet ? null : new NotifierPlugin({
compilationSuccessMessage: `You application is accessible at ${chalk.cyan(`http://localhost:${context.serverPort}`)}`,
onErrors: (...args) => desktopNotification(context, ...args)
}),
makeHappy(args, context, loaders),
makeDllPlugins(context, args)
),
resolve: {
modules: [path.join(context.projectDir, 'src'), 'node_modules'],
extensions: ['.js', '.jsx', '.ts', '.tsx']
Expand All @@ -64,6 +73,20 @@ module.exports = function devConfig(context, args) {
}
};

function desktopNotification(context, severity, errors) {
const showNotifications = context.userConfig.build.showNotifications;
if (!showNotifications || severity !== 'error') {
return;
}
const error = errors[0];
notifier.notify({
title: context.pkg.name,
message: severity + ': ' + error.name,
subtitle: error.file || '',
icon: LOGO
});
}

function makeHappy(args, context, loaders) {
if (!context.userConfig.happypack.enabled && !args.happy) {
return [];
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"express": "4.14.0",
"extract-text-webpack-plugin": "2.0.0-beta.3",
"file-loader": "0.9.0",
"friendly-errors-webpack-plugin": "~1.0.3",
"happypack": "2.1.3",
"html-webpack-plugin": "2.22.0",
"http-proxy-middleware": "0.17.0",
Expand All @@ -60,6 +61,7 @@
"lodash.find": "4.5.1",
"lodash.groupby": "4.5.1",
"lodash.merge": "4.5.1",
"node-notifier": "4.6.0",
"open": "0.0.5",
"postcss-loader": "0.9.1",
"react-transform-catch-errors": "1.0.2",
Expand Down
6 changes: 5 additions & 1 deletion templates/app/tarec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ aliases:

plugins:
- tarec-plugin-mocha-test
- tarec-plugin-sass
- tarec-plugin-sass

build:
# show notifications on build failure. Default, true
showNotification: true
2 changes: 1 addition & 1 deletion test/Command.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const addBuiltInCommands = require('../lib/commands/addBuiltInCommands');
const test = require('ava');
const debug = require('../lib/utils/debug');

test('should add built-in commands', t => {
test('commands: should add built-in commands', t => {
const commands = new Commands();
addBuiltInCommands(commands);

Expand Down
2 changes: 1 addition & 1 deletion test/integration.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ test.cb('it-start : Should init and run', t => {
expect(nodeModulesDir).toHaveDirectories('react', 'react-dom');

const port = findOpenPort();
tarec(tmp, ['start', '-p', port]);
tarec(tmp, ['start', '-p', port, '--quiet']);

Nightmare()
.goto(`http://0.0.0.0:${port}`)
Expand Down
6 changes: 2 additions & 4 deletions test/loadUserConfig.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ const os = require('os');

test('config : should parse config', () => {
process.env['ENV_VAR'] = 'http://localhost:8181';
const projectDir = path.resolve('fixtures/config/fullConfig.yml');
const userConfig = loadUserConfig(projectDir);
const userConfig = loadUserConfig(path.resolve('fixtures/config/fullConfig.yml'));

expect(userConfig.happypack).toEqual({
enabled: true,
Expand All @@ -32,8 +31,7 @@ test('config : should parse config', () => {
});

test('config : should parse empty config', () => {
const projectDir = path.resolve('fixtures/config/emptyConfig.yml');
const userConfig = loadUserConfig(projectDir);
const userConfig = loadUserConfig(path.resolve('fixtures/config/emptyConfig.yml'));

expect(userConfig.happypack).toEqual({
enabled: false,
Expand Down
2 changes: 1 addition & 1 deletion test/utils/webpack-expect.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ function toNotHavePlugin (pluginName) {

const matchingPlugins = plugins.filter(p => p.name == pluginName);
expect.assert(
matchingPlugins.length >= 0,
matchingPlugins.length === 0,
'Plugin %s wasn\'t expected in config',
pluginName
);
Expand Down
15 changes: 12 additions & 3 deletions test/webpackConfigs.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ function createMinimalContext() {
projectDir,
rootDir: '/tarec',
userConfig: loadUserConfig(null),
pkg: {name: 'my-project'}
pkg: {name: 'my-project'},
dlls: []
};
}

Expand All @@ -34,11 +35,19 @@ test('webpack : build config should not minify if opted out', () => {
test('webpack : dev config should handle common files', t => {

const context = createMinimalContext();
context.dlls = [];
const args = {};
const devConfig = require('../lib/webpack/webpack.dev.config')(context, args);
expect(devConfig).toHandleFiles('my.css', 'my.module.css', 'my.json', 'my.woff', 'my.woff2', 'my.ttf', 'my.png');
expect(devConfig).toHavePlugin('HotModuleReplacementPlugin');
expect(devConfig).toHavePlugin('FriendlyErrorsWebpackPlugin');
});

test('webpack : dev config should disable friendly errors', t => {

const context = createMinimalContext();
const args = {quiet: true};
const devConfig = require('../lib/webpack/webpack.dev.config')(context, args);
expect(devConfig).toNotHavePlugin('FriendlyErrorsWebpackPlugin');
});

test('webpack : dev config should handle dlls', t => {
Expand All @@ -62,7 +71,7 @@ test('webpack : dev config should handle dlls', t => {
expect(devConfig).toHavePlugin('AddAssetHtmlPlugin', expectedAssetPluginOptions);
});

test('webpack : happyPack', () => {
test('webpack : dev config should configure happyPack', () => {

const context = createMinimalContext();
context.dlls = [];
Expand Down

0 comments on commit a6dc629

Please sign in to comment.