Goal

In this tutorial, we show you how to deploy the Hello World application. We use webpack to bundle the JavaScript and HTML files of our application.

Although webpack is used in this tutorial, you can choose to use other bundlers, such as Rollup or Parcel, in your LuciadRIA application.

Once you have set up the LuciadRIA license loading as explained in the Hello World tutorial, you can use LuciadRIA just as you would use any other third-party library.

Creating a webpack production bundle

In the package.json file within our hello-world project, we add an NPM script to create a production build:

{
  "scripts": {
    "build": "webpack --mode production"
  }
}

Now, we can run the build script:

npm run build

webpack generates a production version of our application in the dist folder.

The dist folder now holds an index.html and a main.js file. dist/main.js is different from src/main.js, though. The dist/main.js bundle is a lot larger because it has all JavaScript modules needed to run your application. That includes imported modules from your application’s dependencies, like LuciadRIA. When you run webpack in production mode, it applies extra optimizations to the bundle, reducing the size of the bundle.

Deploying the application

To deploy the application, just copy the dist folder to your favorite web server for static files, Apache or NGINX for example.

For demonstration purposes, we start a simple NodeJS server that serves the dist directory:

npx serve ./dist -l 8000

Since we used the npx command we did not have to install (and later uninstall) the serve package globally.

When we browse to http://localhost:8000, we see our Hello World application. We successfully deployed a production build of our application.

Using your deployment license in production

You must use your deployment license in production mode, but you probably still want to use your development license for development.

One of the ways to set this up is by defining a webpack module alias for the license file. In production mode, this alias resolves to your deployment license file. In development, it resolves to the development license file.

In the Hello World application, open the webpack.config.js file, and find these settings:

const path = require('path');

module.exports = {
  devServer: {
    static: path.join(__dirname, 'dist')
  },
};

Let’s switch the export configuration so that we pass in the command line options through the argv argument. That allows us to check if we’re running in production mode or not, and set up our license alias accordingly.

const path = require('path');

module.exports = (env, argv) => {
  const isProduction = argv.mode === "production";
  const riaDeploymentLicense = path.resolve(__dirname, "src/luciadria_deployment.txt");
  const riaDevelopmentLicense = path.resolve(__dirname, "src/luciadria_development.txt");

  return {
    devServer: {
      static: path.join(__dirname, 'dist')
    },
    resolve: {
      alias: {
        "ria-license": isProduction ?  riaDeploymentLicense : riaDevelopmentLicense
      }
    }
  }
};

Next, update the license import in src/license-loader.js:

import {setLicenseText} from "@luciad/ria/util/License.js";
import license from 'raw-loader!ria-license'; // alias for actual license, see webpack.config.js' resolve.alias

setLicenseText(license);

Now we use the deployment license file in production mode. We still use the development license file during development.

Trading off build times and bundle sizes

In this section we take a look at how we can trade off build time against bundle size, and how our choice of rendering technology affects bundle size.

The configuration updates in this section are optional. The webpack configuration of the earlier sections will serve you just fine, because it gives you the most optimal bundle size, but it comes at the cost of longer build times. This section only gives you some insight in how to reduce build time and bundle sizes.

Production builds can take some time to run. This is especially true when you are using WebGLMap, because in comparison it pulls in more code than the non-WebGL Map. Note that each LuciadRIA module is already optimized, so re-optimizing those modules will result in only a minor reduction in bundle size. To speed up our production build, we can set up webpack to skip all, or some, of the optimization of LuciadRIA modules.

Because webpack optimization only works at the level of chunks, we’ll put each LuciadRIA module in its own chunk, and tell webpack to skip the optimization of that chunk:

const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');

module.exports = (env, argv) => {
  const isProduction = argv.mode === "production";
  const riaDeploymentLicense = path.resolve(__dirname, "src/luciadria_deployment.txt");
  const riaDevelopmentLicense = path.resolve(__dirname, "src/luciadria_development.txt");

  return {
    devServer: {
      static: path.join(__dirname, 'dist')
    },
    resolve: {
      alias: {
        "ria-license": isProduction ? riaDeploymentLicense : riaDevelopmentLicense
      }
    },
    optimization: {
      splitChunks: {
        cacheGroups: {
          "ria-modules": { // split LuciadRIA into its own chunk
            test: /@luciad.(ria|ria-geometry|ria-milsym)/,
            name: 'ria-modules',
            enforce: true,
            chunks: 'all'
          }
        }
      },
      minimizer: [
        new TerserPlugin({ // disable optimization for the LuciadRIA chunk
          exclude: [/ria-modules/]
        })
      ]
    },
  }
};

Now that webpack generates a separate ria-module.js bundle, we need to include it in our index.html, before our main.js bundle

<!doctype html>
<html lang="en">
  ...
  <body>
    <h1>Hello world</h1>
    <div id="map"/>
    <script src="ria-modules.js"></script>
    <script src="main.js"></script>
  </body>
</html>

If you now run npm run build, you’ll notice that it’s dramatically faster. The total bundle size is slightly larger than if we had put everything into one big chunk, though. Instead of doing no optimizations at all on the LuciadRIA chunk, we can choose to only mangle the bundle, and skip the more advanced compress optimizations. This gives us a middle-ground solution between bundle size and production build time:

const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');

module.exports = (env, argv) => {
  const isProduction = argv.mode === "production";
  const riaDeploymentLicense = path.resolve(__dirname, "src/luciadria_deployment.txt");
  const riaDevelopmentLicense = path.resolve(__dirname, "src/luciadria_development.txt");

  return {
    devServer: {
      static: path.join(__dirname, 'dist')
    },
    resolve: {
      alias: {
        "ria-license": isProduction ? riaDeploymentLicense : riaDevelopmentLicense
      }
    },
    optimization: {
      minimizer: [
        new TerserPlugin({ // run the default optimization on non-RIA chunks
          exclude: [/ria-modules/]
        }),
        new TerserPlugin({ // only mangle the RIA chunk
          include: [/ria-modules/],
          terserOptions: {
            mangle: true,
            compress: false
          }
        })
      ],
      splitChunks: {
        cacheGroups: {
          "ria-modules": {
            test: /@luciad.(ria|ria-geometry|ria-milsym)/,
            name: 'ria-modules',
            enforce: true,
            chunks: 'all'
          }
        }
      }
    },
  }
};

We’ll let you choose how you trade off build time against bundle size. In the table below you can see an overview of some build timings and resulting bundle sizes, for the different options. This should give you an idea of how much effect these options have:

Table 1. Overview of build times and bundle sizes for different optimization options
Map type Optimizations Build time Bundle size

Map

Everything in one chunk, all optimizations

7 seconds

1.01 MB

Map

Everything in one chunk, only mangle

5 seconds

1.22 MB

Map

Split off each LuciadRIA modules in a separate chunk, no optimization

2 seconds

1.67 MB

Map

Split off each LuciadRIA modules in a separate chunk, only mangle LuciadRIA

5 seconds

1.24 MB

WebGLMap

Everything in one chunk, all optimizations

12 seconds

8.35 MB

WebGLMap

Everything in one chunk, only mangle

8 seconds

8.55 MB

WebGLMap

Split off each LuciadRIA modules in a separate chunk, no optimization

3 seconds

9.39 MB

WebGLMap

Split off each LuciadRIA modules in a separate chunk, only mangle LuciadRIA

8 seconds

8.58 MB