Convert Aurelia skeleton navigation to Typescript

Not sure how long these instructions will work (because Aurelia is not stable yet).
Took me a while to figure this all out.. might save you some time!

This guide is outdated Use the https://github.com/aurelia/skeleton-navigation, see the directories skeleton-typescript-asp.net5 or skeleton-typescript

Just give me the code

Optional there is a git repository https://github.com/eriklieben/skeleton-navigation-typescript that is transformed. Be aware that the hacks to the jspm files still need to be made.

git clone https://github.com/eriklieben/skeleton-navigation-typescript.git

cd skeleton-navigation-typescript

npm install

jspm install -y

tsd install  

Apply hacks found in sections:

  • Requires small modification :-(
  • Fixing the build errors - core-js not having a default export

Performing the steps yourself: Getting the code & default install

Get the latest version from https://github.com/aurelia/skeleton-navigation

Clone the git repository to your machine:

git clone https://github.com/aurelia/skeleton-navigation.git  

Change directory to the skeleton-navigation folder:

cd skeleton-navigation  

Install the node packages required:

npm install  

Install the jspm pacakges required:

jspm install -y  

Adding typescript

Install the gulp typescript compiler scripts

npm install gulp-typescript --save-dev  

Rename all the javascript files to typescript in the src and test folder (using PowerShell cmdline)

get-childitem src\, test\ -Recurse -Filter "*.js" | rename-item -NewName {$_.name -replace ".js",".ts"}  

Open up build\paths.js and change the lines

source: appRoot + '**/*.js',  
e2eSpecsSrc: 'test/e2e/src/*.js',  

to

source: appRoot + '**/*.ts',  
e2eSpecsSrc: 'test/e2e/src/*.ts',  

and add the following 2 extra paths:

  jspmDef: "jspm_packages/**/*.d.ts",
  typingDef: "typings/**/*.d.ts",

Install the typescript definition manager

This tool is used to install typescript definitions from http://definitelytyped.org/

npm install tsd --save-dev  

Create the tsd.json config file, which will store installed typings and configuration for tsd:

tsd init  

When using git as source control, open up the .gitignore and add the typings path. We don't want the typings in source control, because we can retrieve them by performing tsd install.

Install the following 2 definitions (they are required to get working Aurelia definition files)

tsd install core-js --save  
tsd install whatwg-fetch --save  

Requires small modification :-(

The core-js lib contains the Promise definition and the es6-promises definition contains it, this gives build errors about being defined twice.

Open up the typings folder and remove the folder es6-promise and open the file whatwg-fetch\whatwg-fetch.d.ts and replace the es6-promise reference with /// <reference path="../core-js/core-js.d.ts" />. And remove the reference to /// <reference path="es6-promise/es6-promise.d.ts" /> from the typings\tsd.d.ts file.

Modify the build script to create Typescript files

Open up the file build\tasks\build.js.

Remove the following lines

var to5 = require('gulp-babel');  
var compilerOptions = require('../babel-options');  
var assign = Object.assign || require('object.assign');  

and replace them with

var ts = require("gulp-typescript");  
var tsProject = ts.createProject('tsconfig.json');  

This loads the gulp-typescript task and creates a TypeScript project with the settings from the file tsconfig.json.

We still don't got our Typescript configuration file yet, rename the jsconfig.json file to tsconfig.json by running copy jsconfig.json tsconfig.json and add the following content:

{
    "compilerOptions": {
        "target": "ES5",
        "module": "system",
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
         "noExternalResolve": true,
         "outDir": "dist/"
 }
}

In the build-system task, replace the line .pipe(to5(assign({}, compilerOptions, {modules:'system'}))) with .pipe(ts(tsProject)) to compile typescript code, instead of running Babel to compile the ES-* JavaScript code.

Running this will still give some errors, because we didn't include any TypeScript definition files (d.ts) for the files we don't own/ 3rd party libraries. Fix them by including the typescript definitions from Aurelia that are placed in the jspm folder and the typings we've installed from DefinitelyTyped by modifing the following lines (still in the file build\tasks\build.js):

gulp.task('build-system', function () {  
return gulp.src(paths.source)  

with

gulp.task('build-system', function () {  
return gulp.src([paths.jspmDef, paths.typingDef, paths.source])  

Fixing the build errors

Core-js not having a default export

Open up jspm_packages\github\aurelia\fetch-client@0.2.0\aurelia-fetch-client.d.ts and replace import 'core-js' with import * as core from 'core-js' and add the following to remove most of the typescript warnings:

/// <reference path="../../../../typings/core-js/core-js.d.ts" />
/// <reference path="../../../../typings/whatwg-fetch/whatwg-fetch.d.ts" />

It will still complain about the following 2 types that it can't find:

error TS2304: Cannot find name 'BufferSource'.  
error TS2304: Cannot find name 'URLSearchParams'.  

I have no clue what they are, so just added interfaces for them in the definition:

interface BufferSource {}  
interface URLSearchParams {}  

Next fix the same core-js issue in the bootstrapper file:
Open up jspm_packages\github\aurelia\bootstrapper@0.17.0\aurelia-bootstrapper.d.ts and replace import core from 'core-js'; with import * as core from 'core-js';

Fix the code in the src/ folder

Open up app.ts and change:

export class App {

  configureRouter(config, router) {

to:

/// <reference path="../jspm_packages/github/aurelia/router@0.12.0/aurelia-router.d.ts" />

import * as router from 'aurelia-router';  
export class App {

  private router: router.RouteConfig;

  configureRouter(config, router: router.RouteConfig) {

Open up blur-image.ts and change:

export class BlurImageCustomAttribute {  
  constructor(element){
    this.element = element;
  }

to:

export class BlurImageCustomAttribute {  
  constructor(private element){
  }

Open up child-router.ts and change:

export class ChildRouter {  
  heading = 'Child Router';

  configureRouter(config, router) {

to:

/// <reference path="../jspm_packages/github/aurelia/router@0.12.0/aurelia-router.d.ts" />

import * as router from 'aurelia-router';  
export class ChildRouter {  
  heading = 'Child Router';
  private router: router.RouteConfig;

  configureRouter(config, router: router.RouteConfig) {

Open up users.ts and change:

  constructor(http){
    http.configure(config => {
      config
        .useStandardConfiguration()
        .withBaseUrl('https://api.github.com/');
    });

    this.http = http;
  }

to:

  constructor(private http: HttpClient){
    http.configure(config => {
      config
        .useStandardConfiguration()
        .withBaseUrl('https://api.github.com/');
    });
  }

And run gulp watch to see it running/ working :-)

Fixing the tests (todo)

Install the typescript definition files for Jasmine:

tsd install jasmine --save  

Open the file test\unit\app.spec.tsand test\unit\child-router.spec.ts and add /// <reference path="../../typings/jasmine/jasmine.d.ts" /> to the top of the files. Change the code from:

class RouterStub {  
  configure(handler) {

to

class RouterStub {  
  private routes;
  configure(handler) {

Open the file test\unit\users.spec.ts and add

/// <reference path="../../typings/jasmine/jasmine.d.ts" />
import * as core from 'core-js';  

to the top of the fil and change the code from:

class HttpStub {  
  fetch(url) {

to

class HttpStub {  
  private url;
  public itemStub;
  fetch(url) {

TsLint instead of JsLint

JsLint is the static code analysis tool for JavaScript files used in the build script. We would probably focus more on building Typescript code (because our JavaScript is generated). The TsLint build task solves this requirement for you, it's static code analysis for TypeScript code).

Create a tslint.json file, with for example:

{
  "rules": {
    "class-name": true,
    "curly": true,
    "eofline": false,
    "forin": true,
    "indent": [true, 4],
    "label-position": true,
    "label-undefined": true,
    "max-line-length": [true, 140],
    "no-arg": true,
    "no-console": [true,
      "debug",
      "info",
      "time",
      "timeEnd",
      "trace"
    ],
    "no-construct": true,
    "no-debugger": true,
    "no-duplicate-key": true,
    "no-duplicate-variable": true,
    "no-empty": true,
    "no-eval": true,
    "no-imports": true,
    "no-string-literal": false,
    "no-trailing-comma": true,
    "no-trailing-whitespace": true,
    "no-unused-variable": false,
    "no-unreachable": true,
    "no-use-before-declare": true,
    "one-line": [true,
      "check-open-brace",
      "check-catch",
      "check-else",
      "check-whitespace"
    ],
    "quotemark": [true, "double"],
    "radix": true,
    "semicolon": true,
    "triple-equals": [false, "allow-null-check"],
    "variable-name": false,
    "whitespace": [true,
      "check-branch",
      "check-decl",
      "check-operator",
      "check-separator"
    ]
  }
}

install tslint

npm install gulp-tslint --save-dev  

Open up build\tasks\lint.js and replace the content with:

var gulp = require('gulp');  
var paths = require('../paths');  
var tslint = require("gulp-tslint");

gulp.task('lint', function() {  
  return gulp.src(paths.source)
    .pipe(tslint())
    .pipe(tslint.report('verbose', { reportLimit: 20 }));
});