Duplicated css files using webpack MiniCssExtractPlugin and OptimizeCSSAssetsPlugin
Asked Answered
L

0

7

I'm getting this problem of duplicated css files under dist/css

enter image description here

My webpack configuration is as follows:

webpack.base.config.js

/* eslint quote-props: ['off'] */
'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const isProduction = process.env.NODE_ENV === 'production';

function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

const createLintingRule = () => ({
  test: /\.(js|vue)$/,
  loader: 'eslint-loader',
  enforce: 'pre',
  include: [resolve('src'), resolve('test')],
  options: {
    formatter: require('eslint-friendly-formatter'),
    emitWarning: !config.dev.showEslintErrorsInOverlay
  }
})

module.exports = {
  context: path.resolve(__dirname, '../'),
  entry: {
    app: './src/entry.client.js'
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      '@': resolve('src'),
      '$': resolve('node_modules'),
      'i18n': resolve('i18n'),
      '@fortawesome/fontawesome-free-solid$': '@fortawesome/fontawesome-free-solid/shakable.es.js',
      '@fortawesome/fontawesome-free-regular$': '@fortawesome/fontawesome-free-regular/shakable.es.js',
      '@fortawesome/fontawesome-free-brand$': '@fortawesome/fontawesome-free-brand/shakable.es.js',
    }
  },
  module: {
    rules: [
      ...(config.dev.useEslint ? [createLintingRule()] : []),
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.s?[ac]ss$/,
        use: [
          !isProduction ? 'style-loader' : MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'sass-loader',
        ],
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('i18n'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
      },
      {
        test: /^(?!.*-inline).*\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /.*(?:\.(gif|png|jpe?g)|(-inline\.svg))$/i,
        use: [
          {
            loader: 'image-webpack-loader',
            options: {
              mozjpeg: {
                progressive: true,
                quality: 65
              },
              // optipng.enabled: false will disable optipng
              optipng: {
                enabled: false,
              },
              pngquant: {
                quality: '65-90',
                speed: 4
              },
              gifsicle: {
                interlaced: false,
              },
            },
          }
        ]
      },
      {
        test: /-inline\.svg$/,
        loader: 'svg-inline-loader',
        options: {
          removeSVGTagAttrs: false,
        }
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('media/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(graphql|gql)$/,
        exclude: /node_modules/,
        loader: 'graphql-tag/loader',
      }
    ]
  },
  node: {
    // prevent webpack from injecting useless setImmediate polyfill because Vue
    // source contains it (although only uses it if it's native).
    setImmediate: false,
    // prevent webpack from injecting mocks to Node native modules
    // that does not make sense for the client
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  }
}

And webpack.prod.config.js

const webpackConfig = merge(baseWebpackConfig, {
  mode: 'production',
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: "vendor",
          chunks: "all"
        },
      },
    },
    concatenateModules: true,
    minimizer: [
      new UglifyJsPlugin({
        uglifyOptions: {
          compress: {
            dead_code: true,
            drop_console: true,
          },
          output: {
            comments: false,
          },
          ecma: 6,
        },
        sourceMap: config.build.productionSourceMap,
        parallel: true
      }),
      new OptimizeCSSAssetsPlugin({}),
    ],
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new CleanPlugin([dist], {
      root: path.resolve(__dirname, '..'),
    }),
    new VueLoaderPlugin(),
    new Dotenv({
      path: path.resolve('.env')
    }),
    new MiniCssExtractPlugin({
      filename: utils.assetsPath('css/[name].[hash].css'),
      chunkFilename: utils.assetsPath('css/[id].[hash].css')
    }),
    // generate dist index.html with correct asset hash for caching.
    // you can customize output by editing /index.html
    // see https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: process.env.NODE_ENV === 'testing'
        ? 'index.html'
        : config.build.index,
      template: 'index.html',
      inject: true,
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      },
      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
      chunksSortMode(a, b) {
        return (a.names[0] < b.names[0]) ? 1 : -1;
      },
    }),
    // Generates manifest to configure pwa options
    new WebpackPwaManifest({
      name: 'My Company',
      short_name: 'MyCompany',
      description: 'Vue + Vuetify progressive app with Loopback server',
      theme_color: "#004d40",
      start_url: "./",
      background_color: "#fafafa",
      icons: [
        {  
          "src": path.resolve('src/assets/logo.png'),  
          "sizes": "200x200",  
          "type": "image/png"  
        },
        {  
          "src": path.resolve('src/assets/logo-512px.png'),  
          "sizes": "512x512",  
          "type": "image/png"  
        },  
      ]
    }),
    // keep module.id stable when vendor modules does not change
    new webpack.HashedModuleIdsPlugin(),

    // copy custom static assets
    new WorkboxPlugin.GenerateSW({
      swDest: path.join(config.build.assetsRoot, 'sw.js'),
      clientsClaim: true,
      skipWaiting: true,
      exclude: [/\.html$/],
    }),
    new HtmlCriticalPlugin({
      base: path.resolve(__dirname, '../dist'),
      src: 'index.html',
      dest: 'index.html',
      inline: true,
      minify: true,
      extract: true,
      width: 375,
      height: 565,
      penthouse: {
        blockJSRequests: false,
      }
    })
  ]
})

// This option is enabled by default, if nginx or another server already implement
// gzip you can disable it in config/index.js

if (config.build.productionGzip) {
  const CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}

if (config.build.productionBrotli) {
  const BrotliWebpackPlugin = require('brotli-webpack-plugin');

  webpackConfig.plugins.push(
    new BrotliWebpackPlugin({
      asset: '[path].br[query]',
      test: new RegExp(
        '\\.(' +
        config.build.productionBrotliExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}

if (config.build.bundleAnalyzerReport) {
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}

module.exports = webpackConfig

I checked the contents of both files and one is compressed by OptimizeCSSAssetsPlugin and the other is not.

I also want to ask why this css chunks doesn't have the brotli and gziped version.

Thanks

Landwaiter answered 1/6, 2018 at 19:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.