javascriptnode.jsnpmfs-extrachokidar

Getting empty string from fs.readFile inside chokidar.watch(path_file).on('change', ...)


I have the following very simple Node project:

https://github.com/tlg-265/chokidar-issue

enter image description here

$ git clone https://github.com/tlg-265/chokidar-issue
$ cd chokidar-issue
$ npm i
$ npm run watch-changes

which basically takes care of detecting changes on file:

/profiles/bill-gates.json

and do an action just after that.

In order to do that I have the following file:

/profile-watcher.js

const fs = require('fs-extra');
const colors = require('colors/safe');
const chokidar = require('chokidar');

const path_file = `profiles/bill-gates.json`;
console.log(`Current Profile: ${colors.red.bgBrightYellow(path_file)}`);

let profile_before = {};

chokidar.watch(path_file).on('change', async (path) => {

  console.log();
  console.log(`${colors.blue.bgYellow(`->`)} Profile changed: ${path}`);

  fs.readFile(path, (err, profile_json) => {
    console.log(`->${profile_json}<-`);
    let profile = JSON.parse(profile_json);
    if (JSON.stringify(profile) != JSON.stringify(profile_before)) {
      console.log('The profile has changed.');
      profile_before = profile;
    }
  });

});

when I run the project with:

$ npm run watch-changes

and do the modifications below on file: /profiles/bill-gates.json

it works fine, outputting the content of this file to the console.

But when I do the next modification:

Then I get the following error:

-> Profile changed: profiles\bill-gates.json
-><-
undefined:1

SyntaxError: Unexpected end of JSON input
    at JSON.parse (<anonymous>)
    at fs.readFile (\chokidar-issue\profile-watcher.js:17:24)
    at \chokidar-issue\node_modules\graceful-fs\graceful-fs.js:115:16
    at FSReqWrap.readFileAfterClose [as oncomplete] (internal/fs/read_file_context.js:53:3)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! chokidar-issue@1.0.0 watch-changes: `node profile-watcher.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the chokidar-issue@1.0.0 watch-changes script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Roaming\npm-cache\_logs\2020-02-28T23_44_01_038Z-debug.log

/profiles/bill-gates.json (Flags: UTF-8 / CRLF)

{
  "name": "Bill Gates",
  "email": "bill.gates@microsoft.com",
  "password": "windows",
  "country": "USA"
}

By the way, if I change from CRLF to LF normally I can do few modifications more before it crashes.

I'm under the impression that for somre reason the file: /profiles/bill-gates.json gets locked at some point and when Node tries to read it it returns an empty string because it is locked.

Any idea on how to make this work without crashing after few tries?

Thanks!


Solution

  • I could make it work by adding some recovery fallback:

    const fs = require('fs-extra');
    const colors = require('colors/safe');
    const chokidar = require('chokidar');
    const sleep = require('sleep');
    
    const path_file = `profiles/bill-gates.json`;
    console.log(`Current Profile: ${colors.red.bgBrightYellow(path_file)}`);
    
    let profile_before = fs.readFileSync(path_file).toString();
    
    chokidar.watch(path_file).on('change', async (path_changed) => {
      let profile = fs.readFileSync(path_changed).toString();
      if (IsValidJson(profile)) {
        if (profile != profile_before) {
          console.log();
          console.log(`Profile changed: ${colors.red.bgBrightYellow(path_changed)}`);
          process_profile(profile);
          profile_before = profile;
        }
      }
      else {
        sleep.msleep(100); // this is necessary
      }
    });
    
    function process_profile(profile_json) {
      const profile = JSON.parse(profile_json);
      console.log(`${profile_json}`);
      console.log(profile.name);
    }
    
    function IsValidJson(str) {
      try {
        JSON.parse(str);
      } catch (e) {
        return false;
      }
      return true;
    }
    

    It seems that when you save a file (at least on Windows), sometimes there is a time in between (very very short time) that the file gets clear and few milliseconds later it gets the actual content. On both cases the on-change event gets fired. So, we just need to verify whether the content of the file is JSON or not. In that case I just need to ignore it and wait for the next on-change event.