Your First Angular 2, ASP.NET Core Project in Visual Studio Code – Part 5

Angular2, ASP.NET Core, Visual Studio Code, Part 5

Derived from photo by Markus Spiske / raumrot.com, CC-BY

This is part five in a series of posts teaching you how to create your first Angular 2 application with ASP.NET Core in Visual Studio Code. Here is the list of posts:

This post is all about automating common development tasks with npm scripts and Visual Studio Code. You learn how to install and clean dependencies, launch browsers, automatically rebuild your .NET code, and setup a build task in Visual Studio Code.

Mac and Linux Users: The commands in this article related to cleaning dependencies and launching browsers are specific to Windows. You can still use npm scripts on Mac and Linux by replacing the Windows commands with those for your environment. Details for specific commands are found readily through your favorite search engine.

.NET Core SDK Preview 3: This tutorial uses .NET Core SDK Preview 3 which is available on GitHub. This version includes 'alpha' support for MSBuild-based projects that use .csproj configuration instead of project.json configuration. Microsoft announced that MSBuild is the common project configuration for .NET Core based projects going forward. By using MSBuild, the goal is to better prepare you for future versions of the tooling.

If you would like to jump straight to the code, it's all on GitHub.

Note: If you have been following along with the series from the beginning, you might have noticed that this post was previously listed as being about Browser Link. Unfortunately, this feature still appears to be heavily tied to the full Visual Studio IDE. The tooling story for ASP.NET Core and Visual Studio Code continues to develop and you can expect more posts on these topics as additional tooling becomes available.

NPM Scripts

For this walkthrough, you are continuing from the npm script you built in part 3. While Visual Studio Code supports a variety of automation tooling, npm scripts are a discoverable and simple way to automate tasks for your project.

The scripts are all located in the package.json file under the "scripts" property. At this point in the walkthrough, this is the current version:

"scripts" : {
    "start": "tsc && concurrently \"tsc -w\" \"dotnet run\" "
},

This script compiles the TypeScript files and then runs the TypeScript compiler in watch mode alongside running the ASP.NET application. You invoke it by entering npm start in the terminal.

Notably, npm has built-in commands as well as custom scripts. For instance, start is a built-in command. You invoke it by entering npm start. However, if you take the same script and call it gyro, it would not be recognized by entering npm gyro because it is not a built-in command. You instead enter npm run gyro into the command line to invoke that script.

It's important to understand that the built-in commands sometimes have special behavior. You can see the full list supported by your version of npm by entering npm -h in the terminal and you can read more about these commands in the documentation.

Installing Dependencies

Part 3 of this series describes how to setup a package.json file and shows how to use npm install to download the dependencies from npm. In part 2, it similarly describes using dotnet restore to do the same thing for .NET Core dependencies. Instead of using these two commands, what if you only had to remember one to install all the dependencies in your project?

The npm install command is one of the npm commands with special behavior. You configure an npm script named install but the commands get executed after the default npm install command has finished. The is the perfect place to call dotnet restore. Add the following to the scripts property:

"scripts" : {
    // ... 
    "install": "dotnet restore"
},

Now when you enter npm install in the terminal, the dotnet dependencies install as well.

Cleaning the Project

In addition to installing dependencies, you should be able to easily delete out-of-date dependencies. Essentially, you need a set of scripts to delete the downloaded packages and build artifacts from the project directory. These commands work well on Windows for this purpose:

"scripts" : {
    // ... 
    "clean": "npm run clean:dotnet && npm run clean:npm",
    "clean:dotnet": "npm run clean:bin && npm run clean:obj",
    "clean:bin": "del /q .\\bin\\*.* && for /d %i in (.\\bin\\*.*) do @rmdir /s /q \"%i\" ",
    "clean:obj": "del /q .\\obj\\*.* && for /d %i in (.\\obj\\*.*) do @rmdir /s /q \"%i\" ",
    "clean:npm": "del /q .\\node_modules\\*.* && for /d %i in (.\\node_modules\\*.*) do @rmdir /s /q \"%i\" "
},

Notice the clean script is composed of multiple npm scripts. When calling multiple commands, concatenate them with two ampersands (&&). The bulk of the work is done with the clean:bin, clean:obj, and clean:npm scripts which delete the contents of the bin, obj, and node_modules folders respectively. Because these commands delete only the contents, they can be called repeatedly without throwing an error.

Also notice that calling these scripts requires the run command. For instance, to call the clean script you type npm run clean in the terminal.

