Node.js, npm, gulp & Co for VS2015

Rainer Stropek | time cockpit

rainer@timecockpit.com | Blog | GitHub

Agenda (German)

Für Webentwicklung auf der Microsoft-Plattform gibt es im Moment sehr viel Neues zu lernen. Nicht nur, dass ASP.NET 5 und die CoreCLR einige grundlegende Änderungen bringen, die gesamte Toolsammlung für Webentwicklung ist nicht mehr wiederzuerkennen. Microsoft wendet sich verstärkt bestehenden plattformunabhängigen Tools aus der Open-Source-Welt zu und integriert sie in die kommende Visual-Studio-Version. Das Ergebnis ist eine Vielzahl an neuen Werkzeugen, die auf den ersten Blick verwirrend wirkt. In dieser Session bringt Rainer Stropek Ordnung ins Chaos, indem er drei der Tools vorstellt, die für Visual-Studio-Webentwicklung neu sind: Node.js, gulp und npm.

What's the problem?

Client-side web development

Ancient past: All logic on server, client just HTML and very simple scripts

Past: Most logic on server, some view-logic on client
E.g. bundling done by ASP.NET

Today: Single Page Apps (SPA), complex logic on the client
E.g. TypeScript

Necessity for build process for client-side web development

Tasks

Why not MSBuild and NuGet?

  • Existing, well-established ecosystem of tools in the web-dev space
    Based on Node.js
  • Need for cross-platform
  • NuGet is not the primary target for JavaScript framework developers

Node.js

What is Node.js?

  • Program for running JavaScript outside of the browser
  • Based on Google's V8 engine (Chakra support is on its way)
    Lot of ECMAScript 2015 already built in
  • Server-side JavaScript
  • Stand-alone JavaScript programs
    Ideal for command-line tools as it is cross-platform
  • Optimized for building scalable network programs
    Async nature makes it so scalable

What is NPM?

Node's Package Manager
Similar to NuGet for C#

Very wide-spread
Compare NuGet and NPM download numbers

Tip: Do not take libraries for client-side web development from NuGet!

Demo: Node.js Console App

Tip: Try Node.js Tools for Visual Studio 2015

npm install chalk
JavaScript:


var chalk = require('chalk');
var i = 5, j = 2;
console.log(chalk.red("result is: " + chalk.white.bgBlue(i / j)));
                    

TypeScript:


import * as chalk from 'chalk';
var i : number = 5;
var j : number = 2;
console.log(chalk.red("result is: " + chalk.white.bgBlue((i / j).toString())));
                    

Don't forget: typings install chalk --save

Demo (continued)

tsconfig.json


{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es2015",
        "sourceMap": false
    },
    "exclude": [ "node_modules" ],
    "files": [ "typings/main.d.ts", "app.ts" ]
}                  

Consider using ts-node to run TypeScript directly
ts-node app.ts instead of node app.js

Demo: NPM in VS2015

Sample on GitHub

Important Node packages for VS2015

Gulp

Getting started

  • Streaming JavaScript build system based on Node.js
  • Code-over-configuration
    Remember: It is just Node.js, you can do whatever Node can do
  • Many small, single-purposed plugins "piped" together

npm install -g gulp
Install Gulp globally to have it available in every folder

npm install gulp --save-dev
Adds Gulp as a dev-dependency to package.json

npm install gulp-concat --save-dev
Adds a Gulp plugin as a dev-dependency to package.json

Demo: Basic Gulp file


// Load Gulp and necessary Gulp plugins
var gulp = require('gulp');
var concat = require('gulp-concat'); 

// Add a task
gulp.task('styles', function() { 
    return gulp.src('css/*.css')    // Sources (note glob pattern)
        .pipe(concat('all.css'))    // Plugin-specific arguments
        .pipe(gulp.dest('dist/'));  // Destination directory
});

// Add a watch function
gulp.task('watch', function() { 
    gulp.watch('css/*.css', ['styles']); 
});

// Define default task 
gulp.task('default', ['styles']);
                    

Demo: Debugging Gulpfile

Use a Node debugger (e.g. node-inspector)
See also detailed walk-through

On Windows:
node-debug "C:\Users\<your-user>\AppData\Roaming\npm\node_modules\gulp\bin\gulp.js" default

Important Gulp Plugins

Try gulp-tool
E.g. gulp-typescript, gulp-babel, gulp-less, etc.

Get inspired by Gulpfiles of frameworks and seed projects
E.g. angular2-seed

Here are some important ones:
gulp-util, del, gulp-rename, gulp-uglify, gulp-concat, gulp-sourcemaps, gulp-if, gulp-template, gulp-minify-css, gulp-autoprefixer, gulp-load-plugins, gulp-inject, gulp-watch

Gulp in ASP.NET Projects

VS adds Gulpfile.js to new ASP.NET projects

IntelliSense for Gulpfile

Task Runner Explorer

