Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: Multiple JS chunks, single CSS chunk #52

Closed
AvraamMavridis opened this issue Mar 23, 2018 · 32 comments
Closed

Question: Multiple JS chunks, single CSS chunk #52

AvraamMavridis opened this issue Mar 23, 2018 · 32 comments

Comments

@AvraamMavridis
Copy link

Is it possible to have multiple JS chunks but a single css output file?

My current setup is:

  optimization: {
    minimize: true,
    minimizer: [
      new UglifyJSPlugin({
        uglifyOptions: {
          compress: {
            passes: 2,
          },
          output: {
            comments: false,
          },
        },
      }),
    ],
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          filename: 'vendorbundle.js',
          test: m => /node_modules/.test(m.context),
        },
      },
    },
  },

...
...

    new MiniCssExtractPlugin({
      filename: 'styles.css',
    }),

This generates 2 JS files, and 2 css files (styles.css, and 1.styles.css)

What I would like to have is a single css file, is that doable?

@Pierre-Do
Copy link
Contributor

Pierre-Do commented Mar 23, 2018

I've been using something like that for the same use-case with success:

  optimization: {
    splitChunks: {
      cacheGroups: {
        default: false,
        // Custom common chunk
        bundle: {
          name: 'commons',
          chunks: 'all',
          minChunks: 3,
          reuseExistingChunk: false,
        },
        // Customer vendor
        vendors: {
          chunks: 'initial',
          name: 'vendors',
          test: 'vendors',
        },
        // Merge all the CSS into one file
        styles: {
          name: 'styles',
          test: /\.s?css$/,
          chunks: 'all',
          minChunks: 1,
          reuseExistingChunk: true,
          enforce: true,
        },
      },
    },
  },

@AvraamMavridis
Copy link
Author

Doesnt seem to work for me

screen shot 2018-03-23 at 13 11 12

@AvraamMavridis
Copy link
Author

@Akkenar can you share all of the config file please?

@Pierre-Do
Copy link
Contributor

Pierre-Do commented Mar 23, 2018

@AvraamMavridis it seems that I was missing an enforce: true on the rule.

You'll find an example project there: https://github.com/Akkenar/CSS-Single-File-Example

Webpack config:

import webpack from 'webpack';
import path from 'path';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin';

const vendors = ['react', 'react-dom'];
const isProduction = process.env.NODE_ENV === 'production';

export default {
  name: 'client',
  mode: isProduction ? 'production' : 'development',
  target: 'web',
  entry: {
    bundle: './entry.js',
    vendors,
  },
  context: path.join(__dirname, 'src'),

  stats: {
    // Disable the verbose output on build
    children: false,
  },

  devtool: isProduction ? false : 'inline-cheap-module-eval-source-map',

  output: {
    path: path.join(__dirname, 'target', 'build'),
    filename: '[name].js',
    chunkFilename: '[name].js',
  },

  resolve: {
    extensions: ['.js', '.jsx', '.scss'],
  },

  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: ['babel-loader?cacheDirectory'],
      },
      {
        test: /\.scss$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
      },
    ],
  },

  optimization: {
    splitChunks: {
      cacheGroups: {
        default: false,
        // Custom common chunk
        bundle: {
          name: 'commons',
          chunks: 'all',
          minChunks: 3,
          reuseExistingChunk: false,
        },
        // Customer vendor
        vendors: {
          chunks: 'initial',
          name: 'vendors',
          test: 'vendors',
        },
        // Merge all the CSS into one file
        styles: {
          name: 'styles',
          test: /\.s?css$/,
          chunks: 'all',
          enforce: true,
        },
      },
    },
  },
  profile: false,

  plugins: [
    new webpack.optimize.ModuleConcatenationPlugin(),
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[name].css',
    }),
    new OptimizeCSSAssetsPlugin(),
    new HtmlWebpackPlugin({
      minify: {
        collapseWhitespace: true,
        preserveLineBreaks: true,
        removeComments: true,
      },
      filename: 'index.html',
      template: path.join(__dirname, 'src', 'index.html'),
    }),
  ],
};

