sass-loader can't resolve #use rule - vue.js

I'm working on a vuejs app and I'm trying to import my variables files using the #use rule within prependData in sass-loader, but it seems not working properly. Using #use import does not work and I receive erros about variables. But using #import everything goes fine. Am I doing something wrong or getting the wrong concept about #use rule?
Using #import in prependData works:
css: {
loaderOptions: {
sass: {
implementation: require('sass'),
sassOptions: {
indentedSyntax: true
},
// prependData: `#use '~abstracts/variables'` //this does not work
prependData: `#import '~abstracts/variables'` //this works
}
}
}
Using #use does not work:
Module build failed (from ./node_modules/sass-loader/dist/cjs.js):
SassError: Undefined variable.
╷
15 │ color: $chuck
│ ^^^^^^
╵
My package.json:
"sass": "^1.26.3",
"sass-loader": "^8.0.2"
My component:
<style lang="sass">
.title
color: $chuck
</style>
My variables.sass file:
$chuck: #BADA55
TIA

I just figured out what I was missing.
Just add as *:
css: {
loaderOptions: {
sass: {
implementation: require('sass'),
sassOptions: {
indentedSyntax: true
},
prependData: `#use '~abstracts/variables' as *`
}
}
}
I'm just getting used to this new way of using sass.

Related

Sass global variables and mixins not working

I've set up a project using Vue 3.2.33 and Vite 2.9.5
When I try to access any global variable or mixin from within any vue component, I get an undefined error. This problem doesn't occur in scss files.
The import itself seems working correctly because any css rules in it are working.
vite.config.ts:
import { fileURLToPath, URL } from 'url';
import { defineConfig } from 'vite';
import vue from '#vitejs/plugin-vue';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'#': fileURLToPath(new URL('./src', import.meta.url)),
},
},
css: {
preprocessorOptions: {
scss: {
additionalData: '#use "#/styles/variables";',
},
},
},
});
src/styles/_variables.scss:
// breakpoints
$breakpoints: (
"sm": 576px,
"md": 768px,
"lg": 992px,
"xl": 1200px,
"xxl": 1400px,
);
#mixin test {
border: 3px solid red;
}
Example use:
<style scoped lang="scss">
#use 'sass:map';
.container {
max-width: 100%;
width: 100%;
margin: 0 auto;
#include test; // <- undefined
&--fluid {
max-width: 100%;
width: 100%;
}
}
$widths: (
'sm': 540px,
'md': 720px,
'lg': 960px,
'xl': 1140px,
'xxl': 1320px,
);
#each $breakpoint, $width in $widths {
#media (min-width: map.get($breakpoints, $breakpoint)) { // <- $breakpoints undefined
.container {
max-width: $width;
}
}
}
</style>
use
#import
in your vite config instead of
#use
vite.config.ts:
export default defineConfig({
plugins: [vue()],
css: {
preprocessorOptions: {
scss: {
additionalData: '#import "./src/styles/variables.scss";',
},
},
},
});
keep in mind that you cannot import the same file variables.scss again in your main.ts file otherwise, you will get this error
[sass] This file is already being loaded.
by the way, you can also import the scss file in every single component manually as you mentioned but that would be really tedious so using a global import in preprocessorOptions in vite.config.ts is a much better option for files used globally like a variables.scss file.
I've managed to "fix" the issue. Turns out, when I replace all #use rules for file imports, the sass code is imported correctly and works. But this produces a new problem as the #import rules cannot be placed before #use, so I had to remove the additionalData key from config and include the imports manually.

Vite - Separate chunk for fonts

I am building a library that contains components and some shared CSS, like a design system.
The problem is that it is not separating the fonts in different chunks, instead, it is inlining them in the font as base 64, so the CSS file gets huge!
// vite.config.ts
import { defineConfig } from 'vite';
import vue from '#vitejs/plugin-vue';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
build: {
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'design-system',
},
rollupOptions: {
external: ['vue'],
output: {
globals: {
vue: 'Vue',
},
},
},
},
resolve: {
alias: [
{ find: '#', replacement: '/src' },
],
},
});
I already tried to place it in root/public, but it didn't work.
I have a file _fonts.css which import fonts like so
#font-face {
font-family: Inter;
src: url('/public/fonts/Inter-Regular.ttf') format('truetype');
font-weight: 400;
}
And then I have a main.scss that imports it
// main.scss
#import 'normalize';
#import 'themes';
#import 'fonts';
Any idea on how to split it?
I was able to accomplish this in a library we're working on by placing the fonts in the special public directory (see: https://vitejs.dev/guide/assets.html#the-public-directory).
Long story short placing assets in there makes Vite copy them over without any actual transformation and/or renaming.
First of all place your font files in public/fonts/.
Then add the following to vite.config.ts:
{
root: './',
publicDir: 'public',
}
This was necessary although it should be a default setting.
Then my font declaration looks like this:
#font-face {
font-family: MyFont;
src: url('./fonts/myfont.woff2');
font-weight: 200;
}
But - this is for the library mode. In case you're building an app just start the font URL path with / instead of ./.

