paulmillr / chokidar

Minimal and efficient cross-platform file watching library
https://paulmillr.com
MIT License
10.95k stars 580 forks source link

Only a single `change` event is fired for each file #237

Closed br1985 closed 5 years ago

br1985 commented 9 years ago

Here's a very basic example:

require('chokidar').watch(process.argv).on("change", function(path) {
  console.log(path);
});

require('http').createServer().listen(8000);

After I run this code and start changing watched files I see the events being fired. The problem is that only the first change for each file is reported!

I have this issue with versions 0.12. and 1.0.0-rc. Versions 0.11.* and below seem to work fine.

I'm using Arch Linux with local Btrfs and Ext4 filesystems (tested both). Kernel: 3.18.6-1-ARCH, node: 0.10.36.

With usePolling option everything works fine.

es128 commented 9 years ago

Version 0.11.* and below used polling on Linux by default, so setting usePolling: true to fix it now is basically equivalent.

Which editor are you using to modify your files? Very similar behavior used to exist when using vim because of its "atomic write" behavior. Perhaps you're using an editor that also does something similar, but following a different pattern than what has already been corrected for in chokidar.

Also, if you add .on('raw', console.log) to your watcher code, it may help uncover what's happening.

br1985 commented 9 years ago

While trying to replicate this issue a moment ago I think I found where's the problem... I realized that I'm trying to monitor a symlink!

Here's my test.js:

require('chokidar').watch(process.argv[2])
  .on("change", function(path) { console.log("CHANGE: " + path); })
  .on("raw", console.log);

require('http').createServer().listen(8000);

And here's my test:

mkdir a
touch a/doc.txt
ln -s a b
gedit b/doc.txt &
node test.js b/doc.txt

Now, I make some changes with gedit and here's the output:

change doc.txt { watchedPath: 'b/doc.txt' }
rename doc.txt { watchedPath: 'b/doc.txt' }
rename doc.txt { watchedPath: 'b/doc.txt' }
CHANGE: b/doc.txt

And that's it. Only the first change is detected, subsequent ones are completely ignored.

Should symlinks be supported?

es128 commented 9 years ago

Yes. I think gedit was actually the most relevant piece of information. Need to look at its save patterns.

Can you try with another editor like - like vim, emacs or sublime for instance.

es128 commented 9 years ago

Actually it would also help if you watch the directory instead of the file directly. If you really only care about the one file you can use a glob pattern like b/[d]oc.txt to get the benefit of the dir-level watcher, which is probably where the subsequent events are landing.

br1985 commented 9 years ago

I've just tried mcedit, vim, phpstorm and netbeans. With phpstorm I've got identical issue. The rest works just fine.

Actually, I've got here trying to solve my issue with aglio (https://github.com/danielgtaylor/aglio). I've already hacked it with usePolling - it's fine for me.

es128 commented 9 years ago

Yep, polling will work, but it's not the preferable solution.

When using fs.watch to watch a single file (and atomic: true) I may change it to just always watch the directory also to catch the events in a situation like this.

janmisek commented 9 years ago

Hello. Just to confirm this bug on ubuntu 14.04. Using JetBrains WebStorm (Java IDE). in mcedit everything works fine.

es128 commented 9 years ago

A workaround not mentioned yet in this thread is to use a glob pattern which watches that one file, the effect of which is to force directory-level watching where the events sometimes land.

So instead of watching file.txt, set it to watch [f]ile.txt.

Would appreciate feedback from anyone for whom this workaround does not work, as the fix in chokidar will likely apply a similar method.

MartinSvarrer commented 9 years ago

Thanks for the workaround. I was having this issue with Visual Studio 2013/15 on win 7, where I would only get the first change event. The [f]ile.txt does work. However, it will now take a while for first events to dispatch (approx 10sec.).

paulmillr commented 8 years ago

Let us know if that still happens with the latest chokidar.

flying-sheep commented 8 years ago

it does still happen with 1.6.0 (at least if gulp’s APIdocs are still right and it gulp 4.0 still uses chokidar behind the scenes)

to fix it, we’d have to either

callumacrae commented 8 years ago

@flying-sheep: Run npm ls | grep 'chokidar' and paste the output here

flying-sheep commented 8 years ago

as said: 1.6.0: │ ├─┬ chokidar@1.6.0

with grep -B 20:

