Skip to content

Commit ae2cf07

Browse files
ro-savageTimer
authored andcommitted
Update CSS Modules localIndetName (#4192)
* Update CSS Modules localIndetName * Add missing file to package * Correct regex * plz plz plz
1 parent 1d4fdc2 commit ae2cf07

File tree

10 files changed

+109
-16
lines changed

10 files changed

+109
-16
lines changed

packages/react-dev-utils/README.md

+37
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,40 @@ module.exports = {
326326
// ...
327327
}
328328
```
329+
330+
#### `getCSSModuleLocalIdent(context: Object, localIdentName: String, localName: String, options: Object): string`
331+
332+
Creates a class name for CSS Modules that uses either the filename or folder name if named `index.module.css`.
333+
334+
For `MyFolder/MyComponent.module.css` and class `MyClass` the output will be `MyComponent.module_MyClass__[hash]`
335+
For `MyFolder/index.module.css` and class `MyClass` the output will be `MyFolder_MyClass__[hash]`
336+
337+
```js
338+
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
339+
340+
// In your webpack config:
341+
// ...
342+
module: {
343+
rules: [
344+
{
345+
test: /\.module\.css$/,
346+
use: [
347+
require.resolve('style-loader'),
348+
{
349+
loader: require.resolve('css-loader'),
350+
options: {
351+
importLoaders: 1,
352+
modules: true,
353+
getLocalIdent: getCSSModuleLocalIdent,
354+
},
355+
},
356+
{
357+
loader: require.resolve('postcss-loader'),
358+
options: postCSSLoaderOptions,
359+
},
360+
],
361+
}
362+
]
363+
}
364+
```
365+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
'use strict';
9+
10+
const loaderUtils = require('loader-utils');
11+
12+
module.exports = function getLocalIdent(
13+
context,
14+
localIdentName,
15+
localName,
16+
options
17+
) {
18+
// Use the filename or folder name, based on some uses the index.js / index.module.css project style
19+
const fileNameOrFolder = context.resourcePath.endsWith('index.module.css')
20+
? '[folder]'
21+
: '[name]';
22+
// Create a hash based on a the file location and class name. Will be unique across a project, and close to globally unique.
23+
const hash = loaderUtils.getHashDigest(
24+
context.resourcePath + localName,
25+
'md5',
26+
'base64',
27+
5
28+
);
29+
// Use loaderUtils to find the file or folder name
30+
const className = loaderUtils.interpolateName(
31+
context,
32+
fileNameOrFolder + '_' + localName + '__' + hash,
33+
options
34+
);
35+
// remove the .module that appears in every classname when based on the file.
36+
return className.replace('.module_', '_');
37+
};

packages/react-dev-utils/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"eslintFormatter.js",
2020
"FileSizeReporter.js",
2121
"formatWebpackMessages.js",
22+
"getCSSModuleLocalIdent.js",
2223
"getProcessForPort.js",
2324
"ignoredFiles.js",
2425
"inquirer.js",

packages/react-scripts/config/webpack.config.dev.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
1717
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
1818
const eslintFormatter = require('react-dev-utils/eslintFormatter');
1919
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
20+
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
2021
const getClientEnvironment = require('./env');
2122
const paths = require('./paths');
2223

@@ -269,7 +270,7 @@ module.exports = {
269270
options: {
270271
importLoaders: 1,
271272
modules: true,
272-
localIdentName: '[path]__[name]___[local]',
273+
getLocalIdent: getCSSModuleLocalIdent,
273274
},
274275
},
275276
{

packages/react-scripts/config/webpack.config.prod.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
1919
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
2020
const eslintFormatter = require('react-dev-utils/eslintFormatter');
2121
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
22+
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
2223
const paths = require('./paths');
2324
const getClientEnvironment = require('./env');
2425

@@ -306,7 +307,7 @@ module.exports = {
306307
minimize: true,
307308
sourceMap: shouldUseSourceMap,
308309
modules: true,
309-
localIdentName: '[path]__[name]___[local]',
310+
getLocalIdent: getCSSModuleLocalIdent,
310311
},
311312
},
312313
{
@@ -422,7 +423,7 @@ module.exports = {
422423
// having to parse `index.html`.
423424
new ManifestPlugin({
424425
fileName: 'asset-manifest.json',
425-
publicPath: publicPath
426+
publicPath: publicPath,
426427
}),
427428
// Generate a service worker script that will precache, and keep up to date,
428429
// the HTML & assets that are part of the Webpack build.

packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@ describe('Integration', () => {
2626

2727
expect(
2828
doc.getElementsByTagName('style')[0].textContent.replace(/\s/g, '')
29+
).to.match(/.+style_cssModulesInclusion__.+\{background:.+;color:.+}/);
30+
expect(
31+
doc.getElementsByTagName('style')[1].textContent.replace(/\s/g, '')
2932
).to.match(
30-
/.+__style-module___cssModulesInclusion+\{background:.+;color:.+}/
33+
/.+assets_cssModulesIndexInclusion__.+\{background:.+;color:.+}/
3134
);
3235
});
3336

packages/react-scripts/fixtures/kitchensink/src/features/webpack/CssModulesInclusion.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@
77

88
import React from 'react';
99
import styles from './assets/style.module.css';
10+
import indexStyles from './assets/index.module.css';
1011

1112
export default () => (
12-
<p className={styles.cssModulesInclusion}>CSS Modules are working!</p>
13+
<div>
14+
<p className={styles.cssModulesInclusion}>CSS Modules are working!</p>
15+
<p className={indexStyles.cssModulesInclusion}>
16+
CSS Modules with index are working!
17+
</p>
18+
</div>
1319
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.cssModulesIndexInclusion {
2+
background: darkblue;
3+
color: lightblue;
4+
}

packages/react-scripts/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"html-webpack-plugin": "2.30.1",
5151
"identity-obj-proxy": "3.0.0",
5252
"jest": "22.1.2",
53+
"loader-utils": "^1.1.0",
5354
"object-assign": "4.1.1",
5455
"postcss-flexbugs-fixes": "3.2.0",
5556
"postcss-loader": "2.0.10",

packages/react-scripts/template/README.md

+13-11
Original file line numberDiff line numberDiff line change
@@ -518,49 +518,51 @@ If you are concerned about using Webpack-specific semantics, you can put all you
518518
<!---
519519
## Adding a CSS Modules stylesheet
520520
521-
This project supports [CSS Modules](https://github.com/css-modules/css-modules) alongside regular stylesheets using the **[name].module.css** file naming convention. CSS Modules allows the scoping of CSS by automatically creating a unique classname of the format **[dir]\_\_[filename]___[classname]**.
521+
This project supports [CSS Modules](https://github.com/css-modules/css-modules) alongside regular stylesheets using the **[name].module.css** file naming convention. CSS Modules allows the scoping of CSS by automatically creating a unique classname of the format **[filename]\_[classname]\_\_[hash]**.
522522
523523
An advantage of this is the ability to repeat the same classname within many CSS files without worrying about a clash.
524524
525525
### `Button.module.css`
526526
527527
```css
528-
.button {
529-
padding: 20px;
528+
.error {
529+
background-color: red;
530530
}
531531
```
532532
533533
### `another-stylesheet.css`
534534
535535
```css
536-
.button {
537-
color: green;
536+
.error {
537+
color: red;
538538
}
539539
```
540540
541541
### `Button.js`
542542
543543
```js
544544
import React, { Component } from 'react';
545-
import './another-stylesheet.css'; // Import regular stylesheet
546545
import styles from './Button.module.css'; // Import css modules stylesheet as styles
546+
import './another-stylesheet.css'; // Import regular stylesheet
547+
547548
548549
class Button extends Component {
549550
render() {
550-
// You can use them as regular CSS styles
551-
return <div className={styles.button} />;
551+
// reference as a js object
552+
return <button className={styles.error}>Error Button</button>;
552553
}
553554
}
554555
```
555556
### `exported HTML`
556-
No clashes from other `.button` classnames
557+
No clashes from other `.error` class names
557558
558559
```html
559-
<div class="src__Button-module___button"></div>
560+
<!-- This button has red background but not red text -->
561+
<button class="Button_error_ax7yz"></div>
560562
```
561563
562564
**This is an optional feature.** Regular html stylesheets and js imported stylesheets are fully supported. CSS Modules are only added when explictly named as a css module stylesheet using the extension `.module.css`.
563-
--->
565+
-->
564566
565567
## Post-Processing CSS
566568

0 commit comments

Comments
 (0)