UNPKG

4.53 kBJavaScriptView Raw
1'use strict';
2
3var brackets = require('expand-brackets');
4var braces = require('braces');
5var parse = require('parse-glob');
6var chars = require('./chars');
7
8/**
9 * Expose `Glob`
10 */
11
12module.exports = Glob;
13
14function Glob(pattern, options) {
15 this.options = options || {};
16 this.pattern = pattern;
17 this.history = [];
18 this.tokens = {};
19 this.init(pattern);
20}
21
22/**
23 * Initialize defaults
24 */
25
26Glob.prototype.init = function(pattern) {
27 this.orig = pattern;
28 this.negated = this.isNegated();
29 this.options.track = this.options.track || false;
30 this.options.dot = this.options.dot || this.options.dotfiles;
31 this.options.makeRe = true;
32};
33
34/**
35 * Push a change into `glob.history`. Useful
36 * for debugging.
37 */
38
39Glob.prototype.track = function(msg) {
40 if (this.options.track) {
41 this.history.push({msg: msg, pattern: this.pattern});
42 }
43};
44
45/**
46 * Return true if the glob pattern has the given
47 * `ch`aracter.
48 *
49 * @param {String} `pattern`
50 * @param {String} `ch`
51 * @return {Boolean}
52 */
53
54Glob.prototype.has = function(pattern, ch) {
55 if (ch instanceof RegExp) {
56 return ch.test(pattern);
57 }
58 return pattern.indexOf(ch) !== -1;
59};
60
61/**
62 * Return true if `glob.pattern` was negated
63 * with `!`. Also removes the `!` from the pattern.
64 *
65 * @return {Boolean}
66 */
67
68Glob.prototype.isNegated = function() {
69 if (this.pattern.charCodeAt(0) === 33 /* '!' */) {
70 this.pattern = this.pattern.slice(1);
71 return true;
72 }
73 return false;
74};
75
76/**
77 * Return true if the glob pattern has braces
78 *
79 * @param {String} `pattern`
80 * @return {Boolean}
81 */
82
83Glob.prototype.hasBraces = function(pattern) {
84 return this.has((pattern || this.pattern), '{');
85};
86
87/**
88 * Expand braces in the given glob pattern.
89 *
90 * We only need to use the [braces] lib when
91 * patterns are nested.
92 */
93
94Glob.prototype.braces = function() {
95 if (this.hasBraces() && this.options.nobraces !== true) {
96 var a = this.pattern.match(/[\{\(\[]/g);
97 var b = this.pattern.match(/[\}\)\]]/g);
98 if (a && b && (a.length !== b.length)) {
99 this.options.makeRe = false;
100 }
101 var expanded = braces(this.pattern, this.options);
102 this.pattern = expanded.join('|');
103 }
104};
105
106/**
107 * Return true if the glob pattern has a POSIX
108 * bracket expression (character class)
109 *
110 * @param {String} `pattern`
111 * @return {Boolean}
112 */
113
114Glob.prototype.hasBrackets = function(pattern) {
115 return this.has((pattern || this.pattern), '[:');
116};
117
118/**
119 * Expand bracket expressions in `glob.pattern`
120 */
121
122Glob.prototype.brackets = function() {
123 if (this.hasBrackets() && this.options.nobrackets !== true) {
124 this.pattern = brackets(this.pattern);
125 }
126};
127
128/**
129 * Parse the given glob `pattern` or `glob.pattern`
130 */
131
132Glob.prototype.parse = function(pattern) {
133 this.tokens = parse(pattern || this.pattern, true);
134 return this.tokens;
135};
136
137/**
138 * Replace `a` with `b`. Also tracks the change before and
139 * after each replacement. This is disabled by default, but
140 * can be enabled by setting `options.track` to true.
141 *
142 * Also, when the pattern is a string, `.split()` is used,
143 * because it's much faster than replace.
144 *
145 * @param {RegExp|String} `a`
146 * @param {String} `b`
147 * @param {Boolean} `escape` When `true`, escapes `*` and `?` in the replacement.
148 * @return {String}
149 */
150
151Glob.prototype._replace = function(a, b, escape) {
152 this.track('before (find): "' + a + '" (replace with): "' + b + '"');
153 if (escape) b = esc(b);
154 if (a && b && typeof a === 'string') {
155 this.pattern = this.pattern.split(a).join(b);
156 } else if (a instanceof RegExp) {
157 this.pattern = this.pattern.replace(a, b);
158 }
159 this.track('after');
160};
161
162/**
163 * Escape special characters in the given string.
164 *
165 * @param {String} `str` Glob pattern
166 * @return {String}
167 */
168
169Glob.prototype.escape = function(str) {
170 this.track('before escape: ');
171 var re = /["\\](['"]?[^"'\\]['"]?)/g;
172
173 this.pattern = str.replace(re, function($0, $1) {
174 var o = chars.ESC;
175 var ch = o && o[$1];
176 if (ch) {
177 return ch;
178 }
179 if (/[a-z]/i.test($0)) {
180 return $0.split('\\').join('');
181 }
182 return $0;
183 });
184
185 this.track('after escape: ');
186};
187
188/**
189 * Unescape special characters in the given string.
190 *
191 * @param {String} `str`
192 * @return {String}
193 */
194
195Glob.prototype.unescape = function(str) {
196 var re = /__([A-Z]+)_([A-Z]+)__/g;
197 this.pattern = str.replace(re, function($0, $1) {
198 return chars[$1][$0];
199 });
200};
201
202/**
203 * Escape utils
204 */
205
206function esc(str) {
207 str = str.split('?').join('%~');
208 str = str.split('*').join('%%');
209 return str;
210}