Custom build in dojo 1.7.

Introduction

In this blog-post I will explain how to do a simple custom build with dojo’s build system. Dojo is a great JavaScript framework and you can read more about it at dojotoolkit.org.

I write this from the experience gained from upgrading our application from dojo 1.6 to dojo 1.7. Dojo’s upgrade to version 1.7 is the biggest change in the dojo core we have seen for a long time, now introducing AMD (asynchronous module loading). This upgrade also affected the build system, which is new for dojo 1.7. We found that we where unable to reuse our current build-set-up after upgrading. I wrote about this because dojo still lacks some easy and simple examples on how to do custom build.

The official documentation for the build system is found at livedocs.dojotoolkit.org/build/buildSystem

If you only are interested in the solution, please head straight to the GitHub repository: https://github.com/ivarconr/dojo-custom-build. Here you will find all the source files used in this tutorial.

Why do we need custom build?

Dojo 1.7 introduced AMD which heavily increases the speed modules, classes and dependencies are loaded. Now your application only need to load dependencies required to satisfy the specific request and can wait with other dependencies until they actually are needed. Modules can even be downloaded to the browser in parallel (huge speed gain). This change in dojo will make many dojo applications start up much faster, especially if they use many modules.

Still, when the application grows, and you get hundreds or even thousands of JavaScript files it will be many HTTP GET request required to download all these small JavaScript files which builds up a typical application. We know by experience that many small files is more costly to download, than if we concatenate them into larger files which are downloaded. This gets even worse if we first have to download module A to find out that it requires module B which again requires module C. We have no way of knowing that we need to download module C before we have downloaded both module A and module B. This is fine in development, but not in production.

Layers to the rescue

What we do in our application is to combine related JavaScript files into layers. We try to see the application from the user-perspective and combines related scripts. We combine our modules in such a way that all files needed to perform a specific action is built into a layer. We also use a core-layer where we put core functionality shared by many different modules (the overlap).

  • Core-layer – Contains files/modules used many places in our application. Definitions in this layer is excluded from the other layers. 
  • Function-layer-x – Contains files/modules required to perform action x in the application. We have many “x”  layers.

The Dojo Build System – A tutorial (1.7.1)

Requirements

Project structure

The file structure used in the tutorial project is: ( alsoavailable at GitHub):

├── index-dev.html
├── index-release.html
├── scripts
│   ├── dojo-release-1.7.1-src
│   │   ├── dijit
│   │   ├── dojo
│   │   ├── dojox
│   │   └── util
│   ├── mycompany
│   │   ├── mycompany.profile.js
│   │   ├── package.json
│   │   └── widgets
│   ├── release
│   │   ├── build-report.txt
│   │   ├── dijit
│   │   ├── dojo
│   │   └── mycompany
│   └── release.profile.js
└── styles

I have placed dojo-src under scripts and my own company package inside the “mycompany” folder. It is important to not mix the dojo-source with my own source files. This makes it much easier to upgrade dojo later.

I use index-dev.html during development. In release I switch to index-release.html. I will explain the difference between these files later.

The built files are placed in the relese directory with the build script.

The package profile

The mycompany folder is the folder containing all the files for the “mycompany” package. To help the build system to build this layer I have a package profile, telling the build system important information about this package, such as which files are valid AMD modules, which files are test files and configuration files to only
copy during the build phase.

var profile = (function(){
  copyOnly = function(filename, mid){
	var list = {
	"mycompany/dojo.profile":1,
	"mycompany/package.json":1
    };
	return (mid in list) || /(css|png|jpg|jpeg|gif|tiff)$/.test(filename);
  };

  return {
	resourceTags:{
		test: function(filename, mid){
			return false;
		},

		copyOnly: function(filename, mid){
			return copyOnly(filename, mid);
		},

		amd: function(filename, mid){
			return !copyOnly(filename, mid) && /\.js$/.test(filename);
		}
	},

	trees:[
		[".", ".", /(\/\.)|(~$)/]
	]
  };
})();

This profile is heavly based on dojo’s profile for the dojo package, found under dojo/dojo.profile.js

The layer build profile

To perform the actual build I use a profile on a higher layer (application layer) which tell the build system about the different packages used in the application, and what layers to build. This can be packages we have built our self, such as the mycompany package, our third party packages, such as the dijit package provided by dojo.

release.profile.js Looks like:

var profile = {
    basePath: "./",
    layerOptimize: "shrinksafe.keepLines",
    releaseDir: "./release",
    hasReport: true,

    packages:[
        {
            name: "dojo",
            location: "./dojo-release-1.7.1-src/dojo"
        },
        {
            name: "dijit",
            location: "./dojo-release-1.7.1-src/dijit"
        },
        {
            name: "mycompany",
            location: "./mycompany"
        }
    ],

    layers: {
        "mycompany/layers/core": {
            include: [
                "dijit/_Widget",
                "dijit/_Templated"
            ],
        },
        "mycompany/layers/example": {
            include: [
                "mycompany/widgets/Example"
            ],
            exclude: [
                "mycompany/layers/core"
            ]
        }
    }
};

