Documentation Index
Fetch the complete documentation index at: https://mintlify.com/zhcndoc/bun/llms.txt
Use this file to discover all available pages before exploring further.
Bun provides a fast, cross-platform glob matcher for finding files using wildcard patterns. It’s built in Zig and optimized for performance.
Basic Usage
import { Glob } from "bun";
// Create a glob pattern
const glob = new Glob("**/*.ts");
// Scan for matching files
for await (const file of glob.scan(".")) {
console.log(file); // "src/index.ts", "test/util.ts", etc.
}
Glob Patterns
Wildcards
// * matches any characters except /
const glob = new Glob("*.txt");
// Matches: file.txt, data.txt
// Not: dir/file.txt
// ** matches any characters including /
const glob2 = new Glob("**/*.js");
// Matches: file.js, src/index.js, src/lib/util.js
// ? matches single character
const glob3 = new Glob("file?.txt");
// Matches: file1.txt, fileA.txt
// Not: file.txt, file10.txt
Character Classes
// [abc] matches any character in brackets
const glob = new Glob("file[123].txt");
// Matches: file1.txt, file2.txt, file3.txt
// [a-z] matches range
const glob2 = new Glob("[a-z]*.js");
// Matches: app.js, utils.js
// Not: App.js, 1.js
// [!abc] matches any character not in brackets
const glob3 = new Glob("*[!0-9].txt");
// Matches: data.txt, fileA.txt
// Not: file1.txt, data5.txt
Braces
// {a,b,c} matches alternatives
const glob = new Glob("*.{js,ts,jsx,tsx}");
// Matches: index.js, App.tsx, util.ts
// Can be nested
const glob2 = new Glob("{src,test}/**/*.{js,ts}");
// Matches: src/index.js, test/util.ts
// Can contain multiple patterns
const glob3 = new Glob("file.{txt,md,json}");
// Matches: file.txt, file.md, file.json
Negation
// ! at start negates pattern
const glob = new Glob("!*.test.js");
// Matches everything except test files
Scanning Files
Async Iteration
Most efficient for large directories:
const glob = new Glob("**/*.js");
for await (const file of glob.scan("./src")) {
console.log(file);
}
Array Result
Collect all matches:
const glob = new Glob("**/*.ts");
const files = await Array.fromAsync(glob.scan("."));
console.log(files);
// ["src/index.ts", "src/util.ts", ...]
Sync Scanning
const glob = new Glob("*.json");
const files = glob.scanSync(".");
// Returns array of matches
Scan Options
Current Working Directory
const glob = new Glob("*.js");
// Scan from specific directory
for await (const file of glob.scan("/path/to/dir")) {
console.log(file);
}
// Use options object
for await (const file of glob.scan({ cwd: "./src" })) {
console.log(file);
}
Dot Files
Include hidden files:
const glob = new Glob("*");
// Exclude dot files (default)
for await (const file of glob.scan({
cwd: ".",
dot: false,
})) {
console.log(file); // Won't include .gitignore, .env, etc.
}
// Include dot files
for await (const file of glob.scan({
cwd: ".",
dot: true,
})) {
console.log(file); // Includes .gitignore, .env, etc.
}
Absolute Paths
Return full paths instead of relative:
const glob = new Glob("**/*.js");
for await (const file of glob.scan({
cwd: "./src",
absolute: true,
})) {
console.log(file); // "/Users/me/project/src/index.js"
}
Only Files
Exclude directories from results:
const glob = new Glob("**/*");
for await (const file of glob.scan({
onlyFiles: true, // default
})) {
console.log(file); // Only files, no directories
}
for await (const path of glob.scan({
onlyFiles: false,
})) {
console.log(path); // Includes directories too
}
Follow Symlinks
const glob = new Glob("**/*.js");
for await (const file of glob.scan({
followSymlinks: true,
})) {
console.log(file); // Follows symlinked directories
}
Throw on Broken Symlinks
const glob = new Glob("**/*");
try {
for await (const file of glob.scan({
followSymlinks: true,
throwErrorOnBrokenSymlink: true,
})) {
console.log(file);
}
} catch (err) {
console.error("Broken symlink:", err);
}
Pattern Matching
Match Method
Test if a path matches the pattern:
const glob = new Glob("**/*.ts");
console.log(glob.match("src/index.ts")); // true
console.log(glob.match("src/index.js")); // false
console.log(glob.match("README.md")); // false
Multiple Patterns
Match against multiple globs:
const patterns = [
new Glob("**/*.ts"),
new Glob("**/*.tsx"),
new Glob("!**/*.test.*"),
];
function matches(path: string): boolean {
let result = false;
for (const glob of patterns) {
if (glob.pattern.startsWith("!")) {
// Negation
if (glob.match(path.slice(1))) {
result = false;
}
} else {
// Inclusion
if (glob.match(path)) {
result = true;
}
}
}
return result;
}
console.log(matches("src/index.ts")); // true
console.log(matches("src/index.test.ts")); // false
Common Patterns
All TypeScript Files
const glob = new Glob("**/*.{ts,tsx}");
All JavaScript Variants
const glob = new Glob("**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}");
Exclude node_modules
const glob = new Glob("**/*.js");
for await (const file of glob.scan(".")) {
if (file.includes("node_modules")) continue;
console.log(file);
}
Or use a function to filter:
const files = [];
for await (const file of glob.scan(".")) {
if (!file.includes("node_modules")) {
files.push(file);
}
}
Specific Directories
// Only src and test directories
const glob = new Glob("{src,test}/**/*.ts");
// Exclude build directories
const glob2 = new Glob("**/*.js");
for await (const file of glob2.scan(".")) {
if (file.match(/\/(dist|build|out)\//)) continue;
console.log(file);
}
Test Files
// Files ending in .test or .spec
const glob = new Glob("**/*.{test,spec}.{ts,js}");
// Files in __tests__ directory
const glob2 = new Glob("**/__tests__/**/*.{ts,js}");
Config Files
const glob = new Glob("*.config.{js,ts,json}");
// Matches: vite.config.js, tsconfig.json, etc.
Fast Scanning
Bun’s glob is highly optimized:
const glob = new Glob("**/*.js");
const start = performance.now();
let count = 0;
for await (const _ of glob.scan(".")) {
count++;
}
console.log(`Found ${count} files in ${performance.now() - start}ms`);
// Typically <50ms for 10,000 files
Comparison
- Bun Glob: ~200,000 files/sec
- fast-glob: ~100,000 files/sec
- node-glob: ~50,000 files/sec
Optimization Tips
-
Use specific patterns
// Faster - specific directory
const glob = new Glob("src/**/*.ts");
// Slower - scans everything
const glob2 = new Glob("**/*.ts");
-
Filter early
// Better - filter while scanning
for await (const file of glob.scan(".")) {
if (!file.includes("node_modules")) {
process(file);
}
}
// Worse - collect then filter
const all = await Array.fromAsync(glob.scan("."));
const filtered = all.filter(f => !f.includes("node_modules"));
-
Use async iteration
// Better - streams results
for await (const file of glob.scan(".")) {
process(file);
}
// Worse - buffers all in memory
const files = await Array.fromAsync(glob.scan("."));
files.forEach(process);
Use Cases
import { Glob } from "bun";
import { build } from "./builder";
const glob = new Glob("src/**/*.ts");
for await (const file of glob.scan(".")) {
await build(file);
}
File Watching
import { watch } from "fs";
import { Glob } from "bun";
const glob = new Glob("src/**/*.ts");
const watcher = watch("src", { recursive: true });
for await (const event of watcher) {
if (glob.match(event.filename)) {
console.log(`Changed: ${event.filename}`);
// Rebuild...
}
}
Testing
import { Glob } from "bun";
// Find all test files
const glob = new Glob("**/*.test.{ts,tsx}");
const testFiles = await Array.fromAsync(glob.scan("."));
// Run tests
for (const file of testFiles) {
await import(file);
}
Linting
import { Glob } from "bun";
import { lint } from "./linter";
const glob = new Glob("**/*.{ts,tsx}");
for await (const file of glob.scan("src")) {
const issues = await lint(file);
if (issues.length > 0) {
console.error(`${file}: ${issues.length} issues`);
}
}
Cleaning
import { Glob } from "bun";
import { unlink } from "fs/promises";
// Remove all .js files
const glob = new Glob("**/*.js");
for await (const file of glob.scan("dist")) {
await unlink(file);
}
Advanced Usage
Multiple Include/Exclude
function createMatcher(include: string[], exclude: string[]) {
const includeGlobs = include.map(p => new Glob(p));
const excludeGlobs = exclude.map(p => new Glob(p));
return (path: string) => {
const included = includeGlobs.some(g => g.match(path));
const excluded = excludeGlobs.some(g => g.match(path));
return included && !excluded;
};
}
const matches = createMatcher(
["**/*.ts", "**/*.tsx"],
["**/*.test.*", "**/node_modules/**"],
);
console.log(matches("src/index.ts")); // true
console.log(matches("src/index.test.ts")); // false
Glob Expansion
Expand pattern to list of files:
async function expand(pattern: string): Promise<string[]> {
const glob = new Glob(pattern);
return await Array.fromAsync(glob.scan("."));
}
const files = await expand("src/**/*.{ts,tsx}");
console.log(files);
Custom Scanner
import { Glob } from "bun";
import { stat } from "fs/promises";
class FilteredGlob {
constructor(public pattern: string) {}
async *scan(dir: string) {
const glob = new Glob(this.pattern);
for await (const file of glob.scan(dir)) {
const stats = await stat(file);
// Only files larger than 1KB
if (stats.size > 1024) {
yield file;
}
}
}
}
const glob = new FilteredGlob("**/*.js");
for await (const file of glob.scan(".")) {
console.log(file);
}
Error Handling
import { Glob } from "bun";
const glob = new Glob("**/*.ts");
try {
for await (const file of glob.scan("/nonexistent")) {
console.log(file);
}
} catch (err) {
if (err.code === "ENOENT") {
console.error("Directory not found");
} else {
throw err;
}
}
Glob patterns work identically on:
- macOS
- Linux
- Windows (uses forward slashes internally)
Windows Paths
// Use forward slashes even on Windows
const glob = new Glob("src/**/*.js");
// Not backslashes
const wrong = new Glob("src\\**\\*.js"); // Wrong!