Gulpで動いている既存のプロジェクトをWebpackのみに変更する
個人用のプロジェクトでのビルドを「gulp + webpack」からwebpackのみに変更したので、その時に対応したことのメモです。
環境
gulpファイルを消そうと思った理由
1. 自分の環境ならwebpackのみで変換できる
gulp上で行っていることが
- pug → htmlへ変換
- stylus → cssへ変換
- img, fonts, static → ファイルのコピー
のみだったので、そのくらいだったらwebpackでもできるんじゃないかって思った。
2. gulpのパッケージ周りがあまり更新されていない?(個人的な主観)
- gulp本体の
Last publish
がa year ago
なので(執筆時)、webpackよりも積極的な開発はされていない?(あくまでもnpmjsを見たイメージ) - 「gulp-*」のパッケージについてもあまり更新されているイメージがなかったので、webpackのみに乗り換えるいい機会だと思った。
変更前のコード
gulpfile.js
const { src, dest, watch, parallel } = require('gulp') const pug = require('gulp-pug') const data = require('gulp-data') const stylus = require('gulp-stylus') const postcss = require('gulp-postcss') const postcssPresetEnv = require('postcss-preset-env') const autoprefixer = require('autoprefixer') const plumber = require('gulp-plumber') const notify = require('gulp-notify') const sourcemaps = require('gulp-sourcemaps') const cleanCSS = require('gulp-clean-css') const browserSync = require('browser-sync') const webpackStream = require('webpack-stream') const webpack = require('webpack') const htmlmin = require('gulp-htmlmin') const mode = require('gulp-mode')({ modes: ['production', 'development'], default: 'development', verbose: false, }) const isProduction = mode.production() const webpackConfigDev = require('./webpack.dev') const webpackConfigProd = require('./webpack.prod') const webpackConfig = isProduction ? webpackConfigProd : webpackConfigDev const srcPath = { html: ['src/pug/**/*.pug', '!' + 'src/pug/**/_*.pug'], stylus: 'src/**/*.styl', js: 'src/**/*.ts', image: 'src/assets/img/**/*', fonts: 'src/assets/fonts/**/*', static: 'src/static/**/*', } const destPath = { root: 'dist/', assets: 'dist/assets/', } const jsFunc = () => { return webpackStream(webpackConfig, webpack) .on('error', function(e) { this.emit('end') }) .pipe(dest(`${destPath.assets}js/`)) .pipe(browserSync.reload({ stream: true })) } const htmlFunc = () => { return src(srcPath.html) .pipe(plumber({ errorHandler: notify.onError('Error: <%= error.message %>') })) .pipe( data(file => { return { relativePath: file.history[0].replace(file.base, ''), // ページ情報仮置き } }) ) .pipe( pug({ basedir: 'src/pug', pretty: true, }) ) .pipe( mode.production( htmlmin({ collapseWhitespace: true, minifyJS: true, removeComments: true, }) ) ) .pipe(dest(destPath.root)) .pipe(browserSync.reload({ stream: true })) } const stylusFunc = () => { return src('src/assets/stylus/*.styl') .pipe(mode.development(sourcemaps.init())) .pipe(plumber({ errorHandler: notify.onError('Error: <%= error.message %>') })) .pipe(stylus()) .pipe(postcss([postcssPresetEnv(autoprefixer)])) .pipe(cleanCSS()) .pipe(mode.development(sourcemaps.write())) .pipe(dest(`${destPath.assets}css/`)) .pipe(browserSync.reload({ stream: true })) } const imageFunc = () => { return src(srcPath.image).pipe(dest(`${destPath.assets}img/`)) } const fontsFunc = () => { return src(srcPath.fonts).pipe(dest(`${destPath.assets}fonts/`)) } const staticFunc = () => { return src(srcPath.static).pipe(dest(destPath.root)) } const browserSyncFunc = () => { browserSync({ server: { baseDir: 'dist/', index: 'index.html', }, }) } const watchFiles = () => { watch(srcPath.html[0], htmlFunc) watch(srcPath.stylus, stylusFunc) watch(srcPath.js, jsFunc) watch(srcPath.image, imageFunc) watch(srcPath.static, staticFunc) watch(srcPath.fonts, fontsFunc) } exports.default = parallel( watchFiles, [htmlFunc, stylusFunc, jsFunc, imageFunc, staticFunc, fontsFunc], browserSyncFunc ) exports.build = parallel(htmlFunc, stylusFunc, jsFunc, imageFunc, staticFunc, fontsFunc)
webpack.dev.js
const merge = require('webpack-merge') const common = require('./webpack.common.js') module.exports = merge(common, { mode: 'development', devtool: 'inline-source-map', })
webpack.prod.js
const merge = require('webpack-merge') const common = require('./webpack.common.js') module.exports = merge(common, { mode: 'production', devtool: 'eval', })
webpack.common.js
const { resolve } = require('path') const appDir = resolve(__dirname, 'src') module.exports = { context: appDir, entry: { bundle: './assets/js/common.ts', head: './assets/js/head.ts' }, output: { filename: '[name].js' }, module: { rules: [{ enforce: 'pre', test: /\.ts$/, exclude: /node_modules/, use: ['ts-loader', { loader: 'eslint-loader', options: { typeCheck: true, }, }], }] }, resolve: { extensions: [".ts"] } }
変更を行った場所
htmlの変更
パッケージのインストール
$ npm i -D html-webpack-plugin
ページ情報の記載
const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', template: 'pug/index.pug', inject: false }), ] }
プロパティについて
参考: https://www.npmjs.com/package/html-webpack-plugin#hello-world-5
個人的に詰まったところ
htmlの出力を行うとjavascriptファイルの読子mが自動的に出力がされてしまうため、inject: false
の設定を行う必要がある。
※あくまでも今回は「gulpからの移行 + htmlにjs記載済」の対応のため、inject: false
の設定を行う。
cssの変更
パッケージのインストール
$ npm i -D css-loader mini-css-extract-plugin postcss-loader style-loader stylus-loader
cssの設定読み込み
const { resolve, join } = require('path') const autoprefixer = require('autoprefixer') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const assetsPath = { jsPath: 'assets/js', cssPath: 'assets/css', } module.exports = { entry: { bundle: './assets/js/common.ts', }, output: { path: buildDir, publicPath: './', filename: join(assetsPath.jsPath, '[name].js'), }, module: { rules: [{ test: /\.styl$/, exclude: /node_modules/, use: [{ loader: 'style-loader' }, MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { url: false, } }, { loader: 'postcss-loader', options: { plugins: [ autoprefixer({ grid: true, flexbox: true }) ] } }, 'stylus-loader', ] }, ] }, plugins: [ new MiniCssExtractPlugin({ filename: join(assetsPath.cssPath, '[name].css'), ignoreOrder: true }), ], }
main.jsにcssファイルの記載
import '../stylus/style.styl' // 指定のcssファイルの場所
img, fonts, staticをコピー
パッケージのインストール
$ npm i -D copy-webpack-plugin
copy-webpack-pluginの設定読み込み
const CopyPlugin = require('copy-webpack-plugin') const assetsPath = { imgPath: 'assets/img', fontPath: 'assets/fonts', staticPath: 'static', } module.exports = { plugins: [ new CopyPlugin({ patterns: [{ from: assetsPath.imgPath, to: assetsPath.imgPath }, ], }), new CopyPlugin({ patterns: [{ from: assetsPath.staticPath, to: '' }, ], }), new CopyPlugin({ patterns: [{ from: assetsPath.fontPath, to: assetsPath.fontPath }, ], }), ] }
ローカルサーバーの起動
パッケージのインストール
$ npm i -D webpack-dev-server
dev-serverの起動
const { resolve } = require('path') const merge = require('webpack-merge') const common = require('./webpack.common.js') module.exports = { devServer: { hot: true, compress: true, writeToDisk: true, contentBase: resolve(__dirname, 'dist'), watchContentBase: true, open: true, port: 3000 }, }
個人的に詰まったところ
TypeScriptを使っている場合、
module.exports = { resolve: { extensions: ['.ts'] } }
のような感じで、extensions: ['.ts']
の部分が.ts
のみだとエラーになる。
このエラーの対応方法として、
module.exports = { resolve: { extensions: ['.ts', '.js'] } }
のように記載を行う。
参考
ビルドのコマンド変更
変更前
start: ローカルサーバーの起動 build: html, css, jsファイルのビルド
{ "script": { "start": "gulp", "build": "gulp build --production", } }
変更後
{ "script": { "start": "webpack-dev-server --config webpack.dev.js", "build": "webpack --config webpack.prod.js", } }
追加したパッケージ・削除したパッケージ
今回追加したパッケージ
ファイルのコピーを行う
css + webpackで必要
webpackでhtmlページの出力を行う
webpackでcssファイルを別で書き出しを行う
postcss + webpackで必要(autoprefixerとの連携のため)
pug + webpackで必要
動的にstyleタグを作成し、head内に差し込まれることでcssが適用される
stylus + webpackで必要
webpckでローカルサーバーを開く
今回削除したパッケージ
gulpでローカルサーバーを開くため
gulp本体
gulpでのcssの圧縮
gulpでのページの情報を送るためのもの
gulpでのhtmlの圧縮
gulpで本番環境・開発環境で実行するものを変更ため
gulpでの通知を出す
gulpでのエラー時、処理が停止するのを止める
gulp + postcssで必要
gulp + pugで必要
gulpでstylus, jsのsourcemapの表示
gulp + stylusで必要
gulp + webpackとの連携に必要
変更後のコード
htmlの圧縮などimgファイルの圧縮については未対応。また、ちゃんと整理できていないので、一部不要なものもあり。
webpack.dev.js
const { resolve } = require('path') const merge = require('webpack-merge') const common = require('./webpack.common.js') module.exports = merge(common, { mode: 'development', devtool: 'inline-source-map', devServer: { hot: true, compress: true, writeToDisk: true, contentBase: resolve(__dirname, 'dist'), watchContentBase: true, open: true, port: 3000 }, })
webpack.prod.js
const merge = require('webpack-merge') const common = require('./webpack.common.js') module.exports = merge(common, { mode: 'production', devtool: 'eval', })
webpcak.common.js
const { resolve, join } = require('path') const autoprefixer = require('autoprefixer') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const CopyPlugin = require('copy-webpack-plugin') const merge = require('webpack-merge') const pages = require('./webpack.pages.js') const appDir = resolve(__dirname, 'src') const buildDir = resolve(__dirname, 'dist') const assetsPath = { basePath: 'assets', jsPath: 'assets/js', cssPath: 'assets/css', imgPath: 'assets/img', fontPath: 'assets/fonts', staticPath: 'static', } module.exports = merge(pages, { context: appDir, entry: { bundle: './assets/js/common.ts', head: './assets/js/head.ts' }, output: { path: buildDir, publicPath: './', filename: join(assetsPath.jsPath, '[name].js'), chunkFilename: join(assetsPath.jsPath, '[name]-[hash].bundle.js') }, module: { rules: [{ enforce: 'pre', test: /\.ts$/, exclude: /node_modules/, use: ['ts-loader', { loader: 'eslint-loader', options: { typeCheck: true, }, }], }, { test: /\.styl$/, exclude: /node_modules/, use: [{ loader: 'style-loader' }, MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { url: false, } }, { loader: 'postcss-loader', options: { plugins: [ autoprefixer({ grid: true, flexbox: true }) ] } }, 'stylus-loader', ] }, { test: /\.pug$/, use: [{ loader: 'pug-loader', options: { pretty: true, root: resolve(appDir, 'pug'), } }] }] }, plugins: [ new CopyPlugin({ patterns: [{ from: assetsPath.imgPath, to: assetsPath.imgPath }, ], }), new CopyPlugin({ patterns: [{ from: assetsPath.staticPath, to: '' }, ], }), new CopyPlugin({ patterns: [{ from: assetsPath.fontPath, to: assetsPath.fontPath }, ], }), new MiniCssExtractPlugin({ filename: join(assetsPath.cssPath, '[name].css'), chunkFilename: join(assetsPath.cssPath, '[name]-[hash].css'), ignoreOrder: true }), ], resolve: { extensions: ['.ts', '.js'] } })
webpack.pages.js
const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { plugins: [ new HtmlWebpackPlugin({ filename: 'index.html', template: 'pug/index.pug', inject: false }) ] }