Closed wartab closed 2 months ago
Pull requests are appreciated.
Some facts:
/.+\.txt$/
const { glob } = require('node:fs/promises');
it’s more complicated, but the community part which uses globs will come up with shortcuts and solutions.
- Regular expressions can be used in filters, such as /.+.txt$/
ignored: (path) => RE.test(path)
Oh, yes. That's what I'm doing. :)
And of course, anyone can just npm install glob
and continue using it as a filter as function.
Worked great to switch from glob to folder + filter 🚀 Nice to drop all those dependencies, thank you! 🙂
Hmm I struggled around an hour already on this. I just had the very basic case of watching all files with a certain extension. Let me first share what does not work:
// obviously broken, only works in v3:
const watcher = watch('**/*.gql');
// broken, because also ignores "." already
const watcher = watch('.', { ignore: (file) => !file.endsWith('.gql') });
// broken, "Error: EMFILE: too many open files, watch"
const watcher = watch('.');
watcher.on('change', (path) => {
if (!file.endsWith('.gql')) return;
// ...
});
The best (?) solution I found so far, if you are willing to use globby
:
import globby from 'globby';
const watcher = watch(await globby('**/*.gql'));
There are of course a bunch of other glob packages. If you are able to use the experimental node v22 glob:
import { glob } from 'node:fs/promises';
const watcher = watch(await Array.fromAsync(glob('**/*.gql')));
Is this working the same as the old code? No idea, but at least it seems to work.
What also seems to work is using the second stats argument for the filter function, it is not in the TS types though:
const watcher = watch('.', {
// @ts-expect-error Doesn't have types for second argument
ignored: (file, stats) => stats && stats.isFile() && !file.endsWith('.gql'),
});
I'm not sure if this is better in any way.
@bxt why doesn’t this work exactly? ignore: (file) => !file.endsWith('.gql')
@paulmillr Maybe I'm doing it wrong, but:
const watcher = watch('.', {
ignored: (file) => {
console.log(`file: ${file}`);
return !file.endsWith('.gql');
},
});
This prints file: .
and nothing else. So it already ignores the current directoy and does not look further. If I un-ignore .
is stops at the immediate child directories, as none on their name end in .gql
either. The solution would be to not ignore any directories, which is what I did with ignored: (file, stats) => stats && stats.isFile() && !file.endsWith('.gql')
but it seems a bit complicated as well.
Maybe the .
is the issue? Suggestion:
const watcher = watch(process.cwd(), {
ignored: (file) => !file.endsWith('.gql'),
});
What about return file !== "." && !file.endsWith('.gql');
?
Yeah, I just realized that subdirs will also need to be excluded. So, using second argument is the proper way to do this:
const watcher = watch('.', {
ignored: (file, stats) => stats && stats.isFile() && !file.endsWith('.gql'),
});
As for typescript errors with this, 4.0.1 will fix it.
Hi I'm writing a tool that let users configure their own watch patterns. To migrate this, users now have to write a bunch of extra logic for ignoring files. On the other hand, using RegExp is not as ergonomic. What do you recommend?
// user code
export default config = {
entries: ['folder-1/**/*.json', 'folder-2/*/file.json'] // tool also supports yaml, json5, user only wants json
}
you can use built-in glob
from fs
as mentioned above
fs.glob
is not available in LTS version while this package supposedly run on nodejs 14+.
Use any other third party library, such as micromatch.
@universse LTS doesn't mean forever https://endoflife.date/nodejs
v14 expired 30 Apr 2023, v16 expired 11 Sep 2023 and v18 expires in 30 Apr 2025.
@millette what I mean is fs.glob
is experimental and unavailable in Node.js LTS version. Using fs.glob
as a workaround requires end users to upgrade to >LTS versions of Node.js.
Use any other third party library, such as micromatch.
for my use case, this requires creating a watcher for each glob pattern, unless I'm missing something.
entries.forEach(entry => {
const watcher = watch(globParent(entry), {
ignored: (file) => {
return !micromatch.isMatch(file, entry)
}
})
})
import { glob } from 'node:fs/promises'; const watcher = watch(await glob('**/*.gql'));
Is this working the same as the old code? No idea, but at least it seems to work.
I don't think it works the same. IIRC the way I use it is to also detect newly created files. This will only track previously created files matching that pattern.
My understanding (please correct me if I'm wrong) is that the old chokidar accepted globs, but then just recursively watched everything and then filtered the events. With the new version, you can't pass in globs, but directories you pass in are still watched recursively (which would include new files), so it should be equivalent to watch dirs (e.g. a prefix on the glob) and then filter the events yourself. It wouldn't be equivalent to apply the globs and then pass them in.
So, for **/*.gql
, it's equivalent to watch .
, then filter paths that match **/*.gql
(or more sussinctly, .endsWith(".gql")
).
This sort of explainer (if correct) would be nice to have in a doc; I was certainly confused until I tested it out. But, maybe that's not actually how it worked (my globs are all like some/path/**/*
where the new path to watch is obvious).
Pull requests are welcome.
Hmm I struggled around an hour already on this. I just had the very basic case of watching all files with a certain extension. Let me first share what does not work:
// obviously broken, only works in v3: const watcher = watch('**/*.gql'); // broken, because also ignores "." already const watcher = watch('.', { ignore: (file) => !file.endsWith('.gql') }); // broken, "Error: EMFILE: too many open files, watch" const watcher = watch('.'); watcher.on('change', (path) => { if (!file.endsWith('.gql')) return; // ... });
The best (?) solution I found so far, if you are willing to use
globby
:import globby from 'globby'; const watcher = watch(await globby('**/*.gql'));
There are of course a bunch of other glob packages. If you are able to use the experimental node v22 glob:
import { glob } from 'node:fs/promises'; const watcher = watch(await glob('**/*.gql'));
Is this working the same as the old code? No idea, but at least it seems to work.
What also seems to work is using the second stats argument for the filter function, it is not in the TS types though:
const watcher = watch('.', { // @ts-expect-error Doesn't have types for second argument ignored: (file, stats) => stats && stats.isFile() && !file.endsWith('.gql'), });
I'm not sure if this is better in any way.
how are you getting passed that TS error to access the stats ?
this seems to be working for me
ignored: ((file: string, stats: any | undefined) => {
if (stats && stats.isFile() && !file.endsWith('.otf') && !file.endsWith('.key') && !file.endsWith('.k')) {
return true;
}
return false;
}) as (file: string, stats?: any) => boolean,
@jcharnley You can use any of the TS workarounds, I used a @ts-expect-error
comment. In the end you can also wait for the next patch release, as a fix for the TypeScript issue is already on main
: https://github.com/paulmillr/chokidar/commit/87e0740cbd21327c82f97d3fe08662a9ae55e6d1
@jcharnley
There are of course a bunch of other glob packages. If you are able to use the experimental node v22 glob:
import { glob } from 'node:fs/promises';
const watcher = watch(await glob('**/*.gql'));
is this working the same as the old code? No idea, but at least it seems to work.
That should not work at all because what is returned by glob is an AsyncGenerator
.
@VandeurenGlenn You're right, you need to use await Array.fromAsync(glob('.*'))
to convert it to an array.
I also created a PR to fix this in the docs: https://github.com/paulmillr/chokidar/pull/1365
Btw in the meantime some docs update upgrading were added 🎉 https://github.com/paulmillr/chokidar?tab=readme-ov-file#upgrading so maybe this issue can be closed.
For anyone writing a tool that needs ”watch mode” with glob support, I'd recommend a library I wrote made exactly for this use case. It's called jumpgen
(https://github.com/alloc/jumpgen) and it's built on top of Chokidar.
It's good to see that chokidar is trying to get rid of dependencies. However I suspect that removing glob support is going to affect many projects. I checked the diff between 3.6.0 and 4.0.0 and the only changes I saw was the change of some examples, but they don't seem to be equivalent (except for the last?):
watcher.add(['new-file-2', 'new-file-3', '**/other-file*']);
->watcher.add(['new-file-2', 'new-file-3']);
await watcher.unwatch('new-file*');
->await watcher.unwatch('new-file');
ignored: '*.txt',
->ignored: (file) => file.endsWith('.txt'),
It would be nice to have a part of the documentation that tells people how it is recommended to migrate away from using globs.
Should we always use
ignored
? How would you migrate examples such as: