輕量版 NodeJS TypeScript Starter

2018-07-24

今天分享一下我的 TypeScript project 設定,當中包括了一些基本功能:

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

安裝和運行

先從 git 下載項目然後安裝模組

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

打開兩個 terminalcmdshell,執行以下語句

第一個 terminal

$ npm run watch-ts

另一個 terminal

$ npm serve

最後打開瀏覽器測試 API

http://localhost:3000/api/env

全部指令

npm run watch-ts

  • 監測並 compile src 內的 TypeScript 原碼

npm run serve

  • 執行 Nodemon,與 npm watch-ts 同時使用

npm start

  • 直接運行 dist/ ,需要先進行 Compile

npm run test

  • 執行 src/**/*.spec.ts 目錄下的 Unit test

npm run build

  • Dev 環境的 Build

npm run build-prod

  • Production 環境的 Build

Tslint

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

{
  "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 可以解決 import 很多層目錄時產生出的 ../,例如

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

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

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

設定好了以後我們就可以在任何地方用 @app 指向根目錄了

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

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

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

export * from "./server.model";

然後我們就可以這樣做

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

fromModels.doSomething();

Module Alias

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

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

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

Unit Test

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

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 的動作

後記

由於我經常會做很多 POC,但 Typescript 不像 JS 一樣只要一個 .js 那麼簡單,所以建立一個 starter 的確讓我省了不少時間