Parcel: How do I exclude a css file from being included as a css module?

Created on 24 Aug 2018  ยท  13Comments  ยท  Source: parcel-bundler/parcel

โ” Question

Can I exclude certain css files from being bundled as css modules?

๐Ÿ”ฆ Context

I'm building a SPA and have split the project into several npm modules. I'm using React, Typescript and PostCSS to CSS Modules for my components. This works fine. However, sometimes I want to use third party react components, that I need to style with custom CSS. In particular I'm currently trying to style the datepicker "react-datetime". To include the default css that comes with the component I simply add

import "react-datetime/css/react-datetime.css"

and the css is bundled correctly.

But, I do not want to use the default styling, so I've added my own css file. To import this one I use:

import "./custom.css";

But this time the css file is imported as a module and all css names are converted and no longer match the css names found in the html produced by the datetime component.

So the question is: Can I exclude certain files from this "css module" processing?

To enable Css Modules in parcel I added a postcss.config.js to the root of my project. It has the following content:

module.exports = {
  "modules": true
};

๐Ÿ’ป Code Sample

๐ŸŒ Your Environment

| Software | Version(s) |
| ---------------- | ---------- |
| Parcel |1.9.7
| Node |8.11.3
| npm/Yarn | 5.6.0
| Operating System | Win10

Question CSS Preprocessing Stale

Most helpful comment

This can also be achieved by importing the necessary css in another css with :global{} rule:

:global {
  @import '~bootstrap/index.css'
}

All 13 comments

After some investigation I found a solution by adding the following to postcss.config.js:

stringHash= require("string-hash");

module.exports = {
  "modules": true,
    "plugins": {
        "postcss-modules": {
      "generateScopedName": 
       function(name, filename, css) {

        if(filename.endsWith("custom.css")) return name;

        const i = css.indexOf(`.${name}`);
        const lineNumber = css.substr(0, i).split(/[\r\n]/).length;
        const hash = stringHash(css)
          .toString(36)
          .substr(0, 5);

        return `_${name}_${hash}_${lineNumber}`;

      }
    ,
            "root": "."
        }
    }

};

This can also be achieved by importing the necessary css in another css with :global{} rule:

:global {
  @import '~bootstrap/index.css'
}

just to add to @SpadarShut's comment:

  • switching a _whole block_ of rules to :global only works if you're importing from a less/sass file (see https://github.com/css-modules/css-modules#usage-with-preprocessors)

  • with vanilla CSS you're stuck with prefixing each and every rule with :global in your custom.css

For me, set postcss.config.js configuration as following works :)

https://github.com/css-modules/postcss-modules#generating-scoped-names

module.exports = {
  modules: true,
  plugins: {
    'postcss-modules': {
      globalModulePaths: [
        // Put your global css file paths.
        'index.css'
      ]
    }
  }
}

Turns out my postcss.config.js was malformed to begin with. Based on @subuta's post I got it working. Thanks!

ideally css modules are only used if suffix .module.css|scss whereas index.css would be treated as vanilla css and index.module.css would be a css module

@subuta Thank you so much for this solution. I've spent a lot of time trying to separate global css files from css modules and avoid weird behavior. My classes inside src/global.css emitted twice in a bundle: normal (as expected) and prefixed right above normal ones. I was surprised when noticed, that plugins declaration in form of array significantly differed from object style. Your config worked well!

Thank you @subuta for this solution, that helped me get css modules working and allowed me to configure it. My challenge, now, is how do I use a custom postcss plugin with that same configuration?

To illustrate:

module.exports = {
  modules: true,
  plugins: {
    'postcss-modules': {
      globalModulePaths: [
        /global.css/
      ]
    },
    autoprefixer: true
  }
}

That works, because plugins is an Object. But plugins as an Object does not let me use a custom plugin, since it is a function. So for example:

const myPostcssPlugin = require('./path/to/my/custom/plugin')

module.exports = {
  modules: true,
  plugins: [
    require('postcss-modules')({
      globalModulePaths: [
        /global.css/
      ]
    }),
    myPostcssPlugin(),
    require('autoprefixer')
  ]
}

Even though this is a valid Postcss configuration (and is the same config as above, with the exception of the custom plugin), this seems to break the CSS modules behavior.

How can I configure Postcss with Parcel to both have a configurable css modules and a custom postcss plugin?

Even though this is a valid Postcss configuration (and is the same config as above, with the exception of the custom plugin), this seems to break the CSS modules behavior.

@thezimmee From reading postcss transform codes, and loadPlugins function and localRequire, try these syntax. (have not tested ๐Ÿ˜)

module.exports = {
  modules: true,
  plugins: {
    'postcss-modules': {
      globalModulePaths: [
        /global.css/
      ]
    },
    './path/to/my/custom/plugin': true,
    autoprefixer: true
  }
}

If ./path/to/my/custom/plugin not found at runtime, then you may try NODE_PATH or adding Module resolution configutation(might not related)
SEE: https://parceljs.org/module_resolution.html

Thanks @subuta for the suggestions. I still can't seem to get it to work. Here's what I've tried:

  1. Relative path (./config/postcss-custom-units.js). This throws an error as if Parcel's looking for the plugin relative to _the source CSS file_ (error: Cannot find module './config/postcss-custom-units.js' from '/.../Projects/.../network-summary').

  2. Relative path with tilde (~/config/postcss-custom-units.js). This causes Parcel's plugin installer to throw an error saying: Failed to install ~. at PromiseQueue.install [as process] (/.../node_modules/parcel/src/utils/installPackage.js:46:11).

  3. Absolute path (/config/postcss-custom-units.js). Throws same error as in #1.

  4. Use the alias feature in package.json, where postcss config refers to it as postcss-custom-units and package.json.alias is "postcss-custom-units": "./config/postcss-custom-units.js". Throws same error as in # 1.

  5. Same as # 4 with alias configured as "postcss-custom-units": "~/config/postcss-custom-units.js". Throws same error as in # 1.

  6. Same as # 4 with alias configured as "postcss-custom-units": "/config/postcss-custom-units.js". Throws same error as in # 1.


If anybody has any ideas, I'm all ears.


UPDATE:
For the record I finally figured it out. I installed the local custom plugin as a local/file dependency:

In package.json:

"devDependencies": {
    "postcss-custom-units": "file:config/postcss-custom-units"
  }

(or add with npm i -D <plugin folder>).

Then in postcss config, just refer to the dependency name:

module.exports = {
  modules: true,
  plugins: {
    'postcss-modules': {
      globalModulePaths: [
        /global.css/
      ]
    },
    'postcss-custom-units': {...},
    autoprefixer: true
  }
}

And it works!!

I'm not able to use Ant Design using the :global approach:

// src/index.less
:global {
  @import '~antd/dist/antd.less';
}
// src/index.js
import './index.less'

image

I'm not able to use Ant Design using the :global approach:

// src/index.less
:global {
  @import '~antd/dist/antd.less';
}
// src/index.js
import './index.less'

image

same problem. Any solution now?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs.

Was this page helpful?
0 / 5 - 0 ratings