Skip to the code
cf.

Glob matching

Write globMatch(pattern, path). It returns true when path matches the glob pattern, and false otherwise. Tools use globs like src/**/*.test.js to pick files.

Paths and patterns are made of segments separated by /, like src/utils/date.js. The pattern must match the whole path, not just part of it, and letters must match in case.

Within a segment, ? matches exactly one character, and * matches any number of characters, including none. Neither matches /, so *.js matches app.js but not src/app.js. Every other character matches only itself: ., +, (, [, and the like have no special meaning.

A segment that's exactly ** matches any number of whole segments, including none: src/**/*.js matches src/app.js and src/a/b/app.js, and docs/** matches docs, docs/intro.md, and docs/a/b. A ** that shares its segment with other characters, as in **.js, works like *.

globMatch("*.js", "app.js");                        // true
globMatch("*.js", "src/app.js");                    // false
globMatch("src/**/*.test.js", "src/date.test.js");  // true
globMatch("src/**/*.test.js", "src/a/b/x.test.js"); // true
globMatch("img/??.png", "img/01.png");              // true
globMatch("img/??.png", "img/1.png");               // false

What the tests check

  1. matches plain text exactly, case included
    expect(globMatch("src/app.js", "src/app.js")).toBe(true);
    expect(globMatch("src/app.js", "src/app.jsx")).toBe(false);
    expect(globMatch("src/app.js", "SRC/app.js")).toBe(false);
    expect(globMatch("README.md", "readme.md")).toBe(false);
    
  2. ? matches exactly one character
    expect(globMatch("img/??.png", "img/01.png")).toBe(true);
    expect(globMatch("img/??.png", "img/1.png")).toBe(false);
    expect(globMatch("img/??.png", "img/001.png")).toBe(false);
    expect(globMatch("v?", "v1")).toBe(true);
    
  3. * matches any run of characters, including none
    expect(globMatch("*.js", "app.js")).toBe(true);
    expect(globMatch("*.js", ".js")).toBe(true);
    expect(globMatch("test-*-v*.js", "test--v.js")).toBe(true);
    expect(globMatch("test-*-v*.js", "test-unit-v12.js")).toBe(true);
    expect(globMatch("*", "README")).toBe(true);
    
  4. * and ? never match a /
    expect(globMatch("*.js", "src/app.js")).toBe(false);
    expect(globMatch("src*", "src/app.js")).toBe(false);
    expect(globMatch("a?b", "a/b")).toBe(false);
    expect(globMatch("src/*", "src/lib/app.js")).toBe(false);
    expect(globMatch("*/*", "src/app.js")).toBe(true);
    
  5. matches the whole path, not part of it
    expect(globMatch("*.js", "app.json")).toBe(false);
    expect(globMatch("app", "my-app")).toBe(false);
    expect(globMatch("src", "src/app.js")).toBe(false);
    expect(globMatch("lib/*.js", "src/lib/app.js")).toBe(false);
    
  6. treats every other character literally
    expect(globMatch("file(1).txt", "file(1).txt")).toBe(true);
    expect(globMatch("file.txt", "fileXtxt")).toBe(false);
    expect(globMatch("a+b.js", "aab.js")).toBe(false);
    expect(globMatch("[ab].js", "a.js")).toBe(false);
    expect(globMatch("[ab].js", "[ab].js")).toBe(true);
    expect(globMatch("$v^{1,2}|x", "$v^{1,2}|x")).toBe(true);
    expect(globMatch("a\\d", "a\\d")).toBe(true);
    expect(globMatch("a\\d", "a5")).toBe(false);
    
  7. ** matches any number of whole segments
    expect(globMatch("src/**/*.js", "src/app.js")).toBe(true);
    expect(globMatch("src/**/*.js", "src/lib/app.js")).toBe(true);
    expect(globMatch("src/**/*.js", "src/a/b/c/app.js")).toBe(true);
    expect(globMatch("src/**/*.js", "lib/app.js")).toBe(false);
    expect(globMatch("src/**/*.js", "src/app.ts")).toBe(false);
    expect(globMatch("a/**/b", "a/xb")).toBe(false);
    
  8. ** works at the start and the end of a pattern
    expect(globMatch("**/*.md", "README.md")).toBe(true);
    expect(globMatch("**/*.md", "docs/guide/intro.md")).toBe(true);
    expect(globMatch("docs/**", "docs")).toBe(true);
    expect(globMatch("docs/**", "docs/intro.md")).toBe(true);
    expect(globMatch("docs/**", "docs/a/b/c")).toBe(true);
    expect(globMatch("docs/**", "documents/intro.md")).toBe(false);
    expect(globMatch("**", "any/path/at/all")).toBe(true);
    
  9. ** inside a segment works like *
    expect(globMatch("**.js", "app.js")).toBe(true);
    expect(globMatch("**.js", "src/app.js")).toBe(false);
    expect(globMatch("a/**b", "a/xyb")).toBe(true);
    expect(globMatch("a/**b", "a/x/yb")).toBe(false);
    

Learn more

solution.jsSaved in this browser

Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.

Ctrl + Enter runs the tests (⌘ + Enter on a Mac).