compilers.js
  1  'use strict';
  2  
  3  var brackets = require('expand-brackets');
  4  
  5  /**
  6   * Extglob compilers
  7   */
  8  
  9  module.exports = function(extglob) {
 10    function star() {
 11      if (typeof extglob.options.star === 'function') {
 12        return extglob.options.star.apply(this, arguments);
 13      }
 14      if (typeof extglob.options.star === 'string') {
 15        return extglob.options.star;
 16      }
 17      return '.*?';
 18    }
 19  
 20    /**
 21     * Use `expand-brackets` compilers
 22     */
 23  
 24    extglob.use(brackets.compilers);
 25    extglob.compiler
 26  
 27      /**
 28       * Escaped: "\\*"
 29       */
 30  
 31      .set('escape', function(node) {
 32        return this.emit(node.val, node);
 33      })
 34  
 35      /**
 36       * Dot: "."
 37       */
 38  
 39      .set('dot', function(node) {
 40        return this.emit('\\' + node.val, node);
 41      })
 42  
 43      /**
 44       * Question mark: "?"
 45       */
 46  
 47      .set('qmark', function(node) {
 48        var val = '[^\\\\/.]';
 49        var prev = this.prev();
 50  
 51        if (node.parsed.slice(-1) === '(') {
 52          var ch = node.rest.charAt(0);
 53          if (ch !== '!' && ch !== '=' && ch !== ':') {
 54            return this.emit(val, node);
 55          }
 56          return this.emit(node.val, node);
 57        }
 58  
 59        if (prev.type === 'text' && prev.val) {
 60          return this.emit(val, node);
 61        }
 62  
 63        if (node.val.length > 1) {
 64          val += '{' + node.val.length + '}';
 65        }
 66        return this.emit(val, node);
 67      })
 68  
 69      /**
 70       * Plus: "+"
 71       */
 72  
 73      .set('plus', function(node) {
 74        var prev = node.parsed.slice(-1);
 75        if (prev === ']' || prev === ')') {
 76          return this.emit(node.val, node);
 77        }
 78        var ch = this.output.slice(-1);
 79        if (!this.output || (/[?*+]/.test(ch) && node.parent.type !== 'bracket')) {
 80          return this.emit('\\+', node);
 81        }
 82        if (/\w/.test(ch) && !node.inside) {
 83          return this.emit('+\\+?', node);
 84        }
 85        return this.emit('+', node);
 86      })
 87  
 88      /**
 89       * Star: "*"
 90       */
 91  
 92      .set('star', function(node) {
 93        var prev = this.prev();
 94        var prefix = prev.type !== 'text' && prev.type !== 'escape'
 95          ? '(?!\\.)'
 96          : '';
 97  
 98        return this.emit(prefix + star.call(this, node), node);
 99      })
100  
101      /**
102       * Parens
103       */
104  
105      .set('paren', function(node) {
106        return this.mapVisit(node.nodes);
107      })
108      .set('paren.open', function(node) {
109        var capture = this.options.capture ? '(' : '';
110  
111        switch (node.parent.prefix) {
112          case '!':
113          case '^':
114            return this.emit(capture + '(?:(?!(?:', node);
115          case '*':
116          case '+':
117          case '?':
118          case '@':
119            return this.emit(capture + '(?:', node);
120          default: {
121            var val = node.val;
122            if (this.options.bash === true) {
123              val = '\\' + val;
124            } else if (!this.options.capture && val === '(' && node.parent.rest[0] !== '?') {
125              val += '?:';
126            }
127  
128            return this.emit(val, node);
129          }
130        }
131      })
132      .set('paren.close', function(node) {
133        var capture = this.options.capture ? ')' : '';
134  
135        switch (node.prefix) {
136          case '!':
137          case '^':
138            var prefix = /^(\)|$)/.test(node.rest) ? '$' : '';
139            var str = star.call(this, node);
140  
141            // if the extglob has a slash explicitly defined, we know the user wants
142            // to match slashes, so we need to ensure the "star" regex allows for it
143            if (node.parent.hasSlash && !this.options.star && this.options.slash !== false) {
144              str = '.*?';
145            }
146  
147            return this.emit(prefix + ('))' + str + ')') + capture, node);
148          case '*':
149          case '+':
150          case '?':
151            return this.emit(')' + node.prefix + capture, node);
152          case '@':
153            return this.emit(')' + capture, node);
154          default: {
155            var val = (this.options.bash === true ? '\\' : '') + ')';
156            return this.emit(val, node);
157          }
158        }
159      })
160  
161      /**
162       * Text
163       */
164  
165      .set('text', function(node) {
166        var val = node.val.replace(/[\[\]]/g, '\\$&');
167        return this.emit(val, node);
168      });
169  };