Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extractStyles not working as intended with css in vue components #2589

Open
sujit-baniya opened this issue Oct 31, 2020 · 16 comments
Open

extractStyles not working as intended with css in vue components #2589

sujit-baniya opened this issue Oct 31, 2020 · 16 comments
Assignees
Labels

Comments

@sujit-baniya
Copy link

  • 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?

@thecrypticace
Copy link
Collaborator

It appears that this has to do w/ style extraction + use of async components. I'll take a look today.

@sujit-baniya
Copy link
Author

@thecrypticace Did you get chance to look into it?

@Daniel4Digital
Copy link

Daniel4Digital commented Nov 5, 2020

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

@alexmccabe
Copy link

alexmccabe commented Dec 9, 2020

@Daniel4Digital This pointed me in the direction as to why something isn't working. When using extractVueStyles: 'path/to/filename.css' the vendor css is just straight up ignored. When setting to extractVueStyles: '[name.css]' it outputs the vendor styles, but in the JS directory. Which is _fine, I guess 🤷 _. Frustrating though. I'd like for a vendor css file to be generated regardless.

@roitto
Copy link

roitto commented Dec 23, 2020

Any fixes to this?

If i apply

.vue({
    extractStyles: 'app.css',
})

in laravel mix vue options, the webpack tries to load chunk called "app.js" when trying to load dynamically imported components.

@thecrypticace thecrypticace self-assigned this Dec 26, 2020
@thecrypticace
Copy link
Collaborator

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.

@Daniel4Digital
Copy link

Daniel4Digital commented Dec 28, 2020

Today I updated to laravel mix 6 (Saw today too that this issue regards version 6) and try to use vue({
extractStyles: 'css/app.css'
})

And the app.css is now on the correct folder, but the vendor.css still goes to js folder no matter what.

Even if I use extractStyles: true, the vendor.css still goes to js folder. And using '[name].css' no longer works, since it seems not to be needed anymore.

Dunno if related to this issue no more. But well.

@sebastiaanluca
Copy link

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'})

@roitto
Copy link

roitto commented Jan 19, 2021

Also when using

.vue({
    extractStyles: '[name].css',
})

In laravel mix vue options, the build output is a plain "[name].css" file. In laravel-mix 5, the css file was created using the input scripts name.

For example if i have .js('resources/application.js', 'app.js').js('resources/admin.js', 'admin.js'); in webpack.mix.js. (Two different vue bootstrapping scripts.) In laravel-mix 5 the extractVueStyles '[name].css' would create two css files; app.css and admin.css. If i use laravel-mix 6, the output file is just [name].css which includes styles from both scripts.

@stale
Copy link

stale bot commented Jun 11, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Jun 11, 2021
@stale stale bot closed this as completed Jun 16, 2021
@vilbergs
Copy link

Attempting to wake this up.

What is the intended behaviour here? If a string is provided the css chunks are concatenated into one css file, if a boolean value is provided then the chunks are correctly output, however we lose control over where they end up.

I'm not sure how hard it is to make this a little more flexible, but I'd be willing to attempt a PR here.

@rw4lll
Copy link

rw4lll commented Jan 17, 2022

Faced with the same problem. In my case scss block in single file components was not extracted. After some investigation, this workaround has helped me:
.js('resources/js/tool.js', 'js').vue({ extractStyles: true, loaders: { js: 'buble-loader', scss: 'vue-style-loader!css-loader!sass-loader' } })

@roitto
Copy link

roitto commented Mar 10, 2022

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?

@jakedowns
Copy link

jakedowns commented Apr 14, 2022

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

webpackPlugins() {
return [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[name].css'
})
];
}

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: {
    }
  }
]

@trd-warren
Copy link

trd-warren commented May 13, 2022

In my case.

I have components with <styles></styles> in it.
I am trying to achieve is those <styles></styles> should only be used when the component is used.
I registered each component globally.

Example.

CompanyList.vue
ProjectList.vue

each component have <styles></styles> in it,
when I used <company_list /> , styles from <project_list /> is included eventhought not called in the page. :(

image

mix.js('resources/assets/js/app.js', 'public/js')
   .sass('resources/assets/sass/app.scss', 'public/css')
   .vue({
      version: 2
   })
   .version();

Is there any workaround?
Component styles should not be extracted or put to app.css

@vilbergs
Copy link

In my case.

I have components with <styles></styles> in it. I am trying to achieve is those <styles></styles> should only be used when the component is used. I registered each component globally.

Example.

CompanyList.vue
ProjectList.vue

each component have <styles></styles> in it, when I used <company_list /> , styles from <project_list /> is included eventhought not called in the page. :(

image

mix.js('resources/assets/js/app.js', 'public/js')
   .sass('resources/assets/sass/app.scss', 'public/css')
   .vue({
      version: 2
   })
   .version();

Is there any workaround? Component styles should not be extracted or put to app.css

I'm not sure if your problem is related to this issue. You might want to look into overwriting https://github.com/webpack-contrib/style-loader with webpack override option.

If registering components locally or registering them as async components fixes your problem, that is the approach I would take instead of messing with the style-loader.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

10 participants