Skip to content

tomasz-sodzawiczny/rfc-pre-post-bundling-processing

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 

Repository files navigation

Pre- and post-bundling processing

An attempt to explain different ways of combining CSS Modules with other CSS processing tools.

Table of contents:
  1. CSS Modules build process
  2. Processing opportunities
  3. Implementation with webpack and PostCSS

CSS Modules build process

Files using CSS Modules syntax need to go through a few transformation steps before they become browser-friendly:

  1. Compilation to ICSS
  2. ICSS value resolution
  3. CSS Bundling (optional)

Compilation to ICSS

All input files with the programmer-friendly syntax like @value or composes are compiled to Interoperable CSS (ICSS).

ICSS value resolution

All :import statements in ICSS are resolved and imported symbols usages are replaced in the file with the actual values.

CSS bundling (optional)

In most common setups the resolved CSS modules are finally merged into a single CSS file (bundle).


Processing opportunities

Usually CSS Modules are not the only CSS transformation used in the project - many other processors can be used to enhance the styling capabilities even more.

The often overlooked opportunity is that the additional processing can be done in any of the steps in the above chain.

In particular 3 spots can be defined:

  • before any CSS Modules transformations
  • after value resolution
  • after bundling

The important thing is - processing (even with the same tool) may bring different results depending on when it's applied. Some tools may work in any case, some may require to be run in a specific step.

Example 1: @each

Any transformation that might result in changing the exported class names must happen before any CSS Modules processing (before compilation to ICSS).

This category contains all scripting extensions to CSS, like postcss-each.

Playground: https://codesandbox.io/s/0mkrv3jrqn

Input

@each $color in red, green, blue {
  .background-$(color) {
    background: $(color);
  }
}

Output (correct)

._input_css_406__background-red {
  background: red;
}
._input_css_406__background-green {
  background: green;
}
._input_css_406__background-blue {
  background: blue;
}
:export {
  background-red: _input_css_406__background-red;
  background-green: _input_css_406__background-green;
  background-blue: _input_css_406__background-blue;
}

Output (incorrect)

Following ouput will be generated if the transformation of @each will happen on CSS already compiled to ICSS.

._input_css_410__background-red {
  background: red;
}
._input_css_410__background-green {
  background: green;
}
._input_css_410__background-blue {
  background: blue;
}
:export {
  background-$(color): _input_css_410__background-$(color);
}

Example 2: calc

Any transformation that relies on the imported values must be done after the value resolution.

Commonly used examples are postcss-calc and postcss-color-function.

Note: postcss-modules-values-replace was created specifically to solve this problem. It's a good, working solution, but it results in unnecessary duplication of the bundler/resolver logic.

Input

/* sizes.css */
@value size_m: 8px;
/* index.css */
@value size_m from './sizes.css';

.foo {
  width: calc(2 * size_m);
}

Output (correct)

.foo {
  width: 16px;
}

Output (incorrect)

Following ouput will be generated if the transformation of calc will happen on unresolved ICSS values.

.foo {
  width: calc(2 * 8px);
}

Example 3: @extend

Some processors may require to be run not only after the value resolution, but on the bundled output.

Example: postcss-modules-extend-rule.

Implementation with webpack and PostCSS

CSS processing before and after bundling can be easily achieved with one of the most common setups: webpack + PostCSS.

CSS Modules bundling

The CSS Modules resolution and bundling is done in webpack by combining css-loader and MiniCssExtractPlugin:

// in webpack.config.js
{
 module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: { modules: true }
          },
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css",
      chunkFilename: "[id].css"
    })
  ]
}

Pre-bundling processing

The input CSS files can be processed before the CSS Modules resolution and bundling by applying postcss-loader before the css-loader:

// in webpack.config.js
{
 module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: { modules: true }
          },
          {
            loader: "postcss-loader",
            options: { plugins: [/* anything you want */]}
          }
        ]
      }
    ]
  },
}

Post-bundling processing

The output CSS bundle can be processed after the CSS Modules bundling by using PostCSSAssetsPlugin after the MiniCssExtractPlugin:

// in webpack.config.js
{
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css',
    }),
    new PostCSSAssetsPlugin({
      test: /\.css$/,
      plugins: [
        /* anything you want */
      ],
    }),
  ];
}

A fully working example can be found in postcss-modules-extend-rule test webpack.config.js

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published