CesiumJS and webpack

webpack is a popular and powerful tool for bundling JavaScript modules. It allows developers to structure their code and assets in an intuitive way and to load different kinds of files as needed with simple require statements. When building, it will trace code dependencies and pack these modules into one or more bundles that are loaded by the browser.

In the first half of this tutorial, we’ll build a simple web app from the ground up using webpack, and then cover the steps to integrate the Cesium npm module. This is a good place to start if you’d like to use CesiumJS to develop a web application. If you’re new to Cesium and are looking to learn to build your first sample app, take a look at our Getting Started Tutorial.

In the second half, we’ll explore more advanced webpack configurations for optimizing an application using CesiumJS.

The complete code and tips for optimizing a CesiumJS webpack app can be found in the official cesium-webpack-example repository.

Prerequisites

  • A basic understanding of the command line, JavaScript, and web development.
  • An IDE or code editor. Developers on the Cesium team members use Visual Studio Code, but a minimal code editor such as Sublime Text will also work.
  • Node.js installed. We recommend using the latest LTS version.

Create a basic webpack app

In this section, we’ll describe how to set up a basic web app with webpack and a development server. If you’ve already got an app set up and just want to add CesiumJS, skip to Add CesiumJS to a webpack app.

Initialize an app with npm

Create a new cesium-webpack-app directory for your app. Open a console, navigate to the new directory, and run the following command:

Copy to clipboard. Data copied clipboard.
npm init

Follow the prompts and populate any details about your app. Press enter to use the defaults. This will create package.json.

Create the app code

Create a src directory for our app code. When we build the app, webpack will produce distribution files in a dist directory.

Create src/index.html and add code for a boilerplate HTML page.

Copy to clipboard. Data copied clipboard.
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport"
      content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
    <title>Hello World!</title>
  </head>
  <body>
    <p>Hello World!</p>
  </body>
</html>

Next, create an entry point for the app. This is the starting point for webpack to find all the JavaScript source code and dependencies for our bundle.

Create src/index.js and add this text code.

Copy to clipboard. Data copied clipboard.
console.log('Hello World!');

Install and configure webpack

Begin by installing webpack.

Copy to clipboard. Data copied clipboard.
npm install --save-dev webpack

Configuration

Create webpack.config.js to define our webpack configuration object.

Copy to clipboard. Data copied clipboard.
const path = require('path');

const webpack = require('webpack');

module.exports = {
    context: __dirname,
    entry: {
        app: './src/index.js'
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist'),
    }
};

context specifies the base path for the files. entry is used to specify bundles. In this case, the app bundle has src/index.js as the entry point. webpack will output the bundle app.js to the dist folder.

Loaders

webpack loads everything like a module. Use loaders to load in CSS and other asset files. Install the style-loader, css-loader, and url-loader.

Copy to clipboard. Data copied clipboard.
npm install --save-dev style-loader css-loader url-loader

Add two module.rules to webpack.config.js: one for CSS files and one for other static files. For each, define test for the types of file to load, and use to specify the list of loaders.

Copy to clipboard. Data copied clipboard.
const path = require('path');

const webpack = require('webpack');

module.exports = {
    context: __dirname,
    entry: {
        app: './src/index.js'
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist'),
    },
    module: {
        rules: [{
            test: /\.css$/,
            use: [ 'style-loader', 'css-loader' ]
        }, {
            test: /\.(png|gif|jpg|jpeg|svg|xml|json)$/,
            use: [ 'url-loader' ]
        }]
    }
};

Plugins

Define index.html and inject our bundle into that page using a webpack plugin called the html-webpack-plugin.

Copy to clipboard. Data copied clipboard.
npm install --save-dev html-webpack-plugin

Require the plugin in webpack.config.js and add it to plugins. Pass src/index.html as our template.

Copy to clipboard. Data copied clipboard.
const path = require('path');

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    context: __dirname,
    entry: {
        app: './src/index.js'
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist'),
    },
    module: {
        rules: [{
            test: /\.css$/,
            use: [ 'style-loader', 'css-loader' ]
        }, {
            test: /\.(png|gif|jpg|jpeg|svg|xml|json)$/,
            use: [ 'url-loader' ]
        }]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        })
    ]
};

