Prevent Webpack encodeing base64 for image - vue.js

I'am learning Vue and building a small app with Vue CLI. During this process, I've met a troubleshoot that made me very confuse, with vue.config.js
This is my vue.config.js's content:
const webpack = require('webpack')
const path = require('path')
const is_production = process.env.NODE_ENV === 'production'
module.exports = {
publicPath: is_production ? "http://localhost:5000" : "https://my.carpi.test:8080",
devServer: {
https: true,
host: 'my.carpi.test'
},
chainWebpack: config => {
config.module
.rule('images')
.use('url-loader')
.loader('url-loader')
.tap(options => {
options.limit = 10000
options.name = '[name].[ext]'
return options
})
},
configureWebpack: {
mode: is_production ? 'production' : 'development',
entry: './src/main.js',
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'dist'),
libraryTarget: 'umd',
globalObject: 'this'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.css$/,
use: [
"style-loader",
'css-loader',
'sass-loader'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf|svg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts',
publicPath: 'assets'
},
},
],
},
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images',
},
},
],
},
]
}
}
}
And below is a sample of my scss file:
.browser-icon, [class^="browser-icon"] {
border-radius: 50%;
height: 40px;
width: 40px;
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
font-weight: 500;
letter-spacing: 0.06em;
flex-shrink: 0;
}
.browser-icon-xs, .browser-icon.xs {
height: 18px;
width: 18px;
background-image: url('../images/Unknown.png');
background-size: cover;
}
.browser-ie-icon {
background-image: url('../images/IE.png') !important;
}
.browser-chrome-icon {
background-image: url('../images/Chrome.png') !important;
}
What I expected is the background images in scss file should be exported to distribution assets folder with [name].[ext] as defined, but webpack always encode them to base64 format, and the following screenshot is an example:
Screenshot Image
I still need to describe more here is I've also googled many threads about this question, I've also followed those answers but still unlucky.
So please help me out of this headache, any help would be appreciated!
Thanks

You are using url-loader which transforms files into base64 URIs.
Ref: https://webpack.js.org/loaders/url-loader/
Removing this will encode the images using the appropriate url.

Related

SCSS/SASS files doesn't work with electron nuxt

