はじめに
この記事はLinkbal Advent Calendar 2018の3日目の記事です。昨日は弊社CTOの開発組織の育て方 – エンジニア3人から30人へという、組織と事業が拡大していくことによる技術や採用の変遷のお話です。ぜひご一読いただければ幸いです。
リンクバルに新卒入社して2年目の水野(キンチキ)です。今年の10月からエンジニアではなくビジネス側(?)になったのですが、プライベートでも仕事でもコードはちょくちょく書いているので、もちろんアドベントカレンダーも書きます!
今回はTypeScript+Webpack+Node(Express)でNodeの再起動を自動で行う方法がわからず、あまり良いとは思わないながらも解決できた方法について書きます。
ちなみにリンクバルはTyepScriptを採用していない(!)です。学習コストは仕方ないとしても、型があるおかげでバグは間違いなく減ると思います。あと採用面での効果は絶対に+のはず(水野予測)。
いずれ使ってくれるといいなあ。
パッケージのインストール
最初の工程から説明します。
必要なパッケージを以下のコマンドでインストールします。
yarn add
のオプションの-D
は--dev
と同じです。
lintは省いています。
$ mkdir sample && cd $_
$ yarn init -y # 初期化
$ yarn add -D @types/express typescript ts-loader webpack webpack-cli nodemon webpack-node-externals # パッケージインストール(devDependencies)
$ yarn add express # パッケージインストール(dependencies)
# npmを使う場合
$ npm init -y
$ npm i -D ...
$ npm i -S express
expresだけdependenciesにしていますが、devDependenciesでもいいかもしれません。
私はdependenciesにしないといけないパッケージがよくわかっていないです。。。
インストールしたパッケージの用途です。
- @types/express: express用の型定義
- typescript: TypeScript
- ts-loader: webpackがTypeScriptをコンパイルできるようになるやつ
- webpack, webpack-cli: webpack周り
- nodemon: node再起動
- webpack-node-externals: WARNING抑制(必須ではない)
tsconfigの作成
tscでtypescriptの設定ファイル(tsconfig)を作成します。
$ npx tsc --init
tsconfigを以下のようにします。
これは開発用なので、本番環境用を作成する際は別途ファイル作成するなどして内容を適宜変更してください。
{
"compilerOptions": {
"sourceMap": true,
"noImplicitAny": true,
"module": "commonjs",
"target": "es5",
"lib": [
"es2018",
"dom"
],
"moduleResolution": "node",
"removeComments": true,
"strict": true,
"noUnusedLocals": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"strictFunctionTypes": false
}
}
webpack.configの作成
touch webpack.config
やエディタでファイルを作成し、以下のようにしてください。
エントリーポイントは./src/server.ts
とし、最終的にビルドされるファイルはdist/server.js
としています。
watch
をtrueにしておくことでファイルの変更を検知します。
こちらも開発用なので、本番環境用を作成する際は別途ファイル作成するなどして内容を適宜変更してください。
const path = require('path');
const nodeExternals = require('webpack-node-externals');
module.exports = {
watch: true,
mode: 'development',
entry: './src/server.ts',
target: 'node',
externals: [nodeExternals()],
devtool: 'inline-source-map',
module: {
rules: [
{
loader: 'ts-loader',
test: /\.ts$/,
exclude: [
/node_modules/
],
options: {
configFile: 'tsconfig.json'
}
}
]
},
resolve: {
extensions: ['.ts', '.js']
},
output: {
filename: 'server.js',
path: path.resolve(__dirname, 'dist')
}
};
nodemon.jsonの作成
touch nodemon.json
などで作成し、以下のようにしてください。
watch対象をwebpackのoutputに指定しているディレクトリ(この場合dist
)にするのがポイントです。こうすることで、コンパイル後に自動でnodeを再起動してくれます。
{
"restartable": "rs",
"watch": [
"dist"
],
"env": {
"NODE_ENV": "development"
}
}
expressの実装
src
ディレクトリを作り、そこにserver.ts
を作成します。/
へのgetリクエストに文字列を返す処理だけ書いておきましょう。
$ mkdir src
$ touch src/server.ts
import * as express from 'express';
const app: express.Application = express();
const router: express.Router = express.Router();
const port = process.env.PORT || 3000;
router.get('/', (req: express.Request, res: express.Response) => {
res.send('Hello, Express with TypeScript!');
});
app.use('/', router);
app.listen(port, () => {
console.log(`Listening at http://localhost:${port}/`);
});
最後にpackage.jsonにscriptsを追加しましょう。
webpackとnodemonを&
で繋いで同時起動しています。tsファイルが変更されたらwebpackがコンパイルし、nodemonがnodeを再起動します。
{
"name": "express-test",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"dev": "nodemon dist/server.js & webpack --config webpack.config.js"
},
"devDependencies": {
"以下略"
}
ここまできたら yarn run dev
で動かすことができます。
ファイルを編集したらnodeが再起動し、リロードしたら反映されていますね!
おわりに
この方法だとエラー箇所がわかりづらかったり、webpackがクラッシュしたらプロセスが残ったりしますが、あまりいい方法が見つからなかったのでいったんこれで開発しています。この方法は手軽なのがウリです。webpack-dev-serverでうまいことホットリロードできると思いましたが、やり方がわかりませんでした。この方法でも支障は特にありません。
プロセスが残っていたら以下のコマンドで一括killしましょう。
kill -9 `ps -ef | grep nodemon | awk '{print $2;}'`
リンクバルでTypeScriptが採用される日を待っています(強い意思はない)。
参考
このページも結構参考にしたのですが、今は見れなくなっています。
https://dev.to/briandgls/using-typescript-with-express--e0k
ts-nodeを使ったりすればホットリロードできそうですね。