Using webpack for Custom Builds

webpack is a module bundler for JavaScript. It can basically process the code in your application and bundle it up, not only into a single file, but bundled files that can be dynamically loaded as needed at runtime, for example, in an application that might have multiple routes. You can use webpack to build applications that use the ArcGIS API for JavaScript by using the @arcgis/webpack-plugin.

To learn more about webpack, please refer to the concepts and guides pages from webpack.

Note: See the prerequisites section on configuring your system before proceeding. If your system is already configured with Node.js, you may proceed to the Getting Started section.

Prerequisites

  1. Node.js version 8.x.x or higher (includes npm, the node package manager)

To check if these are installed:

node --version

Getting Started

We provide a sample application hosted on Github under the webpack directory in the jsapi-resources repository for you to get started.

jsapi-resources/4.x/webpack/demo
├── README.md
├── package-lock.json
├── package.json
├── src
│   ├── assets
│   │   ├── favicon.ico
│   │   ├── green-blue-small.png
│   │   └── icons
│   │       └── icon.png
│   ├── components
│   │   ├── header.tsx
│   │   └── webmapview.tsx
│   ├── config.ts
│   ├── css
│   │   ├── index.scss
│   │   └── main.scss
│   ├── index.html
│   └── index.tsx
├── tsconfig.json
└── webpack.config.js

However, if you prefer to start from scratch you can install the @arcgis/webpack-plugin yourself.

npm install --save-dev @arcgis/webpack-plugin@^4.8.2

You can also clone the jsapi-resources repository (code below) to your local machine or download the repository to your local machine.

git clone https://github.com/Esri/jsapi-resources.git

Understanding webpack

In the provided webpack sample, you are given some npm scripts in the package.json as shortcuts for your development environment.

Note: If you are new to node and are unfamiliar or need a refresher on what is contained in a package.json file, please refer to the documentation.

Your package.json will look something like this.

{
  "name": "arcgis-webpack-plugin-sample",
  ...
  "dependencies": {
    "@arcgis/webpack-plugin": "^4.8.2"
  }
}

What this file is saying is that when you do npm install it will install the latest version of the @arcgis/webpack-plugin, which has a dependency on version 4.9 of the ArcGIS API for JavaScript that it will also install locally. This tells node to download any dependencies and/or devDependencies listed in the package.json file.

The scripts field in the package.json file is used to issue commands to build your application. The webpack-dev-server is a useful development server that also provides live reloading of your application as you write your code.

{
  ...
  "scripts": {
    "start": "webpack-dev-server --mode development --open",
    "build": "webpack --mode production"
  }
  ...
}

See the package.json file in the webpack sample for more information.

Building your app

For both options (clone or download), you need to copy the source files from https://github.com/Esri/jsapi-resources/tree/master/4.x/webpack/demo to a new directory. In the following steps, we'll name the new directory esrijs-webpack.

The contents of your directory should appear as follows:

esrijs-webpack
├── README.md
├── package-lock.json
├── package.json
├── src
│   ├── assets
│   │   ├── favicon.ico
│   │   ├── green-blue-small.png
│   │   └── icons
│   │       └── icon.png
│   ├── components
│   │   ├── header.tsx
│   │   └── webmapview.tsx
│   ├── config.ts
│   ├── css
│   │   ├── index.scss
│   │   └── main.scss
│   ├── index.html
│   └── index.tsx
├── tsconfig.json
└── webpack.config.js
  1. Open a terminal window or shell command window
  2. Change directory to esrijs-webpack
    • In the terminal window run: npm install
    • The first time this is run it may take a while
  3. In the terminal window type npm start and press return
  4. The command will run the webpack bundler using the @arcgis/webpack-plugin
  5. Your application will be available at http://localhost:8080/
  6. You can build a deployable version of your app by running npm run build.
  7. This will take a little longer and build a deployable version of your application in a dist folder.

The critical portion of creating your application is going to be your webpack.config.js.

Note: To learn more about webpack, please refer to the concepts and guides pages from webpack.

See the configuration section of the webpack documentation for details on each of the options available in the configuration file.

