Webpack で 作られた bundle.js から CSS を分離する
2018-03-27

Note

環境は jmfirth/webpack イメージ で構築した Docker コンテナです

  • extract-text-webpack-plugin == 3.0.2 (最終的に 2.1.2 に)

  • nodejs == 8.4.0

  • npm == 5.3.0

  • Webpack == 2.7.0 (イメージが最近更新されていないため少し古い)

経緯

このブログでは webpack を使って CSS もすべて bundle.js にまとめていたのですが、 Google 先生 がうまく解釈してくれないっぽく、 Web master tool とかで見たときに崩れていました。

検索インデックスに影響する恐れもあったので対応として CSS は 分離しようという結論に至りました。

対応

ExtractTextPlugin というプラグインを使うとよいらしいです。

webpack.config.js はこんな感じになりました。

const path = require('path');
const webpack = require("webpack");
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  resolve: {
    alias: {'vue$': 'vue/dist/vue.esm.js'}
  },
  entry: './assets/entry.js',
  output: {
    path: path.resolve('./files/static/'),
    filename: 'bundle.js'
  },
  plugins: [
    new ExtractTextPlugin({
      filename: 'style.css',
      allChunks: true
    }),
    new UglifyJsPlugin(),
  ],
  //devtool: 'source-map',
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: ['babel-loader', 'vue-loader'],
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
      },
      {
        test: /\.styl$/,
        loader: ExtractTextPlugin.extract({
          use: [
            {
              loader: 'css-loader',
              options: {
                minimize: true,
              }
            },
            'stylus-loader'
          ],
        })
      },
      {
        test: /\.svg$/,
        use: ['url-loader',],
      },
    ]
  },
};

エラーが

とくに設定におかしいところはないと思うんですが、 webpack を実行すると以下のようになりました。

npm info it worked if it ends with ok
npm info using npm@5.3.0
npm info using node@v8.4.0
npm info lifecycle cronote@2.0.0~preinstall: cronote@2.0.0
npm info linkStuff cronote@2.0.0
npm info lifecycle cronote@2.0.0~install: cronote@2.0.0
npm info lifecycle cronote@2.0.0~postinstall: cronote@2.0.0
npm info lifecycle cronote@2.0.0~prepublish: cronote@2.0.0
npm info lifecycle cronote@2.0.0~prepare: cronote@2.0.0
npm info lifecycle undefined~preshrinkwrap: undefined
npm info lifecycle undefined~shrinkwrap: undefined
npm info lifecycle undefined~postshrinkwrap: undefined
npm WARN extract-text-webpack-plugin@3.0.2 requires a peer of webpack@^3.1.0 but none was installed.
npm WARN url-loader@0.5.9 requires a peer of file-loader@* but none was installed.
npm WARN cronote@2.0.0 license should be a valid SPDX license expression

npm info ok

Webpack is watching the files…

/root/cronote/node_modules/extract-text-webpack-plugin/dist/index.js:188
            chunk.sortModules();
                  ^

TypeError: chunk.sortModules is not a function
    at /root/cronote/node_modules/extract-text-webpack-plugin/dist/index.js:188:19
    at /root/cronote/node_modules/extract-text-webpack-plugin/node_modules/async/dist/async.js:3096:16
    at eachOfArrayLike (/root/cronote/node_modules/extract-text-webpack-plugin/node_modules/async/dist/async.js:1055:9)
    at eachOf (/root/cronote/node_modules/extract-text-webpack-plugin/node_modules/async/dist/async.js:1103:5)
    at Object.eachLimit (/root/cronote/node_modules/extract-text-webpack-plugin/node_modules/async/dist/async.js:3158:5)
    at Compilation.<anonymous> (/root/cronote/node_modules/extract-text-webpack-plugin/dist/index.js:184:27)
    at Compilation.applyPluginsAsyncSeries (/root/cronote/node_modules/tapable/lib/Tapable.js:206:13)
    at Compilation.seal (/root/cronote/node_modules/webpack/lib/Compilation.js:579:8)
    at /root/cronote/node_modules/webpack/lib/Compiler.js:493:16
    at /root/cronote/node_modules/tapable/lib/Tapable.js:289:11
    at _addModuleChain (/root/cronote/node_modules/webpack/lib/Compilation.js:481:11)
    at processModuleDependencies.err (/root/cronote/node_modules/webpack/lib/Compilation.js:452:13)
    at _combinedTickCallback (internal/process/next_tick.js:131:7)
    at process._tickCallback (internal/process/next_tick.js:180:9)

検索すると 同じ現象が報告されてる GitHub Issue がいくつかあって

どうやら プラグインと webpack のバージョンが噛み合わないエラーみたいです。

残念ながら今回はコンテナの都合で webpack のアップデートができないというかめんどくさいので 2番目の人が言っているように extract-text-webpack-plugin のバージョンを 2.1.2 に下げて、再実行

Webpack is watching the files…

Hash: e370c0230597cfebee3a
Version: webpack 2.7.0
Time: 19358ms
                                    Asset       Size  Chunks             Chunk Names
                                bundle.js     116 kB       0  [emitted]  main
    /root/cronote/files/static/bundle.css    14.9 kB       0  [emitted]  main

通りました。

Warning

  • CSS は output.path のディレクトリを起点に出力されるので 絶対パス指定すると予期せぬ所に出力されます。

  • できればファイル名で指定しましょう。

公式のコンテナ使ったほうがいいってはっきり分かんだね。Docker イメージ node に変えようかな。

参考