I don't know how to fix this, the header and script tag is just rendered in electron app. i've already tried many things but nothing comes work. this is the screenshot. btw, i'm using this template https://github.com/saltyshiomix/nuxtron. if i exclude the scss file, it works. but i need to working with scss. i already added node-sass and sass-loader previously. how to fix this issue? thanks.
the nuxt.config.js file
export default {
// ssr: false,
head: {
title: 'this is broken app',
meta: [
{ charset: "utf-8" },
{ name: "viewport", content: "width=device-width, initial-scale=1" },
{
hid: "description",
name: "description",
content: process.env.npm_package_description || "it has been drive me crazy like days"
}
],
link: [
{
rel: "stylesheet",
href:
"https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght#0,300;0,400;0,600;0,800;1,400&family=Playfair+Display:ital,wght#0,400;0,500;1,400;1,500&display=swap"
}
]
},
loading: { color: "#F4841A" },
css: [
"~/assets/scss/main.scss"
],
plugins: [
// {src: '~/plugins/element.js', mode: 'client'},
],
buildModules: [
// '#nuxtjs/fontawesome',
],
/*
** Axios module configuration
** See https://axios.nuxtjs.org/options
*/
axios: {
// baseURL: process.env.AUTH_API,
retry: { retries: 3 }
// proxy: true
},
build: {
extend: (config) => {
config.target = 'electron-renderer';
},
},
}
the scss file
* {
transition: all 0.15s ease-in-out;
display: block;
background-color: #f8f9fa;
}
html,
body {
font-family: "Open Sans", "Verdana", sans-serif;
line-height: 1.4;
margin: 0px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
project structure
The weird thing is, I created fresh project, same error, make it again over and over, now it worked. I think there's some problem with importing and config #nuxtjs/fontawesome plugin in nuxt.config.js but that's weird. Anyway, when it worked, it worked, right? :D

Sourcemaps inaccurate (Vue + webpack + sentry)

I'm trying to generate correct sourcemaps for my project written in vue2 so I could later use them in sentry. However right now when I upload them to Sentry, everything seems to be working fine apart from Sentry complaining that it cannot match the location in the source map. Sourcemap Validator also thinks so:
http://sourcemaps.io/report/1578938920829_http%3A%2F%2F7ec94d64.ngrok.io%2FSideMenuAnalytics.40e8f1227591b28bea7d.js
It looks that sourcemap mappings are off by X characters. Have you seen something like that and could give me a hint how to solve it? Or can someone share webpack config which is producing correct sourcemaps?
PS. I've found this: https://github.com/webpack/webpack/issues/8302#issuecomment-521779175 but I'm not sure if it's actually related to my problem.
source file (.js):
(window.webpackJsonp=window.webpackJsonp||[]).push([[35],{485:function(t,e,i){var n=i(604);"string"==typeof n&&(n=[[t.i,n,""]]),n.locals&&(t.exports=n.locals);(0,i(8).default)("8850203c",n,!0,{})},603:function(t,e,i){"use strict";var n=i(485);i.n(n).a},604:function(t,e,i){(e=i(7)(!1)).push([t.i,".sidebar-menu__item{display:-webkit-box;display:flex;-webkit-box-align:center;align-items:center;margin-bottom:22px;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column}.sidebar-menu__tab{width:100%;border-left:4px solid transparent;padding-left:16px;color:#6d7688;display:inline-block;font-weight:500;text-decoration:none;vertical-align:middle;word-break:break-word}.sidebar-menu__tab--active{border-left:4px solid #1593ff;color:#1a2a4a;font-weight:500}.sidebar-menu__activetab{color:#0079e1}#media(max-width: 768px){.sidebar-menu{-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-direction:row}}.sidebar-submenu{margin-top:20px;margin-bottom:20px;width:100%;padding-left:20px}.sidebar-submenu .sidebar-menu__item{margin-bottom:1em}",""]),t.exports=e},605:function(t,e,i){var n=i(776);"string"==typeof n&&(n=[[t.i,n,""]]),n.locals&&(t.exports=n.locals);(0,i(8).default)("737a94e4",n,!0,{})},639:function(t,e,i){"use strict";i(22),i(94),i(23);var n={name:"SideMenu",components:{BaseSelect:i(51).r},props:["items","exactlyMatchRoute"],data:function(){return{options:[],menuItem:this.$route.name}},watch:{menuItem:function(t){this.$router.push({name:t}),this.menuItem=t}},methods:{flatten:function(t){var e=this;return t.reduce((function(t,i){return t.push({value:i.name,text:i.i18n}),i.items&&(t=t.concat(e.flatten(i.items))),t}),[])}},created:function(){this.options=this.flatten(this.items)}},a=(i(603),i(1)),o=Object(a.a)(n,(function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",[i("ul",{staticClass:"sidebar-menu hidden-xs"},t._l(t.items,(function(e){return i("li",{key:e.name},[i("div",{staticClass:"sidebar-menu__item"},[i("router-link",{staticClass:"sidebar-menu__tab",attrs:{"active-class":"sidebar-menu__tab--active",to:{name:e.name},tag:"a",exact:t.exactlyMatchRoute}},[t._v(t._s(e.i18n))]),e.items?i("ul",{staticClass:"sidebar-submenu"},t._l(e.items,(function(e){return i("li",{key:e.name},[i("router-link",{staticClass:"sidebar-menu__tab",attrs:{"active-class":"sidebar-menu__tab--active",to:{name:e.name},tag:"a",exact:!0}},[t._v(t._s(e.i18n))])],1)})),0):t._e()],1)])})),0),i("BaseSelect",{attrs:{classes:"large visible-xs-block",options:t.options},model:{value:t.menuItem,callback:function(e){t.menuItem=e},expression:"menuItem"}})],1)}),[],!1,null,null,null);e.a=o.exports},775:function(t,e,i){"use strict";var n=i(605);i.n(n).a},776:function(t,e,i){(e=i(7)(!1)).push([t.i,".partner-settings__contact{margin-bottom:20px}.partner-settings__title{margin-bottom:20px}.settings__row{display:-webkit-box;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-direction:row;margin-bottom:20px}.settings__row button:first-child{margin-right:25px}.settings__column{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column;width:50%;padding-right:20px}.partner-settings__label{font-weight:500}",""]),t.exports=e},858:function(t,e,i){"use strict";i.r(e);var n={components:{SideMenu:i(639).a},data:function(){return{items:[{name:"general-performance",i18n:this.$t("pPartner general performance")},{name:"campaigns-performance",i18n:this.$t("pPartner campaigns performance")},{i18n:this.$t("jsPartner_analytics overview bookings"),name:"bookings-all",items:[{name:"bookings-all",i18n:this.$t("jsPartner_analytics overview all bookings")},{name:"bookings-conducted",i18n:this.$t("jsPartner_analytics overview conducted bookings")}]}]}}},a=(i(775),i(1)),o=Object(a.a)(n,(function(){var t=this.$createElement;return(this._self._c||t)("SideMenu",{attrs:{items:this.items,exactlyMatchRoute:!1}})}),[],!1,null,null,null);e.default=o.exports}}]);
# sourceMappingURL=SideMenuAnalytics.40e8f1227591b28bea7d.js.map
sourcemap file (.js.map):
{"version":3,"sources":["webpack:///./src/components/SideMenu.vue?b476","webpack:///./src/components/SideMenu.vue?f4a6","webpack:///./src/components/SideMenu.vue?ddfe","webpack:///./src/pages/analytics/SideMenuAnalytics.vue?6589","webpack:///./src/components/SideMenu.vue?6646","webpack:///./src/components/SideMenu.vue?82ff","webpack:///src/components/SideMenu.vue","webpack:///./src/components/SideMenu.vue","webpack:///./src/pages/analytics/SideMenuAnalytics.vue?eb0c","webpack:///./src/pages/analytics/SideMenuAnalytics.vue?0508","webpack:///./src/pages/analytics/SideMenuAnalytics.vue?258b","webpack:///./src/pages/analytics/SideMenuAnalytics.vue?a09f","webpack:///src/pages/analytics/SideMenuAnalytics.vue","webpack:///./src/pages/analytics/SideMenuAnalytics.vue"],"names":["content","module","i","locals","exports","add","default","___CSS_LOADER_API_IMPORT___","push","component","_vm","this","_h","$createElement","_c","_self","staticClass","_l","tab","key","name","attrs","exactlyMatchRoute","_v","_s","i18n","t","_e","options","model","value","callback","$$v","menuItem","expression","items"],"mappings":"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","file":"SideMenuAnalytics.40e8f1227591b28bea7d.js","sourcesContent":["// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = require(\"!!../../node_modules/css-loader/dist/cjs.js??ref--2-oneOf-0-1!../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../node_modules/postcss-loader/src/index.js??ref--2-oneOf-0-2!../../node_modules/sass-loader/dist/cjs.js??ref--2-oneOf-0-3!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./SideMenu.vue?vue&type=style&index=0&lang=scss&\");\nif(typeof content === 'string') content = [[module.id, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar add = require(\"!../../node_modules/vue-style-loader/lib/addStylesClient.js\").default\nvar update = add(\"8850203c\", content, true, {});","import mod from \"-!../../node_modules/vue-style-loader/index.js!../../node_modules/css-loader/dist/cjs.js??ref--2-oneOf-0-1!../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../node_modules/postcss-loader/src/index.js??ref--2-oneOf-0-2!../../node_modules/sass-loader/dist/cjs.js??ref--2-oneOf-0-3!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./SideMenu.vue?vue&type=style&index=0&lang=scss&\"; export default mod; export * from \"-!../../node_modules/vue-style-loader/index.js!../../node_modules/css-loader/dist/cjs.js??ref--2-oneOf-0-1!../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../node_modules/postcss-loader/src/index.js??ref--2-oneOf-0-2!../../node_modules/sass-loader/dist/cjs.js??ref--2-oneOf-0-3!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./SideMenu.vue?vue&type=style&index=0&lang=scss&\"","// Imports\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.id, \".sidebar-menu__item{display:-webkit-box;display:flex;-webkit-box-align:center;align-items:center;margin-bottom:22px;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column}.sidebar-menu__tab{width:100%;border-left:4px solid transparent;padding-left:16px;color:#6d7688;display:inline-block;font-weight:500;text-decoration:none;vertical-align:middle;word-break:break-word}.sidebar-menu__tab--active{border-left:4px solid #1593ff;color:#1a2a4a;font-weight:500}.sidebar-menu__activetab{color:#0079e1}#media(max-width: 768px){.sidebar-menu{-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-direction:row}}.sidebar-submenu{margin-top:20px;margin-bottom:20px;width:100%;padding-left:20px}.sidebar-submenu .sidebar-menu__item{margin-bottom:1em}\", \"\"]);\n// Exports\nmodule.exports = exports;\n","// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = require(\"!!../../../node_modules/css-loader/dist/cjs.js??ref--2-oneOf-0-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--2-oneOf-0-2!../../../node_modules/sass-loader/dist/cjs.js??ref--2-oneOf-0-3!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./SideMenuAnalytics.vue?vue&type=style&index=0&lang=scss&\");\nif(typeof content === 'string') content = [[module.id, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar add = require(\"!../../../node_modules/vue-style-loader/lib/addStylesClient.js\").default\nvar update = add(\"737a94e4\", content, true, {});","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('ul',{staticClass:\"sidebar-menu hidden-xs\"},_vm._l((_vm.items),function(tab){return _c('li',{key:tab.name},[_c('div',{staticClass:\"sidebar-menu__item\"},[_c('router-link',{staticClass:\"sidebar-menu__tab\",attrs:{\"active-class\":\"sidebar-menu__tab--active\",\"to\":{ name: tab.name},\"tag\":\"a\",\"exact\":_vm.exactlyMatchRoute}},[_vm._v(_vm._s(tab.i18n))]),(tab.items)?_c('ul',{staticClass:\"sidebar-submenu\"},_vm._l((tab.items),function(t){return _c('li',{key:t.name},[_c('router-link',{staticClass:\"sidebar-menu__tab\",attrs:{\"active-class\":\"sidebar-menu__tab--active\",\"to\":{ name: t.name},\"tag\":\"a\",\"exact\":true}},[_vm._v(_vm._s(t.i18n))])],1)}),0):_vm._e()],1)])}),0),_c('BaseSelect',{attrs:{\"classes\":\"large visible-xs-block\",\"options\":_vm.options},model:{value:(_vm.menuItem),callback:function ($$v) {_vm.menuItem=$$v},expression:\"menuItem\"}})],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../node_modules/babel-loader/lib/index.js??ref--1!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./SideMenu.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../node_modules/babel-loader/lib/index.js??ref--1!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./SideMenu.vue?vue&type=script&lang=js&\"","<template>\n <div>\n <ul class=\"sidebar-menu hidden-xs\">\n <li :key=\"tab.name\" v-for=\"tab in items\">\n <div class=\"sidebar-menu__item\">\n <router-link\n active-class=\"sidebar-menu__tab--active\"\n class=\"sidebar-menu__tab\"\n :to=\"{ name: tab.name}\"\n tag=\"a\"\n :exact=\"exactlyMatchRoute\"\n >{{tab.i18n}}</router-link>\n <!-- second level navigation (this section displays children nested underneath the main element -->\n <ul class=\"sidebar-submenu\" v-if=\"tab.items\">\n <li :key=\"t.name\" v-for=\"t in tab.items\">\n <router-link\n active-class=\"sidebar-menu__tab--active\"\n class=\"sidebar-menu__tab\"\n :to=\"{ name: t.name}\"\n tag=\"a\"\n :exact=\"true\"\n >{{t.i18n}}</router-link>\n </li>\n </ul>\n </div>\n </li>\n </ul>\n <BaseSelect classes=\"large visible-xs-block\" :options=\"options\" v-model=\"menuItem\" />\n </div>\n</template>\n\n<script>\nimport { BaseSelect } from 'Components/base/index';\n\nexport default {\n name: 'SideMenu',\n components: {\n BaseSelect\n },\n props: ['items', 'exactlyMatchRoute'],\n data: function () {\n return {\n options: [],\n menuItem: this.$route.name\n }\n },\n watch: {\n menuItem: function (name) {\n this.$router.push({ name });\n this.menuItem = name;\n }\n },\n methods: {\n flatten: function (array) {\n return array.reduce((acc, value) => {\n acc.push({ value: value.name, text: value.i18n });\n if (value.items) {\n acc = acc.concat(this.flatten(value.items));\n }\n return acc;\n }, []);\n }\n },\n created: function () {\n this.options = this.flatten(this.items)\n }\n};\n</script>\n\n<style lang=\"scss\">\n#import \"~#getyourguide/design-system/colors/colors\";\n#import \"~#getyourguide/design-system/spacing/breakpoints\";\n\n$border-width: 4px;\n$indent: 16px;\n\n.sidebar-menu {\n &__item {\n display: flex;\n align-items: center;\n margin-bottom: 22px;\n flex-direction: column;\n }\n\n &__tab {\n width: 100%;\n border-left: $border-width solid transparent;\n padding-left: $indent;\n color: $ui-slate;\n display: inline-block;\n font-weight: 500;\n text-decoration: none;\n vertical-align: middle;\n word-break: break-word;\n\n &--active {\n border-left: $border-width solid #1593ff;\n color: #1a2a4a;\n font-weight: 500;\n }\n }\n\n &__activetab {\n color: $cta-active;\n }\n\n #media (max-width: $screen-sm-min) {\n flex-direction: row;\n }\n}\n\n.sidebar-submenu {\n margin-top: $border-width + $indent;\n margin-bottom: $border-width + $indent;\n width: 100%;\n padding-left: $border-width + $indent;\n\n // Reset margin in submenu items\n .sidebar-menu__item {\n margin-bottom: 1em;\n }\n}\n</style>\n","import { render, staticRenderFns } from \"./SideMenu.vue?vue&type=template&id=39191e03&\"\nimport script from \"./SideMenu.vue?vue&type=script&lang=js&\"\nexport * from \"./SideMenu.vue?vue&type=script&lang=js&\"\nimport style0 from \"./SideMenu.vue?vue&type=style&index=0&lang=scss&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","import mod from \"-!../../../node_modules/vue-style-loader/index.js!../../../node_modules/css-loader/dist/cjs.js??ref--2-oneOf-0-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--2-oneOf-0-2!../../../node_modules/sass-loader/dist/cjs.js??ref--2-oneOf-0-3!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./SideMenuAnalytics.vue?vue&type=style&index=0&lang=scss&\"; export default mod; export * from \"-!../../../node_modules/vue-style-loader/index.js!../../../node_modules/css-loader/dist/cjs.js??ref--2-oneOf-0-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--2-oneOf-0-2!../../../node_modules/sass-loader/dist/cjs.js??ref--2-oneOf-0-3!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./SideMenuAnalytics.vue?vue&type=style&index=0&lang=scss&\"","// Imports\nvar ___CSS_LOADER_API_IMPORT___ = require(\"../../../node_modules/css-loader/dist/runtime/api.js\");\nexports = ___CSS_LOADER_API_IMPORT___(false);\n// Module\nexports.push([module.id, \".partner-settings__contact{margin-bottom:20px}.partner-settings__title{margin-bottom:20px}.settings__row{display:-webkit-box;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-direction:row;margin-bottom:20px}.settings__row button:first-child{margin-right:25px}.settings__column{display:-webkit-box;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column;width:50%;padding-right:20px}.partner-settings__label{font-weight:500}\", \"\"]);\n// Exports\nmodule.exports = exports;\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('SideMenu',{attrs:{\"items\":_vm.items,\"exactlyMatchRoute\":false}})}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../node_modules/babel-loader/lib/index.js??ref--1!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./SideMenuAnalytics.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/babel-loader/lib/index.js??ref--1!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./SideMenuAnalytics.vue?vue&type=script&lang=js&\"","<template>\n <SideMenu :items=\"items\" :exactlyMatchRoute=\"false\"/>\n</template>\n\n<script>\nimport SideMenu from '../../components/SideMenu.vue';\n\nexport default {\n components: { SideMenu },\n data() {\n return {\n items: [\n { name: 'general-performance', i18n: this.$t('pPartner general performance') },\n { name: 'campaigns-performance', i18n: this.$t('pPartner campaigns performance') },\n {\n i18n: this.$t('jsPartner_analytics overview bookings'),\n name: 'bookings-all',\n items: [\n {name: 'bookings-all', i18n: this.$t('jsPartner_analytics overview all bookings')},\n {name: 'bookings-conducted', i18n: this.$t('jsPartner_analytics overview conducted bookings')},\n ]\n }\n ]\n };\n }\n};\n</script>\n\n<style lang=\"scss\">\n// TODO: title, columns, rows definitions should not\n// live here. All those definitions should be moved into\n// its own components.\n\n.partner-settings__contact {\n margin-bottom: 20px;\n}\n\n.partner-settings__title {\n margin-bottom: 20px;\n}\n\n.settings__row {\n display: flex;\n flex-direction: row;\n margin-bottom: 20px;\n\n button {\n &:first-child {\n margin-right: 25px;\n }\n }\n}\n\n.settings__column {\n display: flex;\n flex-direction: column;\n width: 50%;\n padding-right: 20px;\n}\n\n.partner-settings__label {\n font-weight: 500;\n}\n</style>\n","import { render, staticRenderFns } from \"./SideMenuAnalytics.vue?vue&type=template&id=6982eed3&\"\nimport script from \"./SideMenuAnalytics.vue?vue&type=script&lang=js&\"\nexport * from \"./SideMenuAnalytics.vue?vue&type=script&lang=js&\"\nimport style0 from \"./SideMenuAnalytics.vue?vue&type=style&index=0&lang=scss&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports"],"sourceRoot":""}
webpack config
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const FriendlyErrorsPlugin = require("friendly-errors-webpack-plugin");
const MomentLocalesPlugin = require("moment-locales-webpack-plugin");
const { VueLoaderPlugin } = require("vue-loader");
const TerserPlugin = require("terser-webpack-plugin");
const state = require("../state");
const { isProd } = state;
module.exports = {
mode: isProd ? "production" : "development",
devtool: isProd ? "source-map" : "cheap-module-eval-source-map",
output: {
path: path.resolve(__dirname, "../..", "./public/assets/compiled"),
publicPath: "/assets/compiled/",
filename: "[name].[hash].js"
},
resolve: {
alias: {
Public: path.resolve(__dirname, "../..", "./public"),
Components: path.resolve(__dirname, "../..", "./src/components/"),
Mixins: path.resolve(__dirname, "../..", "./src/mixins/"),
Store: path.resolve(__dirname, "../..", "./src/store/"),
Util: path.resolve(__dirname, "../..", "./src/util/"),
Pages: path.resolve(__dirname, "../..", "./src/pages/"),
Styles: path.resolve(__dirname, "../..", "./src/styles/")
}
},
module: {
noParse: /es6-promise\.js$/, // avoid webpack shimming process
// noParse: /^(vue|vue-router|vuex|vuex-router-sync)$/,
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
options: {
compilerOptions: {
preserveWhitespace: false
}
}
},
{
test: /\.js$/,
loader: "babel-loader",
// In order to enable es6 module for SSR render it's necessary to tell babel
// that it should process module files.
// Reference: https://github.com/nuxt/nuxt.js/issues/3485
// We should only use include || exclude
exclude: function(modulePath) {
return (
/node_modules/.test(modulePath) &&
!/node_modules\/#getyourguide\/design-system/.test(modulePath) &&
!/node_modules\/#getyourguide\/event-logger/.test(modulePath) &&
!/node_modules\/#sentry\/browser/.test(modulePath) &&
!/node_modules\/v-tooltip/.test(modulePath) &&
!/node_modules\/vue-18n/.test(modulePath)
);
},
options: {
presets: [
[
"#babel/preset-env",
{
debug: false,
useBuiltIns: "usage",
corejs: 3,
targets: {
ie: "11"
},
modules: 'auto' // Needed for tree shaking to work.
}
]
],
plugins: [
"#babel/plugin-syntax-dynamic-import",
"#babel/plugin-proposal-object-rest-spread",
"#babel/plugin-transform-shorthand-properties"
]
}
},
{
test: /\.scss?$/,
oneOf: [
{
use: [
"vue-style-loader",
{
loader: "css-loader",
options: {
importLoaders: 1,
sourceMap: false
}
},
{
loader: "postcss-loader",
options: {
sourceMap: false,
plugins: [
require("autoprefixer")({
grid: true,
flexbox: true
})
]
}
},
{
loader: "sass-loader",
options: {
implementation: require("sass"),
sourceMap: false
}
}
]
}
]
}
]
},
performance: {
maxEntrypointSize: 300000,
hints: isProd ? "warning" : false
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: "common.[hash].css"
}),
...(isProd ? [] : [new FriendlyErrorsPlugin()]),
// new BundleAnalyzerPlugin({
// generateStatsFile: true,
// openAnalyzer: true
// }),
// To strip all locales except “en”
new MomentLocalesPlugin()
],
optimization: {
minimizer: isProd
? [
new TerserPlugin({
sourceMap: true
})
]
: []
}
};

Webpack + vue-loader + postcss-modules results in empty $style object

In my app I'm initializing a Vue app, which uses single file .vue components.
I use Webpack to bundle, and vue-loader + postcss-modules to generate scoped classes.
But for some reason I can't access the generated classes inside my components ($style object is empty). I'll explain the problem below and created this repo as an example.
My hello.vue component looks like this:
<template>
<div :class="$style.hello">
Hello World!
</div>
</template>
<script>
export default {
name: "hello",
created() {
console.log(this.$style); // <- empty object :(
}
};
</script>
<style module>
.hello {
background: lime;
}
</style>
hello.vue.json is generated as expected (CSS Modules mapping):
{"hello":"_hello_23p9g_17"}
Scoped styles are appended in the document head, and when using mini-css-extract-plugin it is bundled in app.css:
._hello_23p9g_17 {
background: lime;
}
Does anyone know what the problem is and possibly how to fix this?
Below my config files.
webpack.config.js (trimmed for readability)
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
module.exports = {
entry: {
app: "./src/index.js"
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "build")
},
resolve: {
extensions: [".js", ".vue", ".json", ".css"],
alias: {
vue: "vue/dist/vue.esm.js"
}
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader"
}
},
{
test: /\.vue$/,
loader: "vue-loader"
},
{
test: /\.css$/,
use: [
"vue-style-loader",
// MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
// modules: true,
importLoaders: 1
}
},
"postcss-loader"
]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: "[name].css"
}),
new VueLoaderPlugin()
]
};
postcss.config.js
module.exports = {
ident: "postcss",
plugins: {
"postcss-preset-env": { stage: 0 },
"postcss-modules": {}
}
};
EDIT:
FYI, setting modules: true in the css-loader options works in that it populates the $style object (see docs):
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
modules: true
}
}
]
}
But in our app we use postcss-loader (as per docs) that takes care of all transformations including scoping. Enabling both modules: true and postcss-modules conflicts and breaks the classes/mapping (as expected).
In other words, I'm looking for a way to omit the modules: true option and enable css modules using postcss-modules instead.
Found a workaround: manually import the styles from the JSON file.
If anyone knows a better way please let me know :)
hello.vue.json
{"hello":"_hello_fgtjb_30"}
hello.vue
<template>
<div :class="style.hello">
Hello World!
</div>
</template>
<script>
import style from "./hello.vue.json";
export default {
name: "hello",
beforeCreate() {
this.style = style;
},
created() {
console.log(this.style);
}
};
</script>
<style module>
.hello {
background: lime;
}
</style>
This only works when using postcss-modules (loaded from config by postcss-loader in my case) instead of using modules: true:
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
importLoaders: 1
}
},
"postcss-loader"
]
};
See this PR as a full example.

