Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use webpack's hot module reloading with sailsjs?

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?

like image 349
Jordan Avatar asked Jan 04 '16 04:01

Jordan


People also ask

What is HMR hot module replacement?

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.

Does Webpack have hot reload?

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.

What is Webpack 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.


2 Answers

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!

like image 140
Jordan Avatar answered Oct 21 '22 06:10

Jordan


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.

like image 2
Nick Rempel Avatar answered Oct 21 '22 06:10

Nick Rempel