npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

postcss-es-modules

v2.2.0

Published

Universal styles injection to work with postcss-es-modules

Downloads

26,854

Readme

postcss-es-modules

Postcss plugin which transform css files in to the .js/.ts css modules.

What is this? For example, you have the following CSS:

/* ./component.module.css */
.article {
    font-size: 16px;
}
.title {
  font-size: 24px;
}
.title:hover {
  color: red;
}

After the transformation it will become like this:

/* ./component.module.css.js */
// File generated by the postcss-es-modules plugin. Please do not modify it !!!
import { injectStyles } from 'css-es-modules';
const key = '77d26384-99a7-48e0-9f08-cd25a85864fb';
const css =`._article_9u0vb_1 {
    font-size: 16px;
}
._title_9u0vb_9 {
    font-size: 24px;
}
._title_9u0vb_9:hover {
    color: red;
}
`;
const styles = {
    get ['article']() { injectStyles(key, css); return '_article_9u0vb_1 article'; },
    get ['title']() { injectStyles(key, css); return '_title_9u0vb_9 title'; },
    inject() { injectStyles(key, css); }
};
export default styles;

Then that code can be referred from your component.

/* ./component.jsx */
import styles from './component.module.css.js';
export const Component = () => (
    <div className={styles.article}>
        <div className={styles.title}>Title</div>
    </div>
)

Description

This plugin allows you to write the styles in css/sass/less/... syntax and transform it on the build time in the efficient js/ts code which can be simple referred from your component/application code. That code is responsible for attach the styles into the DOM (or to SSR pipeline) in most possible efficient way. This plugin also generates scoped class names, so you can be sure that your components styles will not leak in to the other parts of the application.

Features

  • Zero runtime dependencies (on eject or embed mode, pleas look at the options)
  • Framework agnostic
  • Javascript and Typescript support
  • Server side rendering
  • Lazy, on demand, instant or none styles injection
  • Rich (optional) configuration

Installation

npm i postcss postcss-es-modules --save-dev

Usage

Configure the postcss to use the plugin:

// postcss.config.js
const { postcssEsModules }  = require('postcss-es-modules');
module.exports = (ctx) => ({
    plugins: [
        postcssEsModules({
            // options
        }),
    ]
})

Please remember that this plugin is generating non css syntax, so it should be last on the list of the used plugins.

This plugin is internally using postcss-modules plugin, so you do not have to add it by self.

With the postcss-cli

postcss src/**/*.module.css --dir src --base src --ext css.js

This command will generate the .js files by the post-cli, directly to your src directory.

With the postcss-cli and typescript

// postcss.config.js
const { postcssEsModules }  = require('postcss-es-modules');
module.exports = (ctx) => ({
    plugins: [
        postcssEsModules({ inject: { scriptType: 'ts' } }),
    ]
})
postcss src/**/*.module.css --dir src --base src --ext css.ts

With the webpack/rollup/...

There is nothing unique to use of this plugin with bundlers.

With the webpack/rollup/... and typescript

If you are using typescript, and you do not want to generate es css modules ahead but just let the bundler do the job, for solving typescript compilation errors please add global declaration to your project, like that:

/* ./global.d.ts */
declare module "*.css" {
    type Styles = { [className: string]: string; } & { inject(): void; }
    export const styles: Styles;
    export const key: string;
    export const css: string;
    export default styles;
}

This will say to the compiler that each *.css import should be mapped to declared type.

Other usage examples

You can find more examples here.

Options

Here is the list of all available options, for more details please go to Recipes.

| Option | Type | Default | Description | | --- | --- | --- | --- | | inject | object | - | The configuration fo the styles injection | | inject.useNounce | string | - | The style nounce key | | inject.useConstructableStylesheet | boolean | true | Use Constructable Stylesheet for styles injection to DOM if the browser supports Constructable Stylesheet | | inject.useStyleTag | boolean | true | Use <style> tag for styles injection to DOM | | inject.useNodeGlobal | boolean | true | Enable node.js global for collecting the styles, required for server side rendering | | inject.moduleType | 'cjs' / 'esm' | 'esm' | Generated code modules type. Options: - esm: ecmascript 6 modules - cjs: commonjs | | inject.injectMode | 'lazy' / 'ondemand' / 'instant' / 'none' | 'lazy' | The mode of the styles injection. Options: - lazy - the stylesheet will be injected on the first use of the style class name within the code- ondemand - the stylesheet will be injected only when the styles.inject() method will be called- instant - the stylesheet will be injected on the module load- none - the stylesheet will be not injected, in order to inject it you will need to import css raw string from the module, and inject it manually | | inject.script | 'embed' / 'eject' / 'import' | 'import' | The way how the styles injector script will be referred from the generated source. Options:- embed: embedding styles injector script in to the target source (so each generated file will contains the loader inside)- eject: the styles injector script will be ejected to the provided inject.scriptEjectPath- import: the styles injector script will be referred by the import statement | | inject.scriptType | 'ts' / 'js' | 'js' | The generated script type. Options:- ts: typescript- js: javascript | | inject.scriptEjectPath | string | - | The path where the styles injector script code will be ejected. This option is required if inject.script is eject | | inject.custom | object| - | The custom style injector configuration | | inject.custom.importStatement | string| - | The custom style injector statement for import required dependencies. Eg: "import { injectMyStyles } from 'somelib'"; | | inject.custom.injectStatement | string| - | The custom style injector statement for executing the injection. There are available two constants in the context:- css - the raw css string code- key - unique key of the stylesheetEg: "injectMyStyles(css)" | | modules | object| - | The CSS Modules options. It is inherits all options from the postcss-modules expect getJSON | | modules.attachOriginalClassName | boolean| false | The CSS Modules options. If you want to still use the original class name next to local one |

