From 2e099feaf96fea63ee24a77aa570e7a182ad250c Mon Sep 17 00:00:00 2001 From: tai2 Date: Mon, 5 Feb 2018 11:50:59 +0900 Subject: [PATCH 1/5] CSS Modules by filename suffix. --- docs/css.md | 10 +++--- lib/install/config/webpacker.yml | 3 ++ package/rules/css.js | 44 ++++--------------------- package/rules/index.js | 4 +++ package/rules/module.css.js | 7 ++++ package/rules/module.sass.js | 10 ++++++ package/rules/sass.js | 17 ++++------ package/rules/style_rule_factory.js | 50 +++++++++++++++++++++++++++++ test/test_app/config/webpacker.yml | 3 ++ 9 files changed, 95 insertions(+), 53 deletions(-) create mode 100644 package/rules/module.css.js create mode 100644 package/rules/module.sass.js create mode 100644 package/rules/style_rule_factory.js diff --git a/docs/css.md b/docs/css.md index 82580028d..437675f6e 100644 --- a/docs/css.md +++ b/docs/css.md @@ -6,10 +6,12 @@ Webpacker supports importing CSS, Sass and SCSS files directly into your JavaScr ## Import styles into your JS app +Stylesheets end with '.modules.*' is treated as [CSS Modules](https://github.com/css-modules/css-modules). + ```sass -// app/javascript/hello_react/styles/hello-react.sass +// app/javascript/hello_react/styles/hello-react.module.sass -.hello-react +.helloReact padding: 20px font-size: 12px ``` @@ -20,10 +22,10 @@ Webpacker supports importing CSS, Sass and SCSS files directly into your JavaScr import React from 'react' import helloIcon from '../hello_react/images/icon.png' -import '../hello_react/styles/hello-react' +import styles from '../hello_react/styles/hello-react' const Hello = props => ( -
+
hello-icon

Hello {props.name}!

