I have the following very simple Node
project:
https://github.com/tlg-265/chokidar-issue
$ 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
- modification 1:
Bill Gates -> Bill Gates ABC
- modification 2:
Bill Gates ABC -> Bill Gates ABC DEF
it works fine, outputting the content of this file to the console.
But when I do the next modification:
- modification 3:
Bill Gates ABC -> Bill Gates ABC DEF GHI
Then I get the following error:
-> Profile changed: profilesbill-gates.json -><- undefined:1 SyntaxError: Unexpected end of JSON input at JSON.parse (<anonymous>) at fs.readFile (chokidar-issueprofile-watcher.js:17:24) at chokidar-issuenode_modulesgraceful-fsgraceful-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:Roamingnpm-cache_logs2020-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!
Advertisement
Answer
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.