This article was peer reviewed by Adrian Sandu and Mark Brown. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!
Over the past few years, the JavaScript programming language has exploded in popularity. It has become the go-to language for developing both rich web applications, as well as hybrid mobile applications. And as JavaScript projects are becoming increasingly complex, developers are experiencing new requirements of the language. One of these is modularity.
As far as I can see, there are two aspects in which modularity has to be achieved:
- Modules that we author
- External modules that are installed as dependencies
ES6 brings a standard module syntax to JavaScript and a loader specification. This is a good step forward, however at the time of writing, there are no browsers that can natively load ES6 modules. This means that if you want to use modules today, you’ll need to use a module bundler.
For an overview of the current landscape see: Understanding JavaScript Modules: Bundling & Transpiling
And what’s more, we don’t have a package manager that allows us to download a package and include it in our application. Package managers (such as Bower and npm) help us download front-end dependencies without having to visit a projects’ website, but that’s as far as it goes.
In this article, we will see how jspm and SystemJS can be used to overcome these problems.
What are jspm and SystemJS?
The JavaScript Package Manager (aka jspm) is a package manager that works on top of the SystemJS universal module loader. It is not an entirely new package manager with its own set of rules, rather it works on top of existing package sources. Out of the box, it works with GitHub and npm. As most of the Bower based packages are based on GitHub, we can install the those packages using jspm as well. It has a registry that lists most of the commonly used front-end packages for easier installation. Like npm, it can be used to differentiate the packages as development and production packages during installation.
SystemJS is a module loader that can import modules at run time in any of the popular formats used today (CommonJS, UMD, AMD, ES6). It is built on top of the ES6 module loader polyfill and is smart enough to detect the format being used and handle it appropriately. SystemJS can also transpile ES6 code (with Babel or Traceur) or other languages such as TypeScript and CoffeeScript using plugins. You configure these things within System.config({ ... })
before importing your module.
jspm uses SystemJS to manage packages and their dependencies, so we need not worry about putting the packages in the right order to load them.
Now that we know what jspm and SystemJS are, let’s see how to use them.
Setting up Our Environment
If you haven’t already, you’ll need to have Node.js installed. A particularly easy way to do this is to use a version manager (such as nvm) and is detailed in this quick tip. Once you’re up and running with Node, you can install jspm globally by running the following from the command line:
npm install -g jspm
Now we can use the jspm command line interface. Let’s set up a project:
mkdir new-project && cd new-project
npm init -y
npm install jspm --save-dev
This creates a directory named new-project
, initializes an npm project and installs jspm locally. This is the recommended way of doing things, as it locks down the jspm version for a project and ensures upgrades to the global jspm will not alter the behavior of your application.
Another advantage of this approach is that if your project is deployed through a continuous integration build, you can configure the build process to use the local jspm package instead of having to install jspm globally on the server as well.
You can use jspm -v
to confirm the local version.
$ jspm -v
0.16.34
Running against local jspm install.
To use jspm in a project, run the following command:
jspm init
You will be prompted for a number of settings, hit enter to accept the default values or type a different value to change them. The following screenshot shows an instance when the command is executed with default settings:
Directory Structure and Configuration
This creates a file named config.js
in the root of the project, as well as a folder named jspm_packages
. If you take a peek inside the jspm_packages
folder you’ll see:
- A
github
sub-directory - An
npm
sub-directory - The main files for the SystemJS module loader
The reason that jspm is creating github
and npm
directories is that it piggybacks on top of these registries, simply aliasing the npm or GitHub package requested. Additionally, the github
directory contains a SystemJS plugin to load JSON files from GitHub and the npm
directory contains the Babel-related packages we will need to transpile our ES6 code
The config.js
file is mainly for SystemJS. If you open it, you will see that it has configuration options set for the paths of the packages from different sources, options for Babel and name maps for the packages to make it easy to refer them. This file gets updated automatically updated when a new package is installed using jspm.
The command also updates the package.json file and adds a section for jspm. The Babel packages installed with the init
command are added to devDependencies
section of the project.
"jspm": {
"devDependencies": {
"babel": "npm:babel-core@^5.8.24",
"babel-runtime": "npm:babel-runtime@^5.8.24",
"core-js": "npm:core-js@^1.1.4"
}
}
Any new package installed using the jspm command with or without the --save
option will get added to thedependencies
section and so it becomes a production dependency. Installing with the --save-dev
option makes the dependency a development time dependency and saves it to the devDependencies
section.
Finally, jspm adds an entry to the map
section of the config.js
file for every package installed using the jspm
command. The mapped name can be used to load the library in any JavaScript file throughout your project. Any package dependencies are added to the map section as well. Following is a fragment from map section of the config.js
file that shows how packages of jquery and jquery-ui are mapped and how the dependency is defined:
"jquery": "npm:jquery@2.2.0",
"jquery-ui": "github:components/jqueryui@1.11.4",
"github:components/jqueryui@1.11.4": {
"jquery": "npm:jquery@2.2.0"
}
As a point of interest, you can also generate these mappings automatically when you install a module:
jspm install jq=jquery
This would allow you to write the following elsewhere in your app:
var $ = require('jq');
jspm and SystemJS in Action
To accompany this article, I have made a simple Wikipedia search sample. You can find the GitHub repo here. It is setup using the default options of the jspm init
command and consequently it uses Babel as ES6 transpiler. It also uses the libraries jQuery and Bootstrap which are installed using jspm. As bootstrap needs jQuery, jspm creates a mapping in the config.js
file to load jQuery before loading bootstrap:
"github:twbs/bootstrap@3.3.6": {
"jquery": "github:components/jquery@2.2.0"
}
As the name of the repo suggests, it queries the Wikipedia search API and displays the data received in the view. It uses jQuery’s $.ajax()
to make a call to the API, displays the results on a page and has a link to show a snippet of the article in a modal popup. The sample has three JavaScript files under the scripts
folder:
search.js
: This file uses an AMD style module. It as loads jQuery as a dependency and makes an Ajax call to the Wikipedia search API. The module returns a function that any other consuming module can call with a parameter.summaryModal.js
: This file uses an ES6 style module. It loads Bootstrap’s JavaScript library. It then exports a function that accepts a piece of escaped HTML and unescapes the HTML text before displaying it in a modal popup.display.js
: This file uses a CommonJS style module. It loads jQuery and both of the above files as dependencies. It calls the method exposed bysearch.js
to get results and then renders this data in an HTML table on the UI. Additionally, it makes use of the method exposed bysummaryModal.js
to show a modal on click of a link button on the table.
As already mentioned, SystemJS understands all module systems in JavaScript. We can load all three of the above files using SystemJS.
To make these scripts run, we need to load system.js
and config.js
on the HTML page. After that we will load the display.js
file using the SystemJS module loader. As this file refers to other files in the repo and these files load the libraries needed, we don’t need to load any other file.
<script src="jspm_packages/system.js"></script>
<script src="config.js"></script>
<script>
System.import("scripts/display.js");
</script>
However, transpiling on the fly is bad for performance and shouldn’t be used in production applications. Don’t forget that jspm is a module bundler, so let’s bundle it.
Bundling
We can create a bundle for the entire application using the following command:
jspm bundle scripts/display.js build.js
This command does the following:
- Combines all files and the libraries in a single file named
build.js
- Converts the module systems used by the files to SystemJS style modules
- Creates a source map file that is loaded during debugging
Now we need to load the build.js
file into index.html
file. Here is the updated set of references:
<script src="jspm_packages/system.js"></script>
<script src="config.js"></script>
<script src="build.js"></script>
<script>
System.import("scripts/display.js");
</script>
Please note that we didn’t remove the import statement to load the module display.js
. This statement doesn’t load a file anymore, instead it loads a SystemJS module that is already available in the build.js
file.
You can follow instructions in the project’s README file to run it. You can browse the code in the individual files in the developer tools and debug through them.
Conclusion
The combination of jspm and SystemJS provides a unified way of installing and loading dependencies. This tool not only makes the job of managing dependencies easier but also opens up the door to use the future module system on today’s browsers. As I have demonstrated, jspm is easy to setup and use, two reasons which are contributing to its rising popularity.
Are you using jspm in your projects? Why? Why not? Let me know in the comments below.
Frequently Asked Questions (FAQs) about Modular JavaScript, SystemJS, and JSPM
What are the key differences between SystemJS and Webpack?
SystemJS and Webpack are both module bundlers, but they have some key differences. SystemJS is a universal dynamic module loader that loads ES6 modules, AMD, CommonJS, and global scripts in the browser and NodeJS. It’s more of a module loader than a bundler. On the other hand, Webpack is a static module bundler for modern JavaScript applications. It builds a dependency graph that includes every module your application needs and packages them into one or more bundles.
How can I install SystemJS?
SystemJS can be installed via npm. You can use the following command to install it: npm install systemjs
. After installation, you can import it into your project using import { System } from 'systemjs'
.
What is the role of JSPM in modular JavaScript?
JSPM (JavaScript Package Manager) is a package manager for SystemJS. It simplifies the process of installing and managing packages. It also provides a unified way of installing packages from npm, GitHub, and other sources. JSPM can also handle transpilation, allowing you to write code in ES6 or TypeScript and have it automatically transpiled to ES5.
How can I use SystemJS to load modules?
To load modules using SystemJS, you can use the System.import()
method. This method returns a promise that resolves to the module. Here’s an example: System.import('path/to/module.js').then(function(module) { /* use module */ })
.
Can I use SystemJS with Node.js?
Yes, SystemJS can be used with Node.js. It provides a universal module loader that works in both the browser and Node.js. This allows you to write code that can be shared between the client and the server.
How does SystemJS handle dependencies?
SystemJS handles dependencies through its configuration. You can specify the paths and dependencies of your modules in the SystemJS configuration. When a module is loaded, SystemJS will automatically load its dependencies as well.
What are the benefits of using modular JavaScript?
Modular JavaScript provides several benefits. It makes your code more organized and easier to manage. It allows you to reuse code across different parts of your application. It also makes it easier to test your code, as each module can be tested independently.
How can I configure SystemJS?
SystemJS can be configured using the System.config()
method. This method accepts an object that specifies the configuration options. Some of the options you can configure include paths, map, packages, and transpiler.
Can I use SystemJS with other JavaScript frameworks?
Yes, SystemJS can be used with other JavaScript frameworks such as Angular, React, and Vue.js. It provides a universal module loader that can load modules from any of these frameworks.
What is the future of SystemJS?
SystemJS continues to be actively maintained and developed. It is a key part of the JavaScript ecosystem, especially for projects that need a universal module loader. With the ongoing development of JavaScript and web technologies, SystemJS is likely to continue evolving to meet the needs of developers.
Rabi Kiran (a.k.a. Ravi Kiran) is a developer working on Microsoft Technologies at Hyderabad. These days, he is spending his time on JavaScript frameworks like Angular JS, latest updates to JavaScript in ES6 and ES7, Web Components, Node.js and also on several Microsoft technologies including ASP.NET 5, SignalR and C#. He is an active blogger, an author at SitePoint and at DotNetCurry. He is rewarded with Microsoft MVP (ASP.NET/IIS) and DZone MVB awards for his contribution to the community.