laravel-mix: extractStyles not working as intended with css in vue components

  • Laravel Mix Version: 6
  • Node Version (node -v): 14.15.0
  • NPM Version (npm -v): 6.14.8
  • OS: Ubuntu 18

Description:

Laravel mix is unable to extract css from vue component and throwing error.

webpack.mix.js

let mix = require('laravel-mix');
const del = require('del');
const tailwindcss = require('tailwindcss');

del('./public');
mix
    .setPublicPath('public')
    .webpackConfig({
        optimization: {
            providedExports: false,
            sideEffects: false,
            usedExports: false
        }
    })
    .js('resources/src/js/app.js', 'public/js/')
    .sass('resources/src/sass/vendor.scss', 'css')
    .sass('resources/src/sass/landing.scss', 'css')
    .sass('resources/src/sass/app.scss', 'css')
    .copyDirectory('resources/src/img', './public/img')
    .copyDirectory('resources/src/font', './public/font')
    .copy('resources/src/favicon.ico', './public/favicon.ico')
    .vue({
        extractStyles: true
    })
    .options({
        processCssUrls: false,
        postCss: [tailwindcss('./tailwind.config.js')]
    }).extract();

Vue component:

<template>
  <div class="relative w-12 my-1 cursor-pointer" @click="toggle">
    <div v-if="isEnabled" class="w-12 h-8 rounded-full" :class="color">
      <div
        class="absolute top-0 flex items-center justify-center w-6 h-6 mt-1 -ml-6 bg-white border-gray-300 rounded-full transition-all transform ease-linear duration-100 shadow-toggle left-96"
      ></div>
    </div>

    <div v-if="!isEnabled" class="w-12 h-8 bg-gray-300 rounded-full">
      <div
        class="absolute top-0 flex items-center justify-center w-6 h-6 mt-1 bg-white border-gray-300 rounded-full transition-all transform ease-linear duration-100 shadow-toggle left-4"
      ></div>
    </div>
  </div>
</template>

<script>
export default {
  name: "ToggleSwitch",
  model: {
    prop: "isEnabled",
    event: "toggle",
  },
  props: {
    isEnabled: Boolean,
    color: {
      type: String,
      required: false,
      default: "bg-green-600",
    },
  },
  methods: {
    toggle() {
      this.$emit("toggle", !this.isEnabled);
    },
  },
};
</script>

<style scoped>
.shadow-toggle {
  box-shadow: 1px 0px 2px 1px rgba(0, 0, 0, 0.1),
    -1px 1px 4px 1px rgba(0, 0, 0, 0.06);
}

.left-4 {
  left: 4%;
}

.left-96 {
  left: 96%;
}
</style>

When running the mix command for watch and visiting the page gave following error: image

What is the possible solution to above?

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 2
  • Comments: 16

Most upvoted comments

In v6, this builds a vendor.css file in public/styles but also a main.css file in public/scripts (?? 😄):

mix.js('resources/scripts/main.js', 'public/scripts/main.js').vue({extractStyles: 'public/styles/vendor.css'})

I’ve a similar problem in laravel mix 4.2.0. In my case, if I use: extractVueStyles: ‘[name].css’,

it works perfectly but it created on the wrong folder, so at public/js/app.css and public/js/vendor.css

My first code attemplt was: extractVueStyles: ‘public/js/vue.css’,

But that would create a vue.css with only the content of vendor.css, the app.css seems to be overwritten by the vendor one

So for now, I’m using extractVueStyles: ‘[name].css’, since it works with the only inconvenient of having the css on a js folder.

best regards

update i guess my use case is a little different, im not trying to do split entry points like others in this thread.

I, too, am curious how i can configure laravel-mix 6.0.43 to extract styles to per-vue-component sheets. in our large webapp, i don’t want to load all of them in a single vendor.css, but rather a [name].css per module (both sync and async modules)

ideally i could use something like:

extractStyles: '[name].css?id=[chunkhash]'

seems like no matter what string value i set vue.extractStyles to, it always outputs vendor.css (tho looking at the sourcecode, it seems like the default should be [name].css? so maybe something else in my config is messing things up) extractStyles: 'extracted-styles.css', => public/dist/vendor.css

https://github.com/laravel-mix/laravel-mix/blob/6ee2985e3597eb097df8f3e40032b1768f567e51/src/components/CssWebpackConfig.js#L178-L185

also, i’m unclear if useVueStyleLoader should be used in this case or not.

as i continue to investigate: i noticed that laravel-mix is requiring v1 of MiniCSSExtractPlugin while, the latest release is: 2.6.0 I wonder if there’s any changes in the newer versions that would address any of the oddities being experienced?

also, FWIW, i’m still using Vue2 .vue({version:2})

also, I’m attempting to get styles extracted from vue SingleFileComponents, do i need to change my <style>@import '....scss'</style> to <script>import(/* chunkname */....scss)</script> imports for this to work?

do need to call mix.extract() explicitly for this to work? or is .vue({extractStyles:true}) enough?

