Search code examples
webpacksasswebpack-5

Webpack 5 & Sass - index.css does not update with new content from main.css


Summary:

My Webpack no longer converts css from main.css into index.css upon running the build script. I need help figuring this out.

Background info:

I have a plain HTML/Sass website, and I'm attempting to push some CSS updates into production. When I updated to Webpack 5 in April 2023, everything worked fine (with some tweaks)¹. I could modify the code and see it reflected served at localhost.

As I understand it, Webpack converts the .scss code into .css code, and puts it into a main.css file within a .gitignore'd /dist directory. The web server then reads from the main.css file. This works fine and dandy in dev, but deploying into production no longer converts that code into an index.css file.

This worked in April, but now it does not do so, and I cannot figure out why. I've only made changes within my index.html and index.scss files, so the edit/test/deploy process should have remained unchanged.

I've tried searching forums for assistance, but most of those similar issues deal with versions of Webpack much older than v5, and those solutions have become deprecated.

Here's my Webpack:

// Generated using webpack-cli https://github.com/webpack/webpack-cli

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

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

const stylesHandler = MiniCssExtractPlugin.loader;

const config = {
  entry: './src/index.js',
  // disabling cache in watch mode 
  cache: false,
  output: {
    path: path.resolve(__dirname, 'dist'),
    assetModuleFilename: 'public/[name][ext]',
  },
  devServer: {
    open: true,
    host: 'localhost',
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'index.html',
    }),

    new MiniCssExtractPlugin(),
  ],
  module: {
    rules: [
      {
        test: /\.html$/i,
        loader: "html-loader",
      },
      {
        test: /\.(js|jsx)$/i,
        loader: 'babel-loader',
      },
      {
        test: /\.css$/i,
        use: [stylesHandler,'css-loader'],
        sideEffects: true,
      },
      {
        test: /\.s[ac]ss$/i,
        use: [stylesHandler, 'css-loader', 'sass-loader'],
        sideEffects: true,
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif|pdf|eot|ttf|woff|woff2)$/i,
        type: 'asset/resource',
      },
    ],
  },
};

module.exports = () => {
  if (isProduction) {
    config.mode = 'production';
  } else {
    config.mode = 'development';
  }
  return config;
};

¹ MiniCssExtractPlugin is required for sass to work properly & cache: false, for main.css to get live updates in watch mode

Here's my package.json:

{
  "version": "1.0.0",
  "description": "JF Portfolio",
  "name": "jf-portfolio",
  "scripts": {
    "build": "webpack --mode=production --node-env=production",
    "build:dev": "webpack --mode=development",
    "build:prod": "webpack --mode=production --node-env=production",
    "watch": "webpack --watch",
    "serve": "webpack serve & compile:sass",
    "compile:sass": "node-sass -w styles/index.scss src/index.css --recursive"
  },
  "devDependencies": {
    "@babel/core": "^7.21.4",
    "@babel/preset-env": "^7.21.4",
    "babel-loader": "^9.1.2",
    "css-loader": "^6.7.3",
    "html-loader": "^4.2.0",
    "html-webpack-plugin": "^5.5.0",
    "mini-css-extract-plugin": "^2.7.5",
    "node-sass": "^8.0.0",
    "prettier": "^2.8.7",
    "sass": "^1.60.0",
    "sass-loader": "^13.2.2",
    "style-loader": "^3.3.2",
    "webpack": "^5.77.0",
    "webpack-cli": "^5.0.1",
    "webpack-dev-server": "^4.13.1"
  }
}

My basic directory structure:

  • /dist (uncommited directory containing Webpack emitted files)
    • /public (all content stored top-level, no sub-directories)
    • index.html
    • main.css (webpack converts index.scss into css in this file)
    • main.js
  • /public (contains content in sub-directories)
    • /fonts
    • /images
    • /svg-icons
    • adocument.pdf
    • anotherdocument.pdf
  • /src (contains the final "converted" CSS file for import into HTML)
    • index.css (this is the file that is no longer getting updated from main.css)
    • index.js
  • /styles
    • index.scss (this is where I make stylistic changes)
  • index.html

When I initially set up this version of Webpack, updates from index.scss where pushed into main.css live (I think it's called hot-loading?). And when it came time to deploy, code from main.css was pushed into index.css. Basically:

index.scss --> main.css --> index.css
dev --> serve --> deploy

Essentially, index.css is supposed to be the "Okay I'm ready to go live with this" file, and it is the one which is imported in the index.html. However, updates are no longer making it from main.css into index.css.

dev ✅ --> serve ✅ --> deploy ❌

Things I have attempted:

  • I have tried switching from production to development and back
    • npm run build:prod & npm run build:dev
  • I've tried emptying out index.css to see if that will force a refill.
  • I've tried deleting index.css altogether to see if a replacement will get generated.
  • I have deleted and reinstalled my /node_modules.
  • I have read and reread Webpack docs
  • I have searched StackOverlow and GitHub, and the only thing that's close is a GH workaround related to hot-loading(?) in dev, but does not touch on getting updates into the production file.

At the moment, my only recourse is to manually copy the code from main.css into index.css and then fix all the imports/references so they use the correct paths (remember, webpack emits it all into /dist/public with no further sub-directories).

I need help! Please and thank you in advance!


Solution

  • I do believe I found a solution, and it's quite silly. There was an error somewhere along the way with the script which wasn't logged out anywhere obviously in the terminal where the compile:sass script either wasn't running, or there was something else that conflicted with its operation. Live updates on the screen weren't working, and changes saved to index.scss were not being transpiled into index.css.

    I found a StackOverflow comment here about running npm scripts concurrently using an appropriately named npm package: concurrently.

    With concurrently installed, I modified my package.json to be this for my serve command:

    // old
    "serve": "webpack serve & compile:sass",
    
    // new
    "serve": "concurrently --kill-others \"webpack serve\" \"npm run compile:sass\"",
    

    My index.css now receives streaming updates from any changes I make in my index.scss whenever the file is saved.