公式ドキュメントに沿ってTypescript + Express + React + Webpack を検証

# Reactを実装するドキュメントは見つけられたけど、、
Javascript + Express のものがほとんどで、 TypeScript + Express でやってるドキュメントや前例がこれがなかなか無くて(´Д⊂

# TypeScript公式ドキュメントにReactについてあるやんけ
[React & Webpack · TypeScript](https://www.typescriptlang.org/docs/handbook/react-&-webpack.html)
公式ドキュメントにTypeScriptでReactコンポーネントを表示することが解説されていたので、この手順にExpressでのルーティングを付け加えました。
バカ正直な部分もありますが、スループットするためにここにやったことを１から書き起こします。

# Node.jsのインストール
Node.jsをインストールする際には、下記を参考とさせていただきました。
nodebrewがあるとバージョン管理が楽ですので、入れることをオススメします。
[node.jsのversionを管理するためにnodebrewを利用する - Qiita](https://qiita.com/sinmetal/items/154e81823f386279b33c)
nodebrewを使ってLTS版を知る術は無い（と思う）ので、Node.jsのGithubを確認してください。
[GitHub - nodejs/Release: Node.js Foundation Release Working Group](https://github.com/nodejs/Release)


# ディレクトリ構成
公式ドキュメントに沿って進めたいので、合わせて下記のようなディレクトリ構成にしました。

```
test-dir/
├── dist/
└── src/
    └── components/
```


# イニシャライズ
`test-dir`ディレクトリ直下で、npmパッケージとするためのコマンドを実行します。

```
npm init
```
すると、いろいろ質問されますが、ひとまずエンターをただただ押していって、、

```
Is this OK? (yes)
```
と聞かれたら`y`と打ってエンターキーを押すと`package.json`が生成されます。

```json:package.json
{
  "name": "test-dir",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}
```

# Node.jsの他に必要なパッケージのインストール
今回の検証に必要なパッケージをインストールします。
### Webpack

```
npm install --save-dev webpack webpack-cli
```

### TypeScript

```
npm install --save-dev typescript awesome-typescript-loader source-map-loader
```

### Express

```
npm install --save express @types/express
```

### React


```
npm install --save react react-dom @types/react @types/react-dom
```

# tsconfig.jsonを作成
TypeScriptの設定ファイルである`tsconfig.json`を`test-dir/`直下に作成します。

```json:tsconfig.json
{
    "compilerOptions": {
        "outDir": "./dist/",
        "sourceMap": true,
        "noImplicitAny": true,
        "module": "commonjs",
        "target": "es5",
        "jsx": "react"
    },
    "include": [
        "./src/**/*"
    ]
}
```

# Reactコンポーネントを作成
公式ドキュメントを参考に`Hello`コンポーネントを書きます。

```typescript:src/components/Hello.tsx
import * as React from "react";

export interface HelloProps { compiler: string; framework: string; library: string; }

// 'HelloProps' describes the shape of props.
// State is never set so we use the '{}' type.
export class Hello extends React.Component<HelloProps, {}> {
    render() {
        return <h1>Hello from {this.props.compiler} and {this.props.framework} and {this.props.library}!</h1>;
    }
}
```
`Hello`コンポーネントをインポートする`index.tsx`も作ります。
公式には`Hello`コンポーネントのインポート方法と`"React"`や`"react-dom"`と異なることに注目と書いてありますね。

```typescript:src/index.tsx
import * as React from "react";
import * as ReactDOM from "react-dom";

import { Hello } from "./components/Hello";

ReactDOM.hydrate(
    <Hello compiler="TypeScript" framework="Express" library="React" />,
    document.getElementById("example")
);
```

# ReactコンポーネントをビルドしてレンダリングするHTMLを作成
Reactコンポーネントを表示する`index.html`を作成します。

```html:index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>Hello React!</title>
    </head>
    <body>
        <div id="example"></div>

        <!-- Dependencies -->
        <script src="./node_modules/react/umd/react.development.js"></script>
        <script src="./node_modules/react-dom/umd/react-dom.development.js"></script>

        <!-- Main -->
        <script src="./dist/bundle.js"></script>
    </body>
</html>
```

# Expressのルーターを作成
これはTypeScriptの公式ドキュメントに書いてないのですが、Expressでルーティングする機能を盛り込みたいので`src/`ディレクトリ以下に`server.ts`を作成しました。

```typescript:src/server.ts
var express = require('express');
var app = express();
 
app.use(express.static('./'));
 
app.listen(process.env.PORT || 3000, function () {
  console.log('express app is started.');
});
```

# Webpackの設定ファイルを作成
これも公式ドキュメントに書いてあるのですが、`server.ts`を追加したのでこちらを少し改修しています。

```javascript:webpack.config.js
module.exports = {
  target: "node",
  mode: "development",
  entry: {
    bundle: "./src/index.tsx",
    server: "./src/server.ts"
  },
  output: {
    filename: "[name].js",
    path: __dirname + "/dist"
  },

  // Enable sourcemaps for debugging webpack's output.
  devtool: "source-map",

  resolve: {
    // Add '.ts' and '.tsx' as resolvable extensions.
    extensions: [".ts", ".tsx", ".js", ".json"]
  },

  module: {
    rules: [
      // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
      { test: /\.tsx?$/, loader: "awesome-typescript-loader" },

      // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
      { enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
    ]
  },

  // When importing a module whose path matches one of the following, just
  // assume a corresponding global variable exists and use that instead.
  // This is important because it allows us to avoid bundling all of our
  // dependencies, which allows browsers to cache those libraries between builds.
  externals: {
    react: "React",
    "react-dom": "ReactDOM"
  }
};
```

# 実行
webpackコマンドでビルドします。

```
webpack
```
すると`dist/`以下にビルドされたjavascriptファイルが生成されるので、それをnodeコマンドで実行します。
`localhost:3000`にアクセスすると、Reactコンポーネントが描画されます。

```
node dist/server.js
```

こういうコマンドは個人的に好きくないので、`package.json`にコマンドを登録しました。

```json:package.json
{
  "name": "test-dir",
  ...
  "scripts": {
    "build": "webpack",
    "start": "node dist/server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  ...
}
```
すると下記コマンドで同様なことができます。

```
npm run build
```

```
npm run start
```

# 最終的なディレクトリ構成
```
test-dir/
├── dist
│   ├── bundle.js
│   ├── bundle.js.map
│   ├── server.js
│   └── server.js.map
├── index.html
├── node_modules
│   ├── ...
├── package.json
├── src
│   ├── components
│   │   └── Hello.tsx
│   ├── index.tsx
│   └── server.ts
├── tsconfig.json
└── webpack.config.js
```

# 参考文献
 - [React & Webpack · TypeScript](https://www.typescriptlang.org/docs/handbook/react-&-webpack.html)
 - [Backend Apps with Webpack (Part I)](https://jlongster.com/Backend-Apps-with-Webpack--Part-I)
 - [npm package.json 日本語版 取扱説明書](http://liberty-technology.biz/PublicItems/npm/package.json.html)
 - [Get started Express + TypeScript する - Qiita](https://qiita.com/IgnorantCoder/items/c9b79dbab8c1a34b769f)