Using Bootstrap 4 with NuxtJS

I'm trying to associate bootstrap 4 (bootstrap-vue) with Nuxt.
I have difficulties using mixins and variables in pages or components, although I added style-resources-module.
Here is an extract of nuxt.config.js:
/*
** Global CSS
*/
css: ["~/scss/vars.scss"],
/*
** Plugins to load before mounting the App
*/
plugins: [],
/*
/*
** Nuxt.js modules
*/
modules: [
// Doc: https://bootstrap-vue.js.org/docs/
"bootstrap-vue/nuxt",
// Doc: https://github.com/nuxt-community/style-resources-module
"#nuxtjs/style-resources"
],
/*
** Disabling Bootstrap Compiled CSS
*/
bootstrapVue: {
bootstrapCSS: false,
bootstrapVueCSS: false
},
/*
** Style resources
*/
styleResources: {
scss: [
"./scss/*.scss",
"~bootstrap/scss/bootstrap.scss",
"~bootstrap-vue/src/index.scss"
]
},
./scss/vars.scss sets variables, and also overrides Bootstrap's
(e.g. $orange: #DD7F58;
Here is an extract of one of the components:
<style lang="scss" scoped>
.myClass{
display: none;
#include media-breakpoint-up(md) {
display: block;
}
}
</style>
Compilation throws the following error: _No mixin named media-breakpoint-up_.
Here is a setup that works, if ever you meet the same issue:
nuxt.config.js:
/*
** Nuxt.js modules
*/
modules: [
// Doc: https://bootstrap-vue.js.org/docs/
"bootstrap-vue/nuxt",
// Doc: https://github.com/nuxt-community/style-resources-module
"#nuxtjs/style-resources"
],
/*
** Disabling Bootstrap Compiled CSS
*/
bootstrapVue: {
bootstrapCSS: false,
bootstrapVueCSS: false
},
/*
** Style resources
*/
styleResources: {
scss: "./scss/*.scss"
},
./scss/custom.scss:
// Variable overrides
$orange: #DD7F58;
// Bootstrap and BootstrapVue SCSS files
#import '~bootstrap/scss/bootstrap.scss';
#import '~bootstrap-vue/src/index.scss';
// General style overrides and custom classes
body {
margin: 0;
}
I use the following code inside nuxt.config.js:
modules: [
'bootstrap-vue/nuxt',
'#nuxtjs/style-resources',
],
bootstrapVue: {
bootstrapCSS: false,
bootstrapVueCSS: false
},
styleResources: {
scss: [
'bootstrap/scss/_functions.scss',
'bootstrap/scss/_variables.scss',
'bootstrap/scss/_mixins.scss',
'bootstrap-vue/src/_variables.scss',
'~/assets/css/_variables.scss', // my custom variable overrides
],
},
For Bootstrap v5, I used
https://www.npmjs.com/package/#nuxtjs/style-resources
nuxt.config.js
css: [
'#/assets/scss/main.scss',
],
styleResources: {
scss: [
'~/node_modules/bootstrap/scss/_functions.scss',
'~/node_modules/bootstrap/scss/_variables.scss',
'~/node_modules/bootstrap/scss/_mixins.scss',
'~/node_modules/bootstrap/scss/_containers.scss',
'~/node_modules/bootstrap/scss/_grid.scss'
]
},
modules: [
'#nuxtjs/style-resources',
],
Component
<style scoped lang="scss">
.header {
#include make-container();
}
</style>

Can't load global scss variables in Vue SFCs

I tried to follow this tutorial to get access to a global file called variables.scss in all SFC files:
https://vueschool.io/articles/vuejs-tutorials/globally-load-sass-into-your-vue-js-applications/
My project uses vue cli 3, so I added the following property to vue.config.js:
{
css: {
loaderOptions: {
sass: {
data: `#import "src/assets/scss/variables.scss";`
}
}
},
}
the variables.scss file looks like this:
// variables.scss
$purple: #5D2D8B;
My component style looks like this:
<!-- Datepicker.vue -->
<style lang="scss" scoped>
.picker {
color: $purple!important;
}
</style>
But I got this error when trying to use the $purple variable
color: $purple!important;
^
Undefined variable: "$purple"
I've also tried to add style-resources-loader vue-cli plugin, but got the same error.
(added this when tried to use it):
{
pluginOptions: {
'style-resources-loader': {
preProcessor: 'scss',
patterns: [
path.resolve(__dirname, 'src/assets/scss/variables.scss')
]
}
}
}
It seems that on .vue files the #import statement is not applied, but in .scss files it is.
Anyone has an idea what is that about?

Vue.js webpack - How to use themify-icons?

