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:
- Typescript (language)
- React (presentation)
- Hapi (api)
- Mongodb, Mariadb, Neo4j (aggregate/relational/graph databases)
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).
- nodejs https://nodejs.org/en/ (version 5) including npm
- install semver globally
https://github.com/npm/node-semver
sudo npm install -g semver
(I found that this was required for some modules) - install typescript node module globally, and the sublime text typescript plugin
http://blogs.msdn.com/b/typescript/archive/2015/06/05/developing-in-typescript-on-a-mac-with-sublime.aspx
sudo npm install -g typescript
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.