Goal

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

Although Vite is used in this tutorial, you can choose to use other bundlers, such as webpack 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 production bundle with Vite

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

{
  "scripts": {
    "build": "vite build"
  }
}

Now, we can run the build script:

npm run build

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

The dist folder now holds an index.html file and an assets folder containing a hashed JavaScript bundle, such as assets/index-4a3f9c2b.js. Unlike the src/index.js file that Vite serves directly, unbundled, during development, this 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.

vite build runs in production mode by default and applies extra optimizations to the bundle, such as minification and tree-shaking, reducing the size of the bundle. Vite also rewrites the <script> tag in dist/index.html to point to the hashed bundle file automatically, so you don’t have to update it by hand.

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 Vite resolve 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, create a vite.config.js file with the following contents:

import path from 'path';
import { fileURLToPath } from 'url';
import { defineConfig } from 'vite';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export default defineConfig(({ mode }) => {
  const isProduction = mode === "production";
  // The '?raw' suffix must be part of the alias target, not the import specifier.
  const riaDeploymentLicense = path.resolve(__dirname, "src/luciadria_deployment.txt?raw");
  const riaDevelopmentLicense = path.resolve(__dirname, "src/luciadria_development.txt?raw");

  return {
    resolve: {
      alias: {
        "ria-license": isProduction ? riaDeploymentLicense : riaDevelopmentLicense
      }
    }
  };
});

The ?raw suffix must be part of the alias target path, not the import specifier. Writing import license from 'ria-license?raw' fails to resolve. Putting ?raw on the target path in vite.config.js, as shown above, is what makes Vite load the file contents as a string.

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

import {setLicenseText} from "@luciad/ria/util/License.js";
import license from 'ria-license'; // alias for actual license, see vite.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.

If you do not explicitly set the license as shown above, LuciadRIA will search for it in its default location. It will first check for /license/luciadria_development.txt and then for /license/luciadria_deployment.txt. In a production environment, you should only have a deployment license. Consequently, the default behavior will result in two HTTP calls: one to fetch the development license, which will fail, and a second to fetch the deployment license, which will succeed.

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.

The configuration updates in this section are optional. The default Vite configuration of the earlier sections will serve you just fine. This section only gives you some insight in how to further tune build time and bundle sizes, if you need to.

By default, vite build minifies your production bundle using esbuild, which is much faster than the Terser-based minification tools older bundlers such as webpack use by default. Note that each LuciadRIA module is already optimized, so re-minifying those modules results in only a minor reduction in bundle size, and esbuild’s speed means you rarely need to trade anything off at all.

If you still want finer control, for example to keep the LuciadRIA modules in their own cacheable chunk that doesn’t change as often as your application code, you can use the manualChunks option, which Vite exposes through build.rollupOptions:

import path from 'path';
import { fileURLToPath } from 'url';
import { defineConfig } from 'vite';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export default defineConfig(({ mode }) => {
  const isProduction = mode === "production";
  const riaDeploymentLicense = path.resolve(__dirname, "src/luciadria_deployment.txt?raw");
  const riaDevelopmentLicense = path.resolve(__dirname, "src/luciadria_development.txt?raw");

  return {
    resolve: {
      alias: {
        "ria-license": isProduction ? riaDeploymentLicense : riaDevelopmentLicense
      }
    },
    build: {
      rollupOptions: {
        output: {
          manualChunks(id) {
            // split LuciadRIA into its own chunk
            if (id.includes("@luciad/ria")) {
              return "ria-modules";
            }
          }
        }
      }
    }
  };
});

Unlike webpack, Vite doesn’t let you selectively skip minification for a single chunk — build.minify applies to the whole bundle. That’s rarely a problem in practice, since esbuild’s minification is fast enough that excluding LuciadRIA from it saves very little build time. The main benefit of splitting LuciadRIA into its own chunk is caching: browsers can keep reusing the cached ria-modules chunk across deployments as long as your LuciadRIA dependency version doesn’t change, even when your own application code does.

You don’t need to update index.html for this: Vite analyzes the module graph during the build and automatically injects the right <script> and pre-load tags for every chunk, including ria-modules.