Demo: Gulp in VS2015

  • Create new ASP.NET Core 1.0 web app
  • Gulpfile walkthrough
  • IntelliSense in Gulpfile
  • Task Runner Explorer
  • Task Bindings in Task Runner Explorer
  • Scripts in project.json

End-to-end Demo

Demo Scenario

Based on this sample in GitHub

  • Uses latest ASP.NET Core 1.0 beta on the server
  • Uses latest Angular2 beta on the client

To follow along:

  • Clone my Samples repo
    For this demo you just need the sample in AspNetCore1Workshop/90-aspnet-angular
  • Delete config.ts and Gulpfile.ts
  • Install ts-node globally
  • Open solution in Visual Studio 2015 >= Update 1

Packages

  • Take a look at package.json
    Note Gulp and Gulp plugins in devDependencies
  • Take a look at typings.json
    Note Gulp and Gulp plugins are necessary as we write our Gulpfile in TypeScript
  • Note postinstall script in package.json
  • Note prepublish script in project.json

Run npm install to restore packages and typings

Goals of Gulpfile

  • For demo purposes: Use TypeScript instead of JavaScript
    Drawback: Task Runner Explorer cannot handle that
  • Provide a task for cleanup
  • Compile TypeScript file from src folder into wwwroot
  • Combine all external scripts (SystemJS, Angular2) and styles (Bootstrap) into single files
  • Copy all HTML files from src folder into wwwroot
  • Create a watch task to build on save

Modularize Gulpfile

Create config.ts


import { join } from 'path';

// Folder names for client code (sources, distribution)
export const APP_SRC = "src";
export const APP_DIST = "wwwroot";

// TypeScript sources
export const TS_SOURCES: string[] =
    ["typings/browser.d.ts", join(APP_SRC, "**/*.ts")]

// External script dependencies (combined into single file)
export const SCRIPT_DEPENDENCIES: string[] = [
    "node_modules/systemjs/dist/system.src.js",
    "node_modules/angular2/bundles/angular2-polyfills.js",
    "node_modules/rxjs/bundles/Rx.js",
    "node_modules/angular2/bundles/angular2.dev.js",
    "node_modules/angular2/bundles/http.dev.js"
];
export const SCRIPT_COMBINED = "scripts.js";

export const STYLES_DEPENDENCIES: string[] = [
    "node_modules/bootstrap/dist/css/bootstrap.css"
];
export const STYLES_COMBINED = "styles.css";
                    

Gulpfile.js


import { join } from 'path';
import * as gulp from 'gulp';
import * as del from 'del';
import * as ts from 'gulp-typescript';
import * as sourcemaps from 'gulp-sourcemaps';
import * as concat from 'gulp-concat';
import * as config from './config';

// Create TypeScript project from tsconfig.json
var tsClientProject = ts.createProject("tsconfig.json", {
    typescript: require("typescript")
});

// Cleanup by deleting target directory
gulp.task("clean", () => {
    del.sync(join(config.APP_DIST, "**/*"));
}); 

// Compile the typescript files of the client app
gulp.task("clientApp", [], () => {
    var tsResult = gulp.src(config.TS_SOURCES)
    	.pipe(ts(tsClientProject))
  		.pipe(gulp.dest(config.APP_DIST));
});

gulp.task("index", [], () => {
    // Combine external scripts into single file
    gulp.src(config.SCRIPT_DEPENDENCIES)
        .pipe(sourcemaps.init())
        .pipe(concat(config.SCRIPT_COMBINED))
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest(config.APP_DIST));

    // Combine external styles into single file
    gulp.src(config.STYLES_DEPENDENCIES)
        .pipe(sourcemaps.init())
        .pipe(concat(config.STYLES_COMBINED))
        .pipe(sourcemaps.write("."))
        .pipe(gulp.dest(config.APP_DIST));

    // Copy all HTML files
    gulp.src(join(config.APP_SRC, "**/*.html"))
        .pipe(gulp.dest(config.APP_DIST));
});

//Set a default tasks
gulp.task("default", ["clean", "clientApp", "index"], () => { });

// Define a watch task
gulp.task("watch", () => {
    gulp.watch(join(config.APP_SRC, "**/*"), ["default"]);
});
                    

Run Gulp

  • Run gulp in command line
  • Publish web app to Azure App Services
    Note how prepublish script in project.json runs
  • Use Gulp in Visual Studio Team Services Build
    See following slides

Gulp in VSTS Build

Gulp in VSTS Build

Necessary steps:

  • npm install
  • Gulp
    Gulp File Path: AspNetCore1Angular2Intro/Gulpfile.ts
  • Copy Files
    Source Folder: $(build.sourcesdirectory)/AspNetCore1Angular2Intro/wwwroot
    Contents: **
    Target Folder: $(build.artifactstagingdirectory)
     
  • Publish Build Artifacts
    Path to publish: $(build.artifactstagingdirectory)
    Artifact name: drop
    Artifact type: Server

Thank you for attending!

See you next BASTA!