15

I'm trying to write a new file inside a grunt-search callback.

The process takes and object, traverses through it for some data, creates a new array, and then writes that array to a JSON file. The writing part isn't working out so well...

// onComplete is the callback, job is a returned object.
onComplete: function(job) {
    console.log("Creating file \"localize_template\"...");
    var fs = require('fs');
    var localArray = {};
    var foundEntries = job.matches;

    var stringCount = 0;

    // Drill down to the strings that matched the search.
    for (var foundEntry in foundEntries) {
        // Stay on target...
        if (foundEntries.hasOwnProperty(foundEntry)) {
            var singleEntry = foundEntries[foundEntry];
            // Almost...there...
            for( var match in singleEntry ) {
                if (singleEntry.hasOwnProperty(match)) {

                    // Direct hit!  We've drilled down to the match string itself.
                    var theMatch = singleEntry[match].match;

                    // Now, get the terms inside the strings that were referenced.
                    var terms = theMatch.match(/".*?"/g);

                    // Iterate through those strings and add them as entries in the localArray.
                    for( var i=0; i<terms.length; i++ ) {
                        var term = terms[i].replace(/"/g, '');

                        localArray[term] = 'xx:'+term;
                        stringCount++;
                    }
                }
            }
        }
    }

    fs.writeFile( 'i18n/localize_template.json', localArray, {encoding: 'utf8'}, function(err){
        console.log("File localize_template.json create successfully.");
        if(err) {
            throw err;
        } else {
           console.log("File localize_template.json create successfully.");
        }
    });    
}

The file is being created, but it's blank. I've tried using a generic Hello World! string instead of localArray to test, but the file is still blank.

Plummer
  • 6,522
  • 13
  • 47
  • 75

5 Answers5

20

You need to use the synchronous version:

fs.writeFileSync("./output.txt", "file contents"); 
LachoTomov
  • 3,312
  • 30
  • 42
  • 28
    can you please explain why isn't asynchronous method is working? – Alex Jones Jul 07 '18 at 11:12
  • You need to explain your answer. – Adam Jun 05 '23 at 02:53
  • @Adam no, I don't. I'm giving you the solution, but I've no idea why it works, nor do I care to dig into it. It is you who needs to read the docs. – LachoTomov Jun 05 '23 at 10:59
  • @LachoTomov if you don't know or care why it works then this is not a good answer. – Adam Jun 05 '23 at 20:53
  • @Adam that's true, I never said it was a good answer. I actually just realized that it's accepted. If I have to guess why this happens - it could be because the node process exits before the async operation completes, so it probably needs some way of waiting for it to finish. – LachoTomov Jun 05 '23 at 21:40
  • @LachoTomov Not only is it accepted but it also has the most upvotes. Interestingly the comment above that says "can you please explain..." currently has the most upvotes of anything. We're looking for you to be a StackOverflow role model here bro'! – Adam Jun 06 '23 at 01:13
  • @Adam as I said, I know as much as you do. It's just that I found a workaround that I decided to post here. You're the one who wants an explanation, but instead of reading the docs, you're trying to talk other people into reading the docs for you. Do you see anything wrong with your approach and understanding of how life works? :) – LachoTomov Jun 07 '23 at 09:02
8

To answer more clearly, fs.writeFile is asynchronous and the top level grunt stuff doesn’t know to wait for asynchronous operations started by onComplete. You need to figure out how to tell grunt that you have an unfinished asynchronous operation, except this is a feature that grunt-search doesn’t support. Otherwise, when you return from onComplete, grunt-search will mark the task as done immediately and grunt will exit causing the node process to exit before the asynchronous write completes.

Another thought is to use grunt.file.write(). This is a synchronous API, so it won’t require you solve the problem of being unable to tell grunt that your task isn’t done. Also, using this API will make your code magically support grunt’s --no-write option by being a no-op when the user requests a dry run.

onComplete: function (matches) {
    grunt.file.write('test', JSON.stringify(matches));
},
binki
  • 7,754
  • 5
  • 64
  • 110
5

Try to find out if your code has

process.exit()

For some reason, I have one temperately for testing only. If you do, comment out this one and you will be good to go. My version is v8.6.0.

Ibio Tan
  • 111
  • 3
  • 8
2

Your problem should be that the fs.writeFile starts "asynchronous". Try to change the localArray in here (hardcode it to see if it works):

fs.writeFile( 'i18n/localize_template.json', localArray, callback)

And there it should work. The solution i think it is that you should use fs.writeFileSync, or to initialize the localArray outside the oncomplete function, before it starts.

mega6382
  • 9,211
  • 17
  • 48
  • 69
AlexP
  • 31
  • 5
  • I had found using `.writeFileSync` worked earlier. What do the `**` around `localArray` do? – Plummer Jul 23 '15 at 18:49
  • It's just the bold sign in this site, but it didn't work with code snippet:) Just to see exactly where to change it. Glad it worked. – AlexP Jul 24 '15 at 06:11
1

By 2020, in a async function, use await as follows:

try {
    // blah blah


    // Write data to the file
    await fs.writeFile(path, data, function (err) {
        if (err) {
            console.error(`An error occurred while writing data to the file: ${err.message}`)
            throw err
        }
    })

    // blah blah
}
catch(err) {
    console.error(`An error occurred while writing data to the file: ${err.message}`)
}
Stéphane de Luca
  • 12,745
  • 9
  • 57
  • 95