OdeToCode IC Logo

Using jspm with Visual Studio 2015 and ASP.NET 5

Wednesday, February 18, 2015

If you’ve been following along with the developments for ASP.NET vNext and Visual Studio 2015, you’ve probably seen the JavaScript tooling of choice for File –> New projects is Grunt and Bower.

But, what if you wanted to use more sophisticated and productive tools, like Gulp and jspm?

In this post, we’ll get setup using jspm instead of Bower, and write some ES6 code with CTP5 of Visual Studio 2015.

jspm

In a nutshell: jspm combines package management with module loading infrastructure and transpilers to provide a magical experience. You can write code using today’s JavaScript, or tomorrow’s JavaScript (ES6), and use any type of module system you like (ES6, AMD, or CommonJS). jspm figures everything out. By integrating package management with a smart script loader, jspm means less work for us.

1. Get Started

In VS2015 we’ll start with a minimum set of features by using File –> New Project, selecting “ASP.NET Web Application” and using the “ASP.NET 5 Empty” template. With ASP vNext, the resulting project looks like the following.

ASP.NET 5 Empty Project

Notice the wwwroot folder, which is new for ASP5. The wwwroot folder is the default folder for static assets like CSS and JS files, and is literally the root of the web site. We can create a new default.html file in wwwroot as the entry page for the application.

<html>
<head>
    <meta charset="utf-8" />
    <title>Working with jspm</title>
</head>
<body>
    <div id="output">Testing</div>
</body>
</html>

Pressing Ctrl+F5 should run the application as always, but we’ll need to add /default.html to the URL as the web server won’t find a default file without some extra configuration (see ‘Making default.html the default’ later in this post).

image

2. Get jspm

Once you have both NodeJs and a command line Git client installed, jspm is simple to setup.

npm install –g jspm
jspm init

You’ll want to run jspm init from the root of the project, which is one level above the wwwroot folder.

The init command will ask a series of questions to setup a project.json file (yes, the same project.json that npm uses, unlike Bower which creates it’s own json file). During the questioning you can choose the ES6 transpiler to use. As you can see below I prefer the 6to5 transpiler over Traceur these days, but note that 6to5 was just renamed to Babel last week.

Here’s how to answer:

Package.json file does not exist, create it? [yes]:
Would you like jspm to prefix the jspm package.json properties under jspm? [yes]:
Enter server baseURL (public folder path) [./]: ./wwwroot
Enter project code folder [wwwroot\]:
Enter jspm packages folder [wwwroot\jspm_packages]:
Enter config file path [wwwroot\config.js]:
Configuration file wwwroot\config.js doesn't exist, create it? [yes]:
Enter client baseURL (public folder URL) [/]:
Which ES6 transpiler would you like to use, Traceur or 6to5? [traceur]: 6to5
ok   Verified package.json at package.json
     Verified config file at wwwroot\config.js
     Looking up loader files...
       system.js
       system.src.js
       system.js.map
       es6-module-loader.js
       es6-module-loader.src.js
       es6-module-loader.js.map
       6to5.js
       6to5-runtime.js
       6to5-polyfill.js

     Using loader versions:
       es6-module-loader@0.13.1
       systemjs@0.13.2
       6to5@3.5.3
ok   Loader files downloaded successfully

The most important answer is the answer to the public folder path (./wwwroot). 

We want jspm to work with a package.json file at the root of the project, but store downloaded packages and configuration in the wwwroot folder. This is one way to work with jspm, but certainly not the only way. If there is enough interest, we can look at building and bundling in a future post.

3. Write Some Script

Next, we’ll update the default.html file to bring in System.js, the dynamic module loader installed by jspm, and the config file created by jspm, which tells the loader where to make requests for specific module and libraries.

Inside the body tag, the markup looks like:

<div id="output">Testing</div>

<script src="jspm_packages/system.js"></script>
<script src="config.js"></script>
<script>System.import("app/main");</script>

If we refresh the browser we should see some errors in the console that app/main.js couldn’t be loaded. This is  the System.import we requested in the above markup, and the syntax should be somewhat familiar to anyone who has used AMD or CommonJS modules before. The file can’t be loaded, because it doesn’t exist, so let’s create a main.js file in the app folder under wwwroot.

var element = document.getElementById("output");
element.innerText = "Hello, from main.js!";

Simple code, but a refresh of the browser should tell us everything is working.

systemjs running the app

Let’s make the code more interesting by adding a greeting.js file to the app folder in wwwroot.

export default element => {
    element.innerText = "Hello from the greeting module!";
};

Now we can change main.js to make use of the new greeting component (which is just an ES6 arrow function).

import greeter from "./greeting";

greeter(document.getElementById("output"));

The import / export syntax we are looking at is the new ES6 module syntax, which I haven’t covered in my series of ES6 posts, as yet, but we’ll get there. With the magic of System.js and friends, all this code, including the ES6 modules and arrow functions – it all just works.

running with es6 modules

4. Install Some Packages

The beauty of jspm is that we can now swing out to the command line to install new packages, like moment.js

> jspm install moment
     Updating registry cache...
     Looking up github:moment/moment
     Downloading github:moment/moment@2.9.0
ok   Installed moment as github:moment/moment@^2.9.0 (2.9.0)
ok   Install tree has no forks.

ok   Install complete.

 

This is just as easy as installing a package with Bower, but with jspm the package is ready to use immediately. Let’s change greeting.js to use moment:

import moment from "moment";

export default element => {

    let pleasantry = "Hello!";
    let timeLeft = moment().startOf("hour").fromNow();

    element.innerText = `${pleasantry} The hour started ${timeLeft}`;
};

And now the application looks like the following.

running with jspm and moment.js

5. Make default.html the default

In ASP.NET 5 there is no web.config, but modifying the behavior for static files is still fairly easy (not nearly as easy, but easy). Step one is to install the NuGet package for static file processing – Microsoft.AspNet.StaticFiles, then adding the following code to Startup.cs.

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseFileServer(new FileServerOptions
        {
             EnableDefaultFiles = true,
             EnableDirectoryBrowsing = true,                   
        });            
    }
}

It’s really just the EnableDefaultFiles that will make our page appear at the root of the web site, because the static file handler will go looking for a default.html file on such a request.

6. Caveats

While everything shown here works well, the biggest hurdle to using ECMAScript 6 with Visual Studio 2015 is the editor, which doesn’t like ES6 syntax. You’ll want to keep another editor handy to avoid all the red squiggles under the import and export keywords, for example. We’ll have to hope ES6 syntax support is ready to go by RTM, because it is time to start using the new JavaScript.