18年07月24日

輕量版NodeJS Express TypeScript Starter

這是一個我常用的TypeScript project設定,當中包括了一些基本功能:

  • Express Server + body parser
  • TypeScript Compile
  • 比較寬鬆的 Tslint
  • Nodemon 自動重啟伺服器
  • Module Alias 方便 import 的工具
  • Mocha Unit Testing
  • Production 和 Development 的 Build Script

安裝與運行

先下載項目跟模組

Copy Code

git clone https://github.com/auphone/node-typescript-starter.git
cd node-typescript-starter
npm install

打開兩個terminalcmdshell,各自執行

Copy Code

npm run watch-ts

以及

Copy Code

npm serve

最後打開瀏覽器測試API http://localhost:3000/api/env

全部指令

指令功能
npm watch-ts監測並compile src內的TypeScript原碼
npm run serve執行Nodemon,與npm watch-ts同時使用
npm start直接運行dist/,需要先進行Compile
npm run testUnit test所有src/**/*.spec.ts
npm run buildDev環境的Build
npm run build-prodProduction環境的Build

Notes

Tslint

這是我的tslint.json設定,我覺得用tslint --init產生出來的預設設定太嚴緊了,所以修改了一下

Copy Code

{
  "rules": {
    "class-name": true,
    "comment-format": [true, "check-space"],
    "indent": [true, "spaces"],
    "one-line": [true, "check-open-brace", "check-whitespace"],
    "no-var-keyword": true,
    "quotemark": [true, "single", "avoid-escape"],
    "semicolon": [true, "always", "ignore-bound-class-methods"],
    "whitespace": [
      true,
      "check-branch",
      "check-decl",
      "check-operator",
      "check-module",
      "check-separator",
      "check-type"
    ],
    "typedef-whitespace": [
      true,
      {
        "call-signature": "nospace",
        "index-signature": "nospace",
        "parameter": "nospace",
        "property-declaration": "nospace",
        "variable-declaration": "nospace"
      },
      {
        "call-signature": "onespace",
        "index-signature": "onespace",
        "parameter": "onespace",
        "property-declaration": "onespace",
        "variable-declaration": "onespace"
      }
    ],
    "no-internal-module": true,
    "no-trailing-whitespace": true,
    "no-null-keyword": true,
    "prefer-const": true,
    "jsdoc-format": true
  }
}

Paths設定

在TypeScript設定paths就可以解決這個問題…

Copy Code

import { app } from "../../../app";

這是我的tsconfig.json,因為我同時有開發Angular所以盡量使用相似的設定

Copy Code

{
    "compilerOptions": {
      ...
        "paths": {
            "@app/*": ["*"],
            "@env/*": ["environments/*"]
        }
    }
}

設定好了以後…

Copy Code

// 以前
import * as someModule from '../../modules/someModule';

// 現在我們可以寫成這樣
import * as someModule from '@app/modules/someModule';

然後我還會在每層資料夾設定一個index.ts(barrel),再在裡面export *所有需要的模組

Copy Code

export * from './server.model';

雖然好像比較多工序,但是以後我只要這樣寫就可以取得所有子function了,但這也有一個缺點就是不能有重覆的命名…

Copy Code

import * as fromModels from '@app/models/';

Module Alias

雖然TypeScript有paths的功能,但是在NodeJS我們不會用WebPack,所以在compile的時候還是要自行添加一些設定,這邊我就用了module-alias這個模組,為了方便在不同環境和unit test的時候使用,我還會把它分拆成一個獨立模組,而設定就跟TypeScript的paths指到一樣的地方

Copy Code

import * as alias from 'module-alias';
import * as path from 'path';

export const init = () => {
  alias.addAliases({
    '@app': __dirname,
    '@env': path.resolve(__dirname, './environments')
  });
};

Unit Testing

Unit Testing通常有幾種結構,我個人比較喜歡把.spec與模組放到同一個目錄亦即src內,也有人喜歡把它們全放在test/內,都是因人而異,mochachai也是個人喜好而採用的

Copy Code

import { expect } from 'chai';
import * as path from 'path';

// Init Alias
import * as alias from '../alias';
alias.init();

import * as fromServer from './server.model';

describe('GET Environment', () => {
  it('should be equal to dev', () => {
    return expect(fromServer.getEnv()).eq('dev');
  });
});

Build

預設有兩個指令,Dev伺服器的npm run build和Production伺服器的npm run build-prod,這邊只做了一個複製environment.prod.tsenvironment.ts的動作,但這可以說是必需要的

後記

由於我經常會做很多Tech的試驗,現在TypeScript已經不能像以前JS那麼簡單了,所以Scripting和不小POC我都還是會用CoffeeScript編寫,而真正有發展的項目才會用到TypeScript…

(*゚ー゚)