Glob

Use Bun's fast native implementation of file globbing

Quickstart#

Scan a directory for files matching *.ts:

import { Glob } from "bun";

const glob = new Glob("**/*.ts");

// Scans the current working directory and each of its sub-directories recursively
for await (const file of glob.scan(".")) {
  console.log(file); // => "index.ts"
}

Match a string against a glob pattern:

import { Glob } from "bun";

const glob = new Glob("*.ts");

glob.match("index.ts"); // => true
glob.match("index.js"); // => false

To match many paths against one pattern, create the Glob once and reuse it. With any pattern, the time match() takes grows linearly with the length of the path.

The Glob class implements the following interface:

class Glob {
  scan(root: string | ScanOptions): AsyncIterable<string>;
  scanSync(root: string | ScanOptions): Iterable<string>;

  match(path: string): boolean;
}

interface ScanOptions {
  /**
   * The root directory to start matching from. Defaults to `process.cwd()`
   */
  cwd?: string;

  /**
   * Allow patterns to match entries that begin with a period (`.`).
   *
   * @default false
   */
  dot?: boolean;

  /**
   * Return the absolute path for entries.
   *
   * @default false
   */
  absolute?: boolean;

  /**
   * Indicates whether to traverse descendants of symbolic link directories.
   *
   * @default false
   */
  followSymlinks?: boolean;

  /**
   * Throw an error when symbolic link is broken
   *
   * @default false
   */
  throwErrorOnBrokenSymlink?: boolean;

  /**
   * Return only files.
   *
   * @default true
   */
  onlyFiles?: boolean;
}

Supported Glob Patterns#

Bun supports the following glob patterns:

? - Match any single character#

const glob = new Glob("???.ts");
glob.match("foo.ts"); // => true
glob.match("foobar.ts"); // => false

* - Matches zero or more characters, except for path separators (/ or \)#

const glob = new Glob("*.ts");
glob.match("index.ts"); // => true
glob.match("src/index.ts"); // => false

** - Match any number of characters including /#

const glob = new Glob("**/*.ts");
glob.match("index.ts"); // => true
glob.match("src/index.ts"); // => true
glob.match("src/index.js"); // => false

[ab] - Matches one of the characters contained in the brackets, as well as character ranges#

const glob = new Glob("ba[rz].ts");
glob.match("bar.ts"); // => true
glob.match("baz.ts"); // => true
glob.match("bat.ts"); // => false

You can use character ranges (for example [0-9], [a-z]). The negation operators ^ or ! match anything except the characters in the brackets (for example [^ab], [!a-z]).

const glob = new Glob("ba[a-z][0-9][^4-9].ts");
glob.match("bar01.ts"); // => true
glob.match("baz83.ts"); // => true
glob.match("bat22.ts"); // => true
glob.match("bat24.ts"); // => false
glob.match("ba0a8.ts"); // => false

{a,b,c} - Match any of the given patterns#

const glob = new Glob("{a,b,c}.ts");
glob.match("a.ts"); // => true
glob.match("b.ts"); // => true
glob.match("c.ts"); // => true
glob.match("d.ts"); // => false

You can nest these patterns to any depth, and they can contain any of the other wildcards.

{1..3} - Match a range of numbers or letters#

const glob = new Glob("v{1..3}.ts");
glob.match("v1.ts"); // => true
glob.match("v3.ts"); // => true
glob.match("v4.ts"); // => false
PatternMatches
{1..3}1, 2, 3
{01..10}01, 02, ..., 10 (zero-padded)
{1..9..2}1, 3, 5, 7, 9 (step of 2)
{a..e}a, b, c, d, e

@(a|b) - Extended glob patterns#

A ?, *, +, @ or ! directly before a parenthesized list of patterns says how many of them to match.

PatternMatches
?(a|b)zero or one of the patterns
*(a|b)zero or more of the patterns
+(a|b)one or more of the patterns
@(a|b)exactly one of the patterns
!(a|b)anything in a path segment except the patterns
const glob = new Glob("!(*.test).ts");
glob.match("index.ts"); // => true
glob.match("index.test.ts"); // => false

Parentheses without one of these prefixes are literal, so app/(marketing)/** matches the directory app/(marketing).

! - Negates the result at the start of a pattern#

const glob = new Glob("!index.ts");
glob.match("index.ts"); // => false
glob.match("foo.ts"); // => true

\ - Escapes any of the special characters above#

const glob = new Glob("\\!index.ts");
glob.match("!index.ts"); // => true
glob.match("index.ts"); // => false

Node.js fs.glob() compatibility#

Bun also implements Node.js's fs.glob() functions:

import { glob, globSync, promises } from "node:fs";

// Array of patterns
const files = await Array.fromAsync(promises.glob(["**/*.ts", "**/*.js"]));

// Exclude patterns
const filtered = await Array.fromAsync(
  promises.glob("**/*", {
    exclude: ["node_modules/**", "**/*.test.*"],
  }),
);

All three functions (fs.glob(), fs.globSync(), fs.promises.glob()) support:

  • Array of patterns as the first argument
  • exclude option to filter results