The configuration file is a JavaScript file, so we can require other Node modules and perform operations.

Bundle the app

Use package.json to define scripts we can call with npm. Add the build command.

Copy to clipboard. Data copied clipboard.
  "scripts": {
    "build": "node_modules/.bin/webpack --config webpack.config.js"
  }

This script calls webpack and passes in the webpack.config.js configuration file.

We’re using the local installation of webpack and the webpack-dev-server in these scripts. This allows each project to use its own individual version, and is what is recommended by the webpack documentation. If you’d prefer to use the global version, install it globally with npm install --global webpack and use the command webpack --config webpack.config.js to run.

When you run the build command,

Copy to clipboard. Data copied clipboard.
npm run build

you should see some output from webpack starting with something like this:

Copy to clipboard. Data copied clipboard.
npm run build

> test-app@1.0.0 build C:\workspace\test-app
> node_modules/.bin/webpack --config webpack.config.js

Hash: 2b42bff7a022b5d956a9
Version: webpack 3.6.0
Time: 2002ms
                                        Asset       Size  Chunks                    Chunk Names
     Assets/Textures/NaturalEarthII/2/0/3.jpg    10.3 kB          [emitted]
                                       app.js     162 kB       0  [emitted]         app

The app.js bundle and index.html file will be output into the dist folder.

Run the development server

Use the webpack-dev-server to serve a development build and see our app in action.

Copy to clipboard. Data copied clipboard.
npm install --save-dev webpack-dev-server

Add a start script to package.json to run the development server. Set the config file via the --config flag. Use the --open flag to open the app in a browser when the command is executed.

Copy to clipboard. Data copied clipboard.
  "scripts": {
    "build": "node_modules/.bin/webpack --config webpack.config.js",
    "start": "node_modules/.bin/webpack-dev-server --config webpack.config.js --open"
  }

Tell the development server to serve files the dist folder. Add this at the bottom of webpack.config.js.

Copy to clipboard. Data copied clipboard.
    // development server options
    devServer: {
        contentBase: path.join(__dirname, "dist")
    }

Finally, we can run the app!

Copy to clipboard. Data copied clipboard.
npm start

You should see your content served at localhost:8080, and you should see the “Hello World!” message when you open up the browser console.

Basic webpack app output

`app.js` console output

Add CesiumJS to a webpack app

Install CesiumJS

Install the cesium module from npm and add it to package.json.

Copy to clipboard. Data copied clipboard.
npm install --save-dev cesium

Configure CesiumJS in webpack

CesiumJS is a large and complex library. In additional to JavaScript modules, it also includes static assets such as CSS, image, and json files. It includes web worker files to perform intensive calculations in separate threads. Unlike traditional npm modules, CesiumJS does not define an entry point because of the diverse ways in which the library is used. We’ll need to configure some additional options to use it with webpack.

First, define where CesiumJS is. This tutorial uses the source code, so webpack can include individual models and trace the dependencies. Alternatively, you can use the built (minified or unminified) version of CesiumJS. However, the modules are already combined and optimized, which gives us less flexibility.

Add the following to the top of webpack.config.js:

Copy to clipboard. Data copied clipboard.
// The path to the CesiumJS source code
const cesiumSource = 'node_modules/cesium/Source';
const cesiumWorkers = '../Build/Cesium/Workers';

This tutorial uses the npm module for ease of installation, but you could also clone the GitHub repository or use an unzipped release download.

Add the following options to the configuration object to resolve some quirks with how webpack compiles CesiumJS.