In the release profile I have defined which packages I have used and the location of them. I have also included the package “mycompany”. This package holds all of the company specific code. The example also shows that I have included two layers:

  • mycompany/layers/core – This layer contain the core functionality
  • mycompany/layers/example – Our example widget. This includes all modules required for ExampleWidget (all transitive dependencies), but excludes dependencies found in the core layer.
More details on about layer configuration can be found in dojo’s live documentation: http://livedocs.dojotoolkit.org/build/transforms/writeAmd

Executing the custom build

I execute the build from the “mywebapp/scripts/dojo-release-1.7.1-src/util/buildscripts” folder by using the build.sh or build.bat script:

./build.sh profile=../../../release.profile.js -r

The build system will place the built files into the release directory inside scripts because I have defined the relaseDir inside our relase.profile.js. The build-script will decide whether to use node or Java. Node is preferred because it can run the analysis and build process in parallel with multiple threads, while Rhino only supports one thread, which makes it much, much, much slower….

Development mode vs. release mode

Development mode

To enable us to develop without having to build the layers all the time I use a special html-file for starting the application in dev mode named index-dev.html. In this mode we just load required modules asynchronously as they are needed. This is supported by dojo out of the box and we basicly requieres stuff just when we need them. It us much easier to debug if we load all modules individually during development.

I have measured some stats in dev mode (no layers):

  • 57 individual HTTP GET requests
  • 840ms before onLoad is fired
As seen from the numbers I actually end up with 57 individual request to fetch all resources required for my little application. This is fine in development because it is easy to change code, and we get better debugging in the browser.

Release mode

After I have performed the build I will have a release catalog containing our built javascript, including my layers. This folder contains the built layers which I want to use in my application. In production I will use index-release.html where I have changed the code a bit to also handle the built layers. The important part is to notice the extra require part in the start-app script:

require(["mycompany/layers/core", "mycompany/layers/example"], function() {
  require(["mycompany/widgets/Example","dojo/domReady!"], function(Example) {
    new Example({}, "app");
  });
});

As you can see I require both layer-modules first, which includes all the required JavaScript files in my application. This way I reduces the number of HTTP GET requests to a minimum. I still keep all my files in the release folder. This enables easy fallback for dojo to load dependencies asynchronously if I for some reason should forget (or exclude) a reuired JavaScript in the layer.

Stats in release mode (with layers):

  • 5 individual HTTP GET requests
  • 355ms before onLoad is fired
This is significantly better performance and better suited for production.

Can different layers include the same modules?

Can I have the same JavaScript bundled into different layers? Yes you can. Dojo will actually handle this very well. You should try though to avoid this when possible, and always evaluate whether these modules can be part of a “core” layer because the layers will be larger if multiple layer modules include the same scripts. This basically comes down to a trade off, we want to limit the size off the core layer and at the same time limit the size of a function layer.

One solution can of course be to have multiple “core layer” for different function groups. It all comes down to you having to find which layering that gives the best performance for your application.

Don’t put everything in one layer

Even though fewer HTTP GET’s are good we must make sure to not put everything inside ONE layer. Putting it in one layer can give us one HUGE file, which basically gives our application an undesired long startup time. We can also gain something from loading a few modules in parallel. We should evaluate the nature of the application when we decide how to define our layers. We should try to group stuff we need together inside modules. It is also important to discover the overlaps between layers and try to create “overlap” layers.

Summary

This ends my blog about how to build custom layers in dojo 1.7.1. I have placed all my files on github: https://github.com/ivarconr/dojo-custom-build. Please post any comments or questions you may have. In the next blogpost I will explain how we are using custom build as part of our maven build process to ensure that our layers are built each time we build our Java Webapplication with Maven.

About these ads