├─┬ browser-sync@2.17.0
│ ├─┬ browser-sync-client@2.4.2
│ │ ├── etag@1.7.0
│ │ └── fresh@0.3.0
│ ├─┬ browser-sync-ui@0.6.1
│ │ ├── async-each-series@0.1.1
│ │ ├── connect-history-api-fallback@1.3.0
│ │ ├─┬ stream-throttle@0.1.3
│ │ │ └── limiter@1.1.0
│ │ └─┬ weinre@2.0.0-pre-I0Z7U9OV
│ │   ├─┬ express@2.5.11
│ │   │ ├─┬ connect@1.9.2
│ │   │ │ └── formidable@1.0.17
│ │   │ ├── mime@1.2.4
│ │   │ ├── mkdirp@0.3.0
│ │   │ └── qs@0.4.2
│ │   ├── nopt@3.0.6
│ │   └── underscore@1.7.0
│ ├── bs-recipes@1.2.3
│ ├─┬ chokidar@1.6.0
mikehenrty commented 7 years ago

I'm also still seeing this issue with chokidar 1.6.1 (which is a dependency of nodemon 1.10.2). I'm also using arch linux and vim. When I save with another program (Libre Office in my case), the watcher work as expected. But when trying to save multiple times with vim, only the first change event is triggered.

The workaround (ie. using glob patterns or polling) still works. Neither is ideal though.

tprobinson commented 7 years ago

Using Ubuntu and Vim here, and chokidar 1.6.1. Only using single-operation tools like echo > file allowed me to change the file without screwing up the single-file watch.

I was able to workaround this by re-watching the file when it's renamed. It's also not ideal, but might be useful when avoiding dir-level handlers is desired.

const startWatching = (watcher, file) => {
  debug('Loading ' + file);
  reloadFile(file)
    .catch(console.error)
    .then(() => {
      watcher.unwatch(file);
      watcher.add(file);
    })
  ;
};

let calledOnce = false;
const watcher = chokidar.watch([], {persistent: true})
  .on('change', reloadFile)
  .on('raw', (type, filename, details) => {
    // trigger only once on a rename, then wait for a moment to re-attach.
    if( type === 'rename' && !calledOnce ) {
      console.warn(filename + ' rename event caught. Avoid editing the file with an editor. Re-initializing chokidar watcher...');
      calledOnce = true;
      // wait for edits to be complete, then reload the file.
      setTimeout(() => {
        calledOnce = false;
        startWatching(watcher, details.watchedPath);
      }, 500);
    }
  })
;

startWatching(watcher, '/file');
Toilal commented 6 years ago

Same issue here, with ubuntu 17.04 and parcel-bundler (which rely on chokidar for hot reloading). Switching to polling mode with CHOKIDAR_USEPOLLING environment variable do the trick ...

Please open the issue again.

deniskabana commented 6 years ago

@paulmillr @es128 Issue still happening in Manjaro (Arch) Linux using latest Neovim. Problem does not happen when globbing, however the advice given in https://github.com/paulmillr/chokidar/issues/237#issuecomment-92803815 about wraping the first letter of file in square brackets leads to error from within chokidar's dependencies.

Please re-open as of 09/2018

pimlie commented 5 years ago

This is probably caused by fs.watch on Linux using inotify which listens for inodes rather then file names. Most editors have a 'save to tmp and replace' strategy to make sure that you dont lose any files when e.g. your disk space is full. The problem is that this creates a new file with a new inode.

On linux chokidar should probably listen for a rename event on the filename. As a proof of concept, save below script as test.js then if you run node test.js it will print CHANGED only once but if you run node test.js 1 it will print CHANGED twice

const fs = require('fs')

const file = 'tmp.file.tmp'

let watcher
const watch = (file, rewatchOnRename) => {
  try {
    watcher = fs.watch(file, {
      persistent: true
    }, (e, f) => {
      if (e === 'change') {
        console.log('CHANGED')
      } else if (rewatchOnRename && e === 'rename') {
        watcher.close()
        watch(file, rewatchOnRename)
      }
    })
  } catch(e) {
    if (e.code === 'ENOENT' && e.path === file) {
      setTimeout(() => watch(file, rewatchOnRename), 1)
    } else {
      console.error(e)
    }
  }
}

function touch(file) {
  if (fs.existsSync(file)) {
    fs.unlinkSync(file)
  }
  fs.writeFileSync(file, '')
}

touch(file) // create the file
watch(file, !!process.argv[2])

setTimeout(() => touch(file), 1)
setTimeout(() => touch(file), 1000)