➜  node -v
v14.16.1
➜  yarn -v
3.2.0
➜  webpack -v
webpack: 5.72.0
webpack-cli: 4.9.2
webpack-dev-server 4.8.1
➜  app git:(feature/inertia) ✗ yarn why mini-css-extract-plugin
├─ @vue/cli-service@npm:5.0.4
│  └─ mini-css-extract-plugin@npm:2.6.0 (via npm:^2.5.3)
│
├─ @vue/cli-service@npm:5.0.4 [0460f]
│  └─ mini-css-extract-plugin@npm:2.6.0 [3fb00] (via npm:^2.5.3 [3fb00])
│
├─ app@workspace:.
│  └─ mini-css-extract-plugin@npm:2.6.0 [0460f] (via npm:^2.6.0 [0460f])
│
├─ laravel-mix@npm:6.0.43
│  └─ mini-css-extract-plugin@npm:1.6.2 (via npm:^1.6.2)
│
└─ laravel-mix@npm:6.0.43 [0460f]
   └─ mini-css-extract-plugin@npm:1.6.2 [09b24] (via npm:^1.6.2 [09b24])

i tried overriding the config using:

mix.override((webpackConfig) => {

        webpackConfig.plugins.map((p,k) => {
            if(p.constructor.name === 'MiniCssExtractPlugin'){
                webpackConfig.plugins[k].options.filename = "[name].[contenthash].css";
                webpackConfig.plugins[k].options.chunkFilename = "[id].[contenthash].css";
            }
        });
Plugins List:
[
  MixDefinitionsPlugin {
    envPath: '.env',
    additionalEnv: { NODE_ENV: 'development' }
  },
  CustomTasksPlugin {
    mix: Mix {
      config: [Object],
      chunks: [Chunks],
      components: [Components],
      dispatcher: [Dispatcher],
      manifest: [Manifest],
      paths: [Paths],
      registrar: [ComponentRegistrar],
      webpackConfig: [WebpackConfig],
      hot: [HotReloading],
      resolver: [Resolver],
      dependencies: [Dependencies],
      logger: [Function],
      tasks: [],
      booted: true,
      bundlingJavaScript: true,
      initialized: true,
      globalStyles: [Object],
      extractingStyles: true,
      _api: [Object]
    }
  },
  BuildCallbackPlugin { callback: [Function (anonymous)] },
  BuildOutputPlugin {
    options: { clearConsole: false, showRelated: true },
    patched: false
  },
  WebpackBarPlugin {
    profile: false,
    handler: [Function (anonymous)],
    modulesCount: 5000,
    dependenciesCount: 10000,
    showEntries: true,
    showModules: true,
    showDependencies: true,
    showActiveModules: true,
    percentBy: undefined,
    options: { name: 'Mix', color: 'green', reporters: [Array], reporter: null },
    reporters: [ SimpleReporter {} ]
  },
  MiniCssExtractPlugin {
    _sortedModulesCache: WeakMap { <items unknown> },
    options: {
      filename: '[name].[contenthash].css',
      ignoreOrder: false,
      experimentalUseImportModule: false,
      chunkFilename: '[id].[contenthash].css'
    },
    runtimeOptions: { insert: undefined, linkType: 'text/css', attributes: undefined }
  },
  VueLoaderPlugin {},
  AppendVueStylesPlugin {},
  WebpackNotifierPlugin {
    options: {
      appID: 'Laravel Mix',
      title: 'Laravel Mix',
      alwaysNotify: true,
      timeout: false,
      hint: 'int:transient:1',
      contentImage: '/node_modules/laravel-mix/icons/laravel.png'
    },
    lastBuildSucceeded: false,
    isFirstBuild: true
  },
  ProvidePlugin {
    definitions: { Buffer: [Array], process: 'process/browser.js' }
  },
  {},
  LimitChunkCountPlugin { options: { maxChunks: 1000 } },
  DefinePlugin {
    definitions: {
    }
  },
  ProvidePlugin {
    definitions: {
    }
  }
]

If i had two different “main” scripts in my webpack.mix.js config:

.js('foo.js')
.js('bar.js')

and if I had set option: extractVueStyles: '[name].css'

The laravel mix bundled files like this:

- foo.js
- foo.css <- contains styles only for the foo.js
- bar.js
- bar.css <-- contains styles only for the bar.js

Now if I use the newer Laravel mix version with .vue({ extractStyles: '[name].css'})

It generates files like this:

- foo.js
- bar.js
- [name].css  <- contains both foo.js styles and bar.js styles and the css extracting does not work like it used to :(

If i try to use .vue({ extractStyles: true})

it generates files like this:

- foo.js
- bar.js
- vue-styles.css <- contains both foo.js styles and bar.js styles :(

Is there still any workarounds for this?

So I’ve confirmed that async components + style extraction doesn’t work properly. If I tell webpack to only extract styles for non-async components things work fine but that’s definitely not ideal.