From 5d40bd97ac0d2c05e81ace7a314ec86b4d31728a Mon Sep 17 00:00:00 2001 From: Zack Radisic <56137411+zackradisic@users.noreply.github.com> Date: Sun, 31 Mar 2024 14:33:20 -0600 Subject: [PATCH 1/6] Make it work with abs patterns --- src/glob.zig | 114 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 78 insertions(+), 36 deletions(-) diff --git a/src/glob.zig b/src/glob.zig index df0d4c82891400..18e9dc6e6f1590 100644 --- a/src/glob.zig +++ b/src/glob.zig @@ -149,6 +149,9 @@ pub fn GlobWalker_( /// If the pattern contains "./" or "../" has_relative_components: bool = false, + end_byte_of_basename_excluding_special_syntax: u32 = 0, + basename_excluding_special_syntax_component_idx: u32 = 0, + patternComponents: ArrayList(Component) = .{}, matchedPaths: ArrayList(BunString) = .{}, i: u32 = 0, @@ -201,8 +204,34 @@ pub fn GlobWalker_( pub fn init(this: *Iterator) !Maybe(void) { log("Iterator init pattern={s}", .{this.walker.pattern}); + const root_work_item = brk: { + var use_posix = bun.Environment.isPosix; + const is_absolute = if (bun.Environment.isPosix) std.fs.path.isAbsolute(this.walker.pattern) else std.fs.path.isAbsolute(this.walker.pattern) or is_absolute: { + use_posix = true; + break :is_absolute std.fs.path.isAbsolutePosix(this.walker.pattern); + }; + + if (!is_absolute) break :brk WorkItem.new(this.walker.cwd, 0, .directory); + + const component_idx = this.walker.basename_excluding_special_syntax_component_idx; + var path_without_special_syntax = this.walker.pattern[0..this.walker.end_byte_of_basename_excluding_special_syntax]; + if (this.walker.pattern.len == this.walker.end_byte_of_basename_excluding_special_syntax) { + path_without_special_syntax = (if (use_posix) std.fs.path.dirnamePosix(path_without_special_syntax) else std.fs.path.dirnameWindows(path_without_special_syntax)) orelse p: { + break :p path_without_special_syntax; + }; + // component_idx -= 1; + // component_idx -= 1; + } + + break :brk WorkItem.new( + path_without_special_syntax, + component_idx, + .directory, + ); + }; + var path_buf: *[bun.MAX_PATH_BYTES]u8 = &this.walker.pathBuf; - const root_path = this.walker.cwd; + const root_path = root_work_item.path; @memcpy(path_buf[0..root_path.len], root_path[0..root_path.len]); path_buf[root_path.len] = 0; const root_path_z = path_buf[0..root_path.len :0]; @@ -217,7 +246,6 @@ pub fn GlobWalker_( this.cwd_fd = cwd_fd; - const root_work_item = WorkItem.new(this.walker.cwd, 0, .directory); switch (try this.transitionToDirIterState(root_work_item, true)) { .err => |err| return .{ .err = err }, else => {}, @@ -609,6 +637,13 @@ pub fn GlobWalker_( Dot, /// ../ DotBack, + + fn isSpecialSyntax(this: SyntaxHint) bool { + return switch (this) { + .Literal => false, + else => true, + }; + } }; }; @@ -646,9 +681,6 @@ pub fn GlobWalker_( const ptr = @intFromPtr(this); log("GlobWalker(0x{x}) components:", .{ptr}); for (components.items) |cmp| { - if (cmp.syntax_hint == .None) { - continue; - } switch (cmp.syntax_hint) { .Single => log(" *", .{}), .Double => log(" **", .{}), @@ -673,6 +705,8 @@ pub fn GlobWalker_( only_files: bool, ) !Maybe(void) { var patternComponents = ArrayList(Component){}; + this.basename_excluding_special_syntax_component_idx = 0; + this.end_byte_of_basename_excluding_special_syntax = @intCast(pattern.len); try GlobWalker.buildPatternComponents( arena, &patternComponents, @@ -680,6 +714,8 @@ pub fn GlobWalker_( &this.cp_len, &this.pattern_codepoints, &this.has_relative_components, + &this.end_byte_of_basename_excluding_special_syntax, + &this.basename_excluding_special_syntax_component_idx, ); this.cwd = cwd; @@ -999,19 +1035,6 @@ pub fn GlobWalker_( return name; } - fn appendMatchedPath( - this: *GlobWalker, - entry_name: []const u8, - dir_name: [:0]const u8, - ) !void { - const subdir_parts: []const []const u8 = &[_][]const u8{ - dir_name[0..dir_name.len], - entry_name, - }; - const name = try this.join(subdir_parts); - try this.matchedPaths.append(this.arena.allocator(), BunString.fromBytes(name)); - } - fn appendMatchedPathSymlink(this: *GlobWalker, symlink_full_path: []const u8) !void { const name = try this.arena.allocator().dupe(u8, symlink_full_path); try this.matchedPaths.append(this.arena.allocator(), BunString.fromBytes(name)); @@ -1073,23 +1096,21 @@ pub fn GlobWalker_( return false; } - fn addComponent( - allocator: Allocator, + fn makeComponent( pattern: []const u8, - patternComponents: *ArrayList(Component), start_cp: u32, end_cp: u32, start_byte: u32, end_byte: u32, has_relative_patterns: *bool, - ) !void { + ) ?Component { var component: Component = .{ .start = start_byte, .len = end_byte - start_byte, .start_cp = start_cp, .end_cp = end_cp, }; - if (component.len == 0) return; + if (component.len == 0) return null; out: { if (component.len == 1 and pattern[component.start] == '.') { @@ -1163,7 +1184,7 @@ pub fn GlobWalker_( component.is_ascii = true; } - try patternComponents.append(allocator, component); + return component; } fn buildPatternComponents( @@ -1173,6 +1194,8 @@ pub fn GlobWalker_( out_cp_len: *u32, out_pattern_cp: *[]u32, has_relative_patterns: *bool, + end_byte_of_basename_excluding_special_syntax: *u32, + basename_excluding_special_syntax_component_idx: *u32, ) !void { var start_cp: u32 = 0; var start_byte: u32 = 0; @@ -1182,6 +1205,7 @@ pub fn GlobWalker_( var cp_len: u32 = 0; var prevIsBackslash = false; + var saw_special = false; while (iter.next(&cursor)) : (cp_len += 1) { const c = cursor.c; @@ -1189,16 +1213,21 @@ pub fn GlobWalker_( '\\' => { if (comptime isWindows) { const end_cp = cp_len; - try addComponent( - arena.allocator(), + if (makeComponent( pattern, - patternComponents, start_cp, end_cp, start_byte, cursor.i, has_relative_patterns, - ); + )) |component| { + if (!saw_special and component.syntax_hint.isSpecialSyntax()) { + saw_special = true; + basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); + end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; + } + try patternComponents.append(arena.allocator(), component); + } start_cp = cp_len + 1; start_byte = cursor.i + cursor.width; continue; @@ -1219,16 +1248,21 @@ pub fn GlobWalker_( end_cp += 1; end_byte += cursor.width; } - try addComponent( - arena.allocator(), + if (makeComponent( pattern, - patternComponents, start_cp, end_cp, start_byte, end_byte, has_relative_patterns, - ); + )) |component| { + if (!saw_special and component.syntax_hint.isSpecialSyntax()) { + saw_special = true; + basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); + end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; + } + try patternComponents.append(arena.allocator(), component); + } start_cp = cp_len + 1; start_byte = cursor.i + cursor.width; }, @@ -1247,16 +1281,24 @@ pub fn GlobWalker_( out_pattern_cp.* = codepoints; const end_cp = cp_len; - try addComponent( - arena.allocator(), + if (makeComponent( pattern, - patternComponents, start_cp, end_cp, start_byte, @intCast(pattern.len), has_relative_patterns, - ); + )) |component| { + if (!saw_special and component.syntax_hint.isSpecialSyntax()) { + saw_special = true; + basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); + end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; + } + try patternComponents.append(arena.allocator(), component); + } + + if (!saw_special) + end_byte_of_basename_excluding_special_syntax.* = @intCast(patternComponents.items.len); } }; } From 0e77524582c376b5bfe11bd565cd8696fece98e7 Mon Sep 17 00:00:00 2001 From: Zack Radisic <56137411+zackradisic@users.noreply.github.com> Date: Sun, 31 Mar 2024 18:12:33 -0600 Subject: [PATCH 2/6] add tests --- src/glob.zig | 13 ++++++++++--- test/js/bun/glob/scan.test.ts | 10 ++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/glob.zig b/src/glob.zig index 18e9dc6e6f1590..0c4c79af50140e 100644 --- a/src/glob.zig +++ b/src/glob.zig @@ -204,6 +204,7 @@ pub fn GlobWalker_( pub fn init(this: *Iterator) !Maybe(void) { log("Iterator init pattern={s}", .{this.walker.pattern}); + var was_absolute = false; const root_work_item = brk: { var use_posix = bun.Environment.isPosix; const is_absolute = if (bun.Environment.isPosix) std.fs.path.isAbsolute(this.walker.pattern) else std.fs.path.isAbsolute(this.walker.pattern) or is_absolute: { @@ -213,14 +214,14 @@ pub fn GlobWalker_( if (!is_absolute) break :brk WorkItem.new(this.walker.cwd, 0, .directory); + was_absolute = true; + const component_idx = this.walker.basename_excluding_special_syntax_component_idx; var path_without_special_syntax = this.walker.pattern[0..this.walker.end_byte_of_basename_excluding_special_syntax]; if (this.walker.pattern.len == this.walker.end_byte_of_basename_excluding_special_syntax) { path_without_special_syntax = (if (use_posix) std.fs.path.dirnamePosix(path_without_special_syntax) else std.fs.path.dirnameWindows(path_without_special_syntax)) orelse p: { break :p path_without_special_syntax; }; - // component_idx -= 1; - // component_idx -= 1; } break :brk WorkItem.new( @@ -246,7 +247,13 @@ pub fn GlobWalker_( this.cwd_fd = cwd_fd; - switch (try this.transitionToDirIterState(root_work_item, true)) { + switch (if (was_absolute) try this.transitionToDirIterState( + root_work_item, + false, + ) else try this.transitionToDirIterState( + root_work_item, + true, + )) { .err => |err| return .{ .err = err }, else => {}, } diff --git a/test/js/bun/glob/scan.test.ts b/test/js/bun/glob/scan.test.ts index 33784723f737e1..461ea458ccbabd 100644 --- a/test/js/bun/glob/scan.test.ts +++ b/test/js/bun/glob/scan.test.ts @@ -26,6 +26,7 @@ import fg from "fast-glob"; import * as path from "path"; import { tempFixturesDir, createTempDirectoryWithBrokenSymlinks, prepareEntries } from "./util"; import { tempDirWithFiles } from "harness"; +import { TestBuilder } from "../shell/test_builder"; let origAggressiveGC = Bun.unsafe.gcAggressionLevel(); let tempBrokenSymlinksDir: string; @@ -445,6 +446,15 @@ test("glob.scan('.')", async () => { expect(entries).toContain("README.md"); }); +test("absolute path pattern should ignore cwd and start at the proper path", async () => { + const tmpdir = TestBuilder.tmpdir(); + const expected = [`${tmpdir}/bunx-foo`, `${tmpdir}/bunx-bar`, `${tmpdir}/bunx-baz`]; + await Bun.$`touch ${expected[0]}; touch ${expected[1]}; touch ${expected[2]}`; + const glob = new Glob(`${path.join(tmpdir, "bunx-*")}`); + const entries = await Array.fromAsync(glob.scan()); + expect(entries.sort()).toEqual(expected.sort()); +}); + describe("glob.scan wildcard fast path", async () => { test("works", async () => { const tempdir = tempDirWithFiles("glob-scan-wildcard-fast-path", { From b0411ce980f87aba0b90a9ed63ef1bf888fb029a Mon Sep 17 00:00:00 2001 From: Zack Radisic <56137411+zackradisic@users.noreply.github.com> Date: Sun, 31 Mar 2024 21:40:52 -0600 Subject: [PATCH 3/6] fixes --- src/glob.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/glob.zig b/src/glob.zig index 0c4c79af50140e..57bdde5d251724 100644 --- a/src/glob.zig +++ b/src/glob.zig @@ -1266,7 +1266,7 @@ pub fn GlobWalker_( if (!saw_special and component.syntax_hint.isSpecialSyntax()) { saw_special = true; basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); - end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; + end_byte_of_basename_excluding_special_syntax.* = cursor.i; } try patternComponents.append(arena.allocator(), component); } @@ -1299,7 +1299,7 @@ pub fn GlobWalker_( if (!saw_special and component.syntax_hint.isSpecialSyntax()) { saw_special = true; basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); - end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; + end_byte_of_basename_excluding_special_syntax.* = cursor.i; } try patternComponents.append(arena.allocator(), component); } From fd508ddc006022bfb68ed124ad3a6a37b2067153 Mon Sep 17 00:00:00 2001 From: Zack Radisic <56137411+zackradisic@users.noreply.github.com> Date: Tue, 16 Apr 2024 22:57:34 +0200 Subject: [PATCH 4/6] Make all the ported fast-glob tests work for absolute path patterns, fix some stuff --- src/glob.zig | 92 ++- src/shell/interpreter.zig | 2 +- .../bun/glob/__snapshots__/scan.test.ts.snap | 525 ++++++++++++++++++ test/js/bun/glob/scan.test.ts | 221 ++++++-- test/js/bun/glob/util.ts | 4 +- 5 files changed, 771 insertions(+), 73 deletions(-) diff --git a/src/glob.zig b/src/glob.zig index f5a3719fc3dd7c..3690a3fc5f5de5 100644 --- a/src/glob.zig +++ b/src/glob.zig @@ -335,6 +335,9 @@ pub fn GlobWalker_( get_next, /// Currently iterating over a directory directory: Directory, + /// A pattern with no special glob syntax was supplied, for example: `/Users/zackradisic/foo/bar` + /// In that case, we stat the file on initialization + root_matched: ?[:0]const u8, const Directory = struct { fd: Accessor.Handle, @@ -376,14 +379,33 @@ pub fn GlobWalker_( was_absolute = true; - const component_idx = this.walker.basename_excluding_special_syntax_component_idx; - var path_without_special_syntax = this.walker.pattern[0..this.walker.end_byte_of_basename_excluding_special_syntax]; - if (this.walker.pattern.len == this.walker.end_byte_of_basename_excluding_special_syntax) { - path_without_special_syntax = (if (use_posix) std.fs.path.dirnamePosix(path_without_special_syntax) else std.fs.path.dirnameWindows(path_without_special_syntax)) orelse p: { - break :p path_without_special_syntax; + const path_without_special_syntax = this.walker.pattern[0..this.walker.end_byte_of_basename_excluding_special_syntax]; + const component_idx = this.walker.basename_excluding_special_syntax_component_idx + 1; + + // This means we got a pattern without any special glob syntax, for example: + // `/Users/zackradisic/foo/bar` + // In that case we don't need to do any walking and can just open up the FS entry + if (component_idx >= this.walker.patternComponents.items.len) { + const path = try this.walker.arena.allocator().dupeZ(u8, path_without_special_syntax); + const fd = switch (try Accessor.open(path)) { + .err => |e| { + if (e.getErrno() == bun.C.E.NOTDIR) { + // TODO check symlink + this.iter_state = .{ .root_matched = path }; + return Maybe(void).success; + } + const errpath = try this.walker.arena.allocator().dupeZ(u8, path); + return .{ .err = e.withPath(errpath) }; + }, + .result => |fd| fd, }; + _ = Accessor.close(fd); + this.iter_state = .{ .root_matched = path }; + return Maybe(void).success; } + bun.assert(this.walker.end_byte_of_basename_excluding_special_syntax < this.walker.pattern.len); + break :brk WorkItem.new( path_without_special_syntax, component_idx, @@ -549,6 +571,11 @@ pub fn GlobWalker_( pub fn next(this: *Iterator) !Maybe(?MatchedPath) { while (true) { switch (this.iter_state) { + .root_matched => { + const maybe_matched = this.iter_state.root_matched; + this.iter_state.root_matched = null; + return .{ .result = maybe_matched }; + }, .get_next => { // Done if (this.walker.workbuf.items.len == 0) return .{ .result = null }; @@ -785,6 +812,7 @@ pub fn GlobWalker_( len: u32, syntax_hint: SyntaxHint = .None, + trailing_sep: bool = false, is_ascii: bool = false, /// Only used when component is not ascii @@ -792,6 +820,14 @@ pub fn GlobWalker_( start_cp: u32 = 0, end_cp: u32 = 0, + pub fn patternSlice(this: *const Component, pattern: []const u8) []const u8 { + return pattern[this.start .. this.start + this.len - @as(u1, @bitCast(this.trailing_sep))]; + } + + pub fn patternSliceCp(this: *const Component, pattern: []u32) []u32 { + return pattern[this.start_cp .. this.end_cp - @as(u1, @bitCast(this.trailing_sep))]; + } + const SyntaxHint = enum { None, Single, @@ -854,7 +890,7 @@ pub fn GlobWalker_( .Double => log(" **", .{}), .Dot => log(" .", .{}), .DotBack => log(" ../", .{}), - .Literal, .WildcardFilepath, .None => log(" hint={s} component_str={s}", .{ @tagName(cmp.syntax_hint), pattern[cmp.start .. cmp.start + cmp.len] }), + .Literal, .WildcardFilepath, .None => log(" hint={s} component_str={s}", .{ @tagName(cmp.syntax_hint), cmp.patternSlice(pattern) }), } } } @@ -1116,6 +1152,8 @@ pub fn GlobWalker_( pattern: *Component, next_pattern: ?*Component, ) bool { + if (pattern.trailing_sep) return false; + // Handle case b) if (!is_last) return pattern.syntax_hint == .Double and component_idx + 1 == this.patternComponents.items.len -| 1 and @@ -1138,11 +1176,11 @@ pub fn GlobWalker_( return switch (pattern_component.syntax_hint) { .Double, .Single => true, .WildcardFilepath => if (comptime !isWindows) - matchWildcardFilepath(this.pattern[pattern_component.start .. pattern_component.start + pattern_component.len], filepath) + matchWildcardFilepath(pattern_component.patternSlice(this.pattern), filepath) else this.matchPatternSlow(pattern_component, filepath), .Literal => if (comptime !isWindows) - matchWildcardLiteral(this.pattern[pattern_component.start .. pattern_component.start + pattern_component.len], filepath) + matchWildcardLiteral(pattern_component.patternSlice(this.pattern), filepath) else this.matchPatternSlow(pattern_component, filepath), else => this.matchPatternSlow(pattern_component, filepath), @@ -1154,7 +1192,7 @@ pub fn GlobWalker_( if (comptime !isWindows) { if (pattern_component.is_ascii and isAllAscii(filepath)) return GlobAscii.match( - this.pattern[pattern_component.start .. pattern_component.start + pattern_component.len], + pattern_component.patternSlice(this.pattern), filepath, ); } @@ -1174,16 +1212,16 @@ pub fn GlobWalker_( } fn componentStringUnicodeWindows(this: *GlobWalker, pattern_component: *Component) []const u32 { - return this.pattern_codepoints[pattern_component.start_cp..pattern_component.end_cp]; + return pattern_component.patternSliceCp(this.pattern_codepoints); } fn componentStringUnicodePosix(this: *GlobWalker, pattern_component: *Component) []const u32 { - if (pattern_component.unicode_set) return this.pattern_codepoints[pattern_component.start_cp..pattern_component.end_cp]; + if (pattern_component.unicode_set) return pattern_component.patternSliceCp(this.pattern_codepoints); - const codepoints = this.pattern_codepoints[pattern_component.start_cp..pattern_component.end_cp]; + const codepoints = pattern_component.patternSliceCp(this.pattern_codepoints); GlobWalker.convertUtf8ToCodepoints( codepoints, - this.pattern[pattern_component.start .. pattern_component.start + pattern_component.len], + pattern_component.patternSlice(this.pattern), ); pattern_component.unicode_set = true; return codepoints; @@ -1353,6 +1391,12 @@ pub fn GlobWalker_( component.is_ascii = true; } + if (pattern[component.start + component.len -| 1] == '/') { + component.trailing_sep = true; + } else if (comptime bun.Environment.isWindows) { + component.trailing_sep = pattern[component.start + component.len -| 1] == '\\'; + } + return component; } @@ -1390,8 +1434,8 @@ pub fn GlobWalker_( cursor.i, has_relative_patterns, )) |component| { - if (!saw_special and component.syntax_hint.isSpecialSyntax()) { - saw_special = true; + saw_special = saw_special or component.syntax_hint.isSpecialSyntax(); + if (!saw_special) { basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; } @@ -1425,10 +1469,10 @@ pub fn GlobWalker_( end_byte, has_relative_patterns, )) |component| { - if (!saw_special and component.syntax_hint.isSpecialSyntax()) { - saw_special = true; + saw_special = saw_special or component.syntax_hint.isSpecialSyntax(); + if (!saw_special) { basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); - end_byte_of_basename_excluding_special_syntax.* = cursor.i; + end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; } try patternComponents.append(arena.allocator(), component); } @@ -1458,16 +1502,16 @@ pub fn GlobWalker_( @intCast(pattern.len), has_relative_patterns, )) |component| { - if (!saw_special and component.syntax_hint.isSpecialSyntax()) { - saw_special = true; + saw_special = saw_special or component.syntax_hint.isSpecialSyntax(); + if (!saw_special) { basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); - end_byte_of_basename_excluding_special_syntax.* = cursor.i; + end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; } try patternComponents.append(arena.allocator(), component); + } else if (!saw_special) { + basename_excluding_special_syntax_component_idx.* = @intCast(patternComponents.items.len); + end_byte_of_basename_excluding_special_syntax.* = cursor.i + cursor.width; } - - if (!saw_special) - end_byte_of_basename_excluding_special_syntax.* = @intCast(patternComponents.items.len); } }; } diff --git a/src/shell/interpreter.zig b/src/shell/interpreter.zig index 85185028184bc3..584ea66c4086c0 100644 --- a/src/shell/interpreter.zig +++ b/src/shell/interpreter.zig @@ -2401,7 +2401,7 @@ pub const Interpreter = struct { var iter = GlobWalker.Iterator{ .walker = this.walker }; defer iter.deinit(); - switch (try iter.init()) { + switch (iter.init() catch |e| OOM(e)) { .err => |err| return .{ .err = err }, else => {}, } diff --git a/test/js/bun/glob/__snapshots__/scan.test.ts.snap b/test/js/bun/glob/__snapshots__/scan.test.ts.snap index 61d0048c425c0a..e756e12afb8a23 100644 --- a/test/js/bun/glob/__snapshots__/scan.test.ts.snap +++ b/test/js/bun/glob/__snapshots__/scan.test.ts.snap @@ -578,3 +578,528 @@ exports[`fast-glob e2e tests only files (cwd) **/*: **/* 1`] = ` "third/library/b/book.md", ] `; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/*: absolute: fixtures/* 1`] = ` +[ + "/fixtures/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/**: absolute: fixtures/** 1`] = ` +[ + "/fixtures/file.md", + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", + "/fixtures/third/library/a/book.md", + "/fixtures/third/library/b/book.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/**/*: absolute: fixtures/**/* 1`] = ` +[ + "/fixtures/file.md", + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", + "/fixtures/third/library/a/book.md", + "/fixtures/third/library/b/book.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/*/nested: absolute: fixtures/*/nested 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/*/nested/*: absolute: fixtures/*/nested/* 1`] = ` +[ + "/fixtures/first/nested/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/*/nested/**: absolute: fixtures/*/nested/** 1`] = ` +[ + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/*/nested/**/*: absolute: fixtures/*/nested/**/* 1`] = ` +[ + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/**/nested/*: absolute: fixtures/**/nested/* 1`] = ` +[ + "/fixtures/first/nested/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/**/nested/**: absolute: fixtures/**/nested/** 1`] = ` +[ + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/**/nested/**/*: absolute: fixtures/**/nested/**/* 1`] = ` +[ + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/{first,second}: absolute: fixtures/{first,second} 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/{first,second}/*: absolute: fixtures/{first,second}/* 1`] = ` +[ + "/fixtures/first/file.md", + "/fixtures/second/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/{first,second}/**: absolute: fixtures/{first,second}/** 1`] = ` +[ + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/{first,second}/**/*: absolute: fixtures/{first,second}/**/* 1`] = ` +[ + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/*/{first,second}/*: absolute: fixtures/*/{first,second}/* 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/*/{first,second}/*/{nested,file.md}: absolute: fixtures/*/{first,second}/*/{nested,file.md} 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/**/{first,second}/**: absolute: fixtures/**/{first,second}/** 1`] = ` +[ + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/**/{first,second}/{nested,file.md}: absolute: fixtures/**/{first,second}/{nested,file.md} 1`] = ` +[ + "/fixtures/first/file.md", + "/fixtures/second/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/**/{first,second}/**/{nested,file.md}: absolute: fixtures/**/{first,second}/**/{nested,file.md} 1`] = ` +[ + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/{first,second}/{nested,file.md}: absolute: fixtures/{first,second}/{nested,file.md} 1`] = ` +[ + "/fixtures/first/file.md", + "/fixtures/second/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/{first,second}/*/nested/*: absolute: fixtures/{first,second}/*/nested/* 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/{first,second}/**/nested/**: absolute: fixtures/{first,second}/**/nested/** 1`] = ` +[ + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/*/{nested,file.md}/*: absolute: fixtures/*/{nested,file.md}/* 1`] = ` +[ + "/fixtures/first/nested/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular fixtures/**/{nested,file.md}/*: absolute: fixtures/**/{nested,file.md}/* 1`] = ` +[ + "/fixtures/first/nested/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular ./fixtures/*: absolute: ./fixtures/* 1`] = ` +[ + "/fixtures/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd *: absolute: * 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd **: absolute: ** 1`] = ` +[ + "/fixtures/file.md", + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", + "/fixtures/third/library/a/book.md", + "/fixtures/third/library/b/book.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd **/*: absolute: **/* 1`] = ` +[ + "/fixtures/file.md", + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", + "/fixtures/third/library/a/book.md", + "/fixtures/third/library/b/book.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd */nested: absolute: */nested 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd */nested/*: absolute: */nested/* 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd */nested/**: absolute: */nested/** 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd */nested/**/*: absolute: */nested/**/* 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd **/nested/*: absolute: **/nested/* 1`] = ` +[ + "/fixtures/first/nested/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd **/nested/**: absolute: **/nested/** 1`] = ` +[ + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd **/nested/**/*: absolute: **/nested/**/* 1`] = ` +[ + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd {first,second}: absolute: {first,second} 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd {first,second}/*: absolute: {first,second}/* 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd {first,second}/**: absolute: {first,second}/** 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd {first,second}/**/*: absolute: {first,second}/**/* 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd */{first,second}/*: absolute: */{first,second}/* 1`] = ` +[ + "/fixtures/first/file.md", + "/fixtures/second/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd */{first,second}/*/{nested,file.md}: absolute: */{first,second}/*/{nested,file.md} 1`] = ` +[ + "/fixtures/first/nested/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd **/{first,second}/**: absolute: **/{first,second}/** 1`] = ` +[ + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd **/{first,second}/{nested,file.md}: absolute: **/{first,second}/{nested,file.md} 1`] = ` +[ + "/fixtures/first/file.md", + "/fixtures/second/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd **/{first,second}/**/{nested,file.md}: absolute: **/{first,second}/**/{nested,file.md} 1`] = ` +[ + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd {first,second}/{nested,file.md}: absolute: {first,second}/{nested,file.md} 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd {first,second}/*/nested/*: absolute: {first,second}/*/nested/* 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd {first,second}/**/nested/**: absolute: {first,second}/**/nested/** 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd */{nested,file.md}/*: absolute: */{nested,file.md}/* 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular cwd **/{nested,file.md}/*: absolute: **/{nested,file.md}/* 1`] = ` +[ + "/fixtures/first/nested/file.md", + "/fixtures/second/nested/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular relative cwd ./*: absolute: ./* 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular relative cwd ./*: absolute: ./* 2`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular relative cwd ./**: absolute: ./** 1`] = ` +[ + "/fixtures/file.md", + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", + "/fixtures/third/library/a/book.md", + "/fixtures/third/library/b/book.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular relative cwd ./**/*: absolute: ./**/* 1`] = ` +[ + "/fixtures/file.md", + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", + "/fixtures/third/library/a/book.md", + "/fixtures/third/library/b/book.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular relative cwd ../*: absolute: ../* 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular relative cwd ../**: absolute: ../** 1`] = ` +[ + "/fixtures/file.md", + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", + "/fixtures/third/library/a/book.md", + "/fixtures/third/library/b/book.md", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns regular relative cwd ../../*: absolute: ../../* 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular relative cwd ../{first,second}: absolute: ../{first,second} 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns regular relative cwd ./../*: absolute: ./../* 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) patterns absolute cwd *: absolute: * 1`] = `[]`; + +exports[`fast-glob e2e tests patterns absolute cwd *: absolute: * 1`] = ` +[ + "", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns absolute cwd **: absolute: ** 1`] = ` +[ + "/fixtures/file.md", + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", + "/fixtures/third/library/a/book.md", + "/fixtures/third/library/b/book.md", +] +`; + +exports[`fast-glob e2e tests patterns absolute cwd **: absolute: ** 1`] = ` +[ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", +] +`; + +exports[`fast-glob e2e tests (absolute) patterns absolute cwd **/*: absolute: **/* 1`] = ` +[ + "/fixtures/file.md", + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", + "/fixtures/third/library/a/book.md", + "/fixtures/third/library/b/book.md", +] +`; + +exports[`fast-glob e2e tests patterns absolute cwd **/*: absolute: **/* 1`] = ` +[ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", +] +`; + +exports[`fast-glob e2e tests (absolute) only files fixtures/*: absolute: fixtures/* 1`] = ` +[ + "/fixtures/file.md", +] +`; + +exports[`fast-glob e2e tests (absolute) only files fixtures/**: absolute: fixtures/** 1`] = ` +[ + "/fixtures/file.md", + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", + "/fixtures/third/library/a/book.md", + "/fixtures/third/library/b/book.md", +] +`; + +exports[`fast-glob e2e tests (absolute) only files fixtures/**/*: absolute: fixtures/**/* 1`] = ` +[ + "/fixtures/file.md", + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", + "/fixtures/third/library/a/book.md", + "/fixtures/third/library/b/book.md", +] +`; + +exports[`fast-glob e2e tests (absolute) only files (cwd) *: absolute: * 1`] = `[]`; + +exports[`fast-glob e2e tests (absolute) only files (cwd) **: absolute: ** 1`] = ` +[ + "/fixtures/file.md", + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", + "/fixtures/third/library/a/book.md", + "/fixtures/third/library/b/book.md", +] +`; + +exports[`fast-glob e2e tests (absolute) only files (cwd) **/*: absolute: **/* 1`] = ` +[ + "/fixtures/file.md", + "/fixtures/first/file.md", + "/fixtures/first/nested/directory/file.json", + "/fixtures/first/nested/directory/file.md", + "/fixtures/first/nested/file.md", + "/fixtures/second/file.md", + "/fixtures/second/nested/directory/file.md", + "/fixtures/second/nested/file.md", + "/fixtures/third/library/a/book.md", + "/fixtures/third/library/b/book.md", +] +`; diff --git a/test/js/bun/glob/scan.test.ts b/test/js/bun/glob/scan.test.ts index 461ea458ccbabd..d999b625cdb2c5 100644 --- a/test/js/bun/glob/scan.test.ts +++ b/test/js/bun/glob/scan.test.ts @@ -27,6 +27,8 @@ import * as path from "path"; import { tempFixturesDir, createTempDirectoryWithBrokenSymlinks, prepareEntries } from "./util"; import { tempDirWithFiles } from "harness"; import { TestBuilder } from "../shell/test_builder"; +import * as os from "node:os"; +import * as fs from "node:fs"; let origAggressiveGC = Bun.unsafe.gcAggressionLevel(); let tempBrokenSymlinksDir: string; @@ -300,10 +302,6 @@ const onlyFilesPatterns = { ], }; -beforeAll(() => { - tempFixturesDir(); -}); - /** * These are the e2e tests from fast-glob, with some omitted because we don't support features like ignored patterns * The snapshots are generated by running fast-glob on them first @@ -313,67 +311,160 @@ beforeAll(() => { * In practice this discrepancy makes no difference, so the snapshots were changed accordingly to match Bun.Glob / Unix bash shell style. */ describe("fast-glob e2e tests", async () => { + let absolute_pattern_dir: string = ""; + // beforeAll(() => { + tempFixturesDir(); + const tmp = os.tmpdir(); + absolute_pattern_dir = fs.mkdtempSync(path.join(tmp, "absolute_patterns")); + // add some more directories so patterns like ../**/* don't break + absolute_pattern_dir = path.join(absolute_pattern_dir, "ooga/booga"); + fs.mkdirSync(absolute_pattern_dir, { recursive: true })!; + tempFixturesDir(absolute_pattern_dir); + // }); + + let buildsnapshot = false; const absoluteCwd = process.cwd(); const cwd = import.meta.dir; console.log("CWD IS", cwd); + const stripAbsoluteDir = (path: string): string => path.slice(absolute_pattern_dir.length); + // const stripAbsoluteDir = (path: string): string => path; + + regular.regular.forEach(pattern => { + console.log("ABSOLUTE PATTERN DIR", absolute_pattern_dir); + const absolutePattern = path.join(absolute_pattern_dir, pattern); + test(`(absolute) patterns regular ${pattern}`, () => { + let entries = buildsnapshot + ? prepareEntries(fg.globSync(absolutePattern, { cwd })) + : prepareEntries(Array.from(new Glob(absolutePattern).scanSync({ cwd, followSymlinks: true }))); + + console.log("PATTERN", absolutePattern, entries); + expect(entries.map(stripAbsoluteDir)).toMatchSnapshot(`absolute: ${pattern}`); + }); - regular.regular.forEach(pattern => test(`patterns regular ${pattern}`, () => { - // let entries = fg.globSync(pattern, { cwd }); - const entries = prepareEntries(Array.from(new Glob(pattern).scanSync({ cwd, followSymlinks: true }))); + let entries = buildsnapshot + ? prepareEntries(fg.globSync(pattern, { cwd })) + : prepareEntries(Array.from(new Glob(pattern).scanSync({ cwd, followSymlinks: true }))); + expect(entries).toMatchSnapshot(pattern); - }), - ); + }); + }); + + regular.cwd.forEach(({ pattern, cwd: secondHalf }) => { + const absolutePattern = path.join(absolute_pattern_dir, pattern); + test(`(absolute) patterns regular cwd ${pattern}`, () => { + const testCwd = path.join(cwd, secondHalf); + let entries = buildsnapshot + ? prepareEntries(fg.globSync(absolutePattern, { cwd: testCwd })) + : prepareEntries(Array.from(new Glob(absolutePattern).scanSync({ cwd: testCwd, followSymlinks: true }))); + + // let entries = ; + expect(entries.map(stripAbsoluteDir)).toMatchSnapshot(`absolute: ${pattern}`); + }); - regular.cwd.forEach(({ pattern, cwd: secondHalf }) => test(`patterns regular cwd ${pattern}`, () => { const testCwd = path.join(cwd, secondHalf); - // let entries = fg.globSync(pattern, { cwd: testCwd }); - let entries = prepareEntries(Array.from(new Glob(pattern).scanSync({ cwd: testCwd, followSymlinks: true }))); + let entries = buildsnapshot + ? prepareEntries(fg.globSync(pattern, { cwd: testCwd })) + : prepareEntries(Array.from(new Glob(pattern).scanSync({ cwd: testCwd, followSymlinks: true }))); expect(entries).toMatchSnapshot(pattern); - }), - ); + }); + }); + + regular.relativeCwd.forEach(({ pattern, cwd: secondHalf }) => { + const absolutePattern = path.join(absolute_pattern_dir, pattern); + test(`(absolute) patterns regular relative cwd ${pattern}`, () => { + const testCwd = secondHalf ? path.join(cwd, secondHalf) : cwd; + let entries = buildsnapshot + ? prepareEntries(fg.globSync(absolutePattern, { cwd: testCwd })) + : prepareEntries(Array.from(new Glob(absolutePattern).scanSync({ cwd: testCwd, followSymlinks: true }))); + + // let entries = + expect(entries.map(stripAbsoluteDir)).toMatchSnapshot(`absolute: ${pattern}`); + }); - regular.relativeCwd.forEach(({ pattern, cwd: secondHalf }) => test(`patterns regular relative cwd ${pattern}`, () => { const testCwd = secondHalf ? path.join(cwd, secondHalf) : cwd; - // let entries = fg.globSync(pattern, { cwd: testCwd }); - let entries = prepareEntries(Array.from(new Glob(pattern).scanSync({ cwd: testCwd, followSymlinks: true }))); + let entries = buildsnapshot + ? prepareEntries(fg.globSync(pattern, { cwd: testCwd })) + : prepareEntries(Array.from(new Glob(pattern).scanSync({ cwd: testCwd, followSymlinks: true }))); + + // let entries = expect(entries).toMatchSnapshot(pattern); - }), - ); + }); + }); + + absolutePatterns.cwd.forEach(({ pattern, cwd: secondHalf }) => { + const absolutePattern = path.join(absolute_pattern_dir, pattern); + test(`(absolute) patterns absolute cwd ${pattern}`, () => { + const testCwd = secondHalf ? path.join(cwd, secondHalf) : cwd; + let entries = buildsnapshot + ? fg.globSync(absolutePattern, { cwd: testCwd, absolute: true }) + : Array.from(new Glob(absolutePattern).scanSync({ cwd: testCwd, followSymlinks: true, absolute: true })); + // entries = entries.sort().map(entry => entry.slice(absoluteCwd.length + 1)); + entries = prepareEntries(entries); + expect(entries.map(stripAbsoluteDir)).toMatchSnapshot(`absolute: ${pattern}`); + }); - absolutePatterns.cwd.forEach(({ pattern, cwd: secondHalf }) => test(`patterns absolute cwd ${pattern}`, () => { const testCwd = secondHalf ? path.join(cwd, secondHalf) : cwd; - // let entries = fg.globSync(pattern, { cwd: testCwd, absolute: true }); - let entries = Array.from(new Glob(pattern).scanSync({ cwd: testCwd, followSymlinks: true, absolute: true })); + let entries = buildsnapshot + ? fg.globSync(pattern, { cwd: testCwd, absolute: true }) + : Array.from(new Glob(pattern).scanSync({ cwd: testCwd, followSymlinks: true, absolute: true })); + entries = entries.sort().map(entry => entry.slice(absoluteCwd.length + 1)); entries = prepareEntries(entries); - expect(entries).toMatchSnapshot(pattern); - }), - ); + expect(entries.map(stripAbsoluteDir)).toMatchSnapshot(`absolute: ${pattern}`); + }); + }); + + onlyFilesPatterns.regular.forEach(pattern => { + const absolutePattern = path.join(absolute_pattern_dir, pattern); + + test(`(absolute) only files ${pattern}`, () => { + let entries = buildsnapshot + ? prepareEntries(fg.globSync(absolutePattern, { cwd, absolute: false, onlyFiles: true })) + : prepareEntries( + Array.from(new Glob(absolutePattern).scanSync({ cwd, followSymlinks: true, onlyFiles: true })), + ); + + expect(entries.map(stripAbsoluteDir)).toMatchSnapshot(`absolute: ${pattern}`); + }); - onlyFilesPatterns.regular.forEach(pattern => test(`only files ${pattern}`, () => { - // let entries = fg.globSync(pattern, { cwd, absolute: false, onlyFiles: true }); - let entries = prepareEntries( - Array.from(new Glob(pattern).scanSync({ cwd, followSymlinks: true, onlyFiles: true })), - ); + let entries = prepareEntries(fg.globSync(pattern, { cwd, absolute: false, onlyFiles: true })); + + // let entries = prepareEntries( + // Array.from(new Glob(pattern).scanSync({ cwd, followSymlinks: true, onlyFiles: true })), + // ); expect(entries).toMatchSnapshot(pattern); - }), - ); + }); + }); + + onlyFilesPatterns.cwd.forEach(({ pattern, cwd: secondHalf }) => { + const absolutePattern = path.join(absolute_pattern_dir, pattern); + test(`(absolute) only files (cwd) ${pattern}`, () => { + const testCwd = secondHalf ? path.join(cwd, secondHalf) : cwd; + let entries = buildsnapshot + ? prepareEntries(fg.globSync(absolutePattern, { cwd: testCwd, absolute: false, onlyFiles: true })) + : prepareEntries( + Array.from(new Glob(absolutePattern).scanSync({ cwd: testCwd, followSymlinks: true, onlyFiles: true })), + ); + + expect(entries.map(stripAbsoluteDir)).toMatchSnapshot(`absolute: ${pattern}`); + }); - onlyFilesPatterns.cwd.forEach(({ pattern, cwd: secondHalf }) => test(`only files (cwd) ${pattern}`, () => { const testCwd = secondHalf ? path.join(cwd, secondHalf) : cwd; - // let entries = fg.globSync(pattern, { cwd: testCwd, absolute: false, onlyFiles: true }); - let entries = prepareEntries( - Array.from(new Glob(pattern).scanSync({ cwd: testCwd, followSymlinks: true, onlyFiles: true })), - ); + let entries = buildsnapshot + ? prepareEntries(fg.globSync(pattern, { cwd: testCwd, absolute: false, onlyFiles: true })) + : prepareEntries( + Array.from(new Glob(pattern).scanSync({ cwd: testCwd, followSymlinks: true, onlyFiles: true })), + ); + expect(entries).toMatchSnapshot(pattern); - }), - ); + }); + }); }); test("broken symlinks", async () => { @@ -446,13 +537,51 @@ test("glob.scan('.')", async () => { expect(entries).toContain("README.md"); }); -test("absolute path pattern should ignore cwd and start at the proper path", async () => { - const tmpdir = TestBuilder.tmpdir(); - const expected = [`${tmpdir}/bunx-foo`, `${tmpdir}/bunx-bar`, `${tmpdir}/bunx-baz`]; - await Bun.$`touch ${expected[0]}; touch ${expected[1]}; touch ${expected[2]}`; - const glob = new Glob(`${path.join(tmpdir, "bunx-*")}`); - const entries = await Array.fromAsync(glob.scan()); - expect(entries.sort()).toEqual(expected.sort()); +describe("trailing directory separator", async () => { + test("matches directories absolute", async () => { + const tmpdir = TestBuilder.tmpdir(); + const files = [`${tmpdir}/bunx-foo`, `${tmpdir}/bunx-bar`, `${tmpdir}/bunx-baz`]; + await Bun.$`touch ${files[0]}; touch ${files[1]}; mkdir ${files[2]}`; + const glob = new Glob(`${path.join(tmpdir, "bunx-*")}${path.sep}`); + const entries = await Array.fromAsync(glob.scan({ onlyFiles: false })); + expect(entries.sort()).toEqual(files.slice(2, 3).sort()); + }); + + test("matches directories relative", async () => { + const tmpdir = TestBuilder.tmpdir(); + const files = [`bunx-foo`, `bunx-bar`, `bunx-baz`]; + await Bun.$`touch ${files[0]}; touch ${files[1]}; mkdir ${files[2]}`.cwd(tmpdir); + const glob = new Glob(`bunx-*/`); + const entries = await Array.fromAsync(glob.scan({ onlyFiles: false, cwd: tmpdir })); + expect(entries.sort()).toEqual(files.slice(2, 3).sort()); + }); +}); + +describe("absolute path pattern", async () => { + test("works *", async () => { + const tmpdir = TestBuilder.tmpdir(); + const files = [`${tmpdir}/bunx-foo`, `${tmpdir}/bunx-bar`, `${tmpdir}/bunx-baz`]; + await Bun.$`touch ${files[0]}; touch ${files[1]}; mkdir ${files[2]}`; + const glob = new Glob(`${path.join(tmpdir, "bunx-*")}`); + const entries = await Array.fromAsync(glob.scan({ onlyFiles: false })); + expect(entries.sort()).toEqual(files.sort()); + }); + + test("works **", async () => { + const tmpdir = TestBuilder.tmpdir(); + const files = [ + `${tmpdir}/bunx-foo`, + `${tmpdir}/bunx-bar`, + `${tmpdir}/bunx-baz`, + `${tmpdir}/bunx-foo/foo`, + `${tmpdir}/bunx-foo/bar`, + `${tmpdir}/bunx-baz/bar`, + ]; + await Bun.$`mkdir -p ${files.slice(0, 3)}; touch ${files.slice(3)}`; + const glob = new Glob(`${path.join(tmpdir, "**")}${path.sep}`); + const entries = await Array.fromAsync(glob.scan({ onlyFiles: false })); + expect(entries.sort()).toEqual(files.slice(0, 3).sort()); + }); }); describe("glob.scan wildcard fast path", async () => { diff --git a/test/js/bun/glob/util.ts b/test/js/bun/glob/util.ts index c29c6ba8af6fda..47b65c0e1bf7db 100644 --- a/test/js/bun/glob/util.ts +++ b/test/js/bun/glob/util.ts @@ -19,7 +19,7 @@ export function createTempDirectoryWithBrokenSymlinks() { return tempDir; } -export function tempFixturesDir() { +export function tempFixturesDir(baseDir: string = import.meta.dir) { const files: Record> = { ".directory": { "file.md": "", @@ -61,7 +61,7 @@ export function tempFixturesDir() { return dir; } - const dir = path.join(import.meta.dir, "fixtures"); + const dir = path.join(baseDir, "fixtures"); fs.mkdirSync(dir, { recursive: true }); impl(dir, files); From 4a96c741392c94827240d317fbdd0af097d33baf Mon Sep 17 00:00:00 2001 From: Zack Radisic Date: Tue, 16 Apr 2024 15:49:08 -0700 Subject: [PATCH 5/6] fix scan test --- src/glob.zig | 10 ++++++++-- test/js/bun/glob/scan.test.ts | 20 ++++++++++---------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/glob.zig b/src/glob.zig index 3690a3fc5f5de5..ed2fe92e2822fc 100644 --- a/src/glob.zig +++ b/src/glob.zig @@ -1425,13 +1425,19 @@ pub fn GlobWalker_( switch (c) { '\\' => { if (comptime isWindows) { - const end_cp = cp_len; + var end_cp = cp_len; + var end_byte = cursor.i; + // is last char + if (cursor.i + cursor.width == pattern.len) { + end_cp += 1; + end_byte += cursor.width; + } if (makeComponent( pattern, start_cp, end_cp, start_byte, - cursor.i, + end_byte, has_relative_patterns, )) |component| { saw_special = saw_special or component.syntax_hint.isSpecialSyntax(); diff --git a/test/js/bun/glob/scan.test.ts b/test/js/bun/glob/scan.test.ts index d999b625cdb2c5..05be353d0dc514 100644 --- a/test/js/bun/glob/scan.test.ts +++ b/test/js/bun/glob/scan.test.ts @@ -330,14 +330,14 @@ describe("fast-glob e2e tests", async () => { // const stripAbsoluteDir = (path: string): string => path; regular.regular.forEach(pattern => { - console.log("ABSOLUTE PATTERN DIR", absolute_pattern_dir); + // console.log("ABSOLUTE PATTERN DIR", absolute_pattern_dir); const absolutePattern = path.join(absolute_pattern_dir, pattern); test(`(absolute) patterns regular ${pattern}`, () => { let entries = buildsnapshot ? prepareEntries(fg.globSync(absolutePattern, { cwd })) : prepareEntries(Array.from(new Glob(absolutePattern).scanSync({ cwd, followSymlinks: true }))); - console.log("PATTERN", absolutePattern, entries); + // console.log("PATTERN", absolutePattern, entries); expect(entries.map(stripAbsoluteDir)).toMatchSnapshot(`absolute: ${pattern}`); }); @@ -540,7 +540,7 @@ test("glob.scan('.')", async () => { describe("trailing directory separator", async () => { test("matches directories absolute", async () => { const tmpdir = TestBuilder.tmpdir(); - const files = [`${tmpdir}/bunx-foo`, `${tmpdir}/bunx-bar`, `${tmpdir}/bunx-baz`]; + const files = [`${tmpdir}${path.sep}bunx-foo`, `${tmpdir}${path.sep}bunx-bar`, `${tmpdir}${path.sep}bunx-baz`]; await Bun.$`touch ${files[0]}; touch ${files[1]}; mkdir ${files[2]}`; const glob = new Glob(`${path.join(tmpdir, "bunx-*")}${path.sep}`); const entries = await Array.fromAsync(glob.scan({ onlyFiles: false })); @@ -560,7 +560,7 @@ describe("trailing directory separator", async () => { describe("absolute path pattern", async () => { test("works *", async () => { const tmpdir = TestBuilder.tmpdir(); - const files = [`${tmpdir}/bunx-foo`, `${tmpdir}/bunx-bar`, `${tmpdir}/bunx-baz`]; + const files = [`${tmpdir}${path.sep}bunx-foo`, `${tmpdir}${path.sep}bunx-bar`, `${tmpdir}${path.sep}bunx-baz`]; await Bun.$`touch ${files[0]}; touch ${files[1]}; mkdir ${files[2]}`; const glob = new Glob(`${path.join(tmpdir, "bunx-*")}`); const entries = await Array.fromAsync(glob.scan({ onlyFiles: false })); @@ -570,12 +570,12 @@ describe("absolute path pattern", async () => { test("works **", async () => { const tmpdir = TestBuilder.tmpdir(); const files = [ - `${tmpdir}/bunx-foo`, - `${tmpdir}/bunx-bar`, - `${tmpdir}/bunx-baz`, - `${tmpdir}/bunx-foo/foo`, - `${tmpdir}/bunx-foo/bar`, - `${tmpdir}/bunx-baz/bar`, + `${tmpdir}${path.sep}bunx-foo`, + `${tmpdir}${path.sep}bunx-bar`, + `${tmpdir}${path.sep}bunx-baz`, + `${tmpdir}${path.sep}foo`, + `${tmpdir}${path.sep}bar`, + `${tmpdir}${path.sep}bar`, ]; await Bun.$`mkdir -p ${files.slice(0, 3)}; touch ${files.slice(3)}`; const glob = new Glob(`${path.join(tmpdir, "**")}${path.sep}`); From d5b15e9c0f5794b422de38991967db588f1a476b Mon Sep 17 00:00:00 2001 From: Zack Radisic <56137411+zackradisic@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:56:59 +0200 Subject: [PATCH 6/6] remove TestBuilder in scan.test.ts --- test/js/bun/glob/scan.test.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/test/js/bun/glob/scan.test.ts b/test/js/bun/glob/scan.test.ts index 05be353d0dc514..c7bce2126f303a 100644 --- a/test/js/bun/glob/scan.test.ts +++ b/test/js/bun/glob/scan.test.ts @@ -26,7 +26,6 @@ import fg from "fast-glob"; import * as path from "path"; import { tempFixturesDir, createTempDirectoryWithBrokenSymlinks, prepareEntries } from "./util"; import { tempDirWithFiles } from "harness"; -import { TestBuilder } from "../shell/test_builder"; import * as os from "node:os"; import * as fs from "node:fs"; @@ -537,9 +536,14 @@ test("glob.scan('.')", async () => { expect(entries).toContain("README.md"); }); +function makeTmpdir(): string { + const tmp = os.tmpdir(); + return fs.mkdtempSync(path.join(tmp, "test_builder")); +} + describe("trailing directory separator", async () => { test("matches directories absolute", async () => { - const tmpdir = TestBuilder.tmpdir(); + const tmpdir = makeTmpdir(); const files = [`${tmpdir}${path.sep}bunx-foo`, `${tmpdir}${path.sep}bunx-bar`, `${tmpdir}${path.sep}bunx-baz`]; await Bun.$`touch ${files[0]}; touch ${files[1]}; mkdir ${files[2]}`; const glob = new Glob(`${path.join(tmpdir, "bunx-*")}${path.sep}`); @@ -548,7 +552,7 @@ describe("trailing directory separator", async () => { }); test("matches directories relative", async () => { - const tmpdir = TestBuilder.tmpdir(); + const tmpdir = makeTmpdir(); const files = [`bunx-foo`, `bunx-bar`, `bunx-baz`]; await Bun.$`touch ${files[0]}; touch ${files[1]}; mkdir ${files[2]}`.cwd(tmpdir); const glob = new Glob(`bunx-*/`); @@ -559,7 +563,7 @@ describe("trailing directory separator", async () => { describe("absolute path pattern", async () => { test("works *", async () => { - const tmpdir = TestBuilder.tmpdir(); + const tmpdir = makeTmpdir(); const files = [`${tmpdir}${path.sep}bunx-foo`, `${tmpdir}${path.sep}bunx-bar`, `${tmpdir}${path.sep}bunx-baz`]; await Bun.$`touch ${files[0]}; touch ${files[1]}; mkdir ${files[2]}`; const glob = new Glob(`${path.join(tmpdir, "bunx-*")}`); @@ -568,7 +572,7 @@ describe("absolute path pattern", async () => { }); test("works **", async () => { - const tmpdir = TestBuilder.tmpdir(); + const tmpdir = makeTmpdir(); const files = [ `${tmpdir}${path.sep}bunx-foo`, `${tmpdir}${path.sep}bunx-bar`,