Skip navigation

SUMMARY:

Using goog.require and goog.provide for your own JavaScript packages and modules will not automatically work. You have to use goog.addDependency, and this is best done using a provided python script which will help you create a dependency file.

Update:
Added a HOWTO for using Google Closure’s Template files with your own code.

The difference between dojo.require and goog.require
(skip if you don’t care about Dojo)

Coming from the world of Dojo to the Google Closure Library I thought I could use goog.require and goog.provide the way I am used to with Dojo. Nope, it’s done differently.

The big difference obviously is that with Dojo, dojo.require is magic and happens automatically. Dojo will use an XMLHttpRequest to evaluate your JavaScript on the fly. Google’s system works differently, it creates script tags. Google’s system is better for debugging. Dojo provides some options for also using script tags but because that’s not the intended means of using dojo.require it can lead to problems.

Using script tags makes debugging easier because in debugging tools (such as Firebug, IE’s Developer Tools, or Webkit’s Web Inspector) JavaScript provided with script tags can be located by filename. How evaluated code shows up in debugging tools depends on the tool but it’s never simple or easy and becomes worse the more JavaScript you’ve evaluated.

Another difference is that goog.require doesn’t request your code if it doesn’t already know where to find it. You tell Closure where to find it by using goog.addDependency, an extra step not required in Dojo. The best way to do this is to use a python script included with the framework.

With Dojo you could dump your dojo.require wherever you wanted, similar to use of #include in C source files. You don’t want to do this with the Google Closure Library as the code is added via a script tag and wont be available until that script tag has been added. Instead create a single place where all your goog.require statements are, probably a separate JavaScript file or in a separate script tag in your markup.

There’s also a difference between Dojo’s custom builds and the Google Closure Compiler. Google Closure Compiler is a separate tool but the best means to use this tool if you’re already using the Google Closure Library is to use the provided python script.

Having explained the differences there are also similarities: directory structure is just as important when using goog.require as it is with dojo.require. If your goog.provide is “myapp.subapp.view” you should have a directory structure as you would for dojo (more below).

The three options

Before I explain the means by which you can use goog.require and goog.provide for your own code you need to know about the three options available when you use the calcdeps.py python script provided by the framework:

  • deps – generate a dependance file – If you use this option, when your site is loaded in the browser many script tags are added, one for each file.
  • script – generates a single JavaScript file that includes all of your code and parts of the goog namespace you’re using. You will only have one script tag, but your JavaScript will be otherwise unchanged.
  • compiled – generates a single compiled JavaScript file that includes everything you need. Requires Google Closure Compiler’s compile.jar (available separately). You will only have one script tag of minified JavaScript. Your code will be significantly modified, the degree to which depend on the compiler flags provided.

Most of the details of these three main options (there’s also a list option useful for information purposes only) can be read about on the documentation page for the calcdeps.py script. What the docs are not clear about is how to point the script to your code. I will explain this to save you a period of trial and error and also show an example.

Initial setup

In this set up I keep goog and my Javascript separate. I don’t like including big external libraries in my source control and Google Closure Library is over 100MB! I used to include JavaScript libraries but as they’ve grown in size and complexity I’ve decided to stop doing this, similar to external python libraries. This is handy if you like to create a lot of releases and branches and don’t want to take up hundreds of megabytes of hard drive space on your subversion server for every branch and release.

My directory structure is as such where / is web root (not / on the computer):

/index.html
/media/css/
/media/js/goog/
/media/js/myapp/
/meda/js/release/

/media/js/goog/ is a symlink to what in the Google Closure docs would be described as closure-library-read-only/closure/goog/. This is on my dev machine where I have the Google Closure library. I don’t have the Google Closure Library on the production machine because when I create a build I don’t need it.

/media/js/myapp/ are the checked in files for my JavaScript app. It is the root for my application.

/meda/js/release/ is where my compiled JavaScript files are, I’ll only use this in production.

Let’s say I have a sub application with two files:

/media/js/myapp/subapp/controller.js
/media/js/myapp/subapp/view.js

At the top of the file in in /media/js/myapp/subapp/controller.js I will put:

goog.provide( "myapp.subapp.controller" ); 

and in the other file I’ll put:

goog.provide( "myapp.subapp.view" ); 

Whatever code these files have go below these lines.

Next I create a file that will have all my goog.require statements:

/media/js/myapp/require.js

The contents of this file will be:

goog.require( "goog.events" );
goog.require( "goog.dom" );
goog.require( "myapp.subapp.controller" );
goog.require( "myapp.subapp.view" );

I have added require statements for goog.dom and goog.events because I will be using those parts of Google Closure Library in my own code.

What is in my index.html file varies depending on if I am using it for development or production. In development I’ll have:

<script type="text/javascript" src="/media/js/goog/base.js"></script>
<script type="text/javascript" src="/media/js/myapp/deps.js"></script>
<script type="text/javascript" src="/media/js/myapp/require.js"></script>

Note the deps.js, this is a generated file. In production I’ll simply have:

<script type="text/javascript" src="/media/js/release/subapp-compiled.js"></script>

I create different files for every subapp because each subapp in my architecture exists on a separate page. This might be different for you. This would require a different require.js for every subapp, and during compilation additional compilations for every compiled file.

Using calcdeps.py to create a dependency file

This section explains how to use the calcdeps.py script included in the Google Closure Library. I recommend reading the documentation carefully. Even so, what I describe below, with regard to the difference between creating a dependency file and a compiled file and the paths is not included in the documentation.