const ArcGISPlugin = require("@arcgis/webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const path = require("path");
const webpack = require("webpack");

module.exports = {
  entry: {
    index: "./src/index.tsx"
  },
  output: {
    filename: "[name].bundle.js",
    publicPath: ""
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: "ts-loader",
        options: {
          transpileOnly: true
        }
      },
      {
        test: /\.html$/,
        use: [
          {
            loader: "html-loader",
            options: { minimize: false }
          }
        ],
        exclude: /node_modules/
      },
      {
        test: /\.scss$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"]
      }
    ]
  },
  plugins: [
    // Be sure to have the @arcgis/webpack-plugin run first in the list
    new CleanWebpackPlugin(["dist"]),
    new ArcGISPlugin(),
    new HtmlWebPackPlugin({
      template: "./src/index.html",
      filename: "./index.html",
      chunksSortMode: "none"
    }),
    new MiniCssExtractPlugin({
      filename: "[name].css",
      chunkFilename: "[id].css"
    })
  ],
  resolve: {
    modules: [path.resolve(__dirname, "/src"), "node_modules/"],
    extensions: [".ts", ".tsx", ".js", ".scss"]
  },
  externals: [
    (context, request, callback) => {
      if (/pe-wasm$/.test(request)) {
        return callback(null, "amd " + request);
      }
      callback();
    }
  ],
  node: {
    process: false,
    global: false
  }
};

In the current version of the @arcgis/webpack-plugin, you need to configure the application to use the workers of the ArcGIS API for JavaScript from the cdn. The workers configuration for this sample application is located under jsapi-resources/4.x/webpack/demo/src/config.ts.

import esriConfig = require("esri/config");

const DEFAULT_WORKER_URL = "https://js.arcgis.com/4.9/";
const DEFAULT_LOADER_URL = `${DEFAULT_WORKER_URL}dojo/dojo-lite.js`;

(esriConfig.workers as any).loaderUrl = DEFAULT_LOADER_URL;
esriConfig.workers.loaderConfig = {
  baseUrl: `${DEFAULT_WORKER_URL}dojo`,
  packages: [
    { name: "esri", location: DEFAULT_WORKER_URL + "esri" },
    { name: "dojo", location: DEFAULT_WORKER_URL + "dojo" },
    { name: "dojox", location: DEFAULT_WORKER_URL + "dojox" },
    { name: "dijit", location: DEFAULT_WORKER_URL + "dijit" },
    { name: "dstore", location: DEFAULT_WORKER_URL + "dstore" },
    { name: "moment", location: DEFAULT_WORKER_URL + "moment" },
    { name: "@dojo", location: DEFAULT_WORKER_URL + "@dojo" },
    {
      name: "cldrjs",
      location: DEFAULT_WORKER_URL + "cldrjs",
      main: "dist/cldr"
    },
    {
      name: "globalize",
      location: DEFAULT_WORKER_URL + "globalize",
      main: "dist/globalize"
    },
    {
      name: "maquette",
      location: DEFAULT_WORKER_URL + "maquette",
      main: "dist/maquette.umd"
    },
    {
      name: "maquette-css-transitions",
      location: DEFAULT_WORKER_URL + "maquette-css-transitions",
      main: "dist/maquette-css-transitions.umd"
    },
    {
      name: "maquette-jsx",
      location: DEFAULT_WORKER_URL + "maquette-jsx",
      main: "dist/maquette-jsx.umd"
    },
    { name: "tslib", location: DEFAULT_WORKER_URL + "tslib", main: "tslib" }
  ]
} as any;

Best Practices

When working with CSS, you can load the files directly from your application and let the html-webpack-plugin and mini-css-extract-plugin output the CSS file and inject the file location directly into your output HTML file.

// index.tsx
import "./css/main.scss";
// webpack.config.js
  ...
  module: {
    rules: [
      ...
      {
        test: /\.html$/,
        use: [
          {
            loader: "html-loader",
            options: { minimize: false }
          }
        ],
        exclude: /node_modules/
      },
      {
        test: /\.scss$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"]
      }
    ]
  },
  plugins: [
    ...
    // will copy your index.html file
    // and inject assets for you
    new HtmlWebPackPlugin({
      template: "./src/index.html",
      filename: "./index.html",
      chunksSortMode: "none"
    }),
    // will output a css file that you
    // imported in your application
    new MiniCssExtractPlugin({
      filename: "[name].css",
      chunkFilename: "[id].css"
    })
  ]
  ...

However, if you would like to use the style-loader to load your styles at runtime, you will need to modify your code.

// index.ts
import "css!./css/main.scss";
// webpack.config.js
...
  plugins: [
    ...
    // For plugins registered after the ArcGISPlugin, data.request has been normalized and
    // resolved to an absMid and other mappings and aliases have been applied.
    // You will need to update the loader plugin appropriately.
    new webpack.NormalModuleReplacementPlugin(/^css!/, function(data) {
      data.request = data.request.replace(
        /^css!/,
        "!style-loader!css-loader!sass-loader?indentedSyntax=false"
      );
    })
...

Please note, we have tested the @arcgis/webpack-plugin with numerous other plugins, but cannot guarantee that other webpack plugins may not cause some unexpected behavior.