text: module.exports = readdirGlob;
  
  const fs = require('fs');
  const { EventEmitter } = require('events');
  const { Minimatch } = require('minimatch');
  const { resolve } = require('path');
  
  function readdir(dir, strict) {
    return new Promise((resolve, reject) => {
      fs.readdir(dir, {withFileTypes: true} ,(err, files) => {
        if(err) {
          switch (err.code) {
            case 'ENOTDIR':      // Not a directory
              if(strict) {
                reject(err);
              } else {
                resolve([]);
              }
              break;
            case 'ENOTSUP':      // Operation not supported
            case 'ENOENT':       // No such file or directory
            case 'ENAMETOOLONG': // Filename too long
            case 'UNKNOWN':
              resolve([]);
              break;
            case 'ELOOP':        // Too many levels of symbolic links
            default:
              reject(err);
              break;
          }
        } else {
          resolve(files);
        }
      });
    });
  }
  function stat(file, followSyslinks) {
    return new Promise((resolve, reject) => {
      const statFunc = followSyslinks ? fs.stat : fs.lstat;
      statFunc(file, (err, stats) => {
        if(err) {
          switch (err.code) {
            case 'ENOENT':
              if(followSyslinks) {
                // Fallback to lstat to handle broken links as files
                resolve(stat(file, false)); 
              } else {
                resolve(null);
              }
              break;
            default:
              resolve(null);
              break;
          }
        } else {
          resolve(stats);
        }
      });
    });
  }
  
  async function* exploreWalkAsync(dir, path, followSyslinks, useStat, strict) {
    let files = await readdir(path + dir, strict);
    for(const file of files) {
      const filename = dir + '/' + file.name;
      const relative = filename.slice(1); // Remove the leading /
      const absolute = path + '/' + relative;
      let stats = null;
      if(useStat || followSyslinks) {
        stats = await stat(absolute, followSyslinks);
      }
      stats = stats || file;
  
      yield {relative, absolute, stats};
  
      if(stats.isDirectory()) {
        yield* exploreWalkAsync(filename, path, followSyslinks, useStat, false);
      }
    }
  }
  async function* explore(path, followSyslinks, useStat) {
    yield* exploreWalkAsync('', path, followSyslinks, useStat, true);
  }
  
  
  function readOptions(options) {
    return {
      pattern: options.pattern,
      dot: !!options.dot,
      noglobstar: !!options.noglobstar,
      matchBase: !!options.matchBase,
      nocase: !!options.nocase,
      ignore: options.ignore,
  
      follow: !!options.follow,
      stat: !!options.stat,
      nodir: !!options.nodir,
      mark: !!options.mark,
      silent: !!options.silent,
      absolute: !!options.absolute
    };
  }
  
  class ReaddirGlob extends EventEmitter {
    constructor(cwd, options, cb) {
      super();
      if(typeof options === 'function') {
        cb = options;
      }
  
      this.options = readOptions(options || {});
    
      this.matchers = [];
      if(this.options.pattern) {
        const matchers = Array.isArray(this.options.pattern) ? this.options.pattern : [this.options.pattern];
        this.matchers = matchers.map( m =>
          new Minimatch(this.options.pattern, {
            dot: this.options.dot,
            noglobstar:this.options.noglobstar,
            matchBase:this.options.matchBase,
            nocase:this.options.nocase
          })
        );
      }
    
      this.ignoreMatchers = [];
      if(this.options.ignore) {
        const ignorePatterns = Array.isArray(this.options.ignore) ? this.options.ignore : [this.options.ignore];
        this.ignoreMatchers = ignorePatterns.map( ignore =>
          new Minimatch(ignore, {dot: true})
        );
      }
  
      this.iterator = explore(resolve(cwd || '.'), this.options.follow, this.options.stat);
      this.paused = false;
      this.inactive = false;
      this.aborted = false;
    
      if(cb) {
        this._matches = []; 
        this.on('match', match => this._matches.push(this.options.absolute ? match.absolute : match.relative));
        this.on('error', err => cb(err));
        this.on('end', () => cb(null, this._matches));
      }
  
      setTimeout( () => this._next(), 0);
    }
  
    _fileMatches(relative, isDirectory) {
      const file = relative + (isDirectory ? '/' : '');
      return this.matchers.every(m => m.match(file))
        && !this.ignoreMatchers.some(m => m.match(file))
        && (!this.options.nodir || !isDirectory);
    }
  
    _next() {
      if(!this.paused && !this.aborted) {
        this.iterator.next()
        .then((obj)=> {
          if(!obj.done) {
            const isDirectory = obj.value.stats.isDirectory();
            if(this._fileMatches(obj.value.relative, isDirectory )) {
              let relative = obj.value.relative;
              let absolute = obj.value.absolute;
              if(this.options.mark && isDirectory) {
                relative += '/';
                absolute += '/';
              }
              if(this.options.stat) {
                this.emit('match', {relative, absolute, stat:obj.value.stats});
              } else {
                this.emit('match', {relative, absolute});
              }
            }
            this._next(this.iterator);
          } else {
            this.emit('end');
          }
        })
        .catch((err) => {
          this.abort();
          this.emit('error', err);
          if(!err.code && !this.options.silent) {
            console.error(err);
          }
        });
      } else {
        this.inactive = true;
      }
    }
  
    abort() {
      this.aborted = true;
    }
  
    pause() {
      this.paused = true;
    }
  
    resume() {
      this.paused = false;
      if(this.inactive) {
        this.inactive = false;
        this._next();
      }
    }
  }
  
  
  function readdirGlob(pattern, options, cb) {
    return new ReaddirGlob(pattern, options, cb);
  }
  readdirGlob.ReaddirGlob = ReaddirGlob;