Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CSS Module support. #1248

Merged
merged 5 commits into from
Feb 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion docs/css.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
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
Expand All @@ -30,6 +30,36 @@ const Hello = props => (
)
```

## Import scoped 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.module.sass

.helloReact
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 styles from '../hello_react/styles/hello-react'

const Hello = props => (
<div className={styles.helloReact}>
<img src={helloIcon} alt="hello-icon" />
<p>Hello {props.name}!</p>
</div>
)
```

**Note:** Declared class is referenced as object property in JavaScript.


## Link styles from your Rails views

Expand Down
3 changes: 3 additions & 0 deletions lib/install/config/webpacker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ default: &default
- .sass
- .scss
- .css
- .module.sass
- .module.scss
- .module.css
- .png
- .svg
- .gif
Expand Down
3 changes: 3 additions & 0 deletions package/__tests__/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ describe('Webpacker.yml config', () => {
'.sass',
'.scss',
'.css',
'.module.sass',
'.module.scss',
'.module.css',
'.png',
'.svg',
'.gif',
Expand Down
44 changes: 6 additions & 38 deletions package/rules/css.js
Original file line number Diff line number Diff line change
@@ -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,
[]
)
4 changes: 4 additions & 0 deletions package/rules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
7 changes: 7 additions & 0 deletions package/rules/module.css.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const styleRuleFactory = require('./style_rule_factory')

module.exports = styleRuleFactory(
/\.(css)$/i,
true,
[]
)
10 changes: 10 additions & 0 deletions package/rules/module.sass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const styleRuleFactory = require('./style_rule_factory')

module.exports = styleRuleFactory(
/\.(scss|sass)$/i,
true,
[{
loader: 'sass-loader',
options: { sourceMap: true }
}]
)
17 changes: 6 additions & 11 deletions package/rules/sass.js
Original file line number Diff line number Diff line change
@@ -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
)
60 changes: 60 additions & 0 deletions package/rules/style_rule_factory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
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 = Object.assign(
{},
{ test, use: ExtractTextPlugin.extract(extractOptions) },
modulesCondition
)

// For hot-reloading use regular loaders
const inlineCSSLoader = Object.assign(
{},
{ test, use: [styleLoader].concat(extractOptions.use) },
modulesCondition
)

return extractCSS ? extractCSSLoader : inlineCSSLoader
}

module.exports = styleRuleFactory
3 changes: 3 additions & 0 deletions test/test_app/config/webpacker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ default: &default
- .sass
- .scss
- .css
- .module.sass
- .module.scss
- .module.css
- .png
- .svg
- .gif
Expand Down