How to use functions in grunt-sass? - sass

My current project uses grunt-sass to translate scss files into css.
According to the grunt-sass documentation, I can use any option that node-sass provides except for a few:
Options
See the node-sass options, except for file, outFile, success, error.
My project compiles two CSS files out of one SCSS file. The SCSS file has if-statements in it that provides different code depending on the value of the version variable ($bootstrap-version: 3; or $bootstrap-version: 4;).
I would like to use the node-sass option functions to return the bootstrap version to my SCSS files.
Here's my gruntfile. The functions: {...} path is how I would have expected the option to work.
// Compile Sass
sass: {
bootstrap3: {
options: {
style: 'expanded'
functions: {getBootstrapVersion: function(){return 3;}}
},
files: {
'dist/bootstrap3/css/my-bs3-variation.css': './bootstrap3/scss/my-bs3-variation.scss'
},
},
bootstrap4: {
options: {
style: 'expanded',
functions: {getBootstrapVersion: function(){return 4;}}
},
files: {
'dist/bootstrap4/css/my-bs4-variation.css': './bootstrap4/scss/my-bs4-variation.scss'
}
}
In the SCSS file, I call the function like this:
$bootstrap-version: getBootstrapVersion();
h1:before {
content: "Bootstrap " + $bootstrap-version + " Version of ";
}
However, when trying to compile it, I get the errir >> Error: error in C function getBootstrapVersion: A SassValue object was expected. for the line where I call the function.
The example in the node-sass documentation does not help me much. What did I do wrongly? Or are there any better examples out there that I just did not find?
I use grunt-sass version 2.0.0 and node-sass version 4.7.2

The functions must return a SassValue object.
Unfortunately, I don't believe it is possible to do that with grunt-sass alone because we need to wrap your returned numbers 4 and 3 in typing functions defined in node-sass (which grunt-sass does not expose...).
For example, with node-sass in javascript, one may use something like sass.types.Number(4) to declare 4 as a Number SassValue.
Here, sass is the node-sass import const sass = require('node-sass');, which is not available in grunt...

Related

Gatsby fails after using Sass files with '#use 'sass:color'

I'm setting up a Gatsby Project with gatsby-plugin-sass.
my gatsby-config.js file:
module.exports = {
plugins: [
'gatsby-plugin-resolve-src',
'gatsby-plugin-sass',
'gatsby-plugin-react-helmet',
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/assets/images`,
},
},
],
}
I have the following styles file structure:
|
|src
|-styles
|--base
|--- _variables.scss
|--components
|--- _Buttons.scss
|--- ...
|--main.scss
Im my _Buttons.scss file I'm importing like this:
#import '../base/variables';
#use 'sass:color as *;
When I'm trying to use the sass color functions like this (as specified on https://sass-lang.com/documentation/modules)
%buttons-focus {
background-color: color.adjust($primary-color, $alpha: -0.5);
}
I get the following Error:
Invalid CSS after "...nd-color: color": expected expression (e.g. 1px, bold), was ".adjust($primary-co"
In my main.scss I'm importing styles like this:
#import './components/Buttons';
Am I overseeing something?
I've tried changing up #use with #import, with no luck. For me it seems like the gatsby-sass-plugin is not aware of sass modules.
gatsby-plugin-sass uses node-sass under the hood. But in order to support built-in modules with #use, you need to configure gatsby-plugin-sass to use dart-sass instead. See below.
Built-In Modules - sass-lang
Only Dart Sass currently supports loading built-in modules with #use. Users of other implementations must call functions using their global names instead.
Alternative Sass Implementations - gatsby-plugin-sass
By default the node implementation of Sass (node-sass) is used. To use the implementation written in Dart (dart-sass), you can install sass instead of node-sass and pass it into the options as the implementation:
npm install --save-dev sass
gatsby-config.js
plugins: [
{
resolve: `gatsby-plugin-sass`,
options: {
implementation: require("sass"),
},
},
]

How do I mix promises and pipe in gulp?

In my project I compile multiple bundles from source files in nested directories using rollup.
I had a gulpfile with the following code, which worked fine:
function build_app_js(file, name) {
return gulp.src(file)
.pipe(sourcemaps.init())
.pipe(rollup({format:'iife'}))
.pipe(terser())
.pipe(rename(name + '.js'))
.pipe(rename({suffix: '.min'}))
.pipe(sourcemaps.write())
.pipe(gulp.dest(js_apps_dir))
}
// call the above for multiple sets of file+app_name
But then I changed one of the dependencies in my ES6 code which I accessed by relative path into an npm package, so it is now in node_modules. Rollup needs a plugin to resolve this, so I changed the above to this:
.pipe(rollup({plugins: [resolveNodeModules()], format:'iife'}))
However this simply does not work.
I consulted rollup's docs on gulp, and adapted the example to my case, so it now looks like this:
function build_app_js(file, name) {
return rollup.rollup({
input: file,
plugins: [
resolveNodeModules()
]
}).then(bundle => {
return bundle.write({
file: js_apps_dir + '/' + name + '.js',
format: 'iife',
sourcemap: true
});
});
}
This works, but has no minification step, and I don't know how to add one.
More generally, this is a totally different paradigm from using pipe(), and I do not know how to make both work together.
Do I try to add minification in the Promise syntax, or do I wrap the Promise function in such a way that I can use it with pipe?
Answering own question after 8 days.
Minification can be achieved via rollup plugins, such as rollup-plugin-terser.
You just need to be careful with how you import them:
var rollup = require('rollup');
var resolveNodeModules = require('rollup-plugin-node-resolve');
//var terser = require('rollup-plugin-terser'); // WRONG
var {terser} = require('rollup-plugin-terser'); // CORRECT
function build_app_js(file, name) {
return rollup.rollup({
input: file,
plugins: [
resolveNodeModules(),
terser()
]
}).then(bundle => {
return bundle.write({
file: js_apps_dir + '/' + name + '.js',
format: 'iife',
sourcemap: true
});
});
}
If you import it the wrong way, you will get a terser() is not a function type error, which is because it will have imported terser as a module.
It's a bit annoying that different rollup-plugins can't be imported the same way, but hey.

Is it possible to inject NODE_ENV param into sass file using Webpack & sass loader?

I'm trying to inject NODE_ENV into a sass file, so I can have different output for dev/prod env, and have a sass function that has a condition like that inside it:
#if (NODE_ENV == 'prod') {}
my webpack.config looks like this:
module: {
loaders: [{
test: /\.scss$/,
loader: "style-loader!raw-loader!sass-loader?includePaths[]=" + path.resolve(__dirname, "./node_modules/compass-mixins/lib")
}]
}
I tried passing a data parameter to the loader, but nothing that I tried worked.
will appreciate any help!
this is the code directly from sass-loader repo.
webpack.config.js
...
sassLoader: {
data: "$env: " + process.env.NODE_ENV + ";"
}
...
You can try the prependData option for sass-loader
{
loader: 'sass-loader',
options: {
prependData: '$env: ' + process.env.NODE_ENV + ';',
}
}
prependData also accepts a function which receives the loaderContext with which you can make it more dynamic. Once you have implemented this; you can :
#if ($env == 'prod') {}
More information here : https://webpack.js.org/loaders/sass-loader/#prependdata
Good Luck...
Took a look at the sass-loader source and it doesn't seem to support any options that would allow you to insert a variable -- so it won't be possible this way.
I don't think you'll be able to 'insert' an env/build-time variable into your Sass like this, but you could get the same behavior by creating multiple Sass files and requiring the one you want (from your js source) based on a condition. Note that webpack has limited ability to parse the logic in your code, however -- if you want to require something within a conditional, the conditional has to check a boolean value for it to work. So if you do if (NODE_ENV === 'production') {...} else {...} webpack will process all the requires in all those conditions. If you did something like if (IS_PROD_ENV) {...} else {...}, where the conditional value is a boolean, webpack will follow the conditional logic and only process the correct require. The UglifyJS plugin will remove the unneeded conditional branches for you.

Using Grunt.js to dynamically watch, and subsequently compile, a directory of SASS files into one CSS file

I'm brand new to Grunt.js, but I'm starting to get the hang of it. The main thing I'd like to do with it however, I can't seem to nail down.
My goal here, is to point grunt at a directory, and have it watch all of the matching files, and upon changes, compile them into a new single CSS file.
Here's my current gruntfile:
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
// CONFIG =========================/
pkg: grunt.file.readJSON('package.json'),
sass: {
dist: {
files: {
'assets/css/style.css' : 'assets/css/sass/*.scss'
}
}
},
watch: {
css: {
files: 'assets/css/sass/*.scss',
tasks: ['sass']
}
}
});
// DEPENDENT PLUGINS =========================/
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-sass');
// TASKS =====================================/
grunt.registerTask('default', ['watch']);
};
Thus far I've been using grunt-contrib-watch, and grunt-contrib-sass. I've tried compass, as well as directory import but I couldn't get either of them to do what I'm trying to do either.
At the end of the day, I'm really just trying to avoid writing an import file, both because source order isn't going to matter for the way I'm writing my SASS, and becuase I'd really like to know how to make this happen.
I'm not sure of a way to do exactly what you want to achieve by just using Sass and Grunt-Contrib-Sass but you can achieve something similar by using Sass-Globbing, a SASS plug-in that lets you import entire directories. To use the plug-in, you'd use the require option in Grunt-Contrib-Sass and you'd have it target a main styles.scss file that may look something like:
#import "vendor/*";
#import "modules/*";
#import "partials/*";
And then your grunt file would have something like:
sass: {
dist: {
options: {
require: 'sass-globbing'
},
files: {
'assets/css/style.css' : 'assets/css/sass/style.scss'
}
}
}

Sass source maps with minified file (grunt)

Using Sass with sourcemaps works fine for me with unminified CSS, but using my minified CSS it doesn't.
I'm guessing this might be because the references first get's built to the compiled css file, but then the minified version changes everything and references then fail, could that be it? If so, I still don't know what to do about it. Any help to find a solution would be much appreciated.
This is in my last line of my main *scss-file:
/*# sourceMappingURL=mytheme-full.css.map */
I'm thinking; If I just change to the following, it should work. But no!
/*# sourceMappingURL=mytheme-full-min.css.map */
This is from my Gruntfile.js:
cssmin: {
build: {
files: {
'sites/all/themes/mytheme/css/mytheme-full-min.css': 'sites/all/themes/mytheme/css/mytheme-full.css'
}
}
},
sass: {
dist: {
options: {
sourcemap: 'auto'
},
files: {
'sites/all/themes/mytheme/css/mytheme-full.css': 'sites/all/themes/mytheme/sass/mytheme-full.scss'
}
}
},
To date, grunt-contrib-cssmin doesn't support sourcemaps (see here and here).
However, both grunt-contrib-sass and grunt-autoprefixer support sourcemaps, so your best bet is probably to enable sourcemaps on those and use the unminified css for development and debugging. To enable sourcemaps in autoprefixer, just set:
options: {
map: true
}

Resources