fs: implement glob natively - #65392
Conversation
|
Review requested:
|
|
cc @isaacs @nodejs/fs |
f172fce to
8bedc81
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #65392 +/- ##
==========================================
- Coverage 90.14% 89.90% -0.24%
==========================================
Files 752 765 +13
Lines 251870 255172 +3302
Branches 47365 48094 +729
==========================================
+ Hits 227037 229411 +2374
- Misses 16177 16684 +507
- Partials 8656 9077 +421
🚀 New features to boost your workflow:
|
I would feel more comfortable adopting one of those instead of having to maintain our own, have you explored that route? |
I have, but the perf wins here are dependent on this being in native, and not in JS:
I also think this can bring some improvements to things like the test runner. For instance, |
8bedc81 to
55e4ca3
Compare
|
(Whoops!) |
|
Here's the results of Notes:
Given the deep cuts made here, I'd expect to see the native implementation get at least 5 to 10 times faster than node-glob. No longer being 6x slower is indeed a significant improvement, perhaps enough to make it worth landing this as-is and making further improvements later. But it's definitely still not what I'd call "done", by quite a bit. |
fd617c2 to
77675d2
Compare
It's not what I meant though, my question is whether you explored adopting one of the native alternatives your AI based itself on (you mentioned Rust, surely that is not JS) |
https://github.com/oxc-project/fast-glob, the Rust implementation I mentioned, isn't compliant with minimatch (e.g. negated patterns don't work). A lot of the really fast implementations aren't compatible with our current set up (as they match different globs). As @isaacs said about a different |
Signed-off-by: avivkeller <me@aviv.sh>
c7f32c9 to
721c2e2
Compare
|
Minimatch had its own tests that covered it extensively. Those tests have not been ported over to this codebase, which I think should be to guarantee backward-compatibility and no regressions. |
| paths.reserve(entries.size()); | ||
| for (const WalkEntry& entry : entries) { | ||
| Local<Value> path; | ||
| if (!ToV8Value(context, entry.path, isolate).ToLocal(&path)) { |
There was a problem hiding this comment.
Hmm... we really ought to make sure that things work with non-UTF8 characters in the file path. Not necessarily critical for this version as long as it matches what the js version does, but we should check on it.
|
Minimatch also had documentation for the glob syntax that does not appear to be preserved. The source itself could use code comments and a description of the glob syntax and rules. |
|
I plan on taking an additional detailed review pass on this over the next day or two. I know you have two sign offs already. I'd appreciate if you let it sit for a few days for review before landing. |
|
Of course! |
| const CompileFlags& flags) { | ||
| std::vector<PatternString> matched; | ||
| for (size_t i = 0; i + 1 < glob_parts->size(); i++) { | ||
| for (size_t j = i + 1; j < glob_parts->size(); j++) { |
There was a problem hiding this comment.
The double nesting here is rather awkward. Might be worthwhile splitting these out into two separate functions.
| char16_t close = 0xFDD1; | ||
| }; | ||
|
|
||
| BraceSentinels ChooseSentinels(PatternView pattern) { |
There was a problem hiding this comment.
If I'm reading this function correctly, you've got a minor correctness issue here. Someone could theoretically craft a patterns ≥ 65KB containing every code unit U+0080..U+FFFF ... consuming all possible sentinels.
There was a problem hiding this comment.
In the unlikely scenario that a pattern is both > 65412 characters (the sentinel search length) and < 65536 characters (the max pattern length), yes, this would collide.
Let me see if I can think of a way around this without lowering the max, if I can't, I can lower the max by 124 characters and call this a breaking change (although, the percent of clients being broken would be infinitesimal)
There was a problem hiding this comment.
Okay, I think the best solution would be if the string contains every possible unicode value, while being less than the max length, we'd throw a kPatternTooLong. That makes this a breaking change
|
ok, handful of additional comments. Main issue is that test coverage needs to be expanded. couple of bugs but overall solid. |
ecd22dd to
fd43e85
Compare
|
Breaking change since a string that contains every unicode character will now throw an error (that being said, this should never occur in pretty much any case). But, it's still a change. |
Ref: https://openjs-foundation.slack.com/archives/C019Y2T6STH/p1787069174254509
(AI assistance used to help port minimatch to C++ and take improvements from other implementations, e.g. Rust's fast-glob [oxc] and picomatch, etc).
I'd love some pretty pedantic reviews since this is substantial. It's 1:1 with the existing glob, so it's non-breaking, but please tear this apart just in case. This implementation is over 2x faster than the old one.
Benchmarking Result
AI-Assisted-By: Claude Fable 5, Claude Opus 5