How to overwrite grunt task options in grunt-cli? - javascript

I am using grunt-contrib-concat and I have a simple concat task / config like below
concat: {
options: {
sourceMap: true
},
vendor: {
src:['lib/**/*.js'],
dest: 'dist/scripts/vendor.js'
},
app: {
src:['app/**/*.js'],
dest: 'dist/scripts/app.js'
}
}
So when I am running above task through console I would like to be able to specify enable / disable sourceMap generation. Source map generation can take forever.
I tried below but none worked.
grunt concat:vendor --sourceMap=false
grunt concat --sourceMap=false
Thanks.

I know one way to do this, it requires you to write a custom task, it's easy.
// Leave your `concat` task as above
concat: ...
// and then define a custom task as below (out of `grunt.config.init` call)
grunt.registerTask('TASK_NAME', 'OPTIONAL_DESCRIPTION', function (arg) {
// CLI can pass an argument which will be passed in this function as `arg` parameter
// We use this parameter to alter the `sourceMap` option
if (arg) {
grunt.config.set('concat.options.sourceMap', false);
}
// Just run `concat` with modified options or pass in an array as tasks list
grunt.task.run('concat');
});
This is simple, you can customize this template as your wishes.
To use it, just use a ":" to pass extra parameter(s) in CLI like below:
$ grunt concat:noSrcMap
Basically you can pass anything as the parameter, it will be treated like a string (or undefined if no parameter passed).

Related

How to run unit tests in mtime order with Grunt and Mocha

So when you do TDD, do you wait it to run all tests till the one you're working on? It takes too much time. When I'm in rush, I rename test file to something like aaaaaaaaaaaaaaaa_testsomething.test.js so it's run first and I see errors asap.
I don't like this approach and I'm sure there's solution, but I can't find it. So what is the easiest way to run unit tests in mtime order with Mocha? There's -sort option, but it sorts files by name only. How can I sort them by modification time?
There's my Gruntfile.js:
module.exports = function(grunt) {
grunt.initConfig({
watch: {
tests: {
files: ['**/*.js', '!**/node_modules/**'],
tasks: ['mochacli:local']
}
},
mochacli: {
options: {
require: ['assert'],
reporter: 'spec',
bail: true,
timeout: 6000,
sort: true,
files: ['tests/*.js']
},
local: {
timeout: 25000
}
}
});
grunt.loadNpmTasks('grunt-mocha-cli');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('test', ['mochacli:local']);
grunt.registerTask('livetests', [ 'watch:tests']);
};
Note: it's not duplicate. I don't want to edit my tests or Gruntfile.js each time I save source code file. I'm asking about how to modify Grunt task so it runs tests from last modified *.test.js file first. Sort unit tests by mtime, as stated in Title.
Simple scenario: I open test1.test.js in editor, change it, hit Ctrl+B and it runs unit tests from test1.test.js then test4.test.js. I open test4.test.js, hit Ctrl+S, Ctrl+B and it runs tests from test4.test.js then test1.test.js
I'm thinking about some Grunt plugin to sort files first, so I can put its results there insted of 'tests/*.js' with grunt.config.set('mochacli.options.files', 'tests/recent.js,tests/older.js', ....); but I can't find anything I can use as middleware there, don't want to invent bycicle as I'm sure there's something for this implemented already.
don't want to invent bicycle as I'm sure there's something for this implemented already.
...Sometimes you have to ride the bicycle ;)
Solution
This can be achieved by registering an intermediate custom-task in your Gruntfile.js to perform the following dynamically:
Obtain filepaths for all unit test files (.js) utilizing grunt.file.expand with the appropriate globbing pattern.
Sort each matched filepath by the files mtime/modified-date.
Configure the mochacli.options.file Array with the chronologically sorted filepaths using grunt.config
Run the local Target defined in the mochacli Task using grunt.task.run
Gruntfile.js
Configure your Gruntfile.js as follows:
module.exports = function(grunt) {
// Additional built-in node module.
var statSync = require('fs').statSync;
grunt.initConfig({
watch: {
tests: {
files: ['**/*.js', '!**/node_modules/**', '!Gruntfile.js'],
tasks: ['runMochaTests']
}
},
mochacli: {
options: {
require: ['assert'],
reporter: 'spec',
bail: true,
timeout: 6000,
files: [] // <-- Intentionally empty, to be generated dynamically.
},
local: {
timeout: 25000
}
}
});
grunt.loadNpmTasks('grunt-mocha-cli');
grunt.loadNpmTasks('grunt-contrib-watch');
/**
* Custom task to dynamically configure the `mochacli.options.files` Array.
* All filepaths that match the given globbing pattern(s), which is specified
# via the `grunt.file.expand` method, will be sorted chronologically via each
* file(s) latest modified date (i.e. mtime).
*/
grunt.registerTask('runMochaTests', function configMochaTask() {
var sortedPaths = grunt.file.expand({ filter: 'isFile' }, 'tests/**/*.js')
.map(function(filePath) {
return {
fpath: filePath,
modtime: statSync(filePath).mtime.getTime()
}
})
.sort(function (a, b) {
return a.modtime - b.modtime;
})
.map(function (info) {
return info.fpath;
})
.reverse();
grunt.config('mochacli.options.files', sortedPaths);
grunt.task.run(['mochacli:local']);
});
grunt.registerTask('test', ['runMochaTests']);
grunt.registerTask('livetests', [ 'watch:tests']);
};
Additional Notes
Using the configuration above. Running $ grunt livetests via your CLI, then subsequently saving a modified test file will cause Mocha to run each test file in chronological order based on the files last modified date (I.e. The most recent modified file will run first and the last modified file will run last). This same logic applies when running $ grunt test too.
However, if you want Mocha to run the most recent modified file first, yet run the other files in normal order (I.e. by name), then the custom runMochaTests Task in the Gruntfile.js above should be replaced with the following logic instead:
/**
* Custom task to dynamically configure the `mochacli.options.files` Array.
* The filepaths that match the given globbing pattern(s), which is specified
# via the `grunt.file.expand` method, will be in normal sort order (by name).
* However, the most recently modified file will be repositioned as the first
* item in the `filePaths` Array (0-index position).
*/
grunt.registerTask('runMochaTests', function configMochaTask() {
var filePaths = grunt.file.expand({ filter: 'isFile' }, 'tests/**/*.js')
.map(function(filePath) {
return filePath
});
var latestModifiedFilePath = filePaths.map(function(filePath) {
return {
fpath: filePath,
modtime: statSync(filePath).mtime.getTime()
}
})
.sort(function (a, b) {
return a.modtime - b.modtime;
})
.map(function (info) {
return info.fpath;
})
.reverse()[0];
filePaths.splice(filePaths.indexOf(latestModifiedFilePath), 1);
filePaths.unshift(latestModifiedFilePath);
grunt.config('mochacli.options.files', filePaths);
grunt.task.run(['mochacli:local']);
});
If you're using mocha, you can set .only on the test you are interested in:
describe(function () {
// these tests will be skipped
});
describe.only(function () {
// these tests will run
})

