記事に従ってやってみた。
Creating a Node Express-Webpack App with Dev and Prod Builds を翻訳しながら進める。(google翻訳頼みのため間違ってたら指摘ください。)
原文にないコメントは引用で書きます。
私が作りたいもの
開発ビルドとプロダクションビルドを分ける。
開発ビルド
- ES6+のトランスパイル。
- lint
- 単体テスト
- カバレッジレポート
- HMR(Hot Module Reloading)
- minifyしない
- imageやcssをBase64エンコードせずに保持する。
プロダクションビルド
- minifyする。
- uglify する。
- imageとcssはBase64エンコードする。
開発とプロダクションで別々のExpressサーバーファイルとWebpack設定ファイルで管理している。(不要なインポートを避けるため)
Tech Stack(技術スタック)
- Express -- server
- Webpack 4 -- bundling
- Jest -- testing
- Babel -- ES6+ transpilation
- ESlint -- Linting
- Webpack Dev Middleware -- Bundle code in memory instead of in a file
- Webpack Hot Middleware -- Enables Hot Module Reloading (HMR)
- UglifyJS -- uglifies code
- mini-css-extract-plugin -- minifies CSS
OK,Let's Begin
Step 1: The Express Server
筆者の検証環境
- macOS Sierra 10.12.6
- Node v10.0.0
- NPM 6.0.0
- Webpack 4
- Express 4.16.3
自分の検証環境
- Windows8.1
- Node v10.14.1
- NPM 6.4.1
- Webpack 4
- Express 4.16.4
検証環境のディレクトリ作成
mkdir express-webpack
cd express-webpack
package.jsonを作成
npm init -y
Expressをインストール
npm install --save express
package.jsonに以下を追加してください。
"scripts": {
"start": "node ./server.js"
},
基本的なExpressサーバー・ファイルをプロジェクト・ルート・ディレクトリserver.jsに書き込んで、動作することをテストしましょう
const path = require('path')
const express = require('express')
const app = express(),
DIST_DIR = __dirname,
HTML_FILE = path.join(DIST_DIR, 'index.html')
app.use(express.static(DIST_DIR))
app.get('*', (req, res) => {
res.sendFile(HTML_FILE)
})
const PORT = process.env.PORT || 8080
app.listen(PORT, () => {
console.log(`App listening to ${PORT}....`)
console.log('Press Ctrl+C to quit.')
})
そしてもちろん、 "Hello"と言う素敵なシンプルなHTMLファイル。(index.htmlを作成)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Express and Webpack App</title>
<link rel="shortcut icon" href="#">
</head>
<body>
<h1>Expack</h1>
<p class="description">Express and Webpack Boilerplate App</p>
</body>
</html>
さて、それが動作することをテストするには、npm start
を実行し、http:// localhost:8080にナビゲートしてください。ページにHTMLを正しく表示する必要があります。
npm start
を実行してブラウザで http://localhost:8080 にアクセスする。
実行結果
Step 2: Webpackをインストールして有効にする(Install and Enable Webpack)
webpackと関連するパッケージをインストールする。
-
webpack
-- version 4 -
webpack-cli
-- cliツール -
webpack-node-externals
-- node-modules をサーバーサイドでbundleする。
npm install --save-dev webpack webpack-cli webpack-node-externals
ES6+をES5にトランスパイルするため Babel
をインストールする。
npm install --save-dev babel-core babel-loader babel-preset-env
index.htmlファイルをdistディレクトリにコピーするには、html-loaderとhtml-webpack-pluginもインストールする必要があります。
npm install --save-dev html-loader html-webpack-plugin
Webpack設定ファイル -- webpack.config.jsを作成する必要があります。
const path = require('path')
const webpack = require('webpack')
const nodeExternals = require('webpack-node-externals')
const HtmlWebPackPlugin = require("html-webpack-plugin")
module.exports = {
entry: {
server: './server.js',
},
output: {
path: path.join(__dirname, 'dist'),
publicPath: '/',
filename: '[name].js'
},
target: 'node',
node: {
// Need this when working with express, otherwise the build fails
__dirname: false, // if you don't put this is, __dirname
__filename: false, // and __filename return blank or /
},
externals: [nodeExternals()], // Need this to avoid error when working with Express
module: {
rules: [
{
// Transpiles ES6-8 into ES5
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
// Loads the javacript into html template provided.
// Entry point is set below in HtmlWebPackPlugin in Plugins
test: /\.html$/,
use: [{loader: "html-loader"}]
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./index.html",
filename: "./index.html",
excludeChunks: [ 'server' ]
})
]
}
Note:
excludeChunksはサーバーと呼ばれるファイルを除外します。これはWebファイルであり、アプリケーション自体には必要ないため、HTMLファイルに含めたくないファイルです。
require
の代わりにserver.jsにES6 +のインポート構文を設定して、Babelの転送が正しく行われているかどうかをテストする必要があります。
import path from 'path'
import express from 'express'
const app = express(),
DIST_DIR = __dirname,
HTML_FILE = path.join(DIST_DIR, 'index.html')
app.use(express.static(DIST_DIR))
app.get('*', (req, res) => {
res.sendFile(HTML_FILE)
})
const PORT = process.env.PORT || 8080
app.listen(PORT, () => {
console.log(`App listening to ${PORT}....`)
console.log('Press Ctrl+C to quit.')
})
あなたのルートに.babelrcという名前のファイルを作成し、このコードで入力してください
{
'presets': ['env']
}
package.jsonのscriptを次のように変更します。
Unix系OSの場合
"scripts": {
"build": "rm -rf dist && webpack --mode development",
"start": "node ./dist/server.js"
},
Windowsの場合
package.json
"scripts": {
"build": "(if exist dist rd dist /s /q) && webpack --mode development",
"start": "node ./dist/server.js"
},
こうすることで、常に新しいdistフォルダから始め、コマンドラインから開発モードを宣言的に設定します。
これで、`npm run build`と`npm start`を実行し、http:// localhost:8080にナビゲートしてテストすることができます。
この時点で、エラーはないはずです。私はこの記事を書いているので、このステップを段階的に構築しています。
> 実行したところエラーが発生
>```terminal
npm run build
Version: webpack 4.27.1
Time: 312ms
Built at: 2018-12-06 20:08:10
Asset Size Chunks Chunk Names
./index.html 278 bytes [emitted]
server.js 7.16 KiB server [emitted] server
Entrypoint server = server.js
[./server.js] 3.12 KiB {server} [built] [failed] [1 error]
ERROR in ./server.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
Error: Cannot find module '@babel/core'
babel-loader@8 requires Babel 7.x (the package '@babel/core'). If you'd like to use Babel 6.x ('babel-core'), you should install 'babel-loader@7'.
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:580:15)
at Function.Module._load (internal/modules/cjs/loader.js:506:25)
at Module.require (internal/modules/cjs/loader.js:636:17)
at require (e:\00_Development\src\NodeJS\express-webpack\node_modules\v8-compile-cache\v8-compile-cache.js:159:20)
at Object.<anonymous> (e:\00_Development\src\NodeJS\express-webpack\node_modules\babel-loader\lib\index.js:10:11)
at Module._compile (e:\00_Development\src\NodeJS\express-webpack\node_modules\v8-compile-cache\v8-compile-cache.js:178:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
at Module.load (internal/modules/cjs/loader.js:598:32)
at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
at Function.Module._load (internal/modules/cjs/loader.js:529:3)
at Module.require (internal/modules/cjs/loader.js:636:17)
at require (e:\00_Development\src\NodeJS\express-webpack\node_modules\v8-compile-cache\v8-compile-cache.js:159:20)
at loadLoader (e:\00_Development\src\NodeJS\express-webpack\node_modules\loader-runner\lib\loadLoader.js:13:17)
at iteratePitchingLoaders (e:\00_Development\src\NodeJS\express-webpack\node_modules\loader-runner\lib\LoaderRunner.js:169:2)
at runLoaders (e:\00_Development\src\NodeJS\express-webpack\node_modules\loader-runner\lib\LoaderRunner.js:362:2)
at NormalModule.doBuild (e:\00_Development\src\NodeJS\express-webpack\node_modules\webpack\lib\NormalModule.js:280:3)
at NormalModule.build (e:\00_Development\src\NodeJS\express-webpack\node_modules\webpack\lib\NormalModule.js:427:15)
at Compilation.buildModule (e:\00_Development\src\NodeJS\express-webpack\node_modules\webpack\lib\Compilation.js:633:10)
at moduleFactory.create (e:\00_Development\src\NodeJS\express-webpack\node_modules\webpack\lib\Compilation.js:1019:12)
at factory (e:\00_Development\src\NodeJS\express-webpack\node_modules\webpack\lib\NormalModuleFactory.js:405:6)
at hooks.afterResolve.callAsync (e:\00_Development\src\NodeJS\express-webpack\node_modules\webpack\lib\NormalModuleFactory.js:155:13)
at AsyncSeriesWaterfallHook.eval [as callAsync] (eval at create (e:\00_Development\src\NodeJS\express-webpack\node_modules\tapable\lib\HookCodeFactory.js:32:10), <anonymous>:6:1)
at AsyncSeriesWaterfallHook.lazyCompileHook (e:\00_Development\src\NodeJS\express-webpack\node_modules\tapable\lib\Hook.js:154:20)
at resolver (e:\00_Development\src\NodeJS\express-webpack\node_modules\webpack\lib\NormalModuleFactory.js:138:29)
at process.nextTick (e:\00_Development\src\NodeJS\express-webpack\node_modules\webpack\lib\NormalModuleFactory.js:342:9)
at process._tickCallback (internal/process/next_tick.js:61:11)
babel-loader
のバージョンが8.x だったのが原因のため、babel-loader
を 7.xにダウングレードする。
npm install babel-loader@7 -D
> ビルド成功
>```terminal
npm run build
Version: webpack 4.27.1
Time: 1397ms
Built at: 2018-12-06 20:15:36
Asset Size Chunks Chunk Names
./index.html 278 bytes [emitted]
server.js 5.06 KiB server [emitted] server
Entrypoint server = server.js
[./server.js] 677 bytes {server} [built]
[express] external "express" 42 bytes {server} [built]
[path] external "path" 42 bytes {server} [built]
Child html-webpack-plugin for "index.html":
1 asset
Entrypoint undefined = ./index.html
[./node_modules/html-webpack-plugin/lib/loader.js!./index.html] 330 bytes {0} [built]
ビルド後のserver.jsが正常に実行できることを確認
npm start
![hello world.jpg](https://qiita-image-store.s3.amazonaws.com/0/160966/5b34a7ea-ff49-7aa3-c115-19d6960eb9a5.jpeg)
## Step 3: アプリケーションにCSSとJavascriptの機能を追加する
すでにかなりの機能が実装されていますが、CSSスタイル、Javascript、画像をアプリに追加することができます。
これを行うには、Webpack設定を2つのファイルに分割する必要があります。後で3つのファイルになります。
* **webpack.server.config.js**<br>サーバーコードだけをバンドルする。
* **webpack.config.js**<br>アプリケーションコードをバンドルする。
後で、このメインの設定ファイルをDevとProdのバージョンに分け、サーバーファイルをDevとProdのバージョンに分けます。
まず、必要な依存関係をインストールしましょう。
```terminal
npm install --save-dev css-loader file-loader style-loader
私たちのディレクトリ構造は次のようになります
.babelrc
.git
.gitignore
README.md
dist
node_modules
package-lock.json
package.json
webpack.config.js
webpack.server.config.js
src
index.js
html
index.html
css
style.css
js
logger.js ※原文ではindex.jsだが誤り。
img
bg.jpg ※原文ではawful-selfie.jpgだが誤り。原文ではbg.jpgを置く手順が抜けている。
server
server.js
package.jsonのscriptを調整します。
Unix系OSの場合
"scripts": {
"build": "rm -rf dist && webpack --mode development --config webpack.server.config.js && webpack --mode development",
"start": "node ./dist/server.js"
},
Windowsの場合
pakage.json
"scripts": {
"build": "(if exist dist rd dist /s /q) && webpack --mode development --config webpack.server.config.js && webpack --mode development",
"start": "node ./dist/server.js"
},
./src/html/index.htmlを更新してください。
```index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Express and Webpack App</title>
<link rel="shortcut icon" href="#">
</head>
<body>
<h1>Expack</h1>
<p class="description">Express and Webpack Boilerplate App</p>
<div class="awful-selfie"></div>
</body>
</html>
./src/css/style.css を更新してください。
h1, h2, h3, h4, h5, p {
font-family: helvetica;
color: #3e3e3e;
}
.description {
font-size: 14px;
color: #9e9e9e;
}
.awful-selfie{
background: url(../img/bg.jpg);
width: 300px;
height: 300px;
background-size: 100% auto;
background-repeat: no-repeat;
}
./src/index.jsを更新して、インポートとスタイルが機能しているか、基本的な機能を確認します。
import logMessage from './js/logger'
import './css/style.css'
// Log message to console
logMessage('Welcome to Expack!')
もちろん./src/js/logger.js
const logMessage = msg => console.log(msg)
export default logMessage
server.jsをルートから./src/serverに移動するだけです。これにより、ルートがきれいに保たれ、サーバーコードが適切な場所に保持されます。
最後に、Webpackの設定をしましょう。我々は./webpack.server.config.jsから始めます。
const path = require('path')
const webpack = require('webpack')
const nodeExternals = require('webpack-node-externals')
module.exports = {
entry: {
server: './src/server/server.js',
},
output: {
path: path.join(__dirname, 'dist'),
publicPath: '/',
filename: '[name].js'
},
target: 'node',
node: {
// Need this when working with express, otherwise the build fails
__dirname: false, // if you don't put this is, __dirname
__filename: false, // and __filename return blank or /
},
externals: [nodeExternals()], // Need this to avoid error when working with Express
module: {
rules: [
{
// Transpiles ES6-8 into ES5
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
}
}
そして、./webpack.config.jsですべてを終わらせましょう。
const path = require("path")
const webpack = require('webpack')
const HtmlWebPackPlugin = require("html-webpack-plugin")
module.exports = {
entry: {
main: './src/index.js'
},
output: {
path: path.join(__dirname, 'dist'),
publicPath: '/',
filename: '[name].js'
},
target: 'web',
devtool: '#source-map',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
},
{
// Loads the javacript into html template provided.
// Entry point is set below in HtmlWebPackPlugin in Plugins
test: /\.html$/,
use: [
{
loader: "html-loader",
//options: { minimize: true }
}
]
},
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ['file-loader']
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./src/html/index.html",
filename: "./index.html",
excludeChunks: [ 'server' ]
})
]
}
appビルドにtarget: 'web'を使う方法に注目してください。これは非常に重要です。target: 'node'を使用するとエラーが発生しますので、再度確認してください。
npm run build
を実行すると、エラーは発生しません。
ビルド実行でエラー発生
npm runbuild
ERROR in ./src/css/style.css (./node_modules/css-loader!./src/css/style.css)
Module not found: Error: Can't resolve '../img/bg.jpg' in 'e:\00_Development\src\NodeJS\express-webpack\src\css'
@ ./src/css/style.css (./node_modules/css-loader!./src/css/style.css) 7:221-245
@ ./src/css/style.css
@ ./src/index.js
ERROR in ./src/js/logger.js
Module not found: Error: Can't resolve './css/style.css' in 'e:\00_Development\src\NodeJS\express-webpack\src\js'
@ ./src/js/logger.js 7:0-26
@ ./src/index.js
ERROR in ./src/js/logger.js
Module not found: Error: Can't resolve './js/logger' in 'e:\00_Development\src\NodeJS\express-webpack\src\js'
@ ./src/js/logger.js 3:14-36
@ ./src/index.js
> ./src/img/bg.jpg に適当な画像をおいてからビルドしたら成功
>```
npm run build
Version: webpack 4.27.1
Time: 772ms
Built at: 2018-12-06 21:06:33
Asset Size Chunks Chunk Names
server.js 5.13 KiB server [emitted] server
Entrypoint server = server.js
[./src/server/server.js] 677 bytes {server} [built]
[express] external "express" 42 bytes {server} [built]
[path] external "path" 42 bytes {server} [built]
Hash: 1825c3d14b01b8828f32
Version: webpack 4.27.1
Time: 1228ms
Built at: 2018-12-06 21:06:36
Asset Size Chunks Chunk Names
./index.html 374 bytes [emitted]
40f2415d89a70d2c43d735d7607767c0.jpg 12.7 KiB [emitted]
main.js 23.6 KiB main [emitted] main
main.js.map 26.6 KiB main [emitted] main
Entrypoint main = main.js main.js.map
[./node_modules/css-loader/index.js!./src/css/style.css] ./node_modules/css-loader!./src/css/style.css 573 bytes {main} [built]
[./src/css/style.css] 1.06 KiB {main} [built]
[./src/img/bg.jpg] 82 bytes {main} [built]
[./src/index.js] 299 bytes {main} [built]
[./src/js/logger.js] 183 bytes {main} [built]
+ 4 hidden modules
Child html-webpack-plugin for "index.html":
1 asset
Entrypoint undefined = ./index.html
[./node_modules/html-webpack-plugin/lib/loader.js!./src/html/index.html] 379 bytes {0} [built]
npm start
を実行してブラウザで http://localhost:8080 にアクセスする。
npm start
![expack2.jpg](https://qiita-image-store.s3.amazonaws.com/0/160966/daaefbc9-a1a5-31fb-ba33-3aad8727cd80.jpeg)
続きはこちら [ExpressをWebpackでバンドルする その2](https://wakusan.qrunch.io/entries/ADg11L2Jw4O4HrR1) qrunch投稿なのであとでQiita用にupします。