I added the package
yarn add themify-icons-sass
then in my component , I imported it in the script and in the style
<script>
....
import 'themify-icons-sass'
<style lang="scss" scoped>
#import 'themify-icons-sass/themify-icons';
...
but I get a build error
This dependency was not found:
* themify-icons-sass in ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/components/HelloWorld.vue
To install it, you can run: npm install --save themify-icons-sass
Where am I wrong in the import ? thanks for feedback
UPDATE
First, if I want o import it from ./node_modules , then the #import should NOT BE in a scoped style...
So I moved it to a global style in my App.vue
<style lang="scss">
#import "~themify-icons-scss/scss/themify-icons.scss";
#app { ...
Then I got an erro , the node-sass/vendor directory was not bulit.. so I add to rebuild node_sass
yarn add node-sass --force
Now it's taking in account the package but I get another error related to the relative path to the fonts:
These relative modules were not found:
* ../fonts/themify.eot in ./node_modules/css-loader?{"sourceMap":true}!./node_modules/vue-loader/lib/style-compiler?{"vue":true,"id":"data-v-7ba5bd90","scoped":false,"hasInlineConfig":false}!./node_modules/sass-loader/lib/loader.js?{"sourceMap":true}!./node_modules/vue-
loader/lib/selector.js?type=styles&index=0!./src/App.vue
* ../fonts/themify.eot?-fvbane in ./node_modules/css-loader?{"sourceMap":true}!./node_modules/vue-loader/lib/style-compiler?{"vue":true,"id":"data-v-7ba5bd90","scoped":false,"hasIn
lineConfig":false}!./node_modules/sass-loader/lib/loader.js?{"sourceMap":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/App.vue
* ../fonts/themify.svg?-fvbane in ./node_modules/css-loader?{"sourceMap":true}!./node_modules/vue-loader/lib/style-compiler?{"vue":true,"id":"data-v-7ba5bd90","scoped":false,"hasIn
lineConfig":false}!./node_modules/sass-loader/lib/loader.js?{"sourceMap":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/App.vue
* ../fonts/themify.ttf?-fvbane in ./node_modules/css-loader?{"sourceMap":true}!./node_modules/vue-loader/lib/style-compiler?{"vue":true,"id":"data-v-7ba5bd90","scoped":false,"hasInlineConfig":false}!./node_modules/sass-loader/lib/loader.js?{"sourceMap":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/App.vue
* ../fonts/themify.woff?-fvbane in ./node_modules/css-loader?{"sourceMap":true}!./node_modules/vue-loader/lib/style-compiler?{"vue":true,"id":"data-v-7ba5bd90","scoped":false,"hasInlineConfig":false}!./node_modules/sass-loader/lib/loader.js?{"sourceMap":true}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/App.vue
the themify-icons-scss file structure is the following :
node_modules/themify-icons-scss
fonts
themify.eot
themify.svg
themify.ttf
themify.woff
scss
_core.scss
_extras.scss
_icons.scss
_mixins.scss
_paths.scss
_variables.scss
themify-icons.scss
node_modules/themify-icons-scss/scss/themify-icons.scss
#import "variables";
#import "mixins";
#import "path";
#import "core";
#import "extras";
#import "icons";
themify-icons-scss/scss/_path.scss
#font-face {
font-family: 'themify';
src:url('#{$ti-font-path}/themify.eot?-fvbane');
src:url('#{$ti-font-path}/themify.eot?#iefix-fvbane') format('embedded-opentype'),
url('#{$ti-font-path}/themify.woff?-fvbane') format('woff'),
url('#{$ti-font-path}//themify.ttf?-fvbane') format('truetype'),
url('#{$ti-font-path}/themify.svg?-fvbane#themify') format('svg');
font-weight: normal;
font-style: normal;
this is where there is some issue... with the $ti-font-path as defined in the variables, relative to the scss directory in the package..
themify-icons-scss/scss/_variables.scss
$ti-font-path: "../fonts" !default;
$ti-class-prefix: "ti" !default;
}
SOLVED...
there is a nice recent package ( updated 3 months go, I forked it as an archive for me ..). see themify-icons-scss
yarn add git+http://github.com/Frolki1-Dev/themify-icons-sass
to solve the issue with the relative font path variable in the package _path.scss , I added the package resolve-url-loader. ( see resolve-url-loader on github
yarn add resolve-url-loader --dev
And as per the resolve-url-loader readme, as I want to use webpack loaders I inserted into build/utils
build/utils.js
exports.cssLoaders = function (options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
// added resolve-url-loaderr with sourceMap true
const resolveUrlLoader = {
loader: 'resolve-url-loader',
options: {
sourceMap: options.sourceMap
}
}
In my App.vue I can import (global , cannot be scoped)
<style lang="scss">
#import "~themify-icons-scss/scss/themify-icons.scss";
#app {
...
And I can check the themify-icons in my component template
<h2>Test Themify Icons</h2>
<div class="row" style="margin-bottom: 30px;">
<div class="col-4"></div>
<div class="col-4">
<span class="ti-email"> Email Me</span>
</div>
</div>