Recipes

Server side rendering

Example of server side rendering with the React and Express.js:

/* ./index.css */
.app {
    background-color: red;
}
// ./index.js
const express = require('express');
const { createElement } = require('react');
const { renderToString } = require('react-dom/server');
const { collectStyles } = require('css-es-modules');
// imports the transformed css file
const { styles } = require('./index.module.css.js');

const app = express();

// example component to render
const App = () => createElement('div', { className: styles.app}, "Hello Word");

/**
 * App template
 * @param styles - the StylesCollector object
 * @param html - prerendered app markup
 */
const template = (styles, html) => `
    <html lang="en">
        <head>${styles.html}</head>
        <body><div id="app">${ html }</div></body>
    </html>`;

// handling request
app.get('/', (req, res) => {
    res.send(
        // render template
        template(
            // firstly start collecting styles
            collectStyles(),
            // then render application
            renderToString(createElement(App))));
});

// starting app
app.listen(3000);

To run this example you have to transpile css file ahead. With the inject.moduleType set to cjs. The full working example you will find here.

Lazy/On demand/Instant/None styles injection

There are few modes how the styles injection can work.

Lazy (default)

The lazy injection means that the generated stylesheet will be not attached to the DOM/Node globals until some code will not call the getter of className. So even you are importing module with styles that styles are not applied to the application until some of the components will not use the class. This technique is very useful beacause on the server side rendering we will reder just critical stylesheets.

import { injectStyles } from 'css-es-modules';
const key = '77d26384-99a7-48e0-9f08-cd25a85864fb';
const css =`._title_9u0vb_9 {
    font-size: 24px;
}
`;
const styles = {
    get ['title']() { injectStyles(key, css); return '_title_9u0vb_9 title'; },
    inject() { injectStyles(key, css); }
};
export default styles;

None

In this mode the provided module will not provide any way for the styles attaching to the DOM/Node globals. So you will have to take exported raw css code and do that by self. You can use various libraries for that like lit-element.

const key = 'h6TLzUjXxsnSeNRWMPxAjG';
const css =`._title_6jm2u_1 {
    font-size: 24px;
}
`;
const styles = {
    ['title']: '_title_6jm2u_1',
    inject() { throw "This stylesheet can't be injected, instead please use exported css constant." }
};
export { styles, css, key };
export default styles;

On demand

This mode gives possibility to call the styles.inject() method for manual on demand styles injection. This mode should be used in case when you wants to use Shardow DOM.

import { injectStyles } from 'css-es-modules';
const key = 'e1ph4XxYADCPaqpZhcgqRT';
const css =`._title_6jm2u_1 {
    font-size: 24px;
}
`;
const styles = {
    ['title']: '_title_6jm2u_1',
    inject(shadowRoot) { injectStyles(key, css, undefined, shadowRoot); }
};
export { styles, css, key };
export default styles;

As you see the generated inject function accept optional shadowRoot parameter, by that within your WebComponent you will be able to inject styles into shadowRoot.

Instant

This mode is calls the styles.inject() method internally.

import { injectStyles } from 'css-es-modules';
const key = '63Jw35UDb1fWpxJiCNGuB9';
const css =`._title_6jm2u_1 {
    font-size: 24px;
}
`;
const styles = {
    ['title']: '_title_6jm2u_1',
    inject() { injectStyles(key, css); }
};
styles.inject();
export { styles, css, key };
export default styles;

Embedding or ejecting the injector code

In some development workflows you can decide that you do not want to import the injector code from the dependencies library, in such case you have 2 options:

Embedding

By setting embed value of the inject.script option you will force transformer to embed the injector code within each transformed file in place. So the generated file will be much bigger but will not contain any import statements:

// File generated by the postcss-es-modules plugin. Please do not modify it !!!
/* eslint-disable */
...
function injectStyles(stylesheetKey, stylesheetBody, options) {
    ....
}

const key = '7Ut3ZUGvF7pyP5RnMM8pzt';
const css =`._title_6jm2u_1 {
    font-size: 24px;
}
`;
const styles = {
    get ['title']() { injectStyles(key, css);  return '_title_6jm2u_1'; },
    inject() { injectStyles(key, css); }
};
export { styles, css, key };
export default styles;

This option can be very useful on the development process.

Ejecting

By setting eject value of the inject.script option you will force transformer to eject the injector code into provided inject.scriptEjectPath.

Config:

const { postcssEsModules }  = require('postcss-es-modules');
module.exports = (ctx) => ({
    plugins: [
        postcssEsModules({
            inject: {
                script: "eject",
                scriptEjectPath: __dirname + "/src/styles-inject"
            }
        }),
    ]
})

The inject.scriptEjectPath have to be an absolute path.

Generated code:

import { injectStyles } from './styles-inject/inject-styles';
const key = 'hT8K48DCnQc2Z9FkPUDzb7';
const css =`._title_6jm2u_1 {
    font-size: 24px;
}
`;
const styles = {
    get ['title']() { injectStyles(key, css);  return '_title_6jm2u_1'; },
    inject() { injectStyles(key, css); }
};
export { styles, css, key };
export default styles;

Within the .src/styles-inject/inject-styles you will find ejected code of injector.

The ejection is not overwriting the existing files, you can eject code once, and modify it. If you will get clean ejected code, please just delete old files.

This option can be very useful on the development process.

Next steps

For more information please go to the api reference documentation or to the examples section.

Need a help ?

If you have any problems, issues, ect. please use github discussions.