Skip to content

Commit

Permalink
bug #508 Support CSS modules in Vue.js projects by default (Lyrkan)
Browse files Browse the repository at this point in the history
This PR was merged into the master branch.

Discussion
----------

Support CSS modules in Vue.js projects by default

With the current version of Encore you are able to either:

* only use standard CSS (default behavior)
* only use CSS modules (by calling `Encore.configureCssLoader()`)

This PR should help detecting when `<style module>` is used and enabling css modules accordingly (fixes #460).

**Reference:** https://vue-loader.vuejs.org/guide/css-modules.html#opt-in-usage

Commits
-------

356e538 Support CSS modules in Vue.js projects by default
  • Loading branch information
Ryan Weaver authored and Ryan Weaver committed Feb 2, 2019
2 parents 244141c + 356e538 commit 6867443
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 3 deletions.
15 changes: 15 additions & 0 deletions fixtures/vuejs-css-modules/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<div id="app" class="red" :class="$style.italic"></div>
</template>

<style>
.red {
color: red;
}
</style>

<style module>
.italic {
font-style: italic;
}
</style>
8 changes: 8 additions & 0 deletions fixtures/vuejs-css-modules/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Vue from 'vue'
import App from './App'

new Vue({
el: '#app',
template: '<App/>',
components: { App }
})
16 changes: 15 additions & 1 deletion lib/config-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,21 @@ class ConfigGenerator {
},
{
test: /\.css$/,
use: cssExtractLoaderUtil.prependLoaders(this.webpackConfig, cssLoaderUtil.getLoaders(this.webpackConfig))
oneOf: [
{
resourceQuery: /module/,
use: cssExtractLoaderUtil.prependLoaders(
this.webpackConfig,
cssLoaderUtil.getLoaders(this.webpackConfig, true)
)
},
{
use: cssExtractLoaderUtil.prependLoaders(
this.webpackConfig,
cssLoaderUtil.getLoaders(this.webpackConfig)
)
}
]
}
];

Expand Down
7 changes: 5 additions & 2 deletions lib/loaders/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ const applyOptionsCallback = require('../utils/apply-options-callback');
module.exports = {
/**
* @param {WebpackConfig} webpackConfig
* @param {boolean} useCssModules
* @return {Array} of loaders to use for CSS files
*/
getLoaders(webpackConfig) {
getLoaders(webpackConfig, useCssModules = false) {
const usePostCssLoader = webpackConfig.usePostCssLoader;

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

const cssLoaders = [
Expand Down
55 changes: 55 additions & 0 deletions test/functional.js
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,61 @@ module.exports = {
});
});

it('Vue.js supports CSS modules', (done) => {
const appDir = testSetup.createTestAppDir();
const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev');
config.enableSingleRuntimeChunk();
config.setPublicPath('/build');
config.addEntry('main', './vuejs-css-modules/main');
config.enableVueLoader();
config.enableSassLoader();
config.enableLessLoader();
config.configureCssLoader(options => {
// Remove hashes from local ident names
// since they are not always the same.
options.localIdentName = '[local]_foo';
});

testSetup.runWebpack(config, (webpackAssert) => {
expect(config.outputPath).to.be.a.directory().with.deep.files([
'main.js',
'main.css',
'manifest.json',
'entrypoints.json',
'runtime.js',
]);

// Standard CSS
webpackAssert.assertOutputFileContains(
'main.css',
'.red {'
);

// CSS modules
webpackAssert.assertOutputFileContains(
'main.css',
'.italic_foo {'
);

testSetup.requestTestPage(
path.join(config.getContext(), 'www'),
[
'build/runtime.js',
'build/main.js'
],
(browser) => {
// Standard CSS
browser.assert.hasClass('#app', 'red');

// CSS modules
browser.assert.hasClass('#app', 'italic_foo');

done();
}
);
});
});

it('Vue.js error when using non-activated loaders', (done) => {
const config = createWebpackConfig('www/build', 'dev');
config.setPublicPath('/build');
Expand Down
18 changes: 18 additions & 0 deletions test/loaders/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ describe('loaders/css', () => {
expect(actualLoaders).to.have.lengthOf(1);
expect(actualLoaders[0].options.sourceMap).to.be.true;
expect(actualLoaders[0].options.minimize).to.be.false;
expect(actualLoaders[0].options.modules).to.be.false;
});

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

it('getLoaders() with options callback', () => {
Expand All @@ -56,6 +58,22 @@ describe('loaders/css', () => {
expect(actualLoaders).to.have.lengthOf(1);
expect(actualLoaders[0].options.minimize).to.be.true;
expect(actualLoaders[0].options.url).to.be.false;
expect(actualLoaders[0].options.modules).to.be.false;
});

it('getLoaders() with CSS modules enabled', () => {
const config = createConfig();

config.configureCssLoader(function(options) {
options.minimize = true;
options.url = false;
});

const actualLoaders = cssLoader.getLoaders(config, true);
expect(actualLoaders).to.have.lengthOf(1);
expect(actualLoaders[0].options.minimize).to.be.true;
expect(actualLoaders[0].options.url).to.be.false;
expect(actualLoaders[0].options.modules).to.be.true;
});

describe('getLoaders() with PostCSS', () => {
Expand Down

0 comments on commit 6867443

Please sign in to comment.