Commit fc7c9915 authored by Ro Savage's avatar Ro Savage Committed by Joe Haddad
Browse files

Add support for CSS Modules with explicit filename (#2285)

* Add css modules with [name].modules.css file convention

* Add e2e for CSS Modules

* Updated based on feedback

* Change css modules class name to be deterministic and fix licences

* Update css modules class naming convention
parent 0ddc043a
3 merge requests!12191Lim.Pisey.168:/Identified - We are currently investigating reports of missing build logs. The issue has been identified and a resolution is in progress. We will provide a further update when available.Mar 21, 09:02 UTC,!12853brikk,!5717Automatically extract project file structure from build bundle file
Showing with 186 additions and 19 deletions
+186 -19
......@@ -30,6 +30,20 @@ const publicUrl = '';
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);
// Options for PostCSS as we reference these options twice
// Adds vendor prefixing to support IE9 and above
const postCSSLoaderOptions = {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
flexbox: 'no-2009',
}),
],
};
// This is the development configuration.
// It is focused on developer experience and fast rebuilds.
// The production configuration is different and lives in a separate file.
......@@ -209,8 +223,10 @@ module.exports = {
// "style" loader turns CSS into JS modules that inject <style> tags.
// In production, we use a plugin to extract that CSS to a file, but
// in development "style" loader enables hot editing of CSS.
// By default we support CSS Modules with the extension .module.css
{
test: /\.css$/,
exclude: /\.module\.css$/,
use: [
require.resolve('style-loader'),
{
......@@ -221,18 +237,28 @@ module.exports = {
},
{
loader: require.resolve('postcss-loader'),
options: postCSSLoaderOptions,
},
],
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: /\.module\.css$/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
flexbox: 'no-2009',
}),
],
importLoaders: 1,
modules: true,
localIdentName: '[path]__[name]___[local]',
},
},
{
loader: require.resolve('postcss-loader'),
options: postCSSLoaderOptions,
},
],
},
// "file" loader makes sure those assets get served by WebpackDevServer.
......
......@@ -55,6 +55,20 @@ const extractTextPluginOptions = shouldUseRelativeAssetPaths
{ publicPath: Array(cssFilename.split('/').length).join('../') }
: {};
// Options for PostCSS as we reference these options twice
// Adds vendor prefixing to support IE9 and above
const postCSSLoaderOptions = {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
flexbox: 'no-2009',
}),
],
};
// This is the production configuration.
// It compiles slowly and is focused on producing a fast and minimal bundle.
// The development configuration is different and lives in a separate file.
......@@ -221,8 +235,10 @@ module.exports = {
// tags. If you use code splitting, however, any async bundles will still
// use the "style" loader inside the async code so CSS from them won't be
// in the main CSS file.
// By default we support CSS Modules with the extension .module.css
{
test: /\.css$/,
exclude: /\.module\.css$/,
loader: ExtractTextPlugin.extract(
Object.assign(
{
......@@ -243,18 +259,43 @@ module.exports = {
},
{
loader: require.resolve('postcss-loader'),
options: postCSSLoaderOptions,
},
],
},
extractTextPluginOptions
)
),
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: /\.module\.css$/,
loader: ExtractTextPlugin.extract(
Object.assign(
{
fallback: {
loader: require.resolve('style-loader'),
options: {
hmr: false,
},
},
use: [
{
loader: require.resolve('css-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
flexbox: 'no-2009',
}),
],
importLoaders: 1,
minimize: true,
sourceMap: shouldUseSourceMap,
modules: true,
localIdentName: '[path]__[name]___[local]',
},
},
{
loader: require.resolve('postcss-loader'),
options: postCSSLoaderOptions,
},
],
},
extractTextPluginOptions
......
......@@ -21,6 +21,16 @@ describe('Integration', () => {
).to.match(/#feature-css-inclusion\{background:.+;color:.+}/);
});
it('css modules inclusion', async () => {
const doc = await initDOM('css-modules-inclusion');
expect(
doc.getElementsByTagName('style')[0].textContent.replace(/\s/g, '')
).to.match(
/.+__style-module___cssModulesInclusion+\{background:.+;color:.+}/
);
});
it('image inclusion', async () => {
const doc = await initDOM('image-inclusion');
......
......@@ -81,6 +81,11 @@ class App extends Component {
this.setFeature(f.default)
);
break;
case 'css-modules-inclusion':
import(
'./features/webpack/CssModulesInclusion'
).then(f => this.setFeature(f.default));
break;
case 'custom-interpolation':
import('./features/syntax/CustomInterpolation').then(f =>
this.setFeature(f.default)
......
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import styles from './assets/style.module.css';
export default () => (
<p className={styles.cssModulesInclusion}>CSS Modules are working!</p>
);
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import CssModulesInclusion from './CssModulesInclusion';
describe('css modules inclusion', () => {
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<CssModulesInclusion />, div);
});
});
.cssModulesInclusion {
background: darkblue;
color: lightblue;
}
......@@ -45,6 +45,7 @@
"file-loader": "1.1.6",
"fs-extra": "5.0.0",
"html-webpack-plugin": "2.30.1",
"identity-obj-proxy": "3.0.0",
"jest": "22.1.1",
"object-assign": "4.1.1",
"postcss-flexbugs-fixes": "3.2.0",
......
......@@ -39,9 +39,13 @@ module.exports = (resolve, rootDir, isEjecting) => {
'config/jest/fileTransform.js'
),
},
transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs)$'],
transformIgnorePatterns: [
'[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs)$',
'^.+\\.module\\.css$',
],
moduleNameMapper: {
'^react-native$': 'react-native-web',
'^.+\\.module\\.css$': 'identity-obj-proxy',
},
moduleFileExtensions: [
'web.js',
......
......@@ -24,6 +24,7 @@ You can find the most recent version of this guide [here](https://github.com/fac
- [Importing a Component](#importing-a-component)
- [Code Splitting](#code-splitting)
- [Adding a Stylesheet](#adding-a-stylesheet)
- [Adding a CSS Modules stylesheet](#adding-a-css-modules-stylesheet)
- [Post-Processing CSS](#post-processing-css)
- [Adding a CSS Preprocessor (Sass, Less etc.)](#adding-a-css-preprocessor-sass-less-etc)
- [Adding Images, Fonts, and Files](#adding-images-fonts-and-files)
......@@ -513,6 +514,51 @@ In development, expressing dependencies this way allows your styles to be reload
If you are concerned about using Webpack-specific semantics, you can put all your CSS right into `src/index.css`. It would still be imported from `src/index.js`, but you could always remove that import if you later migrate to a different build tool.
## Adding a CSS Modules stylesheet
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]**.
An advantage of this is the ability to repeat the same classname within many CSS files without worrying about a clash.
### `Button.module.css`
```css
.button {
padding: 20px;
}
```
### `another-stylesheet.css`
```css
.button {
color: green;
}
```
### `Button.js`
```js
import React, { Component } from 'react';
import './another-stylesheet.css'; // Import regular stylesheet
import styles from './Button.module.css'; // Import css modules stylesheet as styles
class Button extends Component {
render() {
// You can use them as regular CSS styles
return <div className={styles.button} />;
}
}
```
### `exported HTML`
No clashes from other `.button` classnames
```html
<div class="src__Button-module___button"></div>
```
**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`.
## Post-Processing CSS
This project setup minifies your CSS and adds vendor prefixes to it automatically through [Autoprefixer](https://github.com/postcss/autoprefixer) so you don’t need to worry about it.
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment