At this point, the basic framework of the application is in place and we’re ready to start developing the application functionality. Since browsers often lag behind Node.js in terms of support for the latest JavaScript features, we need a way to convert modern JavaScript into a common dialect that browsers will understand.
The practice of compiling source code from one programming language into another (or from one language version to another version) is called transpiling. There is a rich tradition of transpiling in the JavaScript community—both from other languages into JavaScript and JavaScript-to-JavaScript. Today, two of the most popular JavaScript transpilers are Babel and TypeScript.
Babel transpiles code written using the latest ECMAScript techniques (like those we’ve been using throughout this book),[74] into a dialect of ECMAScript that’s widely available in browsers. Babel can be used in conjunction with Flow, an analysis tool by Facebook, which performs type inference and type checking.[75] In many cases, Flow can infer the type of a variable and inform you of subtle code errors before they become runtime issues. If that’s not enough, you can also provide specific type information about your variables that Flow will enforce.
TypeScript, by Microsoft, is a superset of JavaScript that includes typings.[76] The TypeScript toolchain performs transpiling down into common ECMAScript, as well as static type inference and checking on your source code. Like Flow, in many cases TypeScript can infer variable types, and you can also provide specific types through a rich type system.
For our immediate purpose, either Babel or TypeScript would be fine. Both are relatively easy to integrate with webpack, and both can transpile the latest ECMAScript down to a version understood by browsers. I’ve chosen to use TypeScript in this book for a couple of reasons.
First, since TypeScript performs type inference and checking in addition to transpiling, you get a layer of protection for free. With Babel, we’d have to add Flow to get the same benefit.
Second, TypeScript has a large and growing repository of community-contributed typings called DefinitelyTyped.[77] This repository is directly available through npm as a collection of packages that begin with the prefix @types/. For example, the Bootstrap typings are available as @types/bootstrap.
I think momentum is on TypeScript’s side, but time will tell. In either case, the code that you’ll be writing throughout the rest of this chapter is compatible with both TypeScript and Babel/Flow.
To use TypeScript in your project, start by installing it with npm.
| | $ npm install --save-dev --save-exact typescript@2.5.3 |
You’ll also need the ts-loader plugin for webpack.
| | $ npm install --save-dev --save-exact ts-loader@2.3.7 |
To transpile TypeScript into JavaScript, you’ll need a tsconfig.json to configure your project. Open your text editor and enter the following:
| | { |
| | "compilerOptions": { |
| | "outDir": "./dist/", |
| | "sourceMap": true, |
| | "module": "CommonJS", |
| | "target": "ES5", |
| | "lib": ["DOM", "ES2015.Promise", "ES5"], |
| | "allowJs": true, |
| | "alwaysStrict": true |
| | } |
| | } |
Via the tsconfig.json, TypeScript allows you to fine-tune the compilation through many compiler options.[78] Here’s a quick rundown of the ones we’re using.
outDir—Directory where TypeScript should emit transpiled js files. These files won’t actually appear here when using the ts-loader and webpack dev server.
sourceMap—Whether to generate a source map along with the transpiled output. We’ll explore this in more detail later.
module—Which module system to use to represent transpiled dependencies. CommonJS is the module system used by Node.js, characterized by the require function for pulling in dependent modules.
target—Which version of ECMAScript to target for transpiled output. ECMAScript version 5 has wide support among modern browsers.
lib—Collection of built-in typings that TypeScript should be aware of. In our case, we’ll employ features of the DOM and presume that the user’s browser has support for Promises.
allowJs—Whether to permit TypeScript to compile js source files as well as ts.
alwaysStrict—Whether to always interpret code in strict mode and emit ’use strict’; in generated files. This lets us omit the use strict clause from the tops of ts files.
After you save this file, it’s time to update your webpack.config.js. To start, open that file and update the entry to point to ./app/index.ts.
| | entry: './app/index.ts', |
In addition to switching from a js file to a ts file, let’s also create a subdirectory to organize the front-end code files. It is a webpack convention to call this directory app. We’ll create the app directory and index.ts in a bit, but first let’s finish updating the webpack config.
Down in the module.rules section of your webpack.config.js file, add a rule to the top of the list to associate the ts with TypeScript via the ts-loader plugin. Your module.rules section should now look like this:
| | module: { |
| | rules: [{ |
| | test: /\.ts$/, |
| | loader: 'ts-loader', |
| | },{ |
| | test: /\.css$/, |
| | use: [ 'style-loader', 'css-loader' ] |
| | },{ |
| | test: /\.(png|woff|woff2|eot|ttf|svg)$/, |
| | loader: 'url-loader?limit=100000', |
| | }], |
| | }, |
Good—now it’s time to create the app directory.
| | $ mkdir app |
Rather than have all of the application code lumped into one file, let’s split out the HTML parts into a separate file called templates.ts. Open your text editor and enter the following:
| | export const main = ` |
| | <div class="container"> |
| | <h1>B4 - Book Bundler</h1> |
| | <div class="b4-alerts"></div> |
| | <div class="b4-main"></div> |
| | </div> |
| | `; |
| | |
| | export const welcome = ` |
| | <div class="jumbotron"> |
| | <h1>Welcome!</h1> |
| | <p>B4 is an application for creating book bundles.</p> |
| | </div> |
| | `; |
| | |
| | export const alert = ` |
| | <div class="alert alert-success alert-dismissible fade in" role="alert"> |
| | <button class="close" data-dismiss="alert" aria-label="Close"> |
| | <span aria-hidden="true">×</span> |
| | </button> |
| | <strong>Success!</strong> Bootstrap is working. |
| | </div> |
| | `; |
The multiline strings of HTML you see here are all copied directly from the entry.js from earlier. But rather than assigning them to the innerHTML properties of DOM elements, we’re making them available through the ES6 module keyword export. This is similar to setting module.exports like you’ve done in previous examples in this book.
Once you save this file, start a new file in your editor for the index.ts file. Enter the following to get it started.
| | import '../node_modules/bootstrap/dist/css/bootstrap.min.css'; |
| | import 'bootstrap'; |
| | import * as templates from './templates.ts'; |
| | |
| | document.body.innerHTML = templates.main; |
| | |
| | const mainElement = document.body.querySelector('.b4-main'); |
| | const alertsElement = document.body.querySelector('.b4-alerts'); |
| | |
| | mainElement.innerHTML = templates.welcome; |
| | alertsElement.innerHTML = templates.alert; |
At the outset of this file, we import the same bootstrap.css and bootstrap module as before. Note the subtle difference in the path to bootstrap.css. Now that we’re down one subdirectory in app, the path to bootstrap.css starts with ../node_modules/ instead of ./node_modules/.
Next, we import all of the exported members from templates.ts into a local variable called templates. These will be used to populate the innerHTML elements in the page.
As before, in entry.js we start by populating the document.body with the main structural HTML. After that, we grab references to the mainElement and the alertsElement in which we’ll render page content and notifications, respectively.
Lastly, we fill in the main content element with the HTML from templates.welcome, and the alerts element with the HTML from templates.alert. This produces the same behavior as the entry.js from earlier, and represents a good test to ensure that all of the TypeScript and webpack configuration is working.
After saving this file, restart your webpack dev server, then confirm that the page at localhost:60800 looks the same as before and the close button on the alert works as expected. If so, great!
Next, we’ll add HTML templating to the project. This will afford dynamically generating HTML, rather than relying on only static strings.