Using Bower for Custom Builds
Using Bower (A package manager for the front-end web development) is another option for developers of the JavaScript API to generate custom builds of the ArcGIS API for JavaScript. You would use the Bower download of the ArcGIS API for JavaScript if you wanted to create local builds of your application. This is similar to what you can do with the JavaScript Optimizer but locally.
- Prerequisites
- Getting started
- Understanding Bower and npm
- Generate a custom build (Dojo)
- Generate a custom build (RequireJS)
Prerequisites
- Node.js (includes npm, the node package manager)
- git
- Grunt - The JavaScript Task Runner
- Bower - A package manager for the front-end web development
- Java 7 or greater - required by Closure Compiler (Dojo build)
To check if these are installed:
node --version
git --version
grunt --version
bower --version
java -version
Tip: if you have issues with ssh
on your machine, which is common on Windows or due to firewall settings, you can tell git to use https
over ssh
.
git config --global url."https://github.com".insteadOf git://github.com
Getting Started
Now that your system is configured, it is time to make a decision on whether you are going to use the Dojo Toolkit or RequireJS.
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 Bower and npm
Understanding Bower
Bower requires Node.js so they naturally work well together. In the Generate a custom build (Dojo) and Generate a custom build (RequireJS) sections, you will be asked to issue a command npm install
. You can read more about npm-scripts, but essentially this tells node to download any dependencies
and/or devDependencies
listed in the package.json
file. See the package.json file in the dojo or RequireJS samples for more information.
The npm-scripts field in the package.json
file is special in the fact that some of the named scripts such as install
have the option to run special pre
and post
hooks. This is important because it allows you to perform operations before or after a given command. In our instance, we want to run the bower install
command on our preinstall
hook.
When using Bower, you can predefine what Bower packages you want to install via a file called bower.json
. Take a look at the example below:
{
"name": "arcgis-js-api-sample-app",
"version": "1.0.0",
"dependencies": {
"esri": "arcgis-js-api#4.4.0"
},
"resolutions": {
"dojo": "v1.10.4/esri-3.14.0"
}
}
What this file is saying is that when you do bower install
it will download version 4.4 of the ArcGIS API for JavaScript.
The default folder all Bower dependencies are downloaded to is called bower_components
. Notice how we have a dependency called esri that is pointing the arcgis-js-api
Bower package. This means it will name the folder esri
that the Bower package is downloaded, so it will look like bower_components/esri
.
The ArcGIS JS API package has some of its own predefined dependencies that will also be downloaded. This is why there is a resolution for the dojo
dependency to use our version. You can see a list of the dependencies in the README for the Bower package. When everything is downloaded, you should have a folder structure that looks like this.
bower_components
├── dgrid
├── dijit
├── dojo
├── dojox
├── dstore
├── esri
├── moment
└── util
When you develop your application, you can set up your dojoConfig
to point to these folders.
var dojoConfig = {
baseUrl: '.',
packages: [
{
name: 'dgrid',
location: 'bower_components/dgrid',
},
{
name: 'dijit',
location: 'bower_components/dijit',
},
{
name: 'dojo',
location: 'bower_components/dojo',
},
{
name: 'dojox',
location: 'bower_components/dojox',
},
{
name: 'dstore',
location: 'bower_components/dstore',
},
{
name: 'esri',
location: 'bower_components/esri',
}
]
};
Now you have full access to the ArcGIS API for JavaScript and its dependencies.
You could take this one step further and have the Bower packages installed into a different folder. Let's assume you are writing your application in a src/app
folder. You can have the Bower packages downloaded into the src
folder by default by creating a .bowerrc
file in the root of your application that looks like this.
{
"directory":"src/"
}
When you do bower install
it will download all the same packages into the src
directory. This allows you to simplify your dojoConfig
inside your src
folder.
var dojoConfig = {
baseUrl: '.',
packages: [
'app',
'dijit',
'dojo',
'dojox',
'dstore',
'dgrid',
'esri'
]
};
This will make it easier to do local custom builds of your application.
Understanding npm
Since Bower is run under the preinstall
hook of the npm-scripts it precedes the install
script. So when you issue the command npm install
, preinstall
runs first which is actually the command bower install
. Then npm install
runs looking for dependencies listed in the package.json
file. Npm will then download and install any dependencies listed in the dependencies
and/or devDependencies
section in the package.json
file. Finally, the npm install
sequence is where Grunt and the supporting plugins grunt-contrib-clean and grunt-contrib-copy are installed.
See the package.json
file in the Dojo or RequireJS samples for more information.
Generate a custom build (Dojo)
For both options (clone or download), you need to copy the source files from https://github.com/Esri/jsapi-resources/tree/master/4.x/bower/dojo
to a new directory. In the following steps, we'll name the new directory esrijs-bower-dojo
.
The contents of your directory should appear as follows:
esrijs-bower-dojo
├── .bowerrc
├── Gruntfile.js
├── README.md
├── bower.json
├── build.profile.js
├── package.json
└── src
├── app
│ ├── main.js
│ ├── package.js
│ └── package.json
├── built.html
└── index.html
- Open a terminal window or shell command window
- Change directory to
esrijs-bower-dojo
- In the terminal window run:
npm install
- The first time this is run it may take a while
- The
arcgis-js-api
will be downloaded by bower - Subsequent times when bower runs, the
arcgis-js-api
repository will be cached by bower and take less time to download dependencies
and/ordevDependencies
will download via npm (Grunt plugins includinggrunt-dojo
)
- In the terminal window run:
- In the terminal window type
npm run build
and press return - The command will run the dojo build system against the ArcGIS API for JavaScript library and your application
- Note: (This will delete the built application!). In the terminal window type
npm run clean
and press return to delete the built application
Dojo technical details
The Dojo build system accepts a build configuration profile. A sample build profile is available in the Bower dojo sample in the jsapi-resources repository on GitHub. The key is to define your build packages, which you can learn more about in this AMD loader tutorial. In addition to packages, you should understand what a layer file is, which is essentially a single file that is composed of many modules and their dependencies.
layers: {
'dojo/dojo': {
include: [
'app/main', // Your application entry point
'esri/widgets/Attribution' // a dynamically loaded module
]
}
}
This tells the Dojo build system to build the dojo/dojo.js
file and to include other files into it. This will get you an optimized single-file that you can use to deploy your application. Please see the sample build profile for more details.
Generate a custom build (RequireJS)
For both options (clone or download), you need to copy the source files from https://github.com/Esri/jsapi-resources/tree/master/4.x/bower/requirejs
to your esrijs-bower-requirejs
directory.
The contents of your directory should appear as follows:
esrijs-bower-requirejs
├── .bowerrc
├── Gruntfile.js
├── README.md
├── bower.json
├── package.json
└── src
├── app
│ └── main.js
├── built.html
└── index.html
- Open a terminal window or shell command window
- Change directory to
esrijs-bower-requirejs
- In the terminal window run:
npm install
- The first time this is run it may take a while
- The
arcgis-js-api
will be downloaded by bower - Subsequent times when bower runs, the
arcgis-js-api
repository will be cached by bower and take less time to download dependencies
and/ordevDependencies
will download via npm (Grunt plugins including grunt-contrib-requirejs and uglify-js
- In the terminal window type
npm run build
and press return - The command will run the requirejs build system against the ArcGIS API for JavaScript library and your application
- Note: (This will delete the built application!). In the terminal window type
npm run clean
and press return to delete the built application
RequireJS technical details: The RequireJS Optimizer has some limitations when trying to build some of the Esri and Dojo modules. What this means is that you can get pretty close to a single-file release; however, some modules will still need to be dynamically loaded.
Many of the issues related to using the RequireJS Optimizer have to do with how it handles loader plugins during the build process.
An example of a loader plugin is the dojo/has plugin, that can do conditional loading of modules. The RequireJS Optimizer will attempt to execute these plugins as it sees them in a module.
However, the RequireJS Optimizer by default runs in a Node environment and does not have JavaScript host objects such as document that may be used by a loader plugin. The Dojo build tools work around this issue. You can setup the build configuration to work around most of these use cases. You would need to tell the RequireJS Optimizer to ignore modules that will cause an error with loader plugins.
paths: {
'dojox/gfx': 'empty'
}
This is the syntax you would use to tell the RequireJS Optimizer to not load the module during the build process. Some Dojo plugins have RequireJS counterparts that can be swapped out during the build process, so you can do something like this.
paths: {
'dojo/text': 'text/text'
}
This syntax tells the RequireJS Optimizer to load the text/text
module in place of the dojo/text
module allowing your build to complete.
Attempting to build many modules into a single file may require a little trial and error. Some modules will attempt to dynamically load modules when they are needed, which means they are not picked up by the build process. If you notice some files dynamically loaded after you do a build, you can try and force them into your built file.
include: [
'esri/widgets/Attribution'
]
This syntax will tell the RequireJS Optimizer to add this module and its dependencies to your output file. You can find more details in the RequireJS build configuration file.