Brak opisu

index.js 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  1. 'use strict';
  2. /**
  3. * Module dependencies
  4. */
  5. var util = require('util');
  6. var toRegex = require('to-regex');
  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 cache = require('./lib/cache');
  14. var utils = require('./lib/utils');
  15. var MAX_LENGTH = 1024 * 64;
  16. /**
  17. * The main function takes a list of strings and one or more
  18. * glob patterns to use for matching.
  19. *
  20. * ```js
  21. * var nm = require('nanomatch');
  22. * nm(list, patterns[, options]);
  23. *
  24. * console.log(nm(['a.js', 'a.txt'], ['*.js']));
  25. * //=> [ 'a.js' ]
  26. * ```
  27. * @param {Array} `list` A list of strings to match
  28. * @param {String|Array} `patterns` One or more glob patterns to use for matching.
  29. * @param {Object} `options` See available [options](#options) for changing how matches are performed
  30. * @return {Array} Returns an array of matches
  31. * @summary false
  32. * @api public
  33. */
  34. function nanomatch(list, patterns, options) {
  35. patterns = utils.arrayify(patterns);
  36. list = utils.arrayify(list);
  37. var len = patterns.length;
  38. if (list.length === 0 || len === 0) {
  39. return [];
  40. }
  41. if (len === 1) {
  42. return nanomatch.match(list, patterns[0], options);
  43. }
  44. var negated = false;
  45. var omit = [];
  46. var keep = [];
  47. var idx = -1;
  48. while (++idx < len) {
  49. var pattern = patterns[idx];
  50. if (typeof pattern === 'string' && pattern.charCodeAt(0) === 33 /* ! */) {
  51. omit.push.apply(omit, nanomatch.match(list, pattern.slice(1), options));
  52. negated = true;
  53. } else {
  54. keep.push.apply(keep, nanomatch.match(list, pattern, options));
  55. }
  56. }
  57. // minimatch.match parity
  58. if (negated && keep.length === 0) {
  59. if (options && options.unixify === false) {
  60. keep = list.slice();
  61. } else {
  62. var unixify = utils.unixify(options);
  63. for (var i = 0; i < list.length; i++) {
  64. keep.push(unixify(list[i]));
  65. }
  66. }
  67. }
  68. var matches = utils.diff(keep, omit);
  69. if (!options || options.nodupes !== false) {
  70. return utils.unique(matches);
  71. }
  72. return matches;
  73. }
  74. /**
  75. * Similar to the main function, but `pattern` must be a string.
  76. *
  77. * ```js
  78. * var nm = require('nanomatch');
  79. * nm.match(list, pattern[, options]);
  80. *
  81. * console.log(nm.match(['a.a', 'a.aa', 'a.b', 'a.c'], '*.a'));
  82. * //=> ['a.a', 'a.aa']
  83. * ```
  84. * @param {Array} `list` Array of strings to match
  85. * @param {String} `pattern` Glob pattern to use for matching.
  86. * @param {Object} `options` See available [options](#options) for changing how matches are performed
  87. * @return {Array} Returns an array of matches
  88. * @api public
  89. */
  90. nanomatch.match = function(list, pattern, options) {
  91. if (Array.isArray(pattern)) {
  92. throw new TypeError('expected pattern to be a string');
  93. }
  94. var unixify = utils.unixify(options);
  95. var isMatch = memoize('match', pattern, options, nanomatch.matcher);
  96. var matches = [];
  97. list = utils.arrayify(list);
  98. var len = list.length;
  99. var idx = -1;
  100. while (++idx < len) {
  101. var ele = list[idx];
  102. if (ele === pattern || isMatch(ele)) {
  103. matches.push(utils.value(ele, unixify, options));
  104. }
  105. }
  106. // if no options were passed, uniquify results and return
  107. if (typeof options === 'undefined') {
  108. return utils.unique(matches);
  109. }
  110. if (matches.length === 0) {
  111. if (options.failglob === true) {
  112. throw new Error('no matches found for "' + pattern + '"');
  113. }
  114. if (options.nonull === true || options.nullglob === true) {
  115. return [options.unescape ? utils.unescape(pattern) : pattern];
  116. }
  117. }
  118. // if `opts.ignore` was defined, diff ignored list
  119. if (options.ignore) {
  120. matches = nanomatch.not(matches, options.ignore, options);
  121. }
  122. return options.nodupes !== false ? utils.unique(matches) : matches;
  123. };
  124. /**
  125. * Returns true if the specified `string` matches the given glob `pattern`.
  126. *
  127. * ```js
  128. * var nm = require('nanomatch');
  129. * nm.isMatch(string, pattern[, options]);
  130. *
  131. * console.log(nm.isMatch('a.a', '*.a'));
  132. * //=> true
  133. * console.log(nm.isMatch('a.b', '*.a'));
  134. * //=> false
  135. * ```
  136. * @param {String} `string` String to match
  137. * @param {String} `pattern` Glob pattern to use for matching.
  138. * @param {Object} `options` See available [options](#options) for changing how matches are performed
  139. * @return {Boolean} Returns true if the string matches the glob pattern.
  140. * @api public
  141. */
  142. nanomatch.isMatch = function(str, pattern, options) {
  143. if (typeof str !== 'string') {
  144. throw new TypeError('expected a string: "' + util.inspect(str) + '"');
  145. }
  146. if (utils.isEmptyString(str) || utils.isEmptyString(pattern)) {
  147. return false;
  148. }
  149. var equals = utils.equalsPattern(options);
  150. if (equals(str)) {
  151. return true;
  152. }
  153. var isMatch = memoize('isMatch', pattern, options, nanomatch.matcher);
  154. return isMatch(str);
  155. };
  156. /**
  157. * Returns true if some of the elements in the given `list` match any of the
  158. * given glob `patterns`.
  159. *
  160. * ```js
  161. * var nm = require('nanomatch');
  162. * nm.some(list, patterns[, options]);
  163. *
  164. * console.log(nm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js']));
  165. * // true
  166. * console.log(nm.some(['foo.js'], ['*.js', '!foo.js']));
  167. * // false
  168. * ```
  169. * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found.
  170. * @param {String|Array} `patterns` One or more glob patterns to use for matching.
  171. * @param {Object} `options` See available [options](#options) for changing how matches are performed
  172. * @return {Boolean} Returns true if any patterns match `str`
  173. * @api public
  174. */
  175. nanomatch.some = function(list, patterns, options) {
  176. if (typeof list === 'string') {
  177. list = [list];
  178. }
  179. for (var i = 0; i < list.length; i++) {
  180. if (nanomatch(list[i], patterns, options).length === 1) {
  181. return true;
  182. }
  183. }
  184. return false;
  185. };
  186. /**
  187. * Returns true if every element in the given `list` matches
  188. * at least one of the given glob `patterns`.
  189. *
  190. * ```js
  191. * var nm = require('nanomatch');
  192. * nm.every(list, patterns[, options]);
  193. *
  194. * console.log(nm.every('foo.js', ['foo.js']));
  195. * // true
  196. * console.log(nm.every(['foo.js', 'bar.js'], ['*.js']));
  197. * // true
  198. * console.log(nm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js']));
  199. * // false
  200. * console.log(nm.every(['foo.js'], ['*.js', '!foo.js']));
  201. * // false
  202. * ```
  203. * @param {String|Array} `list` The string or array of strings to test.
  204. * @param {String|Array} `patterns` One or more glob patterns to use for matching.
  205. * @param {Object} `options` See available [options](#options) for changing how matches are performed
  206. * @return {Boolean} Returns true if any patterns match `str`
  207. * @api public
  208. */
  209. nanomatch.every = function(list, patterns, options) {
  210. if (typeof list === 'string') {
  211. list = [list];
  212. }
  213. for (var i = 0; i < list.length; i++) {
  214. if (nanomatch(list[i], patterns, options).length !== 1) {
  215. return false;
  216. }
  217. }
  218. return true;
  219. };
  220. /**
  221. * Returns true if **any** of the given glob `patterns`
  222. * match the specified `string`.
  223. *
  224. * ```js
  225. * var nm = require('nanomatch');
  226. * nm.any(string, patterns[, options]);
  227. *
  228. * console.log(nm.any('a.a', ['b.*', '*.a']));
  229. * //=> true
  230. * console.log(nm.any('a.a', 'b.*'));
  231. * //=> false
  232. * ```
  233. * @param {String|Array} `str` The string to test.
  234. * @param {String|Array} `patterns` One or more glob patterns to use for matching.
  235. * @param {Object} `options` See available [options](#options) for changing how matches are performed
  236. * @return {Boolean} Returns true if any patterns match `str`
  237. * @api public
  238. */
  239. nanomatch.any = function(str, patterns, options) {
  240. if (typeof str !== 'string') {
  241. throw new TypeError('expected a string: "' + util.inspect(str) + '"');
  242. }
  243. if (utils.isEmptyString(str) || utils.isEmptyString(patterns)) {
  244. return false;
  245. }
  246. if (typeof patterns === 'string') {
  247. patterns = [patterns];
  248. }
  249. for (var i = 0; i < patterns.length; i++) {
  250. if (nanomatch.isMatch(str, patterns[i], options)) {
  251. return true;
  252. }
  253. }
  254. return false;
  255. };
  256. /**
  257. * Returns true if **all** of the given `patterns`
  258. * match the specified string.
  259. *
  260. * ```js
  261. * var nm = require('nanomatch');
  262. * nm.all(string, patterns[, options]);
  263. *
  264. * console.log(nm.all('foo.js', ['foo.js']));
  265. * // true
  266. *
  267. * console.log(nm.all('foo.js', ['*.js', '!foo.js']));
  268. * // false
  269. *
  270. * console.log(nm.all('foo.js', ['*.js', 'foo.js']));
  271. * // true
  272. *
  273. * console.log(nm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js']));
  274. * // true
  275. * ```
  276. * @param {String|Array} `str` The string to test.
  277. * @param {String|Array} `patterns` One or more glob patterns to use for matching.
  278. * @param {Object} `options` See available [options](#options) for changing how matches are performed
  279. * @return {Boolean} Returns true if any patterns match `str`
  280. * @api public
  281. */
  282. nanomatch.all = function(str, patterns, options) {
  283. if (typeof str !== 'string') {
  284. throw new TypeError('expected a string: "' + util.inspect(str) + '"');
  285. }
  286. if (typeof patterns === 'string') {
  287. patterns = [patterns];
  288. }
  289. for (var i = 0; i < patterns.length; i++) {
  290. if (!nanomatch.isMatch(str, patterns[i], options)) {
  291. return false;
  292. }
  293. }
  294. return true;
  295. };
  296. /**
  297. * Returns a list of strings that _**do not match any**_ of the given `patterns`.
  298. *
  299. * ```js
  300. * var nm = require('nanomatch');
  301. * nm.not(list, patterns[, options]);
  302. *
  303. * console.log(nm.not(['a.a', 'b.b', 'c.c'], '*.a'));
  304. * //=> ['b.b', 'c.c']
  305. * ```
  306. * @param {Array} `list` Array of strings to match.
  307. * @param {String|Array} `patterns` One or more glob pattern to use for matching.
  308. * @param {Object} `options` See available [options](#options) for changing how matches are performed
  309. * @return {Array} Returns an array of strings that **do not match** the given patterns.
  310. * @api public
  311. */
  312. nanomatch.not = function(list, patterns, options) {
  313. var opts = extend({}, options);
  314. var ignore = opts.ignore;
  315. delete opts.ignore;
  316. list = utils.arrayify(list);
  317. var matches = utils.diff(list, nanomatch(list, patterns, opts));
  318. if (ignore) {
  319. matches = utils.diff(matches, nanomatch(list, ignore));
  320. }
  321. return opts.nodupes !== false ? utils.unique(matches) : matches;
  322. };
  323. /**
  324. * Returns true if the given `string` contains the given pattern. Similar
  325. * to [.isMatch](#isMatch) but the pattern can match any part of the string.
  326. *
  327. * ```js
  328. * var nm = require('nanomatch');
  329. * nm.contains(string, pattern[, options]);
  330. *
  331. * console.log(nm.contains('aa/bb/cc', '*b'));
  332. * //=> true
  333. * console.log(nm.contains('aa/bb/cc', '*d'));
  334. * //=> false
  335. * ```
  336. * @param {String} `str` The string to match.
  337. * @param {String|Array} `patterns` Glob pattern to use for matching.
  338. * @param {Object} `options` See available [options](#options) for changing how matches are performed
  339. * @return {Boolean} Returns true if the patter matches any part of `str`.
  340. * @api public
  341. */
  342. nanomatch.contains = function(str, patterns, options) {
  343. if (typeof str !== 'string') {
  344. throw new TypeError('expected a string: "' + util.inspect(str) + '"');
  345. }
  346. if (typeof patterns === 'string') {
  347. if (utils.isEmptyString(str) || utils.isEmptyString(patterns)) {
  348. return false;
  349. }
  350. var equals = utils.equalsPattern(patterns, options);
  351. if (equals(str)) {
  352. return true;
  353. }
  354. var contains = utils.containsPattern(patterns, options);
  355. if (contains(str)) {
  356. return true;
  357. }
  358. }
  359. var opts = extend({}, options, {contains: true});
  360. return nanomatch.any(str, patterns, opts);
  361. };
  362. /**
  363. * Returns true if the given pattern and options should enable
  364. * the `matchBase` option.
  365. * @return {Boolean}
  366. * @api private
  367. */
  368. nanomatch.matchBase = function(pattern, options) {
  369. if (pattern && pattern.indexOf('/') !== -1 || !options) return false;
  370. return options.basename === true || options.matchBase === true;
  371. };
  372. /**
  373. * Filter the keys of the given object with the given `glob` pattern
  374. * and `options`. Does not attempt to match nested keys. If you need this feature,
  375. * use [glob-object][] instead.
  376. *
  377. * ```js
  378. * var nm = require('nanomatch');
  379. * nm.matchKeys(object, patterns[, options]);
  380. *
  381. * var obj = { aa: 'a', ab: 'b', ac: 'c' };
  382. * console.log(nm.matchKeys(obj, '*b'));
  383. * //=> { ab: 'b' }
  384. * ```
  385. * @param {Object} `object` The object with keys to filter.
  386. * @param {String|Array} `patterns` One or more glob patterns to use for matching.
  387. * @param {Object} `options` See available [options](#options) for changing how matches are performed
  388. * @return {Object} Returns an object with only keys that match the given patterns.
  389. * @api public
  390. */
  391. nanomatch.matchKeys = function(obj, patterns, options) {
  392. if (!utils.isObject(obj)) {
  393. throw new TypeError('expected the first argument to be an object');
  394. }
  395. var keys = nanomatch(Object.keys(obj), patterns, options);
  396. return utils.pick(obj, keys);
  397. };
  398. /**
  399. * Returns a memoized matcher function from the given glob `pattern` and `options`.
  400. * The returned function takes a string to match as its only argument and returns
  401. * true if the string is a match.
  402. *
  403. * ```js
  404. * var nm = require('nanomatch');
  405. * nm.matcher(pattern[, options]);
  406. *
  407. * var isMatch = nm.matcher('*.!(*a)');
  408. * console.log(isMatch('a.a'));
  409. * //=> false
  410. * console.log(isMatch('a.b'));
  411. * //=> true
  412. * ```
  413. * @param {String} `pattern` Glob pattern
  414. * @param {Object} `options` See available [options](#options) for changing how matches are performed.
  415. * @return {Function} Returns a matcher function.
  416. * @api public
  417. */
  418. nanomatch.matcher = function matcher(pattern, options) {
  419. if (utils.isEmptyString(pattern)) {
  420. return function() {
  421. return false;
  422. };
  423. }
  424. if (Array.isArray(pattern)) {
  425. return compose(pattern, options, matcher);
  426. }
  427. // if pattern is a regex
  428. if (pattern instanceof RegExp) {
  429. return test(pattern);
  430. }
  431. // if pattern is invalid
  432. if (!utils.isString(pattern)) {
  433. throw new TypeError('expected pattern to be an array, string or regex');
  434. }
  435. // if pattern is a non-glob string
  436. if (!utils.hasSpecialChars(pattern)) {
  437. if (options && options.nocase === true) {
  438. pattern = pattern.toLowerCase();
  439. }
  440. return utils.matchPath(pattern, options);
  441. }
  442. // if pattern is a glob string
  443. var re = nanomatch.makeRe(pattern, options);
  444. // if `options.matchBase` or `options.basename` is defined
  445. if (nanomatch.matchBase(pattern, options)) {
  446. return utils.matchBasename(re, options);
  447. }
  448. function test(regex) {
  449. var equals = utils.equalsPattern(options);
  450. var unixify = utils.unixify(options);
  451. return function(str) {
  452. if (equals(str)) {
  453. return true;
  454. }
  455. if (regex.test(unixify(str))) {
  456. return true;
  457. }
  458. return false;
  459. };
  460. }
  461. var fn = test(re);
  462. Object.defineProperty(fn, 'result', {
  463. configurable: true,
  464. enumerable: false,
  465. value: re.result
  466. });
  467. return fn;
  468. };
  469. /**
  470. * Returns an array of matches captured by `pattern` in `string, or
  471. * `null` if the pattern did not match.
  472. *
  473. * ```js
  474. * var nm = require('nanomatch');
  475. * nm.capture(pattern, string[, options]);
  476. *
  477. * console.log(nm.capture('test/*.js', 'test/foo.js'));
  478. * //=> ['foo']
  479. * console.log(nm.capture('test/*.js', 'foo/bar.css'));
  480. * //=> null
  481. * ```
  482. * @param {String} `pattern` Glob pattern to use for matching.
  483. * @param {String} `string` String to match
  484. * @param {Object} `options` See available [options](#options) for changing how matches are performed
  485. * @return {Boolean} Returns an array of captures if the string matches the glob pattern, otherwise `null`.
  486. * @api public
  487. */
  488. nanomatch.capture = function(pattern, str, options) {
  489. var re = nanomatch.makeRe(pattern, extend({capture: true}, options));
  490. var unixify = utils.unixify(options);
  491. function match() {
  492. return function(string) {
  493. var match = re.exec(unixify(string));
  494. if (!match) {
  495. return null;
  496. }
  497. return match.slice(1);
  498. };
  499. }
  500. var capture = memoize('capture', pattern, options, match);
  501. return capture(str);
  502. };
  503. /**
  504. * Create a regular expression from the given glob `pattern`.
  505. *
  506. * ```js
  507. * var nm = require('nanomatch');
  508. * nm.makeRe(pattern[, options]);
  509. *
  510. * console.log(nm.makeRe('*.js'));
  511. * //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/
  512. * ```
  513. * @param {String} `pattern` A glob pattern to convert to regex.
  514. * @param {Object} `options` See available [options](#options) for changing how matches are performed.
  515. * @return {RegExp} Returns a regex created from the given pattern.
  516. * @api public
  517. */
  518. nanomatch.makeRe = function(pattern, options) {
  519. if (pattern instanceof RegExp) {
  520. return pattern;
  521. }
  522. if (typeof pattern !== 'string') {
  523. throw new TypeError('expected pattern to be a string');
  524. }
  525. if (pattern.length > MAX_LENGTH) {
  526. throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters');
  527. }
  528. function makeRe() {
  529. var opts = utils.extend({wrap: false}, options);
  530. var res = nanomatch.create(pattern, opts);
  531. var regex = toRegex(res.output, opts);
  532. Object.defineProperty(regex, 'result', {
  533. configurable: true,
  534. enumerable: false,
  535. value: res
  536. });
  537. return regex;
  538. }
  539. return memoize('makeRe', pattern, options, makeRe);
  540. };
  541. /**
  542. * Parses the given glob `pattern` and returns an object with the compiled `output`
  543. * and optional source `map`.
  544. *
  545. * ```js
  546. * var nm = require('nanomatch');
  547. * nm.create(pattern[, options]);
  548. *
  549. * console.log(nm.create('abc/*.js'));
  550. * // { options: { source: 'string', sourcemap: true },
  551. * // state: {},
  552. * // compilers:
  553. * // { ... },
  554. * // output: '(\\.[\\\\\\/])?abc\\/(?!\\.)(?=.)[^\\/]*?\\.js',
  555. * // ast:
  556. * // { type: 'root',
  557. * // errors: [],
  558. * // nodes:
  559. * // [ ... ],
  560. * // dot: false,
  561. * // input: 'abc/*.js' },
  562. * // parsingErrors: [],
  563. * // map:
  564. * // { version: 3,
  565. * // sources: [ 'string' ],
  566. * // names: [],
  567. * // mappings: 'AAAA,GAAG,EAAC,kBAAC,EAAC,EAAE',
  568. * // sourcesContent: [ 'abc/*.js' ] },
  569. * // position: { line: 1, column: 28 },
  570. * // content: {},
  571. * // files: {},
  572. * // idx: 6 }
  573. * ```
  574. * @param {String} `pattern` Glob pattern to parse and compile.
  575. * @param {Object} `options` Any [options](#options) to change how parsing and compiling is performed.
  576. * @return {Object} Returns an object with the parsed AST, compiled string and optional source map.
  577. * @api public
  578. */
  579. nanomatch.create = function(pattern, options) {
  580. if (typeof pattern !== 'string') {
  581. throw new TypeError('expected a string');
  582. }
  583. function create() {
  584. return nanomatch.compile(nanomatch.parse(pattern, options), options);
  585. }
  586. return memoize('create', pattern, options, create);
  587. };
  588. /**
  589. * Parse the given `str` with the given `options`.
  590. *
  591. * ```js
  592. * var nm = require('nanomatch');
  593. * nm.parse(pattern[, options]);
  594. *
  595. * var ast = nm.parse('a/{b,c}/d');
  596. * console.log(ast);
  597. * // { type: 'root',
  598. * // errors: [],
  599. * // input: 'a/{b,c}/d',
  600. * // nodes:
  601. * // [ { type: 'bos', val: '' },
  602. * // { type: 'text', val: 'a/' },
  603. * // { type: 'brace',
  604. * // nodes:
  605. * // [ { type: 'brace.open', val: '{' },
  606. * // { type: 'text', val: 'b,c' },
  607. * // { type: 'brace.close', val: '}' } ] },
  608. * // { type: 'text', val: '/d' },
  609. * // { type: 'eos', val: '' } ] }
  610. * ```
  611. * @param {String} `str`
  612. * @param {Object} `options`
  613. * @return {Object} Returns an AST
  614. * @api public
  615. */
  616. nanomatch.parse = function(pattern, options) {
  617. if (typeof pattern !== 'string') {
  618. throw new TypeError('expected a string');
  619. }
  620. function parse() {
  621. var snapdragon = utils.instantiate(null, options);
  622. parsers(snapdragon, options);
  623. var ast = snapdragon.parse(pattern, options);
  624. utils.define(ast, 'snapdragon', snapdragon);
  625. ast.input = pattern;
  626. return ast;
  627. }
  628. return memoize('parse', pattern, options, parse);
  629. };
  630. /**
  631. * Compile the given `ast` or string with the given `options`.
  632. *
  633. * ```js
  634. * var nm = require('nanomatch');
  635. * nm.compile(ast[, options]);
  636. *
  637. * var ast = nm.parse('a/{b,c}/d');
  638. * console.log(nm.compile(ast));
  639. * // { options: { source: 'string' },
  640. * // state: {},
  641. * // compilers:
  642. * // { eos: [Function],
  643. * // noop: [Function],
  644. * // bos: [Function],
  645. * // brace: [Function],
  646. * // 'brace.open': [Function],
  647. * // text: [Function],
  648. * // 'brace.close': [Function] },
  649. * // output: [ 'a/(b|c)/d' ],
  650. * // ast:
  651. * // { ... },
  652. * // parsingErrors: [] }
  653. * ```
  654. * @param {Object|String} `ast`
  655. * @param {Object} `options`
  656. * @return {Object} Returns an object that has an `output` property with the compiled string.
  657. * @api public
  658. */
  659. nanomatch.compile = function(ast, options) {
  660. if (typeof ast === 'string') {
  661. ast = nanomatch.parse(ast, options);
  662. }
  663. function compile() {
  664. var snapdragon = utils.instantiate(ast, options);
  665. compilers(snapdragon, options);
  666. return snapdragon.compile(ast, options);
  667. }
  668. return memoize('compile', ast.input, options, compile);
  669. };
  670. /**
  671. * Clear the regex cache.
  672. *
  673. * ```js
  674. * nm.clearCache();
  675. * ```
  676. * @api public
  677. */
  678. nanomatch.clearCache = function() {
  679. nanomatch.cache.__data__ = {};
  680. };
  681. /**
  682. * Compose a matcher function with the given patterns.
  683. * This allows matcher functions to be compiled once and
  684. * called multiple times.
  685. */
  686. function compose(patterns, options, matcher) {
  687. var matchers;
  688. return memoize('compose', String(patterns), options, function() {
  689. return function(file) {
  690. // delay composition until it's invoked the first time,
  691. // after that it won't be called again
  692. if (!matchers) {
  693. matchers = [];
  694. for (var i = 0; i < patterns.length; i++) {
  695. matchers.push(matcher(patterns[i], options));
  696. }
  697. }
  698. var len = matchers.length;
  699. while (len--) {
  700. if (matchers[len](file) === true) {
  701. return true;
  702. }
  703. }
  704. return false;
  705. };
  706. });
  707. }
  708. /**
  709. * Memoize a generated regex or function. A unique key is generated
  710. * from the `type` (usually method name), the `pattern`, and
  711. * user-defined options.
  712. */
  713. function memoize(type, pattern, options, fn) {
  714. var key = utils.createKey(type + '=' + pattern, options);
  715. if (options && options.cache === false) {
  716. return fn(pattern, options);
  717. }
  718. if (cache.has(type, key)) {
  719. return cache.get(type, key);
  720. }
  721. var val = fn(pattern, options);
  722. cache.set(type, key, val);
  723. return val;
  724. }
  725. /**
  726. * Expose compiler, parser and cache on `nanomatch`
  727. */
  728. nanomatch.compilers = compilers;
  729. nanomatch.parsers = parsers;
  730. nanomatch.cache = cache;
  731. /**
  732. * Expose `nanomatch`
  733. * @type {Function}
  734. */
  735. module.exports = nanomatch;