Skip to content
This repository was archived by the owner on Jan 15, 2019. It is now read-only.

Commit b3a054a

Browse files
committed
Enable CSS Modules for SASS files
1 parent 11a65dd commit b3a054a

File tree

4 files changed

+456
-66
lines changed

4 files changed

+456
-66
lines changed

index.js

+29-4
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,47 @@
11

22
const ruleChildren = (loader) => loader.use || loader.oneOf || Array.isArray(loader.loader) && loader.loader || []
33

4-
const getRule = (rulesSource, matcher) => {
5-
let matchingRule = undefined
4+
const findRulesWithMatchingRule = (rulesSource, matcher) => {
5+
let result = undefined
66
const rules = Array.isArray(rulesSource) ? rulesSource : ruleChildren(rulesSource)
7-
rules.some(rule => matchingRule = matcher(rule) ? rule : getRule(ruleChildren(rule), matcher))
8-
return matchingRule
7+
rules.some((rule, index) => result = matcher(rule) ? {index, rules} : findRulesWithMatchingRule(ruleChildren(rule), matcher))
8+
return result
9+
}
10+
11+
const getRule = (rulesSource, matcher) => {
12+
const {index, rules} = findRulesWithMatchingRule(rulesSource, matcher)
13+
return rules[index]
914
}
1015

1116
const createLoaderMatcher = (loader) => (rule) => rule.loader && rule.loader.indexOf(`/${loader}/`) !== -1
1217
const cssLoaderMatcher = createLoaderMatcher('css-loader')
18+
const postcssLoaderMatcher = createLoaderMatcher('postcss-loader')
19+
const fileLoaderMatcher = createLoaderMatcher('file-loader')
1320
const cssRuleMatcher = (rule) => rule.test && String(rule.test) === String(/\.css$/)
1421

22+
const cloneDeep = require('lodash.clonedeep')
23+
24+
const addAfterLoader = (rulesSource, matcher, value) => {
25+
const {index, rules} = findRulesWithMatchingRule(rulesSource, matcher)
26+
rules.splice(index + 1, 0, value)
27+
}
28+
29+
const addBeforeLoader = (rulesSource, matcher, value) => {
30+
const {index, rules} = findRulesWithMatchingRule(rulesSource, matcher)
31+
rules.splice(index, 0, value)
32+
}
33+
1534
module.exports = function (config, env) {
1635
const cssRule = getRule(config.module.rules, cssRuleMatcher)
1736
const cssRuleCssLoader = getRule(cssRule, cssLoaderMatcher)
1837

1938
cssRuleCssLoader.options = Object.assign({modules: true, localIdentName: '[local]___[hash:base64:5]'}, cssRuleCssLoader.options)
2039

40+
const sassRule = cloneDeep(cssRule)
41+
sassRule.test = /\.s[ac]ss$/
42+
addAfterLoader(sassRule, postcssLoaderMatcher, require.resolve('sass-loader'))
43+
44+
addBeforeLoader(config.module.rules, fileLoaderMatcher, sassRule)
45+
2146
return config
2247
}

index.test.js

+55-14
Original file line numberDiff line numberDiff line change
@@ -109,25 +109,66 @@ describe('CSS Modules rewire', () => {
109109
}
110110
}
111111

112-
it('should enable CSS modules on the existing development CSS loader', () => {
113-
const result = subject(mockDevelopmentConfig)
112+
describe('CSS loader', () => {
113+
it('should enable CSS modules (development)', () => {
114+
const result = subject(mockDevelopmentConfig)
114115

115-
expect(result.module.rules[1].oneOf[2].use[1].options).toEqual({
116-
importLoaders: 1,
117-
modules: true,
118-
localIdentName: '[local]___[hash:base64:5]'
116+
expect(result.module.rules[1].oneOf[2].use[1].options).toEqual({
117+
importLoaders: 1,
118+
modules: true,
119+
localIdentName: '[local]___[hash:base64:5]'
120+
})
121+
})
122+
123+
it('should enable CSS modules (production)', () => {
124+
const result = subject(mockProductionConfig)
125+
126+
expect(result.module.rules[1].oneOf[2].loader[2].options).toEqual({
127+
importLoaders: 1,
128+
minimize: true,
129+
sourceMap: true,
130+
modules: true,
131+
localIdentName: '[local]___[hash:base64:5]'
132+
})
119133
})
120134
})
121135

122-
it('should enable CSS modules on the existing production CSS loader', () => {
123-
const result = subject(mockProductionConfig)
136+
describe('SASS loader', () => {
137+
describe('development', () => {
138+
139+
const result = subject(mockDevelopmentConfig)
140+
const cssLoader = result.module.rules[1].oneOf[2]
141+
const sassLoader = result.module.rules[1].oneOf[3]
142+
143+
it('should configure the test regex', () => {
144+
expect(result.module.rules[1].oneOf[3].test).toEqual(/\.s[ac]ss$/)
145+
})
146+
147+
it('should build upon the CSS loader', () => {
148+
expect(sassLoader.use.slice(0, 3)).toEqual(cssLoader.use)
149+
})
150+
151+
it('should append the sass-loader', () => {
152+
expect(sassLoader.use[3]).toContain('/sass-loader/')
153+
})
154+
})
155+
156+
describe('production', () => {
157+
const result = subject(mockProductionConfig)
158+
const cssLoader = result.module.rules[1].oneOf[2]
159+
const sassLoader = result.module.rules[1].oneOf[3]
160+
161+
it('should configure the test regex', () => {
162+
expect(result.module.rules[1].oneOf[3].test).toEqual(/\.s[ac]ss$/)
163+
})
164+
165+
it('should build upon the CSS loader', () => {
166+
expect(sassLoader.loader.slice(0, 4)).toEqual(cssLoader.loader)
167+
})
124168

125-
expect(result.module.rules[1].oneOf[2].loader[2].options).toEqual({
126-
importLoaders: 1,
127-
minimize: true,
128-
sourceMap: true,
129-
modules: true,
130-
localIdentName: '[local]___[hash:base64:5]'
169+
it('should append the sass-loader', () => {
170+
expect(sassLoader.loader[4]).toContain('/sass-loader/')
171+
})
131172
})
132173
})
133174
})

package.json

+10-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@
33
"test": "jest"
44
},
55
"devDependencies": {
6-
"jest": "^21.2.1"
6+
"jest": "^21.2.1",
7+
"node-sass": "^4.5.3",
8+
"sass-loader": "^6.0.6"
9+
},
10+
"dependencies": {
11+
"lodash.clonedeep": "^4.5.0"
12+
},
13+
"peerDependencies": {
14+
"node-sass": "^4.5.3",
15+
"sass-loader": "^6.0.6"
716
}
817
}

0 commit comments

Comments
 (0)