TypeScript + webpackでサーバサイドもTypeScriptしようぜのはなし

主はおっしゃいましたwebpackはフロントエンドのjavascriptにだけ与えられたものではないと。よろしい!ではサーバサイドもTypeScriptでwebpackでes5(or es6 or es2015(まさかり対策))にしようではないか。

* 今回のサンプルは[こちら](https://github.com/JumpeiArashi-blog/serverside-typescript-with-webpack/)に保存してありますm(\_ \_)m

サーバサイドnodejsにも型が欲しかった
----

そもそも、サーバサイドなら別に他の言語を使いなさいというのはもっともですが、IOレイヤーの処理のひとつにスクレイピングでデータを取得するという部分があって(もちろんseleniumでもいいんだが)、そいつを[nightmare](https://github.com/segmentio/nightmare)で書いていたので、これはもうwrapするレイヤーもnodejsで行くしかないなっという背景であります。

ただせっかくですし、サーバサイドですし、割りときっちりした型がほしいなぁと思いまして。それでTypeScriptをサーバサイドで使ってwebpackでbuildしようという試みです。

Building with webpack in server side
----

TypeScriptに進む前にまずはサーバサイドnodejsをwebpackでひとつのindex.jsとしましょう。適当に`npm init`して、`npm install --save-dev webpack`(ぼくはglobal installを推奨したくないので、local installでpathを解決する方向でいきます。)
`src/index.js`に簡単な関数を指定して`module.exports`しただけのものが、[こちら]( https://github.com/JumpeiArashi-blog/serverside-typescript-with-webpack/commit/9ed73b0e9f946c1ea4f7595aa28c77a120514ffc)。まだこの段階ではただのwebpackを使ったbuildです。

Using nodejs modules
----

次に適当なnodejsのmoduleを使ってみます。ブラウザで動かせない且つ、今回のmodelに合わせるために、nightmareを使用します。`npm installe nightmare --save`します。んでもって、model層の処理として簡単なdataを取得する処理を書きます。

```javascript
//src/models/scraper.js
'use strict';

const Nightmare = require('nightmare');

class HtmlContent {
  constructor(url) {
    this.url = url;
  }

  getContent() {
    new Nightmare()
      .goto(this.url)
      .wait(5000)
      .evaluate(() => {
        return document.querySelector('body').innerHTML;
      })
      .end()
      .then((result) => {
        return result;
      });
  }
}
module.exports = HtmlContent;
```

んでもって、exportsされたmodelをそのままindex.jsにてexportsします。

```javascript
//src/index.js
'use strict';

const Scraper = require('./models/scraper');

module.exports = {
  Scraper: Scraper
};
```

ここまでの処理が[こちら](https://github.com/JumpeiArashi-blog/serverside-typescript-with-webpack/commit/964705be6bcc6425d10e51776d7e472bcb6010e4)。

Building nodejs program by webpack
----

ここでおもむろにwebpackでbuildしてみる。

```bash
$(npm bin)/webpack
Hash: d86c13400a0572da64bc
Version: webpack 1.12.15
Time: 23ms
   Asset     Size  Chunks             Chunk Names
index.js  1.41 kB       0  [emitted]  main
    + 1 hidden modules
```

build自体は通る。しかし、ここでこんな風に成果物をrequireしてみると...

```bash

$node
> var index = require('./dist/index')
ReferenceError: nightmare is not defined
```

nightmareがundefinedだと。ﾌｧｯ??っと言いたい。requireしてるだけやんと。(実際ぼくはいいたくなった) この原因はmoduleの解決がdefaultだと`var`という値が設定されていてglobal変数から解決しようとするから起こっています(ということが調べていたらわかった)。っというわけで、moduleの解決にcommonjsを使ってみましょう。(やっとwebpack.config.jsに辿りつけた（；^ω^）)

```javascript
//webpack.config.js
'use strict';

const path = require('path');

module.exports = {
  target: 'node',
  externals: /^(?!^\.\/)/,
  context: path.join(__dirname, 'src'),
  entry: './index.js',
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'index.js',
    libraryTarget: 'commonjs2'
  },
  resolve: {
    extensions: ['', '.js']
  }
};
```

ここで特に抑えておきたいのは`libraryTarget: 'commonjs2'`と、`externals: /^(?!^\.\/)/`でありんす。

### libraryTarget

こいつは、さっきのdefaultのvarをcommonjsに差し替えるってやつで、moduleの解決をcommonjsで解決してくれます。なのでこのオプションを設定すると`nightmare is not defined`がでなくなるます。

### externals

externalsは除外対象なので、buildの対象外って感じ。今回はnode_modulesをnpmによって解決したいので、*./で始まってないmodule*をexcludeしてます。

さて、ここまでの成果物が[こちら](https://github.com/JumpeiArashi-blog/serverside-typescript-with-webpack/commit/df84b48a902b088f46281b565b330381346e725a)。

TypeScript
----

ふぅー、やっとtypescriptにたどりつけましたね。まずは、typescript環境が欲しいので`npm install --save-dev typescript webpack ts-loader typings`しましょう。

－－－－－－－－－－　TypeScript化　－－－－－－－－－－－ 

1. `$(npm bin)/typings init`でd.tsをgetできる環境を用意しときましょう。
2. `$(npm bin)/typings install nightmare --save --ambient`nightmareのd.tsを取得。
3. `mv src/index.js src/index.ts && mv src/models/scraper.js src/models/scraper.ts`
4. ごにょごにょっとtypescriptしますw

まぁ、このTypeScript化には深い意味はないですｗ

webpack.config.js + ts-loader
----

ts-loaderから.tsファイルを読み込みます。単純に`tsc --init`してts-loaderを使うとバラバラバラっとたくさんエラーが出ます。

```bash
$(npm bin)/webpack
ERROR in /Users/JumpeiArashi/tmp/serverside-typescript-with-webpack/typings/main/ambient/nightmare/index.d.ts
(121,3): error TS2300: Duplicate identifier 'export='.

ERROR in /Users/JumpeiArashi/tmp/serverside-typescript-with-webpack/typings/browser/ambient/nightmare/index.d.ts
(121,3): error TS2300: Duplicate identifier 'export='.

ERROR in ./models/scraper.ts
(17,8): error TS2339: Property 'end' does not exist on type 'Nightmare'.
```

nightmae.endが定義されてないのは残念ですが、typings/browser配下のindex.d.tsもbuild対象になってますねぇ。。。ts-loader側にexcludeしたいところですが、[ts-loaderのREADME](https://github.com/TypeStrong/ts-loader/blob/master/README.md)を見ると、tsconfig.jsonに書けと書いてありますねぇ。っというわけで、tsconfig.jsonに

```json
{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "noImplicitAny": false,
        "sourceMap": false
    },
    "files": [
      "src/index.ts",
      "typings/main.d.ts"
    ]
}
```

filesを指定する。ブラウザで動くようには作れてませんが、nightmareをブラウザで動かそうって人はいないと思うってのと、今回はサーバサイドTypeScript + webpackが目的なのでこれにて完了。.。o○(Nightmareのambient moduleにPR出そうかな)

----

2016/11/25 追記

### Nightmare.end()も定義された(というかした！)

半年が経過したので、[nightmareを2.xに上げました](https://github.com/JumpeiArashi-blog/serverside-typescript-with-webpack/pull/1)。またdevDependenciesもupgradeさせました。んでもって[typed-nightmare](https://github.com/JumpeiArashi/typed-nightmare)を書いてPRしました。これで`.end`の未定義エラーも発生しませんね！

なお前のrepositoryのworking directoryが残ってる人は(そんな人ほとんどいないと思いますが)、お決まりの `rm -fr ./node_modules && npm install && typings install` をお昼寝しながらやってくださいね。