With these scripts, whenever you get an updated version of the repository, type npm run clean follow by npm install in the terminal and you have a fresh copy of the required npm and .NET dependencies. You might even decide to create a new script that calls them both.

Launching the Browser

Another important development task is opening the browser, sometimes multiple browsers, to test your application. This too, is an automation candidate. Here are three commands for launching different browsers to the application URL:

"scripts" : {
    // ... 
    "edge": "start microsoft-edge:http://localhost:5000",
    "chrome": "start chrome http://localhost:5000",
    "firefox": "start firefox http://localhost:5000"
},

Once added, type npm run chrome in the terminal to startup Google Chrome at the development URL.

Watching ASP.NET Code Changes

If you look at the start script, tsc -w initializes the file watcher for the TypeScript compiler. This command watches the file system for any TypeScript file changes and automatically re-runs the TypeScript build. To duplicate this functionality for C# code, use the dotnet watch command.

The dotnet watch command is a dotnet CLI extension. You reference the tool by putting the following in the project's .csproj file:

<ItemGroup>
    <DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="1.0.0-msbuild2-final"/>
</ItemGroup>

You can now use your new npm install script to restore the .NET dependency or simply run dotnet restore to download the package. Once it's installed, modify the build script to use dotnet watch run instead of dotnet run. This is the full scripts property in the package.json file including the dotnet watch change:

"scripts": {
    "start": "tsc && concurrently \"tsc -w\" \"dotnet watch run\" ",
    "install": "dotnet restore",
    "clean": "npm run clean:dotnet && npm run clean:npm",
    "clean:dotnet": "npm run clean:bin && npm run clean:obj",
    "clean:bin": "del /q .\\bin\\*.* && for /d %i in (.\\bin\\*.*) do @rmdir /s /q \"%i\" ",
    "clean:obj": "del /q .\\obj\\*.* && for /d %i in (.\\obj\\*.*) do @rmdir /s /q \"%i\" ",
    "clean:npm": "del /q .\\node_modules\\*.* && for /d %i in (.\\node_modules\\*.*) do @rmdir /s /q \"%i\" ",
    "edge": "start microsoft-edge:http://localhost:5000",
    "chrome": "start chrome http://localhost:5000",
    "firefox": "start firefox http://localhost:5000"
},

Now you can make changes to your C# code while the application is running and when you save the file, the .NET project rebuilds without any developer intervention.

Key Binding for NPM Start

While npm scripts are already easily accessible through the built-in terminal, Visual Studio Code offers a way to map a build task to a keyboard shortcut.

The default build task key binding is Ctrl+Shift+B and it is configurable. To map the npm start command to this task, begin by pressing Ctrl+Shift+B from anywhere in Visual Studio Code. At this point, Visual Studio Code sees that you are using npm scripts and prompts you to create a tasks.json file within the .vscode directory. Press the Configure Task Runner button to create the file.

Configure Task Runner

Now that the file is created, modify the tasks property to map the npm start script. In addition, set the task's isBuildCommand property to true to associate the task with the build command key binding, Ctrl+Shift+B.

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "0.1.0",
    "command": "npm",
    "isShellCommand": true,
    "showOutput": "always",
    "suppressTaskName": true,
    "tasks": [
        {
            "isBuildCommand": true,
            "taskName": "start",
            "args": ["start"],
            "isWatching": true            
        }
    ]
}

Save the file and try out the new functionality by pressing Ctrl+Shift+B. You should see the application starting in the terminal. To terminate the task, press F1 and find the command to Terminate Running Task.

Terminate Running Task

You can create additional tasks out of the other npm scripts but Visual Studio Code limits you to having one task running at a time. Furthermore because this build task runs in watch mode, it may be running most of the time you are in the editor. Therefore, it makes sense to continue running your other npm scripts from the terminal.

Build Succeeded

You are now equipped to zip through your development workflow using npm scripts and Visual Studio Code tasks. If you're coming from the Visual Studio IDE, this gives you an idea of how to accomplish some of what the full-fledged IDE provides and hopefully further inspires you to customize the tasks that Visual Studio Code enables.

Now that you have gotten a glimpse at what is possible, what other automated functionality would you like to see? How have you configured Visual Studio Code to help you be more productive? Let everyone know in the comments.

Get Your TypeScript Definitions from NPM

Get Your TypeScript Definitions from npm

Derived from photo by raumrot.com, CC-BY

If you use TypeScript, you appreciate what the type system provides with IntelliSense and easier refactoring. This experience is quite seemless with code you have written. However, using third-party libraries has left many struggling with complex configurations.

