🔧

expressの中でwebpack-dev-serverをミドルウェアとして使う。

2022/01/20に公開

概要

webpack-dev-server を express のミドルウェアとして使えるようにします。

環境

  • node.js 17.4.0
モジュール
  • express 4.17.2
  • webpack-dev-middleware 5.3.0
  • webpack-hot-middleware 2.25.1

devServer を立ち上げるフロントエンドは完全に別のプロジェクトです。
ディレクトリ構造はこんな感じ

rootDir
    │  package.json
    │  tsconfig.json
    │  webpack.config.ts
    │
    └─src
        │  index.html
        │  index.tsx
        │
        └─pages
                _App.tsx

ここにあるwebpack.config.tsの内容で devServer を立ち上げます。

webpack.config.ts
import webpack from 'webpack'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import path from 'path'

const config: webpack.Configuration = {
    name: 'renderer',
    target: 'web',
    context: path.resolve(__dirname),
    entry: ['./src'],
    output: {
        path: path.resolve(__dirname, 'dist'),
        publicPath: '/',
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.jsx'],
    },
    module: {
        rules: [
            {
                test: /\.tsx$|\.ts$/,
                exclude: /node_modules/,
                loader: 'ts-loader',
            },
        ],
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html',
            scriptLoading: 'blocking',
            inject: 'body',
            minify: false,
        }),
    ],
    devtool: 'source-map',
}

export default config

方法

まずは必要なモジュールをインストールします。

yarn add -D webpack webpack-dev-middleware webpack-hot-middleware
または
npm i -D webpack webpack-dev-middleware webpack-hot-middleware

とりあえずコード全体

app.ts
import express from 'express'
import path from 'path'
import webpack from 'webpack'
import webpackDevMiddleware from 'webpack-dev-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware'

const useDevServer = async (app: express.Express, cwd: string) => {
    const config = (await import(path.resolve(cwd, 'webpack.config.ts'))).default as webpack.Configuration

    config.entry = ['webpack-hot-middleware/client?reload=true', './src']
    config.plugins?.push(new webpack.HotModuleReplacementPlugin())

    const compiler = webpack(config)

    app.use(
        webpackDevMiddleware(compiler, {
            publicPath: config.output?.publicPath,
        }),
    )
    app.use(
        webpackHotMiddleware(compiler, {
            log: false,
            path: '/__webpack_hmr',
            heartbeat: 10 * 1000,
        }),
    )
}

const app = express()

if(process.env.NODE_ENV === 'development') useDevServer(app, process.argv[2] || '.')

app.listen(3030)


順番に見ていきます。

import express from 'express'
import path from 'path'
import webpack from 'webpack'
import webpackDevMiddleware from 'webpack-dev-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware'

const app = express()

//省略

app.listen(3030)

ここでは、モジュールをインポートして express サーバーを立ち上げています。


const useDevServer = async (app: express.Express, cwd: string) => {
	const config = (await import(path.resolve(cwd, 'webpack.config.ts'))).default as webpack.Configuration

	config.entry = ['webpack-hot-middleware/client?reload=true', './src']
	config.plugins?.push(new webpack.HotModuleReplacementPlugin())

	const compiler = webpack(config)

	app.use(
		webpackDevMiddleware(compiler, {
			publicPath: config.output?.publicPath,
		})
	)
	app.use(
		webpackHotMiddleware(compiler)
	)
}

ここで webpack-dev-server を立ち上げています。
今回は一つの関数としてまとめました。引数に express のインスタンスとプロジェクトのルートディレクトリを渡しています。
詳しく見ていきます。


const config = (await import(path.resolve(cwd, 'webpack.config.ts'))).default as webpack.Configuration

config.entry = ['webpack-hot-middleware/client?reload=true', './src']
config.plugins?.push(new webpack.HotModuleReplacementPlugin())

const compiler = webpack(config)

まずimportを使ってwebpack.config.tsをインポートしています。
typescript ですが、何もしないでインポートできます。

そして config を少しいじります。
entry にwebpack-hot-middleware/client?reload=trueを、plugins にwebpack.HotModuleReplacementPlugin()を追加します。
これをしないとリロードがうまく動きません。

最後にwebpack(config)で compiler を作ります。


app.use(
	webpackDevMiddleware(compiler, {
		publicPath: config.output?.publicPath,
	})
)
app.use(
	webpackHotMiddleware(compiler)
)

次にミドルウェアを追加します。
webpackDevMiddlewareにはコンパイラとpublicPathにconfigで指定したpublicPathを渡します。
webpackHotMiddlewareにはコンパイラのみを渡します。


これで一度実行してみます。

yarn cross-env NODE_ENV=development ts-node src/app.ts /path/to/project

すると見慣れたwebpack-dev-serverのログが表示されます。

webpack built renderer c4a2c952cc178e81b877 in 8129ms
assets by path render/*.ts 66 bytes
  asset render/_App.d.ts 66 bytes [emitted]
assets by path *.ts 12 bytes
  asset index.d.ts 12 bytes [emitted]
  
  .....省略

この状態で指定したポートにアクセスすると...

ページが表示されました!!
コンソールにも[HMR] connectedと表示されているので成功ですね。


webpack5でリロードが効かない問題

2022/01/20現在webpack-hot-middlewareはwebpack5においてリロードが効かない問題を抱えています。

このようにIgnored an update to unaccepted module **** と表示され、更新が無視されてしまいます。

これの対処法としてwebpack.config.tsでentryに指定したファイルに以下の行を追加します。

//@ts-ignore
if (module.hot) module.hot.accept()

Discussion