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

Version 4: Typescript, ESM, glob removal, drop node <18 #1195

Merged
merged 47 commits into from
Jul 1, 2024
Merged

Conversation

paulmillr
Copy link
Owner

@paulmillr paulmillr commented Jan 18, 2022

  • Remove globs
  • Switch to ESM
  • Switch to typescript
  • Release readdirp v4
  • Changelog
  • Release chokidar v4

@paulmillr paulmillr changed the title Version 4: Typescript, glob removal Version 4: Typescript, glob removal, drop node <14 Jan 18, 2022
@paulmillr paulmillr changed the title Version 4: Typescript, glob removal, drop node <14 Version 4: Typescript, ESM, glob removal, drop node <14 Jan 18, 2022
package.json Show resolved Hide resolved
@sw360cab
Copy link

@paulmillr is this alpha release blocked ?
Is there any plan to release it? Maybe some contribution needed.

Thanks a lot for your great work.

@paulmillr
Copy link
Owner Author

Nah -- someone just needs to find some time to debug it.

The dynamic import causes the `describe` blocks to happen later, which
means the `after` and `afterEach` have already run (and cleaned up) at
that point.

It also turns out the old version of rimraf wasn't awaitable, so it has
been upgraded in this change.
This reverts to what the test is in `master` but fixes an async race
condition.

Essentially:

```ts
await waitForWatcher(watcher1);
// at this point, the ready event has fired for watcher1 _and_ watcher2
await waitForWatcher(watcher2);
// this never gets reached because it fired before we added the event
// handler
```

Awaiting both in a `Promise.all` fixes this since the event handlers
will be attached immediately.
Since we're locally importing this, node will not resolve the import
path as if it is an NPM package (i.e. it will not infer the `main` path,
or package exports).

Instead, we need to import the exact path it seems (`lib/index.js`).
This reverts the test for ignoring events after close, since it seems
the original passes fine and makes this consistent with the other tests
again in structure.
@paulmillr paulmillr force-pushed the master branch 2 times, most recently from 368cb02 to 7c50e25 Compare February 6, 2024 22:52
This reworks the anymatch function to be a little less generic since we
now know exactly what we want to call it with. This means we have much
clearer/stronger types.

On top of that, support for ignored paths has been implemented again (it
was removed with a `TODO` until now, in v4).

Keep in mind the way we store ignored paths may change in future so we
can more efficiently access and remove them without the need for full
iterations through the set.
We were running ESLint against `*.js` (the default) which no longer
exists, so `--ext` has been added to make it check `*.ts` files.

Alongside that, TSESLint has been added and the recommended config
enabled.

For now, these two rules are disabled:

- `no-explicit-any` - until we strongly type everything
- `no-unused-vars` - probably permanently, using typescript's own
  `noUnusedLocals` instead
This basically does two things:

1. Adds `WatchHelper` to some of the methods for a stronger type
2. Reworks how we add/remove ignore paths ("matchers")

In the latter, thanks to a previous change where we can have "matcher
objects", we need to iterate through existing ignore paths to make sure
we don't duplicate them.

Similarly, when removing a "path", we need to find any matcher objects
which use the same path and remove those too.

Basically introducing `addIgnorePath` and `removeIgnorePath` instead of
mutating the set directly.
43081j and others added 5 commits May 26, 2024 12:42
Switches to using the built-in node test runner and drops mocha.

Temporarily also introduces a `TEST_TIMEOUT` as `--test-timeout` is only
available in 20.x and above, but we run CI in 18.x.
Adds a small delay in some tests after the initial path has been added
(to avoid the FS de-duping/cancelling events).
test: switch to node test runner
Upgrades eslint and switches to using the new flat config structure.
chore: upgrade to flat eslint configs
@paulmillr
Copy link
Owner Author

We also need to make this ESM and upgrade https://github.com/paulmillr/readdirp/

@43081j
Copy link
Contributor

43081j commented Jun 2, 2024

We also need to make this ESM and upgrade https://github.com/paulmillr/readdirp/

i have a branch where i've tried to remove readdirp since node itself should be capable by now (with recursive: true)

though it has a few test failures so i was planning on trying to fix those

what do you think?

unrelated - the same failure as my last few PRs happened again here. seems to be windows specific 🤔 i'd like to solve that one somehow

@paulmillr
Copy link
Owner Author

ideally we'll just use node built-ins, ofc.

Try readdirp-ing 400K-file directory and seeing how's the speed with both solutions. I imagine nodejs is still shit. Keep in mind that readdirp uses streams etc.

@43081j
Copy link
Contributor

43081j commented Jun 2, 2024

thats true, i'll try it out on a larger project if i can find one some place 👍

@43081j
Copy link
Contributor

43081j commented Jun 2, 2024

i did some benchmarking, and it seems like readdirp recursively visits some symlinked directories, whereas node won't

in a test repo:

readdirp - Found 122112 items
node - Found 32967 items

it seems the missing items are already part of the 32967 (symlinked by pnpm in this case)

