Building and testing projects

We have already talked about building and testing TypeScript projects at the beginning of this book. In this section, we will go a little bit further for frontend projects, including the basis of using Webpack to load static assets as well as code linting.

Static assets packaging with webpack

Modularizing helps code keep a healthy structure and makes it maintainable. However, it could lead to performance issues if development-time code written in small modules are directly deployed without bundling for production usage. So static assets packaging becomes a serious topic of frontend engineering.

Back to the old days, packaging JavaScript files was just about uglifying source code and concatenating files together. The project might be modularized as well, but in a global way. Then we have libraries like Require.js, with modules no longer automatically exposing themselves to the global scope.

But as I have mentioned, having the client download module files separately is not ideal for performance; soon we had tools like browserify, and later, webpack - one of the most popular frontend packaging tools these days.

Introduction to webpack

Webpack is an integrated packaging tool dedicated (at least at the beginning) to frontend projects. It is designed to package not only JavaScript, but also other static assets in a frontend project. Webpack provides built-in support for both asynchronous module definition (AMD) and commonjs, and can load ES6 or other types of resources via plugins.

Note

ES6 module support will get built-in for webpack 2.0, but by the time this chapter is written, you still need plugins like babel-loader or ts-loader to make it work. And of course we are going to use ts-loader later.

To install webpack via npm, execute the following command:

$ npm install webpack -g

Bundling JavaScript

Before we actually use webpack to load TypeScript files, we'll have a quick walk through of bundling JavaScript.

First, let's create the file index.js under the directory client/src/ with the following code inside:

var Foo = require('./foo'); 
 
Foo.test(); 

Then create the file foo.js in the same folder with the following content:

exports.test = function test() { 
  console.log('Hello, Webpack!'); 
}; 

Now we can have them bundled as a single file using the webpack command-line interface:

$ webpack ./client/src/index.js ./client/out/bundle.js

By viewing the bundle.js file generated by webpack, you will see that the contents of both index.js and foo.js have been wrapped into that single file, together with the bootstrap code of webpack. Of course, we would prefer not to type those file paths in the command line every time, but to use a configuration file instead.

Webpack provides configuration file support in the form of JavaScript files, which makes it more flexible to generate necessary data like bundle entries automatically. Let's create a simple configuration file that does what the previous command did.

Create file client/webpack.config.js with the following lines:

'use strict'; 
 
const Path = require('path'); 
 
module.exports = { 
  entry: './src/index', 
  output: { 
    path: Path.join(__dirname, 'out'), 
    filename: 'bundle.js' 
  } 
}; 

These are the two things to mention:

  1. The value of the entry field is not the filename, but the module id (most of the time this is unresolved) instead. This means that you can have the .js extension omitted, but have to prefix it with ./ or ../ by default when referencing a file.
  2. The output path is required to be absolute. Building an absolute path with __dirname ensures it works properly if we are not executing webpack under the same directory as the configuration file.

Loading TypeScript

Now we are going to load and transpile our beloved TypeScript using the webpack plugin ts-loader. Before updating the configuration, let's install the necessary npm packages:

$ npm install typescript ts-loader --save-dev

If things go well, you should have the TypeScript compiler as well as the ts-loader plugin installed locally. We may also want to rename and update the files index.js and foo.js to TypeScript files.

Rename index.js to index.ts and update the module importing syntax:

import * as Foo from './foo'; 
 
Foo.test(); 

Rename foo.js to foo.ts and update the module exporting syntax:

export function test() { 
  console.log('Hello, Webpack!'); 
} 

Of course, we would want to add the tsconfig.json file for those TypeScript files (in the folder client):

{ 
  "compilerOptions": { 
    "target": "es5", 
    "module": "commonjs" 
  }, 
  "exclude": [ 
    "out", 
    "node_modules" 
  ] 
} 

Note

The compiler option outDir is omitted here because it is managed in the webpack configuration file.

To make webpack work with TypeScript via ts-loader, we'll need to tell webpack some information in the configuration file:

  1. Webpack will need to resolve files with .ts extensions. Webpack has a default extensions list to resolve, including '' (empty string), '.webpack.js', '.web.js', and '.js'. We need to add '.ts' to this list for it to recognize TypeScript files.
  2. Webpack will need to have ts-loader loading .ts modules because it does not compile TypeScript itself.

And here is the updated webpack.config.js:

'use strict'; 
 
const Path = require('path'); 
 
module.exports = { 
  entry: './src/index', 
  output: { 
    path: Path.join(__dirname, 'bld'), 
    filename: 'bundle.js' 
  }, 
  resolve: { 
    extensions: ['', '.webpack.js', '.web.js', '.ts', '.js'] 
  }, 
  module: { 
    loaders: [ 
      { test: /.ts$/, loader: 'ts-loader' } 
    ] 
  } 
}; 

Now execute the command webpack under the client folder again, we should get the compiled and bundled output as expected.

During development, we can enable transpile mode (corresponding to the compiler option isolatedModules) of TypeScript to have better performance on compiling changing files. But it means we'll need to rely on an IDE or an editor to provide error hints. And remember to make another compilation with transpile mode disabled after debugging to ensure things still work.

To enable transpile mode, add a ts field (defined by the ts-loader plugin) with transpileOnly set to true:

module.exports = { 
  ... 
  ts: { 
      transpileOnly: true 
  } 
}; 

Splitting code

To take the advantage of code caching across pages, we might want to split the packaged modules as common pieces. The webpack provides a built-in plugin called CommonsChunkPlugin that can pick out common modules and have them packed separately.

For example, if we create another file called bar.ts that imports foo.ts just like index.ts does, foo.ts can be treated as a common chunk and be packed separately:

module.exports = { 
  entry: ['./src/index', './src/bar'], 
  ... 
  plugins: [ 
    new Webpack.optimize.CommonsChunkPlugin({ 
      name: 'common', 
      filename: 'common.js' 
    }) 
  ] 
}; 

For multi-page applications, it is common to have different pages with different entry scripts. Instead of manually updating the entry field in the configuration file, we can take advantage of it being JavaScript and generate proper entries automatically. To do so, we might want the help of the npm package glob for matching page entries:

$ npm install glob --saved-dev

And then update the webpack configuration file:

const glob = require('glob'); 
 
module.exports = { 
  entry: glob 
    .sync('./src/pages/*/*.ts') 
    .filter(path => 
      Path.basename(path, '.ts') === 
      Path.basename(Path.dirname(path)) 
    ), 
  ... 
}; 

Splitting the code can be rather a complex topic for deep dive, so we'll stop here and let you explore.

Loading other static assets

As we've mentioned, webpack can also be used to load other static assets like stylesheet and its extensions. For example, you can use the combination of style-loader, css-loader and sass-loader/less-loader to load .sass/.less files.

The configuration is similar to ts-loader so we'll not spend extra pages for their introductions. For more information, refer to the following URLs:

Adding TSLint to projects

A consistent code style is an important factor of code quality, and linters are our best friends when it comes to code styles (and they also helps with common mistakes). For TypeScript linting, TSLint is currently the simplest choice.

The installation and configuration of TSLint are easy. To begin with, let's install tslint as a global command:

$ npm install tslint -g

And then we need to initialize a configuration file using the following command under the project root directory:

$ tslint --init

TSLint will then generate a default configuration file named tslint.json, and you may customize it based on your own preferences. And now we can use it to lint our TypeScript source code:

$ tslint */src/**/*.ts

Integrating webpack and tslint command with npm scripts

As we've mentioned before, an advantage of using npm scripts is that they can handle local packages with executables properly by adding node_modules/.bin to PATH. And to make our application easier to build and test for other developers, we can have webpack and tslint installed as development dependencies and add related scripts to package.json:

"scripts": { 
  "build-client": "cd client && webpack", 
  "build-server": "tsc --project server", 
  "build": "npm run build-client && npm run build-server", 
  "lint": "tslint ./*/src/**/*.ts", 
  "test-client": "cd client && mocha", 
  "test-server": "cd server && mocha", 
  "test": "npm run lint && npm run test-client && npm run test-server" 
} 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.86.211