compiler2.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. 'use strict';
  2. /**
  3. * Nanomatch compilers
  4. */
  5. module.exports = function(nanomatch, options) {
  6. var star = '[^/]*?';
  7. var compiler = nanomatch.compiler;
  8. var ast = nanomatch.ast = nanomatch.parser.ast;
  9. ast.state = nanomatch.parser.state;
  10. compiler.state = ast.state;
  11. /**
  12. * Negation / escaping
  13. */
  14. compiler.set('not', function(node) {
  15. var prev = this.prev();
  16. if (this.options.nonegate === true || prev.type !== 'bos') {
  17. return this.emit('\\' + node.val, node);
  18. }
  19. return this.emit(node.val, node);
  20. });
  21. compiler.set('escape', function(node) {
  22. if (this.options.unescape && /^[\w_.-]/.test(node.val)) {
  23. return this.emit(node.val, node);
  24. }
  25. return this.emit('\\' + node.val, node);
  26. });
  27. compiler.set('quoted', function(node) {
  28. return this.emit(node.val, node);
  29. });
  30. /**
  31. * Regex
  32. */
  33. compiler.set('dollar', function(node) {
  34. if (node.parent.type === 'bracket') {
  35. return this.emit(node.val, node);
  36. }
  37. return this.emit('\\' + node.val, node);
  38. });
  39. /**
  40. * Dot: "."
  41. */
  42. compiler.set('dot', function(node) {
  43. if (node.dotfiles === true) this.dotfiles = true;
  44. return this.emit('\\' + node.val, node);
  45. });
  46. /**
  47. * Slashes: "/" and "\"
  48. */
  49. compiler.set('backslash', function(node) {
  50. return this.emit(node.val, node);
  51. });
  52. compiler.set('slash', function(node, nodes, i) {
  53. var val = '[\\/\\\\]';
  54. var parent = node.parent;
  55. var prev = this.prev();
  56. // set "node.hasSlash" to true on all ancestor parens nodes
  57. while (parent.type === 'paren' && !parent.hasSlash) {
  58. parent.hasSlash = true;
  59. parent = parent.parent;
  60. }
  61. if (prev.addQmark) {
  62. val += '?';
  63. }
  64. // word boundary
  65. if (node.rest.slice(0, 2) === '\\b') {
  66. return this.emit(val, node);
  67. }
  68. // globstars
  69. if (node.parsed === '**' || node.parsed === './**') {
  70. this.output = '(?:' + this.output;
  71. return this.emit(val + ')?', node);
  72. }
  73. // negation
  74. if (node.parsed === '!**' && this.options.nonegate !== true) {
  75. return this.emit(val + '?\\b', node);
  76. }
  77. return this.emit(val, node);
  78. });
  79. /**
  80. * Square brackets
  81. */
  82. compiler.set('bracket', function(node) {
  83. var close = node.close;
  84. var open = !node.escaped ? '[' : '\\[';
  85. var negated = node.negated;
  86. var inner = node.inner;
  87. var val = node.val;
  88. if (node.escaped === true) {
  89. inner = inner.replace(/\\?(\W)/g, '\\$1');
  90. negated = '';
  91. }
  92. if (inner === ']-') {
  93. inner = '\\]\\-';
  94. }
  95. if (negated && inner.indexOf('.') === -1) {
  96. inner += '.';
  97. }
  98. if (negated && inner.indexOf('/') === -1) {
  99. inner += '/';
  100. }
  101. val = open + negated + inner + close;
  102. return this.emit(val, node);
  103. });
  104. /**
  105. * Square: "[.]" (only matches a single character in brackets)
  106. */
  107. compiler.set('square', function(node) {
  108. var val = !/^\w/.test(node.val) ? '\\' + node.val : node.val;
  109. return this.emit(val, node);
  110. });
  111. /**
  112. * Question mark: "?"
  113. */
  114. compiler.set('qmark', function(node) {
  115. var prev = this.prev();
  116. var val = '[^.\\\\/]';
  117. if (this.options.dot || (prev.type !== 'bos' && prev.type !== 'slash')) {
  118. val = '[^\\\\/]';
  119. }
  120. if (node.parsed.slice(-1) === '(') {
  121. var ch = node.rest.charAt(0);
  122. if (ch === '!' || ch === '=' || ch === ':') {
  123. return this.emit(node.val, node);
  124. }
  125. }
  126. if (node.val.length > 1) {
  127. val += '{' + node.val.length + '}';
  128. }
  129. return this.emit(val, node);
  130. });
  131. /**
  132. * Plus
  133. */
  134. compiler.set('plus', function(node) {
  135. var prev = node.parsed.slice(-1);
  136. if (prev === ']' || prev === ')') {
  137. return this.emit(node.val, node);
  138. }
  139. if (!this.output || (/[?*+]/.test(ch) && node.parent.type !== 'bracket')) {
  140. return this.emit('\\+', node);
  141. }
  142. var ch = this.output.slice(-1);
  143. if (/\w/.test(ch) && !node.inside) {
  144. return this.emit('+\\+?', node);
  145. }
  146. return this.emit('+', node);
  147. });
  148. /**
  149. * globstar: '**'
  150. */
  151. compiler.set('globstar', function(node, nodes, i) {
  152. if (!this.output) {
  153. this.state.leadingGlobstar = true;
  154. }
  155. var next = this.next();
  156. var prev = this.prev();
  157. var next2 = this.next(2);
  158. var prev2 = this.prev(2);
  159. var type = prev.type;
  160. var val = node.val;
  161. if (prev.type === 'slash' && next.type === 'slash') {
  162. if (prev2.type === 'text') {
  163. this.output += '?';
  164. if (next2.type !== 'text') {
  165. this.output += '\\b';
  166. }
  167. }
  168. }
  169. var parsed = node.parsed;
  170. if (parsed.charAt(0) === '!') {
  171. parsed = parsed.slice(1);
  172. }
  173. var isInside = node.isInside.paren || node.isInside.brace;
  174. if (parsed && type !== 'slash' && type !== 'bos' && !isInside) {
  175. val = star;
  176. } else {
  177. val = this.options.dot !== true
  178. ? '(?:(?!(?:[\\/\\\\]|^)\\.).)*?'
  179. : '(?:(?!(?:[\\/\\\\]|^)(?:\\.{1,2})($|[\\/\\\\]))(?!\\.{2}).)*?';
  180. }
  181. if ((type === 'slash' || type === 'bos') && this.options.dot !== true) {
  182. val = '(?!\\.)' + val;
  183. }
  184. if (prev.type === 'slash' && next.type === 'slash' && prev2.type !== 'text') {
  185. if (next2.type === 'text' || next2.type === 'star') {
  186. node.addQmark = true;
  187. }
  188. }
  189. if (this.options.capture) {
  190. val = '(' + val + ')';
  191. }
  192. return this.emit(val, node);
  193. });
  194. /**
  195. * Star: "*"
  196. */
  197. compiler.set('star', function(node, nodes, i) {
  198. var prior = nodes[i - 2] || {};
  199. var prev = this.prev();
  200. var next = this.next();
  201. var type = prev.type;
  202. function isStart(n) {
  203. return n.type === 'bos' || n.type === 'slash';
  204. }
  205. if (this.output === '' && this.options.contains !== true) {
  206. this.output = '(?!\\/)';
  207. }
  208. if (type === 'bracket' && this.options.bash === false) {
  209. var str = next && next.type === 'bracket' ? star : '*?';
  210. if (!prev.nodes || prev.nodes[1].type !== 'posix') {
  211. return this.emit(str, node);
  212. }
  213. }
  214. var prefix = !this.dotfiles && type !== 'text' && type !== 'escape'
  215. ? (this.options.dot ? '(?!(?:^|[\\/\\\\])\\.{1,2}(?:$|[\\/\\\\]))' : '(?!\\.)')
  216. : '';
  217. if (isStart(prev) || (isStart(prior) && type === 'not')) {
  218. if (prefix !== '(?!\\.)') {
  219. prefix += '(?!(\\.{2}|\\.\\/))(?=.)';
  220. } else {
  221. prefix += '(?=.)';
  222. }
  223. } else if (prefix === '(?!\\.)') {
  224. prefix = '';
  225. }
  226. if (prev.type === 'not' && prior.type === 'bos' && this.options.dot === true) {
  227. this.output = '(?!\\.)' + this.output;
  228. }
  229. var output = prefix + star;
  230. if (this.options.capture) {
  231. output = '(' + output + ')';
  232. }
  233. return this.emit(output, node);
  234. });
  235. /**
  236. * Text
  237. */
  238. compiler.set('text', function(node) {
  239. return this.emit(node.val, node);
  240. });
  241. /**
  242. * End-of-string
  243. */
  244. compiler.set('eos', function(node) {
  245. var prev = this.prev();
  246. var val = node.val;
  247. this.output = '(?:(?:\\.[\\/\\\\])(?=.))?' + this.output;
  248. if (this.state.metachar && prev.type !== 'qmark' && prev.type !== 'slash') {
  249. val += (this.options.contains ? '[\\/\\\\]?' : '(?:[\\/\\\\]|$)');
  250. }
  251. return this.emit(val, node);
  252. });
  253. /**
  254. * Allow custom compilers to be passed on options
  255. */
  256. if (options && typeof options.compilers === 'function') {
  257. options.compilers(nanomatch.compiler);
  258. }
  259. };