Skip to main content

How to Build TypeScript App and Deploy it on GitHub Pages

Quick Summary

In this post, I will show you how to easily build and deploy a simple TicksToDate time web app like this: https://zubialevich.github.io/ticks-to-datetime.

Requirements

  1. Text Editor (Visual Studio Code was used)
  2. Node.js (I've used v8.12.0 and higher)
  3. Yarn (I've used v1.9.4)
  4. TypeScript (2.9.2 and higher)

Introduction

TypeScript is a superset of JavaScript and adds optional static typing to the language. TypeScript is designed for development of large applications and transcompiles to JavaScript. In this tutorial, we will learn how easily build a simple web app with a TypeScript and deploy it on a GitHub Pages, free static website hosting.

So let's do this!

Ticks To DateTime

We will build an app that converts ticks to a date time string. A single tick represents one hundred nanoseconds or one ten-millionth of a second. There are 10,000 ticks in a millisecond or 10 million ticks in a second. The value of this property represents the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001 (0:00:00 UTC on January 1, 0001, in the Gregorian calendar). Ticks is a good way to store DateTime data with a high level of precision in some data store.

Setup

Create a folder for your project, for example, "ticks-to-datetime". Inside this folder create another one called "src" where we will store all our .ts, .html and .css files.
Let's start with first file "index.html":
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Ticks to DateTime</title>
</head>

<body>
    <div id="container">
        <h1>[ Ticks to DateTime ]</h1>
        <form>
            <div class="input">
                <input type="text" placeholder="Ticks" id="ticks" size="17" autofocus="autofocus" />
            </div>
        </form>
        <div id="datetime" class="item"></div>
    </div>
    <script src="./index.ts"></script>
</body>

</html>
Here we specify on input field "ticks", where we will put the value of ticks. And an empty div "datetime" where we will output normal date string.

Now let's create a file with the logic for our app. Inside /src folder create file index.ts. Your /src folder now should look like this:

After the main window is loaded we should add some handlers that will listen to changes in "ticks" input. To do this add this in the index.ts file:
var possibleEvents = new Set(["input", "onpropertychange", "keyup", "change", "paste"]);

window.onload = () => {
    var ticksInput = document.getElementById("ticks") as HTMLInputElement;
    possibleEvents.forEach((eventName: string) => {
        ticksInput.addEventListener(eventName, (ev: Event) => {
            var inputElement = ev.target as HTMLInputElement;
            var handler = new TickInputHandler();
            handler.showResult(inputElement);
        })
    });
};
Here we specify the list of events for ticks input we will listen: input, onpropertychhange, keyup, change, paste. Names are pretty informative. Inside the addEventListener, we get variable "ev" of type Event. Event is the base ts type for all HTML event which we then try to convert to the desired type of HTMLInputElement, which will store the value of our input.

Also inside the handler, we create an object of type TickInputHandler and call on it method showResult. This method will get the value from the input, convert it to DateTime string and show it in the "datetime" div.

Your file index.ts should look like this:
var possibleEvents = new Set(["input", "onpropertychange", "keyup", "change", "paste"]);

window.onload = () => {
    var ticksInput = document.getElementById("ticks") as HTMLInputElement;
    possibleEvents.forEach((eventName: string) => {
        ticksInput.addEventListener(eventName, (ev: Event) => {
            var inputElement = ev.target as HTMLInputElement;
            var handler = new TickInputHandler();
            handler.showResult(inputElement);
        })
    });
};

class TickInputHandler {
    // Ticks value for date 01-01-1970
    static epochTicks: number = 621355968000000000;
    static ticksPerMillisecond: number = 10000;
    // http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.1
    static maxDateMilliseconds: number = 8640000000000000;

    public showResult(inputElement: HTMLInputElement) {
        // Get value from the input and try to convert it to type Number
        var valueStr = inputElement.value;
        var ticks = Number(valueStr);

        var dateString = String();
        // If we were not able to parse input as a Number - show empty DateTimeString
        if (isNaN(ticks)) {
            dateString = "____-__-__T__:__:__.____Z";
        }

        // convert the ticks into something typescript understands
        var ticksSinceEpoch = ticks - TickInputHandler.epochTicks;
        var millisecondsSinceEpoch = ticksSinceEpoch / TickInputHandler.ticksPerMillisecond;
        // If the value of the input is more than max value - show special DateTime string for this case
        if (millisecondsSinceEpoch > TickInputHandler.maxDateMilliseconds) {
            dateString = "9999-99-99T99:99:99:9999Z";
        }

        // output the result in something the human understands
        var date = new Date(millisecondsSinceEpoch);
        return date.toISOString();
        var dateTimeOutput = document.getElementById("datetime");
        dateTimeOutput.innerHTML = dateString;
    }
}
Here inside showResult method we get the value from ticks input and convert it to DateTime string. Comments in the code should add some light on what's happening there.

Build and Deploy Setup

After we've built our app, we want now to run it on our local machine and then will be nice to deploy it somewhere on the internet. To do this we need to set up the required dependencies and for these add in the root directory of your project (not /src folder) two empty files: package.json and tsconfig.json.

File package.json will describe required dependencies and 3 useful scripts that we will use in future.
{
    "files": [
        "src/"
    ],
    "scripts": {
        "start": "parcel serve src/index.html",
        "build": "cross-env NODE_ENV=production parcel build src/index.html --public-url .",
        "deploy": "gh-pages -d dist"
    },
    "devDependencies": {
        "cross-env": "^5.2.0",
        "gh-pages": "^1.2.0",
        "parcel-bundler": "^1.9.7",
        "typescript": "^2.9.2"
    }
}
Here we add dependencies that our project will require. The most interesting packages are gh-pages and parcel-bundler.

Package gh-pages publish files to a "gh-pages" branch on GitHub. From this branch, GitHub will create a GitHub Page for you like https://{github-account-name}.github.io/{repo-name}.

Package parcel-bundler is a web application bundler that will help to build, deploy and run locally our app without any configs.

File tsconfig.json will look next way:
{
    "compilerOptions": {
        "module": "commonjs",
        "moduleResolution": "node",
        "newLine": "LF",
        "outDir": "./lib/",
        "target": "es5",
        "sourceMap": true,
        "declaration": true,
        "jsx": "preserve",
        "lib": [
            "es2017",
            "dom"
        ],
        "strict": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "noImplicitReturns": true,
        "noFallthroughCasesInSwitch": true
    },
    "include": [
        "src/**/*"
    ],
    "exclude": [
        ".git",
        "node_modules"
    ]
}
After this changes your project folder will look like this:

Run Locally

Now open your favorite command line tool (cmd, bash, etc) and navigate to the folder with your project. Before the start, we should install all dependencies. Run next command:
$ yarn install
This can take some time as it will download and install all dependencies required to build and deploy our app.
After finish, run command:
$ yarn start

It will run a local web server on the URL http://localhost:1234. If you will open it in a browser you will see your running site. If everything was correct - congratulations! Now you have your site up and running.

To build your app run command:
$ yarn build
It will build your app and put resulted files in folder /dist. This files you can deploy on any web server or put it on GitHub Pages.

Deploy on GitHub Pages

Before deploying on GitHub Pages you should create a repository for your project and push your code there. You can read how to do this here. After you have your repository on GitHub, run next command from the command line tool:
$ yarn deploy
It will build your project and push resulted files in the gh-pages branch. After success, if open URL https://{github-account-name}.github.io/{repo-name}. In my case it was https://zubialevich.github.io/ticks-to-datetime.

Note that all yarn commands (exсept yarn install) are calling scripts we've added in the package.json file in section scripts.

Summary

Now you have learned how easily and fast create a small web app with TypeScript and put it on GitHub Pages. In this tutorial, I've skipped a part with styles and additional checks, so if you want to see how does the code for final project looks like, visit my GitHub repo.

Comments

Post a Comment

Popular posts from this blog

Pros and cons of different ways of storing Enum values in the database

Lately, I was experimenting with Dapper for the first time. During these experiments, I've found one interesting and unexpected behavior of Dapper for me. I've created a regular model with string and int fields, nothing special. But then I needed to add an enum field in the model. Nothing special here, right? Long story short, after editing my model and saving it to the database what did I found out? By default Dapper stores enums as integer values in the database (MySql in my case, can be different for other databases)! What? It was a surprise for me! (I was using ServiceStack OrmLite for years and this ORM by default set's enums to strings in database) Before I've always stored enum values as a string in my databases! After this story, I decided to analyze all pros and cons I can imagine of these two different ways of storing enums. Let's see if I will be able to find the best option here.

Caching strategies

One of the easiest and most popular ways to increase system performance is to use caching. When we introduce caching, we automatically duplicate our data. It's very important to keep your cache and data source in sync (more or less, depends on the requirements of your system) whenever changes occur in the system. In this article, we will go through the most common cache synchronization strategies, their advantages, and disadvantages, and also popular use cases.

How to maintain Rest API backward compatibility?

All minor changes in Rest API should be backward compatible. A service that is exposing its interface to internal or/and external clients should always be backward compatible between major releases. A release of a new API version is a very rare thing. Usually, a release of a new API version means some global breaking changes with a solid refactoring or change of business logic, models, classes and requests. In most of the cases, changes are not so drastic and should still work for existing clients that haven't yet implemented a new contract. So how to ensure that a Rest API doesn't break backward compatibility?