Laravel-mix: CSS file compiled from SASS empty while using nested dynamic component imports

Created on 6 Jun 2019  Â·  4Comments  Â·  Source: JeffreyWay/laravel-mix

  • Laravel Mix Version: 4.0.16
  • Node Version v12.3.1
  • NPM Version 6.4.1
  • OS: MacOSX

Description:

I am attempting to implement the newly added feature go laravel-mix with the addition of dynamic vue component loading. While this works on a small scale, I have noticed that if you begin dynamically loading components within components (ie not at the app.js level) this will cause the compiled SASS stylesheet to be empty. The stylesheet will still be created, but the contents will be empty.

Steps To Reproduce:

Begin to construction of the main javascript file (app.js) like so with the necessary VueJS components:

app.js

import Vue from "vue";

Vue.component('admin-open-projects', () => import(`./components/AdminOpenProjects`));
Vue.component('admin-closed-projects', () => import('./components/AdminClosedProjects'));
...

Within the AdminClosedProjects component, I continued to dynamically load components like so.

components/AdminClosedProjects.vue

<template>
    <div class="inner-wrapper">
        <slot name="header"></slot>
    </div>
</template>

<script>
    const ClosedResult = () => require('./ClosedResult');
    const DeleteProjectModal = () => require('./DeleteProjectModal');
    const AdminCommentModal = () => require('./AdminCommentModal');

    export default {
        name: "AdminClosedProjects",
        components: {
            AdminCommentModal,
            DeleteProjectModal,
            ClosedResult,
        },
        props: { },
        data() {
            return {
                loading: false,
            }
        },
    }
</script>

Upon compilation no errors are reported, and the terminal output looks like this:

 /build/css/main.css   0 bytes  /build/js/app, /build/js/metrics

Most helpful comment

The unavoidable nature of the problem has to do with the possible generation of mostly empty — but required — JS files which are generated alongside CSS files under certain conditions. When you have CSS extractions that generate these files your page must load them for your JS to load at all. And these CSS files can be generated by async chunks making things rather complicated.

webpack 5 (still in beta at the time of this writing) supports filtering for chunk splitting making this no longer an issue. The migration for Mix to webpack 5 Is currently underway.

See https://github.com/webpack-contrib/mini-css-extract-plugin/issues/85 for more information.

All 4 comments

There are unavoidable issues with dynamic imports until webpack is out.

@JeffreyWay How are those issues "unavoidable" exactly ? :thinking: Because from what I can read in this comment, the problem comes from extract-text-webpack-plugin which is now deprecated and it is still used in laravel-mix's latest release (v5.0.4) ( apparently ).

So why no switching to mini-css-extract-plugin yet ?

Hackish workaround time :sparkles:

I managed to extract my CSS successfully (while having dynamic imports :tada: ) with this :

webpack.mix.js

const mix = require('laravel-mix');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

mix.webpackConfig({
  plugins: [
    new MiniCssExtractPlugin(),
  ],
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
      {
        test: /\.scss$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
      }
    ],
  },
});

// try to remove traces of "extract-text-webpack-plugin"
// inside the webpack config generated by laravel-mix
mix.override((config) => {

  // STEP 1 : remove related plugins
  const cleanedPlugins = [];

  for(let i in config.plugins) {
    const plugin = config.plugins[i];
    if(plugin.constructor.name !== 'ExtractTextPlugin') {
      cleanedPlugins.push(plugin);
    }
  }
  config.plugins = cleanedPlugins;


  // STEP 2 : remove style related rules
  const cleanedRules = [];

  for(let i in config.module.rules) {
    const rule = config.module.rules[i];

    if(!rule.loaders || !rule.loaders.includes('style-loader')) {
      cleanedRules.push(rule);
    }
  }
  config.module.rules = cleanedRules;

});

I had to avoid using the mix.sass() method because it won't work with this workaround. Instead I created a new Javascript file in which I imported my SCSS file like that : import 'entry.scss', and then in my webpack.mix.js I could load it via mix.js() and the styles would be processed properly.

Oh and don't set the extractVueStyles laravel-mix option (it's pointless).

NOTE : This workaround probably does not handle all uses cases (works for my case tho), but it tells me that it should be possible to implement mini-css-extract-plugin in laravel-mix ! :smiley:

The unavoidable nature of the problem has to do with the possible generation of mostly empty — but required — JS files which are generated alongside CSS files under certain conditions. When you have CSS extractions that generate these files your page must load them for your JS to load at all. And these CSS files can be generated by async chunks making things rather complicated.

webpack 5 (still in beta at the time of this writing) supports filtering for chunk splitting making this no longer an issue. The migration for Mix to webpack 5 Is currently underway.

See https://github.com/webpack-contrib/mini-css-extract-plugin/issues/85 for more information.

@wmcmurray 's answer works I just tested it on my own build.

This solution will move your .css file into public/js folder so you have to reference it right in your blade file.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Bomavi picture Bomavi  Â·  3Comments

rderimay picture rderimay  Â·  3Comments

mstralka picture mstralka  Â·  3Comments

hasnatbabur picture hasnatbabur  Â·  3Comments

terion-name picture terion-name  Â·  3Comments