compilers.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. 'use strict';
  2. var brackets = require('expand-brackets');
  3. /**
  4. * Extglob compilers
  5. */
  6. module.exports = function(extglob) {
  7. function star() {
  8. if (typeof extglob.options.star === 'function') {
  9. return extglob.options.star.apply(this, arguments);
  10. }
  11. if (typeof extglob.options.star === 'string') {
  12. return extglob.options.star;
  13. }
  14. return '.*?';
  15. }
  16. /**
  17. * Use `expand-brackets` compilers
  18. */
  19. extglob.use(brackets.compilers);
  20. extglob.compiler
  21. /**
  22. * Escaped: "\\*"
  23. */
  24. .set('escape', function(node) {
  25. return this.emit(node.val, node);
  26. })
  27. /**
  28. * Dot: "."
  29. */
  30. .set('dot', function(node) {
  31. return this.emit('\\' + node.val, node);
  32. })
  33. /**
  34. * Question mark: "?"
  35. */
  36. .set('qmark', function(node) {
  37. var val = '[^\\\\/.]';
  38. var prev = this.prev();
  39. if (node.parsed.slice(-1) === '(') {
  40. var ch = node.rest.charAt(0);
  41. if (ch !== '!' && ch !== '=' && ch !== ':') {
  42. return this.emit(val, node);
  43. }
  44. return this.emit(node.val, node);
  45. }
  46. if (prev.type === 'text' && prev.val) {
  47. return this.emit(val, node);
  48. }
  49. if (node.val.length > 1) {
  50. val += '{' + node.val.length + '}';
  51. }
  52. return this.emit(val, node);
  53. })
  54. /**
  55. * Plus: "+"
  56. */
  57. .set('plus', function(node) {
  58. var prev = node.parsed.slice(-1);
  59. if (prev === ']' || prev === ')') {
  60. return this.emit(node.val, node);
  61. }
  62. var ch = this.output.slice(-1);
  63. if (!this.output || (/[?*+]/.test(ch) && node.parent.type !== 'bracket')) {
  64. return this.emit('\\+', node);
  65. }
  66. if (/\w/.test(ch) && !node.inside) {
  67. return this.emit('+\\+?', node);
  68. }
  69. return this.emit('+', node);
  70. })
  71. /**
  72. * Star: "*"
  73. */
  74. .set('star', function(node) {
  75. var prev = this.prev();
  76. var prefix = prev.type !== 'text' && prev.type !== 'escape'
  77. ? '(?!\\.)'
  78. : '';
  79. return this.emit(prefix + star.call(this, node), node);
  80. })
  81. /**
  82. * Parens
  83. */
  84. .set('paren', function(node) {
  85. return this.mapVisit(node.nodes);
  86. })
  87. .set('paren.open', function(node) {
  88. var capture = this.options.capture ? '(' : '';
  89. switch (node.parent.prefix) {
  90. case '!':
  91. case '^':
  92. return this.emit(capture + '(?:(?!(?:', node);
  93. case '*':
  94. case '+':
  95. case '?':
  96. case '@':
  97. return this.emit(capture + '(?:', node);
  98. default: {
  99. var val = node.val;
  100. if (this.options.bash === true) {
  101. val = '\\' + val;
  102. } else if (!this.options.capture && val === '(' && node.parent.rest[0] !== '?') {
  103. val += '?:';
  104. }
  105. return this.emit(val, node);
  106. }
  107. }
  108. })
  109. .set('paren.close', function(node) {
  110. var capture = this.options.capture ? ')' : '';
  111. switch (node.prefix) {
  112. case '!':
  113. case '^':
  114. var prefix = /^(\)|$)/.test(node.rest) ? '$' : '';
  115. var str = star.call(this, node);
  116. // if the extglob has a slash explicitly defined, we know the user wants
  117. // to match slashes, so we need to ensure the "star" regex allows for it
  118. if (node.parent.hasSlash && !this.options.star && this.options.slash !== false) {
  119. str = '.*?';
  120. }
  121. return this.emit(prefix + ('))' + str + ')') + capture, node);
  122. case '*':
  123. case '+':
  124. case '?':
  125. return this.emit(')' + node.prefix + capture, node);
  126. case '@':
  127. return this.emit(')' + capture, node);
  128. default: {
  129. var val = (this.options.bash === true ? '\\' : '') + ')';
  130. return this.emit(val, node);
  131. }
  132. }
  133. })
  134. /**
  135. * Text
  136. */
  137. .set('text', function(node) {
  138. var val = node.val.replace(/[\[\]]/g, '\\$&');
  139. return this.emit(val, node);
  140. });
  141. };