Share css-modules compiled class names over pre-processers
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
lib
test
.babelrc
.eslintignore
.eslintrc
.gitignore
.travis.yml
LICENSE
README.md
index.js
package.json
webpack.config.js

README.md

premodules-loader

npm Travis

Share css-modules compiled class names over pre-processers!

Install

npm i premodules-loader --save-dev

What's the problem

css-modules is a great workaround to generate scoped css. However since scoped css are tightly bound with js files that import them, when you are trying to write a reusable component, you're possibly getting a headache - how can other developers override the default styles in my components?

So,

What if we can import class name maps into sass/less/stylus code?

How it works

As is explained in this article, this loader executed twice in the webpack workflow by the following order:

  1. Create directives:

  2. Use some directives to interpolate class names into source code - surely it should be converted to sass/less/stylus hashes.

  3. Use variable interpolation syntax to define class name, e.g. ${map-get($comp, 'main')} in sass.

  4. Process css-modules output:

  5. Restore twice-transformed class names to the previous one (both in stylesheet and class map).

  6. Save the class map for interpolation.

workflow

Usage

Webpack config (execute twice):

// webpack.config.js
module.exports = {
  ...
  module: {
    loaders: [{
      test: /\.scss/,
      loaders: [
        'style',
        'premodules?restore', // restore here!
        'css?modules&importLoaders=1&localIdentName=[path]_[name]_[local]_[hash:base64:5]',
        'postcss',
        'sass',
        'premodules?parse', // parse here!
      ]
    }]
  },
  premodules: {
    transformer(varName, hash) {
      // custom code transformer
    }
  }
};

Components define:

// button.js
import React from 'react';
import styles from './button.scss';

const Button = () => (
  <button className={style.button}>click me</button>
);

export default Button;
// button.scss
.button {
  border-radius: 3px;
}

Your react app:

// app.js
import React from 'react';
import './app.scss';

const App = () => (
  <Button />
);
// app.scss
@module './button.scss' => $comp;

#{map-get($comp, 'button')} {
  background: #123;
}

Directive Syntax

// import node_modules
@module 'module_name/dir/file' => $var;

// or use relative/absolute path
@module '/path/of/module' => $var;

Customize Transformer

Default transform is designed for scss code transformation, write your own transformer to support your pre-processor.

Transformer function receives two parameters:

  1. varName:string: variable name defined in directive
  2. hash:object: css-modules exported class name map

A typical transformer may like this:

// scss transformer
// `@module './comp.scss' => $comp` -> `$comp: ( key: value, ... )`

const transformer = (varName, hash) => {
  if (!hash) return '';

  const main = Object.keys(hash).map(key => `${key}: '.${hash[key]}'`).join(',');

  return `${varName}: (${main});`;
};

Limitations

  1. Must import all stylesheets you need into JavaScript code, otherwise it won't be passed through webpack.

  2. Importing files that declared with @module directives in your sass/less/stylus codes may cause breaks (pre-processors won't understand this directive).

License

MIT.