@AvraamMavridis
Copy link
Author

AvraamMavridis commented Mar 23, 2018

@Akkenar It doesnt really work:

screen shot 2018-03-23 at 15 14 40

That is my setup:

const path = require('path');
const loaders = require('./loaders');
const plugins = require('./plugins');
const webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const frontend = require('../package.json');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const DIRECTORY = `${__dirname}/../`;

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: {
    app: ['babel-polyfill', path.join(__dirname, './../src/App.js')],
  },
  optimization: {
    minimize: true,
    minimizer: [
      new UglifyJSPlugin({
        uglifyOptions: {
          compress: {
            passes: 2,
          },
          output: {
            comments: false,
          },
        },
      }),
    ],
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        default: false,
        // Custom common chunk
        bundle: {
          name: 'commons',
          chunks: 'all',
          minChunks: 3,
          reuseExistingChunk: false,
        },
        // Customer vendor
        vendor: {
          filename: 'vendorbundle.js',
          test: m => /node_modules/.test(m.context),
        },
        // Merge all the CSS into one file
        styles: {
          name: 'styles',
          test: /\.s?css$/,
          chunks: 'all',
          minChunks: 1,
          reuseExistingChunk: true,
          enforce: true,
        },
      },
    },
  },
  output: {
    filename: 'bundle.js',
    path: path.join(DIRECTORY, './artifacts/'),
    chunkFilename: 'chunk-[name]-[hash].js',
    publicPath: '/savingglobal/assets/dist/js/',
  },
  plugins: plugins.concat([
    new ProgressBarPlugin(),

    /* Css specific plugins */
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[name].css',
    }),
    new OptimizeCssAssetsPlugin({
      cssProcessor: require('cssnano'),
      cssProcessorOptions: {
        discardComments: { removeAll: true },
        zindex: false,
      },
      canPrint: true,
    }),
    /* Define the enviroment */
    new webpack.DefinePlugin({
      PRODUCTION: JSON.stringify(true),
      'process.env.NODE_ENV': JSON.stringify('production'),
      'process.env': {
        NODE_ENV: JSON.stringify('production'),
        frontend_version: JSON.stringify(frontend.version),
      },
    }),
    new webpack.optimize.ModuleConcatenationPlugin(),
  ]),
  module: {
    rules: loaders.concat([
      {
        test: /\.scss$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
      },
    ]),
  },
  resolve: {
    alias: {
      'raisin-frontend': path.resolve(__dirname, '../../'),
      'ui-lib': '@raisin/ui-lib',
      assets: path.resolve(__dirname, '../assets'),
    },
    extensions: ['.js', '.jsx', '.json', '.scss'],
  },
};

@Pierre-Do
Copy link
Contributor

It seems that the vendors are still generated for the CSS.

You could try:

  • Add vendors: false, in cacheGroups, see if this disables the vendor~ generation
  • Try to add priorities to the rules within cacheGroups, see if you can have the style one being more greedy.

@sokra
Copy link
Member

sokra commented Mar 26, 2018

Do you want to send a PR adding this to the documentation?

minChunks: 1, and reuseExistingChunk: true, are not needed.

@Pierre-Do
Copy link
Contributor

Thanks for your feedback @sokra .

I'll send a PR whenever I find time.

@dtothefp
Copy link

duplicate here #41

@Cople
Copy link

Cople commented Mar 28, 2018

how to prevent styles.js? it's a redundant file.

@sokra
Copy link
Member

sokra commented Mar 28, 2018

how to prevent styles.js? it's a redundant file.

This need to be solved on webpack side

@sokra sokra closed this as completed Mar 28, 2018
@aboeglin
Copy link

Is this supposed to work at the moment ?

@alexander-akait
Copy link
Member

@aboeglin no, please create issue in webpack repo with minimum reproducible test repo. thanks

@mnpenner
Copy link

mnpenner commented Jun 26, 2018

