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.


Comments
gravatar Christopher Wednesday, February 18, 2015
Thank you for this. I've been recently trying to determine what set of JavaScript tooling to use, and after having explored Grunt, Gulp, Bower, requirejs, Browserify, etc, I've been left a little dismayed and overwhelmed. Which to choose? Which requires the least amount of ceremony and friction? Which have the most community support? I really like Gulp over Grunt. I've been using it with Browserify, which, while I find so much better than requirejs, has left me not too excited about the server-side build/loader process. jspm seems like the way to go (especially with ES6 syntax). I'm grateful that you post clear and straightforward explanations with practical examples of the technologies you cover. I really am.
gravatar Scott Wednesday, February 18, 2015
@Christopher: Thanks! The one downside is that the VS 2015 editor still doesn't like ES6 syntax, you'll get lots of red squiggles. It's handy to adopt a second editor. On the plus side, at least the project is file based, now, so adding scripts to the project from a 2nd editor just works.
gravatar Quinntyne Wednesday, February 18, 2015
OdetoAureila? Interesting!
Nat Wednesday, February 18, 2015
@Quinntyne: Yes, I noticed the name-drop in the solution as well. Aurelia looks like an elegant and simple SPA framework with enormous potential. I must say, I'm very pleased to see Scott focusing on Aurelia - even at this early stage. If he is half as capable with this framework as he has been with Angular, then we can look forward to some great forthcoming content!
Excited Mike Thursday, February 19, 2015
I would love to read a series of short posts on Aurelia like you have done for ES6 & Angular. Keep up the great work.
gravatar jdan Thursday, February 19, 2015
Nice. I didn't get any of the errors when I didn't have main.js created yet (and I had to manually create the app folder, maybe I missed that), but walked through this easily. Thanks.
gravatar Glen Monday, February 23, 2015
"If there is enough interest, we can look at building and bundling in a future post." I'm interested! :-) (I would prefer the ES6 to be transpiled on the server-side)
gravatar Imran Baloch Tuesday, February 24, 2015
AFAIK, web.config will be used when we host in IIS/IIS-Express, so system.webserver will work. Microsoft.AspNet.StaticFiles will be only required when you host other than IIS :)
gravatar Rick Strahl Sunday, March 8, 2015
Great post, Scott. What does the JavaScript debugging experience look like for ES6 code that's been transpiled? Do you see the ES6 code or are do you end up debugging the transpiled code (I assume the latter unless maps handle this somehow)?
gravatar Dave Ward Sunday, March 8, 2015
@Rick: The source maps work pretty well. In Chrome and using Babel as the transpiler at least, you see both the originally authored scripts and an !eval.js version of any scripts that have been transpiled. You can even set/hit breakpoints in your authored ES6 code and mostly ignore that the transpiled version is there at all during development.
gravatar scott Sunday, March 8, 2015
@Rick - yes, what Dave said :)
Tuesday, March 10, 2015
really good article.
gravatar lee Wednesday, March 11, 2015
Any idea on how to get jspm to work for angular-new-router?
gravatar scott Wednesday, March 11, 2015
@lee - You should be able to "jspm install npm:angular-new-router", and you'll have what you need in the jspm_packages folder (\jspm_packages\npm\angular-new-router@0.4.0\dist). Import the router module, or serve the .es5 directly. Does that help?
gravatar lee Wednesday, March 11, 2015
Thanks for the reply. When I did that, it said entry point not found, override package or something to that effect. there was also a .jspm.errors file created with unexpected token on lines3:36, 3:53:3:54 in .jspm/packages/npm/angular-new-router@0.4.0/docs/traceur-package/templates/data-module.template.js:
gravatar scott Friday, March 13, 2015
@lee - Hmm, I've tried a few times and haven't seen that particular error. I'm just not sure what could make that happen.
gravatar Colin Thursday, March 19, 2015
Hey Scott - I'm doing some real dev in Aurelia - and I've found that I can't hit breakpoints when debugging from VS in IE (or any browser for that matter). My Gulp is producing map files when transpiling from TypeScript to js, and I can debug using the map files in Chrome. However, VS says that "no symbols have been loaded" when I set breakpoints in my TypeScript files in VS. I suspect this is because of the ES6 module loader - but maybe it's just something I'm doing wrong. Any ideas?
gravatar Scott Sunday, March 22, 2015
@Colin: I haven't seen that behavior. I occasionally see odd things going on in Chrome, but breakpoints seem ok as long as I open the original source code files and not the files generated by Babel/Traceur.
gravatar Cory House Wednesday, March 25, 2015
Great post, Scott! I really like jspm, though I do have a few concerns: 1. No search. Instead, it appears we simply view the list of packages here: https://github.com/jspm/registry/blob/master/registry.json. Apparently there's an API available, so the issue for missing search was closed: https://github.com/jspm/jspm-cli/issues/3 2. Closely related to #1, search isn't necessary because there are currently only 289 packages. Bower has over 23,000. So currently choosing jspm likely means adding some of your favorites to the jspm registry. That's a nice thing to do for the community, but in the short-term it burns up the time you were supposed to save over Bower. 3. I'm unclear how people are choosing between an npm endpoint and a GitHub endpoint when adding a new package to the registry. The registry currently contains mostly GitHub endpoints, which makes sense to me because a node package is just an abstraction layer over git URL. So if your package is on GitHub, the GitHub endpoint is a logical choice. But why is React in the registry using a npm endpoint instead of https://github.com/facebook/react? To me, the npm endpoint should only be used when the project isn't on Github. I assume I'm missing something simple here. 4. I haven't found a way to specify a specific version. This instantly bit me when I wanted the 1.x jQuery branch.
gravatar Scott Thursday, March 26, 2015
@Cory - there are rough edges, to be sure, and I've encountered one library (Benchmark.js) that just doesn't want to work in the new environment because it it using a global variable (it could well be user error, too). No search, it's true. When I went looking to add Q to a project I had to do some digging and decide what/where/how to load it. If a module isn't in the registry I tend to favor npm because there is a little more structure and consistency there. 2. Yes, still cutting edge :) 3. I don't think the docs are very clear, but you can use "jspm install jQuery=1.9" and you'll get 1.9.1.
gravatar Cory House Friday, March 27, 2015
Thanks Scott! I like what I see in jspm. It smells like the future, though I have to admit the wins at the moment aren't huge over Browserify. Bottom line: Would you use it in prod yet?
gravatar Scott Friday, March 27, 2015
@Cory - yes, it is working in development and the key pieces for the production runtime is really the es6 model loader that jspm sets everything up to use, and the module loader is in good shape. jspm is just the development tool, so not quite so risky.
Comments are closed.

My Pluralsight Courses

K.Scott Allen OdeToCode by K. Scott Allen
What JavaScript Developers Should Know About ECMAScript 2015
The Podcast!