マークアップエンジニアが考える最低限のwebpack構成
🔴2023/04/26追記🔴
こちらでご紹介しているpackage構成は、2023年現在では一部脆弱性エラーが発生しております。
2023年に構築した環境を紹介する記事を投稿しましたので、よろしければこちらをご参考ください
近年はgrunt/gulp < webpackの風潮が強く、例にもれず自分もしょっちゅうwebpackで環境構築をしております。
【build-toolの関心度】
ただ、毎回package構成やらバージョンの相性やらを忘れて調べているので、自分用の最小構成メモろうっていうのが本記事の趣旨。
webpack(モジュールバンドラー)なんだから全部バンドルしてjsにしちゃえばもっと楽なんだろうけど、企業勤めだと大体backend絡める時点でそんな作りには出来ない状況なのが常。。。
デザイナー・マークアップエンジニア・プログラマがノンストレスな、スキルセットや制作環境があればみんなハッピーなのになぁ。。。
閑話休題。
今回の環境で使う想定機能としてはこちら
- HTMLファイルを圧縮
- SCSSをCSSとしてファイル生成&圧縮
- JS(ES6可)を圧縮
- 画像を圧縮
- webpackのstatsはエラーと警告のみにして、watch中はchokidarを使って変更ファイルを監視
developmentとproductionで分けて非圧縮・圧縮分けても良いけど、今回の環境では割愛。
常時mode:productionとしてbuildされます。
/* folder */
root
├── .babelrc
├── .gitignore
├── html-minifier.js
├── package.json
├── src
│ ├── img
│ │ └── img.jpg
│ ├── index.html
│ ├── js
│ │ ├── component
│ │ │ └── _sample.js
│ │ └── script.js
│ └── scss
│ ├── _sample.scss
│ └── style.scss
└── webpack.config.js
/* package.json */
{
"name": "webpack_sample",
"version": "1.0.0",
"description": "",
"scripts": {
"build": "webpack --config webpack.config.js",
"w": "webpack --watch --info-verbosity none --config webpack.config.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/cli": "^7.14.8",
"@babel/core": "^7.15.0",
"@babel/preset-env": "^7.15.0",
"autoprefixer": "^9.8.6",
"babel-loader": "^8.2.2",
"chokidar": "^3.5.2",
"copy-webpack-plugin": "^6.3.2",
"css-loader": "^4.2.2",
"html-minifier": "^4.0.0",
"imagemin-gifsicle": "^7.0.0",
"imagemin-mozjpeg": "^9.0.0",
"imagemin-pngquant": "^9.0.2",
"imagemin-svgo": "^9.0.0",
"imagemin-webpack-plugin": "^2.4.2",
"mini-css-extract-plugin": "^0.11.0",
"npm-run-all": "^4.1.5",
"postcss": "^8.3.6",
"postcss-loader": "^3.0.0",
"rimraf": "^3.0.2",
"sass-loader": "^7.2.0",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12",
"webpack-fix-style-only-entries": "^0.5.1"
}
}
/* webpack.config.js */
const glob = require("glob");
const { execSync } = require('child_process');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const FixStyleOnlyEntriesPlugin = require("webpack-fix-style-only-entries");
const CopyPlugin = require("copy-webpack-plugin");
const ImageminPlugin = require('imagemin-webpack-plugin').default;
const ImageminMozjpeg = require('imagemin-mozjpeg');
const ImageminPngquant = require('imagemin-pngquant');
const ImageminGifsicle = require('imagemin-gifsicle');
const ImageminSvgo = require('imagemin-svgo');
const chokidar = require('chokidar');
const fs = require('fs');
// mode set
let mode = "build";
if(process.argv.includes("--watch")){
mode = "watch";
}
// entry set
let entries = {};
glob.sync("./src/?(js|scss)/**/*.?(js|scss)").map(function(file){
fileName = file.match(".+/(.+?)\.[a-z]+([\?#;].*)?$")[1];
if(fileName.slice(0,1) !== "_"){
filePath = file.substring(0, file.lastIndexOf("."));
filePath = filePath.replace(/src/g,"assets");
filePath = filePath.replace(/scss/g,"css");
entries[filePath] = file;
}
});
const watcher = chokidar.watch('src/');
const watch = () => {
watcher.on('ready', () => {
console.log("watch start");
watcher.on('all', (event, path) => {
if(event === "unlink"){
fs.unlinkSync(path.replace(/src/,"assets"));
}
if(path.indexOf(".html") > 0){
execSync("npx html-minifier --input-dir src/ --output-dir assets/ --file-ext html -c html-minifier.js");
}
console.log(event, path);
});
});
};
module.exports = {
stats: 'errors-warnings',
mode: "production", // or development
entry: entries,
// ファイルの出力設定
output: {
// 出力ファイルのディレクトリ名
path: `${__dirname}`,
// 出力ファイル名
filename: '[name].js'
},
module: {
rules: [
{
test: /\.scss/, // 対象となるファイルの拡張子
use: [
{ // JSデータをCSSとして外部ファイル化
loader: MiniCssExtractPlugin.loader
},
{ // CSSをJSに変換
loader: 'css-loader',
options: {
url: false,
sourceMap: true,
}
},
{ // PostCSSのための設定
loader: 'postcss-loader',
options: {
sourceMap: true,
plugins: [
require('autoprefixer')({
overrideBrowserslist: ['ie >= 11', '> 5%']
})
]
}
},
{ // SCSSをCSSに変換
loader: 'sass-loader'
},
]
},
{
test: /\.js/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [ '@babel/preset-env' ]
}
}
}
]
},
plugins: [
// 不要なJSファイルは削除
new FixStyleOnlyEntriesPlugin({
silent: true
}),
// cssの出力先を指定する
new MiniCssExtractPlugin({
filename: '[name].css'
}),
// 画像の圧縮
new ImageminPlugin({
test: /\.(jpe?g|png|gif|svg)$/i,
// minFileSize: 250000, // bite // 250kb以下で設定中
plugins: [
ImageminMozjpeg({ quality: 80 }),
ImageminPngquant({ quality: [0.7, 0.8] }),
ImageminGifsicle(),
ImageminSvgo()
]
}),
// 画像ファイルの移動
new CopyPlugin({
patterns: [
{ from: "./src/img/", to: "./assets/img/" }
],
}),
() => {
// assetsフォルダの削除
execSync("rimraf assets");
// HTMLファイルの圧縮
execSync("mkdir assets && npx html-minifier --input-dir src/ --output-dir assets/ --file-ext html -c html-minifier.js");
if(mode == "watch"){
watch();
}
},
],
};
ディスカッション
コメント一覧
まだ、コメントがありません