Gulp: webpack and ES6 - fevrcoding/wok GitHub Wiki
This recipe will guide you through the installation and configuration of webpack4 and Babel (ES6) in the WOK workflow
Install babel polyfill, core, and webpack and its babel loader
yarn add babel-core babel-loader babel-plugin-transform-runtime babel-preset-env babel-preset-stage-2 webpack json-loader --save-dev
yarn add babel-runtime babel-polyfill --save
Note: you may replace yarn add
with npm install
.
Create a webpack.conf.js
file under build/gulp-config
folder and paste the following code:
module.exports = (options) => {
const path = require('path');
const paths = require('./paths');
const template = require('lodash/template');
const webpack = require('webpack');
const srcPath = paths.toAbsPath('src.assets');
const destPath = paths.toPath('dist.assets');
const plugins = [];
plugins.push(
new webpack.NoEmitOnErrorsPlugin(),
new webpack.DefinePlugin({
__PRODUCTION__: options.production,
__TARGET__: JSON.stringify(options.target),
__TARGET_HOST__: JSON.stringify(options.hosts[options.target] || {}),
'process.env': {
'NODE_ENV': JSON.stringify(options.production ? 'production' : 'development') //eslint-disable-line quote-props
}
})
);
if (options.production) {
plugins.push(
new webpack.LoaderOptionsPlugin({
minimize: true
}),
new webpack.BannerPlugin({
banner: template(options.banners.application)(options),
entryOnly: true,
raw: true
})
);
}
return {
context: srcPath,
entry: {},
output: {
path: path.join(process.cwd(), destPath),
publicPath: destPath.replace(paths.toPath('dist.root'), '').replace(/\\/g, '/') + '/',
filename: `${paths.get('js')}/[name].js`
},
watch: !!options.isWatching,
devtool: (options.production ? '#source-map' : '#cheap-module-source-map'),
mode: options.production ? "production" : "development",
plugins,
target: 'web',
performance: {
hints: false
},
node: {
fs: 'empty'
},
module: {
rules: [
{ parser: { amd: false } },
{
test: /\.js$/,
include: [
paths.toAbsPath('src.assets/js')
],
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}, {
test: /\.json$/,
exclude: /(node_modules|vendors)/,
loader: 'json-loader'
}
]
},
resolve: {
modules: ['node_modules', paths.toAbsPath('src.assets/vendors')]
}
};
};
Next, ensure you have a .babelrc
file in the project root that looks something like this:
{
"presets": [
["env", {
"modules": false,
"loose": true,
"useBuiltIns": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie < 11"]
}
}],
"stage-2"
],
"plugins": [
["transform-runtime", {"polyfill": false, "regenerator": false }]
]
}
Notes:
-
the
entry
property will be automatically populated with every top level JavaScript file into theapplication/assets/javascripts
folder. -
application source files are loaded by the babel-loader.
Replace existing build/gulp-tasks/scripts.js
contents with:
/**
* JavaScript Related Task
* ===============================
*/
module.exports = (gulp, $, options) => {
const path = require('path');
const _ = require('lodash');
const webpackConfigDefault = require('../gulp-config/webpack.conf')(options);
const paths = require('../gulp-config/paths');
const webpack = require('webpack');
let compiler;
let watcher;
function getEntryPoints(cwd) {
const entryObj = {};
require('glob').sync(`./${paths.get('js')}/*.js`, {
cwd
}).forEach((filepath) => {
const entryId = path.basename(filepath, '.js');
entryObj[entryId] = [filepath];
});
return entryObj;
}
function compilerCallback(err, stats) {
$.util.log((stats || {}).toString({
colors: $.util.colors.supportsColor,
hash: false,
timings: false,
chunks: false,
chunkModules: false,
modules: false,
children: true,
version: true,
cached: false,
cachedAssets: false,
reasons: false,
source: false,
errorDetails: false
}));
if (err) {
throw new $.util.PluginError('webpack', err);
}
}
const webpackConfig = _.assign({}, webpackConfigDefault);
webpackConfig.entry = _.assign({}, webpackConfigDefault.entry, getEntryPoints(webpackConfigDefault.context));
compiler = webpack(webpackConfig);
gulp.task('scripts', (done) => {
compiler.run((err, stats) => {
compilerCallback(err, stats);
if (stats && stats.hasErrors()) {
done('Error compiling');
} else {
done();
}
});
});
gulp.task('scripts:watch', (done) => {
const notifier = $.notify({ message: 'Scripts Compiled' });
const bs = require('browser-sync');
const del = require('del');
const browserSync = bs.has(options.buildHash) ? bs.get(options.buildHash) : null;
function createWatcher(webpackCompiler) {
return webpackCompiler.watch({
aggregateTimeout: 200,
poll: false
}, (err, stats) => {
compilerCallback(err, stats);
if (stats && stats.hasErrors()) {
notifier.emit('error', new Error('Compilation error!'));
} else {
notifier.write('Scripts Compiled');
if (browserSync) {
browserSync.reload();
}
}
});
}
notifier.on('error', $.notify.onError({
message: 'Error: <%= error.message %>'
}));
//force watching
if (!options.isWatching) {
options.isWatching = true; //eslint-disable-line
}
watcher = createWatcher(compiler);
process.on('exit', () => {
watcher.close(done);
});
gulp.watch('*.js', { cwd: webpackConfig.context }, (event) => {
//also delete removed entry point
if (event.type === 'deleted') {
const filePathFromSrc = path.relative(paths.toPath('src.assets/js'), event.path);
del.sync(path.resolve(paths.toPath('dist.assets/js'), filePathFromSrc));
//also external sourcemap
del.sync(path.resolve(paths.toPath('dist.assets/js'), filePathFromSrc + '.map'));
}
if (event.type === 'added' || event.type === 'deleted') {
//update entry point list and restart
watcher.close();
webpackConfig.entry = _.assign({}, webpackConfigDefault.entry, getEntryPoints(webpackConfig.context));
compiler = webpack(webpackConfig);
watcher = createWatcher(compiler);
}
});
});
};
This will register two tasks:
-
gulp scripts
: one time script compilation -
gulp scripts:watch
: launch webpack in watch mode and live-reload browser on change. Will also watch for added / removed entry points
You should remove from application/views
files any reference to external polyfills (for example <script defer src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.26.0/polyfill.min.js"></script>
).
To polyfill unsupported ES2015+ features you need to include at the top of the first encountered entry point the babel-polyfill
file.
//line 1 in application.js
import 'babel-polyfill';
Open build/gulp-tasks/serve.js
and replace these lines:
gulp.watch([
paths.toPath('src.assets/js/**/*.js'),
'!' + paths.toPath('src.assets/js/**/*.{spec,conf}.js')
], ['scripts-watch']);
with these line:
gulp.run('scripts:watch');
You may also want to disable built-in source minification since webpack already does it for you. To do so edit build/gulp-tasks/views.js
and remove the following lines (around line 88):
.pipe($.uglify, {preserveComments: 'license'})
.pipe($.header, options.banners.application, {pkg: options.pkg})
- Hot reloading and webpack-dev-server are not enabled for simplicity.