From 51011cb2d10d10f7f41c714cbaca0f1e7d2f3457 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 30 Dec 2020 06:30:36 -0500 Subject: [PATCH] Switch to vue-style-loader when using mix.vue() --- src/builder/WebpackConfig.js | 1 + src/components/CssWebpackConfig.js | 9 ++++- src/components/Preprocessor.js | 2 +- test/features/vue.js | 32 +++++++++++++++ test/features/vue3.js | 32 +++++++++++++++ test/helpers/assertions.js | 62 ++++++++++++++++++++++++++++++ test/helpers/webpack.js | 3 ++ 7 files changed, 138 insertions(+), 3 deletions(-) diff --git a/src/builder/WebpackConfig.js b/src/builder/WebpackConfig.js index a4a94c2cb..65975198b 100644 --- a/src/builder/WebpackConfig.js +++ b/src/builder/WebpackConfig.js @@ -19,6 +19,7 @@ class WebpackConfig { * Build the Webpack configuration object. */ async build() { + /** @type {import("webpack").Configuration} */ this.webpackConfig = webpackDefaultConfig(); await this.buildEntry(); diff --git a/src/components/CssWebpackConfig.js b/src/components/CssWebpackConfig.js index b1aebda82..8e10cbc44 100644 --- a/src/components/CssWebpackConfig.js +++ b/src/components/CssWebpackConfig.js @@ -164,8 +164,9 @@ class CssWebpackConfig extends AutomaticComponent { * * @param {object} [options] * @param {"auto" | "inline" | "extract"} options.method The method to use when handling CSS. + * @param {"default" | "per-file"} options.location Where these loaders are applied. The `default` set or on a per-file basis (used by preprocessors). */ - static afterLoaders({ method = 'auto' } = {}) { + static afterLoaders({ method = 'auto', location = 'default' } = {}) { const loaders = []; if (method === 'auto') { @@ -178,7 +179,11 @@ class CssWebpackConfig extends AutomaticComponent { } if (method === 'inline') { - loaders.push({ loader: 'style-loader' }); + if (Mix.components.get('vue') && location === 'default') { + loaders.push({ loader: 'vue-style-loader' }); + } else { + loaders.push({ loader: 'style-loader' }); + } } else if (method === 'extract') { loaders.push({ loader: MiniCssExtractPlugin.loader, diff --git a/src/components/Preprocessor.js b/src/components/Preprocessor.js index d3c2fea71..2f71a3462 100644 --- a/src/components/Preprocessor.js +++ b/src/components/Preprocessor.js @@ -53,7 +53,7 @@ class Preprocessor { let processUrls = this.shouldProcessUrls(preprocessor); let loaders = [ - ...CssWebpackConfig.afterLoaders({ method: 'extract' }), + ...CssWebpackConfig.afterLoaders({ method: 'extract', location: 'per-file' }), { loader: 'css-loader', options: { diff --git a/test/features/vue.js b/test/features/vue.js index 121b401ae..60b440b19 100644 --- a/test/features/vue.js +++ b/test/features/vue.js @@ -38,6 +38,38 @@ test('it knows the Vue 2 compiler name', t => { t.true(dependencies.includes('vue-template-compiler')); }); +test('it switches to vue-style-loader when not extracting styles', async t => { + mix.vue({ version: 2, extractStyles: false }); + + const config = await webpack.buildConfig(); + + assert.hasWebpackLoader(t, config, 'vue-style-loader'); + assert.doesNotHaveWebpackLoader(t, config, 'style-loader'); + assert.doesNotHaveWebpackLoader(t, config, loader => + loader.includes('mini-css-extract-plugin') + ); +}); + +test('it does not switch to vue-style-loader when extracting styles', async t => { + mix.vue({ version: 2, extractStyles: true }); + + const config = await webpack.buildConfig(); + + assert.doesNotHaveWebpackLoader(t, config, 'vue-style-loader'); + assert.hasWebpackLoader(t, config, loader => + loader.includes('mini-css-extract-plugin') + ); +}); + +test('it does not use vue-style-loader when not using .vue', async t => { + const config = await webpack.buildConfig(); + + assert.doesNotHaveWebpackLoader(t, config, 'vue-style-loader'); + assert.hasWebpackLoader(t, config, loader => + loader.includes('mini-css-extract-plugin') + ); +}); + test('it appends vue styles to your sass compiled file', async t => { mix.vue({ version: 2, extractStyles: true }); mix.js(`test/fixtures/app/src/vue/app-with-vue-and-scss.js`, 'js/app.js').sass( diff --git a/test/features/vue3.js b/test/features/vue3.js index 3145212ac..a8cc851c5 100644 --- a/test/features/vue3.js +++ b/test/features/vue3.js @@ -34,6 +34,38 @@ test('it knows the Vue 3 compiler name', t => { t.true(dependencies.includes('@vue/compiler-sfc')); }); +test('it switches to vue-style-loader when not extracting styles', async t => { + mix.vue({ version: 3, extractStyles: false }); + + const config = await webpack.buildConfig(); + + assert.hasWebpackLoader(t, config, 'vue-style-loader'); + assert.doesNotHaveWebpackLoader(t, config, 'style-loader'); + assert.doesNotHaveWebpackLoader(t, config, loader => + loader.includes('mini-css-extract-plugin') + ); +}); + +test('it does not switch to vue-style-loader when extracting styles', async t => { + mix.vue({ version: 3, extractStyles: true }); + + const config = await webpack.buildConfig(); + + assert.doesNotHaveWebpackLoader(t, config, 'vue-style-loader'); + assert.hasWebpackLoader(t, config, loader => + loader.includes('mini-css-extract-plugin') + ); +}); + +test('it does not use vue-style-loader when not using .vue', async t => { + const config = await webpack.buildConfig(); + + assert.doesNotHaveWebpackLoader(t, config, 'vue-style-loader'); + assert.hasWebpackLoader(t, config, loader => + loader.includes('mini-css-extract-plugin') + ); +}); + test('it appends vue styles to your sass compiled file', async t => { mix.vue({ version: 3, extractStyles: true }); diff --git a/test/helpers/assertions.js b/test/helpers/assertions.js index 50a227c2c..1118cf58d 100644 --- a/test/helpers/assertions.js +++ b/test/helpers/assertions.js @@ -1,6 +1,68 @@ import File from '../../src/File'; +/** + * Check that a matching webpack rule can be found + * + * @param {import("ava").Assertions} t + * @param {import("webpack").Configuration} config + * @param {(rule: import("webpack").RuleSetRule) => boolean} test + */ +function hasWebpackRule(config, test) { + /** @param {import("webpack").RuleSetRule} rule */ + const checkRule = rule => + test(rule) || + (rule.oneOf || []).find(checkRule) || + (rule.rules || []).find(checkRule); + + return !!config.module.rules.find(checkRule); +} + +/** + * Check that a matching rule with the given loader can be found + * + * @param {import("webpack").Configuration} config + * @param {string|(loader: string) => boolean} loader + */ +function hasWebpackLoader(config, loader) { + const checkLoader = typeof loader === 'string' ? str => str === loader : loader; + + return hasWebpackRule( + config, + rule => + (rule.loader && checkLoader(rule.loader)) || + (rule.use || []).find(use => checkLoader(use.loader)) + ); +} + export default { + /** + * Assert that a matching webpack rule can be found + * + * @param {import("ava").Assertions} t + * @param {import("webpack").Configuration} config + * @param {(rule: import("webpack").RuleSetRule) => boolean} test + */ + hasWebpackRule: (t, config, test) => t.true(hasWebpackRule(config, test)), + + /** + * Assert that a rule with the given loader can be found + * + * @param {import("ava").Assertions} t + * @param {import("webpack").Configuration} config + * @param {string|(loader: string) => boolean} loader + */ + hasWebpackLoader: (t, config, loader) => t.true(hasWebpackLoader(config, loader)), + + /** + * Assert that a rule with the given loader cannot be found + * + * @param {import("ava").Assertions} t + * @param {import("webpack").Configuration} config + * @param {string|(loader: string) => boolean} loader + */ + doesNotHaveWebpackLoader: (t, config, loader) => + t.false(hasWebpackLoader(config, loader)), + manifestEquals: (expected, t) => { let manifest = JSON.parse( File.find(`test/fixtures/app/dist/mix-manifest.json`).read() diff --git a/test/helpers/webpack.js b/test/helpers/webpack.js index c61bf184e..0e2fdd38e 100644 --- a/test/helpers/webpack.js +++ b/test/helpers/webpack.js @@ -1,5 +1,8 @@ import webpack from 'webpack'; +/** + * @returns {Promise} + */ export async function buildConfig() { await Mix.dispatch('init');