webpacksasscss-modulescss-loaderpostcss-loader

Webpack 5 and css-loader aren't compiling @value directive from CSS Modules


We have a React app with Webpack 5. There are some .scss files that are importing values from a component library installed in node_modules using the @value directive from CSS Modules. Everything was working great until css-loader version 3, but we upgraded css-loader to the last version (6.7.1) and now those values aren't compiled in the final code. Before we got the actual value for those variables (a hexadecimal color value) and now we get the name of the variable with some kind of prefix and suffix.

Before we got:

.my-class {
  background: #5efa0b;
}

for @value brightGreen: #5efa0b; in a color.css file that was imported in .scss file. And now we get:

.my-class {
  background: i__const_brightGreen_135;
}

The .scss files are like:

@value brightGreen from '~@MyComponentsLibrary/src/Styles/colors.css'

.my-class {
  color: brightGreen;
}

and colors.css is something like:

@value brightGreen: #5efa0b;
@value brightBlue: #4664ec 

//more colors

The actual Webpack configuration is this:

const path = require('path');
const webpack = require('webpack');
const packageJson = require('./package.json');

module.exports = {
  entry: {
    bundle: ['whatwg-fetch', '@babel/polyfill', './src/js/index.js'],
    depVersions: './src/depVersions'
  },
  resolve: {
    // allows importing without file extension for these
    extensions: ['*', '.js', '.json', '.jsx'],
    modules: [path.resolve(__dirname, './src/js'), 'node_modules'],
    fallback: {
      'os': require.resolve('os-browserify/browser'),
      'buffer': require.resolve('buffer')
    }
  },
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: '[name].js',
    libraryTarget: 'umd'
  },
  externals: {
    '@MyComponentsLibrary': 'components-library',
    'react': 'react',
    'react-dom': 'react-dom'
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        loader: 'babel-loader',
        include: [path.resolve(__dirname, 'src/')]
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  'postcss-modules-values'
                ]
              },
            }
          },
          {
            loader: 'sass-loader',
            options: {
              sourceMap: true
            }
          }
        ]
      },
      {
        test: /\.woff(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 10000
          }
        },
        generator: {
          dataUrl: {
            mimetype: 'application/font-woff'
          }
        }
      },
      {
        test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, 
        type: 'asset'
      },
      { 
        test: /\.(png|jpg)$/, 
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8192 // inline base64 URLs for <=8k images, direct URLs for the rest
          }
        }
      }
    ]
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development'),
        'npm_package_name': JSON.stringify(packageJson.name),
        'npm_package_version': JSON.stringify(packageJson.version)
      }
    }),
  ]
}

And these are the versions of the relevant packages installed.

    "my-components-library": "2.2.0",
    "css-loader": "6.7.1",
    "os-browserify": "^0.3.0",
    "sass": "^1.53.0",
    "sass-loader": "^13.0.2",
    "style-loader": "^3.3.1",
    "webpack": "^5.73.0",
    "webpack-cli": "^4.10.0",
    "webpack-dev-server": "3.11.3",
    "webpack-merge": "^5.8.0"
    "bootstrap-sass": "^3.4.3",
    "node-sass": "4.14.1",
    "postcss": "^8.4.14",
    "postcss-loader": "^7.0.1",
    "postcss-modules-values": "^4.0.0",
    "webworkify-webpack": "^2.1.5",
    "whatwg-fetch": "^3.6.2"

I have very little experience with Webpack so maybe (for sure) I'm missing something. I read the documentation for Webpack, css-loader and postcss-modules-values, but I couldn't find an answer. I also tried two things:

  1. Remove the ~ character from the first line of the .scss files, which didn't work.
  2. If I declare the value in the .scss file where it is used, it works... If I have a .scss file like this the value is compiled:
@value brightGreen: #5efa0b;

.my-class {
  color: brightGreen;
}

but the idea is to be able to use the values from our components library.

I really appreciate any help you can provide. Let me know if you need more information.


Solution

  • The @value directive is a CSS module feature. I know that css-loader will by default only apply the CSS module specification to files ending in .module.css (source). Can you try renaming colors.css to colors.module.css or updating css-loader or postcss-loader configs to consider all .css files as modules (this option will decrease performance)?