Jam Documentation

Installation

Before getting Jam, make sure you install Node.js.

Jam can then be installed using NPM (Node Package Manager), which comes installed with Node.js.

npm install -g jamjs

Using packages

Installing

Jam installs packages to ./jam by default (this can be easily configured in you project's package.json). This directory contains your installed packages and a specially configured require.js you can use to load them in the browser.

$ jam install jquery

This will download the latest version of jQuery from the package repository and install it. You can list installed packages using jam ls.

$ jam ls
* jquery 1.7.2

Jam will also fetch any dependencies for the target package.

$ jam install backbone
installing from repositories backbone
Building version tree...
repositories checking "backbone"
repositories checking "underscore"
downloading http://packages.jamjs.org/backbone/backbone-0.9.2.tar.gz
downloading http://packages.jamjs.org/underscore/underscore-1.3.3.tar.gz
extracting /home/caolan/.jam/cache/backbone/0.9.2/backbone-0.9.2.tar.gz
extracting /home/caolan/.jam/cache/underscore/1.3.3/underscore-1.3.3.tar.gz
installing underscore@1.3.3
installing backbone@0.9.2
updating jam/jam.json
updating jam/require.config.js
updating jam/require.js
OK

When using jam ls, packages that are in your jam.dependencies property in package.json (direct dependencies) are prefixed with an asterisk, other packages (indirect dependencies) are not. Packages that are not directly depended on may be marked as unused if your project's dependencies change.

$ jam ls
  underscore 1.3.3
* jquery 1.7.2
* backbone 0.9.2

Loading

To use the packages you've installed, include the specially configured require.js script in the page. All your other dependencies can then be loaded using the require function.

<script src="jam/require.js"></script>

<script>
  require(['jquery'], function ($) {
    $(document.body).text('Hello, world!');
  });
</script>

You can load any number of modules in this way, without polluting the global namespace. Jam can also bundle the immediately useful modules into a single file for production use, and you can load less frequently used code in the background when it's needed. All of this can be done without changing the above code.

The first argument to require is an array of module names to load, these are then passed to the callback containing the code you want to run. For more information, see the RequireJS docs.

Original or alternative require.js

<script src="http://requirejs.org/docs/release/2.0.1/minified/require.js"></script>
<script src="jam/require.config.js"></script>

If you want to use a non-configured version of RequireJS (perhaps from a CDN, or existing app) you can use jam/require.config.js to set up the correct paths for the installed jam packages. However, you probably want to avoid the extra request and just include the jam/require.js file instead.

Creating a project dependency list

If you've used Node.js and NPM, you may be familiar with keeping a list of dependencies in a project-level package.json file. You can also put a list of Jam dependencies in this file, which allows you to lock to specific versions of dependencies.

{
  "name": "myproject",
  "version": "0.0.1",
  "description": "An example Node.js project",
  "dependencies": {
    // NPM dependencies go here...
    "async": "0.1.22"
  },
  "jam": {
    "dependencies": {
      // Jam dependencies go here...
      "jquery": "1.7.1",
      "underscore": null
    }
  }
}

Of course, it doesn't have to be a Node.js project for you to add a package.json file with your Jam packages!

Custom install paths

If you'd prefer to install JavaScript dependencies to a location other than ./jam, you can set the jam.packageDir property in package.json. You can also use jam.baseUrl to tell Jam which directory to make relative package paths from. For example:

{
  "jam": {
    "packageDir": "public/vendor",
    "baseUrl": "public",
    "dependencies": {
      ...
    }
  }
}

This will install packages to public/vendor and set up the package paths so you can include public/vendor/require.js from a file at public/*. Remember, these are file paths relative to package.json, not URLs.

Upgrading

When a new version of a library is released it can be a pain to keep your projects up to date. With Jam, when packages are updated you can upgrade to all the latest versions with just one step.

$ jam upgrade

Running this command will check the package repositories for any updates and install any upgraded packages. You can also specify individual packages to upgrade:

$ jam upgrade jquery

Version locking

Of course, automatically upgrading to the latest version isn't always a good idea. If an installed package depends on a specific version of a library, then the correct version will always be retained. Dependencies with specific version requirements in your project's package.json will also not be upgraded. To upgrade these, you should modify the acceptable versions in package.json first. If you don't care which version is installed, you can set the version to just null.

Packages tied to a specific version also show up when doing jam ls

$ jam ls
  underscore 1.3.3
* jquery 1.6.2 [locked 1.6.2]
* backbone 0.9.2

Removing

To remove a package:

$ jam remove jquery

If you remove a package that has dependencies installed, and those dependencies are not used by any other packages, you can remove them using jam clean. This will prompt you to remove any packages not included in your package.json (and not a dependency of one of those packages). If you decide you need to keep a package from this list, you should add the package to your package.json to make sure it is not removed by mistake.

$ jam clean
Building version tree...

The following directories are not required by packages in
package.json and will be REMOVED:
    jam/underscore

Continue [Y/n]:

Production

Compilation

For best performance in production, you should always compile modules into a single file. It's much quicker than waiting for require.js to parse then have it make the requests asynchronously. For complex applications, which use modules only in specific circumstances or on a few pages, you may want to compile a base set of modules that are needed immediately, then load the additional modules in the background only when they are needed.

Jam offers an easy to use command which combines and minifies your installed packages, and can even include code from your own project.

$ jam compile output.js

This will combine all the packages you have installed into a single file, including the configured require.js library. You can include this file in place of require.js and get all the benefits of the optimized download without having to change any JavaScript code in your application. In fact, you may find it useful to replace jam/require.js with the output of the compile command, so you don't even have to change the script tag which includes it. By doing this you get the benefits without having to change anything in your application.

$ jam compile jam/require.js

Of course, the next time you jam install or otherwise modify the package list, the jam/require.js file will be rewritten and will not include the compiled modules from before. You would have to re-run the compilation command. Because of this, it makes sense to add this step to your build system. Add it to a Makefile or script you normally use to create production versions of your code, preferably creating a new copy of the whole project (eg myapp/dist) and running the jam compile step in the new directory.

Including specific modules

You may not want to include all the installed packages in the compiled output, or you may want to include some modules from your project that are not being managed by Jam. Thankfully, you can specify individual modules to include in the compiled output.

$ jam compile -i myapp/base -i jquery -o output.js

You can use package names or paths to AMD modules from your own application. The output will include the specified modules and their dependencies. Any modules not included will still be accessible using require.js, they'll just be loaded asynchronously as you need them.

Almond

If you know you won't be loading additional resources in the background you can add --almond to the compile command to have it use the almond.js shim to provide the require and define functions. This saves a little off the download size, which may be significant on mobile devices.

Best practice

Make it easy to find dependencies

If you're specifying individual modules to include in jam's compiled output, you can make it easier to manage by using a single, top-level module for your application. This module should include all the other modules and top-level dependencies you need. Then, when doing jam compile you need only specify this file and Jam will find all the dependencies from there.

myapp/app.js
define('myapp/app', [
  'jquery',
  'myapp/foo'
],
function ($, foo) {

  return {
    init: function () {
      // add you app initialization code here
      // ...
    }
  };

});
index.html
<script src="jam/require.js"></script>
<script>
  require(['myapp/app'], function (app) {
    app.init();
  });
</script>

Then, to include all the immediately required files into a single download, simply do:

$ jam compile -i myapp/app -o output.js

By doing it this way, you can add that command to your build script and not have to update it when your app's dependencies or module structure changes. Instead, you just manage compiled dependencies in myapp/app.js.

Load large, infrequently used resources on demand

In the majority of cases, you'll want to just include your modules in a single compiled download. Occasionally, however, you may have large resources which are only needed in very specific circumstances. In this situation, it can be useful to load the resources in the background only once they are needed.

The following examples build on the files from the previous section: "Make it easy to find dependencies".

index.html
<button id="testbtn">Test</button> 

<script src="jam/require.js"></script>
<script>
  require(['myapp/app'], function (app) {
    app.init();
  });
</script>
myapp/app.js
define('myapp/app', [
  'jquery',
  'myapp/foo'
],
function ($, foo) {

  return {
    init: function () {
      $('#testbtn').click(function () {

        // by making a require call here, we can load this
        // asynchronously, only once we've clicked the button!

        require(['myapp/bar'], function (bar) {
          alert(bar.message);
        });

      });
    }
  };

});
myapp/bar.js
define('myapp/bar', [], function () {
  return {message: 'Hello, world!'};
});

Now, if you were to compile according to the previous section, including only myapp/app:

$ jam compile -i myapp/app -o jam/require.js

Then open index.html in the browser, you should notice that on page load only require.js is fetched (since it has myapp/foo and jquery compiled). But, when you click the "Test" button the myapp/bar module is fetched in the background.

Developing packages

Package.json

The package.json file inside each package directory provides Jam with important meta-data, including description, version information and required dependencies. It is loosely based on the Package Descriptor File format described in the Packages 1.0 specification available as part of CommonJS. Often, you'll find that these files are compatible with the package.json used for publishing to NPM.

Short example

{
  "name": "example",
  "version": "0.0.1",
  "description": "An example Jam package",
  "jam": {
    "dependencies": {
      "jquery": ">1.4.2",
      "underscore": ">1.3.1"
    },
  }
}

These are the essential properties every package.json must have, though you might omit the dependencies field if the package has no dependencies. Ideally, you should provide as much information about the package as possible.

Full example

{
  "name": "example",
  "version": "0.0.1",
  "description": "An example Jam package",
  "homepage": "http://example.com",
  "keywords": [
    "package",
    "example"
  ],
  "jam": {
    "dependencies": {
      "jquery": ">1.4.2",
      "underscore": ">1.3.1"
    },
    "main": "example.js",
    "shim": {
      "deps": ["jquery"],
      "exports": "Example"
    },
    "include": [
      "example.js",
      "README"
    ]
  },
  "maintainers": [
    {
      "name": "Bill Smith",
      "email": "bills@example.com",
      "web": "http://www.example.com" 
    } 
  ],
  "contributors": [
    {
      "name": "Mary Brown",
      "email": "maryb@example.com",
      "web": "http://www.example.com" 
    } 
  ],
  "bugs": {
    "mail": "dev@example.com",
    "web": "http://www.example.com/bugs" 
  },
  "licenses": [
    {
      "type": "MIT",
      "url": "http://www.opensource.org/licenses/mit-license.php",
    }
  ],
  "repositories": [
    {
      "type": "git", 
      "url": "http://github.com/example/example.git"
    }
  ],
  "github": "http://github.com/example/example"
}

Required fields

name The name of the package. Ideally, this should be all lowercase, and if necessary, use a "-" as a separator between words (eg, "example-package").
version A version string conforming to the Semantic Versioning requirements (http://semver.org). See the section on versioning for more information.
description A brief description of the package.

Additional fields

homepage URL string for the package website.
keywords An Array of string keywords to assist users searching for the package.
jam
main When requiring a package by name, eg jquery, RequireJS will attempt to load jam/jquery/main.js. If you would like to specify an alternate file to load, use this field to specify the relative path that should be used, eg "jquery-1.7.2.js" would load jam/jquery/jquery-1.7.2.js.
include An array of file paths to include in the jam package. This is the reverse of using a .jamignore file, and if set, will exclude any files not explicitly included in this list.
{"browser": {"include": ["browser-build.js", "README"]}}
dependencies Hash of prerequisite packages on which this package depends. Unlike the CommonJS Package Descriptor File format, you must specify a version, a version range, or null for each dependency. Examples: ">1.4.2", ">=1.5.0 && <1.6.0", "2.7.x", null. If you specify null, that means any version will do. Avoid being too specific, remember that the person using your package may be using a different version of a library, and would not want to run multiple versions. Use it to avoid versions you know will not work rather than specify the exact version you test most often with.
shim Allows you to configure the dependencies and exports for older, traditional "browser globals" scripts that do not use define() to declare the dependencies and set a module value. See the RequireJS docs.
maintainers Array of maintainers of the package. Each maintainer is a hash which must have a "name" property and may optionally provide "email" and "web" properties.
contributors Array of hashes each containing the details of a contributor. Format is the same as for maintainers.
bugs URL for submitting bugs. Can be mailto or http.
licenses Array of licenses under which the package is provided. Each license is a hash with a "type" property specifying the type of license and a url property linking to the actual text. If the license is one of the official open source licenses the official license name or its abbreviation may be added with the "type" property. If an abbreviation is provided (in parentheses), the abbreviation must be used.
repositories Array of repositories where the package can be located. Each repository is a hash with properties for the "type" and "url" location of the repository to clone/checkout the package. A "path" property may also be specified to locate the package in the repository if it does not reside at the root.
github The appropriate GitHub page for the package. This mean the package details page on the jam website will display a link to GitHub and a watchers count in search results. This can sometimes be inferred from the repositories property.

Linking packages

During development, you may want to use your package in a project while continuing to work on it. Using jam link you can create a symlink to the package directory and add it to your paths in jam/require.js.

$ jam link /path/to/dev-package

Remember that your symlink locations are unlikely to be useful to anyone else that checks your code out. This should only be used during development until it makes sense to install a copy in your project.

To stop Jam from trying to upgrade linked packages you should add "linked" as the version in your project's package.json for the package you are linking.

Versioning

Jam uses semantic versioning (http://semver.org). Loosely speaking, this translates to MAJOR.MINOR.PATCH, for example 1.2.3.

If you are directly publishing a library from it's source repository, without modifications, then you can simply publish it with the version number of the library itself (provided it conforms to the semantic versioning requirements). If you make any modifications to the upstream library to prepare it for publishing to the Jam repositories, you should add a Jam version number. For example 1.2.3-jam.1, 1.2.3-jam.2. This allows you to update the Jam-specific code without changing the upstream version number.

Publishing

To publish a package so others can install it, you must first sign-up for an account on this site. Once you have an account, just do jam publish inside your package directory to pack and upload it. Then repeat the publish command every time you want to release a new version. If you make a mistake and want to publish over an existing version, use the -f or --force flag. Be warned, this is not recommended, since it will not prompt people using the previously published copy to update.

If you would like to remove a package from the Jam repositories, use jam unpublish PACKAGE, where PACKAGE is the name of the package you'd like to remove. You can specify a single version to remove by doing jam unpublish PACKAGE@VERSION, eg jam unpublish example@1.2.3.

Categories

To make your package easy to find, you can use a number of pre-defined categories in your package.json file. You can see the list of available packages on the packages page. The categories must be typed exactly as shown, and are case-sensitive.

{
  "categories": ["Frameworks", "DOM"],
  ...
}

Getting help

Mailing list

http://groups.google.com/group/jamjs

IRC

#jamjs on FreeNode

GitHub

https://github.com/caolan/jam

Twitter

@caolan and @groundcomputing