Skip to content

Commit e87e139

Browse files
ro-savagerubenbp
authored andcommitted
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
1 parent d932ab1 commit e87e139

File tree

10 files changed

+304
-107
lines changed

10 files changed

+304
-107
lines changed

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

+54-32
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,20 @@ const publicUrl = '';
3030
// Get environment variables to inject into our app.
3131
const env = getClientEnvironment(publicUrl);
3232

33+
// Options for PostCSS as we reference these options twice
34+
// Adds vendor prefixing to support IE9 and above
35+
const postCSSLoaderOptions = {
36+
// Necessary for external CSS imports to work
37+
// https://github.com/facebookincubator/create-react-app/issues/2677
38+
ident: 'postcss',
39+
plugins: () => [
40+
require('postcss-flexbugs-fixes'),
41+
autoprefixer({
42+
flexbox: 'no-2009',
43+
}),
44+
],
45+
};
46+
3347
// This is the development configuration.
3448
// It is focused on developer experience and fast rebuilds.
3549
// The production configuration is different and lives in a separate file.
@@ -123,7 +137,7 @@ module.exports = {
123137
// please link the files into your node_modules/ and let module-resolution kick in.
124138
// Make sure your source files are compiled, as they will not be processed in any way.
125139
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
126-
new TsconfigPathsPlugin({configFile: paths.appTsConfig})
140+
new TsconfigPathsPlugin({ configFile: paths.appTsConfig }),
127141
],
128142
},
129143
module: {
@@ -176,6 +190,7 @@ module.exports = {
176190
// in development "style" loader enables hot editing of CSS.
177191
{
178192
test: /\.css$/,
193+
exclude: /\.module\.css$/,
179194
use: [
180195
require.resolve('style-loader'),
181196
{
@@ -186,30 +201,34 @@ module.exports = {
186201
},
187202
{
188203
loader: require.resolve('postcss-loader'),
204+
options: postCSSLoaderOptions,
205+
},
206+
],
207+
},
208+
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
209+
// using the extension .module.css
210+
{
211+
test: /\.module\.css$/,
212+
use: [
213+
require.resolve('style-loader'),
214+
{
215+
loader: require.resolve('css-loader'),
189216
options: {
190-
// Necessary for external CSS imports to work
191-
// https://github.com/facebookincubator/create-react-app/issues/2677
192-
ident: 'postcss',
193-
plugins: () => [
194-
require('postcss-flexbugs-fixes'),
195-
autoprefixer({
196-
browsers: [
197-
'>1%',
198-
'last 4 versions',
199-
'Firefox ESR',
200-
'not ie < 9', // React doesn't support IE8 anyway
201-
],
202-
flexbox: 'no-2009',
203-
}),
204-
],
217+
importLoaders: 1,
218+
modules: true,
219+
localIdentName: '[path]__[name]___[local]',
205220
},
206221
},
222+
{
223+
loader: require.resolve('postcss-loader'),
224+
options: postCSSLoaderOptions,
225+
},
207226
],
208227
},
209-
210228
// BIKO::START
211229
{
212230
test: /\.scss$/,
231+
exclude: /\.module\.scss$/,
213232
use: [
214233
require.resolve('style-loader'),
215234
{
@@ -220,24 +239,27 @@ module.exports = {
220239
},
221240
{
222241
loader: require.resolve('postcss-loader'),
242+
options: postCSSLoaderOptions,
243+
},
244+
require.resolve('sass-loader'),
245+
],
246+
},
247+
{
248+
test: /\.module\.scss$/,
249+
use: [
250+
require.resolve('style-loader'),
251+
{
252+
loader: require.resolve('css-loader'),
223253
options: {
224-
// Necessary for external CSS imports to work
225-
// https://github.com/facebookincubator/create-react-app/issues/2677
226-
ident: 'postcss',
227-
plugins: () => [
228-
require('postcss-flexbugs-fixes'),
229-
autoprefixer({
230-
browsers: [
231-
'>1%',
232-
'last 4 versions',
233-
'Firefox ESR',
234-
'not ie < 9', // React doesn't support IE8 anyway
235-
],
236-
flexbox: 'no-2009',
237-
}),
238-
],
254+
importLoaders: 1,
255+
modules: true,
256+
localIdentName: '[path]__[name]___[local]',
239257
},
240258
},
259+
{
260+
loader: require.resolve('postcss-loader'),
261+
options: postCSSLoaderOptions,
262+
},
241263
require.resolve('sass-loader'),
242264
],
243265
},

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

+90-31
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,20 @@ const extractTextPluginOptions = shouldUseRelativeAssetPaths
5555
{ publicPath: Array(cssFilename.split('/').length).join('../') }
5656
: {};
5757

58+
// Options for PostCSS as we reference these options twice
59+
// Adds vendor prefixing to support IE9 and above
60+
const postCSSLoaderOptions = {
61+
// Necessary for external CSS imports to work
62+
// https://github.com/facebookincubator/create-react-app/issues/2677
63+
ident: 'postcss',
64+
plugins: () => [
65+
require('postcss-flexbugs-fixes'),
66+
autoprefixer({
67+
flexbox: 'no-2009',
68+
}),
69+
],
70+
};
71+
5872
// This is the production configuration.
5973
// It compiles slowly and is focused on producing a fast and minimal bundle.
6074
// The development configuration is different and lives in a separate file.
@@ -130,7 +144,7 @@ module.exports = {
130144
// please link the files into your node_modules/ and let module-resolution kick in.
131145
// Make sure your source files are compiled, as they will not be processed in any way.
132146
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
133-
new TsconfigPathsPlugin({configFile: paths.appTsConfig})
147+
new TsconfigPathsPlugin({ configFile: paths.appTsConfig }),
134148
],
135149
},
136150
module: {
@@ -186,8 +200,10 @@ module.exports = {
186200
// tags. If you use code splitting, however, any async bundles will still
187201
// use the "style" loader inside the async code so CSS from them won't be
188202
// in the main CSS file.
203+
// By default we support CSS Modules with the extension .module.css
189204
{
190205
test: /\.css$/,
206+
exclude: /\.module\.css$/,
191207
loader: ExtractTextPlugin.extract(
192208
Object.assign(
193209
{
@@ -208,24 +224,43 @@ module.exports = {
208224
},
209225
{
210226
loader: require.resolve('postcss-loader'),
227+
options: postCSSLoaderOptions,
228+
},
229+
],
230+
},
231+
extractTextPluginOptions
232+
)
233+
),
234+
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
235+
},
236+
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
237+
// using the extension .module.css
238+
{
239+
test: /\.module\.css$/,
240+
loader: ExtractTextPlugin.extract(
241+
Object.assign(
242+
{
243+
fallback: {
244+
loader: require.resolve('style-loader'),
245+
options: {
246+
hmr: false,
247+
},
248+
},
249+
use: [
250+
{
251+
loader: require.resolve('css-loader'),
211252
options: {
212-
// Necessary for external CSS imports to work
213-
// https://github.com/facebookincubator/create-react-app/issues/2677
214-
ident: 'postcss',
215-
plugins: () => [
216-
require('postcss-flexbugs-fixes'),
217-
autoprefixer({
218-
browsers: [
219-
'>1%',
220-
'last 4 versions',
221-
'Firefox ESR',
222-
'not ie < 9', // React doesn't support IE8 anyway
223-
],
224-
flexbox: 'no-2009',
225-
}),
226-
],
253+
importLoaders: 1,
254+
minimize: true,
255+
sourceMap: shouldUseSourceMap,
256+
modules: true,
257+
localIdentName: '[path]__[name]___[local]',
227258
},
228259
},
260+
{
261+
loader: require.resolve('postcss-loader'),
262+
options: postCSSLoaderOptions,
263+
},
229264
],
230265
},
231266
extractTextPluginOptions
@@ -237,6 +272,7 @@ module.exports = {
237272
// BIKO::START
238273
{
239274
test: /\.scss$/,
275+
exclude: /\.module\.scss$/,
240276
loader: ExtractTextPlugin.extract(
241277
Object.assign(
242278
{
@@ -257,24 +293,47 @@ module.exports = {
257293
},
258294
{
259295
loader: require.resolve('postcss-loader'),
296+
options: postCSSLoaderOptions,
297+
},
298+
{
299+
loader: require.resolve('sass-loader'),
300+
options: {
301+
sourceMap: shouldUseSourceMap,
302+
},
303+
},
304+
],
305+
},
306+
extractTextPluginOptions
307+
)
308+
),
309+
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
310+
},
311+
{
312+
test: /\.module\.scss$/,
313+
loader: ExtractTextPlugin.extract(
314+
Object.assign(
315+
{
316+
fallback: {
317+
loader: require.resolve('style-loader'),
318+
options: {
319+
hmr: false,
320+
},
321+
},
322+
use: [
323+
{
324+
loader: require.resolve('css-loader'),
260325
options: {
261-
// Necessary for external CSS imports to work
262-
// https://github.com/facebookincubator/create-react-app/issues/2677
263-
ident: 'postcss',
264-
plugins: () => [
265-
require('postcss-flexbugs-fixes'),
266-
autoprefixer({
267-
browsers: [
268-
'>1%',
269-
'last 4 versions',
270-
'Firefox ESR',
271-
'not ie < 9', // React doesn't support IE8 anyway
272-
],
273-
flexbox: 'no-2009',
274-
}),
275-
],
326+
importLoaders: 1,
327+
minimize: true,
328+
sourceMap: shouldUseSourceMap,
329+
modules: true,
330+
localIdentName: '[path]__[name]___[local]',
276331
},
277332
},
333+
{
334+
loader: require.resolve('postcss-loader'),
335+
options: postCSSLoaderOptions,
336+
},
278337
{
279338
loader: require.resolve('sass-loader'),
280339
options: {

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

+10
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ describe('Integration', () => {
2121
).to.match(/#feature-css-inclusion\{background:.+;color:.+}/);
2222
});
2323

24+
it('css modules inclusion', async () => {
25+
const doc = await initDOM('css-modules-inclusion');
26+
27+
expect(
28+
doc.getElementsByTagName('style')[0].textContent.replace(/\s/g, '')
29+
).to.match(
30+
/.+__style-module___cssModulesInclusion+\{background:.+;color:.+}/
31+
);
32+
});
33+
2434
it('image inclusion', async () => {
2535
const doc = await initDOM('image-inclusion');
2636

0 commit comments

Comments
 (0)