Skip to content

Building app into dist folder using webpack

Julian Knight edited this page Feb 5, 2022 · 8 revisions

WARNING: This page is now hopelessly out of date and needs updating. If you know how to use webpack, please consider updating this page.


Note: This page has not been updated for uibuilder v2 so may need some tweaking. If you use webpack, please consider updating this page.

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',
    'normalize.css'
  ],
  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'
      },
      {
        // this is necessary as normally we do not want css from node modules
        // so for normalize.css have to include an explicit rule
        test: /normalize.css$/,
        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>
    <link rel="stylesheet" href="vendor/normalize.css/normalize.css">

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

If using uibuilder version 0.4.7 or above then also remove the line

    <script src="/uibuilder/socket.io/socket.io.js"></script>

as socket.io client will automatically be bundled in.

Having to have two versions of index.html 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, so 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 :1880/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
Clone this wiki locally