Wednesday, July 6, 2016

Building a web application using ASP.NET Core 1.0, Aurelia, TypeScript, Webpack in Visual Studio Code – Part 3

Introduction

YouTube Video Link for Part 3:

This is the third part of Building a web application using ASP.NET Core 1.0, Aurelia, TypeScript, Webpack in Visual Studio Code.

The source code for this tutorial is available on GitHub.

In Part 2 of this series, we setup TypeScript, Typings, created a tsconfig.json and wrote our first TypeScript class. In this part, we’ll focus on setting up Aurelia and Webpack. Let us get started.

Prerequisites

Please create a new folder named “part3” under “C:\tutorial”. You’ll need the source code from part2 to practice this part of the series. You can pull the source code from my github repository: https://github.com/rajajhansi/aspnetcore-aurelia-tutorial . After you got the source code, delete the “.git” directory.

Webpack

Webpack is a powerful module bundler that takes the concept of modules to static assets like image files and stylesheets besides JavaScript files. Webpack supports code splitting via chunks, loaders that can transform resources into JavaScript and has a rich Plugin system. Webpack is well suited for big projects that use lots of 3rd party libraries, modules built using ES2015, 2016, TypeScript and stylesheets built using SASS/LESS. Webpack has a cool featured named HMR (Hot Module Replacement) that improves productivity during development of web applications. The webpack-dev-server is a little node.js Express server that supports HMR. You no longer need to go through that vicious cycle of modify, transpile and serve the application. HMR does that for you out of the box! Do not use webpack-dev-server for production deployment. I’ll show you how you can use ASP.Net Core 1.0 to server the bundles created by Webpack later in this post.

Before I introduce Aurelia, let us see how to configure Webpack to run our Greeter class using modules instead of including using <script src=””></script>. Install the latest version of webpack by specifying the version using npm as follows:

npm install webpack@2.1.0-beta.15 --save-dev

It is very important the fundamentals of Webpack clearly before I start showing you how to use Webpack with TypeScript and Aurelia. So, let me show you one step at a time on how you can use TypeScript compiler outside of Webpack to transpile .ts files into .js, introduce the concept of modules, how you can use TypeScript modules to start using one TypeScript class from another, load the generated modules in JavaScript using Webpack and finally show how to leverage typescript loader from within Webpack.

Modules

The concept of modules has been there for quite some time in JavaScript and are implemented using libraries. CommonJs and AMD (Asynchronous Module Definition) were (and still are) very popular module standards (or should I say non-standards) before the standards body has finally defined the syntax and semantics for modules in ES6.

When it comes to modules, you just need to remember two important things: export and import. As their names imply, “export” is used to export a symbol like class, function or variable and “import” is used to access the exported symbol from another module. In ES6, there are two kinds of exports: named exports i.e., several symbols per module and default exports i.e., only one symbol per module.

TypeScript has support for modules for quite some time. TypeScript’s modulesyntax is slightly different from ES6 but you can transpile TypeScript module into any module formats like: commonjs, amd and es6.

Adding a TypeScript class as an entry point

In any application be it console or windows or web application, we always need an entry point aka “main”. The convention is to either call the main or app classes. So, let us define an entry point class named  main.ts and import greeter class. Before we do that, lets export our Greeter class using the module syntax i.e., “export” as follows:

export class Greeter {
..
}

You can also export the Greeter class like this:

export { Greeter };

Let’s define our main class in main.ts:

import { Greeter } from "./Greeter";
export class Main {
    private greeter: Greeter;
    constructor() {
        this.greeter = new Greeter("World");
    }
    sayHello() {
        this.greeter.sayHello();
        document.getElementById("greeting").innerHTML = "<h1>" + m.greetingMessage + "</h1>";
    }
    get greetingMessage() : string {
        return this.greeter.greetingMessage;
    }
} var m = new Main();
m.sayHello();
console.log(m.greetingMessage);

Let us run tsc compiler in watch mode so it automatically transpiles .ts files whenever we add new .ts files or make changes to any .ts file like this:

tsc –w
Notice that in line 1, we import the Greeter class using the TypeScript "import" syntax. After the class definition, we create an instance of the class Main and invoke its sayHello method as well as greetingMessage accessor. We are doing that mainly to avoid having to import {Main} from "./Main" and invoking those methods from within Index.html because that would require the browser to understand ES6 module syntax and load it, which is not a feature available in current browsers.

