Monday, April 15, 2013

ESRI JSAPI 3.4 and the Dojo Build System

[Update: The Saga Continues]

In a previous post, I outlined how I use the Dojo Build System to optimize my web app code for production. Specifically I showed how I get around the problem of working with ESRI's ArcGIS API for JavaScript library which has already been run through the build system. However, with their recent upgrade to AMD-style module loading my handy trick of using:

dojo['require']('esri.map');

... to fool the build system into skipping 'esri...' modules imports didn't work anymore. I had no idea how to exclude modules when loading them like this:

define(['esri.map'], function (Map) { /* ... */ });

So I headed over to #dojo to see if the experts had any ideas. Fortunately for me, brianarn was there and was aware of the problem. After some brain storming, we came up with the idea to use a custom loader plugin for loading ESRI modules. Since the build system doesn't try to flatten modules that are imported with nested requires, we hoped that importing them through the plugin would solve my problem. The plugin was a relatively simple implementation:

You can put this module within any package and use it like this:

define(['app/EsriLoader!esri/map'], function(Map) { /* ... */ });

Using the plugin to load ESRI module effectively prevents the build system from trying to include them in your layer files thus allowing the build script to complete successfully. Of course, none of this hacking would be needed if ESRI would just release their source code. :)

If you find a better way of getting around this problem or have any other suggestions please let me know in the comments section below.

4 comments:

  1. Hi Scott,

    Have another solution. We have a python script, which collects all requires included in jsapi.js and create a file of these extracted requires. We use that file in build profile layer for ecxlude param.

    But the problem exists, that we want to have all esri files not included in jsapi.js (like esri/dijit/Measurement) in our layer file. But if we include built files we got "multipleDefine" errors.

    ReplyDelete
    Replies
    1. That sounds cool! I would love to see your script if you are able to share.

      Recently I've been working on a new build solution that uses a new AMD build of ESRI's api. Basically they have built their api but keep all of there modules separate. No layer file. So it's minified, but at least still modular. Take a look at: http://serverapi.arcgisonline.com/jsapi/arcgis/3.5amd/js/dojo/dojo/dojo.js

      I'm close to having a fairly automated build process and will write a post about it hopefully soon.

      Delete
  2. It's really a simple regular expression for collecting these requires from jsapi.js. As You can see in jsapi.js file modules are cached in such a pattern:

    "esri/tasks/FindResult" : function () {

    So You can use lookahead assertion in regexp and collect all dojo|dijit|dojox|esri requires. The new file should look like this:

    define([ /* here goes all requires separated by commas "esri/tasks/FindResult" */ ], function () {});

    I think it would be easy for You to write such a simple Python script. But if You still have any problems, just ask.

    Yes, that 3.5amd is small, but we need to avoid many HTTP requests. And the best solution would be to create a build layer with all these (project specific, esri, dojo, others modules, which are not preloaded) in one file. That's the problem, because we don't have ESRI JS API source files and when we make a build from those built modules we got smth like this:

    "esri/dijit/Measurement" : function () {
    define("esri/dijit/Measurement", ["dojo", "dijit", "dojox"], function (dojo, _656, _657) {
    ...
    define("esri/dijit/Measurement",
    ...

    And this is not able to be required. Throws multipleDefine error.

    ReplyDelete
  3. Brilliant just what i have been looking for. Really grateful thankyou.

    ReplyDelete