Vue SFC styles not being extracted in webpack production build

Trying to add vue (and SFCs) to my webpack app. The <template> and <script> blocks work fine, but for some reason the styles in the <style> block are not being extracted for production build.
In the dev build, it's extracting the .vue <style> block to a separate css file (named for the entrypoint). Which is OK but I'd prefer they went into my main stylesheet.
But no matter what I try, I can't get any .vue styles to show up (in any file) for the production build.
This is an abbreviated version of my webpack config:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
...
module.exports = (env) => {
return {
entry: {
app: ["./src/polyfills.js", "./src/scss/styles.scss", "./src/app.js"],
...
testview: "./src/js/views/TestView.js"
},
output: {
path: assets,
filename: "[name].[hash].js",
publicPath: "/static/"
},
resolve: {
modules: ["node_modules", "src"],
alias: {
vue$: "vue/dist/vue.esm.js"
},
extensions: ["*", ".js", ".vue"]
},
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader"
},
{
test: /\.js?$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
options: {
presets: [
[
"#babel/preset-env",
{
targets: {
browsers: ["> 1%", "last 2 versions", "ie >= 11"]
}
}
]
],
plugins: ["#babel/plugin-proposal-class-properties"],
code: true,
comments: true,
cacheDirectory: true,
babelrc: false
}
}
]
},
{
test: /\.s?[ac]ss$/,
use: [
"vue-style-loader",
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
sourceMap: ifNotProduction()
}
},
{
loader: "postcss-loader",
options: {
ident: "postcss",
sourceMap: ifNotProduction(),
plugins: () =>
ifProduction([
require("autoprefixer")({
preset: "default"
}),
require("cssnano"),
require("css-mqpacker")
])
}
},
{
loader: "sass-loader",
options: {
sourceMap: ifNotProduction()
}
}
]
}
]
},
optimization: {
splitChunks: {
cacheGroups: {
commons: {
name: "commons",
chunks: "initial",
minChunks: 2,
minSize: 0
},
styles: {
name: "styles",
test: /\.css$/,
chunks: "all",
enforce: true
}
}
},
occurrenceOrder: true
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: "style.[hash].css"
}),
new HtmlWebpackPlugin({
hash: true,
inject: false,
template: "./src/jinja-templates/base.html.j2",
filename: `${templates}/base.html.j2`,
})
]
};
};
The .vue file I'm using is this demo one. I'm trying to import it into the entrypoint called 'testview' which contains simply:
import Vue from "vue";
import MainContent from "../components/main-content";
let MainComponent = Vue.extend(MainContent);
new MainComponent().$mount("#mainContent");
Did figure it out. I had to remove sideEffects: false from my package.json. This issue explains it further.
Still would like to know how to extract the .vue styles to my main stylesheet As it is now, the .vue styles are extracting to a separate stylesheet (dev and production).