Remember that we configured tsconfig.json to output js files with “commonjs” as the module format. So, TypeScript will happily transpile our TypeScript class into JavaScript that uses the CommonJs module syntax. But to use the modules transpiled by TypeScript, you still need a module loader library. Since Webpack is a module bundler that has module loaders as well, we’ll leverage Webpack for loading the modules. Let us bundle our transpiled js files using the webpack command:

webpack main.js bundle.js

We need to load the Main module from index.html in order for us to see the results in the browser. Here’s our index.html:

<html>
<head>
    <title>Welcome to ASP.NET Core 1.0</title>   
</head> <body>
    <div id="greeting">
    </div>
    <script src="app/bundle.js"></script>
</body> </html>

Webpack has a lot of command line options and it will become overwhelming for the users to enter them every time .So, you can create a webpack.config.js file at the root folder, where you can specify the configuration options and can just run wepack. For our example, here’s the webpack.config.js

module.exports = {
    entry: "./wwwroot/app/main.js",
    output: {       
        path: __dirname + "/wwwroot/app",
        filename: "bundle.js"
    }
};

Now, you should be able to run webpack without any arguments. After you run “webpack”, it will generate bundle.js that has the source code of main.js and greeter.js bundled and Webpack knows to how to load the modules inside of bundle.js.

fig0.1


Running the application

We can run our application from Visual Studio Code by hitting F5. After VSCode launches Chrome and shows the default page i.e., /Home/Index, just change the url to load index.html. Make sure to open the developer tools (by hitting F12) and you can see that webpack’s module loader loads bundle.js, which contains 2 other modules: main.js and greeter.js bundled within itself. You should see the message “Hello World from TypeScript!” in the browser window and the same message printed twice in Debugging console as well.

fig0

Although we got Webpack module loader working, we are still running the typescript compiler on watch mode to transpile .ts files. I talked about awesome-typescript-loader and it is time to wire that into our webpack.config.js.

Wiring awesome-typescript-loader into Webpack

We need to install awesome-typescript-loader (lets refer it as atl from now on) as a development type dependency using npm like this:

npm install awesome-typescript-loader --save-dev

After we installed atl, we can configure it in webpack.config.js by visiting the github page for atl at https://github.com/s-panferov/awesome-typescript-loader and copy pasting the sample configuration from there. Our webpack.config.js should look like this:

module.exports = {
    entry: "./src/app/main.ts",
    output: {       
        path: __dirname + "/wwwroot/app",
        filename: "bundle.js"
    },
    // Currently we need to add '.ts' to the resolve.extensions array.
  resolve: {
    extensions: ['', '.ts', '.webpack.js', '.web.js', '.js']
  },   // Source maps support ('inline-source-map' also works)
  devtool: 'source-map',   // Add the loader for .ts files.
  module: {
    loaders: [
      {
        test: /\.ts$/,
        loader: 'awesome-typescript-loader'
      }
    ]
  }
};

We no longer need to run tsc –w. If you’re already running it inside a tab in Cmder, just close that tab. Notice that I changed the entry property from “./wwwroot/app/main.js” to “./src/app/main/ts” because Webpack will take care of the transpiling using atl. Delete the contents of wwwroot\app folder and let Webpack take care of transpiling the .js code, bundling them and loading them for us. Before running webpack, just remove the inlineSourceMap and inlineSources options with “sourceMap”: true in tsconfig.json.  If you run webpack, you should get a warning from atl like this:

fig0.2

The warning clearly says that we should add “exclude”":” property to speed up transpilation, which makes total sense. So, let us go ahead and add the “excludes” into tsconfig.json like this:

"exclude": [
            "node_modules"           
  ]

You can now run webpack and it should generate bundle.js and bundle.js.map files.

fig0.3

Hit F5 from VSCode and after it launches Chrome, change the url to http://localhost:5000/index.html and you should get the same output like before. The main difference is that now transpiling, bundling and module loading are all taken care of by Webpack!!! In the next part of the series, I’ll show how to setup Aurelia framework from scratch and kick start a new project using Aurelia’s typescript-webpack-skeleton navigation project as the foundation. Happy coding!



1 comment:

  1. Hi Raja,

    I am quite surprise to find someone who are really up-to-date in this field. I would like to ask your opinion related to this. Hope you don't mind. Please email me at christopher.linkoln2724@gmail.com

    thank you

    ReplyDelete