You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							177 lines
						
					
					
						
							5.6 KiB
						
					
					
				
			
		
		
	
	
							177 lines
						
					
					
						
							5.6 KiB
						
					
					
				'use strict'; | 
						|
 | 
						|
const path = require('path'); | 
						|
const globToRegExp = require('glob-to-regexp'); | 
						|
 | 
						|
module.exports = normalizeOptions; | 
						|
 | 
						|
let isWindows = /^win/.test(process.platform); | 
						|
 | 
						|
/** | 
						|
 * @typedef {Object} FSFacade | 
						|
 * @property {fs.readdir} readdir | 
						|
 * @property {fs.stat} stat | 
						|
 * @property {fs.lstat} lstat | 
						|
 */ | 
						|
 | 
						|
/** | 
						|
 * Validates and normalizes the options argument | 
						|
 * | 
						|
 * @param {object} [options] - User-specified options, if any | 
						|
 * @param {object} internalOptions - Internal options that aren't part of the public API | 
						|
 * | 
						|
 * @param {number|boolean|function} [options.deep] | 
						|
 * The number of directories to recursively traverse. Any falsy value or negative number will | 
						|
 * default to zero, so only the top-level contents will be returned. Set to `true` or `Infinity` | 
						|
 * to traverse all subdirectories.  Or provide a function that accepts a {@link fs.Stats} object | 
						|
 * and returns a truthy value if the directory's contents should be crawled. | 
						|
 * | 
						|
 * @param {function|string|RegExp} [options.filter] | 
						|
 * A function that accepts a {@link fs.Stats} object and returns a truthy value if the data should | 
						|
 * be returned.  Or a RegExp or glob string pattern, to filter by file name. | 
						|
 * | 
						|
 * @param {string} [options.sep] | 
						|
 * The path separator to use. By default, the OS-specific separator will be used, but this can be | 
						|
 * set to a specific value to ensure consistency across platforms. | 
						|
 * | 
						|
 * @param {string} [options.basePath] | 
						|
 * The base path to prepend to each result. If empty, then all results will be relative to `dir`. | 
						|
 * | 
						|
 * @param {FSFacade} [options.fs] | 
						|
 * Synchronous or asynchronous facades for Node.js File System module | 
						|
 * | 
						|
 * @param {object} [internalOptions.facade] | 
						|
 * Synchronous or asynchronous facades for various methods, including for the Node.js File System module | 
						|
 * | 
						|
 * @param {boolean} [internalOptions.emit] | 
						|
 * Indicates whether the reader should emit "file", "directory", and "symlink" events | 
						|
 * | 
						|
 * @param {boolean} [internalOptions.stats] | 
						|
 * Indicates whether the reader should emit {@link fs.Stats} objects instead of path strings | 
						|
 * | 
						|
 * @returns {object} | 
						|
 */ | 
						|
