Webpack dev server - Concat CSS files into one file on file save

1.1k views Asked by At

The problem statement

I have a requirement in a legacy React application which has multiple scss files in a particular directory. The CSS is not imported at the component level, the application uses gulp as a build system where it watches for file changes, spits out corresponding CSS files, merges them, finally injects a link tag into the index.html file(This step need not be in watch mode) and the page gets reloaded.

What is needed

We need to move this from the gulp configuration to using Webpack for the above-mentioned steps.

What I have tried so far - What is working

My initial thoughts were to (Since we are using react-scripts and react-app-rewired)

  1. Use node-sass-chokidar to compile the sass files into CSS and write them into a specific directory.
  2. Once this step is done, in the config-overrides.js, I have used a Webpack plugin called webpack-concat-plugin, to merge the files in a specific directory into a single file and writes it to the build folder.
  3. The index.html has a link tag that points to the location of this file

The above steps are working in production mode.

What I have tried so far - What is not working

I am trying to implement the same in dev mode as well, but I am not able to figure out "How do I run the webpack-concat-plugin everytime node-sass-chokidar spits out css files(it is running in watch mode and runs whenever an scss file changes)". I tried creating a custom Webpack plugin and use the watch mode to tap in, but I not sure about the above-mentioned question. The webpack plugin looks like this.

const pluginName = 'ConcatCSSPlugin';
const ConcatPlugin = require('webpack-concat-plugin');

class ConcatCSSOnFileChangeWebpackPlugin {
  apply(compiler) {
    compiler.hooks.watchRun.tap(pluginName, compilation => {
      console.log('The webpack hook is running');
      /* How should I invoke the ConcatPlugin */
    });
  }
}

module.exports = ConcatCSSOnFileChangeWebpackPlugin;

The config-overrides.js looks like this

module.exports = {
  webpack: function override(config, env) {
    if (env === 'production') {
      config.plugins.push(new ConcatPlugin({
        outputPath: 'build/styles',
        fileName: 'style.css',
        injectType: 'none',
        filesToConcat: ['./dist/compiled-css/**.css'],
        attributes: {
            async: true
        }
      }), new TestPlugin());

      config.stats = {
        all: true
      }
    }
    return config;
  }
}
2

There are 2 answers

0
Cosmin Ababei On BEST ANSWER

You can use the additionalData option from sass-loader to create a pseudo root file which includes all your scss files. To make sure you append it only once, you may need to use something like this:

let appendedOnce = false;

const sassLoaderRule = {
  loader: 'sass-loader',
  options: {
    additionalData: function (content) {
      if (appendedOnce) return content;

      appendedOnce = true;

      return `
        @import 'fileOne.scss'; 
        @import 'fileTwo.scss'; 
        @import 'etc.scss';
      ` + content;
    }
  }
}

Additionally, if there are too many files to import, you can use node-sass-glob-importer and instead write: @import './**/*.scss';. Check out this answer for more details.

4
felixmosh On

You don’t need to invent the wheel.... utilize webpack’s power.

Create a root.scss file which will import (using sass import) all the relevant scss files.

Then import it from you app root js file (you will need to provide loaders for handling sass).

These steps will add your scss files into webpacks depend your tree, that means that in dev mode webpack will watch them an recompile whatever’s needed.

The second part is to use html-webpack-plugin for building & injecting static assets to it.