Next-generation module loading in all browsers today.
- Loads ES6 modules, AMD, CommonJS and global scripts detecting the format automatically.
- Optionally load modules directly from GitHub or npm with jspm registry and CDN support out of the box.
- Uses RequireJS-inspired configuration options including baseURL, map, shim and custom paths.
- Built on top of the standards-compliant ES6 Module Loader polyfill.
- ~25KB total size minified, ~15KB for loader.js and ~11KB for es6-module-loader.js.
For the loader documentation and getting started guide, read below.
For a basic introduction and explanation with examples, see https://jspm.io.
- Including the Loader
- Setting the Configuration
- Loading Application Code from the baseURL
- Loading External Packages from Endpoints
- Map Configuration
- Loading External Packages from the jspm Registry
- Installing Packages Locally
- Package Configuration
- Cache Busting
- Plugins
The loader can be included from the CDN or locally. The CDN version can be useful for quick experimentation and is also suitable for production use.
Include the following script in the page:
<script src="https://jspm.io/loader.js"></script>
Download es6-module-loader.js
and traceur.js
from the ES6-loader polyfill and locate them in the same folder as loader.js
from this repo.
Then include loader.js
with a script tag:
<script src="loader.js"></script>
es6-module-loader.js
will then be included automatically and the Traceur parser is dynamically included from traceur.js
when loading an ES6 module only.
Typically one would include a configuration file immediately after the loader script, although this can be done inline as well.
<script src="config.js"></script>
config.js:
jspm.config({
baseURL: '/lib'
});
The baseURL
sets the folder where local scripts will be loaded. By default it is set to the HTML page URL.
Modules are dependency-managed JavaScript files. They are loaded by a module name reference.
Each module name directly corresponds to a JavaScript file URL, but without the .js
extension, and with baseURL rules.
For example, modules can be loaded from the baseURL with the syntax ~
:
~/my/module -> [baseURL]/my/module.js
In the index.html
page we can then load a module from the baseURL folder with:
<script>
// loads [baseURL]/test.js returning the defined module object.
jspm.import('~/test', function(test) {
console.log(test);
});
</script>
Since we set the baseURL to /lib
, we can write:
lib/test.js:
define(function() {
return {
isAMD: 'yup'
};
});
Note that when running locally, ensure you are running from a local server or a browser with local XHR requests enabled. If not you will get an error message.
For Chrome on Mac, you can run it with:
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --allow-file-access-from-files &> /dev/null &
In Firefox this requires navigating to
about:config
, enteringsecurity.fileuri.strict_origin_policy
in the filter box and toggling the option to false.
It is recommended to write modular code in either AMD or CommonJS. Both are equally supported by jspm, with the format detected automatically.
For example, we can write modular CommonJS:
/lib/app.js:
var subModule = require('./submodule/submodule');
//...
subModule.someMethod();
//...
/lib/submodule/submodule.js:
exports.someMethod = function() {
}
and load this with jspm.import('~/app')
in the page.
Note: always use relative requires of the form
./
or../
to reference modules in the same package. This is important within any package for modularity.
jspm provides an AMD compatibility layer, with the goal of supporting as much of the RequireJS test suite as possible to ensure functioning of existing AMD code.
To create the requirejs
and require
globals as AMD globals, simply include the following <script>
tag immediately after the inclusion of the jspm loader:
<script>
require = requirejs = jspm.require;
</script>
This should replicate a fair amount of RequireJS functionality, and support is improving over time.
Note that AMD-style plugins are not supported.
jspm is an ES6 module loader. It will detect and load ES6 modules, parsing them with Traceur dynamically. This allows for dynamic loading of ES6 without a build step, although a build step still needs to be run to transpile ES6 back to ES5 and AMD for production.
The CDN and CLI provide support for converting ES6 modules into ES5 with AMD. Simply add the following to the package.json:
{
"buildConfig": {
"traceur": true,
"uglify": true
}
}
Then either run a CLI build with jspm build
, or upload the package to the CDN to have the build done there.
The CLI build configuration is explained further at the project page, there is also a sample repo demonstrating the ES6 build here.
Read more about ES6 modules and module loaders at the ES6 Module Loader polyfill repo.
Global scripts can also be loaded with jspm, and any globals they write to the window
object will be converted into a special separate module object for the global.
This provides careful protection against global collisions, allowing global scripts to write to the same global variables without conflict.
Most global scripts will still need some shim configuration as in RequireJS to set their dependencies.
Endpoints provide collections of packages that can be downloaded or served over CDN.
Currently the following endpoints are supported:
- GitHub:
jspm.import('github:name/repo@version')
- npm:
jspm.import('npm:repo@version')
- cdnJS:
jspm.import('cdnjs:name/version')
Use of these endpoints is entirely optional, and custom endpoints can easily be created or overrided in the loader.
GitHub and npm both have endpoint services provided by jspm, allowing easy access to their modules.
The following will work without any installation necessary:
// load the latest stable version of underscore from npm
jspm.import('npm:lodash-node/modern/objects/isEqual', function($) {
});
// load the latest stable version of Bootstrap, from the 'js' folder in the Bootstrap release on GitHub
jspm.import('github:twbs/bootstrap/js/bootstrap', function(bootstrap) {
});
// load the latest minor version of jquery from GitHub
jspm.import('github:components/[email protected]/jquery', function($) {
});
In the above examples, the following module names are converted directly into the following URLs:
npm:lodash-node/modern/objects/isEqual -> https://npm.jspm.io/lodash-node/modern/objects/isEqual.js github:twbs/bootstrap/js/bootstrap -> https://github.jspm.io/twbs/bootstrap/js/bootstrap.js github:components/[email protected]/jquery -> https://github.jspm.io/components/[email protected]/jquery.js
The endpoint servers then provide the correct modular code from the package and version specified.
The CDN runs over SPDY so that all requests can travel over a single connection, and SPDY push is used to send module dependencies so that a new round trip is not needed everytime a dependency of a module is discovered.
To create a custom endpoint, use the configuration:
jspm.config({
endpoints: {
myendpoint: 'https://some-url.com'
}
});
// now jspm.import('myendpoint:hello') -> https://some-url.com/hello.js
Endpoints are independent CDNs. Other examples of services that would be appropriate for endpoints include Bitbucket, Google Code, etc. The NodeJS server running the npm and GitHub CDNs will be open sourced when ready to allow anyone to host a custom service endpoint, as well as a mirror of the GitHub or npm endpoints. For more information about endpoints, read the wiki here.
Instead of writing github:components/[email protected]
, typically one would write jquery
in all local application code.
We then map jquery -> github:components/[email protected]
with global map config for our application.
jspm.config({
baseURL: '/lib',
map: {
jquery: 'github:components/[email protected]'
}
});
This means that any import/require for jquery
in any module will now get the exact jQuery version we want.
This is far better than specifying the full endpoint directly as it makes it very easy to update jQuery to a new version or endpoint. This is the most important configuration option for dependency-managed packages.
It can be difficult to know exactly what the most maintained endpoint is for a specific package and remember this endpoint each time.
Instead of needing to know github:twbs/bootstrap
is the maintained package endpoint for Bootstrap, the jspm registry provides a simple service to remember the main endpoint for a package.
We simply write:
jspm.import('bootstrap');
And if no other map configuration applies, the loader will convert this into the URL:
bootstrap -> https://registry.jspm.io/bootstrap.js
This module simply forwards to the correct location at github:twbs/bootstrap
.
Use of the jspm registry is also entirely optional. The registryURL
can also be changed to be a local folder or another URL with the configuration:
jspm.config({
registryURL: '/public-libs'
});
The registry is still growing, and a list of the modules currently in the registry can be found here. Additions can be made with pull requests, or requested as issues.
The jspm registry, GitHub and npm endpoints use simple version conventions to provide version-managed packages.
When no version is specified, the latest stable version is used.
Otherwise an @
sign can be used to indicate a version.
Examples:
Latest Stable Version
jspm.import('jquery');
jspm.import('npm:underscore');
Latest Revision of Minor Version
jspm.import('[email protected]');
jspm.import('[email protected]');
Exact Version
This supports both version names and semvers:
jspm.import('[email protected]');
jspm.import('[email protected]');
Generally it is advised to write import/requires without any version or endpoint specified, and then use the package map configuration to set the endpoint and minor version.
This provides the most flexibility.
For example, in config.js:
jspm.config({
baseURL: '/lib',
map: {
'jquery': '[email protected]',
'bootstrap': 'github:twbs/[email protected]',
'underscore': 'npm:[email protected]'
}
}
});
While the CDN endpoints make it very convenient to create and load modules without the friction of installation, it is easy to switch to installing packages locally at any time.
With the jspm CLI, any endpoint package or registry package can be directly installed:
jspm install jquery bootstrap underscore
The CLI will then update the config.js
configuration file, as well as the package.json
and all packages will then be loaded locally from the same server.
To get started with the CLI, follow the guide at the jspm CLI page.
All other configuration in jspm is package-specific to ensure complete configuration modularity.
Package configuration is injected automatically from the package.json file by both the CDN and the jspm CLI when installing packages locally.
This configuration allows for easy usage of external packages without having to manually set up complex configuration.
The package-specific configuration options set the main entry point, module format, map and shim.
For example, here is the package configuration that is injected when downloading Bootstrap:
jspm.config({
packages: {
'github:twbs/[email protected]': {
main: 'js/bootstrap',
format: 'global',
shim: {
'js/bootstrap': ['jquery']
}
}
}
});
Map configuration can also be set at the package level, allowing for dependency version support and avoiding name conflicts in dependencies.
Read more about configuring external packages for jspm here..
This sets the main entry point for the package. When the package is requested directly by name, eg:
jspm.import('bootstrap')
this will then load js/bootstrap.js
, as if we had written:
jspm.import('bootstrap/js/bootstrap')
jspm automatically detects the module format of files using regular expression detection techniques, but this comes with processing cost and perhaps the detected format may not be the format intended.
Setting the format
property for a package will disable any detection and ensure that the module files are treated accordingly.
The options for format are:
es6
amd
cjs
global
Map configuration set within a package will only apply to dependencies of that package.
This is useful when two packages may refer to the same module name, but expect different underlying versions or implementations.
Shim configuration allows dependencies for global scripts to be enforced, just like RequireJS.
In the example,
jspm.import('bootstrap/js/bootstrap')
will load Bootstrap only after loading jQuery.
More advanced shim configuration can also be set, allowing the exporter to be defined as well:
jspm.config({
packages: {
'github:components/[email protected]': {
shim: {
'lib/jquery': {
exports: 'jQuery'
}
}
},
'github:some/[email protected]': {
shim: {
imports: ['./jquery'],
exports: 'jQuery.somePlugin'
}
}
});
When developing locally, you may want to automatically cache bust the local URLs.
For this, set the urlArgs
configuration option:
jspm.config({
urlArgs: '?bust=' + new Date().getTime()
});
jspm.import('./test'); // requests [baseURL]/test.js?bust=1383745775497
Supported Plugins:
- CSS
jspm.import('my/file.css!')
- Image
jspm.import('some/image.png!image')
- JSON
jspm.import('some/data.json!')
- Text
jspm.import('some/text.txt!text')
- WebFont
jspm.import('#google Port Lligat Slab, Droid Sans !font')
The name of the plugin is by default the file extension before the !
, or the name can be provided after with !pluginName
when the plugin name is not the same as the resource extension name.
By default, plugins are loaded from the jspm registry, as the exact plugin name.
To submit a plugin, create a pull request on the registry page.
Custom transpiler plugins allow for loading templates or languages that compile to JavaScript.
These are different from RequireJS in that they are extension-based plugin names:
jspm.import('some/module.coffee!')
Where some/module.coffee
is a CoffeeScript file.
The plugin name can also be specified if not identical to the extension:
jspm.import('some/module.cs!coffee');
The plugin itself is loaded from the resource name [pluginname]
. This name itself can be mapped with standard configuration:
jspm.config({
map: {
'coffee': 'my/custom/coffee/plugin',
}
});
Otherwise, plugins are loaded from the registry.
[baseURL]/my/custom/coffee/plugin.js:
var CoffeeScript = require('./coffee-script');
module.exports = function(name, url, fetch, callback, errback) {
fetch(url, function(source) {
callback(CoffeeScript.compile(source));
}, errback);
}
Note the plugin here is written as CommonJS for an example only - any module format will work.
MIT