├── .README ├── babel-plugin-react-css-modules.png └── babel-plugin-react-css-modules.sketch ├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .flowconfig ├── .github └── FUNDING.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── benchmark ├── package.json └── test.js ├── demo ├── .babelrc ├── README.md ├── index.html ├── package.json ├── src │ ├── components │ │ ├── AnonymousStyleResolution.js │ │ ├── NamedStyleResolution.js │ │ ├── RuntimeStyleResolution.js │ │ └── table.css │ └── index.js └── webpack.config.js ├── package.json ├── src ├── attributeNameExists.js ├── conditionalClassMerge.js ├── createObjectExpression.js ├── createSpreadMapper.js ├── findMatchedFiletype.js ├── getClassName.js ├── handleSpreadClassName.js ├── index.js ├── replaceJsxExpressionContainer.js ├── requireCssModule.js ├── resolveStringLiteral.js ├── schemas │ ├── optionsDefaults.js │ └── optionsSchema.json └── types.js └── test ├── fixtures └── react-css-modules │ ├── adds module hot accept for CSS imports when file only contains import statements │ ├── bar.css │ ├── input.js │ ├── options.json │ └── output.js │ ├── adds module hot accept for CSS imports │ ├── bar.css │ ├── input.js │ ├── options.json │ └── output.js │ ├── applies extra plugins │ ├── bar.scss │ ├── input.js │ ├── options.json │ └── output.js │ ├── custom attribute mapping │ ├── foo.css │ ├── input.js │ ├── options.json │ └── output.js │ ├── disable default styleName transform │ ├── input.js │ ├── options.json │ └── output.js │ ├── does not apply plugin if no styleName │ ├── bar.css │ ├── input.js │ ├── options.json │ └── output.js │ ├── does not throw error for stylename not found when handleMissingStyleName is warn │ ├── foo.css │ ├── input.js │ ├── options.json │ └── output.js │ ├── does not throw error if attribute has no name property │ ├── bar.css │ ├── input.js │ ├── options.json │ └── output.js │ ├── does not throw error on excluded JSXElement │ ├── input.js │ ├── options.json │ └── output.js │ ├── exclude styles from react-css-modules │ ├── bar.css │ ├── input.js │ ├── not_me.css │ ├── options.json │ └── output.js │ ├── handle spread attributes │ ├── foo.css │ ├── input.js │ ├── options.json │ └── output.js │ ├── merges the resolved styleName with the existing className values │ ├── bar.css │ ├── input.js │ └── output.js │ ├── options.js │ ├── provides handleMissingStyleName to getClassName at runtime │ ├── foo.css │ ├── input.js │ ├── options.json │ └── output.js │ ├── resolves absolute path stylesheets │ ├── bar.css │ ├── input.js │ ├── options.json │ └── output.js │ ├── resolves less stylesheets matching RegExp │ ├── bar.less │ ├── bar.md.less │ ├── input.js │ ├── options.json │ └── output.js │ ├── resolves less stylesheets │ ├── bar.less │ ├── input.js │ ├── options.json │ └── output.js │ ├── resolves namespaced styleName matching RegExp │ ├── bar.md.css │ ├── foo.css │ ├── input.js │ ├── options.json │ ├── output.js │ └── styles │ │ └── base.css │ ├── resolves namespaced styleName │ ├── bar.css │ ├── input.js │ └── output.js │ ├── resolves non-namespaced styleName (anonymous import) │ ├── bar.css │ ├── input.js │ └── output.js │ ├── resolves non-namespaced styleName (named import) │ ├── bar.css │ ├── input.js │ └── output.js │ ├── throws if autoResolveMultipleImport with duplicates │ ├── bar.css │ ├── foo.css │ ├── input.js │ └── options.json │ ├── throws if autoResolveMultipleImport with no match │ ├── bar.css │ ├── foo.css │ ├── input.js │ └── options.json │ ├── throws if styleName is used without import │ ├── input.js │ └── options.json │ ├── throws if unset autoResolveMultipleImport with multiple anonymous imports │ ├── bar.css │ ├── foo.css │ ├── input.js │ └── options.json │ ├── uses autoResolveMultipleImport no duplicates │ ├── bar.css │ ├── foo.css │ ├── input.js │ ├── options.json │ └── output.js │ ├── uses autoResolveMultipleImport on runtime │ ├── bar.css │ ├── foo.css │ ├── input.js │ ├── options.json │ └── output.js │ ├── uses getClassName to resolve non-literal styleName (with already existing className) │ ├── bar.css │ ├── foo.css │ ├── input.js │ └── output.js │ └── uses getClassName to resolve non-literal styleName │ ├── bar.css │ ├── foo.css │ ├── input.js │ └── output.js └── index.js /.README/babel-plugin-react-css-modules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gajus/babel-plugin-react-css-modules/eb008aaa7c8efc61c266f15b03c101cbb111a53e/.README/babel-plugin-react-css-modules.png -------------------------------------------------------------------------------- /.README/babel-plugin-react-css-modules.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gajus/babel-plugin-react-css-modules/eb008aaa7c8efc61c266f15b03c101cbb111a53e/.README/babel-plugin-react-css-modules.sketch -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-transform-flow-strip-types" 4 | ], 5 | "presets": [ 6 | [ 7 | "@babel/env", 8 | { 9 | "targets": { 10 | "node": "8.0" 11 | } 12 | } 13 | ] 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /test/fixtures 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "canonical", 4 | "canonical/flowtype" 5 | ], 6 | "root": true 7 | } 8 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | /node_modules/config-chain/test/broken.json 3 | /node_modules/conventional-changelog-core/test/fixtures/_malformation.json 4 | /node_modules/npmconf/test/fixtures/package.json 5 | /node_modules/npm/node_modules/config-chain/test/broken.json 6 | 7 | [options] 8 | module.ignore_non_literal_requires=true 9 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: gajus 2 | patreon: gajus 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | dist 3 | node_modules 4 | *.log 5 | .* 6 | !.babelrc 7 | !.editorconfig 8 | !.eslintrc 9 | !.eslintignore 10 | !.flowconfig 11 | !.gitignore 12 | !.npmignore 13 | !.README 14 | !.travis.yml 15 | /package-lock.json 16 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | test 3 | coverage 4 | .* 5 | *.log 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | - 8 5 | script: 6 | - npm run lint 7 | - npm run build 8 | - npm run test 9 | after_success: 10 | - semantic-release 11 | notifications: 12 | email: false 13 | sudo: false 14 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Gajus Kuizinas (http://gajus.com/) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the Gajus Kuizinas (http://gajus.com/) nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL ANUARY BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # babel-plugin-react-css-modules 2 | 3 | [![Travis build status](http://img.shields.io/travis/gajus/babel-plugin-react-css-modules/master.svg?style=flat-square)](https://travis-ci.org/gajus/babel-plugin-react-css-modules) 4 | [![NPM version](http://img.shields.io/npm/v/babel-plugin-react-css-modules.svg?style=flat-square)](https://www.npmjs.org/package/babel-plugin-react-css-modules) 5 | [![Canonical Code Style](https://img.shields.io/badge/code%20style-canonical-blue.svg?style=flat-square)](https://github.com/gajus/canonical) 6 | [![Gitter](https://img.shields.io/gitter/room/babel-plugin-react-css-modules/Lobby.svg?style=flat-square)](https://gitter.im/babel-plugin-react-css-modules/Lobby) 7 | [![Twitter Follow](https://img.shields.io/twitter/follow/kuizinas.svg?style=social&label=Follow)](https://twitter.com/kuizinas) 8 | 9 | > # Looking for maintainers 10 | > 11 | > This project is not actively maintained by the original author. However, I am happy to nominate new maintainers. 12 | > If you wish to contribute to `babel-plugin-react-css-modules`, please begin by raising PRs that fix existing issues. 13 | > PRs must pass CI/CD tests, include tests (if they change behavior or fix a bug), and include documentation. 14 | 15 | 16 | 17 | Transforms `styleName` to `className` using compile time [CSS module](#css-modules) resolution. 18 | 19 | In contrast to [`react-css-modules`](https://github.com/gajus/react-css-modules), `babel-plugin-react-css-modules` has a lot smaller performance overhead (0-10% vs +50%; see [Performance](#performance)) and a lot smaller size footprint (less than 2kb vs 17kb react-css-modules + lodash dependency). 20 | 21 | * [CSS Modules](#css-modules) 22 | * [Difference from `react-css-modules`](#difference-from-react-css-modules) 23 | * [Performance](#performance) 24 | * [How does it work?](#how-does-it-work) 25 | * [Conventions](#conventions) 26 | * [Anonymous reference](#anonymous-reference) 27 | * [Named reference](#named-reference) 28 | * [Configuration](#configuration) 29 | * [Configurate syntax loaders](#configurate-syntax-loaders) 30 | * [Custom Attribute Mapping](#custom-attribute-mapping) 31 | * [Installation](#installation) 32 | * [React Native](#react-native) 33 | * [Demo](#demo) 34 | * [Example transpilations](#example-transpilations) 35 | * [Anonymous `styleName` resolution](#anonymous-stylename-resolution) 36 | * [Named `styleName` resolution](#named-stylename-resolution) 37 | * [Runtime `styleName` resolution](#runtime-stylename-resolution) 38 | * [Limitations](#limitations) 39 | * [Have a question or want to suggest an improvement?](#have-a-question-or-want-to-suggest-an-improvement) 40 | * [FAQ](#faq) 41 | * [How to migrate from react-css-modules to babel-plugin-react-css-modules?](#how-to-migrate-from-react-css-modules-to-babel-plugin-react-css-modules) 42 | * [How to reference multiple CSS modules?](#how-to-reference-multiple-css-modules) 43 | * [How to live reload the CSS?](#hot-to-live-reload-the-css) 44 | 45 | ## CSS Modules 46 | 47 | [CSS Modules](https://github.com/css-modules/css-modules) are awesome! If you are not familiar with CSS Modules, it is a concept of using a module bundler such as [webpack](http://webpack.github.io/docs/) to load CSS scoped to a particular document. CSS module loader will generate a unique name for each CSS class at the time of loading the CSS document ([Interoperable CSS](https://github.com/css-modules/icss) to be precise). To see CSS Modules in practice, [webpack-demo](https://css-modules.github.io/webpack-demo/). 48 | 49 | In the context of React, CSS Modules look like this: 50 | 51 | ```js 52 | import React from 'react'; 53 | import styles from './table.css'; 54 | 55 | export default class Table extends React.Component { 56 | render () { 57 | return
58 |
59 |
A0
60 |
B0
61 |
62 |
; 63 | } 64 | } 65 | 66 | ``` 67 | 68 | Rendering the component will produce a markup similar to: 69 | 70 | ```js 71 |
72 |
73 |
A0
74 |
B0
75 |
76 |
77 | 78 | ``` 79 | 80 | and a corresponding CSS file that matches those CSS classes. 81 | 82 | Awesome! 83 | 84 | However, there are several disadvantages of using CSS modules this way: 85 | 86 | * You have to use `camelCase` CSS class names. 87 | * You have to use `styles` object whenever constructing a `className`. 88 | * Mixing CSS Modules and global CSS classes is cumbersome. 89 | * Reference to an undefined CSS Module resolves to `undefined` without a warning. 90 | 91 | `babel-plugin-react-css-modules` automates loading of CSS Modules using `styleName` property, e.g. 92 | 93 | ```js 94 | import React from 'react'; 95 | import './table.css'; 96 | 97 | export default class Table extends React.Component { 98 | render () { 99 | return
100 |
101 |
A0
102 |
B0
103 |
104 |
; 105 | } 106 | } 107 | 108 | ``` 109 | 110 | Using `babel-plugin-react-css-modules`: 111 | 112 | * You are not forced to use the `camelCase` naming convention. 113 | * You do not need to refer to the `styles` object every time you use a CSS Module. 114 | * There is clear distinction between global CSS and CSS modules, e.g. 115 | 116 | ```js 117 |
118 | ``` 119 | 120 | 124 | 125 | ## Difference from `react-css-modules` 126 | 127 | [`react-css-modules`](https://github.com/gajus/react-css-modules) introduced a convention of using `styleName` attribute to reference [CSS module](https://github.com/css-modules/css-modules). `react-css-modules` is a higher-order [React](https://facebook.github.io/react/) component. It is using the `styleName` value to construct the `className` value at the run-time. This abstraction frees a developer from needing to reference the imported styles object when using CSS modules ([What's the problem?](https://github.com/gajus/react-css-modules#whats-the-problem)). However, this approach has a measurable performance penalty (see [Performance](#performance)). 128 | 129 | `babel-plugin-react-css-modules` solves the developer experience problem without impacting the performance. 130 | 131 | ## Performance 132 | 133 | The important metric here is the "Difference from the base benchmark". "base" is defined as using React with hardcoded `className` values. The lesser the difference, the bigger the performance impact. 134 | 135 | > Note: 136 | > This benchmark suite does not include a scenario when `babel-plugin-react-css-modules` can statically construct a literal value at the build time. 137 | > If a literal value of the `className` is constructed at the compile time, the performance is equal to the base benchmark. 138 | 139 | |Name|Operations per second (relative margin of error)|Sample size|Difference from the base benchmark| 140 | |---|---|---|---| 141 | |Using `className` (base)|9551 (±1.47%)|587|-0%| 142 | |`react-css-modules`|5914 (±2.01%)|363|-61%| 143 | |`babel-plugin-react-css-modules` (runtime, anonymous)|9145 (±1.94%)|540|-4%| 144 | |`babel-plugin-react-css-modules` (runtime, named)|8786 (±1.59%)|527|-8%| 145 | 146 | > Platform info: 147 | > 148 | > * Darwin 16.1.0 x64 149 | > * Node.JS 7.1.0 150 | > * V8 5.4.500.36 151 | > * NODE_ENV=production 152 | > * Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz × 8 153 | 154 | View the [./benchmark](./benchmark). 155 | 156 | Run the benchmark: 157 | 158 | ```bash 159 | git clone git@github.com:gajus/babel-plugin-react-css-modules.git 160 | cd ./babel-plugin-react-css-modules 161 | npm install 162 | npm run build 163 | cd ./benchmark 164 | npm install 165 | NODE_ENV=production ./test 166 | ``` 167 | 168 | ## How does it work? 169 | 170 | 1. Builds index of all stylesheet imports per file (imports of files with `.css` or `.scss` extension). 171 | 1. Uses [postcss](https://github.com/postcss/postcss) to parse the matching CSS files into a lookup of CSS module references. 172 | 1. Iterates through all [JSX](https://facebook.github.io/react/docs/jsx-in-depth.html) element declarations. 173 | 1. Parses the `styleName` attribute value into anonymous and named CSS module references. 174 | 1. Finds the CSS class name matching the CSS module reference: 175 | * If `styleName` value is a string literal, generates a string literal value. 176 | * If `styleName` value is a [`jSXExpressionContainer`](https://babeljs.io/docs/en/next/babel-types.html#jsxexpressioncontainer), uses a helper function ([`getClassName`](./src/getClassName.js)) to construct the `className` value at the runtime. 177 | 1. Removes the `styleName` attribute from the element. 178 | 1. Appends the resulting `className` to the existing `className` value (creates `className` attribute if one does not exist). 179 | 180 | ## Configuration 181 | 182 | Configure the options for the plugin within your `.babelrc` as follows: 183 | 184 | ```json 185 | { 186 | "plugins": [ 187 | ["react-css-modules", { 188 | "option": "value" 189 | }] 190 | ] 191 | } 192 | 193 | ``` 194 | 195 | ### Options 196 | 197 | |Name|Type|Description|Default| 198 | |---|---|---|---| 199 | |`context`|`string`|Must match webpack [`context`](https://webpack.js.org/configuration/entry-context/#context) configuration. [`css-loader`](https://github.com/webpack/css-loader) inherits `context` values from webpack. Other CSS module implementations might use different context resolution logic.|`process.cwd()`| 200 | |`exclude`|`string`|A RegExp that will exclude otherwise included files e.g., to exclude all styles from node_modules `exclude: 'node_modules'`| 201 | |`filetypes`|`?FiletypesConfigurationType`|Configure [postcss syntax loaders](https://github.com/postcss/postcss#syntaxes) like sugarss, LESS and SCSS and extra plugins for them. || 202 | |`generateScopedName`|`?GenerateScopedNameConfigurationType`|Refer to [Generating scoped names](https://github.com/css-modules/postcss-modules#generating-scoped-names). If you use this option, make sure it matches the value of `localIdentName` [in webpack config](https://webpack.js.org/loaders/css-loader/#localidentname). See this [issue](https://github.com/gajus/babel-plugin-react-css-modules/issues/108#issuecomment-334351241) |`[path]___[name]__[local]___[hash:base64:5]`| 203 | |`removeImport`|`boolean`|Remove the matching style import. This option is used to enable server-side rendering.|`false`| 204 | |`webpackHotModuleReloading`|`boolean`|Enables hot reloading of CSS in webpack|`false`| 205 | |`handleMissingStyleName`|`"throw"`, `"warn"`, `"ignore"`|Determines what should be done for undefined CSS modules (using a `styleName` for which there is no CSS module defined). Setting this option to `"ignore"` is equivalent to setting `errorWhenNotFound: false` in [react-css-modules](https://github.com/gajus/react-css-modules#errorwhennotfound). |`"throw"`| 206 | |`attributeNames`|`?AttributeNameMapType`|Refer to [Custom Attribute Mapping](#custom-attribute-mapping)|`{"styleName": "className"}`| 207 | |`skip`|`boolean`|Whether to apply plugin if no matching `attributeNames` found in the file|`false`| 208 | |`autoResolveMultipleImports`|`boolean`|Allow multiple anonymous imports if `styleName` is only in one of them.|`false`| 209 | 210 | Missing a configuration? [Raise an issue](https://github.com/gajus/babel-plugin-react-css-modules/issues/new?title=New%20configuration:). 211 | 212 | > Note: 213 | > The default configuration should work out of the box with the [css-loader](https://github.com/webpack/css-loader). 214 | 215 | #### Option types (flow) 216 | 217 | ```js 218 | type FiletypeOptionsType = {| 219 | +syntax: string, 220 | +plugins?: $ReadOnlyArray> 221 | |}; 222 | 223 | type FiletypesConfigurationType = { 224 | [key: string]: FiletypeOptionsType 225 | }; 226 | 227 | type GenerateScopedNameType = (localName: string, resourcePath: string) => string; 228 | 229 | type GenerateScopedNameConfigurationType = GenerateScopedNameType | string; 230 | 231 | type AttributeNameMapType = { 232 | [key: string]: string 233 | }; 234 | 235 | ``` 236 | 237 | ### Configurate syntax loaders 238 | 239 | To add support for different CSS syntaxes (e.g. SCSS), perform the following two steps: 240 | 241 | 1. Add the [postcss syntax loader](https://github.com/postcss/postcss#syntaxes) as a development dependency: 242 | 243 | ```bash 244 | npm install postcss-scss --save-dev 245 | ``` 246 | 247 | 2. Add a `filetypes` syntax mapping to the Babel plugin configuration. For example for SCSS: 248 | 249 | ```json 250 | "filetypes": { 251 | ".scss": { 252 | "syntax": "postcss-scss" 253 | } 254 | } 255 | ``` 256 | 257 | And optionally specify extra plugins: 258 | 259 | ```json 260 | "filetypes": { 261 | ".scss": { 262 | "syntax": "postcss-scss", 263 | "plugins": [ 264 | "postcss-nested" 265 | ] 266 | } 267 | } 268 | ``` 269 | 270 | > NOTE: [`postcss-nested`](https://github.com/postcss/postcss-nested) is added as an extra plugin for demonstration purposes only. It's not needed with [`postcss-scss`](https://github.com/postcss/postcss-scss) because SCSS already supports nesting. 271 | 272 | Postcss plugins can have options specified by wrapping the name and an options object in an array inside your config: 273 | 274 | ```json 275 | "plugins": [ 276 | ["postcss-import-sync2", { 277 | "path": ["src/styles", "shared/styles"] 278 | }], 279 | "postcss-nested" 280 | ] 281 | ``` 282 | 283 | 284 | ### Custom Attribute Mapping 285 | 286 | You can set your own attribute mapping rules using the `attributeNames` option. 287 | 288 | It's an object, where keys are source attribute names and values are destination attribute names. 289 | 290 | For example, the [<NavLink>](https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/api/NavLink.md) component from [React Router](https://github.com/ReactTraining/react-router) has a `activeClassName` attribute to accept an additional class name. You can set `"attributeNames": { "activeStyleName": "activeClassName" }` to transform it. 291 | 292 | The default `styleName` -> `className` transformation **will not** be affected by an `attributeNames` value without a `styleName` key. Of course you can use `{ "styleName": "somethingOther" }` to change it, or use `{ "styleName": null }` to disable it. 293 | 294 | ## Installation 295 | 296 | When `babel-plugin-react-css-modules` cannot resolve CSS module at a compile time, it imports a helper function (read [Runtime `styleName` resolution](#runtime-stylename-resolution)). Therefore, you must install `babel-plugin-react-css-modules` as a direct dependency of the project. 297 | 298 | ```bash 299 | npm install babel-plugin-react-css-modules --save 300 | ``` 301 | 302 | 303 | ### React Native 304 | 305 | If you'd like to get this working in React Native, you're going to have to allow custom import extensions, via a `rn-cli.config.js` file: 306 | 307 | ```js 308 | module.exports = { 309 | getAssetExts() { 310 | return ["scss"]; 311 | } 312 | } 313 | ``` 314 | 315 | Remember, also, that the bundler caches things like plugins and presets. If you want to change your `.babelrc` (to add this plugin) then you'll want to add the `--reset-cache` flag to the end of the package command. 316 | 317 | ### Demo 318 | 319 | ```bash 320 | git clone git@github.com:gajus/babel-plugin-react-css-modules.git 321 | cd ./babel-plugin-react-css-modules/demo 322 | npm install 323 | npm start 324 | ``` 325 | 326 | ```bash 327 | open http://localhost:8080/ 328 | ``` 329 | 330 | ## Conventions 331 | 332 | ### Anonymous reference 333 | 334 | Anonymous reference can be used when there is only one stylesheet import. 335 | 336 | Format: `CSS module name`. 337 | 338 | Example: 339 | 340 | ```js 341 | import './foo1.css'; 342 | 343 | // Imports "a" CSS module from ./foo1.css. 344 |
; 345 | ``` 346 | 347 | ### Named reference 348 | 349 | Named reference is used to refer to a specific stylesheet import. 350 | 351 | Format: `[name of the import].[CSS module name]`. 352 | 353 | Example: 354 | 355 | ```js 356 | import foo from './foo1.css'; 357 | import bar from './bar1.css'; 358 | 359 | // Imports "a" CSS module from ./foo1.css. 360 |
; 361 | 362 | // Imports "a" CSS module from ./bar1.css. 363 |
; 364 | ``` 365 | 366 | ## Example transpilations 367 | 368 | ### Anonymous `styleName` resolution 369 | 370 | When `styleName` is a literal string value, `babel-plugin-react-css-modules` resolves the value of `className` at the compile time. 371 | 372 | Input: 373 | 374 | ```js 375 | import './bar.css'; 376 | 377 |
; 378 | 379 | ``` 380 | 381 | Output: 382 | 383 | ```js 384 | import './bar.css'; 385 | 386 |
; 387 | 388 | ``` 389 | 390 | ### Named `styleName` resolution 391 | 392 | When a file imports multiple stylesheets, you must use a [named reference](#named-reference). 393 | 394 | > Have suggestions for an alternative behaviour? 395 | > [Raise an issue](https://github.com/gajus/babel-plugin-react-css-modules/issues/new?title=Suggestion%20for%20alternative%20handling%20of%20multiple%20stylesheet%20imports) with your suggestion. 396 | 397 | Input: 398 | 399 | ```js 400 | import foo from './foo1.css'; 401 | import bar from './bar1.css'; 402 | 403 |
; 404 |
; 405 | ``` 406 | 407 | Output: 408 | 409 | ```js 410 | import foo from './foo1.css'; 411 | import bar from './bar1.css'; 412 | 413 |
; 414 |
; 415 | 416 | ``` 417 | 418 | ### Runtime `styleName` resolution 419 | 420 | When the value of `styleName` cannot be determined at the compile time, `babel-plugin-react-css-modules` inlines all possible styles into the file. It then uses [`getClassName`](https://github.com/gajus/babel-plugin-react-css-modules/blob/master/src/getClassName.js) helper function to resolve the `styleName` value at the runtime. 421 | 422 | Input: 423 | 424 | ```js 425 | import './bar.css'; 426 | 427 |
.5 ? 'a' : 'b'}>
; 428 | 429 | ``` 430 | 431 | Output: 432 | 433 | ```js 434 | import _getClassName from 'babel-plugin-react-css-modules/dist/browser/getClassName'; 435 | import foo from './bar.css'; 436 | 437 | const _styleModuleImportMap = { 438 | foo: { 439 | a: 'bar__a', 440 | b: 'bar__b' 441 | } 442 | }; 443 | 444 |
.5 ? 'a' : 'b', _styleModuleImportMap)}>
; 445 | 446 | ``` 447 | 448 | ## Limitations 449 | 450 | * [Establish a convention for extending the styles object at the runtime](https://github.com/gajus/babel-plugin-react-css-modules/issues/1) 451 | 452 | ## Have a question or want to suggest an improvement? 453 | 454 | * Have a technical questions? [Ask on Stack Overflow.](http://stackoverflow.com/questions/ask?tags=babel-plugin-react-css-modules) 455 | * Have a feature suggestion or want to report an issue? [Raise an issues.](https://github.com/gajus/babel-plugin-react-css-modules/issues) 456 | * Want to say hello to other `babel-plugin-react-css-modules` users? [Chat on Gitter.](https://gitter.im/babel-plugin-react-css-modules) 457 | 458 | ## FAQ 459 | 460 | ### How to migrate from react-css-modules to babel-plugin-react-css-modules? 461 | 462 | Follow the following steps: 463 | 464 | * Remove `react-css-modules`. 465 | * Add `babel-plugin-react-css-modules`. 466 | * Configure `.babelrc` (see [Configuration](#configuration)). 467 | * Remove all uses of the `cssModules` decorator and/or HoC. 468 | 469 | If you are still having problems, refer to one of the user submitted guides: 470 | 471 | * [Porting from react-css-modules to babel-plugin-react-css-modules (with Less)](http://www.jjinux.com/2018/04/javascript-porting-from-react-css.html) 472 | 473 | ### How to reference multiple CSS modules? 474 | 475 | `react-css-modules` had an option [`allowMultiple`](https://github.com/gajus/react-css-modules#allowmultiple). `allowMultiple` allows multiple CSS module names in a `styleName` declaration, e.g. 476 | 477 | ```js 478 |
479 | ``` 480 | 481 | This behaviour is enabled by default in `babel-plugin-react-css-modules`. 482 | 483 | ### How to live reload the CSS? 484 | 485 | `babel-plugin-react-css-modules` utilises webpack [Hot Module Replacement](https://webpack.js.org/concepts/hot-module-replacement/#root) (HMR) to live reload the CSS. 486 | 487 | To enable live reloading of the CSS: 488 | 489 | * Enable [`webpackHotModuleReloading`](#configuration) `babel-plugin-react-css-modules` configuration. 490 | * Configure `webpack` to use HMR. Use [`--hot`](https://webpack.js.org/configuration/dev-server/#root) option if you are using `webpack-dev-server`. 491 | * Use [`style-loader`](https://github.com/webpack/style-loader) to load the style sheets. 492 | 493 | > Note: 494 | > 495 | > This enables live reloading of the CSS. To enable HMR of the React components, refer to the [Hot Module Replacement - React](https://webpack.js.org/guides/hot-module-replacement/#other-code-and-frameworks) guide. 496 | 497 | > Note: 498 | > 499 | > This is a [webpack](https://webpack.github.io/) specific option. If you are using `babel-plugin-react-css-modules` in a different setup and require CSS live reloading, raise an issue describing your setup. 500 | 501 | ### I get a "Cannot use styleName attribute for style name '`[X]`' without importing at least one stylesheet." error 502 | 503 | First, ensure that you are correctly importing the CSS file following the [conventions](#conventions). 504 | 505 | If you are correctly importing but using different CSS (such as SCSS), this is likely happening because your CSS file wasn't able to be successfully parsed. You need to [configure a syntax loader](#configurate-syntax-loaders). 506 | 507 | ### I get a "Could not resolve the styleName '`[X]`' error but the class exists in the CSS included in the browser. 508 | 509 | First, verify that the CSS is being included in the browser. Remove from `styleName` the reference to the CSS class that's failing and view the page. Search through the `