I'm still getting a CSS file for every chunk with

styles: {
    name: 'styles',
    test: /\.(css|less)$/, // not sure if .less is necessary or its already been compiled at this point
    chunks: 'all',
    enforce: true
}

Which seems to be breaking my CSS CSS break was my own dumb fault. cssnano was shrinking my z-indexes. I still haven't managed to produce a single output CSS file though. Probably something else I messed up...

@klis87
Copy link

klis87 commented Aug 2, 2018

I also cannot make it work, whats happens for me is that I have multiple entries (multi real web pages SPA), and after any combination suggested here, I end up with unnecessary styles.js chunk, and, whats worse, without vendor and commons chunks which are generated otherwise.

For now I migrated back to extract-text-plugin@next, which works fine.

@mnpenner
Copy link

mnpenner commented Aug 7, 2018 via email

@Iuriy-Budnikov
Copy link

@mnpenner , please ignore my message. It doesn't resolve the issue.

@ghost
Copy link

ghost commented Sep 26, 2018

@sokra
Why the issue has closed? This is not fixed. At least point people to a direction.

@anastasiap
Copy link

Non of the above proposed solutions worked for me.

I solved the issue by using underscore for scss files in components and then importing it in style.scss the usual way.

  • components
    -- About
    --- _about.scss
    --- About.vue
    --- About.ts

  • scss
    -- style.scss

@import '../components/About/about';

@oswaldofreitas
Copy link

If someone found an answer please post it here.

@rtxanson
Copy link

Give the explanation here a shot: #257 (comment)

I seemed to be having a similar problem and this fixed it.

@rnicholus
Copy link

Can this please be re-opened until a solution is in place?

@alexander-akait
Copy link
Member

@rnicholus solution of what? use splitChunks and configure chunks how you want (one, two, one hundred)

@rnicholus
Copy link

rnicholus commented Dec 7, 2018

I've followed that guidance, and it didn't seem to have any effect. This seems to have been a problem for other posters in this issue as well.

@2-5
Copy link

2-5 commented Dec 7, 2018

This works for me, change the test regular expression to only match .js (and not something like .css):

optimization: {
  splitChunks: {
    cacheGroups: {
      vendor: {
        test: /[\\/]node_modules[\\/].*\.js$/,
        name: 'vendors',
        chunks: 'all',
      },
    },
  },
},

@rnicholus
Copy link

@2-5 After adding that to my webpack config, I'm still seeing about 50 CSS chunks.

@ShubhamChodankar026531
Copy link

To Create a Single css file and create chunk of vendor.js file, can use the following configuration.

optimization: {
      splitChunks: {
         cacheGroups: {
           commons: {
             test: /[\\/]node_modules[\\/](.(?!.*\.css$))*$/,
             name: 'vendors',
             chunks: 'all'
           }
         },
       }
  }

the regex is different from one mentioned in above comments : test: /[\\/]node_modules[\\/].*\.js$/,
in a way that it takes all files apart from .css files in vendors.js...
can use either one based on file types used in project.
this is compatible with Webpack version > 4 at the time.

@matzeeable
Copy link

Stumbled over the same problem but I go one step further because my script imports also .scss and .less files from vendors:

splitChunks: {
    vendor: {
        // [...]
        test: /node_modules.*(?<!\.css)(?<!\.scss)(?<!\.less)$/
        // [...]
    }
}

@StNekroman
Copy link

StNekroman commented Apr 10, 2020

Very bad bug.
It's impossible to fetch all styles to one file.

Found the reason - it was due to my custom webpack config, where I was combining chunks by issuer file.
But for one chunk the issuer was completely separate - that's why some styles gone to separate bundle.

@temple
Copy link

temple commented Apr 14, 2020

@StNekroman, have you tried the formula suggested in the PR #60 , which is already merged?
It works for me, combining in one file shared styles among different entry points dependency graphs, no matter if are vendors or local app ones.

@StNekroman
Copy link

@temple nevermind, it was my issue - comment updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests