glob.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. 'use strict';
  2. var chars = require('./chars');
  3. var utils = require('./utils');
  4. /**
  5. * Expose `Glob`
  6. */
  7. var Glob = module.exports = function Glob(pattern, options) {
  8. if (!(this instanceof Glob)) {
  9. return new Glob(pattern, options);
  10. }
  11. this.options = options || {};
  12. this.pattern = pattern;
  13. this.history = [];
  14. this.tokens = {};
  15. this.init(pattern);
  16. };
  17. /**
  18. * Initialize defaults
  19. */
  20. Glob.prototype.init = function(pattern) {
  21. this.orig = pattern;
  22. this.negated = this.isNegated();
  23. this.options.track = this.options.track || false;
  24. this.options.makeRe = true;
  25. };
  26. /**
  27. * Push a change into `glob.history`. Useful
  28. * for debugging.
  29. */
  30. Glob.prototype.track = function(msg) {
  31. if (this.options.track) {
  32. this.history.push({msg: msg, pattern: this.pattern});
  33. }
  34. };
  35. /**
  36. * Return true if `glob.pattern` was negated
  37. * with `!`, also remove the `!` from the pattern.
  38. *
  39. * @return {Boolean}
  40. */
  41. Glob.prototype.isNegated = function() {
  42. if (this.pattern.charCodeAt(0) === 33 /* '!' */) {
  43. this.pattern = this.pattern.slice(1);
  44. return true;
  45. }
  46. return false;
  47. };
  48. /**
  49. * Expand braces in the given glob pattern.
  50. *
  51. * We only need to use the [braces] lib when
  52. * patterns are nested.
  53. */
  54. Glob.prototype.braces = function() {
  55. if (this.options.nobraces !== true && this.options.nobrace !== true) {
  56. // naive/fast check for imbalanced characters
  57. var a = this.pattern.match(/[\{\(\[]/g);
  58. var b = this.pattern.match(/[\}\)\]]/g);
  59. // if imbalanced, don't optimize the pattern
  60. if (a && b && (a.length !== b.length)) {
  61. this.options.makeRe = false;
  62. }
  63. // expand brace patterns and join the resulting array
  64. var expanded = utils.braces(this.pattern, this.options);
  65. this.pattern = expanded.join('|');
  66. }
  67. };
  68. /**
  69. * Expand bracket expressions in `glob.pattern`
  70. */
  71. Glob.prototype.brackets = function() {
  72. if (this.options.nobrackets !== true) {
  73. this.pattern = utils.brackets(this.pattern);
  74. }
  75. };
  76. /**
  77. * Expand bracket expressions in `glob.pattern`
  78. */
  79. Glob.prototype.extglob = function() {
  80. if (this.options.noextglob === true) return;
  81. if (utils.isExtglob(this.pattern)) {
  82. this.pattern = utils.extglob(this.pattern, {escape: true});
  83. }
  84. };
  85. /**
  86. * Parse the given pattern
  87. */
  88. Glob.prototype.parse = function(pattern) {
  89. this.tokens = utils.parseGlob(pattern || this.pattern, true);
  90. return this.tokens;
  91. };
  92. /**
  93. * Replace `a` with `b`. Also tracks the change before and
  94. * after each replacement. This is disabled by default, but
  95. * can be enabled by setting `options.track` to true.
  96. *
  97. * Also, when the pattern is a string, `.split()` is used,
  98. * because it's much faster than replace.
  99. *
  100. * @param {RegExp|String} `a`
  101. * @param {String} `b`
  102. * @param {Boolean} `escape` When `true`, escapes `*` and `?` in the replacement.
  103. * @return {String}
  104. */
  105. Glob.prototype._replace = function(a, b, escape) {
  106. this.track('before (find): "' + a + '" (replace with): "' + b + '"');
  107. if (escape) b = esc(b);
  108. if (a && b && typeof a === 'string') {
  109. this.pattern = this.pattern.split(a).join(b);
  110. } else {
  111. this.pattern = this.pattern.replace(a, b);
  112. }
  113. this.track('after');
  114. };
  115. /**
  116. * Escape special characters in the given string.
  117. *
  118. * @param {String} `str` Glob pattern
  119. * @return {String}
  120. */
  121. Glob.prototype.escape = function(str) {
  122. this.track('before escape: ');
  123. var re = /["\\](['"]?[^"'\\]['"]?)/g;
  124. this.pattern = str.replace(re, function($0, $1) {
  125. var o = chars.ESC;
  126. var ch = o && o[$1];
  127. if (ch) {
  128. return ch;
  129. }
  130. if (/[a-z]/i.test($0)) {
  131. return $0.split('\\').join('');
  132. }
  133. return $0;
  134. });
  135. this.track('after escape: ');
  136. };
  137. /**
  138. * Unescape special characters in the given string.
  139. *
  140. * @param {String} `str`
  141. * @return {String}
  142. */
  143. Glob.prototype.unescape = function(str) {
  144. var re = /__([A-Z]+)_([A-Z]+)__/g;
  145. this.pattern = str.replace(re, function($0, $1) {
  146. return chars[$1][$0];
  147. });
  148. this.pattern = unesc(this.pattern);
  149. };
  150. /**
  151. * Escape/unescape utils
  152. */
  153. function esc(str) {
  154. str = str.split('?').join('%~');
  155. str = str.split('*').join('%%');
  156. return str;
  157. }
  158. function unesc(str) {
  159. str = str.split('%~').join('?');
  160. str = str.split('%%').join('*');
  161. return str;
  162. }