Projekt

Obecné

Profil

Stáhnout (7.45 KB) Statistiky
| Větev: | Revize:
1
'use strict';
2

    
3
var regexNot = require('regex-not');
4
var toRegex = require('to-regex');
5

    
6
/**
7
 * Characters to use in negation regex (we want to "not" match
8
 * characters that are matched by other parsers)
9
 */
10

    
11
var cached;
12
var NOT_REGEX = '[\\[!*+?$^"\'.\\\\/]+';
13
var not = createTextRegex(NOT_REGEX);
14

    
15
/**
16
 * Nanomatch parsers
17
 */
18

    
19
module.exports = function(nanomatch, options) {
20
  var parser = nanomatch.parser;
21
  var opts = parser.options;
22

    
23
  parser.state = {
24
    slashes: 0,
25
    paths: []
26
  };
27

    
28
  parser.ast.state = parser.state;
29
  parser
30

    
31
    /**
32
     * Beginning-of-string
33
     */
34

    
35
    .capture('prefix', function() {
36
      if (this.parsed) return;
37
      var m = this.match(/^\.[\\/]/);
38
      if (!m) return;
39
      this.state.strictOpen = !!this.options.strictOpen;
40
      this.state.addPrefix = true;
41
    })
42

    
43
    /**
44
     * Escape: "\\."
45
     */
46

    
47
    .capture('escape', function() {
48
      if (this.isInside('bracket')) return;
49
      var pos = this.position();
50
      var m = this.match(/^(?:\\(.)|([$^]))/);
51
      if (!m) return;
52

    
53
      return pos({
54
        type: 'escape',
55
        val: m[2] || m[1]
56
      });
57
    })
58

    
59
    /**
60
     * Quoted strings
61
     */
62

    
63
    .capture('quoted', function() {
64
      var pos = this.position();
65
      var m = this.match(/^["']/);
66
      if (!m) return;
67

    
68
      var quote = m[0];
69
      if (this.input.indexOf(quote) === -1) {
70
        return pos({
71
          type: 'escape',
72
          val: quote
73
        });
74
      }
75

    
76
      var tok = advanceTo(this.input, quote);
77
      this.consume(tok.len);
78

    
79
      return pos({
80
        type: 'quoted',
81
        val: tok.esc
82
      });
83
    })
84

    
85
    /**
86
     * Negations: "!"
87
     */
88

    
89
    .capture('not', function() {
90
      var parsed = this.parsed;
91
      var pos = this.position();
92
      var m = this.match(this.notRegex || /^!+/);
93
      if (!m) return;
94
      var val = m[0];
95

    
96
      var isNegated = (val.length % 2) === 1;
97
      if (parsed === '' && !isNegated) {
98
        val = '';
99
      }
100

    
101
      // if nothing has been parsed, we know `!` is at the start,
102
      // so we need to wrap the result in a negation regex
103
      if (parsed === '' && isNegated && this.options.nonegate !== true) {
104
        this.bos.val = '(?!^(?:';
105
        this.append = ')$).*';
106
        val = '';
107
      }
108
      return pos({
109
        type: 'not',
110
        val: val
111
      });
112
    })
113

    
114
    /**
115
     * Dot: "."
116
     */
117

    
118
    .capture('dot', function() {
119
      var parsed = this.parsed;
120
      var pos = this.position();
121
      var m = this.match(/^\.+/);
122
      if (!m) return;
123

    
124
      var val = m[0];
125
      this.state.dot = val === '.' && (parsed === '' || parsed.slice(-1) === '/');
126

    
127
      return pos({
128
        type: 'dot',
129
        dotfiles: this.state.dot,
130
        val: val
131
      });
132
    })
133

    
134
    /**
135
     * Plus: "+"
136
     */
137

    
138
    .capture('plus', /^\+(?!\()/)
139

    
140
    /**
141
     * Question mark: "?"
142
     */
143

    
144
    .capture('qmark', function() {
145
      var parsed = this.parsed;
146
      var pos = this.position();
147
      var m = this.match(/^\?+(?!\()/);
148
      if (!m) return;
149

    
150
      this.state.metachar = true;
151
      this.state.qmark = true;
152

    
153
      return pos({
154
        type: 'qmark',
155
        parsed: parsed,
156
        val: m[0]
157
      });
158
    })
159

    
160
    /**
161
     * Globstar: "**"
162
     */
163

    
164
    .capture('globstar', function() {
165
      var parsed = this.parsed;
166
      var pos = this.position();
167
      var m = this.match(/^\*{2}(?![*(])(?=[,)/]|$)/);
168
      if (!m) return;
169

    
170
      var type = opts.noglobstar !== true ? 'globstar' : 'star';
171
      var node = pos({type: type, parsed: parsed});
172
      this.state.metachar = true;
173

    
174
      while (this.input.slice(0, 4) === '/**/') {
175
        this.input = this.input.slice(3);
176
      }
177

    
178
      node.isInside = {
179
        brace: this.isInside('brace'),
180
        paren: this.isInside('paren')
181
      };
182

    
183
      if (type === 'globstar') {
184
        this.state.globstar = true;
185
        node.val = '**';
186

    
187
      } else {
188
        this.state.star = true;
189
        node.val = '*';
190
      }
191

    
192
      return node;
193
    })
194

    
195
    /**
196
     * Star: "*"
197
     */
198

    
199
    .capture('star', function() {
200
      var pos = this.position();
201
      var starRe = /^(?:\*(?![*(])|[*]{3,}(?!\()|[*]{2}(?![(/]|$)|\*(?=\*\())/;
202
      var m = this.match(starRe);
203
      if (!m) return;
204

    
205
      this.state.metachar = true;
206
      this.state.star = true;
207
      return pos({
208
        type: 'star',
209
        val: m[0]
210
      });
211
    })
212

    
213
    /**
214
     * Slash: "/"
215
     */
216

    
217
    .capture('slash', function() {
218
      var pos = this.position();
219
      var m = this.match(/^\//);
220
      if (!m) return;
221

    
222
      this.state.slashes++;
223
      return pos({
224
        type: 'slash',
225
        val: m[0]
226
      });
227
    })
228

    
229
    /**
230
     * Backslash: "\\"
231
     */
232

    
233
    .capture('backslash', function() {
234
      var pos = this.position();
235
      var m = this.match(/^\\(?![*+?(){}[\]'"])/);
236
      if (!m) return;
237

    
238
      var val = m[0];
239

    
240
      if (this.isInside('bracket')) {
241
        val = '\\';
242
      } else if (val.length > 1) {
243
        val = '\\\\';
244
      }
245

    
246
      return pos({
247
        type: 'backslash',
248
        val: val
249
      });
250
    })
251

    
252
    /**
253
     * Square: "[.]"
254
     */
255

    
256
    .capture('square', function() {
257
      if (this.isInside('bracket')) return;
258
      var pos = this.position();
259
      var m = this.match(/^\[([^!^\\])\]/);
260
      if (!m) return;
261

    
262
      return pos({
263
        type: 'square',
264
        val: m[1]
265
      });
266
    })
267

    
268
    /**
269
     * Brackets: "[...]" (basic, this can be overridden by other parsers)
270
     */
271

    
272
    .capture('bracket', function() {
273
      var pos = this.position();
274
      var m = this.match(/^(?:\[([!^]?)([^\]]+|\]-)(\]|[^*+?]+)|\[)/);
275
      if (!m) return;
276

    
277
      var val = m[0];
278
      var negated = m[1] ? '^' : '';
279
      var inner = (m[2] || '').replace(/\\\\+/, '\\\\');
280
      var close = m[3] || '';
281

    
282
      if (m[2] && inner.length < m[2].length) {
283
        val = val.replace(/\\\\+/, '\\\\');
284
      }
285

    
286
      var esc = this.input.slice(0, 2);
287
      if (inner === '' && esc === '\\]') {
288
        inner += esc;
289
        this.consume(2);
290

    
291
        var str = this.input;
292
        var idx = -1;
293
        var ch;
294

    
295
        while ((ch = str[++idx])) {
296
          this.consume(1);
297
          if (ch === ']') {
298
            close = ch;
299
            break;
300
          }
301
          inner += ch;
302
        }
303
      }
304

    
305
      return pos({
306
        type: 'bracket',
307
        val: val,
308
        escaped: close !== ']',
309
        negated: negated,
310
        inner: inner,
311
        close: close
312
      });
313
    })
314

    
315
    /**
316
     * Text
317
     */
318

    
319
    .capture('text', function() {
320
      if (this.isInside('bracket')) return;
321
      var pos = this.position();
322
      var m = this.match(not);
323
      if (!m || !m[0]) return;
324

    
325
      return pos({
326
        type: 'text',
327
        val: m[0]
328
      });
329
    });
330

    
331
  /**
332
   * Allow custom parsers to be passed on options
333
   */
334

    
335
  if (options && typeof options.parsers === 'function') {
336
    options.parsers(nanomatch.parser);
337
  }
338
};
339

    
340
/**
341
 * Advance to the next non-escaped character
342
 */
343

    
344
function advanceTo(input, endChar) {
345
  var ch = input.charAt(0);
346
  var tok = { len: 1, val: '', esc: '' };
347
  var idx = 0;
348

    
349
  function advance() {
350
    if (ch !== '\\') {
351
      tok.esc += '\\' + ch;
352
      tok.val += ch;
353
    }
354

    
355
    ch = input.charAt(++idx);
356
    tok.len++;
357

    
358
    if (ch === '\\') {
359
      advance();
360
      advance();
361
    }
362
  }
363

    
364
  while (ch && ch !== endChar) {
365
    advance();
366
  }
367
  return tok;
368
}
369

    
370
/**
371
 * Create text regex
372
 */
373

    
374
function createTextRegex(pattern) {
375
  if (cached) return cached;
376
  var opts = {contains: true, strictClose: false};
377
  var not = regexNot.create(pattern, opts);
378
  var re = toRegex('^(?:[*]\\((?=.)|' + not + ')', opts);
379
  return (cached = re);
380
}
381

    
382
/**
383
 * Expose negation string
384
 */
385

    
386
module.exports.not = NOT_REGEX;
(3-3/4)