index.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. 'use strict';
  2. /**
  3. * Module dependencies
  4. */
  5. var toRegex = require('to-regex');
  6. var unique = require('array-unique');
  7. var extend = require('extend-shallow');
  8. /**
  9. * Local dependencies
  10. */
  11. var compilers = require('./lib/compilers');
  12. var parsers = require('./lib/parsers');
  13. var Braces = require('./lib/braces');
  14. var utils = require('./lib/utils');
  15. var MAX_LENGTH = 1024 * 64;
  16. var cache = {};
  17. /**
  18. * Convert the given `braces` pattern into a regex-compatible string. By default, only one string is generated for every input string. Set `options.expand` to true to return an array of patterns (similar to Bash or minimatch. Before using `options.expand`, it's recommended that you read the [performance notes](#performance)).
  19. *
  20. * ```js
  21. * var braces = require('braces');
  22. * console.log(braces('{a,b,c}'));
  23. * //=> ['(a|b|c)']
  24. *
  25. * console.log(braces('{a,b,c}', {expand: true}));
  26. * //=> ['a', 'b', 'c']
  27. * ```
  28. * @param {String} `str`
  29. * @param {Object} `options`
  30. * @return {String}
  31. * @api public
  32. */
  33. function braces(pattern, options) {
  34. var key = utils.createKey(String(pattern), options);
  35. var arr = [];
  36. var disabled = options && options.cache === false;
  37. if (!disabled && cache.hasOwnProperty(key)) {
  38. return cache[key];
  39. }
  40. if (Array.isArray(pattern)) {
  41. for (var i = 0; i < pattern.length; i++) {
  42. arr.push.apply(arr, braces.create(pattern[i], options));
  43. }
  44. } else {
  45. arr = braces.create(pattern, options);
  46. }
  47. if (options && options.nodupes === true) {
  48. arr = unique(arr);
  49. }
  50. if (!disabled) {
  51. cache[key] = arr;
  52. }
  53. return arr;
  54. }
  55. /**
  56. * Expands a brace pattern into an array. This method is called by the main [braces](#braces) function when `options.expand` is true. Before using this method it's recommended that you read the [performance notes](#performance)) and advantages of using [.optimize](#optimize) instead.
  57. *
  58. * ```js
  59. * var braces = require('braces');
  60. * console.log(braces.expand('a/{b,c}/d'));
  61. * //=> ['a/b/d', 'a/c/d'];
  62. * ```
  63. * @param {String} `pattern` Brace pattern
  64. * @param {Object} `options`
  65. * @return {Array} Returns an array of expanded values.
  66. * @api public
  67. */
  68. braces.expand = function(pattern, options) {
  69. return braces.create(pattern, extend({}, options, {expand: true}));
  70. };
  71. /**
  72. * Expands a brace pattern into a regex-compatible, optimized string. This method is called by the main [braces](#braces) function by default.
  73. *
  74. * ```js
  75. * var braces = require('braces');
  76. * console.log(braces.expand('a/{b,c}/d'));
  77. * //=> ['a/(b|c)/d']
  78. * ```
  79. * @param {String} `pattern` Brace pattern
  80. * @param {Object} `options`
  81. * @return {Array} Returns an array of expanded values.
  82. * @api public
  83. */
  84. braces.optimize = function(pattern, options) {
  85. return braces.create(pattern, options);
  86. };
  87. /**
  88. * Processes a brace pattern and returns either an expanded array (if `options.expand` is true), a highly optimized regex-compatible string. This method is called by the main [braces](#braces) function.
  89. *
  90. * ```js
  91. * var braces = require('braces');
  92. * console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}'))
  93. * //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)'
  94. * ```
  95. * @param {String} `pattern` Brace pattern
  96. * @param {Object} `options`
  97. * @return {Array} Returns an array of expanded values.
  98. * @api public
  99. */
  100. braces.create = function(pattern, options) {
  101. if (typeof pattern !== 'string') {
  102. throw new TypeError('expected a string');
  103. }
  104. var maxLength = (options && options.maxLength) || MAX_LENGTH;
  105. if (pattern.length >= maxLength) {
  106. throw new Error('expected pattern to be less than ' + maxLength + ' characters');
  107. }
  108. function create() {
  109. if (pattern === '' || pattern.length < 3) {
  110. return [pattern];
  111. }
  112. if (utils.isEmptySets(pattern)) {
  113. return [];
  114. }
  115. if (utils.isQuotedString(pattern)) {
  116. return [pattern.slice(1, -1)];
  117. }
  118. var proto = new Braces(options);
  119. var result = !options || options.expand !== true
  120. ? proto.optimize(pattern, options)
  121. : proto.expand(pattern, options);
  122. // get the generated pattern(s)
  123. var arr = result.output;
  124. // filter out empty strings if specified
  125. if (options && options.noempty === true) {
  126. arr = arr.filter(Boolean);
  127. }
  128. // filter out duplicates if specified
  129. if (options && options.nodupes === true) {
  130. arr = unique(arr);
  131. }
  132. Object.defineProperty(arr, 'result', {
  133. enumerable: false,
  134. value: result
  135. });
  136. return arr;
  137. }
  138. return memoize('create', pattern, options, create);
  139. };
  140. /**
  141. * Create a regular expression from the given string `pattern`.
  142. *
  143. * ```js
  144. * var braces = require('braces');
  145. *
  146. * console.log(braces.makeRe('id-{200..300}'));
  147. * //=> /^(?:id-(20[0-9]|2[1-9][0-9]|300))$/
  148. * ```
  149. * @param {String} `pattern` The pattern to convert to regex.
  150. * @param {Object} `options`
  151. * @return {RegExp}
  152. * @api public
  153. */
  154. braces.makeRe = function(pattern, options) {
  155. if (typeof pattern !== 'string') {
  156. throw new TypeError('expected a string');
  157. }
  158. var maxLength = (options && options.maxLength) || MAX_LENGTH;
  159. if (pattern.length >= maxLength) {
  160. throw new Error('expected pattern to be less than ' + maxLength + ' characters');
  161. }
  162. function makeRe() {
  163. var arr = braces(pattern, options);
  164. var opts = extend({strictErrors: false}, options);
  165. return toRegex(arr, opts);
  166. }
  167. return memoize('makeRe', pattern, options, makeRe);
  168. };
  169. /**
  170. * Parse the given `str` with the given `options`.
  171. *
  172. * ```js
  173. * var braces = require('braces');
  174. * var ast = braces.parse('a/{b,c}/d');
  175. * console.log(ast);
  176. * // { type: 'root',
  177. * // errors: [],
  178. * // input: 'a/{b,c}/d',
  179. * // nodes:
  180. * // [ { type: 'bos', val: '' },
  181. * // { type: 'text', val: 'a/' },
  182. * // { type: 'brace',
  183. * // nodes:
  184. * // [ { type: 'brace.open', val: '{' },
  185. * // { type: 'text', val: 'b,c' },
  186. * // { type: 'brace.close', val: '}' } ] },
  187. * // { type: 'text', val: '/d' },
  188. * // { type: 'eos', val: '' } ] }
  189. * ```
  190. * @param {String} `pattern` Brace pattern to parse
  191. * @param {Object} `options`
  192. * @return {Object} Returns an AST
  193. * @api public
  194. */
  195. braces.parse = function(pattern, options) {
  196. var proto = new Braces(options);
  197. return proto.parse(pattern, options);
  198. };
  199. /**
  200. * Compile the given `ast` or string with the given `options`.
  201. *
  202. * ```js
  203. * var braces = require('braces');
  204. * var ast = braces.parse('a/{b,c}/d');
  205. * console.log(braces.compile(ast));
  206. * // { options: { source: 'string' },
  207. * // state: {},
  208. * // compilers:
  209. * // { eos: [Function],
  210. * // noop: [Function],
  211. * // bos: [Function],
  212. * // brace: [Function],
  213. * // 'brace.open': [Function],
  214. * // text: [Function],
  215. * // 'brace.close': [Function] },
  216. * // output: [ 'a/(b|c)/d' ],
  217. * // ast:
  218. * // { ... },
  219. * // parsingErrors: [] }
  220. * ```
  221. * @param {Object|String} `ast` AST from [.parse](#parse). If a string is passed it will be parsed first.
  222. * @param {Object} `options`
  223. * @return {Object} Returns an object that has an `output` property with the compiled string.
  224. * @api public
  225. */
  226. braces.compile = function(ast, options) {
  227. var proto = new Braces(options);
  228. return proto.compile(ast, options);
  229. };
  230. /**
  231. * Clear the regex cache.
  232. *
  233. * ```js
  234. * braces.clearCache();
  235. * ```
  236. * @api public
  237. */
  238. braces.clearCache = function() {
  239. cache = braces.cache = {};
  240. };
  241. /**
  242. * Memoize a generated regex or function. A unique key is generated
  243. * from the method name, pattern, and user-defined options. Set
  244. * options.memoize to false to disable.
  245. */
  246. function memoize(type, pattern, options, fn) {
  247. var key = utils.createKey(type + ':' + pattern, options);
  248. var disabled = options && options.cache === false;
  249. if (disabled) {
  250. braces.clearCache();
  251. return fn(pattern, options);
  252. }
  253. if (cache.hasOwnProperty(key)) {
  254. return cache[key];
  255. }
  256. var res = fn(pattern, options);
  257. cache[key] = res;
  258. return res;
  259. }
  260. /**
  261. * Expose `Braces` constructor and methods
  262. * @type {Function}
  263. */
  264. braces.Braces = Braces;
  265. braces.compilers = compilers;
  266. braces.parsers = parsers;
  267. braces.cache = cache;
  268. /**
  269. * Expose `braces`
  270. * @type {Function}
  271. */
  272. module.exports = braces;