0

I'm fairly new to Grunt, so this might be quite a basic question. I've got a Gruntfile.js that looks like this:

/*global module:false*/ 
module.exports = function (grunt) { 

  grunt.initConfig({ 

  }); 

  grunt.registerTask('default', 'measureText'); 

  grunt.registerTask('measureText', 'Measures size of text', function() { 
    grunt.log.writeln('========================================================================'); 
    grunt.log.writeln('= output of ImageMagick should be on next line:                        ='); 
    var im = require("node-imagemagick"); 

    im.identify(['-format', '%wx%h', 'build/text.png'], function(err, output){ 
      if (err) throw err; 
      console.log('dimension: '+output);  // <-- NOTHING WRITTEN!
    }); 

    grunt.log.writeln('========================================================================'); 
    return; 
  });    
}; 

As you can see, it's calling the node-imagemagick method identify to get the width and height of an image (build/text.png). When I run my grunt script above, there is no output from the identify() callback. (That's the console.log line above).

Yet if I create a Node script (e.g. test-node-imagemagick.js) to test that same code, it works just fine, e.g.

#!/usr/bin/env node

var im = require("node-imagemagick");
im.identify(['-format', '%wx%h', 'build/text.png'], function(err, output){
  if (err) throw err;
  console.log('dimension: '+output);
});

So how do I call Node packages from within Grunt tasks, and get returned values?

By the way, I did install the package by doing:

$ npm install node-imagemagick

... from the directory that contains the Gruntfile.js.

Thanks!

antun
  • 2,038
  • 2
  • 22
  • 34

3 Answers3

0

Edit:

The reason your task never completes is because of your ambiguous return at the end:

return; <---

You are disrupting the async call to:

im.identify(function(){
  //do async
})

return; //exit task

Try this:

grunt.registerTask('measureText', function(done) {
    //omitting code

    im.identify(function(){
       //do async stuff

       done()
    })
})

The function argument done() tells the task runner that it should finish after you do something asynchronous instead of ending before any of that code executes.

Original:

Your requires looks very different in each of those scripts.

require('imagemagick')

--- vs ---

require('node-imagemagick')

Grunt is probably swallowing the error

Also, I think its just:

npm install imagemagick
sctskw
  • 1,588
  • 1
  • 12
  • 14
  • Thanks for catching that, but it was just because I was trying a couple of things out while cleaning up the file to a test-case. I checked and the "node-imagemagick" package is just a fork of "imagemagick. If I correct them all to be "node-imagemagick". I'll update my original question to avoid confusion. – antun Feb 19 '15 at 04:54
  • it might be a good idea to put your requires() above the grunt function at the top of the file instead of within the task registration. Also, instead of throwing the error, try outputting it to stderr with console.error() or grunt.log.error() – sctskw Feb 19 '15 at 15:49
  • Thanks for the suggestion. I tried moving the var im = require("node-imagemagick") line to the top (right inside module.exports = function (grunt) {), but the result is the same. I'd also tried writing console.log(err) as well as grunt.log.writeln(err), but neither of those reported any error. Also, im appears to be a valid instance of the ImageMagick object, if I do console.log(im). – antun Feb 19 '15 at 16:01
  • Also, I tried your updated example - specifically omitting the return statement and calling done() from within the identify() callback, as well as passing the done argument to the registerTask's callback, but the behavior is still the same; the identify callback never fires. – antun Feb 19 '15 at 16:06
0

I just found the answer by some more digging, here: Using a node module within a Grunt Task fails

Basically, my Grunt task has to be asynchronous if it is to receive a callback:

/*global module:false*/ 
module.exports = function (grunt) { 
  var im = require("node-imagemagick"); 


  grunt.initConfig({ 
  }); 

  grunt.registerTask('default', 'measureText'); 

  grunt.registerTask('measureText', 'Measures size of text', function() { 
    var done = this.async(); // <-- Make the task asynchronous!
    grunt.log.writeln('========================================================================'); 
    grunt.log.writeln('= output of ImageMagick should be on next line:                        ='); 

    im.identify(['-format', '%wx%h', 'build/text.png'], function(err, output){ 
      if (err) throw err; 
      console.log('dimension: '+output); 
      done();
    });  
  });    
}; 

Of course, that means that the output would not actually appear between the two lines of equals signs, if I'd kept the second line there.

Community
  • 1
  • 1
antun
  • 2,038
  • 2
  • 22
  • 34
0

Doing some more research I was able to get this to work. First off, I would switch to this library which was recommended by the imagemagick developer README.

https://github.com/aheckmann/gm

But, that is up to you if you want to use an unmaintained library.

Anyway here it is:

grunt.registerTask('measureText', function(done) {
  //omitting code

  var done = this.async() //create async task

  im.identify('/path/to/image', function(err, output){
   //do async stuff
   console.log('dimension: %s', output)
   done()
  })

  //or use gm
  gm('/path/to/image')
   .size(function(err, size) {
      console.log('gm size: %s', size)
      done()
   })
})
sctskw
  • 1,588
  • 1
  • 12
  • 14