Get var from config?

I have a custom task:
grunt.registerTask('testtask', 'Test Task', function() {
....
I call on default:
grunt.registerTask('default', ['testtask']);
But I want to be able to get a var from config inside the task, dependent if the task was called by grunt or grunt deploy.
So for grunt I was to get a path var on the local system and on grunt deploy I want an external path.
So inside the task I want to just be able to call something like this.data.path and dependent on whether deploy was called, the path will vary.
Grunt exposes this.name, which you can use to check which task was specifically invoked. In the example below, I've created a function buildTask(), and assigned it to two different tasks, default, and deploy. Within the build task, I'm using a ternary operator to set the path to one of two values, based on whether this.name matches deploy:
function buildTask() {
// if this.name matches 'deploy', set var path to 'foo/bar', else set to 'foo/baz'
var path = (this.name === 'deploy') ? 'foo/bar' : 'foo/baz';
// your code goes here
}
grunt.registerTask('default', buildTask); // path = "foo/baz"
grunt.registerTask('deploy', buildTask); // path = "foo/bar"

r.js optimizer - don't mangle function names

I am using r.js with uglify to minify and concatenate my scripts. I am getting some errors on a production site where the stack trace returned is unintelligible. I would like to temporarily turn off the mangling of function names (variable names are fine) and am having trouble working out how to do this as r.js wraps the configuration options that are passed to uglify.js
The uglify config section in my r,js build config looks like this
uglify: {
beautify: true,
indent_start: 0,
indent_level: 1,
}
I would like to add the
-nmf or --no-mangle-functions – in case you want to mangle variable names, but not touch function names. (from here)
If i add the line
uglify: {
beautify: true,
indent_start: 0,
indent_level: 1,
'--no-mangle-functions': true
}
It does nothing, as does 'no-mangle-functions': true.
How do I pass this option to uglify?
Trying to make uglified/mangled code readable kind of defeats it's purpose in the first place.
Probably, what you are after are source maps.
To generate source maps in Uglify just add these options:
uglify: {
options: {
sourceMap: 'build.min.map',
sourceMappingURL: 'build.min.map'
}
}
Map filename must mirror the final javascript filename:
uglified.js <=> uglified.map
From what i can see in the source code of r.js there is no direct differentiation between functions and variable names. But there is an option called no_functions which is actually passed to the uglify section where the default value is false
Passing of options:
https://github.com/jrburke/r.js/blob/master/dist/r.js#L25067
Defaulting no_functionsto false:
https://github.com/jrburke/r.js/blob/master/dist/r.js#L11492
I cannot test it right now, so i am only guessing. Maybe you can try this option

Grunt - Dynamically Set Config Before Running a Task

So here is my hypothetical config object for a hypothetical fooTask that does something (not relevant to question) to a bunch of JS files
grunt.initConfig({
fooTask: {
app1: 'app1/*.js',
app2: 'app2/*.js',
app3: 'app3/*.js'
}
});
As you can see, with this approach, I have to run fooTask 3 times with each app specified as a target:
grunt fooTask:app1
grunt fooTask:app2
grunt fooTask:app3
Needless to say this does not scale as either the number of apps increase or the number of such foo tasks increase as one has to C&P the same code over and over for each app.
So ideally what I would like to define is just one target with the name of the app passed in as a config variable
grunt.initConfig({
fooTask: {
dist: '<%=appName%>/*.js'
}
});
I would then like to call fooTask 3 times, one for each app, with the right app set as appName
var apps = ['app1', 'app2', 'app3'];
apps.forEach(function(app) {
var currAppName = app;
// Run fooTask but how do I specify the new currAppName config?
grunt.task.run('fooTask');
});
As from code above, I know I can run my fooTask using grunt.task.run but how do I set the appName config for my task?
Note that this question is similar to this other one that also does not have the right answer yet - Pass Grunt config options from task.run
Thanks a lot.
EDIT 2:
So nevermind the garbage below the first edit, leaving as example of what doesn't work. In my case it was really important to be able to set the value within a task at run-time so I settled on the file system. Perhaps it suits your needs.
grunt.initConfig({
someTask: {
someKey: fs.readFileSync('file.txt', { encoding: 'utf8' })
}
});
of course you can do the readFile outside of the task if you need a bunch of different app names.
EDIT:
Hmmm. I swear I had this working when I wrote this...but now it is not. Grunt just sees the extra arguments as additional unfound tasks.
I was trying to figure this out myself, finally a "duh" just moment happened - why not parse process.argv before grunt.initConfig?
module.exports = function(grunt) {
var sourcefile = process.argv[2] || 'default.js'; // <- this
grunt.initConfig({
uglify: {
main: {
src: sourcefile, // <- voila :)
dest: sourcefile.substring(0, sourcefile.length-3) + '.min.js'
}
}
});
grunt.loadNpmTasks('uglify');
grunt.registerTask('default', ['uglify']);
};
and use from command line:
grunt mykillerscript.js
I didn't even try to use grunt.option for the same reason that all the examples only showed directing which task is run, but I wouldn't be surprised if there is a more "grunt" way to do this.

GruntJS, Multiple Tasks and grunt.option

I am starting to use GruntJS for one of my projects.
I have easily accomplished to write a simple build script using simple aliases.
However my script contains many tasks that are basically the same, the only difference are some parameters like the source folder and destination folder.
For example:
sass: {
options:{
trace: true,
debugInfo: true,
style: 'compressed'
},
html: {
files: {
'build/html/css/main.css': 'sass/html.sass'
}
},
html2: {
files: {
'build/html2/css/main.css': 'sass/html2.sass'
}
},
html3: {
files: {
'build/html3/css/main.css': 'sass/html3.sass'
}
}
}
What I would like to achieve is to have only one task and then pass parameters (dest,src) to that task.
I tried to implement this using MultiTasks:
grunt.registerTask('sass2', 'Run all Sass compilation tasks.', function() {
var projects = ['html','html2','html3'];
projects.forEach(function(proj){
grunt.config.set('sass.files.dest', 'build/' + proj + '/css/main.css');
grunt.config.set('sass.files.src', 'sass/' + proj + '.sass');
grunt.log.writeln(grunt.config.get('sass.files.dest'));
grunt.log.writeln(grunt.config.get('sass.files.src'));
grunt.task.run('sass');
});
});
Grunt log outputs the right values for the params, however only the html3 sass gets compiled.
I do not understand why only one of the projects gets compiled and how could I fix this.
Perhaps there is another way to solve this problem. A better way. Maybe using templates?
Any kind of help or tips would be appreciated.
Thank you!
Only the last configuration is used because grunt.task.run queues them to run after the current task is complete. From the API:
Enqueue one or more tasks. Every specified task in taskList will be run immediately after the current task completes, in the order specified
What you have is:
Set config 1
Set config 2
Set config 3
Run three tasks using the currently active config, which is #3.
Instead, you can do something like this to dynamically create lots of config sections and then run those tasks:
grunt.config.set('sass.' + proj + '.files', [{
src: 'sass/' + proj + '.sass',
dest: 'build/' + proj + '/css/main.css'
}]);
and then call runtask with your newly-created section:
grunt.task.run('sass:' + proj);

Categories