How can I use environment variables in node-sass .scss files? - create-react-app

I want a configurable default color variable maintained in an .env file. For example I would want an .env file that contains
PRIMARY=red
And a _variables.scss file that contains
$primary: ${process.env.PRIMARY}
And an SCSS file that contains
.my-class { color: $primary; }
And components where I use the stylesheets that contains
import './MyComponent.scss';
Is this possible? If not, what alternatives would I have?
node v12.13.1
node-sass v4.13.1

Assuming you are using webpack and sass-loader (you will need to eject, which I think is worth it if you don't mind extra files and more dependency updating), modify your webpack.config.js file like this:
const { castToSass } = require('node-sass-utils');
module.exports = {
...
{
test: /\.scss$/,
use: [{
loader: "style-loader"
}, {
loader: "css-loader"
}, {
loader: "sass-loader"
options: {
functions: {
"env($variable)": variable => castToSass(process.env[variable]);
}
}
}]
}
Now you could make a file _variables.scss like this:
$primary: env('primary');
If this doesn't work or you need more flexibility, check out the article I based it off of and feel free to edit my answer.

Robert's answer was a good clue but it didn't work for me. I spent an hour fixing my Webpack config and I just want to share the result here so no one else wastes their time here.
You should create your function in the sass-loader's sassOptions object like below: (using node-sass-utils is not required)
// Import node-sass at the top of your config file
const nodeSass = require('node-sass');
// Add this object to the sass-loader options object
sassOptions: {
functions: {
"env($variable)": variable => {
const value = variable.getValue();
const envValue = process.env[value]
const sassValue = new nodeSass.types.String(envValue);
return sassValue;
}
}
}
Then you can use any env variables in your SASS files like below:
.my-class {
color: env('ENV_VAR_NAME');
}

Related

How to include appropriate SCSS file?

I'm using Vue and I have multiple stylesheets for different configuration. And I need to use only one of them (depending on the configuration).
I have config file with value type: "a", so that I need to include and use "a-setup.scss". I tried to include file in vue.config.js, but it doesn't support imports.
So the question is: what should I do to use certain scss file?
So, the solution I found is to use css additionalData prop inside the vue.config.js. Code:
const filename = process.env.VUE_APP_TEMPLATE;
const additionalData =
... +
`#import "src/assets/styles/setup/${ filename }.scss";`;
module.exports = {
css: {
loaderOptions: {
sass: { additionalData }
}
},
...
}

How do i exclude a directory with mocking files from webpack build with vue.config.js?

I have a directory called mock at root which contains mocking data that I use to run the app in development mode. I would like to exclude them when i build for production. I notice that it is being added into bundle whenever i run vue-cli-service build and it is bloating my app bundle size.
I am using vue-cli and so I have to work with vue.config.js.
It is not clear from the docs or any answers on the wider web how I can specify which folders/files to exclude from the build.
Here is a snippet of my vue.config.js.
module.exports = {
chainWebpack: (config) => {
config.resolve.symlinks(false)
},
configureWebpack: {
plugins: [
new CompressionPlugin()
]
},
css: {
loaderOptions: {
scss: {
prependData: `#import "#/styles/main.scss";`
}
}
}
}
This is not the perfect solution, but...
If you want to exclude that directory at build time, you can try to use require instead of import. Something like this (source):
if (process.env.VUE_APP_MY_CONDITION) {
require('./conditional-file.js');
}
But be aware of this!

VueJS build started throwing Error: Conflict: Multiple assets emit to the same filename

My app used to work fine until I updated VueJS this morning. Now when I build, it shows the following error:
Error: Conflict: Multiple assets emit to the same filename img/default-contractor-logo.0346290f.svg
There's only one file like this in the repo.
Here's my vue.config.js:
module.exports = {
baseUrl: '/my/',
outputDir: 'dist/my',
css: {
loaderOptions: {
sass: {
data: `
#import "#/scss/_variables.scss";
#import "#/scss/_mixins.scss";
#import "#/scss/_fonts.scss";
`
}
}
},
devServer: {
disableHostCheck: true
}
};
I tried webpack fixes recommended in similar cases, but non helped.
I had the same error when importing SVG files using dynamically generated path in the require statement:
const url = require("../assets/svg/#{value}");
<img src={{url}} />
In this case file-loader processes all SVG files and saves them to the output path. My file-loader options were:
{
loader: "file-loader",
options: { name: "[name].[ext]" }
}
The folders structure has duplicate file names, something like this:
assets
|__ one
|____ file.svg
|__ two
|____ file.svg
In this case file-loader saves both file.svg files to the same output file: build/assets/file.svg - hence the warning.
I managed to fix it by adding [path] to the name option:
{
loader: "file-loader",
options: { name: "[path][name].[ext]" }
}
The answer by #ischenkodv is definitely correct, but because of my inexperience with webpack, I needed a little more context to use the information to fix the problem.
For the benefit of anyone else in the same situation, I'm adding the following details which I hope will be useful.
This section of the Vue.js documentation was particularly helpul:
VueJS - Modifying Options of a Loader
For the TL;DR fix, here is the relevant chunk of my vue.config.js:
// vue.config.js
module.exports = {
// ---snip---
chainWebpack: config =>
{
config.module
.rule('svg')
.test(/\.svg$/)
.use('file-loader')
.tap(options =>
{
return { name: "[path][name].[ext]" };
});
}
// ---snip---
};
In my project it was the flag-icon-css NPM package that was causing the Multiple assets emit to the same filename conflict errors. The above update to the vue.config.js file resolved the problem for me.
I suspect that the regular expression in the test could be tightened up to target just the items in the flag-icon-css package rather than matching all SVG files, but I haven't bothered since it's not causing any adverse effects so far.

Webpack, Scss - add cdn server path before compilation

How do I add a cdn path variable into the styles.scss before compilation? I have a webpack 4 configuration that works fine for JS. The JS/CSS files are loaded correctly from the desired cdn url.
What I want to achieve is, that the project running on localhost will use a different cdn url for images/icons/fonts than the same web running on production.
My webpack config has these lines for setting the cdn domain:
if (process.env.NODE_ENV === 'production') {
webUrl = 'https://cdn.project.com/';
}
else if (process.env.NODE_ENV === 'development') {
webUrl = 'http://localcdn.localhost/';
}
This code works well for JS/CSS files, but CSS always loads backgrounds/fonts using a relative path, which of course is the main domain and not the cdn.
Maybe just open the styles.scss and update the file manually before webpack compilation? Isn't there a better way?
After hours of trial and error I found this solution for webpack 4. It may help someone trying to solve the same issue.
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options : {
url : false,
sourceMap: devMode
}
},
{
loader: 'sass-loader',
options : {
processCssUrls : false,
sourceMap: devMode,
data : "$cdnFolder: 'http://cdn.myweb.net/';"
}
}
],
},
This is a part of webpack.config.js, in the section:
module : {
rules : {
This configuration makes the scss variable $cdnFolder available in every scss file. CDN link can be changed based on the current mode dev/prod/stage.
For webpack 5, the config changed a little:
{
loader: 'sass-loader',
options : {
additionalData : "$cdnFolder: '" + cdnUrl + "';"
}
}

Setting global sass variables in a vue project

I created a vue.config.js file to set some global sass variables (just like the documentation specifies), however when trying to access the variables in a component, I get an undefined error. Adding the same import statement manually in the component works, but somehow it's not being picked up from inside the vue.config.js file. I checked that I have node-sass and sass-loader installed and that the vue.config.js is in the project root (next to the package.json). What am I missing?
module.exports = {
css: {
loaderOptions: {
sass: {
data: `
#import "#/assets/styles/_variables.scss";
`
}
}
}
}
Change data to prependData
module.exports = {
css: {
loaderOptions: {
sass: {
prependData: `
#import "#/assets/styles/_variables.scss";
`
}
}
}
}
You can read more about this here: pre-loader docs
this code is for vue3
install:
Sass
npm install -D sass-loader#^10 sass
fibers
npm install -D fibers
Style Resources Loader
npm i style-resources-loader -D
in your vue.config.js (root level project)
module.exports = {
css: {
loaderOptions: {
sass: {
additionalData: `
#import "#/assets/styles/scss/_variables.scss";
`
}
}
}
}enter image description here
symbol # means that your file start from src folder
npm install --save-dev node-sass sass-loader
Create file 'vue.config.js' in the root
Add code below. Remember - # refers to /src folder
module.exports = {
css: {
loaderOptions: {
sass: {
data: '#import "#/assets/css/variables/_colors.scss";'
}
}
}
}
Restart the project
It's hard to troubleshoot without the full context, so here are several options you can try:
Ensure you've restarted the dev environment since changing the config (re-run yarn dev or npm run dev)
Keep the template literal to one line, as is used in docs. This shouldn't make a difference, but it might. (e.g. data: `#import "#/assets/styles/_variables.scss";`)
As you probably know, the underscore in front of a sass file denotes a sass partial. A partial is not used in the example, so it is possible that this has an effect as well. (e.g. rename _variables.scss to variables.scss and use data: `#import "#/assets/styles/variables.scss";`)
Ensure that the sass-loader, node-sass, and css-loader packages are up to date.
Try using the path without the slash after the #. e.g. #assets/styles/_variables.scss.
Try with a ~ instead of the #. e.g. ~assets/styles/_variables.scss. If nothing else has worked, try replacing the # with src as well.
Good luck!