29

I feel like I'm missing something.

Here is what I want to achieve :

Having a grunt task that executes my server.js and runs watch task in parallel. It feels to me that this is precisely one of the tasks grunt was designed for but I can't achieve this configuration.

Among others, I have read this : Running Node app through Grunt but I still can't make it.

Here is my Gruntfile.js :

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    watch: {
      scripts: {
        files: ['*.js'],
        tasks: ['start'],
        options: {
          nospawn: true
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-watch');

  grunt.registerTask('start', function() {
    grunt.util.spawn({
      cmd: 'node',
      args: ['server.js']
    });
    grunt.task.run('watch');
  });

  grunt.registerTask('default', 'start');
};

I have "grunt-contrib-watch": "~0.3.1" which should be higher version than grunt-contrib-watch@0.3.0 as in the previously mentioned post.

If you could help me achieve the proper configuration, I would be extremely grateful. But more in general, I don't understand why there is no official grunt-contrib-nodemon-like package and task since I have the feeling it would be another great reason to use grunt (which I really like as a tool !)

Thanks

Community
  • 1
  • 1
Augustin Riedinger
  • 20,909
  • 29
  • 133
  • 206

3 Answers3

28

Edit: grunt-nodemon

since writing this, a nice person developed that.


I was having a lot of trouble using grunt.util.spawn to fire off new processes. They would run, but they wouldn't give me any output back. Perhaps you can figure out what I could not in these docs. http://gruntjs.com/api/grunt.util#grunt.util.spawn

Two problems I see with what you have:

  • I think grunt.registerTask() has to take three arguments when you use a callback function to run your task.
  • I don't think you can just call node server.js over and over again everytime a file changes. It will work on the first time, for it to really work you'd have to manage the server as a child process, killing and restarting it on subsequent file changes.

For the registerTask arguments try this, just to see if you can get something to work in your current implementation.

http://gruntjs.com/api/grunt.task#grunt.task.registertask

It takes (taskName, description, taskFunction) like so:

grunt.registerTask('start', 'My start task description', function() {
  grunt.util.spawn({
    cmd: 'node',
    args: ['server.js']
  });
  grunt.task.run('watch');
});

That might at least get your watch to run node server.js the first time a file changes.

Here's what I would do instead.

Either just use nodemon $ nodemon server.js as is

or...

Read the source and use grunt-develop

He is managing the server as a child process, might be what you're looking for.

or...

Get grunt-shell
npm install grunt-shell --save-dev

And use it to run nodemon for you:

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    serverFile: 'server.js',
    shell: {
      nodemon: {
        command: 'nodemon <%= serverFile %>',
        options: {
          stdout: true,
          stderr: true
        }
      }
    },
    watch: { /* nothing to do in watch anymore */ }
  });

  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-shell');

  grunt.registerTask('default', 'shell:nodemon');
};

$ grunt shell:nodemon

I sincerely hope that helps. Good luck!

nackjicholson
  • 4,557
  • 4
  • 37
  • 35
  • 1
    Thanks a lot for the help! Grunt develop was pretty much what I was looking for. Though I still think it would be usefull that the Grunt team start a contrib package for it. :) – Augustin Riedinger Mar 11 '13 at 08:31
  • [grunt-contrib-livereload](https://github.com/gruntjs/grunt-contrib-livereload) is their solution to this I believe. I'm not experienced with that plugin at all, but at a glance it looks like `$ grunt livereload-start` sets up your server. – nackjicholson Mar 11 '13 at 16:23
  • 2
    I think the server in `livereload` is just for the purposes of reloading assets via websockets, and should be kept separate. Might be wrong though. –  Mar 11 '13 at 23:06
  • 1
    Thanks Oliver. Disregard that comment above. Maybe I should just delete it. But then it would look like you're talking to no one. – nackjicholson Mar 12 '13 at 06:09
3

Hi I also came across this problem and here is my solution (based on nackjicholson's answer). This uses grunt-nodemon in a spawned process. so I can:

  • Reload nodejs
  • Watch for changes to e.g. .less files
  • Get output of both tasks

    grunt.loadNpmTasks('grunt-nodemon');
    grunt.initConfig({
        nodemon: {
            dev: {
                options: {
                    file: 'server.js',
                    nodeArgs: ['--debug'],
                    env: {
                        PORT: '8282'
                    }
                }
            }
        },
    });
    
    grunt.registerTask('server', function (target) {
        // Running nodejs in a different process and displaying output on the main console
        var nodemon = grunt.util.spawn({
             cmd: 'grunt',
             grunt: true,
             args: 'nodemon'
        });
        nodemon.stdout.pipe(process.stdout);
        nodemon.stderr.pipe(process.stderr);
    
        // here you can run other tasks e.g. 
        // grunt.task.run([ 'watch' ]);
    
    });
    
actual_kangaroo
  • 5,971
  • 2
  • 31
  • 45
2

Use grunt-concurrent

The issue is that tasks like watch and nodemon will never terminate, so grunt will never reach them. You need to spawn a new process.

You can do this easily using grunt-concurrent:

https://github.com/sindresorhus/grunt-concurrent

For example:

module.exports = function(grunt) {
  grunt.initConfig({

    ...

    concurrent: {
      dev: {
        tasks: ['nodemon', 'watch'],
        options: {
          logConcurrentOutput: true
        }
      }
    }
  });
};

The two will now run happily side by side.

superluminary
  • 47,086
  • 25
  • 151
  • 148
  • Especially with the logConcurrentOutput option, this does what I was looking for and was very easy to implement. Thanks. – rainabba Dec 03 '15 at 17:53