| 'use strict'; | 
|   | 
| const fs = require('fs'); | 
| const { Readable } = require('stream'); | 
| const sysPath = require('path'); | 
| const { promisify } = require('util'); | 
| const picomatch = require('picomatch'); | 
|   | 
| const readdir = promisify(fs.readdir); | 
| const stat = promisify(fs.stat); | 
| const lstat = promisify(fs.lstat); | 
| const realpath = promisify(fs.realpath); | 
|   | 
| /** | 
|  * @typedef {Object} EntryInfo | 
|  * @property {String} path | 
|  * @property {String} fullPath | 
|  * @property {fs.Stats=} stats | 
|  * @property {fs.Dirent=} dirent | 
|  * @property {String} basename | 
|  */ | 
|   | 
| const BANG = '!'; | 
| const RECURSIVE_ERROR_CODE = 'READDIRP_RECURSIVE_ERROR'; | 
| const NORMAL_FLOW_ERRORS = new Set(['ENOENT', 'EPERM', 'EACCES', 'ELOOP', RECURSIVE_ERROR_CODE]); | 
| const FILE_TYPE = 'files'; | 
| const DIR_TYPE = 'directories'; | 
| const FILE_DIR_TYPE = 'files_directories'; | 
| const EVERYTHING_TYPE = 'all'; | 
| const ALL_TYPES = [FILE_TYPE, DIR_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE]; | 
|   | 
| const isNormalFlowError = error => NORMAL_FLOW_ERRORS.has(error.code); | 
| const [maj, min] = process.versions.node.split('.').slice(0, 2).map(n => Number.parseInt(n, 10)); | 
| const wantBigintFsStats = process.platform === 'win32' && (maj > 10 || (maj === 10 && min >= 5)); | 
|   | 
| const normalizeFilter = filter => { | 
|   if (filter === undefined) return; | 
|   if (typeof filter === 'function') return filter; | 
|   | 
|   if (typeof filter === 'string') { | 
|     const glob = picomatch(filter.trim()); | 
|     return entry => glob(entry.basename); | 
|   } | 
|   | 
|   if (Array.isArray(filter)) { | 
|     const positive = []; | 
|     const negative = []; | 
|     for (const item of filter) { | 
|       const trimmed = item.trim(); | 
|       if (trimmed.charAt(0) === BANG) { | 
|         negative.push(picomatch(trimmed.slice(1))); | 
|       } else { | 
|         positive.push(picomatch(trimmed)); | 
|       } | 
|     } | 
|   | 
|     if (negative.length > 0) { | 
|       if (positive.length > 0) { | 
|         return entry => | 
|           positive.some(f => f(entry.basename)) && !negative.some(f => f(entry.basename)); | 
|       } | 
|       return entry => !negative.some(f => f(entry.basename)); | 
|     } | 
|     return entry => positive.some(f => f(entry.basename)); | 
|   } | 
| }; | 
|   | 
| class ReaddirpStream extends Readable { | 
|   static get defaultOptions() { | 
|     return { | 
|       root: '.', | 
|       /* eslint-disable no-unused-vars */ | 
|       fileFilter: (path) => true, | 
|       directoryFilter: (path) => true, | 
|       /* eslint-enable no-unused-vars */ | 
|       type: FILE_TYPE, | 
|       lstat: false, | 
|       depth: 2147483648, | 
|       alwaysStat: false | 
|     }; | 
|   } | 
|   | 
|   constructor(options = {}) { | 
|     super({ | 
|       objectMode: true, | 
|       autoDestroy: true, | 
|       highWaterMark: options.highWaterMark || 4096 | 
|     }); | 
|     const opts = { ...ReaddirpStream.defaultOptions, ...options }; | 
|     const { root, type } = opts; | 
|   | 
|     this._fileFilter = normalizeFilter(opts.fileFilter); | 
|     this._directoryFilter = normalizeFilter(opts.directoryFilter); | 
|   | 
|     const statMethod = opts.lstat ? lstat : stat; | 
|     // Use bigint stats if it's windows and stat() supports options (node 10+). | 
|     if (wantBigintFsStats) { | 
|       this._stat = path => statMethod(path, { bigint: true }); | 
|     } else { | 
|       this._stat = statMethod; | 
|     } | 
|   | 
|     this._maxDepth = opts.depth; | 
|     this._wantsDir = [DIR_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE].includes(type); | 
|     this._wantsFile = [FILE_TYPE, FILE_DIR_TYPE, EVERYTHING_TYPE].includes(type); | 
|     this._wantsEverything = type === EVERYTHING_TYPE; | 
|     this._root = sysPath.resolve(root); | 
|     this._isDirent = ('Dirent' in fs) && !opts.alwaysStat; | 
|     this._statsProp = this._isDirent ? 'dirent' : 'stats'; | 
|     this._rdOptions = { encoding: 'utf8', withFileTypes: this._isDirent }; | 
|   | 
|     // Launch stream with one parent, the root dir. | 
|     this.parents = [this._exploreDir(root, 1)]; | 
|     this.reading = false; | 
|     this.parent = undefined; | 
|   } | 
|   | 
|   async _read(batch) { | 
|     if (this.reading) return; | 
|     this.reading = true; | 
|   | 
|     try { | 
|       while (!this.destroyed && batch > 0) { | 
|         const { path, depth, files = [] } = this.parent || {}; | 
|   | 
|         if (files.length > 0) { | 
|           const slice = files.splice(0, batch).map(dirent => this._formatEntry(dirent, path)); | 
|           for (const entry of await Promise.all(slice)) { | 
|             if (this.destroyed) return; | 
|   | 
|             const entryType = await this._getEntryType(entry); | 
|             if (entryType === 'directory' && this._directoryFilter(entry)) { | 
|               if (depth <= this._maxDepth) { | 
|                 this.parents.push(this._exploreDir(entry.fullPath, depth + 1)); | 
|               } | 
|   | 
|               if (this._wantsDir) { | 
|                 this.push(entry); | 
|                 batch--; | 
|               } | 
|             } else if ((entryType === 'file' || this._includeAsFile(entry)) && this._fileFilter(entry)) { | 
|               if (this._wantsFile) { | 
|                 this.push(entry); | 
|                 batch--; | 
|               } | 
|             } | 
|           } | 
|         } else { | 
|           const parent = this.parents.pop(); | 
|           if (!parent) { | 
|             this.push(null); | 
|             break; | 
|           } | 
|           this.parent = await parent; | 
|           if (this.destroyed) return; | 
|         } | 
|       } | 
|     } catch (error) { | 
|       this.destroy(error); | 
|     } finally { | 
|       this.reading = false; | 
|     } | 
|   } | 
|   | 
|   async _exploreDir(path, depth) { | 
|     let files; | 
|     try { | 
|       files = await readdir(path, this._rdOptions); | 
|     } catch (error) { | 
|       this._onError(error); | 
|     } | 
|     return { files, depth, path }; | 
|   } | 
|   | 
|   async _formatEntry(dirent, path) { | 
|     let entry; | 
|     try { | 
|       const basename = this._isDirent ? dirent.name : dirent; | 
|       const fullPath = sysPath.resolve(sysPath.join(path, basename)); | 
|       entry = { path: sysPath.relative(this._root, fullPath), fullPath, basename }; | 
|       entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath); | 
|     } catch (err) { | 
|       this._onError(err); | 
|     } | 
|     return entry; | 
|   } | 
|   | 
|   _onError(err) { | 
|     if (isNormalFlowError(err) && !this.destroyed) { | 
|       this.emit('warn', err); | 
|     } else { | 
|       this.destroy(err); | 
|     } | 
|   } | 
|   | 
|   async _getEntryType(entry) { | 
|     // entry may be undefined, because a warning or an error were emitted | 
|     // and the statsProp is undefined | 
|     const stats = entry && entry[this._statsProp]; | 
|     if (!stats) { | 
|       return; | 
|     } | 
|     if (stats.isFile()) { | 
|       return 'file'; | 
|     } | 
|     if (stats.isDirectory()) { | 
|       return 'directory'; | 
|     } | 
|     if (stats && stats.isSymbolicLink()) { | 
|       const full = entry.fullPath; | 
|       try { | 
|         const entryRealPath = await realpath(full); | 
|         const entryRealPathStats = await lstat(entryRealPath); | 
|         if (entryRealPathStats.isFile()) { | 
|           return 'file'; | 
|         } | 
|         if (entryRealPathStats.isDirectory()) { | 
|           const len = entryRealPath.length; | 
|           if (full.startsWith(entryRealPath) && full.substr(len, 1) === sysPath.sep) { | 
|             const recursiveError = new Error( | 
|               `Circular symlink detected: "${full}" points to "${entryRealPath}"` | 
|             ); | 
|             recursiveError.code = RECURSIVE_ERROR_CODE; | 
|             return this._onError(recursiveError); | 
|           } | 
|           return 'directory'; | 
|         } | 
|       } catch (error) { | 
|         this._onError(error); | 
|       } | 
|     } | 
|   } | 
|   | 
|   _includeAsFile(entry) { | 
|     const stats = entry && entry[this._statsProp]; | 
|   | 
|     return stats && this._wantsEverything && !stats.isDirectory(); | 
|   } | 
| } | 
|   | 
| /** | 
|  * @typedef {Object} ReaddirpArguments | 
|  * @property {Function=} fileFilter | 
|  * @property {Function=} directoryFilter | 
|  * @property {String=} type | 
|  * @property {Number=} depth | 
|  * @property {String=} root | 
|  * @property {Boolean=} lstat | 
|  * @property {Boolean=} bigint | 
|  */ | 
|   | 
| /** | 
|  * Main function which ends up calling readdirRec and reads all files and directories in given root recursively. | 
|  * @param {String} root Root directory | 
|  * @param {ReaddirpArguments=} options Options to specify root (start directory), filters and recursion depth | 
|  */ | 
| const readdirp = (root, options = {}) => { | 
|   let type = options.entryType || options.type; | 
|   if (type === 'both') type = FILE_DIR_TYPE; // backwards-compatibility | 
|   if (type) options.type = type; | 
|   if (!root) { | 
|     throw new Error('readdirp: root argument is required. Usage: readdirp(root, options)'); | 
|   } else if (typeof root !== 'string') { | 
|     throw new TypeError('readdirp: root argument must be a string. Usage: readdirp(root, options)'); | 
|   } else if (type && !ALL_TYPES.includes(type)) { | 
|     throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(', ')}`); | 
|   } | 
|   | 
|   options.root = root; | 
|   return new ReaddirpStream(options); | 
| }; | 
|   | 
| const readdirpPromise = (root, options = {}) => { | 
|   return new Promise((resolve, reject) => { | 
|     const files = []; | 
|     readdirp(root, options) | 
|       .on('data', entry => files.push(entry)) | 
|       .on('end', () => resolve(files)) | 
|       .on('error', error => reject(error)); | 
|   }); | 
| }; | 
|   | 
| readdirp.promise = readdirpPromise; | 
| readdirp.ReaddirpStream = ReaddirpStream; | 
| readdirp.default = readdirp; | 
|   | 
| module.exports = readdirp; |