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

3

6

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?

Whitton answered 4/1, 2016 at 4:38 Comment(0)
W
8

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!

Whitton answered 4/1, 2016 at 4:38 Comment(3)
I am the author of sails-webpack. Keep in mind that support for this module is ending: github.com/balderdash-projects/sails-webpack/issues/2Marlomarlon
Also bear in mind that the instructions for sails-webpack indicate disabling of the grunt hook entirely. If you do go with sails-webpack, you may need to find another way to do what the grunt hook provided through the default tasks (all the asset pipeline stuff like clean, uglify, copy, etc.). I'm currently playing with just adding grunt-webpack into the out-of-the-box mix (haven't quite got it yet). Maybe webpack can include all of that - I haven't familiarized myself with it yet.Raymer
Looks good, it's working! But what kind of code should to add to compile stylus to css? Thanks!Beauchamp
C
2

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.

Camphor answered 13/4, 2016 at 17:25 Comment(0)
M
1

We created our own Sails hook to handle Webpack internally, works great at the moment. The config needs some cleaning however: https://github.com/teamfa/sails-hook-webpack

Milklivered answered 19/4, 2016 at 15:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.