Skip to content
Snippets Groups Projects
index.js 3.11 KiB
Newer Older
  • Learn to ignore specific revisions
  • Jay's avatar
    Jay committed
    'use strict';
    
    Object.defineProperty(exports, "__esModule", { value: true });
    
    const picomatch = require('picomatch');
    const normalizePath = require('normalize-path');
    
    /**
     * @typedef {(testString: string) => boolean} AnymatchFn
     * @typedef {string|RegExp|AnymatchFn} AnymatchPattern
     * @typedef {AnymatchPattern|AnymatchPattern[]} AnymatchMatcher
     */
    const BANG = '!';
    const DEFAULT_OPTIONS = {returnIndex: false};
    const arrify = (item) => Array.isArray(item) ? item : [item];
    
    /**
     * @param {AnymatchPattern} matcher
     * @param {object} options
     * @returns {AnymatchFn}
     */
    const createPattern = (matcher, options) => {
      if (typeof matcher === 'function') {
        return matcher;
      }
      if (typeof matcher === 'string') {
        const glob = picomatch(matcher, options);
        return (string) => matcher === string || glob(string);
      }
      if (matcher instanceof RegExp) {
        return (string) => matcher.test(string);
      }
      return (string) => false;
    };
    
    /**
     * @param {Array<Function>} patterns
     * @param {Array<Function>} negPatterns
     * @param {String|Array} args
     * @param {Boolean} returnIndex
     * @returns {boolean|number}
     */
    const matchPatterns = (patterns, negPatterns, args, returnIndex) => {
      const isList = Array.isArray(args);
      const _path = isList ? args[0] : args;
      if (!isList && typeof _path !== 'string') {
        throw new TypeError('anymatch: second argument must be a string: got ' +
          Object.prototype.toString.call(_path))
      }
      const path = normalizePath(_path, false);
    
      for (let index = 0; index < negPatterns.length; index++) {
        const nglob = negPatterns[index];
        if (nglob(path)) {
          return returnIndex ? -1 : false;
        }
      }
    
      const applied = isList && [path].concat(args.slice(1));
      for (let index = 0; index < patterns.length; index++) {
        const pattern = patterns[index];
        if (isList ? pattern(...applied) : pattern(path)) {
          return returnIndex ? index : true;
        }
      }
    
      return returnIndex ? -1 : false;
    };
    
    /**
     * @param {AnymatchMatcher} matchers
     * @param {Array|string} testString
     * @param {object} options
     * @returns {boolean|number|Function}
     */
    const anymatch = (matchers, testString, options = DEFAULT_OPTIONS) => {
      if (matchers == null) {
        throw new TypeError('anymatch: specify first argument');
      }
      const opts = typeof options === 'boolean' ? {returnIndex: options} : options;
      const returnIndex = opts.returnIndex || false;
    
      // Early cache for matchers.
      const mtchers = arrify(matchers);
      const negatedGlobs = mtchers
        .filter(item => typeof item === 'string' && item.charAt(0) === BANG)
        .map(item => item.slice(1))
        .map(item => picomatch(item, opts));
      const patterns = mtchers
        .filter(item => typeof item !== 'string' || (typeof item === 'string' && item.charAt(0) !== BANG))
        .map(matcher => createPattern(matcher, opts));
    
      if (testString == null) {
        return (testString, ri = false) => {
          const returnIndex = typeof ri === 'boolean' ? ri : false;
          return matchPatterns(patterns, negatedGlobs, testString, returnIndex);
        }
      }
    
      return matchPatterns(patterns, negatedGlobs, testString, returnIndex);
    };
    
    anymatch.default = anymatch;
    module.exports = anymatch;