Test vue.js components with vue-loader dependency injection

I'm trying to test my Vue.js component using webpack's loader vue-loader.
I was following their tutorial but things didn't work out as expected.
Here's my component:
<template>
<header v-once class="header">
<router-link to="/" class="brand">
<span class="brand-name">{{ brand }}</span>
<span class="brand-version">{{ version }}</span>
</router-link>
</header>
</template>
<script>
import { brand, version } from './Header.json';
export default {
name: 'Header',
data() {
return {
brand,
version
}
}
};
</script>
<style lang="sass" rel="stylesheet/scss" scoped>
$font-stack: 'Open Sans', sans-serif;
$primary-color: #2d5079;
$border-color: #345b88;
$brand-color: whitesmoke;
.header {
.brand {
.brand-name {
letter-spacing: -1px;
}
.brand-version {
font-size: 50%;
}
display: table-cell;
padding-left: 10px;
vertical-align: middle;
text-decoration: none;
color: $brand-color;
text-transform: lowercase;
font-weight: 100;
font-size: 120%;
}
display: table-row;
height: 50px;
font-family: $font-stack;
background-color: $primary-color;
border-color: $border-color;
border-bottom: 1px solid;
align-items: center;
padding: 0 10px;
user-select: none;
}
</style>
Here's my spec file:
import Vue from 'vue';
const headerInjector = require('!!vue?inject!./Header.vue');
const header = headerInjector({
'./Header.json': {
brand: "Test",
version: "1.2.3"
}
});
describe('Header', () => {
it('should be named Header', () => {
expect(header.name).toEqual('Header');
});
it('should render', () => {
const vm = new Vue({
template: '<div><header></header></div>',
components: {
header
}
}).$mount();
expect(vm.$el.querySelector('.brand-name').textContent).toBe('Test');
expect(vm.$el.querySelector('.brand-version').textContent).toBe('1.2.3');
});
});
I use karma to run my tests.
Here's karma config file:
const conf = require('./gulp.conf');
module.exports = function (config) {
const configuration = {
basePath: '../',
singleRun: false,
autoWatch: true,
logLevel: 'INFO',
junitReporter: {
outputDir: 'test-reports'
},
browsers: [
'PhantomJS'
],
frameworks: [
'jasmine'
],
files: [
'node_modules/es6-shim/es6-shim.js',
conf.path.src('app.spec.js')
],
preprocessors: {
[conf.path.src('app.spec.js')]: [
'webpack'
]
},
reporters: ['progress', 'coverage'],
coverageReporter: {
type: 'html',
dir: 'coverage/'
},
webpack: require('./webpack-test.conf'),
webpackMiddleware: {
noInfo: true
},
plugins: [
require('karma-jasmine'),
require('karma-junit-reporter'),
require('karma-coverage'),
require('karma-phantomjs-launcher'),
require('karma-phantomjs-shim'),
require('karma-webpack')
]
};
config.set(configuration);
};
and here's the used webpack config for the pre-processing:
module.exports = {
module: {
preLoaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint'
}
],
loaders: [
{
test: /.json$/,
loader: 'json'
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel'
},
{
test: /.vue$/,
loader: 'vue'
}
]
},
plugins: [],
debug: true,
devtool: 'source-map'
};
Here's app.spec.js which is the entry point for the tests which karma loads.
It's responsible for loading all spec files:
const context = require.context('./components', true, /\.spec\.js$/);
context.keys().forEach(context);
Now, when I run the tests with karma, I get the following error:
PhantomJS 2.1.1 (Windows 8 0.0.0) ERROR
TypeError: Object is not a constructor (evaluating '__vue_exports__(injections)')
at src/app.spec.js:1271
I have no clue for why this is happening, or what this error means, it's a bit cryptic to me.
Could someone pinpoint my error and explain it?
Was experiencing the same thing, seems to be an issue with version 3+ of inject loader. Perhaps the vuejs testing with mocks docs needs to be updated.
force the version in your package.json
"inject-loader": "^2.0.1",