{"ast":null,"code":"'use strict';\n\nconst Readable = require('stream').Readable;\n\nconst EventEmitter = require('events').EventEmitter;\n\nconst path = require('path');\n\nconst normalizeOptions = require('./normalize-options');\n\nconst stat = require('./stat');\n\nconst call = require('./call');\n/**\n * Asynchronously reads the contents of a directory and streams the results\n * via a {@link stream.Readable}.\n */\n\n\nclass DirectoryReader {\n  /**\n   * @param {string} dir - The absolute or relative directory path to read\n   * @param {object} [options] - User-specified options, if any (see {@link normalizeOptions})\n   * @param {object} internalOptions - Internal options that aren't part of the public API\n   * @class\n   */\n  constructor(dir, options, internalOptions) {\n    this.options = options = normalizeOptions(options, internalOptions); // Indicates whether we should keep reading\n    // This is set false if stream.Readable.push() returns false.\n\n    this.shouldRead = true; // The directories to read\n    // (initialized with the top-level directory)\n\n    this.queue = [{\n      path: dir,\n      basePath: options.basePath,\n      posixBasePath: options.posixBasePath,\n      depth: 0\n    }]; // The number of directories that are currently being processed\n\n    this.pending = 0; // The data that has been read, but not yet emitted\n\n    this.buffer = [];\n    this.stream = new Readable({\n      objectMode: true\n    });\n\n    this.stream._read = () => {\n      // Start (or resume) reading\n      this.shouldRead = true; // If we have data in the buffer, then send the next chunk\n\n      if (this.buffer.length > 0) {\n        this.pushFromBuffer();\n      } // If we have directories queued, then start processing the next one\n\n\n      if (this.queue.length > 0) {\n        if (this.options.facade.sync) {\n          while (this.queue.length > 0) {\n            this.readNextDirectory();\n          }\n        } else {\n          this.readNextDirectory();\n        }\n      }\n\n      this.checkForEOF();\n    };\n  }\n  /**\n   * Reads the next directory in the queue\n   */\n\n\n  readNextDirectory() {\n    let facade = this.options.facade;\n    let dir = this.queue.shift();\n    this.pending++; // Read the directory listing\n\n    call.safe(facade.fs.readdir, dir.path, (err, items) => {\n      if (err) {\n        // fs.readdir threw an error\n        this.emit('error', err);\n        return this.finishedReadingDirectory();\n      }\n\n      try {\n        // Process each item in the directory (simultaneously, if async)\n        facade.forEach(items, this.processItem.bind(this, dir), this.finishedReadingDirectory.bind(this, dir));\n      } catch (err2) {\n        // facade.forEach threw an error\n        // (probably because fs.readdir returned an invalid result)\n        this.emit('error', err2);\n        this.finishedReadingDirectory();\n      }\n    });\n  }\n  /**\n   * This method is called after all items in a directory have been processed.\n   *\n   * NOTE: This does not necessarily mean that the reader is finished, since there may still\n   * be other directories queued or pending.\n   */\n\n\n  finishedReadingDirectory() {\n    this.pending--;\n\n    if (this.shouldRead) {\n      // If we have directories queued, then start processing the next one\n      if (this.queue.length > 0 && this.options.facade.async) {\n        this.readNextDirectory();\n      }\n\n      this.checkForEOF();\n    }\n  }\n  /**\n   * Determines whether the reader has finished processing all items in all directories.\n   * If so, then the \"end\" event is fired (via {@Readable#push})\n   */\n\n\n  checkForEOF() {\n    if (this.buffer.length === 0 && // The stuff we've already read\n    this.pending === 0 && // The stuff we're currently reading\n    this.queue.length === 0) {\n      // The stuff we haven't read yet\n      // There's no more stuff!\n      this.stream.push(null);\n    }\n  }\n  /**\n   * Processes a single item in a directory.\n   *\n   * If the item is a directory, and `option.deep` is enabled, then the item will be added\n   * to the directory queue.\n   *\n   * If the item meets the filter criteria, then it will be emitted to the reader's stream.\n   *\n   * @param {object} dir - A directory object from the queue\n   * @param {string} item - The name of the item (name only, no path)\n   * @param {function} done - A callback function that is called after the item has been processed\n   */\n\n\n  processItem(dir, item, done) {\n    let stream = this.stream;\n    let options = this.options;\n    let itemPath = dir.basePath + item;\n    let posixPath = dir.posixBasePath + item;\n    let fullPath = path.join(dir.path, item); // If `options.deep` is a number, and we've already recursed to the max depth,\n    // then there's no need to check fs.Stats to know if it's a directory.\n    // If `options.deep` is a function, then we'll need fs.Stats\n\n    let maxDepthReached = dir.depth >= options.recurseDepth; // Do we need to call `fs.stat`?\n\n    let needStats = !maxDepthReached || // we need the fs.Stats to know if it's a directory\n    options.stats || // the user wants fs.Stats objects returned\n    options.recurseFn || // we need fs.Stats for the recurse function\n    options.filterFn || // we need fs.Stats for the filter function\n    EventEmitter.listenerCount(stream, 'file') || // we need the fs.Stats to know if it's a file\n    EventEmitter.listenerCount(stream, 'directory') || // we need the fs.Stats to know if it's a directory\n    EventEmitter.listenerCount(stream, 'symlink'); // we need the fs.Stats to know if it's a symlink\n    // If we don't need stats, then exit early\n\n    if (!needStats) {\n      if (this.filter(itemPath, posixPath)) {\n        this.pushOrBuffer({\n          data: itemPath\n        });\n      }\n\n      return done();\n    } // Get the fs.Stats object for this path\n\n\n    stat(options.facade.fs, fullPath, (err, stats) => {\n      if (err) {\n        // fs.stat threw an error\n        this.emit('error', err);\n        return done();\n      }\n\n      try {\n        // Add the item's path to the fs.Stats object\n        // The base of this path, and its separators are determined by the options\n        // (i.e. options.basePath and options.sep)\n        stats.path = itemPath; // Add depth of the path to the fs.Stats object for use this in the filter function\n\n        stats.depth = dir.depth;\n\n        if (this.shouldRecurse(stats, posixPath, maxDepthReached)) {\n          // Add this subdirectory to the queue\n          this.queue.push({\n            path: fullPath,\n            basePath: itemPath + options.sep,\n            posixBasePath: posixPath + '/',\n            depth: dir.depth + 1\n          });\n        } // Determine whether this item matches the filter criteria\n\n\n        if (this.filter(stats, posixPath)) {\n          this.pushOrBuffer({\n            data: options.stats ? stats : itemPath,\n            file: stats.isFile(),\n            directory: stats.isDirectory(),\n            symlink: stats.isSymbolicLink()\n          });\n        }\n\n        done();\n      } catch (err2) {\n        // An error occurred while processing the item\n        // (probably during a user-specified function, such as options.deep, options.filter, etc.)\n        this.emit('error', err2);\n        done();\n      }\n    });\n  }\n  /**\n   * Pushes the given chunk of data to the stream, or adds it to the buffer,\n   * depending on the state of the stream.\n   *\n   * @param {object} chunk\n   */\n\n\n  pushOrBuffer(chunk) {\n    // Add the chunk to the buffer\n    this.buffer.push(chunk); // If we're still reading, then immediately emit the next chunk in the buffer\n    // (which may or may not be the chunk that we just added)\n\n    if (this.shouldRead) {\n      this.pushFromBuffer();\n    }\n  }\n  /**\n   * Immediately pushes the next chunk in the buffer to the reader's stream.\n   * The \"data\" event will always be fired (via {@link Readable#push}).\n   * In addition, the \"file\", \"directory\", and/or \"symlink\" events may be fired,\n   * depending on the type of properties of the chunk.\n   */\n\n\n  pushFromBuffer() {\n    let stream = this.stream;\n    let chunk = this.buffer.shift(); // Stream the data\n\n    try {\n      this.shouldRead = stream.push(chunk.data);\n    } catch (err) {\n      this.emit('error', err);\n    } // Also emit specific events, based on the type of chunk\n\n\n    chunk.file && this.emit('file', chunk.data);\n    chunk.symlink && this.emit('symlink', chunk.data);\n    chunk.directory && this.emit('directory', chunk.data);\n  }\n  /**\n   * Determines whether the given directory meets the user-specified recursion criteria.\n   * If the user didn't specify recursion criteria, then this function will default to true.\n   *\n   * @param {fs.Stats} stats - The directory's {@link fs.Stats} object\n   * @param {string} posixPath - The item's POSIX path (used for glob matching)\n   * @param {boolean} maxDepthReached - Whether we've already crawled the user-specified depth\n   * @returns {boolean}\n   */\n\n\n  shouldRecurse(stats, posixPath, maxDepthReached) {\n    let options = this.options;\n\n    if (maxDepthReached) {\n      // We've already crawled to the maximum depth. So no more recursion.\n      return false;\n    } else if (!stats.isDirectory()) {\n      // It's not a directory. So don't try to crawl it.\n      return false;\n    } else if (options.recurseGlob) {\n      // Glob patterns are always tested against the POSIX path, even on Windows\n      // https://github.com/isaacs/node-glob#windows\n      return options.recurseGlob.test(posixPath);\n    } else if (options.recurseRegExp) {\n      // Regular expressions are tested against the normal path\n      // (based on the OS or options.sep)\n      return options.recurseRegExp.test(stats.path);\n    } else if (options.recurseFn) {\n      try {\n        // Run the user-specified recursion criteria\n        return options.recurseFn.call(null, stats);\n      } catch (err) {\n        // An error occurred in the user's code.\n        // In Sync and Async modes, this will return an error.\n        // In Streaming mode, we emit an \"error\" event, but continue processing\n        this.emit('error', err);\n      }\n    } else {\n      // No recursion function was specified, and we're within the maximum depth.\n      // So crawl this directory.\n      return true;\n    }\n  }\n  /**\n   * Determines whether the given item meets the user-specified filter criteria.\n   * If the user didn't specify a filter, then this function will always return true.\n   *\n   * @param {string|fs.Stats} value - Either the item's path, or the item's {@link fs.Stats} object\n   * @param {string} posixPath - The item's POSIX path (used for glob matching)\n   * @returns {boolean}\n   */\n\n\n  filter(value, posixPath) {\n    let options = this.options;\n\n    if (options.filterGlob) {\n      // Glob patterns are always tested against the POSIX path, even on Windows\n      // https://github.com/isaacs/node-glob#windows\n      return options.filterGlob.test(posixPath);\n    } else if (options.filterRegExp) {\n      // Regular expressions are tested against the normal path\n      // (based on the OS or options.sep)\n      return options.filterRegExp.test(value.path || value);\n    } else if (options.filterFn) {\n      try {\n        // Run the user-specified filter function\n        return options.filterFn.call(null, value);\n      } catch (err) {\n        // An error occurred in the user's code.\n        // In Sync and Async modes, this will return an error.\n        // In Streaming mode, we emit an \"error\" event, but continue processing\n        this.emit('error', err);\n      }\n    } else {\n      // No filter was specified, so match everything\n      return true;\n    }\n  }\n  /**\n   * Emits an event.  If one of the event listeners throws an error,\n   * then an \"error\" event is emitted.\n   *\n   * @param {string} eventName\n   * @param {*} data\n   */\n\n\n  emit(eventName, data) {\n    let stream = this.stream;\n\n    try {\n      stream.emit(eventName, data);\n    } catch (err) {\n      if (eventName === 'error') {\n        // Don't recursively emit \"error\" events.\n        // If the first one fails, then just throw\n        throw err;\n      } else {\n        stream.emit('error', err);\n      }\n    }\n  }\n\n}\n\nmodule.exports = DirectoryReader;","map":{"version":3,"sources":["D:/Development/Work/CENOS/cenos-ui/node_modules/@mrmlnc/readdir-enhanced/lib/directory-reader.js"],"names":["Readable","require","EventEmitter","path","normalizeOptions","stat","call","DirectoryReader","constructor","dir","options","internalOptions","shouldRead","queue","basePath","posixBasePath","depth","pending","buffer","stream","objectMode","_read","length","pushFromBuffer","facade","sync","readNextDirectory","checkForEOF","shift","safe","fs","readdir","err","items","emit","finishedReadingDirectory","forEach","processItem","bind","err2","async","push","item","done","itemPath","posixPath","fullPath","join","maxDepthReached","recurseDepth","needStats","stats","recurseFn","filterFn","listenerCount","filter","pushOrBuffer","data","shouldRecurse","sep","file","isFile","directory","isDirectory","symlink","isSymbolicLink","chunk","recurseGlob","test","recurseRegExp","value","filterGlob","filterRegExp","eventName","module","exports"],"mappings":"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","sourcesContent":["'use strict';\n\nconst Readable = require('stream').Readable;\nconst EventEmitter = require('events').EventEmitter;\nconst path = require('path');\nconst normalizeOptions = require('./normalize-options');\nconst stat = require('./stat');\nconst call = require('./call');\n\n/**\n * Asynchronously reads the contents of a directory and streams the results\n * via a {@link stream.Readable}.\n */\nclass DirectoryReader {\n  /**\n   * @param {string} dir - The absolute or relative directory path to read\n   * @param {object} [options] - User-specified options, if any (see {@link normalizeOptions})\n   * @param {object} internalOptions - Internal options that aren't part of the public API\n   * @class\n   */\n  constructor (dir, options, internalOptions) {\n    this.options = options = normalizeOptions(options, internalOptions);\n\n    // Indicates whether we should keep reading\n    // This is set false if stream.Readable.push() returns false.\n    this.shouldRead = true;\n\n    // The directories to read\n    // (initialized with the top-level directory)\n    this.queue = [{\n      path: dir,\n      basePath: options.basePath,\n      posixBasePath: options.posixBasePath,\n      depth: 0\n    }];\n\n    // The number of directories that are currently being processed\n    this.pending = 0;\n\n    // The data that has been read, but not yet emitted\n    this.buffer = [];\n\n    this.stream = new Readable({ objectMode: true });\n    this.stream._read = () => {\n      // Start (or resume) reading\n      this.shouldRead = true;\n\n      // If we have data in the buffer, then send the next chunk\n      if (this.buffer.length > 0) {\n        this.pushFromBuffer();\n      }\n\n      // If we have directories queued, then start processing the next one\n      if (this.queue.length > 0) {\n        if (this.options.facade.sync) {\n          while (this.queue.length > 0) {\n            this.readNextDirectory();\n          }\n        }\n        else {\n          this.readNextDirectory();\n        }\n      }\n\n      this.checkForEOF();\n    };\n  }\n\n  /**\n   * Reads the next directory in the queue\n   */\n  readNextDirectory () {\n    let facade = this.options.facade;\n    let dir = this.queue.shift();\n    this.pending++;\n\n    // Read the directory listing\n    call.safe(facade.fs.readdir, dir.path, (err, items) => {\n      if (err) {\n        // fs.readdir threw an error\n        this.emit('error', err);\n        return this.finishedReadingDirectory();\n      }\n\n      try {\n        // Process each item in the directory (simultaneously, if async)\n        facade.forEach(\n          items,\n          this.processItem.bind(this, dir),\n          this.finishedReadingDirectory.bind(this, dir)\n        );\n      }\n      catch (err2) {\n        // facade.forEach threw an error\n        // (probably because fs.readdir returned an invalid result)\n        this.emit('error', err2);\n        this.finishedReadingDirectory();\n      }\n    });\n  }\n\n  /**\n   * This method is called after all items in a directory have been processed.\n   *\n   * NOTE: This does not necessarily mean that the reader is finished, since there may still\n   * be other directories queued or pending.\n   */\n  finishedReadingDirectory () {\n    this.pending--;\n\n    if (this.shouldRead) {\n      // If we have directories queued, then start processing the next one\n      if (this.queue.length > 0 && this.options.facade.async) {\n        this.readNextDirectory();\n      }\n\n      this.checkForEOF();\n    }\n  }\n\n  /**\n   * Determines whether the reader has finished processing all items in all directories.\n   * If so, then the \"end\" event is fired (via {@Readable#push})\n   */\n  checkForEOF () {\n    if (this.buffer.length === 0 &&   // The stuff we've already read\n    this.pending === 0 &&             // The stuff we're currently reading\n    this.queue.length === 0) {        // The stuff we haven't read yet\n      // There's no more stuff!\n      this.stream.push(null);\n    }\n  }\n\n  /**\n   * Processes a single item in a directory.\n   *\n   * If the item is a directory, and `option.deep` is enabled, then the item will be added\n   * to the directory queue.\n   *\n   * If the item meets the filter criteria, then it will be emitted to the reader's stream.\n   *\n   * @param {object} dir - A directory object from the queue\n   * @param {string} item - The name of the item (name only, no path)\n   * @param {function} done - A callback function that is called after the item has been processed\n   */\n  processItem (dir, item, done) {\n    let stream = this.stream;\n    let options = this.options;\n\n    let itemPath = dir.basePath + item;\n    let posixPath = dir.posixBasePath + item;\n    let fullPath = path.join(dir.path, item);\n\n    // If `options.deep` is a number, and we've already recursed to the max depth,\n    // then there's no need to check fs.Stats to know if it's a directory.\n    // If `options.deep` is a function, then we'll need fs.Stats\n    let maxDepthReached = dir.depth >= options.recurseDepth;\n\n    // Do we need to call `fs.stat`?\n    let needStats =\n      !maxDepthReached ||                                 // we need the fs.Stats to know if it's a directory\n      options.stats ||                                    // the user wants fs.Stats objects returned\n      options.recurseFn ||                                // we need fs.Stats for the recurse function\n      options.filterFn ||                                 // we need fs.Stats for the filter function\n      EventEmitter.listenerCount(stream, 'file') ||       // we need the fs.Stats to know if it's a file\n      EventEmitter.listenerCount(stream, 'directory') ||  // we need the fs.Stats to know if it's a directory\n      EventEmitter.listenerCount(stream, 'symlink');      // we need the fs.Stats to know if it's a symlink\n\n    // If we don't need stats, then exit early\n    if (!needStats) {\n      if (this.filter(itemPath, posixPath)) {\n        this.pushOrBuffer({ data: itemPath });\n      }\n      return done();\n    }\n\n    // Get the fs.Stats object for this path\n    stat(options.facade.fs, fullPath, (err, stats) => {\n      if (err) {\n        // fs.stat threw an error\n        this.emit('error', err);\n        return done();\n      }\n\n      try {\n        // Add the item's path to the fs.Stats object\n        // The base of this path, and its separators are determined by the options\n        // (i.e. options.basePath and options.sep)\n        stats.path = itemPath;\n\n        // Add depth of the path to the fs.Stats object for use this in the filter function\n        stats.depth = dir.depth;\n\n        if (this.shouldRecurse(stats, posixPath, maxDepthReached)) {\n          // Add this subdirectory to the queue\n          this.queue.push({\n            path: fullPath,\n            basePath: itemPath + options.sep,\n            posixBasePath: posixPath + '/',\n            depth: dir.depth + 1,\n          });\n        }\n\n        // Determine whether this item matches the filter criteria\n        if (this.filter(stats, posixPath)) {\n          this.pushOrBuffer({\n            data: options.stats ? stats : itemPath,\n            file: stats.isFile(),\n            directory: stats.isDirectory(),\n            symlink: stats.isSymbolicLink(),\n          });\n        }\n\n        done();\n      }\n      catch (err2) {\n        // An error occurred while processing the item\n        // (probably during a user-specified function, such as options.deep, options.filter, etc.)\n        this.emit('error', err2);\n        done();\n      }\n    });\n  }\n\n  /**\n   * Pushes the given chunk of data to the stream, or adds it to the buffer,\n   * depending on the state of the stream.\n   *\n   * @param {object} chunk\n   */\n  pushOrBuffer (chunk) {\n    // Add the chunk to the buffer\n    this.buffer.push(chunk);\n\n    // If we're still reading, then immediately emit the next chunk in the buffer\n    // (which may or may not be the chunk that we just added)\n    if (this.shouldRead) {\n      this.pushFromBuffer();\n    }\n  }\n\n  /**\n   * Immediately pushes the next chunk in the buffer to the reader's stream.\n   * The \"data\" event will always be fired (via {@link Readable#push}).\n   * In addition, the \"file\", \"directory\", and/or \"symlink\" events may be fired,\n   * depending on the type of properties of the chunk.\n   */\n  pushFromBuffer () {\n    let stream = this.stream;\n    let chunk = this.buffer.shift();\n\n    // Stream the data\n    try {\n      this.shouldRead = stream.push(chunk.data);\n    }\n    catch (err) {\n      this.emit('error', err);\n    }\n\n    // Also emit specific events, based on the type of chunk\n    chunk.file && this.emit('file', chunk.data);\n    chunk.symlink && this.emit('symlink', chunk.data);\n    chunk.directory && this.emit('directory', chunk.data);\n  }\n\n  /**\n   * Determines whether the given directory meets the user-specified recursion criteria.\n   * If the user didn't specify recursion criteria, then this function will default to true.\n   *\n   * @param {fs.Stats} stats - The directory's {@link fs.Stats} object\n   * @param {string} posixPath - The item's POSIX path (used for glob matching)\n   * @param {boolean} maxDepthReached - Whether we've already crawled the user-specified depth\n   * @returns {boolean}\n   */\n  shouldRecurse (stats, posixPath, maxDepthReached) {\n    let options = this.options;\n\n    if (maxDepthReached) {\n      // We've already crawled to the maximum depth. So no more recursion.\n      return false;\n    }\n    else if (!stats.isDirectory()) {\n      // It's not a directory. So don't try to crawl it.\n      return false;\n    }\n    else if (options.recurseGlob) {\n      // Glob patterns are always tested against the POSIX path, even on Windows\n      // https://github.com/isaacs/node-glob#windows\n      return options.recurseGlob.test(posixPath);\n    }\n    else if (options.recurseRegExp) {\n      // Regular expressions are tested against the normal path\n      // (based on the OS or options.sep)\n      return options.recurseRegExp.test(stats.path);\n    }\n    else if (options.recurseFn) {\n      try {\n        // Run the user-specified recursion criteria\n        return options.recurseFn.call(null, stats);\n      }\n      catch (err) {\n        // An error occurred in the user's code.\n        // In Sync and Async modes, this will return an error.\n        // In Streaming mode, we emit an \"error\" event, but continue processing\n        this.emit('error', err);\n      }\n    }\n    else {\n      // No recursion function was specified, and we're within the maximum depth.\n      // So crawl this directory.\n      return true;\n    }\n  }\n\n  /**\n   * Determines whether the given item meets the user-specified filter criteria.\n   * If the user didn't specify a filter, then this function will always return true.\n   *\n   * @param {string|fs.Stats} value - Either the item's path, or the item's {@link fs.Stats} object\n   * @param {string} posixPath - The item's POSIX path (used for glob matching)\n   * @returns {boolean}\n   */\n  filter (value, posixPath) {\n    let options = this.options;\n\n    if (options.filterGlob) {\n      // Glob patterns are always tested against the POSIX path, even on Windows\n      // https://github.com/isaacs/node-glob#windows\n      return options.filterGlob.test(posixPath);\n    }\n    else if (options.filterRegExp) {\n      // Regular expressions are tested against the normal path\n      // (based on the OS or options.sep)\n      return options.filterRegExp.test(value.path || value);\n    }\n    else if (options.filterFn) {\n      try {\n        // Run the user-specified filter function\n        return options.filterFn.call(null, value);\n      }\n      catch (err) {\n        // An error occurred in the user's code.\n        // In Sync and Async modes, this will return an error.\n        // In Streaming mode, we emit an \"error\" event, but continue processing\n        this.emit('error', err);\n      }\n    }\n    else {\n      // No filter was specified, so match everything\n      return true;\n    }\n  }\n\n  /**\n   * Emits an event.  If one of the event listeners throws an error,\n   * then an \"error\" event is emitted.\n   *\n   * @param {string} eventName\n   * @param {*} data\n   */\n  emit (eventName, data) {\n    let stream = this.stream;\n\n    try {\n      stream.emit(eventName, data);\n    }\n    catch (err) {\n      if (eventName === 'error') {\n        // Don't recursively emit \"error\" events.\n        // If the first one fails, then just throw\n        throw err;\n      }\n      else {\n        stream.emit('error', err);\n      }\n    }\n  }\n}\n\nmodule.exports = DirectoryReader;\n"]},"metadata":{},"sourceType":"script"}