3

I've recently upgrade my webpack version from 3 to 4.x. Since then, I've noticed that I'm having some style collisions under certain circumstances.

For instance, I have two different components named PlanCard that are in two different directories within an application. For the sake of brevity, let's say the directory structure resembles something like:

- app
+ - Onboarding
  + - OnboardingMembership
    + - PlanCard
      + PlanCard.jsx
      + PlanCard.module.scss 
+ - Settings
  + - ClientSelect
    + - PlanCard
      + PlanCard.jsx
      + PlanCard.module.scss

However, I'm seeing that styles from the OnboardingMembership/PlanCard are getting applied to the ClientSelect/PlanCard when it's used within the application. For instance, on a page within the ClientSelect, which is importing the ClientSelect/PlanCard, I see the following in the Element inspector:

enter image description here

Another peculiar thing I've noticed when searching for PlanCard in the css files coming bundled with the application is the following:

enter image description here

What stands out to me is the hash. To my knowledge, these values should be different and I suspect that's where this clash is coming from.

To be complete, I'll include some of the relevant webpack files. Bear with me, as it's a fairly large application so the webpack files are broken up across several files:

// webpack.dev.js
/* eslint-env node */
const merge = require('webpack-merge')

const common = require('./webpack.common')
const plugins = require('./webpack/plugins')
const outputs = require('./webpack/outputs')
const modules = require('./webpack/modules')

module.exports = merge(common, {
  mode: 'development',
  devtool: 'cheap-module-source-map',
  output: outputs.client,
  module: modules.client,
  plugins: [...plugins.client, ...plugins.clientDev],
})

// webpack.common.js

/* eslint-env node */

const { ROOT_PATH, path } = require('./webpack/constants')
const entries = require('./webpack/entries')
const resolves = require('./webpack/resolves')

module.exports = {
  context: path.join(ROOT_PATH, 'frontend'),
  optimization: {
    splitChunks: {
      name: 'common',
      minChunks: 3,
    },
  },
  entry: entries.client,
  resolve: resolves.client,
}
// webpack/plugins.js
'use strict'
/* eslint-env node */

const {
  extractCSSModules,
  extractCSS,
  extractCSSModulesProd,
  extractCSSProd,
  extractCreatorSCSS,
} = require('./constants')

const ManifestPlugin = require('webpack-manifest-plugin')

const plugins = {}

plugins.client = [
  new ManifestPlugin({
    generate: generateManifest,
  }),
]

plugins.clientDev = [
  extractCSS,
  extractCSSModules,
]

plugins.clientProd = [
  extractCSSProd,
  extractCSSModulesProd,
]


module.exports = plugins

// webpack/outputs.js
'use strict'
/* eslint-env node */

const { path, ROOT_PATH, outputTemplates } = require('./constants')

const outputs = {}

outputs.client = {
  filename: outputTemplates.development.js,
  chunkFilename: outputTemplates.development.jsChunk,
  path: path.join(ROOT_PATH, '/public/build'),
  publicPath: '/build/',
}

outputs.clientProd = {
  filename: outputTemplates.production.js,
  chunkFilename: outputTemplates.production.jsChunk,
  path: path.join(ROOT_PATH, '/public/build'),
  publicPath: '/build/',
}

outputs.testUtils = {
  filename: '[name].bundle.js',
  path: path.join(ROOT_PATH, 'frontend_build/testUtils'),
}

module.exports = outputs
// webpack/module.js
'use strict'
/* eslint-env node */

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

const { path, ROOT_PATH } = require('./constants')

const modules = {}

const isDevelopment = process.env.NODE_ENV === 'development'

modules.client = {
  rules: [
    {
      test: /\.(js|jsx)$/,
      use: 'babel-loader',
      exclude: /node_modules/,
    },
    {
      test: /\.module\.(css|scss)$/,
      use: [
        isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
        {
          loader: 'css-loader',
          query: {
            modules: true,
            namedExport: true,
            sourceMap: isDevelopment,
            camelCase: true,
            importLoaders: true,
            localIdentName: '[name]__[local]__[hash:base64:5]',
          },
        },
        {
          loader: 'postcss-loader',
          options: {
            plugins: () => [
              require('autoprefixer')({
                browsers: ['> 1%', 'last 2 versions'],
              }),
            ],
          },
        },
        { loader: 'sass-loader' },
      ],
      include: path.join(ROOT_PATH, 'frontend'),
      exclude: path.join(ROOT_PATH, 'frontend', 'css'),
    },
    {
      test: /\.scss$/,
      use: [
        isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
        { loader: 'css-loader' },
        {
          loader: 'postcss-loader',
          options: {
            plugins: () => [
              require('autoprefixer')({
                browsers: ['> 1%', 'last 2 versions'],
              }),
            ],
          },
        },
        { loader: 'sass-loader' },
      ],
      include: path.join(ROOT_PATH, 'frontend', 'css'),
    },
    {
      test: /\.scss$/,
      include: '/node_modules/foundation/scss',
      use: [
        isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
        {
          loader: 'css-loader',
          query: {
            namedExport: true,
            importLoaders: true,
          },
        },
        {
          loader: 'postcss-loader',
          options: {
            plugins: () => [
              require('autoprefixer')({
                browsers: ['> 1%', 'last 2 versions'],
              }),
            ],
          },
        },
        { loader: 'sass-loader' },
      ],
    },
    {
      test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
      use: [
        {
          loader: 'file-loader',
          options: {
            name: '[name].[hash].[ext]',
          },
        },
      ],
    },
  ],
}