With the advent of ECMAScript 2015 (ES2015) modules and the rise of npm as the defacto JavaScript package manager, configuring your type definitions has gotten more complicated. Not only did you have to ensure that your import statements resolved to the correct scripts but now also the correct type definitions.

Thankfully, the community is standardizing third-party type resolution similar to how it is standardizing on ES2015 modules and npm.

Once Upon a Time

Back in the early days of TypeScript, you used to reference type definitions at the top of the .ts file like this:

/// <reference path="../lib/my-favorite-library.d.ts" />

Also you probably got the type definitions from a dedicated registry somewhere like DefinitelyTyped and maybe even used command line tools to setup and configure these type definitions like tsd or typings.

There were shortcuts and optimizations but ultimately it was a whole other set of configuration and package management to get third party libraries compiling with TypeScript.

Relative Path Module Resolution

While you can still write your comment-based type references, the industry has adopted the ES2015 module standard and so has TypeScript.

By adopting this standard, each JavaScript (or TypeScript) file essentially represents a module with its own scope. Any dependencies that this module requires are imported directly from another module by the module's URL. In the future, browsers will understand how to parse this syntax and load each module as needed from the server. Today, TypeScript transpiles these import statements into a configurable module loader syntax such as AMD, UMD, or CommonJS.

Take for example the fact that your GrooveService takes a dependency on your FunkService. Because you wrote your FunkService in TypeScript, both the scripts and the types are found at the same location.

import { FunkService } from './funk-service';

On the other hand, what if you are using angular in Angular 1.x? Initially, you might think to reference it by relative path:

import * as angular from './node_modules/angular/index.js';

This makes sense because this location is where the scripts reside and the browser will load the correct library. However, TypeScript complains that it can't find angular. Well, it turns out the angular library doesn't include TypeScript definitions in the package. TypeScript tried to find the type definitions at the relative path, but they weren't there and you got the dreaded red squiggle..

Angular IntelliSense Squiggle

Traditionally, this is where tsd or typings came into the picture. These repositories contained packaged type defintions for many popular libraries independent of the library provider. And while these repositories solved a real problem in obtaining type definitions, they weren't easy to setup and configure. After all, who wants to maintain two sets of packages managed by two different tools with two different configurations just for the ability to reference one library using TypeScript?

There's another problem with taking a relative path reference to an npm module, what if your code is distributed as an npm package? If you have spent time reading up on npm dependency resolution, you know that you can't guarantee the location of your dependencies in npm. They may be dropped at different levels of the folder structure and therefore attempts to reference npm modules by relative path are fragile.

Node Module Resolution

In order to solve the npm module resolution problem, the folks at TypeScript decided to just follow the npm model. This solution makes it seamless to reference both the code and the types using the same syntax used in NodeJS.

import * as angular from 'angular';

Given this import statement, NodeJS understands that angular is equivalent to /node_modules/**/angular/index.js. It doesn't know exactly which directory it will be in but it knows how to find it.

As of TypeScript 1.6, the compiler understands how to look for type definitions in the same fashion. TypeScript looks for something roughly equivalent to /node_modules/**/angular/index.d.ts. This is great because it ensures that the code and the types can be found in the same npm package.

But what if the code and the types aren't in the same package? For instance, the Angular 2 libraries ship with type definitions included in the package but Angular 1.x does not. This introduces another great solution from the community. Instead of using tsd or typings for your third-party type defintions, now you can just use npm.

The @types organization is available on the public npm registry for the purpose of hosting third-party type definitions that don't ship with their repective package. So if there is a set of types available independent of the package's publisher, by convention you can find them on npm using @types/package-name. For example to download types for angular, you get them by installing the @types/angular npm package. Furthermore, TypeScript understands how to resolve types from the @types directory out of the box.

To ensure node module resolution for your project, set it in your tsconfig.json file:

{
    "compilerOptions": {        
        "moduleResolution": "node"
    }
}

Note: To read more about how TypeScript handles node module resolution, there are more details in the TypeScript documentation.

Bringing Node Module Resolution to the Browser

This all sounds great – less tooling dependencies, easier configuration. NodeJS runs everywhere – desktop clients, web servers, Linux, MacOS, Windows. But unfortunately, when the web browser tries to load scripts from angular you get a 404 result. The browser doesn't understand how to convert the npm package name into the URL for which the script resides.