setTimeout(() => {
  watcher.close()
  fs.unlinkSync(file)
}, 2000)
mitar commented 5 years ago

So instead of watching file.txt, set it to watch [f]ile.txt.

Would appreciate feedback from anyone for whom this workaround does not work, as the fix in chokidar will likely apply a similar method.

I tested this and I prefer solution with @(file.txt). And it works for me.

mitar commented 5 years ago

I think I fixed this in #791.

rkyoku commented 5 years ago

A workaround not mentioned yet in this thread is to use a glob pattern which watches that one file, the effect of which is to force directory-level watching where the events sometimes land.

So instead of watching file.txt, set it to watch [f]ile.txt.

Would appreciate feedback from anyone for whom this workaround does not work, as the fix in chokidar will likely apply a similar method.

Hello!

@es128 Does not work for me on Windows with Git Bash: it never triggers using this trick.

Here is the (original) script in package.json:

"watch-css": "nodemon -e css -w icomoon/style.css -w css/style.css -x \"npm run concat-css\"",

I tried the [] trick on the first letter of the folder, then on the first letter of the file. I also tried both single quote and double quote. In these 4 scenarii, there is not a single trigger.

Before trying the [] hack, I got the "triggered only after the first change of the file" bug that @mikehenrty described in the nodemon repo.

I couldn't get the @() trick of @mitar to work either.

I am using the latest version of nodemon which seems to be using version 2.1.5 of chokidar.

Then I tried to install the latest version (3.0.0) of chokidar, but it gets worse: it does not trigger even once :-/ Maybe it's because I'm a n00b and I should not manually install a newer version than the one intended by nodemon. But still, at least you know everything I tried.

Thanks for any input in this matter!

Best,

drk-mtr commented 5 years ago

This is still present (chokidar 3.0.2) when using Ubuntu 19.04 in WSL on Windows 10 alongside neovim. Fairly esoteric use case I know but just thought I'd mention it as I saw this had been closed.

The usePolling options works. I haven't tested the globbing pattern approach as in my scenario I don't know how to do that just yet hehe.

albertnetymk commented 4 years ago

Encountered this on Debian 11, with chokidar 3.3.0, npm 6.13.1, and node 10.17.0.

paulmillr commented 4 years ago

A code that reproduces this would be great.

albertnetymk commented 4 years ago

It's basically the same as the original example.

const chokidar = require('chokidar');

chokidar.watch('./test.txt', {
  usePolling: false
}).on('change', (event, path) => {
  console.log('changed');
});

My first save to test.txt was picked up by chokidar, but not later ones. Setting usePolling to true fixes the issue, as others reported.

paulmillr commented 4 years ago

Which code editor do you use?

albertnetymk commented 4 years ago

Neovim 0.3.8; I tried nano as well, and the behavior is the same.

Surprisingly, touch 0 > test.txt triggers change every time.

scooper91 commented 4 years ago

I'm having the same issue when using Vim and chokidar-cli. The first time the file is saved, the watch fires. Subsequent saves do nothing. The app is running in docker, with the app directory mounted. Setting polling to true works.

nkeor commented 4 years ago

Here on FreeBSD 12.1, using gulp.watch fires once when writing to a file with neovim. If I then rmove and touch the file, gulp.watch doesn't fire. But if I restart the watcher, and rmove and touch the file, it's fired on every change. If I write to the file with nvim, fires once and stops until I restart. When I add usePolling: true to gulp.watch (which is passed to chokidar as per the docs), it works as it should, firing on every edit (and :w) from nvim. BTW, I'm using Yarn 1.22.4, Node 13.10.1 and Gulp 4.0.2.

$ yarn list | grep chokidar
│  ├─ chokidar@^2.0.4
├─ chokidar@2.1.8
│  ├─ chokidar@^2.0.0

I believe Gulp's version is 2.1.8.

paulmillr commented 4 years ago

You should update to chokidar 3.

nkeor commented 4 years ago

I see, didn't checked if chokidar had newer versions. Sorry. Anyway, if someone gets here, looks like Gulp can't upgrade chokidar (for now): https://github.com/gulpjs/glob-watcher/issues/49

albertnetymk commented 4 years ago

Just FYI: chokidar is working fine with neovim 0.4.3 chokidar 3.3.0, npm 6.13.1, and node 10.17.0, so I guess it's neovim that's misbehaving in my prior testing.

jonnytest1 commented 3 months ago

in case someone still has it today : check if setTimeout works -.- in my case jsdom hooked setTimeout 🙈 and broke it