Automating Transpilation

As we examine the newest es2015 features, it is quite likely that we'll find ourselves wanting to implement them in building our games. However, browsers do not support all features, and determining what is and isn't supported, as well as which browser version our players are using, can get quite messy.

An alternative approach is to transpile our code - write it in es2016 syntax, and then use a transpiler to convert it to an earlier, and more broadly suppported, syntax. Effectively, the process is similar to compiling, but instead of converting from a higher-order language to machine code, what we are doing is converting from one version of that langauge (or a superset) into another version.

The most popular transpiler for JavaScript is probably (Babel)[https://babeljs.io/]. Babel supports transpilation of es2015, Typescript (a superset of JavaScript that adds static types), and JSX (React's sytnax for blending HTML and JavaScript in a single code file). Babel can be installed as a stand-alone compiler and run from the command line, much like gcc and javac. But it can also be combined with other tools as part of a development pipeline.

One of these other tools is (Webpack)[https://webpack.github.io/], which seeks to address the challenge of developing complex websites, which may consist of hundreds of JavaScript, CSS, and image files - especially if the developers focus on component-based design and code encapsulation (where small classes and files with a clear purpose are desirable).

While desirable for cleanliness and mantainibility of code for developers, this style of development can cause serious problems for the end-user. In the browser, loading hundreds of small files can create significant overhead and delay page loads. In this client environment, therefore, it is desirable to have only a few, larger files. Webpack provides the functionality to combine similar files, as well as minify (remove extraneous characters) from code.

By combining tools like Babel and Webpack in a development pipeline, we can create code files using good design practices, and then create a release build optimized for web use. Moreover, once a pipeline is estabilished, it can also be automated - we can create scripts to launch a development server, and watch for changes to our script files, automatically redeploying changes to our development server.

One such pipeline tool is the (react-scripts)[https://github.com/facebookincubator/create-react-app/tree/master/packages/react-scripts] project. A part of the create-react-app tool for creating React applications, react-scripts was built using a modular style that allows us to use it independently of its parent library. We'll use it to apply a development pipeline approach to our Snake game.

Packaging our Game

The first step will be to convert our Snake game from a simple HTML project into a Node package. At the command line in your project directory, run:

> npm init

This will launch a wizard that asks you a number of questions to define your project. For now, feel free to hit the enter button to use the default settings. This creates a package.json file in your project:

In [ ]:
{
  "name": "snake",
  "version": "1.0.0",
  "description": "A snake arcade game implementation.",
  "main": "game.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/CIS580F17/snake.git"
  },
  "author": "Nathan H. Bean",
  "license": "THE UNLICENSE",
  "bugs": {
    "url": "https://github.com/CIS580F17/snake/issues"
  },
  "homepage": "https://github.com/CIS580F17/snake#readme"
}

The package.json file describes your project settings that are used by the node package manager (npm) to build your project. We'll also need to add a .gitignore file to your project directory, with at least the line:

/node_modules

The node_modules directory is used by npm to store libraries your project is dependent on. We don't include them in our git repo because they consume a lot of space, and may be platform-specific. It is better practice to allow developers using your project to download the dependencies themselves, which can be done via npm. To allow them to do so, we must add the dependencies to our package.json file. We can do so manually, or through the npm install command:

npm install react-scripts --save-dev

Go ahead and run this command. It will add react-scripts as a development dependency (because of the --save-dev flag) in your package.json file, and will also download react-scripts and any of its dependencies into the node_modules directory. If you clone your project on another machine in the future, you can install the dependencies in package.json with the command:

npm install

We'll also need to add a couple of script commands to our package.json file to use this new tool:

In [ ]:
{
  "name": "snake",
  "version": "1.0.0",
  "description": "A snake arcade game implementation.",
  "main": "game.js",
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/CIS580F17/snake.git"
  },
  "author": "Nathan H. Bean",
  "license": "THE UNLICENSE",
  "bugs": {
    "url": "https://github.com/CIS580F17/snake/issues"
  },
  "homepage": "https://github.com/CIS580F17/snake#readme",
  "devDependencies": {
    "react-scripts": "^1.0.13"
  }
}

The scripts start and build provide commands for launching a development server (start) and building the production version of our game (build). But before we do that, we need to tweak our project structure to match the expectations of react-scripts. We need to create three new directories: build, src, and public, so that our directory structure looks like:

project
  + build
  + node_modules
  + public
  + src

The public directory

The public folder is where any static files we are using will be located, i.e. the main HTML page and your css files. If you are starting from a blank project, this should contain at least one html file named index.html. For our snake game, it would look something like this:

In [ ]:
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Snake</title>
  </head>
  <body>
    <noscript>
      You need to enable JavaScript to run this app.
    </noscript>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

This file needs to be named index.html because react-scripts will, during the build process, inject a webpack-ed JavaScript bundle (and potentially a css bundle). The index.html file can otherwise be modified like any HTML file.

The src directory

The src directory is where your JavaScript and compiled CSS goes. It too requires an index file - index.js - which is the entry point into the program. This main program can use the es2015 include syntax to include both JavaScript files and CSS files (the CSS files aren't actually included in the code sense, rather this serves as an indicator for react-scripts and webpack to bundle the included CSS files. An example index.js might then look like:

In [ ]:
// index.js
import Game from './game';
import './game.css';

/* Construct the game */
new Game();

The build directory

Finally, the build directory is where react-scripts drops the compiled release files. As suggested above, the index.html file will be modified to include built resources, which are generated by webpack in a static subdirectory. Other files in the public directory are simply copied into the release directory. And, a few additional files, like service-worker.js, may be added.

Running the Development Build

To run a development build, from the terminal issue the command:

> npm run start

This will launch a webserver, open a new tab to display the page in a currently-open or your default browser, and start watching the src and public directories. Any changes to these files will recompile the project, and hot-load the code into the browser. This way, you can edit your code and see the results almost immediately without needing to issue a lot of extra commands.

Building a Release

To build a release, from the terminal issue the command:

> npm run build

This will create the release files and place them in the build directory. These can then be deployed to a webserver of your choice.