This is the place where the node module abstraction breaks down. But think about it for a second. Yes, NodeJS doesn't run in the browser. Yes, you aren't necessarily writing a NodeJS backend. But what NodeJS has done is unify the runtime on which web developers run their client-side tooling. This is the environment on which you do your TypeScript compilation, combining modules, minifying – basically optimizing your code to be consumed by the browser. And so it still makes sense that npm would be the best solution for module resolution of both the scripts and the types.

There are a ton of different ways to prepare your application's npm modules for delivery to the browser and you can bet that there will be more coming in the future. For now, SystemJS is a good place to start. Many deveopers already use this library as a script loader and by adding some additional configuration, it's possible to map npm-based dependencies to a relative URL.

Without any additional configuration, the browser is going to see the import statment for angular and attempt to request /angular from the server. What should really happen is that the browser requests the code from the actual package folder like node_modules/angular/index.js. So using your SystemJS configuration file, add the following:

System.config({        
    packages: {
        '': {
            defaultExtension: 'js',
            map: {
                'angular': './node_modules/angular/index.js'
            }
        }
    }
});

This code configures the root of your application (at the path '') to always add a .js extension if one is not present and to also map the 'angular' path. Now when SystemJS sees the import statement for 'angular', it maps that to a request for node_modules/angular/index.js as defined in the configuration.

Automatic Typings Acquisition

If you've made it this far, you understand how this simplifies the process of acquiring and referencing type definitions. It's become simple enough in fact that the Visual Studio Code team is working to make it automatic in the editor. As of this writing, the feature is released but the team admits it still has rough edges. Even still, you can read more about it in the release notes and start trying it by downloading version 1.7.2 or later.

Keep in mind, the tooling may have drawbacks even when it is complete and it's helpful to know what the tooling is doing underneath the covers. Also, this tooling doesn't address the need for mapping npm package names to URLs in the browser like discussed with SystemJS.

The End

The point here is that node module resolution has become the defacto standard in the web development community and TypeScript is no exception. Now that @types is available on npm, you have one place to get your third-party libraries and type definitions – npm.

The tricky party is still configuring node module resolution for the browser but you see how it's possible to setup SystemJS to handle the mapping. Look for more tooling discussion in future posts.

Are you using ES2015 modules and node module resolution with TypeScript? What is your preferred method of mapping the module names to URLs for the browser?

Getting Started with npm in Visual Studio

npm and Visual Studio Header
Derived from photo by Markus Spiske / raumrot.com, CC-BY

The defacto package manager for JavaScript frameworks and tooling has become npm (node package manager). If you are a Visual Studio developer using Nuget through the years, this may be news to you. Likely, though, you understand there is a much bigger web development world outside of ASP.NET and Visual Studio – and this world uses npm. So why shouldn't you?

Go ahead, continue to get your .NET libraries from Nuget, but get your web frameworks from npm. This post teaches you the npm basics from a Visual Studio perspective. And while the command line is still currently the best place to use npm, there are some nice tricks to learn in Visual Studio as well.

This post assumes you are using Visual Studio 2015. Furthermore, web developers should install Mads Kristensen's prolific Web Extension Pack to get the most current web tooling for Visual Studio. If you don't see some of the described features below in your own installation, it's most likely because you don't have these tools installed.

Good Old Command Line

As much as Visual Studio developers love having a UI for their tools, npm is still most easily used at the command line. There are GUI tools such as Web Essentials Package Installer, but you may find these tools too simple to install packages the way you want.

Beyond installing packages, there are other advantages to using the command line. First, any new npm features debut in the CLI (command line interface) version of the tool so you can more easily take advantage of productivity enhancements. Second, your CLI skills are portable to other web development platforms, IDEs (integreated development environments), or text editors.

One extension in particular, Open Command Line, is a must for any command line work in Visual Studio. It is included in Web Extension Pack or as an individual download here. You can also get these extensions directly from within Visual Studio in the Extensions and Updates Manager. Open Command Line enables you to open the command line (Windows Command Prompt or PowerShell) from anywhere in Visual Studio with keyboard shortcuts or from a right-click in Solution Explorer.

Open Command Line Extension

Even better, when you use these shortcuts, the command line initializes to the directory from which you called the extension. So if you are writing code in C:\git\billion-dollar-idea\FlamingTomatoes\Web\index.html and decide you need a new npm package, press AltSpace and you get this:

Command Prompt

Sweet!!

The npm Command Line Basics

So you know how to get to the command line quickly from Visual Studio, now what? First, install NodeJS on your machine. Being that you are using this for development purposes, go head and install the current version instead of the LTS version.

