index.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /*!
  2. * extglob <https://github.com/jonschlinkert/extglob>
  3. *
  4. * Copyright (c) 2015, Jon Schlinkert.
  5. * Licensed under the MIT License.
  6. */
  7. 'use strict';
  8. /**
  9. * Module dependencies
  10. */
  11. var isExtglob = require('is-extglob');
  12. var re, cache = {};
  13. /**
  14. * Expose `extglob`
  15. */
  16. module.exports = extglob;
  17. /**
  18. * Convert the given extglob `string` to a regex-compatible
  19. * string.
  20. *
  21. * ```js
  22. * var extglob = require('extglob');
  23. * extglob('!(a?(b))');
  24. * //=> '(?!a(?:b)?)[^/]*?'
  25. * ```
  26. *
  27. * @param {String} `str` The string to convert.
  28. * @param {Object} `options`
  29. * @option {Boolean} [options] `esc` If `false` special characters will not be escaped. Defaults to `true`.
  30. * @option {Boolean} [options] `regex` If `true` a regular expression is returned instead of a string.
  31. * @return {String}
  32. * @api public
  33. */
  34. function extglob(str, opts) {
  35. opts = opts || {};
  36. var o = {}, i = 0;
  37. // fix common character reversals
  38. // '*!(.js)' => '*.!(js)'
  39. str = str.replace(/!\(([^\w*()])/g, '$1!(');
  40. // support file extension negation
  41. str = str.replace(/([*\/])\.!\([*]\)/g, function (m, ch) {
  42. if (ch === '/') {
  43. return escape('\\/[^.]+');
  44. }
  45. return escape('[^.]+');
  46. });
  47. // create a unique key for caching by
  48. // combining the string and options
  49. var key = str
  50. + String(!!opts.regex)
  51. + String(!!opts.contains)
  52. + String(!!opts.escape);
  53. if (cache.hasOwnProperty(key)) {
  54. return cache[key];
  55. }
  56. if (!(re instanceof RegExp)) {
  57. re = regex();
  58. }
  59. opts.negate = false;
  60. var m;
  61. while (m = re.exec(str)) {
  62. var prefix = m[1];
  63. var inner = m[3];
  64. if (prefix === '!') {
  65. opts.negate = true;
  66. }
  67. var id = '__EXTGLOB_' + (i++) + '__';
  68. // use the prefix of the _last_ (outtermost) pattern
  69. o[id] = wrap(inner, prefix, opts.escape);
  70. str = str.split(m[0]).join(id);
  71. }
  72. var keys = Object.keys(o);
  73. var len = keys.length;
  74. // we have to loop again to allow us to convert
  75. // patterns in reverse order (starting with the
  76. // innermost/last pattern first)
  77. while (len--) {
  78. var prop = keys[len];
  79. str = str.split(prop).join(o[prop]);
  80. }
  81. var result = opts.regex
  82. ? toRegex(str, opts.contains, opts.negate)
  83. : str;
  84. result = result.split('.').join('\\.');
  85. // cache the result and return it
  86. return (cache[key] = result);
  87. }
  88. /**
  89. * Convert `string` to a regex string.
  90. *
  91. * @param {String} `str`
  92. * @param {String} `prefix` Character that determines how to wrap the string.
  93. * @param {Boolean} `esc` If `false` special characters will not be escaped. Defaults to `true`.
  94. * @return {String}
  95. */
  96. function wrap(inner, prefix, esc) {
  97. if (esc) inner = escape(inner);
  98. switch (prefix) {
  99. case '!':
  100. return '(?!' + inner + ')[^/]' + (esc ? '%%%~' : '*?');
  101. case '@':
  102. return '(?:' + inner + ')';
  103. case '+':
  104. return '(?:' + inner + ')+';
  105. case '*':
  106. return '(?:' + inner + ')' + (esc ? '%%' : '*')
  107. case '?':
  108. return '(?:' + inner + '|)';
  109. default:
  110. return inner;
  111. }
  112. }
  113. function escape(str) {
  114. str = str.split('*').join('[^/]%%%~');
  115. str = str.split('.').join('\\.');
  116. return str;
  117. }
  118. /**
  119. * extglob regex.
  120. */
  121. function regex() {
  122. return /(\\?[@?!+*$]\\?)(\(([^()]*?)\))/;
  123. }
  124. /**
  125. * Negation regex
  126. */
  127. function negate(str) {
  128. return '(?!^' + str + ').*$';
  129. }
  130. /**
  131. * Create the regex to do the matching. If
  132. * the leading character in the `pattern` is `!`
  133. * a negation regex is returned.
  134. *
  135. * @param {String} `pattern`
  136. * @param {Boolean} `contains` Allow loose matching.
  137. * @param {Boolean} `isNegated` True if the pattern is a negation pattern.
  138. */
  139. function toRegex(pattern, contains, isNegated) {
  140. var prefix = contains ? '^' : '';
  141. var after = contains ? '$' : '';
  142. pattern = ('(?:' + pattern + ')' + after);
  143. if (isNegated) {
  144. pattern = prefix + negate(pattern);
  145. }
  146. return new RegExp(prefix + pattern);
  147. }