webpack 4 複数のstyl(css)を別ファイルで出力するためのwebpack.config.js

webpack

webpack 4では extract-text-webpack-plugin が deplicated になったので、代替の minicss-ectract-plugin を使おうと思ったのだけど、簡単にはいかなかった。のでまとめ。

dir.src = 'src' , dir.dest = 'public' って感じです。

/////////////////////////////////////////////////////////////////////////////////////

// Requirement

const path = require('path')
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const FixStyleOnlyEntriesPlugin = require('webpack-fix-style-only-entries')

const cssDeclarationSorter = require('css-declaration-sorter')
const cssMqpacker = require('css-mqpacker')
const autoprefixer = require('autoprefixer')

/////////////////////////////////////////////////////////////////////////////////////

// Config

const config = require('./config')
const dir = config.dir
const devMode = process.env.NODE_ENV !== 'production'

/////////////////////////////////////////////////////////////////////////////////////

// Exports

module.exports = {
  mode: process.env.NODE_ENV || 'development',

  entry: {
    index: path.resolve(__dirname, dir.src, 'js', 'index.js'),
    index2: path.resolve(__dirname, dir.src, 'js', 'index2.js'),
    'style.css': path.resolve(__dirname, dir.src, 'stylus', 'style.styl'),
    'style2.css': path.resolve(__dirname, dir.src, 'stylus', 'style2.styl')
  },

  output: {
    filename: 'js/[name].js',
    path: path.resolve(__dirname, dir.dest)
  },

  optimization: {
    minimizer: [
      new OptimizeCssAssetsPlugin()
    ],
    minimize: !devMode
  },

  plugins: [
    new FixStyleOnlyEntriesPlugin({
      extensions: ['styl', 'css']
    }),
    new MiniCssExtractPlugin({
      filename: 'css/[name]'
    })
  ],

  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        },
        exclude: /node_modules/
      },
      {
        test: /\.(styl|css)$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader
          },
          {
            loader: 'css-loader'
          },
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: () => [
                cssMqpacker({
                  sort: true
                }),
                cssDeclarationSorter({
                  order: 'smacss'
                }),
                autoprefixer({
                  grid: true,
                  browsers: [
                    'last 2 versions'
                  ]
                })
              ]
            }
          },
          {
            loader: 'stylus-loader',
            options: {
              'include css': true
            }
          }
        ]
      }
    ]
  }
}

ポイント

まず、 entry のキーにファイル名を指定するが、このときJavaScriptの拡張子は output.filename で指定、CSSの拡張子はキーに直接書き、プラグインオプションの filename では拡張子は指定しないようにする。理由は以下のとーり。

順を追って説明すると、まず extract-text-webpack-plugin の代わりに使う minicss-extract-plugin は、なぜかcssを取り出す前のjsファイルが残ってしまう。そもそも不要だし、ともすると「同じファイル名のChunkがあってコンフリクトしてます」とかエラーが出る。

これを解決するのが webpack-fix-style-only-entries なんだけど、これは cssの名前.js ファイルを消してくれる挙動っぽいので、正しく消してもらうためにはこのパターン以外うまくいかない。

もうひとつ。 webpack-fix-style-only-entriesextensions に、対象のスタイルの拡張子を配列で書く。デフォルトは ["less", "scss", "css"] なので、stylやsassなら書き換えが必要。

参考文献