IMPORTANT: On your development machine find your way to your /media/js/myapp directory. You will want to be here when you call the calcdeps.py script. If you are in any other directory this will fail because of how the script works. There is a CLOSURE_BASE_PATH variable you can set in your JavaScript that can help with any errors you might run into if you do this differently.

I call the calcdeps.py as such (closure-library-read-only is the path to Google Closure Library on your system):

closure-library-read-only/closure/bin/calcdeps.py -i ./require.js -p .. -o deps > deps.js

This created:

// This file was autogenerated by calcdeps.py
goog.addDependency('../utils.js', [], []);
goog.addDependency('../myapp/deps.js', [], []);
goog.addDependency('../myapp/require.js', [], ['goog.events', 'goog.dom', 'myapp subapp.controller', 'myapp subapp.view']);
goog.addDependency('../myapp/subapp/controller.js', ['myapp.subapp.controller'], []);
goog.addDependency('../myapp/subapp/model.js', ['myapp.subapp.Model'], []);
goog.addDependency('../myapp/subapp/view.js', ['myapp.subapp.view'], []);

Couple of things of note:

Notice utils.js and model.js. I didn’t talk about these files or add them to require.js because I’m not using them but look the script found them anyway. calcdeps.py uses the path given via -p option and traveres all its subdirectories recursively looking for files. It searches the files for goog.require and goog.provide statements and uses those to calculate what goog.addDependency statements it needs to create.

The first argument to goog.addDepedency is the file name, the second is the goog.provide statements found in the file, and the third is the goog.require statements in the file.

The .. path is relative to /media/js/goog/base.js. Very important.

If you now request index.html and look in Firebug’s net tab you’ll see requests for all of the necessary dependencies for goog.events and goog.dom, as well as

/media/js/myapp/deps.js
/media/js/myapp/require.js
/media/js/myapp/subapp/controller.js
/media/js/myapp/subapp/view.js

You wont see:

/media/js/myapp/subapp/model.js
/media/js/utils.js

because you haven’t requested them with goog.require. You can simply add another goog.require statement for /media/js/myapp/subapp/model.js to /media/js/myapp/require.js without calling calcdeps.py again and it will request the file! utils.js doesn’t provide a goog.provide statement so you have to create a separate script tag for it.

Using calcdeps.py to create a compiled file

Remember: You need to download compile.jar separately, it’s not included in the library!

For the purpose of creating a dependency file we used relative paths which created relative paths in your deps.js file. For creating a compiled file we will use absolute paths and we have to add the path to the Google Closure Library in addition to the path to our own code.

I changed my current working directory to /media/js/release and call this in my terminal:

closure-library-read-only/closure/bin/calcdeps.py -i /Users/Bjorn/projects/site/media/js/myapp/require.js -p closure-library-read-only/ -p /Users/Bjorn/projects/site/media/js/myapp/ -c /Users/Bjorn/closure/compiler/compiler.jar -o compiled > subapp-compiled.js

/Users/Bjorn/projects/site is where my website is in my file system.
closure-library-read-only is wherever your copy of the ENTIRE Google Closure Library is on your system, not just the JavaScript.
/Users/Bjorn/closure/compiler/compiler.jar is where I have put the compiler.jar file for the purpose of this example on my file system.

Running this statement creates:

/media/js/release/subapp-compiled.js

This is a JavaScript files that has all the code I need to run subapp. If I later want to include myapp.subapp.model I have to rerun the statement. That’s why it’s better to use a dependency script for dev and compiled file for production. You don’t want to have to compile your code every time you edit your files during development and debugging obfuscated code created by compilation wont be fun. In production however you don’t need to do any of this and having one file reduces page load times for users.

In conclusion
I hope this has helped you figure out how to use goog.require and goog.provide for your own JavaScript code. I recommend also looking at the compilation flag options and reading the rest of the documentation.

About these ads

6 Comments

  1. Hi,

    I try to use calcdeps this way but it fails with “Could not find Closure Library in the specified paths”, if I change the “-p ..” parameter to “-p ../goog” it works but doesn’t include controller.js and view.js in deps.js

  2. Great post, Been playing around with this four many hours, the docs are shamefully lacking on the calcdep page.

  3. Update for new version of calcdeps.py. You now need to use the -d flag. Example:

    python path-to-closure-lib/closure/bin/calcdeps.py -i requirements.js -o deps -d path-to-closure-lib/closure/ -p path-to-your-javascript/ –output_file=path-to-your-release/deps.js

    Which is actually a lot easier than before but you just have to use ESP yo find out about it because its not docummented anywhere.

    Also the calcdeps.py script does not run with Python 3.1 which is awesome also (a few tweaks and you can fix it easily enough).

    This was a painful journey.

    Guido

  4. @daniel garcia

    Where are you running the command from? That part is important. Sorry for the late response.

  5. @Guido Tapia

    Thanks for the update. It has gotten a little better I agree.

  6. Thanks apphackers. Good post. You inspired me :)

    Thanks guido. The -d option works!!

    In code documentation related to this case

    -d –dep Directories or files that should be traversed to find required dependencies for the deps file. Does not generate dependency information for names provided by these files. Only useful in deps mode.

    -p –path The paths that should be traversed to build the dependencies.


One Trackback/Pingback

  1. [...] First make sure you know how to use goog.require and goog.provide for your own code. [...]

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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: