gruntjs - command line arguments - gruntjs

Where can I get a handle for command line arguments?
eg grunt dist --env=UAT. How do I get the value for env?
While I'm at it, how would I assign a default value to this if it's not set on the command line?

You can use grunt.option() or more specifically:
var env = grunt.option('env') || 'default';
to grab the env argument or default to the string 'default' if the argument is not present.

I find the handling of defaults in grunt to be sorely lacking. The method outlined above works, but it quickly gets tiresome when you have lots of options.
A little helper function can ease this:
function defaultOptions(options) {
for(var key in options) {
if(options.hasOwnProperty(key) && !grunt.option(key)) {
grunt.option(key, options[key]);
}
}
}
You can then use like:
defaultOptions({
env : "staging"
});
And at the CLI:
grunt // { env : "staging" }
grunt --env=UAT // { env : "UAT" }

Related

Getting TestCafe to recognize dotenv variables

I might be mixing up concepts, but I'd read that it's possible to get TestCafe to recognize variables of the form process.env.MY_COOL_VARIABLE. Also for my Vue.js frontend (built using Vue-CLI, which uses dotenv under the hood), I found I could make a file in .env.test for test values like so:
VUE_APP_MY_COOL_VARIABLE
which I would then access in my test code like so:
test('my fixture', async (t) => {
...
await t
.click(mySelector.find('.div').withText(process.env.VUE_APP_MY_COOL_VARIABLE));
...
}
However, I get the following error:
"text" argument is expected to be a string or a regular expression, but it was undefined.
Seems like my environment variables aren't getting picked up. I build my code like so: vue-cli-service build --mode test.
TestCafe doesn't provide support for .env files out of the box. You can create a test file that will require the dotenv module and load your configuration file:
// enable-dotenv.test.js
require('dotenv').config({ path: '.my.env' });
testcafe chrome enable-dotenv.test.js tests/
Here's how I solved my issue. When debugging, I did a console.log of process.env and noticed that the variable that vue recognizes wasn't visible during testcafe's run. From our package.json:
"test:ui:run": "VUE_APP_MY_COOL_VARIABLE=ui-test yarn build:test && testcafe -a ../ui-test-server.sh chrome",
Also this bit of javascript is run by both the test and mainline code, so I had to use a conditional.
import * as dotenv from 'dotenv';
if (process.env.npm_package_scripts_test_ui_run) { // are we running a testcafe script
dotenv.config({ path: '.env.test' });
}
Have you tried process.env[VUE_APP_MY_COOL_VARIABLE]? It's worth noting that everything in dotenv comes back as a string so you may need to do the casting yourself. For example:
function getEnvVariableValue(envVariable: string) {
// Cast to boolean
if (envVariableValue.toUpperCase() === "TRUE") {
return true;
} else if (envVariableValue.toUpperCase() === "FALSE") {
return false;
// Cast to number
} else if (!isNaN(Number(envVariableValue))) {
return Number(envVariableValue);
} else {
return envVariableValue;
}
}
You can also try creating a .env file in the root folder to see if it picks it that way. I use dotenv in my project directly by including it in the package.json as a dependency and it works this way.

How to get a variable from another file in gruntfile

I am new to grunt.
My requirement is that I need to uglify my files based on the variable set on another file.
Let's say I have some abc.js file in which I have a variable var is_uglify = true;
I need to read the value of the variable in gruntfile and perform the build accordingly.
If variable is true, I need to do:
grunt.registerTask('build', ['clean:build','uglify']);
otherwise
grunt.registerTask('build', ['clean:build']);
Can anyone help with this?
This can be achieved by:
Defining a Custom Task - namely getValue in the gist below.
In the getValue Task/Function utilize grunt.file.read to obtain the contents of abc.js.
Then use a regex to match the Boolean value assigned to the variable is_uglify.
Once the Boolean value for is_uglify is obtained then conditionally run either the registered Task named buildA or buildB using grunt.task.run
Gruntfile.js
The following gist shows how this can be achieved:
module.exports = function (grunt) {
'use strict';
grunt.initConfig({
uglify: {
// ... <-- Define as necessary.
},
clean: {
build: {
//... <-- Define as necessary.
}
}
});
grunt.registerTask('getValue', 'Gets the value of a variable', function () {
var filepath = "path/to/abc.js", // <-- Define as necessary.
content = grunt.file.read(filepath),
value = content.match(/(?:var is_uglify = )(true|false);/);
if (!value) {
grunt.fail.warn(
'\'var is_uglify = [true|false];\' not found in: ' + filepath['yellow']
)
}
if (value[1] === 'true') {
grunt.task.run(['buildA']);
} else {
grunt.task.run(['buildB']);
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.registerTask('build', ["getValue"]);
grunt.registerTask('buildA', ['clean:build','uglify']);
grunt.registerTask('buildB', ['clean:build']);
};
Additional Notes
In the following line of code the "path/to/abc.js" part will need to be redefined as per your directory structure. This must be the path to your file containing var is_uglify = ...;:
var filepath = "path/to/abc.js",
An explanation to the regex pattern /(?:var is_uglify = )(true|false);/ can be found here. It will find the first match only in abc.js.
The getValue Task/Function will warn if the is_uglify variable is missing and the Task/function simply fails and returns early.
After you have defined the configuration for the uglify and clean:build Task run $ grunt build via your CLI.

how to pass file as argument to gulp task

I need some help regarding my gulp task.
I have gulp karma task and I want to pass karma config file as a argument to that task.
I am able to achieve this grunt. Like in grunt , we can use
grunt.option("file")
and we can called grunt task as
grunt taskName --file=myFileName
So How Can I achieve same with gulp?
There's nothing in gulp to parse command line arguments for you.
You can use node's global process.argv directly if you feel like it.
I like to use the yargs module to handle arguments in my gulpfiles.
var args = require('yargs').argv;
var karmaFile = argv.file; // for the argument --file="myFileName"
Adding to #kombucha's answer, here's an example of how to use process.argv. If in your case you passed:
gulp taskName --file myFileName
(without the '=' ) then the below would retrieve the file value:
var filename, i = process.argv.indexOf("--file");
if(i>-1) {
filename = process.argv[i+1];
}
It should be simple enough to turn the above into a generic function for retrieving command-line values. I have used this before to good effect:
function getCLValue(name) {
var i = process.argv.indexOf(name);
return (i>-1) ? process.argv[i+1] : null;
}

How to Gulp-Watch Multiple files?

I have something like this:
gulp.task('default', ['css', 'browser-sync'] , function() {
gulp.watch(['sass/**/*.scss', 'layouts/*.css'], function() {
gulp.run('css');
});
});
but it does not work, because it watches two directories, the sass and the layouts directory for changes.
How do I make it work, so that gulp watches anything that happens inside those directories?
gulp.task('default', ['css', 'browser-sync'] , function() {
gulp.watch(['sass/**/*.scss', 'layouts/**/*.css'], ['css']);
});
sass/**/*.scss and layouts/**/*.css will watch every directory and subdirectory for any changes to .scssand .css files that change. If you want to change that to any file make the last bit *.*
You can write a watch like this.
gulp.task('watch', function() {
gulp.watch('path/to/file', ['gulp task name for css/scss']);
gulp.watch('path/to/file', ['gulp task name for js']);
});
This way you can set up as many tasks as you want via the file path of what you want to watch followed by the name of the task you created. Then you can write your default like this:
gulp.task('default', ['gulp task name for css/scss', 'gulp task name for js']);
If you want to simply watch for various file changes, then just watch files using glob like *.css in your task.
One problem that has arisen for multiple people (including me) is that adding a gulp.filter outside of the task causes gulp.watch to fail after the first pass. So if you have something like this:
var filter = gulpFilter(['fileToExclude.js'])
gulp.task('newTask', function(){ ...
Then you need to change it to:
gulp.task('newTask', function(){
var filter = gulpFilter(['fileToExclude.js'])
The filter has to be included in the task function. Hope that helps someone.
This works for me (Gulp 4):
function watchSass() {
return gulp.watch(sassGlob, { ignoreInitial: false }, buildCss)
}
function watchImages() {
return gulp.watch(imagesGlob, copyImages)
}
exports.watch = gulp.parallel(watchSass, watchImages)
#A.J Alger's answer worked for me when using Gulp v3.x.
But starting with Gulp 4, The following appears to work for me.
Notice that each task has to return a value or call "done()". The main task in this example is 'watchSrc' which in parallel calls the other tasks.
gulp.task('watchHtml', function(){
return watch('src/**/*.html', function () {
gulp.src('src/**/*')
.pipe(gulp.dest(BUILD_DIR))
})
})
gulp.task('watchJS', function(){
return watch('src/**/*.js', 'devJS')
})
gulp.task('watchCSS', function(){
return watch(['src/**/*.css', 'src/**/*.scss'], 'buildStyles')
})
gulp.task('watchSrc', gulp.parallel('watchHtml', 'watchJS', 'watchCSS'), function(done)
{
done()
})
As of gulp 4, this is another option:
const { watch, series, parallel } = require('gulp');
function watchTask(cb) {
// this will execute all task on any changes
watch(['src/**/*'],
series(parallel(jsTask, htmlTask, assetTask),
));
// this will run specific task based on file type or folder
watch(['src/**/*.js'], series(jsTask));
watch(['src/**/*.html'], series(htmlTask));
watch(['assets/**/*'], series(assetTask));
}
exports.default = series(parallel(jsTask, htmlTask, assetTask), watchTask);
If you convert your tasks into functions
function task1(){
return gulp...
...
}
There are then 2 useful methods you can use:
GULP.SERIES will run the tasks synchronously
gulp.task('default', gulp.series(task1,task2));
GULP.PARALLEL will run them asynchronously
gulp.task('default', gulp.parallel(task1,task2));

How to pass options to handlebars.precompile?

So I've got this code that is running fine (I'm using gulp not grunt for what it matters) :
var handlebars = require('handlebars'),
rename = require('gulp-rename'),
map = require('vinyl-map');
gulp.task('test', function(){
return gulp.src(config.path.template+"/*.handlebars")
.pipe(map(function(contents) {
return handlebars.precompile(contents.toString());
}))
.pipe(rename({ extname: '.js' }))
.pipe(gulp.dest(config.path.template+"/test"))
});
Everything runs perfectly, the .js files generate in the good folder, but I need them to generate without the -s parameter. For an example, when I run handlebars path/to/my/hbs.handlebars -f path/to/my/out/folder.js -s (or --simple), the file generated is the same. But I need this command to run without the -s parameter, and I can't find a way to pass this argument in my gulpfile. I tried alot of things, in a String, in a Json, in an array, tried to go with -s false, with simple false, with isSimple false (something I found in handlebars code).
None of this is working and I really need to pass the -s parameter to false. I assume that I need to do something like :
[...]
return handlebars.precompile(contents.toString(), options);
[...]
But I can't find the proper syntax or way to use these options. And that is my problem.
PS : I use this instead of gulp-handlebars so that I can use the version of handlebars I want to use and not another.
EDIT
Searching in handlebars.js code, I just found that options is an object, but I can't find what he's filled with as I'm not a good javascript user.
It seems to me there's no such switch in the source.
Taken from the precompiler source used from the command line tool:
if (opts.simple) {
output.push(Handlebars.precompile(data, options) + '\n');
} else if (opts.partial) {
if (opts.amd && (opts.templates.length == 1 && !fs.statSync(opts.templates[0]).isDirectory())) {
output.push('return ');
}
output.push('Handlebars.partials[\'' + template + '\'] = template(' + Handlebars.precompile(data, options) + ');\n');
} else {
if (opts.amd && (opts.templates.length == 1 && !fs.statSync(opts.templates[0]).isDirectory())) {
output.push('return ');
}
output.push('templates[\'' + template + '\'] = template(' + Handlebars.precompile(data, options) + ');\n');
}
opts is what you passed in, another variable options is passed to precompile. The decoration is added by the command line tool.
There's a second block with closing brackets a few lines below in the script.
You best copy that source to your code or maybe access the .cli object in your gulp script.

Resources