Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
br1985 opened this issue Feb 21, 2015 · 33 comments · Fixed by #791 or parcel-bundler/parcel#2878
Closed

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

br1985 opened this issue Feb 21, 2015 · 33 comments · Fixed by #791 or parcel-bundler/parcel#2878

Comments

@br1985
Copy link

br1985 commented Feb 21, 2015

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
Copy link
Contributor

es128 commented Feb 23, 2015

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
Copy link
Author

br1985 commented Feb 28, 2015

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
Copy link
Contributor

es128 commented Feb 28, 2015

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
Copy link
Contributor

es128 commented Feb 28, 2015

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
Copy link
Author

br1985 commented Feb 28, 2015

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
Copy link
Contributor

es128 commented Feb 28, 2015

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
Copy link

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

@es128
Copy link
Contributor

es128 commented Apr 14, 2015

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
Copy link

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
Copy link
Owner

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

@flying-sheep
Copy link

flying-sheep commented Oct 6, 2016

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

  • only use dir-level handlers, or
  • once any given file-level handler is deleted, create a new dir-level handler that waits for a file of the given name/path to be recreated, and then attaches a new file-level handler onto the newly created file of that name

@callumacrae
Copy link

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

@flying-sheep
Copy link

flying-sheep commented Oct 7, 2016

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
Copy link

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
Copy link

tprobinson commented Mar 31, 2017

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
Copy link

Toilal commented Mar 28, 2018

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
Copy link

@paulmillr @es128 Issue still happening in Manjaro (Arch) Linux using latest Neovim. Problem does not happen when globbing, however the advice given in #237 (comment) 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
Copy link

pimlie commented Jan 8, 2019

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
Copy link
Contributor

mitar commented Feb 8, 2019

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
Copy link
Contributor

mitar commented Feb 8, 2019

I think I fixed this in #791.

@rkyoku
Copy link

rkyoku commented May 5, 2019

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
Copy link

drk-mtr commented Aug 29, 2019

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
Copy link

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

@paulmillr
Copy link
Owner

A code that reproduces this would be great.

@albertnetymk
Copy link

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
Copy link
Owner

Which code editor do you use?

@albertnetymk
Copy link

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
Copy link

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
Copy link

nkeor commented Apr 15, 2020

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
Copy link
Owner

You should update to chokidar 3.

@nkeor
Copy link

nkeor commented Apr 15, 2020

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): gulpjs/glob-watcher#49

@albertnetymk
Copy link

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
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.