5

In Gatsby v2 you could enable this syntax className={styles["block__element--modifier"]} by adding the following code to gatsby-node.js:

const process_rule = rule => {
  if (rule.oneOf) {
    return {
      ...rule,
      oneOf: rule.oneOf.map(process_rule),
    };
  }

  if (Array.isArray(rule.use)) {
    return {
      ...rule,
      use: rule.use.map(use => {
        const css_loader_regex = /\/css-loader\//;

        if (!css_loader_regex.test(use.loader)) {
          return use;
        }

        return {
          ...use,
          options: {
            ...use.options,
            camelCase: false,
          },
        };
      }),
    };
  }

  return rule;
};

exports.onCreateWebpackConfig = ({ getConfig, actions }) => {
  const config = getConfig();

  const new_config = {
    ...config,
    module: {
      ...config.module,
      rules: config.module.rules.map(process_rule),
    },
  };
  actions.replaceWebpackConfig(new_config);
};

However Gatsby v3 is using css-loader v5 which no longer accepts the camelCase: false field in options. Instead it has the field exportLocalsConvention which can take the value asIs, but when enabling namedExport the compiler throws the error:

The "modules.namedExport" option requires the "modules.exportLocalsConvention" option to be "camelCaseOnly" or "dashesOnly".

I've tried:

options: {
  ...use.options,
  modules: {
    ...use.options.modules,
    auto: true,
    exportLocalsConvention: "asIs",
    exportOnlyLocals: false,
    namedExport: false,
  },
},

But this doesn't make the CSS class names accessible in JSX and gives the warning:

warn Attempted import error: 'block' is not exported from './styles.module.css' (imported as 'styles').

It looks like namedExport must be set to true in order to access the compiled class names in JSX however then css-loader will only accept camelCaseOnly or dashesOnly for exportLocalsConvention.

I'm not sure how to enable the same functionality as Gatsby v2 in v3. I'd like to be able to continue using the className={styles["block__element--modifier"]} syntax because it makes identifying CSS classes easy and also just keeps things consistent between CSS and JSX (also I'd prefer not having to rewrite a bunch of my code).

I'm importing styles like the Gatsby v2 to v3 migration guide explains:

import * as styles from "./styles.module.css"

I've also tried importing them in the old way (import styles from "./styles.module.css") but it made no difference unfortunately.

pks
  • 91
  • 1
  • 8
  • 1
    Can you share how are you importing/exporting the CSS modules? Just to check – Ferran Buireu Mar 15 '21 at 07:39
  • Yeah of course. I'm importing styles like this: `import * as styles from "./styles.module.css"` as per the Gatsby v2 to v3 migration guidelines. I've also tried importing them in the old way (`import styles from "./styles.module.css"`) but it made no difference to my problem. I'm not exporting the CSS in any way though. They are just plain CSS files. Should I be doing something differently? – pks Mar 15 '21 at 08:37
  • 2
    Hi @pks did you make any progress on this topic? Can you operate gatsby v3 with a previous version of the css-loader as temporary workaround? – soosap Apr 28 '21 at 09:34
  • 1
    @soosap no unfortunately I haven't made any progress yet so I opted to continue using Gatsby v2 for the time being. I've been very busy lately so I haven't had the chance to try using a previous version of css-loader. I think I attempted it before I wrote this question but got stuck. I'll definitely post an update here if I get it working though. – pks May 03 '21 at 00:38
  • 1
    @pks i assume no solution found or? Also struggling with that issue. – ius May 10 '21 at 12:25
  • @ius no not yet unfortunately. I'm still very busy and haven't had the chance to dive into it yet. – pks May 24 '21 at 03:16

1 Answers1

1

So it turns out the issue is with the esModule field in the css-loader options. It is enabled by default and:

generates JS modules that use the ES modules syntax. There are some cases in which using ES modules is beneficial, like in the case of module concatenation and tree shaking.

As far as I understand it, this converts CSS class names into JS variables, which can't have hyphens so instead they are changed to camelcase.

So to keep the hyphens in the CSS class names and use the syntax className={styles["block__element--modifier"]) we need to override the css-loader options with:

options: {
  ...use.options,
  esModule: false,
  modules: {
    exportLocalsConvention: "asIs",
    namedExport: false,
  },
},

However I am still running into a build error when passing these options directly to webpack config through gatsby-node.js but have found a workaround using the Gatsby plugin gatsby-plugin-postcss (only 129B minified + gzipped) with these options in gatsby-config.js:

{
  resolve: "gatsby-plugin-postcss",
  options: {
    cssLoaderOptions: {
      esModule: false,
      modules: {
        exportLocalsConvention: "asIs",
        namedExport: false,
      },
    },
  },
},

This will produce the warning:

warn You did not set any plugins, parser, or stringifier. Right now, PostCSS does nothing. Pick plugins for your case on https://www.postcss.parts/ and use them in postcss.config.js.

However it's just a warning that doesn't cause any other issues!

With this implementation you will need to import styles using the Gatsby v2 method:

import styles from "./styles.module.css";

Bear in mind that this prevents the default tree shaking behaviour of Gatbsy v3 / css-loader v5.

Also it turns out this was actually covered in the migration guide here. It however does not indicate to use exportLocalsConvention: "asIs" which, from my testing, is required.

pks
  • 91
  • 1
  • 8