function normalizeOptions (options, internalOptions) { | 
						|
  if (options === null || options === undefined) { | 
						|
    options = {}; | 
						|
  } | 
						|
  else if (typeof options !== 'object') { | 
						|
    throw new TypeError('options must be an object'); | 
						|
  } | 
						|
 | 
						|
  let recurseDepth, recurseFn, recurseRegExp, recurseGlob, deep = options.deep; | 
						|
  if (deep === null || deep === undefined) { | 
						|
    recurseDepth = 0; | 
						|
  } | 
						|
  else if (typeof deep === 'boolean') { | 
						|
    recurseDepth = deep ? Infinity : 0; | 
						|
  } | 
						|
  else if (typeof deep === 'number') { | 
						|
    if (deep < 0 || isNaN(deep)) { | 
						|
      throw new Error('options.deep must be a positive number'); | 
						|
    } | 
						|
    else if (Math.floor(deep) !== deep) { | 
						|
      throw new Error('options.deep must be an integer'); | 
						|
    } | 
						|
    else { | 
						|
      recurseDepth = deep; | 
						|
    } | 
						|
  } | 
						|
  else if (typeof deep === 'function') { | 
						|
    recurseDepth = Infinity; | 
						|
    recurseFn = deep; | 
						|
  } | 
						|
  else if (deep instanceof RegExp) { | 
						|
    recurseDepth = Infinity; | 
						|
    recurseRegExp = deep; | 
						|
  } | 
						|
  else if (typeof deep === 'string' && deep.length > 0) { | 
						|
    recurseDepth = Infinity; | 
						|
    recurseGlob = globToRegExp(deep, { extended: true, globstar: true }); | 
						|
  } | 
						|
  else { | 
						|
    throw new TypeError('options.deep must be a boolean, number, function, regular expression, or glob pattern'); | 
						|
  } | 
						|
 | 
						|
  let filterFn, filterRegExp, filterGlob, filter = options.filter; | 
						|
  if (filter !== null && filter !== undefined) { | 
						|
    if (typeof filter === 'function') { | 
						|
      filterFn = filter; | 
						|
    } | 
						|
    else if (filter instanceof RegExp) { | 
						|
      filterRegExp = filter; | 
						|
    } | 
						|
    else if (typeof filter === 'string' && filter.length > 0) { | 
						|
      filterGlob = globToRegExp(filter, { extended: true, globstar: true }); | 
						|
    } | 
						|
    else { | 
						|
      throw new TypeError('options.filter must be a function, regular expression, or glob pattern'); | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  let sep = options.sep; | 
						|
  if (sep === null || sep === undefined) { | 
						|
    sep = path.sep; | 
						|
  } | 
						|
  else if (typeof sep !== 'string') { | 
						|
    throw new TypeError('options.sep must be a string'); | 
						|
  } | 
						|
 | 
						|
  let basePath = options.basePath; | 
						|
  if (basePath === null || basePath === undefined) { | 
						|
    basePath = ''; | 
						|
  } | 
						|
  else if (typeof basePath === 'string') { | 
						|
    // Append a path separator to the basePath, if necessary | 
						|
    if (basePath && basePath.substr(-1) !== sep) { | 
						|
      basePath += sep; | 
						|
    } | 
						|
  } | 
						|
  else { | 
						|
    throw new TypeError('options.basePath must be a string'); | 
						|
  } | 
						|
 | 
						|
  // Convert the basePath to POSIX (forward slashes) | 
						|
  // so that glob pattern matching works consistently, even on Windows | 
						|
  let posixBasePath = basePath; | 
						|
  if (posixBasePath && sep !== '/') { | 
						|
    posixBasePath = posixBasePath.replace(new RegExp('\\' + sep, 'g'), '/'); | 
						|
 | 
						|
    /* istanbul ignore if */ | 
						|
    if (isWindows) { | 
						|
      // Convert Windows root paths (C:\) and UNCs (\\) to POSIX root paths | 
						|
      posixBasePath = posixBasePath.replace(/^([a-zA-Z]\:\/|\/\/)/, '/'); | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  // Determine which facade methods to use | 
						|
  let facade; | 
						|
  if (options.fs === null || options.fs === undefined) { | 
						|
    // The user didn't provide their own facades, so use our internal ones | 
						|
    facade = internalOptions.facade; | 
						|
  } | 
						|
  else if (typeof options.fs === 'object') { | 
						|
    // Merge the internal facade methods with the user-provided `fs` facades | 
						|
    facade = Object.assign({}, internalOptions.facade); | 
						|
    facade.fs = Object.assign({}, internalOptions.facade.fs, options.fs); | 
						|
  } | 
						|
  else { | 
						|
    throw new TypeError('options.fs must be an object'); | 
						|
  } | 
						|
 | 
						|
  return { | 
						|
    recurseDepth, | 
						|
    recurseFn, | 
						|
    recurseRegExp, | 
						|
    recurseGlob, | 
						|
    filterFn, | 
						|
    filterRegExp, | 
						|
    filterGlob, | 
						|
    sep, | 
						|
    basePath, | 
						|
    posixBasePath, | 
						|
    facade, | 
						|
    emit: !!internalOptions.emit, | 
						|
    stats: !!internalOptions.stats, | 
						|
  }; | 
						|
}
 | 
						|
 |