Grunt watch - detecting success and failure of tasks - gruntjs

UPDATE
I'm currently using a solution similar to that described here for error notifications, and the 'Current workaround' below (without modifying the grunt force option) for success notifications.
ORIGINAL QUESTION
I'm having trouble determining when a sub-task run by the grunt-contrib-watch has finished (successfully or not).
Specifically, I'm using grunt-contrib-coffee and grunt watch to compile my CoffeeScript files as they change. The compilation is working fine.
What I would like to do is notify myself of the status of the compilation. Here's what I've tried (all code in CS):
Current workaround
From SO Question (How can I make a Grunt task fail if one of its sub tasks fail?)
What I don't like: setting and restoring a global option seems clunky, especially since it's happening in different tasks / event handlers. Also, I have to delete the destination file every time.
Without setting the global option, I can notify a successful compilation, which is great, but I would like to notify a failed one as well.
grunt.initConfig
watch:
options: nospawn: true
coffee:
files: '<%= coffee.dev.cwd %>/<%= coffee.dev.src %>'
options:
events: ['changed', 'added']
coffee:
dev:
expand: true
cwd: 'app'
src: '**/*.coffee'
dest: 'public'
ext: '.js'
grunt.registerTask 'completedCompile', (srcFilePath, destFilePath) ->
grunt.option 'force', false
if grunt.file.exists( destFilePath )
# notify success
else
# notify failure
grunt.event.on 'watch', (action, filepath) ->
if grunt.file.isMatch grunt.config('watch.coffee.files'), filepath
filepath = # compose source filepath from config options (omitted)
dest = # compose destination filepath from config options (omitted)
if grunt.file.exists( dest )
grunt.file.delete dest # delete the destination file so we can tell in 'completedCompile' whether or not 'coffee:dev' was successful
grunt.option 'force', true # needed so 'completedCompile' runs even if 'coffee:dev' fails
grunt.config 'coffee.dev.src', filepath # compile just the one file, not all watched files
grunt.task.run 'coffee:dev'
grunt.task.run 'completedCompile:'+filepath+':'+dest # call 'completedCompile' task with args
Another option (so slow)
As suggested by another SO Question (Gruntfile getting error codes from programs serially), I used grunt.util.spawn.
This worked, but it was pretty slow (several seconds every time the CS file is saved).
grunt.event.on 'watch', (action, filepath) ->
if grunt.file.isMatch grunt.config('watch.coffee.files'), filepath
filepath = # compose source filepath from config options (omitted)
dest = # compose destination filepath from config options (omitted)
if grunt.file.exists( dest )
grunt.file.delete dest # delete the destination file so we can tell in 'completedCompile' whether or not 'coffee:dev' was successful
grunt.util.spawn {
grunt: true # use grunt to spawn
args: ['coffee:dev']
options: { stdio: 'inherit' } # print to same stdout
}, -> # coffee:dev finished
if grunt.file.exists( dest )
# notify success
else
# notify error
Other attempts
I tried a slew of things.
grunt.fail.errorcount (when used in the 'completedCompile' task) is non-zero if previous compilations have failed. (Is it safe to manually reset this to zero? If yes, I wouldn't have to delete the dest file every time.) Even so, this requires setting the global option 'force' to true.
Anything involving specifying the 'watch.coffee.tasks' options in grunt.initConfig doesn't work because the 'coffee:dev' task is run after the 'watch' event handler has completed.
grunt.task.current always refers to the 'watch' task, of course
If you've made it this far, thanks for reading :).

I've also been having the same issue, trying to figure out when a watch sub task is complete.
Part of the problem it seem's is that Watch by default will spawn a new Grunt process to run the sub task. So your main Grunt process won't know about the tasks finishing. You can set 'nospawn' but this doesn't help much as watch doesn't expose the sub task itself.
The nearest I could get to this was using Grunt.util.hooker (inspired by Grunt Notify) to react when the Grunt fail 'report' method is called.
grunt.util.hooker.hook(grunt.fail, 'report', function(){});
However this contains no information on the actual task completed, which would be helpful if you wanted to do something based on specific sub tasks within your watch task.
Looking at the Grunt Watch github there seems to be some traction to implement a complete/fail event:
https://github.com/gruntjs/grunt-contrib-watch/issues/131

I think Grunt-Notify would do what you are looking for.

Related

Grunt error task "default" not found

When trying to run grunt, I get an error message:
Warning: Task "default" not found. Use --force to continue.
Aborted due to warnings.
I have already found several posts on this topic, and in each of them the problem was a missing comma. But in my case I have no idea what's wrong, I think I didn't miss any comma (btw, this content was copy/pasted from the internet).
module.exports = (grunt) => {
grunt.initConfig({
execute: {
target: {
src: ['server.js']
}
},
watch: {
scripts: {
files: ['server.js'],
tasks: ['execute'],
},
}
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-execute');
};
What could be the problem?
You didn't registered the default task. Add this after the last loadNpmTask
grunt.registerTask('default', ['execute']);
The second parameter is what you want to be executed from the config, you can put there more tasks.
Or you can run run existing task by providing the name as parameter in cli.
grunt execute
With you config you can use execute and watch. See https://gruntjs.com/api/grunt.task for more information.
If you run grunt in your terminal it is going to search for a "default" task, so you have to register a task to be executed with Grunt defining it with the grunt.registerTask method, with a first parameter which is the name of your task, and a second parameter which is an array of subtasks that it will run.
In your case, the code could be something like that:
...
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-execute');
grunt.registerTask("default", ["execute", "watch"]);
...
In this way the "default" task will run rispectively the "execute" and the "watch" commands.
However here you can find the documentation to create tasks with Grunt.
Hope it was helpful.

Less Middleware with Express

I am trying to setup less on my nodejs project and when i request my page from browser less-middleware debug show me wrong path and dest path. When i ask for example '/public/css/index.css' from my webpage i adds '/public/css/' to the source path. Below are mine configures:
app.use(lessMiddleware(__dirname+'/server/less',{
debug: true,
dest: __dirname+'/public/css',
once: true
}));
And debug screen shows:
pathname : /public/css/index.css
source : D\Work\project\server\less\public\css\index.less
destination : D\Work\project\public\css\public\css\index.css
Even if i fix the destination path, i am having problem with source and i dont want to put my less files into public folder. Is there a way of remove that /public/css from compiled less.
I have tried to add:
'preprocess.path': function(pathname, req){
console.log(pathname);
}
but console.log never shows. Also perhaps there is a way to not compile every css file users ask and just compile those less files which i have in less folder.
P.S. every module i am using is under "*" version.
You might try this (accessing "/css/style.css" within your browser):
app.use(lessMiddleware({
src: __dirname+"/server/less",
dest: __dirname+"/public/css",
prefix: "/css",
// force true recompiles on every request... not the
// best for production, but fine in debug while working
// through changes
force: true,
debug: true
}));
Actually it was simple enough.
app.use(lessMiddleware(__dirname+'/server/less/',{
debug: true,
dest: __dirname,
force: true
}));
So now in the folder /server/less we create folder public/css and everything is pulling in right way. Also if we have folder admin/css it works too. Trick was to create folders in folder /server/less

How to run a command in child process in grunt tasks?

I want to write a grunt task, which will start a server, and run some tests based on that server:
grunt.initConfig({
shell: {
sbtRun: {
options: {
stdout: true
},
command: './sbt run'
}
}
});
grunt.loadNpmTasks('grunt-shell');
grunt.registerTask('run-test-only', ...);
grunt.registerTask('start-server-and-test', ['shell:sbtRun', 'run-test-only']);
But the problem is, the task ./sbt run is not running in daemon. When I run:
grunt start-server-and-test
It will blocking in the shell:sbtRun task forever.
Is there any way to start it in a child process? So the second task run-test-only will be called, and the server will be destroyed automatically after testing?
I was facing a similar issue. I wanted to connect to a database and store the result of a query in a .csv. I created a separate file to do all that work and I was successful in running that file through the command line using:
node filename.js
I used grunt-shell and grunt-exec to run this command but everytime I saw the same error:
const child = require('child_process').exec;
Then, I found another grunt plugin i.e grunt-execute which was able to create a child process and the task was completed successfully. You could find more about grunt-execute here, https://www.npmjs.com/package/grunt-execute.
Hope this helps.

grunt-contrib-clean task doesn't delete files with network src path

I faced a trouble attempting to clean distributive directory on remote file server.
I have a folder and access it via double slash notation like
//foldername/projectname/subfolder/
I've configured my Gruntfile.js for grunt-contrib-clean task like this:
clean: {
files: [
{
src: ['//foldername/projectname/subfolder/']
}
]
}
In my tasks list I also have a grunt-contrib-copy task which launches after grunt-contrib-clean to load distributive and this task uses the same src path to folder on network.
So when i launch the grunt - "clean" task actually doesn't clean the subfolder but the "copy" succesfully copies files.
When I try to launch grunt with --verbose command i have a next log:
Running "clean" (clean) task
Verifying property clean exists in config...OK
Files: [no src]
Options: force=false, no-write=false
Options: force=false, no-write=false
So it seems that task can't find the files but the reason is undefined.
Access to server is authorized and i'm enter credentials via default Windows prompt. And I have credentials for changing/deleting folder.
Can anybody help me and answer why the clean task doesn't find my directory.
Thanks!
It does. Here is an example:
dev: {
options: {
'force': true
},
expand: true,
cwd: '//someserver/someshare/somepath',
src: '{,*/}*'
},
Use force=true to go clean the subfolder:
force
Type: Boolean
Default: false
This overrides this task from blocking deletion of folders outside current working dir (CWD). Use with caution.
and no-write=true to get verbose output.
Also, check the version to see if the Windows bug has been fixed:
2013-07-15   v0.5.0   Use rimraf directly, version 2.2.1 to fix issue on Windows. Add no-write option to mimic grunt.file.delete behavior.
References
npmjs: grunt-contrib-clean
grunt-contrib-clean: Failing on Windows 8

Grunt uglifym - call stack size exceeded

I am trying to use uglify with grunt to concat and minify some files. I have already used the npm to install grunt-contrib-uglify.
I have the following in my grunt.js file: (I have removed some other tasks for anonymity)
module.exports = function(grunt) {
'use strict';
grunt.loadNpmTasks('grunt-contrib-uglify');
uglify: {
options: {
sourceMap: 'app/map/source-map.js'
},
files: {
'app/dist/sourcefiles.min.js': [
'app/test_js/test.js'
]
}
}
};
I then run:
grunt uglify
but I keep getting the following error:
Warning: Maximum call stack size exceeded Use --force to continue.
If I use force, the grunt task never stops running.
Can someone tell me where I am going wrong? I am tearing my hair out on this one.
I had the same problem, using an other Grunt plugin called recess.
The error message was not explicit.
Warning: Cannot read property 'message' of undefined Use --force to continue.
But the verbose mode showed that my task was called hundred of times.
The problem was that I created a "cyclic dependency" (causing an infinite loop) when I registered my task.
grunt.registerTask('recess', ['recess']); //does not work => cyclic dependency!
The first parameter of registerTask method is an "alias task" and has to be different from the task names defined in the second parameter.
I corrected like this:
grunt.registerTask('my-recess-task', ['recess']);
And I runned the task calling this (in the Command prompt window)
grunt my-recess-task
And then it was OK!
More about registerTask() method, from grunt API:
http://gruntjs.com/api/grunt.task#grunt.task.registertask
I also met this problem, i solved this by removing
grunt.registerTask('uglify', ['uglify']);
before i solved this, i ran grunt uglify -v to check what happend.
I found it because that where you using this grunt.loadNpmTasks('grunt-contrib-uglify'); ,it implicitly executes the grunt.registerTask('uglify', ['uglify']); ^_^

Resources