Security News
Fluent Assertions Faces Backlash After Abandoning Open Source Licensing
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
postcss-es-modules
Advanced tools
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>
)
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.
npm i postcss postcss-es-modules --save-dev
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.
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.
// 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
There is nothing unique to use of this plugin with bundlers.
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.
You can find more examples here.
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 |
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.
There are few modes how the styles injection can work.
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;
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;
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.
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;
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:
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.
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.
For more information please go to the api reference documentation or to the examples section.
If you have any problems, issues, ect. please use github discussions.
FAQs
Universal styles injection to work with postcss-es-modules
The npm package postcss-es-modules receives a total of 6,059 weekly downloads. As such, postcss-es-modules popularity was classified as popular.
We found that postcss-es-modules demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
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.
Security News
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
Research
Security News
Socket researchers uncover the risks of a malicious Python package targeting Discord developers.
Security News
The UK is proposing a bold ban on ransomware payments by public entities to disrupt cybercrime, protect critical services, and lead global cybersecurity efforts.