express-generatorの結果をTypeScriptにリファクタリングしてwebpackでビルドする
TL;DR.
初めに
最近Expressを使うときが多々ある。
generatorがあるから雛形は一瞬で作れる。
当たり前だけど生成されるのはjs。
このままでも良いには良いんだけどTypeScriptで書きたい。
そこで、TSにリファクタリングした上でWebpackでビルドしてみる。
webpackなのはtscでビルドするのがめんどくさいから。
準備
express-generatorをインストールしてプロジェクトを作成するとこまでやっとく。
上のサイトから見ればすぐ。
生成されたpackage.json
のinstallされているものはコピーしておく。
リファクタリング
とりあえず拡張子を全部.ts
にするとこからリファクタリングスタート。
方針としては、tsで動くようにするのとvar
とかやめるのをメインにする。
※所々jsのままかも知れない...
app.ts
生成されたファイルの中でサーバの設定を書いてあるファイル。
一応クラス化して似たような初期化はfunctionにまとめた。
error処理周りはそのまま返却するよう修正。
ソースは下記の通り。
import * as createError from 'http-errors'; import * as express from 'express'; import * as path from 'path'; import * as cookieParser from 'cookie-parser'; import * as logger from 'morgan'; import { router as indexRouter } from './routes/index'; import { router as usersRouter } from './routes/users'; class App { public express: express.Application = express(); constructor() { this.middleWareInit(); this.routerInit(); this.errorHandlerInit(); } /** * middleware系初期化 */ private middleWareInit() { // viewEngineは今回使わないのでスルー // view engine setup // express.set('views', path.join(__dirname, 'views')); // express.set('view engine', 'jade'); this.express.use(logger('dev')); this.express.use(express.json()); this.express.use(express.urlencoded({ extended: false })); this.express.use(cookieParser()); this.express.use(express.static(path.join(__dirname, 'public'))); } /** * router初期化 */ private routerInit() { this.express.use('/index', indexRouter); this.express.use('/users', usersRouter); this.express.get('*', (req, res) => { res.send(`${req.url}にアクセスしたね。`); }); } /** * error handling系初期化 */ private errorHandlerInit() { // catch 404 and forward to error handler this.express.use(function (req, res, next) { // next(createError(404)); res.send(createError(404)); }); // error handler this.express.use(function (err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.send('error'); }); } } // appをexport export default new App().express;
index.ts & users.ts
生成されたファイルの中でルーティング系の設定、処理を書くところ。
ここはサラッと。ただtsに書き換えただけ。
両方共内容は大して変わらないから下のサンプルはindex.ts
。
import * as express from 'express'; const router = express.Router(); /* GET home page. */ router.get('/', function(req, res, next) { res.send('in index'); }); export { router };
www.ts
生成されたファイルの中でサーバの実行ファイル。
こいつを叩くと起動する。
ほぼそのまま。起動するだけだから良いかなと。
/** * Module dependencies. */ import { default as app } from '../app'; import * as debug from 'debug'; import * as http from 'http'; /** * Get port from environment and store in Express. */ const port = normalizePort(process.env.PORT || '3000'); app.set('port', port); /** * Create HTTP server. */ const server = http.createServer(app); /** * Listen on provided port, on all network interfaces. */ server.listen(port); server.on('error', onError); server.on('listening', onListening); /** * Normalize a port into a number, string, or false. */ function normalizePort(val) { const port = parseInt(val, 10); if (isNaN(port)) { // named pipe return val; } if (port >= 0) { // port number return port; } return false; } /** * Event listener for HTTP server "error" event. */ function onError(error) { if (error.syscall !== 'listen') { throw error; } var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port; // handle specific listen errors with friendly messages switch (error.code) { case 'EACCES': console.error(bind + ' requires elevated privileges'); process.exit(1); break; case 'EADDRINUSE': console.error(bind + ' is already in use'); process.exit(1); break; default: throw error; } } /** * Event listener for HTTP server "listening" event. */ function onListening() { var addr = server.address(); var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port; debug('Listening on ' + bind); }
Webpackでビルドする
必要なものはnpm install --save ts-loader typescript webpack webpack-cli
で入れとく。
webpack.config.js
は下記の通り。
実行するwww.ts
をentryにしておく。
出力はまとまっているのでserver.js
という名前にした。
const path = require('path'); module.exports = { mode: 'development', entry: './tsserver/bin/www.ts', target: 'node', module: { rules: [ { test: /\.ts$/, use: 'ts-loader' } ] }, resolve: { modules: [ path.resolve(__dirname, "tsserver"), 'node_modules' ], extensions: [ '.ts', '.js' ] }, output: { // 出力するファイル名 filename: 'server.js', path: path.join(__dirname, 'dist') } };
実行 & 確認してみる
事前にコピったpackage.json
の内容をexpress
コマンドを叩いたフォルダのpackage.json
に戻してインストール。
その後、node server.js
で実行。
結果は画像の通り。
まとめ
念願だったtsでExpressを書くことができた。
正直そもそもtsにする必要があるのか。
webpackでまとめる必要があるのかとかとかあるけど、気にしない方向で。
おまけ
静的ファイル返したい
res.sendFile('ファイル')
でOK。
今回の例だとしたみたいな感じ。
// app.tsのrouterInit内 this.express.use('/index', indexRouter); this.express.use('/users', usersRouter); this.express.get('*', (req, res) => { res.sendFile(path.join(path.resolve(''), './dist/index.html')); // res.send(`${req.url}にアクセスしたね。`); });