Socket
Socket
Sign inDemoInstall

postcss-es-modules

Package Overview
Dependencies
25
Maintainers
2
Versions
16
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

    postcss-es-modules

Universal styles injection to work with postcss-es-modules


Version published
Weekly downloads
2.1K
decreased by-43.26%
Maintainers
2
Install size
653 kB
Created
Weekly downloads
 

Readme

Source

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.

OptionTypeDefaultDescription
injectobject-The configuration fo the styles injection
inject.useNouncestring-The style nounce key
inject.useConstructableStylesheetbooleantrueUse Constructable Stylesheet for styles injection to DOM if the browser supports Constructable Stylesheet
inject.useStyleTagbooleantrueUse <style> tag for styles injection to DOM
inject.useNodeGlobalbooleantrueEnable 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.scriptEjectPathstring-The path where the styles injector script code will be ejected. This option is required if inject.script is eject
inject.customobject-The custom style injector configuration
inject.custom.importStatementstring-The custom style injector statement for import required dependencies. Eg: "import { injectMyStyles } from 'somelib'";
inject.custom.injectStatementstring-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 stylesheet
Eg: "injectMyStyles(css)"
modulesobject-The CSS Modules options. It is inherits all options from the postcss-modules expect getJSON
modules.attachOriginalClassNamebooleanfalseThe 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.

Keywords

FAQs

Last updated on 02 Apr 2024

Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc