diff --git a/fixtures/css/postcss_extension.postcss b/fixtures/css/postcss_extension.postcss new file mode 100644 index 00000000..4dce6d71 --- /dev/null +++ b/fixtures/css/postcss_extension.postcss @@ -0,0 +1 @@ +@import "autoprefixer_test.css"; \ No newline at end of file diff --git a/fixtures/vuejs-css-modules/App.vue b/fixtures/vuejs-css-modules/App.vue index 9dbff2fb..3e09beb0 100644 --- a/fixtures/vuejs-css-modules/App.vue +++ b/fixtures/vuejs-css-modules/App.vue @@ -1,5 +1,5 @@ + + + + + \ No newline at end of file diff --git a/lib/config-generator.js b/lib/config-generator.js index df8176a2..51461509 100644 --- a/lib/config-generator.js +++ b/lib/config-generator.js @@ -236,6 +236,15 @@ class ConfigGenerator { return applyOptionsCallback(this.webpackConfig.loaderConfigurationCallbacks[name], defaultRules); }; + // When the PostCSS loader is enabled, allow to use + // files with the `.postcss` extension. It also + // makes it possible to use `lang="postcss"` in Vue + // files. + const cssExtensions = ['css']; + if (this.webpackConfig.usePostCssLoader) { + cssExtensions.push('postcss'); + } + let rules = [ applyRuleConfigurationCallback('javascript', { // match .js and .jsx @@ -246,9 +255,9 @@ class ConfigGenerator { applyRuleConfigurationCallback('css', { resolve: { mainFields: ['style', 'main'], - extensions: ['.css'], + extensions: cssExtensions.map(ext => `.${ext}`), }, - test: /\.css$/, + test: new RegExp(`\\.(${cssExtensions.join('|')})$`), oneOf: [ { resourceQuery: /module/, diff --git a/test/config-generator.js b/test/config-generator.js index b9aa8bf3..6f3ce7f0 100644 --- a/test/config-generator.js +++ b/test/config-generator.js @@ -1052,7 +1052,7 @@ describe('The config-generator function', () => { }); const webpackConfig = configGenerator(config); - const rule = findRule(/\.css$/, webpackConfig.module.rules); + const rule = findRule(/\.(css)$/, webpackConfig.module.rules); expect(rule.camelCase).to.be.true; }); @@ -1210,4 +1210,42 @@ describe('The config-generator function', () => { expect(rule.use[0].options.fooBar).to.be.equal('fooBar'); }); }); + + describe('enablePostCssLoader() makes the CSS rule process .postcss file', () => { + it('without enablePostCssLoader()', () => { + const config = createConfig(); + config.outputPath = '/tmp/output/public-path'; + config.publicPath = '/public-path'; + config.enableSingleRuntimeChunk(); + config.addEntry('main', './main'); + // do not call disableImagesLoader + + const actualConfig = configGenerator(config); + + expect(function() { + findRule(/\.(css)$/, actualConfig.module.rules); + }).not.to.throw(); + expect(function() { + findRule(/\.(css|postcss)$/, actualConfig.module.rules); + }).to.throw(); + }); + + it('with enablePostCssLoader()', () => { + const config = createConfig(); + config.outputPath = '/tmp/output/public-path'; + config.publicPath = '/public-path'; + config.addEntry('main', './main'); + config.enableSingleRuntimeChunk(); + config.enablePostCssLoader(); + + const actualConfig = configGenerator(config); + + expect(function() { + findRule(/\.(css)$/, actualConfig.module.rules); + }).to.throw(); + expect(function() { + findRule(/\.(css|postcss)$/, actualConfig.module.rules); + }).to.not.throw(); + }); + }); }); diff --git a/test/functional.js b/test/functional.js index b213da46..36e0edaa 100644 --- a/test/functional.js +++ b/test/functional.js @@ -948,6 +948,7 @@ module.exports = { // load a file that @import's another file, so that we can // test that @import resources are parsed through postcss config.addStyleEntry('styles', ['./css/imports_autoprefixer.css']); + config.addStyleEntry('postcss', './css/postcss_extension.postcss'); config.enablePostCssLoader(); testSetup.runWebpack(config, (webpackAssert) => { @@ -957,6 +958,14 @@ module.exports = { '-webkit-full-screen' ); + // check that the .postcss file was also processed + // correctly (it also @import the autoprefixer_test.css + // file) + webpackAssert.assertOutputFileContains( + 'postcss.css', + '-webkit-full-screen' + ); + done(); }); }); @@ -1421,7 +1430,7 @@ module.exports = { }); }); - it('Vue.js supports CSS/Sass/Less/Stylus modules', (done) => { + it('Vue.js supports CSS/Sass/Less/Stylus/PostCSS modules', (done) => { const appDir = testSetup.createTestAppDir(); const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); config.enableSingleRuntimeChunk(); @@ -1437,6 +1446,18 @@ module.exports = { options.localIdentName = '[local]_foo'; }); + // Enable the PostCSS loader so we can use `lang="postcss"` + config.enablePostCssLoader(); + fs.writeFileSync( + path.join(appDir, 'postcss.config.js'), + ` +module.exports = { + plugins: [ + require('autoprefixer')() + ] +} ` + ); + testSetup.runWebpack(config, (webpackAssert) => { expect(config.outputPath).to.be.a.directory().with.deep.files([ 'main.js', @@ -1457,11 +1478,13 @@ module.exports = { expectClassDeclaration('large'); // Standard SCSS expectClassDeclaration('justified'); // Standard Less expectClassDeclaration('lowercase'); // Standard Stylus + expectClassDeclaration('block'); // Standard Postcss expectClassDeclaration('italic_foo'); // CSS Module expectClassDeclaration('bold_foo'); // SCSS Module expectClassDeclaration('underline_foo'); // Less Module expectClassDeclaration('rtl_foo'); // Stylus Module + expectClassDeclaration('hidden_foo'); // Stylus Module testSetup.requestTestPage( path.join(config.getContext(), 'www'), @@ -1474,11 +1497,13 @@ module.exports = { browser.assert.hasClass('#app', 'large'); // Standard SCSS browser.assert.hasClass('#app', 'justified'); // Standard Less browser.assert.hasClass('#app', 'lowercase'); // Standard Stylus + browser.assert.hasClass('#app', 'block'); // Standard Stylus browser.assert.hasClass('#app', 'italic_foo'); // CSS module browser.assert.hasClass('#app', 'bold_foo'); // SCSS module browser.assert.hasClass('#app', 'underline_foo'); // Less module browser.assert.hasClass('#app', 'rtl_foo'); // Stylus module + browser.assert.hasClass('#app', 'hidden_foo'); // Stylus module done(); }