modules.clientProd = {
  rules: [
    {
      test: /\.(js|jsx)$/,
      use: 'babel-loader',
      exclude: /node_modules/,
    },
    {
      test: /\.module\.(css|scss)$/,
      use: [
        isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
        {
          loader: 'css-loader',
          options: { modules: true, exportOnlyLocals: true },
        },
        {
          loader: 'postcss-loader',
          options: {
            plugins: () => [
              require('autoprefixer')({
                browsers: ['> 1%', 'last 2 versions'],
              }),
            ],
          },
        },
        { loader: 'sass-loader' },
      ],
      include: path.join(ROOT_PATH, 'frontend'),
      exclude: path.join(ROOT_PATH, 'frontend', 'css'),
    },
    {
      test: /\.scss$/,
      use: [
        isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
        { loader: 'css-loader' },
        {
          loader: 'postcss-loader',
          options: {
            plugins: () => [
              require('autoprefixer')({
                browsers: ['> 1%', 'last 2 versions'],
              }),
            ],
          },
        },
        { loader: 'sass-loader' },
      ],
      include: path.join(ROOT_PATH, 'frontend', 'css'),
    },
    {
      test: /\.scss$/,
      include: '/node_modules/foundation/scss',
      use: [
        {
          loader: MiniCssExtractPlugin.loader,
        },
        { loader: 'style-loader' },
        {
          loader: 'css-loader',
          query: {
            namedExport: true,
            importLoaders: true,
          },
        },
        {
          loader: 'postcss-loader',
          options: {
            plugins: () => [
              require('autoprefixer')({
                browsers: ['> 1%', 'last 2 versions'],
              }),
            ],
          },
        },
        { loader: 'sass-loader' },
      ],
    },
    {
      test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
      use: [
        {
          loader: 'file-loader',
          options: {
            name: '[name].[hash].[ext]',
          },
        },
      ],
    },
  ],
}

module.exports = modules
// webpack/constants.js

'use strict'
/* eslint-env node */

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

const ROOT_PATH = path.resolve(__dirname, '../../')

const outputTemplates = {
  production: {
    scss: '[name].scss',
    css: '[name].[hash].css',
    cssModule: '[name].modules.[hash].css',
    js: '[name].[hash].js',
    jsChunk: '[name].chunk.[chunkhash].js',
  },
  development: {
    scss: '[name].scss',
    css: '[name].css',
    cssModule: '[name].modules.css',
    js: '[name].js',
    jsChunk: '[name].chunk.js',
  },
}

const extractSCSS = new MiniCssExtractPlugin({
  filename: outputTemplates.development.scss,
  allChunks: true,
})

const extractCSSModules = new MiniCssExtractPlugin({
  filename: outputTemplates.development.cssModule,
  allChunks: true,
})
const extractCSS = new MiniCssExtractPlugin({
  filename: outputTemplates.development.css,
  allChunks: true,
})

const extractCSSModulesProd = new MiniCssExtractPlugin({
  filename: outputTemplates.production.cssModule,
  allChunks: true,
})
const extractCSSProd = new MiniCssExtractPlugin({
  filename: outputTemplates.production.css,
  allChunks: true,
})

const extractCreatorSCSS = new MiniCssExtractPlugin({
  filename: 'creator.bundle.css',
  allChunks: true,
})


module.exports = {
  webpack,
  path,
  ROOT_PATH,
  extractCSSModules,
  extractCSS,
  extractSCSS,
  extractCSSModulesProd,
  extractCSSProd,
  extractCreatorSCSS,
  outputTemplates,
}

If you're still with me, I really appreciate you giving me your time. My specific questions are:

  1. What exactly is causing this naming collision (or not properly generating a new hash for a different component with the same name)?
  2. What about my configuration should change?
  3. Although I use webpack everyday, I've ever really dug into it enough to understand the internals or best practices. If there's anything else you spot that can be done better, please feel free to call it out in a comment.
wheresmyspaceship
  • 870
  • 2
  • 12
  • 19
  • Have you tried giving them different names? – James South Aug 18 '20 at 21:32
  • @JamesSouth Yes but it's a fairly large team so there's no guarantee that this won't happen again. Additionally, to my understanding, css-modules was designed to handle cases like this (if done properly) so I'm also interested in figuring out what's going on here. – wheresmyspaceship Aug 18 '20 at 21:36

0 Answers0