Copy to clipboard. Data copied clipboard.
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist'),

        // Needed to compile multiline strings in Cesium
        sourcePrefix: ''
    },
    amd: {
        // Enable webpack-friendly use of require in Cesium
        toUrlUndefined: true
    },
    node: {
        // Resolve node module use of fs
        fs: 'empty'
    },
  • output.sourcePrefix: '' overrides the webpack default for adding a \t tab character before each line. CesiumJS has some multiline strings, so we need to override this default with an empty prefix ''.
  • amd.toUrlUndefined: true tells CesiumJS that the version of AMD webpack uses to evaluate require statements is not compliant with the standard toUrl function.
  • node.fs: 'empty' resolves some third-party usage of the fs module, which is targeted for use in a Node environment rather than the browser.

Add a cesium alias alias so we can reference it in our app code.

Copy to clipboard. Data copied clipboard.
    resolve: {
        alias: {
            // CesiumJS module name
            cesium: path.resolve(__dirname, cesiumSource)
        }
    },

Manage CesiumJS static files

Lastly, make sure the static CesiumJS asset, widget, and web worker files are served and loaded correctly.

Use the copy-webpack-plugin to copy static files to the dist directory as part of the build process.

Copy to clipboard. Data copied clipboard.
npm install --save-dev copy-webpack-plugin

Require it near the top of our webpack.config.js file.

Copy to clipboard. Data copied clipboard.
const CopywebpackPlugin = require('copy-webpack-plugin');

Add the following to the plugins array:

Copy to clipboard. Data copied clipboard.
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        }),
        // Copy Cesium Assets, Widgets, and Workers to a static directory
        new CopywebpackPlugin([ { from: path.join(cesiumSource, cesiumWorkers), to: 'Workers' } ]),
        new CopywebpackPlugin([ { from: path.join(cesiumSource, 'Assets'), to: 'Assets' } ]),
        new CopywebpackPlugin([ { from: path.join(cesiumSource, 'Widgets'), to: 'Widgets' } ])
    ],

This copies the Assets and Widgets directories, and the built web worker scripts.

If you are using a fork of the CesiumJS repo, the Build folder will not already exist. Run npm run release to produce the build output. For more information, see the Cesium Build Guide.

Define an environment variable that tells CesiumJS the base URL for loading static files using the webpack DefinePlugin. The plugins array will now look like this:

Copy to clipboard. Data copied clipboard.
    plugins: [
        new HtmlWebpackPlugin({
            template: 'src/index.html'
        }),
        // Copy Cesium Assets, Widgets, and Workers to a static directory
        new CopywebpackPlugin([ { from: path.join(cesiumSource, cesiumWorkers), to: 'Workers' } ]),
        new CopywebpackPlugin([ { from: path.join(cesiumSource, 'Assets'), to: 'Assets' } ]),
        new CopywebpackPlugin([ { from: path.join(cesiumSource, 'Widgets'), to: 'Widgets' } ]),
        new webpack.DefinePlugin({
            // Define relative base path in cesium for loading assets
            CESIUM_BASE_URL: JSON.stringify('')
        })
    ],

Require CesiumJS modules in our app

There are a few ways we can require CesiumJS modules within our application. You can use either CommonJS syntax or ES6 import statements.

You can import the whole CesiumJS library or require only the specific modules you need. Including modules will result in webpack compiling only those modules and their dependencies in your bundle instead of the entire library.

CommonJS style require

To require all of CesiumJS:

Copy to clipboard. Data copied clipboard.
var Cesium = require('cesium/Cesium');
var viewer = new Cesium.Viewer('cesiumContainer');

To require an individual module:

Copy to clipboard. Data copied clipboard.
var Color = require('cesium/Core/Color');
var color = Color.fromRandom();

ES6 style import

To require all of CesiumJS:

Copy to clipboard. Data copied clipboard.
import Cesium from 'cesium/Cesium';
var viewer = new Cesium.Viewer('cesiumContainer');

To require an individual module:

Copy to clipboard. Data copied clipboard.
import Color from 'cesium/core/Color';
var color = Color.fromRandom();

Requiring asset files

The philosophy behind webpack is that every file is treated like a module. This makes importing assets the same as including JavaScript. We told webpack how to load in each type of file in our configuration using loaders, so we just need to call require.

Copy to clipboard. Data copied clipboard.
require('cesium/Widgets/widgets.css');

