The correct way to bundle CSS modules

Viewed 1936

I have a weird thing happening when my css modules are exported with the * as styles becomes inaccessible when I bundle my code and use it in other repo's.

The response from styles when bundled:

{default: {... my class names} }

When I change my code to import styles from '...' it works when bundled because styles is the default but fails the tests because styles does not have access to the named exports.

rollup config.js


import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import typescript from 'rollup-plugin-typescript2'
import { terser } from 'rollup-plugin-terser'
import postcss from 'rollup-plugin-postcss'
import postCssConfig from '@cinch-labs/postcss-config'
import pkg from './package.json'
import { designTokens, toJSON } from './src/tokens'

const extensions = ['.ts', '.tsx']

// stylelint does work but the postcss one needed to be removed
const postcssPlugins = postCssConfig(toJSON(designTokens)).filter(
  ({ postcssPlugin }: { postcssPlugin: string }) => postcssPlugin !== 'stylelint',
)

export default [
  {
    input: './src/index.ts',
    output: [
      {
        file: pkg.main,
        format: 'cjs',
      },
      {
        file: pkg.module,
        format: 'es',
      },
    ],
    plugins: [
      postcss({
        modules: true,
        extract: false,
        syntax: 'postcss-scss',
        plugins: postcssPlugins,
        use: ['sass'],
      }),
      resolve({
        extensions,
      }),
      commonjs(),
      typescript({ tsconfig: 'tsconfig.rollup.json' }),
      terser(),
    ],
    external: ['react', 'react-dom'],
  },
]


test.component.tsx

import React from 'react'
import classNames from 'classnames'

// I expected the bundler to resolve this for me...
import * as styles from './text.module.scss'

import { TextProps } from './text.types'

export const Text: React.FC<TextProps> = ({
  children,
  fontSize = 'm',
  fontWeight = 'medium',
  fontStyle = 'normal',
  lineHeight = 'body',
  element = 'p',
  className,
  ...props
}) => {
  const HtmlEl = element

  const classes = classNames(
    {
      [styles[`textSize${fontSize.toUpperCase()}`]]: fontSize,
      [styles[`textWeight${fontWeight.toUpperCase()}`]]: fontWeight,
      [styles[`textLineHeight${lineHeight.toUpperCase()}`]]: lineHeight,
      [styles[`textFontStyle${fontStyle.toUpperCase()}`]]: fontStyle,
    },
    className,
  )

  // classes returns undefined when bundled because of commonjs format.

  return (
    <HtmlEl className={classes} {...props}>
      {children}
    </HtmlEl>
  )
}

I know this is due to the way common JS works however I would expect for the import * as styles to work. When I change it to import styles from './text.module.scss' it works fine when bundled but does not work in tests.

3 Answers

Using import * as styles from './text.module.scss' you are importing the styles as a named export. Since this also returns {default: {... my class names} }, you can use styles.default instead, or, perhaps, assign it to a new variable like const style = styles.default

for me to compile and include with rollup.js the scss into the bundle/build worked adding:

plugins: [
      postcss({
        modules: true,
        extract: false,
        syntax: 'postcss-scss',
        use: ['sass'],
      }),
],

Hope this will help someone else in this journey :)

Related