home | email: henrik@bechmann.ca | business website: internetcommons.ca | current projects | about

Typescript/React in a Vagrant/Virtualbox environment

In this blog

Strategy

When choosing a software stack, there are two main considerations. First of course, the stack has to support the purpose of the build. But as important for the developer, the stack should, if possible, anticipate the near future. The idea is to choose a stack which will remain somewhat relevant for a reasonable period. In the software business that's a couple of years; a year or two more if you're lucky. A little stability affords the opportunity to put more effort into actual development, investigating related technologies (such as planning or design), and maybe even some social time.

When a commitment is made, if the project is demanding, then technology can fly by in the meantime. So even if you get ahead once in a while, you end up slightly behind. Next time around, you try to leapfrog slightly ahead again.

So, I've recently selected a stack for a new project, which in my opinion fits that bill. It's a javascript stack, with the following key components:

Thanks to Typescript, this positions the stack to take advantage of Angular 2 if that becomes as productive as it promises to be. And of course aggregate and graph databases are surging in uptake.

Pretty good. We'll see. Now for the work of building the dev environment, and digesting the learning curves.

Step one: Typescript

First, Typescript. This language comes with a great pedigree, having been developed by Anders Hejlsberg, the developer of Turbo Pascal, Delphi, and C#. It's a modern superset of Javascript, and as such can both co-exist with Javascript, and transcompile down to whatever current version is extant. This adds a pre-processing step to the development workflow.

What follows are some implementation notes for this part of the stack.

Implementation

The first decision was to choose between Webstorm (an IDE) and Sublime Text (a code editor). I selected the latter, because it is a simpler, more performant environment, while still being well supported and popular. It turns out that Sublime Text 3 (still in beta, and unlike version 2 requiring a $70US registration) was the best choice, as the the Typescript plugin for this version supports more features (such as on demand error listings).

I use VirtualBox/Vagrant rather than Homebrew for my local machine dev environment, because it isolates more dev components (in the virtual machines), and so keeps my base machine much cleaner. It's not as popular as Homebrew however (my impression), so there is marginally less support. Even so, I try to push as much as I can into the virtual servers. The salient fact about the virtual machines, is that they share space with the host machine -- local directories 'remotely' mounted by the virtual machines. This allows host access to project source files by productivity tools like code editors, while making the source available on the virtual machine for runtime.

In the case of the Typescript plugin for Sublime Text 3, nodejs/npm had to be installed on the host machine to support the Typescript transpile server. The rest of the supporting tools could be added through the server (with some resource files shared with the host through the server in the shared directories).

This produces a build routine accessible in Sublime 3 ([Ctrl-B]), that requires some additional resources.

On the virtual server, install the typescript definitions manager:

npm install -g tsd

In the project front end directory install the react definitions that may be needed:

tsd init

tsd install react* --save

This creates a tsd.json file. See https://github.com/DefinitelyTyped/tsd

Add the same resources to the backend directory.

Also create a tsconfig.json file in a source code subdirectory (if placed in the project root conflicts arise, which I haven't fully investigated yet), with the following content:

{
    "compilerOptions": {
        "outDir":"../testapp",
        "module": "commonjs",
        "jsx": "react",
        "declaration": false,
        "noImplicitAny": false,
        "removeComments": true,
        "noLib": false,
        "preserveConstEnums": true,
        "suppressImplicitAnyIndexErrors": true
    }
}

The first three are required; the rest are conventions, some of which I haven't fully investigated yet.

The conventions are taken from https://github.com/markogresak/typescript-react-jsx-example, although the other named objects in the tsconfig.json example when tried caused build failure in Sublime Text (with no error message, just presentation of a parameter entry field).

Obviously react has to be installed (from the project directory of the server): https://facebook.github.io/react/docs/getting-started.html

npm install --save react react-dom browserify  

Now .ts (typescript) and .tsx (both typescript and jsx) files can be written and transpiled, with the output generated into a separate target build subdirectory of .js files.

There's a critical addition required in the nginx config file for this server to avoid a VirtualBox bug (\u0 characters added to the end of source code files copied to the browser):

sendfile off;  

Otherwise uncaught syntax error; unexpected token illegal error shows up in the browser.

Directory structure

The host directory www is mounted in the virtual server as \var\www\, and is shared between the local host and the virtual server.

├─ Vagrantfile
└─ www/
   ├─ dev.resources/ (backend)
   │  ├─ index.js
   │  ├─ node_modules/
   │  |  └─ ...
   │  ├─ package.json
   │  ├─ testbackend/
   │  |  └─ ...
   │  ├─ testserver.js
   │  ├─ ts/
   │  |  └─ ...
   │  ├─ tsd.json
   │  └─ typings/
   │     └─ ...
   └─ dev.budgetcommons.ca/ (frontend)
     ├─ gulpfile.js
     ├─ index.html
     ├─ node_modules/
     │  └─ ...
     ├─ package.json
     ├─ testapp/
     │  └─ ...
     ├─ ts/
     │  ├─ test.ts
     │  ├─ testtsx.tsx
     │  ├─ tsconfig.json
     │  └─ ...
     ├─ tsd.json
     └─ typings/
        └─ ...

dev.resources is the back end, accessed by a reverse proxy through hapijs.

dev.budgetcommon.ca is the front end, essentially the static assets.

ts holds the typescript source files and the tsconfig.json file.

testapp holds the output (.js files) of the build of the ts content.

typings holds the typescript type definitions. This is currently accessed as required through a /// <reference path="../typings/react/react.d.ts" /> statement at the head of the .tsx file. There are config alternatives to this statement developing, and I'm investigating.

Test code

Here's some random test code (loosely based on some test code from a Minko Gechev blog:

In ts/testtsx.tsx:

/// <reference path="../typings/react/react.d.ts" />
/// <reference path="../typings/react/react-dom.d.ts" />
import * as React from 'react';  
import * as ReactDom from 'react-dom';

class DemoProps {  
    public name: string;
    public age: number;
}

class Demo extends React.Component<DemoProps, any> {  
    private foo: number;
    constructor(props: DemoProps) {
        super(props);
        this.foo = 42;
    }
    render() {
        return <div>Hello world!! {this.props.name}</div>
    }
}

ReactDom.render(<Demo age={65} name="Henrik"/>, document.getElementById('container'));

Run the build routine from Sublime Text 3 (Ctrl-B).

In testapp/index.html:

<!DOCTYPE html>  
<html>  
    <head><title>TypeScript Greeter</title></head>
    <body>
        <div id="container"></div>
        <!-- // <script src="greeter.js"></script> -->
        <script src="bundle.js"></script>
    </body>
</html>

Then in testapp/ from the vm:

browserify testtsx.js -o bundle.js

Eventually building will happen automatically triggered by some watch code.

Load the page (localvmdomain/testapp), and you should see 'Hello world!! Henrik'.

Issues

  • There is a conflict reported when tsconfig.json is placed in the front end root directory.
  • Investigate CompilerOption details.
  • Investigate source example tsconfig.json options, and why most fail when copied and pasted to test setup.
  • Find alternative method of configuring reference path= for typescript type definitions required for source files.

Next steps

This typescript configuration is the first step of establishing a 'steel thread' (messages from the ui through the api to the databases and back) for this stack.

I'll blog about the other pieces as I work them out.