Hello World!

Create a new file, src/css/main.css, for styling our app:

Copy to clipboard. Data copied clipboard.
html, body, #cesiumContainer {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    overflow: hidden;
}

Create a div for the CesiumJS Viewer in the index.html body. Replace <p>Hello World!</p> with this div:

Copy to clipboard. Data copied clipboard.
<div id="cesiumContainer"></div>

Delete the contents of index.js and include Cesium and our CSS files:

Copy to clipboard. Data copied clipboard.
var Cesium = require('cesium/Cesium');
require('./css/main.css');
require('cesium/Widgets/widgets.css');

Add this line to create the Viewer:

Copy to clipboard. Data copied clipboard.
var viewer = new Cesium.Viewer('cesiumContainer');

Run the app with npm start to see the Viewer in your browser!

Styled Hello World App

Copy and paste your favorite Sandcastle example. For example, The Particle System demo makes for a great conclusion.

Example App

Advanced webpack configurations

webpack can be leveraged in many more ways to increase performance, decrease your bundle size, and perform additional or complex build steps. Here we’ll discuss a few configuration options relevant to using the CesiumJS library.

Our configuration for an optimal production Cesium webpack build can be found in our example repo at webpack.release.config.js

Code splitting

webpack packages CesiumJS in the same chunk as our application by default, which results in a large file. We can split CesiumJS into its own bundle and improve our app performance using the CommonChunksPlugin. If you end up creating multiple chunks for your app, they can all reference one common cesium chunk.

Add the plugin to your webpack.config.js file, and specify the rule for breaking out CesiumJS modules:

Copy to clipboard. Data copied clipboard.
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: 'cesium',
            minChunks: module => module.context && module.context.indexOf('cesium') !== -1
        })
    ]

Enable source maps

Source maps allow webpack to trace errors back to the original content. They offer more or less detailed debugging information in exchange for compiling speed. We recommend using the 'eval'option.

Copy to clipboard. Data copied clipboard.
    devtool: 'eval'

Source maps are not recommended for production.

Remove pragmas

There are developer errors and warnings in the CesiumJS source code that are removed from our minified release builds. Since there’s no built-in webpack way to remove these warnings, we’ll use the strip-pragma-loader.

Install the package:

Copy to clipboard. Data copied clipboard.
npm install strip-pragma-loader --save-dev

Include the loader in module.rules with debug set to false:

Copy to clipboard. Data copied clipboard.
    rules: [{
        // Strip cesium pragmas
        test: /\.js$/,
            enforce: 'pre',
            include: path.resolve(__dirname, cesiumSource),
            use: [{
                loader: 'strip-pragma-loader',
                options: {
                    pragmas: {
                        debug: false
                    }
                }
            }]
    }]

Uglify and minify

Uglifying and minifying code allows for smaller file sizes in production. For a release build, CesiumJS uglifies JavaScript files and minifies CSS files.

Use the uglifyjs-webpack-plugin to uglify the CesiumJS source.

Copy to clipboard. Data copied clipboard.
npm install uglifyjs-webpack-plugin --save-dev

Require it in your config file.

Copy to clipboard. Data copied clipboard.
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

Include it in the list of plugins.

Copy to clipboard. Data copied clipboard.
    plugins: [
        new webpack.optimize.UglifyJsPlugin()
    ]

Use the minimize option on the css-loader to minify the CSS.

Copy to clipboard. Data copied clipboard.
    module: {
        rules: [{
            test: /\.css$/,
            use: [
                'style-loader',
                {
                    loader: 'css-loader',
                    options: {
                        // minify loaded css
                        minimize: true
                    }
                }
            ]
        }]
    }

Resources

The official cesium-webpack-example repo contains the minimal webpack configuration, the hello world code covered in this tutorial, and instructions for optional code configurations.

For a tour of CesiumJS feature to include in your new app, see the Cesium Workshop Tutorial.

Explore demos in Sandcastle and see the CesiumJS Documentation.

To learn more about webpack, take a look at webpack Concepts , or dive into the Documentation .