Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/majo44/postcss-es-modules
Postcss plugin which transform css files in to the .js/.ts css modules.
https://github.com/majo44/postcss-es-modules
Last synced: 2 months ago
JSON representation
Postcss plugin which transform css files in to the .js/.ts css modules.
- Host: GitHub
- URL: https://github.com/majo44/postcss-es-modules
- Owner: majo44
- Created: 2021-01-30T01:16:59.000Z (almost 4 years ago)
- Default Branch: master
- Last Pushed: 2024-06-04T12:26:28.000Z (8 months ago)
- Last Synced: 2024-11-03T17:43:38.027Z (3 months ago)
- Language: TypeScript
- Homepage: https://majo44.github.io/postcss-es-modules/
- Size: 1.09 MB
- Stars: 18
- Watchers: 4
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# postcss-es-modules
[Postcss](https://postcss.org/) plugin which transform css files in to the .js/.ts css modules.What is this? For example, you have the following CSS:
```css
/* ./component.module.css */
.article {
font-size: 16px;
}
.title {
font-size: 24px;
}
.title:hover {
color: red;
}
```
After the transformation it will become like this:
```javascript
/* ./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.
```javascript
/* ./component.jsx */
import styles from './component.module.css.js';
export const Component = () => (
Title
)
```## 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
```bash
npm i postcss postcss-es-modules --save-dev
```## Usage
Configure the postcss to use the plugin:
```javascript
// 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
```bash
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
```javascript
// postcss.config.js
const { postcssEsModules } = require('postcss-es-modules');
module.exports = (ctx) => ({
plugins: [
postcssEsModules({ inject: { scriptType: 'ts' } }),
]
})
```
```bash
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:```typescript
/* ./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](https://majo44.github.io/postcss-es-modules/#/examples/).## Options
Here is the list of all available options, for more details please go to [Recipes](#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 \ 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:<br/> - `esm`: ecmascript 6 modules<br/> - `cjs`: commonjs |
| `inject.injectMode` | `'lazy'` / `'ondemand'` / `'instant'` / `'none'` | `'lazy'` | The mode of the styles injection. Options:<br/> - `lazy` - the stylesheet will be injected on the first use of the style class name within the code<br/>- `ondemand` - the stylesheet will be injected only when the `styles.inject()` method will be called<br/>- `instant` - the stylesheet will be injected on the module load<br/>- `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:</br>- `embed`: embedding styles injector script in to the target source (so each generated file will contains the loader inside)<br/>- `eject`: the styles injector script will be ejected to the provided `inject.scriptEjectPath`<br/>- `import`: the styles injector script will be referred by the import statement |
| `inject.scriptType` | `'ts'` / `'js'` | `'js'` | The generated script type. Options:</br>- `ts`: typescript<br/>- `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:<br/>- `css` - the raw css string code<br/>- `key` - unique key of the stylesheet<br/>Eg: `"injectMyStyles(css)"` |
| `modules` | `object`| - | The CSS Modules options. It is inherits all options from the [postcss-modules](https://github.com/css-modules/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:
```css
/* ./index.css */
.app {
background-color: red;
}
``````typescript jsx
// ./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](https://majo44.github.io/postcss-es-modules/#/examples/react-ssr-webpack-typescript/).### 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.```javascript
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`.```javascript
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.
```javascript
import { injectStyles } from 'css-es-modules';
const key = 'e1ph4XxYADCPaqpZhcgqRT';
const css =`._title_6jm2u_1 {
font-size: 24px;
}
`;
const styles = {
['title']: '_title_6jm2u_1',
inject() { injectStyles(key, css); }
};
export { styles, css, key };
export default styles;
```
#### Instant
This mode is calls the styles.inject() method internally.
```javascript
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:
```javascript
// 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:
```javascript
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:
```javascript
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](https://majo44.github.io/postcss-es-modules/#/api/) documentation
or to the [examples](https://majo44.github.io/postcss-es-modules/#/examples/) section.## Need a help ?
If you have any problems, issues, ect. please use [github discussions](https://github.com/majo44/postcss-es-modules/discussions).