in a directory that doesn't hit that problem, node seems faster:

┌─────────┬────────────┬─────────┬────────────────────┬──────────┬─────────┐
│ (index) │ Task Name  │ ops/sec │ Average Time (ns)  │ Margin   │ Samples │
├─────────┼────────────┼─────────┼────────────────────┼──────────┼─────────┤
│ 0       │ 'readdirp' │ '9'     │ 105280229.69999999 │ '±9.32%' │ 10      │
│ 1       │ 'node'     │ '41'    │ 23874190.62790698  │ '±3.59%' │ 43      │
└─────────┴────────────┴─────────┴────────────────────┴──────────┴─────────┘

@paulmillr
Copy link
Owner Author

Symlinks are definitely important.

@43081j
Copy link
Contributor

43081j commented Jun 2, 2024

Symlinks are definitely important.

im not saying node is ignoring them. readdirp is duplicating them from what i can tell

readdirp - Found 122112 items
node - Found 32967 items

the extra 89,145 entries seem to be files in node_modules we already visited (already accounted for in the 32,967).

symlinks certainly are traversed via node

@benmccann
Copy link

@benmccann how flexible are you in terms of ESM-onliness?

Sorry I missed this question, but don't worry about me. I'm fine with whatever you decide either way

@43081j
Copy link
Contributor

43081j commented Jun 12, 2024

just fyi too the last two weeks have been fairly chaotic for me so i haven't had much time to look into whats left yet

but things should calm down this weekend for a week or two, so ill try catch back up on it. would be nice to at least sort the windows tests out

43081j and others added 2 commits June 25, 2024 10:44
Moves to using `node:fs/promises` rather than using `promisify`
manually.
feat: move to node:fs/promises
@43081j
Copy link
Contributor

43081j commented Jun 28, 2024

@paulmillr if you didn't see, it looks like the windows failures fail in master too

it does seem like it could be a bug but we could at least release a pre-release for now. that would be nice i think (4.0.0-pre.0 or something, in a next tag on npm)

the functionality seems pretty sound and i've been through the diff with master, which shows our only changes really are:

  • removal of globs
  • ignore logic

so should be good, and any problems we see are likely already problems (that we should fix)

@paulmillr
Copy link
Owner Author

I would still prefer to fix tests for v4. They have been broken for many years.

Do you think we can do it?

@43081j
Copy link
Contributor

43081j commented Jun 28, 2024

i agree

its a busy couple of days coming up but ill take a look again when i can and see if i can figure it out

@43081j
Copy link
Contributor

43081j commented Jun 30, 2024

@paulmillr it is a bug, i've figured it out roughly but not how to fix it

when we write a file (add.txt), we cause a rename fs event

this results in us calling the handleDir watcher here:

closer = this._watchWithNodeFs(dir, (dirPath, stats) => {
// if current directory is removed, do nothing
if (stats && stats.mtimeMs === 0) return;
this._handleRead(dirPath, false, wh, target, dir, depth, throttler);
});

that starts readdirp going on the directory:

let stream = this.fsw
._readdirp(directory, {
fileFilter: (entry) => wh.filterPath(entry),
directoryFilter: (entry) => wh.filterDir(entry),
depth: 0,
})

which finds add.txt and adds the node:

this._addToNodeFs(path, initialAdd, wh, depth + 1);

we then reach this stat call (which is async):

const stats = await statMethods[wh.statMethod](wh.watchPath);

meanwhile, a change event fires - nothing really comes of this because we haven't finished our stat of the file, so haven't added a listener yet

when the test passes - a second change event fires at this point, and there's still no file listener so we do not fire a CHANGE event

now the stat finishes and adds the file listener to the set of listeners

when the test fails - the second change event fires at this point, and there is a file listener so we fire a CHANGE event


im not exactly sure what we can do about this

the TLDR of the problem is that the stat finishes before the followup change, so it thinks its a real change (but its not, windows at least fires 2 for whatever reason)

@43081j
Copy link
Contributor

43081j commented Jun 30, 2024

🤔

is it possible this is just wrong:

if (!at || at <= mt || mt !== prevStats.mtimeMs) {

specifically at <= mt:

  • if the access time is before the modified time, it means we haven't accessed it since it was modified, so this is a change
  • if the access time is the same as the modified time, surely it implies it hasn't changed?

if i change this to at < mt, the tests pass

edit:

i made #1335 but it breaks the reverse (ubuntu). ill take another look another time

@paulmillr paulmillr merged commit 064e47d into master Jul 1, 2024
14 of 20 checks passed
@millette
Copy link

millette commented Jul 1, 2024

Related: #1336

@benjamingwynn
Copy link

Will this release address #1225, or will another issue have to be created?

@paulmillr
Copy link
Owner Author

New release drops support for globs. So, it would address it by setting it "out of scope". As in: all glob patterns would become unsupported. You will need to write regexps, or filtering functions.

@paulmillr paulmillr deleted the v4 branch July 2, 2024 17:11
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 this pull request may close these issues.

None yet

7 participants