68 thoughts on “Custom build in dojo 1.7.

  1. i have been at this(*build profile*) for long but i’m gonna try your example where you did the two require makes perfect sense one to require the file(layer) and looking at the layer code it creates a cache mapping the other dependencies built into the layer. i hope this works.

  2. Thanks a lot it worked, i guess i was doing it right but didn’t know about the require of the layer i used the layer as a script tag instead. now i can work with AMD and compact builds. nice one bro.

  3. Firstly, thanks thanks thanks.. i spent an entire day lookin into Dojo docs and got nowhere. This tutorial is awesome. I have one follow up question. In the compressed layer file, one of my classes is defined as follows

    define(["dijit", "dojo", "dojox", "dojo/require!foo/bar/baz"]

    this leads to a calls to

    http:/host:port/release/js/dojo/require.js

    Failed to load resource: the server responded with a status of 404 (Not Found)

    Should’nt this be loaded as part of the dojo layer?

    PS: My entire project works perfectly fine in dev build. All the code is Pre 1.7(sync), just migrating to 1.7

    Thanks in advance

    • The “dojo/require” module (legacy) is not equal to “require” (the new way to require dependencies) and is not part of the built dojo.js module. It is part of the old synchronous loading system used in dojo < 1.7. If you want to use it, you have to build it in to one of your own layers to avvoid the additional get (just add it as an include on one of your layers or you can set the module mapping for dojo correctly).

      I am curious of the source file for this definition. Can you show me that, or extract it into a simple scenario?

      • Thanks the reply. Including it in the layer works.. but it leads to more missing files. Eg.

        http://host:port/js/dojo/cldr/nls/en/number.js

        In my pre 1.7 build, all dependencies are automatically pulled in and built into the layer. Is there a switch to force that now?

        Also, here is a sample of the source file

        dojo.provide(“foo.bar.main”);
        dojo.require(“foo.bar.baz”);

        dojo.addOnLoad(function () {
        function loaded() {
        alert(“loaded”);
        }
        var foo = new foo.bar.baz(loaded());
        });

      • Ahh.. you are absolutely correct. NLS files are not included in the layers. In pre 1.7 you could specify to the build-system that which nls-files you want to bundle (via the localeList option). I have investigated this a lot (reading all documentation and source code for the buildsystem) and found that this is currently not supported in the new build system. I have found some references to a “locales” array, but it has never been used (might be that they plan to include this later?). For our application I have found this to be a minor issue, and we just load the NLS files async from the Google CDN. Personally I would prefer to do a layer-build for a specific language and disabling the NLS feater, as many users don’t need it.

      • Regarding the provided source: you are still using the depreacted legacy syntax (pre 1.7). This is why dojo need to load dojo/require if you don’t included in one of your own layers.

  4. Yes, the codebase is legacy and it is too huge to change now. The regression regarding localization files is mentioned here, http://bugs.dojotoolkit.org/ticket/14169.

    Looking at my build logs, “dojo/cldr/nls/number” is a dependency; However, at runtime, “dojo/cldr/nls/en/number” is looked for which is not available. Do you know why that would happen? I’ve not specified any locale in djConfig.

    • Sorry about the last question, you had already answered it in the previous comment. I will try to load the us-en async from CDN. All this was very smooth with the pre 1.7 build process. Thanks again!

  5. Thanks! for a great article!
    Unfortunately Dojo i well known for its pure documentation…
    specially for people doing the transition to more “serious” js frameworks.

    Hope you will post additional interesting post.

    Thanks for sharing

      • Hi,
        by “more serious” i meant more complex.
        i used to work mostly with jquery and pure javascript, the concepts of builds, layers and AMD are new to me and it is quite hard to find good tutorials for beginner.

        Thanks

  6. Fabulous! Thanks for this useful example, it really helped me get started with the new builds.

    I’m stuck trying to get layers and custom modules to work with the parser and I must be missing something. I can get parseOnLoad to work with dijits (Button, Dialog, whatever), but not with mycompany.widget.Example. Any ideas? I appreciate any hints or help.

    This is how I modified your example to try to use the parser. The programmatic widget shows up, but when it tries to load the declarative/parsed widget I get an error saying “Could not load class ‘mycompany.widgets.Example”.

    loading widget programatically…

    loading widget declaratively…

    var dojoConfig = {
    async: 1,
    parseOnLoad: false
    };

    require(['dojo/_base/kernel','dojo/parser','dojo/_base/loader'], function(dojo, parser){

    dojo.registerModulePath(‘mycompany’,’../mycompany’);

    require(['mycompany/layers/core', 'mycompany/layers/example'], function() {
    require(['mycompany/widgets/Example','dojo/domReady!'], function(Example) {
    new Example({}, “app”);
    parser.parse();
    });
    });

    });

    • We don’t use declarative markup in our project, but I have tested a bit and I have found the following:

      1. declarative markup _works_ for dojo-specific code
      2. declarative markup does not work for custom widgets by default in 1.7.

      I’m not sure if this is because dojo changed how to handle declarative markup (some special requires?) or because they just broke that functionality.

      I will try to dig up some official documentation on this.

  7. Hi ivarconr,
    Im new to Dojo, been digging into how to Build it.

    After building as per this Tutorial, i did get a release folder… if i have to run the file locally… what should i do?
    i tried opening the 2 htmls. But shows “loading webapp..” and nothing happens….

    can u explain in simple terms..?

    Thanks in advance….

      • Thanks Ivarconr for the reply…

        I have a fews doubts about the Build system.

        1. when i build a simple example with a someprofile.js, i get a release folder created which is 14MB or greater.
        im looking for a single JS file or so…. which is less than 1MB. Is there any other way we could achieve it.
        the Build Arguments which i pass is
        build.bat profile=../../dij.profile.js action=release ##### without profile.js as file name also i tried #######

        2. If i have to Deploy the web app which i develop in DOJO to Mobile using phoneGap ….. how should i make a build to make a single JS file which contains all that the app requires….?

      • You only have to include the layer files in your application (which can be 1 single js-file). All your included resources should be inside there. You can even build in a custom dojo-base in to that layer so that you only include what is needed by your application. This will keep the application size to an absolute minimum.

  8. My profile file looks like this:

    dependencies = {
    layers: [
    {
    // This is a specially named layer, literally ‘dojo.js’
    // adding dependencies to this layer will include the modules
    // in addition to the standard dojo.js base APIs.
    name: “dojo.js”,
    dependencies: [
    "dojo.parser",
    "dojo.domReady",
    "dojo.ready",
    "dijit.registry",
    "dijit.Dialog",
    "dijit.Tooltip",
    "dijit.form.Button",
    "dijit.layout.ContentPane"
    ]
    }
    ],

    prefixes: [
    [ "dijit", "../dijit" ],
    ]
    }

    My html requires dojo.js. I notice that the browser attempts to include selector/acme.js, which I didn’t move to the web server – I thought I’d only need dojo.js. When the page loads, the javascript console reveals the error “_5c4 is not a function” when attempting to do something with digit.byId.

    When I instead load the full Dojo from ajax.googleapis.com, everything works perfectly.

    Very frustrated – ay help appreciated!

      • Thanks, that’s a good point. I just thought I could have a smaller build if I went totally custom. We don’t have any custom code to include, so I thought this would be trivial. In fact, it was trivial for my custom 1.3 build, but now we want to upgrade to 1.7.2 in order to take advantage of AMD modularization.

        For some reason, when I use http://build.dojotoolkit.org/, the dojo.js that gets generated also throws that error.

        One more thing – I notice that with my command line build that the browser attempts to include selector/acme.js, which I didn’t move to the web server. I thought I only needed to move dojo.js.

        Anyway, can you help with some direction on using the pre-built core and only bundling the necessary digit stuff? I’d like to host all the dojo-related .js locally, and obviously I want to minimize the number of file requests the browser makes.

        Thanks for your help,

        Christian

  9. Essentially, all I want is the smallest possible dojo.js file to be stored locally which can utilize only the following dojo/dijit types/functions:

    dojoType=”dijit.Tooltip”
    dojoType=”dijit.Dialog”
    dojoType=”dijit.layout.ContentPane”
    dojoType=”dijit.form.Button”
    dijit.byId()

    and set the theme thusly:
    // set the dojo theme
    require(["dojo/domReady!"], function(){
    document.body.className += ” claro”;
    });

  10. Brilliant, thanks for all of your help. I was able to get a working dojo.js file down to the size of 182 K. I was wondering if you had any suggestions on how to go even smaller? Here’s the profile I used:

    var profile = {
    mini: true,
    optimize: “closure”,
    layerOptimize: “closure”,
    stripConsole: “all”,
    selectorEngine: “acme”,
    packages:[
    {
    name: "dojo",
    location: "/dojo"
    },
    {
    name: "dijit",
    location: "/dijit"
    },
    ],

    layers: {
    “dojo/dojo”: {
    include: [
    "dojo/parser",
    "dojo/domReady",
    "dijit/registry",
    "dijit/Dialog",
    "dijit/Tooltip",
    "dijit/form/Button",
    "dijit/layout/ContentPane" ],
    customBase: true,
    boot: true
    }
    },
    resourceTags: {
    amd: function (filename, mid) {
    return /\.js$/.test(filename);
    }
    }
    };

  11. Hi Ivar

    Thank you for your help.
    I am managing dojo for a few time and this is the first time I make a build.
    I have read many profile examples but I am having some troubles, i.e. I dont know what I am doing wrong.

    The build make the next output:
    release
    release/layers // << within the layers
    release/layers/nls // << within localeList js layers
    release/dojo
    release/dojo/dijit // << all the dir content within all themes
    release/dojo/dojo // << all the dir content (within _firebug dir…)
    release/dojo/dojox // << all the dir content
    release/dojo/snet // << all the dir content
    release/dojo/build-report.txt

    In this part, one question is how I can left the dojo/dojo dir with the minimal content (and if posible within 1 file)
    and another question is if there is posible to left in the build only 1 theme.

    The real problem is:
    I have to put the layers dir into dojo dir because dojo needs to find the layers and the nls layer locale file.
    And it gives me another error (in firebug): "Could not load class 'dijit.Menu"

    The HEAD of the HTML file take this form:

    require(["dojo/parser"
    ,"dojo/dom"
    ,"dijit/registry"
    ,"dojo/ready"
    ,"layers/core"
    ,"layers/menu"
    ]
    , function(query,on,dom,ready){
    ready(function(){

    });
    }
    );

    And the profile:

    var RPR = “./../../core/public/”; //path
    var profile = {
    basePath: “./”
    , releaseDir: “./release”
    , releaseName: “dojo”
    , hasReport: true
    , action: “release”
    //
    // Optimizations
    , cssOptimize: “comments”
    , mini: true
    , optimize: “closure”
    , layerOptimize: “closure”
    //, stripConsole: “all”
    , selectorEngine: “acme”
    //
    , localeList: “es-es”
    , packages: [
    {
    name: "dojo"
    , location: RPR + "js/framework/dojo/dojo"
    }
    , {
    name: "dijit"
    , location: RPR + "js/framework/dojo/dijit"
    }
    , {
    name: "dojox"
    , location: RPR + "js/framework/dojo/dojox"
    }
    , {
    name: "snet"
    , location: RPR + "js/framework/dojo_addons/snet"
    }
    ]
    //
    , layers: {
    “dojo/dojo”: {
    //name: “mydojo.js”,
    include: [
    "dojo/dojo"
    , "dojo/dom"
    , "dojo/domReady"
    , "dojo/i18n"
    , "dojo/parser"
    , "dojo/ready"
    , "dijit/registry"
    //
    , "dijit/dijit"
    ]
    , boot: true
    , customBase: true
    }
    , “layers/core”: {
    include: [
    "dijit/Dialog"
    , "dijit/form/Button"
    , "dijit/form/CheckBox"
    , "dijit/layout/ContentPane"
    , "dijit/Tooltip"
    //, "dijit/themes/claro"
    , "snet/DialogConfirm"
    ]
    , exclude: [
    "layers/menu"
    , "layers/accordion"
    , "layers/chart"
    ]
    }
    , “layers/menu”: {
    include: [
    "dijit/form/DropDownButton"
    , "dijit/Menu"
    , "dijit/MenuItem"
    , "dijit/MenuSeparator"
    , "dijit/PopupMenuItem"
    , "dijit/Toolbar"
    ]
    , exclude: [
    "layers/core"
    , "layers/accordion"
    , "layers/chart"
    ]
    }
    , “layers/accordion”: {
    include: [
    "dijit/layout/AccordionContainer"
    //, "dijit/layout/ContentPane"
    ]
    , exclude: [
    "layers/core"
    , "layers/menu"
    , "layers/chart"
    ]
    }
    , “layers/chart”: {
    include: [
    "dojox/charting/Chart2D"
    , "dojox/charting/action2d/Highlight"
    , "dojox/charting/action2d/MoveSlice"
    , "dojox/charting/action2d/Tooltip"
    , "dojox/charting/themes/Julie"
    ]
    , exclude: [
    "layers/core"
    , "layers/menu"
    , "layers/accordion"
    ]
    }
    }
    //
    , staticHasFeatures: {
    ‘dojo-trace-api':0
    , ‘dojo-log-api':0
    , ‘dojo-publish-privates':0
    , ‘dojo-sync-loader':0
    , ‘dojo-xhr-factory':0
    , ‘dojo-test-sniff':0
    }
    //
    , resourceTags: {
    test: function (filename, mid) {
    return false;
    }
    , copyOnly: function (filename, mid) {
    return copyOnly(mid);
    }
    , amd: function (filename, mid) {
    //return /\.js$/.test(filename);
    return !copyOnly(mid) && /\.js$/.test(filename);
    }
    /*, miniExclude: function (filename, mid) {
    return mid in {
    ‘app/profile': 1
    };
    }*/
    }
    };

    I hope you can guide me within this problem.

    Thank you in advance.

    • A bit late answer sorry for that. I hope you have already figured this it, if not here is my input on your questions:

      1. The minimal dojo/dojo-build possible is already built and provided by dojo, I would rather just use that instead of building my own if you don’t have any extremely custom requirements. Just grab it from: http://ajax.googleapis.com/ajax/libs/dojo/1.7.2/dojo/dojo.js

      2. When dojo moved from dojo 1.6 to dojo 1.7 they lost the possibility of including nls-files into the custom layers. You can resolve this issue in two ways:
      a) just use their CDN when including dojo. All files you haven’t included in a layer, such as a nls file, will be automatically required from the proivded CDN.
      b) or you can of course host the source-files yourself if you want. You must specify where dojo is located though. It will assume a dojo, dijit and a dojox folder structure from that location.

      3. I really need more details to be able to help you resolve the “Could not load class ‘dijit.Menu'” issue.

      • Thank you for your reply.
        The problem is that within the build directory structure created (see above) dojo can not find
        – layers/core.js
        – layers/menu.js
        When I move the layers dir into dojo dir (so: relese/dojo/layers/) dojo find them,
        but gives an error “Could not load class ‘dijit.Menu”, and I dont know how to resolve it.

        As I said the build’s dir structure dont have the layers dir into dojo dir (profile->releaseName), so dojo cant find the layers.
        The build output structure should be:
        release
        release/dojo
        release/dojo/layers //<< in dojo
        release/dojo/layers/nls //<< in dojo
        release/dojo/dijit
        release/dojo/dojo
        release/dojo/dojox
        release/dojo/snet
        release/dojo/build-report.txt

        Sorry, I can't give you more details.

    • I think you should not put your custom layers and own packages inside the release/dojo folder. This sort of mix up different concerns, It would be better to tell dojo where to find your own custom built modules. This can be done with this code snippet:

      require(['dojo/_base/kernel'], function(kernel){
      kernel.registerModulePath(“layers”, “/scripts/release/layers”);
      });

      Why it can’t find dijit.menu is a mystery for me. I have built your menu layers and after inspecting the layer-module it seems to have included dijit/menu correctly. I really need a more concrete example/some extracted code that shows the problem to help you with that issue.

      • Trying to run ‘kernel’ gives me errors. The only way I can run this (within registerModulePath) is:
        (giving me the same error “Could not load class ‘dijit.Menu”):

        require(["dojo/parser"
        ,"dojo/dom"
        ,"dijit/registry"
        ,"dojo/ready"
        //,"layers/core"
        //,"layers/menu"
        ]
        , function(query,on,dom,ready){
        ready(function(){
        dojo.registerModulePath(“layers”, “/../dojo_build/release/layers”);
        require(["layers/core"
        ,"layers/menu"
        ,"dojo/ready"
        ]
        , function(ready){
        //… actions
        }
        );
        });
        }
        );

        //———————————
        I have seen that when I make the build it gives me 71 errors (and many more warnings).
        Have you got errors in the build ?

  12. When I open the file “layers/core.js” at the final it has the next lines:


    require(["dojo/i18n"], function(i18n){
    i18n._preloadLocalizations(“layers/nls/core”, []);
    });
    define(“layers/core”, [], 1);

    //——————————————
    As you can see the arrays are empty.
    (The other layers’ files have the same problem).

    The profile I use is really correct ?

    • I have tried to put together a working example for you at: https://github.com/ivarconr/dojo-build-help

      ./build.sh profile=../../../test.profile.js -r

      Because you where building your own custom dojo (I don’t see you have any reason for doing that?) you also have to add the kernel and loader to be able to register a module-path.

      In the index.html I load first the base layers:
      “layers/core” and “layers/menu”

      before requiring “dijit/menu”. If you see in the network logs you will see that only the layers are loaded.

      I also noticed that you had cyclic dependencies in your layers-definition. This makes the build-system complain, and you should watch the build-logs carefully. You must never have an error in the build log, and the warnings should be at a minimum.

      I have also demonstrated how to use “”dojo/domReady!”, which is an AMD-plugin. This is the new way to require that the dom is loaded, and should be prefered over the legacy dojo.ready.

      The simplified build profile is located here:

      https://github.com/ivarconr/dojo-build-help/blob/master/scripts/test.profile.js

      Hope this help you with resolving your issues.

      • Thanks Ivar, it works very well.

        About build’s errors I had, well… , I corrected them.

        Thanks.

  13. Thanks, Ivar, for your very useful post.
    I have a doubt and I appreciate your help. I followed step-by-step your example, and I was able to make my build.
    I have 3 layers: base (for modules of dijit and dojox), core (my core modules), widget (my widgets). At header of my pages I put:

    require(['dojo/_base/kernel', 'dojo/_base/loader'], function(dojo){
    dojo.registerModulePath(“Manager”, “/public/scripts/manager”);
    });
    require(["Manager/layers/base","Manager/layers/core","Manager/layers/widget"], function() {
    require(["manager/Core"], function(Manager) {
    var manager = new Manager();
    manager.page = new Manager.Page();
    });
    });

    and this works. But I need that var manager to be global…:-( If I try:

    require(["Manager/layers/base","Manager/layers/core","Manager/layers/widget"]);
    require(["manager/Core"]);
    var manager = new Manager();
    manager.page = new Manager.Page();

    it works, but the file manager/Core,js is loaded again (and it is already embedded at manager/layers/core.js). Do you know how I can avoid this redundant load?

    Again, thanks for post and I hope you can help.
    Ely

    • Happy to hear that the guide is useful.

      If your need a variable to be global just drop the “var” assignment, or in a browsert you can always assign it to the window object to make it global because the window object is always the global scope in all browsers.

      require(["Manager/layers/base","Manager/layers/core","Manager/layers/widget"], function() {
      require(["manager/Core"], function(Manager) {
      window.manager = new Manager();
      manager.page = new Manager.Page();
      });
      });

      That said I would find another way to access it, as globals breaks the whole idea of using AMD. What do you try to achieve? Make a signleton? If that’s the case your should redesign your “manager” module to only export one instance of the manager object, and you can then just require that one object every place you need it.

      Good luck!

      • Thanks a lot! Yes, it is a singleton..I’m researching how to use singleton pattern with AMD, but your tip was valueable. One thing is strange yet: if I call require to a specific module (e.g “manager/Lookup”) out of the code:
        require(["Manager/layers/base","Manager/layers/core","Manager/layers/widget"], function() {
        });

        the file “manager/Lookup.js” is request directly from browser (it doesn’t use the code inside the layer “widget”)…I don’t know why…
        Thanks!
        Ely

  14. Hi Ivar,

    Sorry to bother you – I need your help. I’m trying to have a release build for my Dojo project,
    and I seem to have hit a wall.

    Everything worked fine in development mode, where all dojo modules and custom
    application modules loaded separately. Only there were more than 400 HTTP requests
    to load the application, so it took a while.
    I could build a release layer containing all dojo, dojox and dijit components that my
    application was using, and the application loaded OK, while the custom modules were
    still loading one by one. That was good, but not good enough.

    Next, I built another layer aggregating the custom components.

    The “bootstrap” component is called geno.Base, and it has the openWidget
    method that needs to be invoked, and that method initializes the rest of application.

    My index.html file is:

    djConfig =
    {
    baseUrl: “/dojoroot/dojo”,
    modulePaths:
    {
    “geno”: “/tgp/geno”,
    },
    require: ["geno.Base"],
    parseOnLoad: true,
    usePlainJson: true,
    locale: “en”
    };

    dojo.ready(function(){
    console.log(“I AM HERE”);
    var a = new geno.Base();
    a.openWidget();
    });

    When I load index.html, nothing happens. I.e., all the files load; “I AM HERE” is not printed.

    If, instead, I have this as the inline script:

    require(['dojo/geno'], function () {
    console.log(“I AM HERE”);
    var a = new geno.Base();
    a.openWidget();
    });

    the “I AM HERE is printed, but then the message “geno is not defined \n var a = new geno.Base();” appears in console.

    (and it doesn’t matter if I use “geno.js.uncompressed.js” or “geno.js”).

    If I have this:

    dojo.ready(function () {
    console.log(“I AM HERE”);
    var a = new geno.Base();
    a.openWidget();
    });

    (I pointed the second script to the location of individual bootstrap module, and removed waiting for dojo/geno),
    everything works – although loading dozens of individual application files.

    You can find relevant files on this server (it is the one that I’m doing development on):
    build profile: http://vilsha.com/dojoroot/util/buildscripts/profiles/geno.profile.js
    release directory: http://vilsha.com/dojoroot/release/dojo/dojo

    build command I use: ./build.sh profile=geno action=release
    build report: http://vilsha.com/dojoroot/release/dojo/build-report.txt

    • Hi this guide is targeted at dojo 1.7 builds using the new AMD-loader. You are still using the legacy “format” with the old “loader”. To make your code work you have to do the following:

      1. Explicitly include each layer as a “regular” HTML tags where needed.
      2. To require a dependency you should use the lagacy, e.g.: dojo.require(“geno.Base”)

      You can read more about the pre 1.7 build system here: http://livedocs.dojotoolkit.org/build/pre17/build

      Hope you figure it out!

      • Thank you for your reply! I recognized that when I copied-and-pasted my question, most of the text (namely, file snippets) was deleted – probably, because of HTML tags that were there… and my carefully crafted question appeared to be very sloppy…

        I did read the doc you pointed me to, and I reviewed it again just to make sure I comply. I do. Or I think I do :-)

        Anyhow, I did your (1) and (2).
        For (1), I have the following in my index.html file (is that what you meant?):
        <script type=”text/javascript” src=”/dojoroot/release/dojo/dojo/dojo.js”></script>
        <script type=”text/javascript” src=”/dojoroot/release/dojo/dojo/geno.js”></script>
        to include both layers that build script generated for me.
        For (2), I did the inline script like this:

        <script type=”text/javascript”>
        dojo.require(“geno.Base”);
        console.log(“I AM HERE”);
        var a = new geno.Base();
        a.openWidget();
        </script>

        The result is: “I AM HERE” is printed; next “geno is not defined” printed.

        Just for test – I’ll try to show my index.html file below:

        <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” http://www.w3.org/TR/html4/loose/dtd“>
        <html>
        <head>
        <meta http-equiv=”Content-Type” content=”text/html; charset=ISO-8859-1″ />
        <meta http-equiv=”X-UA-Compatible” content=”IE=EmulateIE7″ >

        <script type=”text/javascript”>
        djConfig =
        {
        baseUrl: “/dojoroot/dojo”,
        modulePaths:
        {
        “geno”: “/tgp/geno”,
        },
        parseOnLoad: true,
        usePlainJson: true,
        locale: “en”
        };
        </script>

        <script type=”text/javascript” src=”/dojoroot/release/dojo/dojo/dojo.js”></script>
        <script type=”text/javascript” src=”/dojoroot/release/dojo/dojo/geno.js”></script>

        <script type=”text/javascript”>
        dojo.require(“geno.Base”);
        console.log(“I AM HERE”);
        var a = new geno.Base();
        a.openWidget();
        </script>

        <link rel=”stylesheet” href=”css/home.css” type=”text/css”>
        <link rel=”stylesheet” href=”css/home2.css” type=”text/css”>
        </head>

        <body class=”soria”>
        <span>
        <div id=”topBanner”></div>
        </span>
        <span>
        <div id=”mainWorkArea”></div>
        </span>
        <span>
        <div id=”copyrightNotice”></div>
        </span>
        </body>

        </html>

  15. How to remove ‘.uncompressed.js’ files from the release folder?
    How to remove the files, CSS, templates of unused components from the release folder ?

  16. I replace the dojo1.7.1 with dojo 1.7.2 and build your example.

    the build success, but when run it, there are two errors:

    SCRIPT438: object don’t support “_preloadLocalizations” property or method
    core.js, line 1828 char 1
    SCRIPT438: object don’t support “_preloadLocalizations” property or method
    example.js, line 2533 char 1

    with debug tool of IE9, I found that:

    require(["dojo/i18n"],function(i18n){
    i18n._preloadLocalizations(“mycompany/layers/nls/core”,[]);
    });

    require(["dojo/i18n"],function(i18n){
    i18n._preloadLocalizations(“mycompany/layers/nls/example”,["nl-nl","en-us","da","fi-fi","pt-pt","hu","sk","sl","pl","ca","sv","zh-tw","ar","en-gb","he-il","de-de","ko-kr","ja-jp","nb","ru","es-es","th","cs","it-it","pt-br","fr-fr","el","tr","zh-cn"]);
    });

    how to fix it? Thanks.

  17. Hi,

    For sure there is something I don’t anderstand.
    I set up a test web site on my local server.

    If I run index-dev, there is no problem.

    If tried to use dojo from my local server so y replaced :

    by

    I get the error : Uncaught ReferenceError: require is not defined

    A little explication will be very appreciate.

    Best regards

    Roger

  18. Follow to the precedent post

    If tried to use dojo from my local server so y replaced :

    data-dojo-config=”async: true” src=”http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojo/dojo.js”

    by

    data-dojo-config=”async: true” src=”http://http://testasync/scripts/dojo-release-1.7.2-src/dojo/dojo.js”

    Roger

  19. Sorry, you can delete the two previous messages it was an type fault error in index-dev.html file, both dev and release are working well.

    Thanks for your tutorial.

  20. Do you know if it is possible to omit the rebuilding of files like dojo.js and just build only your layers? The build process takes a few minutes and mostly I am only interested in the layer files.

    • I do not think this was an option in dojo 1.7 but I havn’t look in to the possibilities in dojo 1.8, recently released.

      Another way to speed you build-process is to switch to the node-runner. This will heavily boost the build process to complete in a few seconds. Linux also usually helps.

      Good luck and let me now if you find a suitable way.

  21. Pingback: Trying to understand layers and parsing in dojo 1.7 | Pearl of Civilization

  22. Pingback: Trying to understand layers and parsing in dojo 1.7 | Bookmarks

  23. Hello, I do believe your web site might be having browser
    compatibility issues. When I take a look at your website in Safari,
    it looks fine however when opening in I.E., it’s got some
    overlapping issues. I simply wanted to give you a quick heads up!
    Apart from that, excellent blog!

  24. Hi , it is indeed a good article. I need some help on migrating legacy < 1.7 code base, our code base is so huge and it will take lot of time to convert to AMD format. Is the new build process backward compatible , I mean will it be able to parse the old format of dojo classes?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s