Skip to content

Commit 356e538

Browse files
committed
Support CSS modules in Vue.js projects by default
1 parent a33bd02 commit 356e538

File tree

6 files changed

+116
-3
lines changed

6 files changed

+116
-3
lines changed

fixtures/vuejs-css-modules/App.vue

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<template>
2+
<div id="app" class="red" :class="$style.italic"></div>
3+
</template>
4+
5+
<style>
6+
.red {
7+
color: red;
8+
}
9+
</style>
10+
11+
<style module>
12+
.italic {
13+
font-style: italic;
14+
}
15+
</style>

fixtures/vuejs-css-modules/main.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import Vue from 'vue'
2+
import App from './App'
3+
4+
new Vue({
5+
el: '#app',
6+
template: '<App/>',
7+
components: { App }
8+
})

lib/config-generator.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,21 @@ class ConfigGenerator {
228228
},
229229
{
230230
test: /\.css$/,
231-
use: cssExtractLoaderUtil.prependLoaders(this.webpackConfig, cssLoaderUtil.getLoaders(this.webpackConfig))
231+
oneOf: [
232+
{
233+
resourceQuery: /module/,
234+
use: cssExtractLoaderUtil.prependLoaders(
235+
this.webpackConfig,
236+
cssLoaderUtil.getLoaders(this.webpackConfig, true)
237+
)
238+
},
239+
{
240+
use: cssExtractLoaderUtil.prependLoaders(
241+
this.webpackConfig,
242+
cssLoaderUtil.getLoaders(this.webpackConfig)
243+
)
244+
}
245+
]
232246
}
233247
];
234248

lib/loaders/css.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ const applyOptionsCallback = require('../utils/apply-options-callback');
1515
module.exports = {
1616
/**
1717
* @param {WebpackConfig} webpackConfig
18+
* @param {boolean} useCssModules
1819
* @return {Array} of loaders to use for CSS files
1920
*/
20-
getLoaders(webpackConfig) {
21+
getLoaders(webpackConfig, useCssModules = false) {
2122
const usePostCssLoader = webpackConfig.usePostCssLoader;
2223

2324
const options = {
@@ -27,7 +28,9 @@ module.exports = {
2728
// be applied to those imports? This defaults to 0. When postcss-loader
2829
// is used, we set it to 1, so that postcss-loader is applied
2930
// to @import resources.
30-
importLoaders: usePostCssLoader ? 1 : 0
31+
importLoaders: usePostCssLoader ? 1 : 0,
32+
modules: useCssModules,
33+
localIdentName: '[local]_[hash:base64:5]',
3134
};
3235

3336
const cssLoaders = [

test/functional.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,6 +1350,61 @@ module.exports = {
13501350
});
13511351
});
13521352

1353+
it('Vue.js supports CSS modules', (done) => {
1354+
const appDir = testSetup.createTestAppDir();
1355+
const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev');
1356+
config.enableSingleRuntimeChunk();
1357+
config.setPublicPath('/build');
1358+
config.addEntry('main', './vuejs-css-modules/main');
1359+
config.enableVueLoader();
1360+
config.enableSassLoader();
1361+
config.enableLessLoader();
1362+
config.configureCssLoader(options => {
1363+
// Remove hashes from local ident names
1364+
// since they are not always the same.
1365+
options.localIdentName = '[local]_foo';
1366+
});
1367+
1368+
testSetup.runWebpack(config, (webpackAssert) => {
1369+
expect(config.outputPath).to.be.a.directory().with.deep.files([
1370+
'main.js',
1371+
'main.css',
1372+
'manifest.json',
1373+
'entrypoints.json',
1374+
'runtime.js',
1375+
]);
1376+
1377+
// Standard CSS
1378+
webpackAssert.assertOutputFileContains(
1379+
'main.css',
1380+
'.red {'
1381+
);
1382+
1383+
// CSS modules
1384+
webpackAssert.assertOutputFileContains(
1385+
'main.css',
1386+
'.italic_foo {'
1387+
);
1388+
1389+
testSetup.requestTestPage(
1390+
path.join(config.getContext(), 'www'),
1391+
[
1392+
'build/runtime.js',
1393+
'build/main.js'
1394+
],
1395+
(browser) => {
1396+
// Standard CSS
1397+
browser.assert.hasClass('#app', 'red');
1398+
1399+
// CSS modules
1400+
browser.assert.hasClass('#app', 'italic_foo');
1401+
1402+
done();
1403+
}
1404+
);
1405+
});
1406+
});
1407+
13531408
it('Vue.js error when using non-activated loaders', (done) => {
13541409
const config = createWebpackConfig('www/build', 'dev');
13551410
config.setPublicPath('/build');

test/loaders/css.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ describe('loaders/css', () => {
3131
expect(actualLoaders).to.have.lengthOf(1);
3232
expect(actualLoaders[0].options.sourceMap).to.be.true;
3333
expect(actualLoaders[0].options.minimize).to.be.false;
34+
expect(actualLoaders[0].options.modules).to.be.false;
3435
});
3536

3637
it('getLoaders() for production', () => {
@@ -42,6 +43,7 @@ describe('loaders/css', () => {
4243
expect(actualLoaders).to.have.lengthOf(1);
4344
expect(actualLoaders[0].options.sourceMap).to.be.false;
4445
expect(actualLoaders[0].options.minimize).to.be.true;
46+
expect(actualLoaders[0].options.modules).to.be.false;
4547
});
4648

4749
it('getLoaders() with options callback', () => {
@@ -56,6 +58,22 @@ describe('loaders/css', () => {
5658
expect(actualLoaders).to.have.lengthOf(1);
5759
expect(actualLoaders[0].options.minimize).to.be.true;
5860
expect(actualLoaders[0].options.url).to.be.false;
61+
expect(actualLoaders[0].options.modules).to.be.false;
62+
});
63+
64+
it('getLoaders() with CSS modules enabled', () => {
65+
const config = createConfig();
66+
67+
config.configureCssLoader(function(options) {
68+
options.minimize = true;
69+
options.url = false;
70+
});
71+
72+
const actualLoaders = cssLoader.getLoaders(config, true);
73+
expect(actualLoaders).to.have.lengthOf(1);
74+
expect(actualLoaders[0].options.minimize).to.be.true;
75+
expect(actualLoaders[0].options.url).to.be.false;
76+
expect(actualLoaders[0].options.modules).to.be.true;
5977
});
6078

6179
describe('getLoaders() with PostCSS', () => {

0 commit comments

Comments
 (0)