Skip to content

Building app into dist folder using webpack

Colin Law edited this page Oct 17, 2017 · 8 revisions

How to use webpack to bundle a uibuilder app into the dist folder for most efficient operation when page loading).

This is not necessarily fully authoritative as I am still learning about webpack, so any input will be received gladly, but it does seem to work for me.

So far it will only cope with a single uibuilder url. How to handle multiple pages (so multiple folders in the uibuilder folder) is on the todo list.

It assumes we are starting with the default uibuilder app automatically installed when a uibuilder node is initially deployed. I have assumed that the url specified in the node is wptest. It should be fairly obvious what to change for a different url.

First install webpack. I installed it globally as it seems to make it easier to use.

cd ~/.node-red
sudo npm install webpack -g 

now install some plugins etc that we need as development dependencies (not global)

npm install style-loader css-loader html-webpack-plugin copy-webpack-plugin uglifyjs-webpack-plugin imports-loader --save-dev

You may get an UNMET PEER DEPENDENCY error which may be as a result of installing webpack globally, but it doesn't seem to matter.

Create the file .node-red/webpack.config.js containing:

const url = "wptest"    // change this to the url specified in the uibuilder node
var webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const path = require('path');

var HTMLWebpackPluginConf = new HtmlWebpackPlugin({
  template: __dirname + '/uibuilder/' + url + '/src/index_template.html',
  filename: 'index.html'
});

var CopyWebpackPluginConf = new CopyWebpackPlugin([
  { from: './uibuilder/' + url + '/src/manifest.json' }
]);

var UglifyJSPluginConf = new UglifyJSPlugin({
  uglifyOptions: {
    compress: {
      warnings: false,
    },
    output: {
      comments: false
    },
  }
});

var webpackJQueryPluginConf = new webpack.ProvidePlugin({
  $: "jquery",
  jQuery: "jquery"
});

module.exports = {
  entry: [
    './uibuilder/' + url + '/src/index.js'
  ],
  output: {
      path: path.resolve(__dirname, 'uibuilder/' + url + '/dist'),
      filename: 'bundle.min.js'
  },
  plugins: [
    webpackJQueryPluginConf,
    UglifyJSPluginConf,
    HTMLWebpackPluginConf,
    CopyWebpackPluginConf
  ],
  module: {
    loaders: [
      {
        // Note, if you decide to include babel-loader then, for some reason, this
        // has to go after babel-loader or 'this' is changed to undefined.
        // don't understand why, I expected to have to do it the other way round.
        // This bit is required because uibuilderfe expects 'this' to be set to window
        test: /uibuilderfe.js$/,
        use: "imports-loader?this=>window"
      },
      {
        test: /\.css$/,
        exclude: /node_modules/,
        loader: 'style-loader!css-loader'
      }
    ]
  }
};

Edit the first line to match the url you have set in the uibuilder node.

Make a copy of index.html (in the uibuilder/wptest/src folder) and call it index-template.html and in the copy remove the lines

    <link rel="stylesheet" href="index.css">
    <script src="vendor/jquery/dist/jquery.min.js"></script>
    <script src="uibuilderfe.min.js"></script>
    <script src="index.js"></script>

Those are no longer required as those will all be bundled into bundle.min.js

This aspect is a bit of an issue. It is necessary to keep the original index.html for when the page is being displayed from the src folder rather than dist. It does mean that there are two versions to maintain. Looking at this problem is on the todo list.

Add to the front of index.js

if (typeof require != "undefined") {
 var uibuilder = require('node-red-contrib-uibuilder/nodes/src/uibuilderfe.js')
 require("./index.css")
}

Next check that nothing done so far has prevented the app running at :1800/wptest by refreshing the browser on that page.

Assuming that is ok then, still in the .node-red folder, run

webpack

If all goes well that will write to the dist folder the files index.html, bundle.min.js and manifest.json

In order for uibuilder to serve those files restart node-red and refresh the browser to check it is all working.

If you change any of the source files in the wptest folder then the dist files can be regenerated by running webpack again, then refreshing the browser will pick them up. However if you run

webpack --change

then it will watch the files and automatically rebuild each time any of them is saved. If you edit webpack.config.js then it is necessary to rerun webpack, the --change flag does not cope with that.

To revert to running from the source files delete the contents of the dist folder and restart node-red. Then to run from dist again run webpack and restart node-red.

If your app does not need jquery then remove webpackJQueryPluginConf and its definition from the webpack config file.

Todo:

  • Add riot specific build instructions.
  • Cope with multiple uibuilder pages.
  • Try to improve the requirement for multiple index.html
  • Bundle normalize.css
  • Bundle socket.io.js
Clone this wiki locally