All files / src/node glob.ts

95.83% Statements 46/48
79.16% Branches 19/24
100% Functions 7/7
95.45% Lines 42/44

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 1071x 1x   1x     1x           83x 83x             78x   6x       6x 6x             27x 27x 27x   27x       27x 27x   26x 78x 78x     78x 1x       77x 77x 14x       77x 12x 12x             27x           1x 15x 15x   15x             15x     15x 3x 3x 3x 3x     12x 12x       15x   15x    
import * as pathModule from 'path';
import { toRegex } from 'glob-to-regex.js';
import { IGlobOptions } from './types/options';
import { pathToFilename } from './util';
import Dirent from './Dirent';
 
const { join, relative, resolve } = pathModule.posix;
 
/**
 * Check if a path matches a glob pattern
 */
function matchesPattern(path: string, pattern: string): boolean {
  const regex = toRegex(pattern);
  return regex.test(path);
}
 
/**
 * Check if a path should be excluded based on exclude patterns
 */
function isExcluded(path: string, exclude: string | string[] | ((path: string) => boolean) | undefined): boolean {
  if (!exclude) return false;
 
  Iif (typeof exclude === 'function') {
    return exclude(path);
  }
 
  const patterns = Array.isArray(exclude) ? exclude : [exclude];
  return patterns.some(pattern => matchesPattern(path, pattern));
}
 
/**
 * Walk directory tree and collect matching paths
 */
function walkDirectory(fs: any, dir: string, patterns: string[], options: IGlobOptions, currentDepth = 0): string[] {
  const results: string[] = [];
  const maxDepth = options.maxdepth ?? Infinity;
  const baseCwd = options.cwd ? pathToFilename(options.cwd as any) : process.cwd();
 
  Iif (currentDepth > maxDepth) {
    return results;
  }
 
  try {
    const entries = fs.readdirSync(dir, { withFileTypes: true }) as Dirent[];
 
    for (const entry of entries) {
      const fullPath = join(dir, entry.name.toString());
      const relativePath = relative(baseCwd, fullPath);
 
      // Skip if excluded
      if (isExcluded(relativePath, options.exclude)) {
        continue;
      }
 
      // Check if this path matches any pattern
      const matches = patterns.some(pattern => matchesPattern(relativePath, pattern));
      if (matches) {
        results.push(relativePath);
      }
 
      // Recurse into directories
      if (entry.isDirectory() && currentDepth < maxDepth) {
        const subResults = walkDirectory(fs, fullPath, patterns, options, currentDepth + 1);
        results.push(...subResults);
      }
    }
  } catch (err) {
    // Skip directories we can't read
  }
 
  return results;
}
 
/**
 * Main glob implementation
 */
export function globSync(fs: any, pattern: string, options: IGlobOptions = {}): string[] {
  const cwd = options.cwd ? pathToFilename(options.cwd as any) : process.cwd();
  const resolvedCwd = resolve(cwd);
 
  const globOptions: IGlobOptions = {
    cwd: resolvedCwd,
    exclude: options.exclude,
    maxdepth: options.maxdepth,
    withFileTypes: options.withFileTypes || false,
  };
 
  let results: string[] = [];
 
  // Handle absolute patterns
  if (pathModule.posix.isAbsolute(pattern)) {
    const dir = pathModule.posix.dirname(pattern);
    const basename = pathModule.posix.basename(pattern);
    const dirResults = walkDirectory(fs, dir, [basename], { ...globOptions, cwd: dir });
    results.push(...dirResults.map(r => pathModule.posix.resolve(dir, r)));
  } else {
    // Handle relative patterns
    const dirResults = walkDirectory(fs, resolvedCwd, [pattern], globOptions);
    results.push(...dirResults);
  }
 
  // Remove duplicates and sort
  results = [...new Set(results)].sort();
 
  return results;
}