myTerminal
myTerminal

Reputation: 1646

Unable to skip 3rd party library CSS from CSS-Module transformation

I am trying CSS Modules for the first time with React and Webpack and I came across at least three ways to achieve it:

  1. css-loader
  2. react-css-modules
  3. babel-plugin-react-css-modules

I went with babel-plugin-react-css-modules in order to balance code simplicity and performance and everything seems to be working fine except for one thing: my 3rd party libraries (Bootstrap and Font Awesome) are also included in CSS Modules transformation.

<NavLink to="/about" styleName="navigation-button"></NavLink>

The above assigns a properly transformed className to the NavLink. However, a span inside needs to refer to global styles in order to render an icon.

<span className="fa fa-info" />

The above span is not assigned a transformed className which is expected, but my bundled stylesheet does not have these CSS classes as they are being transformed into something else, to simulate local scope.

Below is the content in my .babelrc file to activate babel-plugin-react-css-modules:

{
  "presets": ["env", "react"],
  "plugins": [
    ["react-css-modules", {
      "generateScopedName": "[name]__[local]___[hash:base64:5]",
      "filetypes": {
        ".less": {
          "syntax": "postcss-less"
        }
      }
    }]
  ]
}

In my Webpack configuration, below is the section to configure css-loader for transforms:

{
    test: /\.(less|css)$/,
    exclude: /node_modules/,
    use: extractCSS.extract({
        fallback: 'style-loader',
        use: [
            {
                loader: 'css-loader',
                options: {
                    minimize: true,
                    modules: true,
                    sourceMap: true,
                    localIdentName: '[name]__[local]___[hash:base64:5]'
                }
            },
            {
                loader: 'less-loader'
            }
        ]
    })
}

As far as I have read, the above rule should exclude the library stylesheets and I also tried adding another rule specifically for the excluded stylesheets, however that did not seem to work, as I guess as those stylesheets were still transformed with the original rule.

In order to import CSS from the two libraries, I have the below two lines in my parent stylesheet that declares some global styles:

@import '../../../node_modules/bootstrap/dist/css/bootstrap.min.css';
@import '../../../node_modules/font-awesome/css/font-awesome.min.css';

Upvotes: 4

Views: 2350

Answers (4)

Jon Egerton
Jon Egerton

Reputation: 41579

Update (in 2024):

The documentation for css loader getLocalIdent has:

Allows to specify a function to generate the classname. By default we use built-in function to generate a classname. If the custom function returns null or undefined, we fallback to the built-in function to generate the classname.

So a custom getLocalIdent function can perform the required filter to choose to return localName, and then just return null to fall back to the built in function.

Updating @GasaiYuno's second answer then:

getLocalIdent: (loaderContext, localIdentName, localName, options) => {
  return loaderContext.resourcePath.includes('semantic-ui-css') ?
    localName :
    null;
}

Upvotes: 0

igor
igor

Reputation: 5475

Updated solution from playing771

 {
    loader: 'css-loader',
    options: {
      modules: {
        auto: (resourcePath) => !resourcePath.includes('node_modules'),
        localIdentName: '[name]__[local]__[hash:base64:5]',
      },
    },
 },

Upvotes: 2

yaya
yaya

Reputation: 8273

For me using :global worked :

.my-component {
    :global {
        .external-ui-component {
           padding: 16px;
           // Some other styling adjustments here
        }
        ...
    }
}

Ps: for doing it with webpack config, please see another answer.

source

Upvotes: 4

Gasai Yuno
Gasai Yuno

Reputation: 51

I find these two approaches below might be helpful:

In short, there seems to be no options to ignore/exclude certain paths from being modularized by the css-modules webpack plugin so far. Ideally it should be supported by the plugin, but here're some approaches you can try out:

use two webpack rules to utilise the webpack rule exclusion/inclusion:

module.exports = {
  rules: [
    {
      test: /\.css$/,
      exclude: /node_modules/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            modules: true,
            localIdentName: '[path]__[local]___[hash:base64:5]',
          },
        },
      ],
    },
    {
      test: /\.css$/,
      include: /node_modules/,
      use: ['style-loader', 'css-loader']
    }
  ]
}

...or, inject into webpack's getLocalIdent from the second answer above to manually exclude certain paths.

const getLocalIdent = require('css-loader/lib/getLocalIdent');

{
  loader: 'css-loader',
  options: {
    modules: true,
    localIdentName: '[path][name]__[local]--[hash:base64:5]',
    getLocalIdent: (loaderContext, localIdentName, localName, options) => {
      return loaderContext.resourcePath.includes('semantic-ui-css') ?
        localName :
        getLocalIdent(loaderContext, localIdentName, localName, options);
    }
  }
}

Upvotes: 5

Related Questions