diff --git a/lib/install/config/webpacker.yml b/lib/install/config/webpacker.yml index d3f24e1b4..6da493bcb 100644 --- a/lib/install/config/webpacker.yml +++ b/lib/install/config/webpacker.yml @@ -18,6 +18,9 @@ default: &default - .sass - .scss - .css + - .module.sass + - .module.scss + - .module.css - .png - .svg - .gif diff --git a/package/rules/css.js b/package/rules/css.js index 521f21d51..8354040b8 100644 --- a/package/rules/css.js +++ b/package/rules/css.js @@ -1,39 +1,7 @@ -const ExtractTextPlugin = require('extract-text-webpack-plugin') -const path = require('path') -const { dev_server: devServer } = require('../config') +const styleRuleFactory = require('./style_rule_factory') -const postcssConfigPath = path.resolve(process.cwd(), '.postcssrc.yml') -const isProduction = process.env.NODE_ENV === 'production' -const inDevServer = process.argv.find(v => v.includes('webpack-dev-server')) -const isHMR = inDevServer && (devServer && devServer.hmr) -const extractCSS = !(isHMR) || isProduction - -const styleLoader = { - loader: 'style-loader', - options: { - hmr: isHMR, - sourceMap: true - } -} - -const extractOptions = { - fallback: styleLoader, - use: [ - { loader: 'css-loader', options: { minimize: isProduction, sourceMap: true, importLoaders: 2 } }, - { loader: 'postcss-loader', options: { sourceMap: true, config: { path: postcssConfigPath } } } - ] -} - -// For production extract styles to a separate bundle -const extractCSSLoader = { - test: /\.(css)$/i, - use: ExtractTextPlugin.extract(extractOptions) -} - -// For hot-reloading use regular loaders -const inlineCSSLoader = { - test: /\.(css)$/i, - use: [styleLoader].concat(extractOptions.use) -} - -module.exports = extractCSS ? extractCSSLoader : inlineCSSLoader +module.exports = styleRuleFactory( + /\.(css)$/i, + false, + [] +) diff --git a/package/rules/index.js b/package/rules/index.js index 7784dd9cc..1a4ea3c2a 100644 --- a/package/rules/index.js +++ b/package/rules/index.js @@ -2,10 +2,14 @@ const babel = require('./babel') const file = require('./file') const css = require('./css') const sass = require('./sass') +const moduleCss = require('./module.css') +const moduleSass = require('./module.sass') module.exports = { babel, css, sass, + moduleCss, + moduleSass, file } diff --git a/package/rules/module.css.js b/package/rules/module.css.js new file mode 100644 index 000000000..6671d8548 --- /dev/null +++ b/package/rules/module.css.js @@ -0,0 +1,7 @@ +const styleRuleFactory = require('./style_rule_factory') + +module.exports = styleRuleFactory( + /\.(css)$/i, + true, + [] +) diff --git a/package/rules/module.sass.js b/package/rules/module.sass.js new file mode 100644 index 000000000..9a8f01a4f --- /dev/null +++ b/package/rules/module.sass.js @@ -0,0 +1,10 @@ +const styleRuleFactory = require('./style_rule_factory') + +module.exports = styleRuleFactory( + /\.(scss|sass)$/i, + true, + [{ + loader: 'sass-loader', + options: { sourceMap: true } + }] +) diff --git a/package/rules/sass.js b/package/rules/sass.js index 9a8107a2c..41cec0899 100644 --- a/package/rules/sass.js +++ b/package/rules/sass.js @@ -1,15 +1,10 @@ -const cssLoader = require('./css') -const deepMerge = require('../utils/deep_merge') +const styleRuleFactory = require('./style_rule_factory') -// Duplicate and remove css loader object reference -let sassLoader = JSON.parse(JSON.stringify(cssLoader)) - -sassLoader = deepMerge(sassLoader, { - test: /\.(scss|sass)$/i, - use: [{ +module.exports = styleRuleFactory( + /\.(scss|sass)$/i, + false, + [{ loader: 'sass-loader', options: { sourceMap: true } }] -}) - -module.exports = sassLoader +) diff --git a/package/rules/style_rule_factory.js b/package/rules/style_rule_factory.js new file mode 100644 index 000000000..1dd1969b8 --- /dev/null +++ b/package/rules/style_rule_factory.js @@ -0,0 +1,50 @@ +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const path = require('path') +const { dev_server: devServer } = require('../config') + +const postcssConfigPath = path.resolve(process.cwd(), '.postcssrc.yml') +const isProduction = process.env.NODE_ENV === 'production' +const inDevServer = process.argv.find(v => v.includes('webpack-dev-server')) +const isHMR = inDevServer && (devServer && devServer.hmr) +const extractCSS = !(isHMR) || isProduction + +const styleRuleFactory = (test, modules, preprocessors) => { + const styleLoader = { + loader: 'style-loader', + options: { + hmr: isHMR, + sourceMap: true + } + } + + const extractOptions = { + fallback: styleLoader, + use: [ + { loader: 'css-loader', options: { minimize: isProduction, sourceMap: true, importLoaders: 2, modules } }, + { loader: 'postcss-loader', options: { sourceMap: true, config: { path: postcssConfigPath } } }, + ...preprocessors + ] + } + + const modulesCondition = modules + ? { include: /\.module\.[a-z]+$/ } + : { exclude: /\.module\.[a-z]+$/ } + + // For production extract styles to a separate bundle + const extractCSSLoader = { + test, + use: ExtractTextPlugin.extract(extractOptions), + ...modulesCondition + } + + // For hot-reloading use regular loaders + const inlineCSSLoader = { + test, + use: [styleLoader].concat(extractOptions.use), + ...modulesCondition + } + + return extractCSS ? extractCSSLoader : inlineCSSLoader +} + +module.exports = styleRuleFactory diff --git a/test/test_app/config/webpacker.yml b/test/test_app/config/webpacker.yml index c2a2a46c6..beaf1831c 100644 --- a/test/test_app/config/webpacker.yml +++ b/test/test_app/config/webpacker.yml @@ -20,6 +20,9 @@ default: &default - .sass - .scss - .css + - .module.sass + - .module.scss + - .module.css - .png - .svg - .gif From dfa7c0c8bcd0b8735966eeb60dab4d492ed70d42 Mon Sep 17 00:00:00 2001 From: tai2 Date: Fri, 9 Feb 2018 22:26:11 +0900 Subject: [PATCH 2/5] Use Object.assign() instread of spread operator. --- package/rules/style_rule_factory.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/package/rules/style_rule_factory.js b/package/rules/style_rule_factory.js index 1dd1969b8..e61f12740 100644 --- a/package/rules/style_rule_factory.js +++ b/package/rules/style_rule_factory.js @@ -31,18 +31,18 @@ const styleRuleFactory = (test, modules, preprocessors) => { : { exclude: /\.module\.[a-z]+$/ } // For production extract styles to a separate bundle - const extractCSSLoader = { - test, - use: ExtractTextPlugin.extract(extractOptions), - ...modulesCondition - } + const extractCSSLoader = Object.assign( + {}, + { test, use: ExtractTextPlugin.extract(extractOptions) }, + modulesCondition + ) // For hot-reloading use regular loaders - const inlineCSSLoader = { - test, - use: [styleLoader].concat(extractOptions.use), - ...modulesCondition - } + const inlineCSSLoader = Object.assign( + {}, + { test, use: [styleLoader].concat(extractOptions.use) }, + modulesCondition + ) return extractCSS ? extractCSSLoader : inlineCSSLoader } From 5aa8b75331debbcdc97a862b7ca2302a4c7a286e Mon Sep 17 00:00:00 2001 From: tai2 Date: Fri, 9 Feb 2018 22:32:14 +0900 Subject: [PATCH 3/5] Fix lint errors. 'object-curly-newline' --- package/rules/style_rule_factory.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/package/rules/style_rule_factory.js b/package/rules/style_rule_factory.js index e61f12740..a6d9bf6a5 100644 --- a/package/rules/style_rule_factory.js +++ b/package/rules/style_rule_factory.js @@ -20,8 +20,18 @@ const styleRuleFactory = (test, modules, preprocessors) => { const extractOptions = { fallback: styleLoader, use: [ - { loader: 'css-loader', options: { minimize: isProduction, sourceMap: true, importLoaders: 2, modules } }, - { loader: 'postcss-loader', options: { sourceMap: true, config: { path: postcssConfigPath } } }, + { + loader: 'css-loader', + options: { + minimize: isProduction, sourceMap: true, importLoaders: 2, modules + } + }, + { + loader: 'postcss-loader', + options: { + sourceMap: true, config: { path: postcssConfigPath } + } + }, ...preprocessors ] } From 4c0f3a0b21b899efc742117c2f9553108e21d37e Mon Sep 17 00:00:00 2001 From: tai2 Date: Fri, 9 Feb 2018 23:05:30 +0900 Subject: [PATCH 4/5] Add regular css description. --- docs/css.md | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/docs/css.md b/docs/css.md index 437675f6e..a863929eb 100644 --- a/docs/css.md +++ b/docs/css.md @@ -4,7 +4,33 @@ Webpacker supports importing CSS, Sass and SCSS files directly into your JavaScript files. -## Import styles into your JS app +## Import global styles into your JS app + +```sass +// app/javascript/hello_react/styles/hello-react.sass + +.hello-react + padding: 20px + font-size: 12px +``` + +```js +// React component example +// app/javascripts/packs/hello_react.jsx + +import React from 'react' +import helloIcon from '../hello_react/images/icon.png' +import '../hello_react/styles/hello-react' + +const Hello = props => ( +
+ hello-icon +

Hello {props.name}!

+
+) +``` + +## Import scoped styles into your JS app Stylesheets end with '.modules.*' is treated as [CSS Modules](https://github.com/css-modules/css-modules). @@ -32,6 +58,8 @@ const Hello = props => ( ) ``` +**Note:** Declared class is referenced as object property in JavaScript. + ## Link styles from your Rails views From 20213307e3260a47f5ab6b43972ec188f2cb8ee5 Mon Sep 17 00:00:00 2001 From: tai2 Date: Fri, 9 Feb 2018 23:27:18 +0900 Subject: [PATCH 5/5] Fix a test case. --- package/__tests__/config.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package/__tests__/config.js b/package/__tests__/config.js index 712ffc178..e5b6b94cc 100644 --- a/package/__tests__/config.js +++ b/package/__tests__/config.js @@ -15,6 +15,9 @@ describe('Webpacker.yml config', () => { '.sass', '.scss', '.css', + '.module.sass', + '.module.scss', + '.module.css', '.png', '.svg', '.gif',