I'm working on a project using sails.js and react. I'd like to be able to use Webpack's hot module replacement so I can edit my code and have it change on the browser instantly. However, it doesn't seem obvious how I can wire it all up.
I'd like to be able to use $ sails lift
and have it all just work.
If this were a node.js project, I'd simply configure webpack to use react-transform-hmr
and start webpack-dev-server
from package.json (e.g. as described here). But, this doesn't seem a very sails-y thing todo.
I see the module webpack-hot-middleware purports to be able to, "add hot reloading into an existing server without webpack-dev-server
." However, I'm not sure where's the appropriate place to add Express middleware configuration in Sails >0.10.
Can anybody recommend a good way to set this up?
Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running, without a full reload. This can significantly speed up development in a few ways: Retain application state which is lost during a full reload. Save valuable development time by only updating what's changed.
webpack configThis will let Webpack inject a websocket into the page so that it can notify the browser when it is time to do a Hot Reload.
Developing a JavaScript application involves reloading the browser each time you save code changes in order to refresh the user interface. Developer tools like Webpack can even run in watch mode to monitor your project files for changes.
Ok, after some noodling around a good approach looks to be to use the old customMiddleware
option of sails's http
middleware configuration, but only for the development environment kept in config/env/development.js
.
1) Install react and react-dom (if you haven't already):
$ npm install react react-dom --save
2) Install webpack, hot module reloading (& ES6) support for sails:
$ npm install sails-webpack babel-core babel-loader \
babel-plugin-syntax-class-properties babel-plugin-syntax-decorators \
babel-plugin-syntax-object-rest-spread \
babel-plugin-transform-class-properties \
babel-plugin-transform-decorators-legacy \
babel-plugin-transform-object-rest-spread \
babel-preset-es2015 babel-preset-react \
copy-webpack-plugin file-loader --save
3) Install react transforms and middleware for hot module reloading:
$ npm install babel-plugin-react-transform
react-transform-catch-errors react-transform-hmr \
webpack-dev-middleware webpack-hot-middleware --save-dev
4) Disable the built-in grunt hook that would normally link your application:
// .sailsrc
{
"hooks": {
"grunt": false
}
}
5) Configure sails webpack configuration:
// config/webpack.js
var webpack = require('webpack');
var CopyWebpackPlugin = require('copy-webpack-plugin');
var path = require('path');
// compile js assets into a single bundle file
module.exports.webpack = {
options: {
context: path.join(__dirname, '..'),
devtool: 'eval',
entry: [
'./assets/js',
'webpack-hot-middleware/client'
],
output: {
path: path.resolve(__dirname, '../.tmp/public'),
publicPath: "/",
filename: 'bundle.js'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
/* Copy sails.io.js unmolested: */
new CopyWebpackPlugin([
{
from: 'assets/js/dependencies',
to: 'dependencies',
force: true
}
]),
],
resolve: {
extensions: ['', '.js', '.jsx']
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(bower_components|node_modules)/,
loader: 'babel',
},
{ test: /\.css$/, loader: 'style!css' },
{
test: /\.jpe?g$|\.gif$|\.png$|\.svg$|\.woff$|\.ttf$|\.wav$|\.mp3$/,
loader: "file" }
]
}
},
// docs: https://webpack.github.io/docs/node.js-api.html#compiler
watchOptions: {
aggregateTimeout: 300
}
};
6) Configure project-wide .babelrc
to use hot module reload in development mode:
{
"presets": [
"es2015",
"react",
],
"plugins": [
"syntax-class-properties",
"syntax-decorators",
"syntax-object-rest-spread",
"transform-class-properties",
"transform-decorators-legacy",
"transform-object-rest-spread"
],
"env": {
"development": {
"plugins": [["react-transform", {
"transforms": [{
"transform": "react-transform-hmr",
"imports": ["react"],
"locals": ["module"]
}]
}]]
}
}
}
7) Lastly, add http.customMiddleware
configuration to sails's config/env/development.js
:
module.exports = {
/* ... */
/*
* Enable webpack hotloading while in development mode:
*/
http: {
customMiddleware: function (app) {
var webpack = require('webpack');
var webpackConfig = require('../webpack').webpack.options;
var compiler = webpack(webpackConfig);
app.use(require("webpack-dev-middleware")(compiler,
{
noInfo: true,
publicPath: webpackConfig.output.publicPath
}
));
app.use(require("webpack-hot-middleware")(compiler,
{ reload: true }
));
},
}
/* ... */
};
Presuming you've got a react application in assets/js/index.jsx
(or similar) and a view which includes your bundle.js
file you should be able to simply $ sails lift
and see the following on your brower's development console:
|> Now connected to Sails.
\___/ For help, see: http://bit.ly/1DmTvgK
(using browser SDK @v0.11.0)
client.js:51 [HMR] connected
And boom you should be in business!
Your solution should work great but I wanted to drop in another quick solution for others reading this:
You can completely remove the Sails.js build pipeline and run webpack separately. Running both commands simultaneously in a subshell should do the trick.
( webpack & sails lift ; )
Both commands will run and you will see the output of both merged in the terminal. Ctrl+C will kill both correctly as well since it's running in a subshell.
You could create an npm script to save yourself from writing out the two commands each time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With