Once installed, npm is available at the command line. Navigate to the directory of your project either manually or with the Open Command Line tool. This is the most basic installation of the Angular 1.x library:

npm install angular

This command makes a request to the public npm registry and downloads the latest version of the Angular package and installs it at the current directory in a folder called node_modules. Furthermore, npm also downloads any dependencies for Angular. You can read more about how npm structures the dependencies here.

The previous example installed the package to a local node_modules folder within the current directory. Some packages, such as those operating as command line tools, require global installation. These packages are not stored in a local node_modules folder but in a centralized location (e.g. C:\Users\<you>\AppData\Roaming\npm). Install packages globally using the -g parameter:

npm install typescript -g

What if you want a specific version of a package? When you want a specific version, append the version to the end of the package name. This installs Angular version 1.4.14:

npm install angular@1.4.14

The npm documentation has a great topic listing the various ways to specify package versions during installation.

Create a package.json File

Ideally, you want to keep a record of which packages you have installed in your project. This record is kept in a file called package.json. This file stores metadata for your application including a listing of packages that can be restored at a later time.

One import reason to keep this listing is source control. It's not ideal to store the contents of every package in source control. By storing the package.json file in source control, you don't have to keep the packages themselves in source control and each individual developer can restore these packages from the npm registry. If you are familiar with how Nuget uses packages.config, the concept is similar.

Visual Studio provides a template for creating a new package.json file making this process familiar to Visual Studio users. Right-click on your web project and select Add -> New File to display the Add New Item dialog. Then under the Web section, select the option for npm Configuration File.

Add New package.json

The contents of the file is incredibly minimal to the point where you may see the npm CLI show warnings. For your purposes of simply obtaining and recording npm packages, this package.json confriguration is sufficient and these warnings are unimportant.

{
    "version": "1.0.0",
    "name": "asp.net",
    "private": true,
    "devDependencies": {
    }
}

Of course, you can create the package.json file from the command line as well. Use the command:

npm init -f

Using the -f parameter creates the package.json file with default values that you can later edit. Otherwise, the init command prompts for a value for each field. The resulting file looks like this:

{
    "name": "folder",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "dependencies": {
    }
}

For the purposes of obtaining and using npm packages, the section you are most concerned about in package.json is "dependencies". When time to publish your project, make sure to learn more about the information listed in the package.json file.

Note: If you know that you do not want your project published online, consider setting "private": true. This setting helps to protect your project from accidentally being published because the npm registry refuses to publish projects with this flag enabled.

Install Packages to package.json

The npm tool allows you to save the packages you install to the package.json file by using parameters at the command line. For instance, to save Angular to your package.json file, use:

npm install angular -S

Using the parameter -S, npm saves the package in your existing package.json file and serializes the package listing in the "dependencies" configuration property.

{
    "dependencies": {
        "angular": "^1.5.8"
    }
}

Note: The caret ^ before the version number indicates that when npm attempts to re-install this package, it downloads this version or a later version compatible with this version. Read more about semantic versioning with npm.

Not all packages in npm are used for the same purpose. Some of the packages are frameworks used in the appliation, like Angular. Some of the packages are used during development like compilers and linters. These frameworks constitute developer tooling rather than application frameworks. npm makes this distinction in the package.json file by listing development dependencies in the "devDependencies" section.

{
    "devDependencies": {
        "typescript": "^2.0.3"
    }
}

Most of your needs are met using "dependencies" and "devDependencies". However, npm also has "peerDependencies" and "optionalDependencies" to register packages with your application. Find out more in the package.json documentation.

Also in Visual Studio, you have the option to type these packages directly in your package.json file with full IntelliSense support:

npm package.json IntelliSense support in Visual Studio

Restore Packages from package.json

As long as you have all of the packages listed in your package.json file, you can safely delete and restore your node_modules folder at any time. In fact, you probably should after installing a new dependency. Because npm resolves dependencies based on the order in which packages are installed, the only way to ensure that dependencies are installed in a consistent manner across machines is to install them from the same package.json file.

To install/restore packages, use the install command by itself at the directory containing an existing package.json file.

npm install

Alternatively, Visual Studio has a handy shortcut in Solution Explorer. Right-click on a package.json file and select the option to Restore Packages:

Visual Studio package.json Restore Packages

Looking Forward

In this tooling tour, you have seen how to install npm packages in various ways using the command line and using Visual Studio. This is still early days. Expect to see more tooling options from Visual Studio in the future.

Do you use npm packages in Visual Studio? What are your favorite tricks for working with them? Please leave a comment and let everyone know.