2ed6cad7edf795430f298fb46382f57b9e24925f58b55a2eee31edad4eb56c98c4b062e6638cd88dd85c397f8a5dff77fc3c301b4a50220067158244e58825 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.expand = expand;
  4. const balanced_match_1 = require("@isaacs/balanced-match");
  5. const escSlash = '\0SLASH' + Math.random() + '\0';
  6. const escOpen = '\0OPEN' + Math.random() + '\0';
  7. const escClose = '\0CLOSE' + Math.random() + '\0';
  8. const escComma = '\0COMMA' + Math.random() + '\0';
  9. const escPeriod = '\0PERIOD' + Math.random() + '\0';
  10. const escSlashPattern = new RegExp(escSlash, 'g');
  11. const escOpenPattern = new RegExp(escOpen, 'g');
  12. const escClosePattern = new RegExp(escClose, 'g');
  13. const escCommaPattern = new RegExp(escComma, 'g');
  14. const escPeriodPattern = new RegExp(escPeriod, 'g');
  15. const slashPattern = /\\\\/g;
  16. const openPattern = /\\{/g;
  17. const closePattern = /\\}/g;
  18. const commaPattern = /\\,/g;
  19. const periodPattern = /\\./g;
  20. function numeric(str) {
  21. return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
  22. }
  23. function escapeBraces(str) {
  24. return str
  25. .replace(slashPattern, escSlash)
  26. .replace(openPattern, escOpen)
  27. .replace(closePattern, escClose)
  28. .replace(commaPattern, escComma)
  29. .replace(periodPattern, escPeriod);
  30. }
  31. function unescapeBraces(str) {
  32. return str
  33. .replace(escSlashPattern, '\\')
  34. .replace(escOpenPattern, '{')
  35. .replace(escClosePattern, '}')
  36. .replace(escCommaPattern, ',')
  37. .replace(escPeriodPattern, '.');
  38. }
  39. /**
  40. * Basically just str.split(","), but handling cases
  41. * where we have nested braced sections, which should be
  42. * treated as individual members, like {a,{b,c},d}
  43. */
  44. function parseCommaParts(str) {
  45. if (!str) {
  46. return [''];
  47. }
  48. const parts = [];
  49. const m = (0, balanced_match_1.balanced)('{', '}', str);
  50. if (!m) {
  51. return str.split(',');
  52. }
  53. const { pre, body, post } = m;
  54. const p = pre.split(',');
  55. p[p.length - 1] += '{' + body + '}';
  56. const postParts = parseCommaParts(post);
  57. if (post.length) {
  58. ;
  59. p[p.length - 1] += postParts.shift();
  60. p.push.apply(p, postParts);
  61. }
  62. parts.push.apply(parts, p);
  63. return parts;
  64. }
  65. function expand(str) {
  66. if (!str) {
  67. return [];
  68. }
  69. // I don't know why Bash 4.3 does this, but it does.
  70. // Anything starting with {} will have the first two bytes preserved
  71. // but *only* at the top level, so {},a}b will not expand to anything,
  72. // but a{},b}c will be expanded to [a}c,abc].
  73. // One could argue that this is a bug in Bash, but since the goal of
  74. // this module is to match Bash's rules, we escape a leading {}
  75. if (str.slice(0, 2) === '{}') {
  76. str = '\\{\\}' + str.slice(2);
  77. }
  78. return expand_(escapeBraces(str), true).map(unescapeBraces);
  79. }
  80. function embrace(str) {
  81. return '{' + str + '}';
  82. }
  83. function isPadded(el) {
  84. return /^-?0\d/.test(el);
  85. }
  86. function lte(i, y) {
  87. return i <= y;
  88. }
  89. function gte(i, y) {
  90. return i >= y;
  91. }
  92. function expand_(str, isTop) {
  93. /** @type {string[]} */
  94. const expansions = [];
  95. const m = (0, balanced_match_1.balanced)('{', '}', str);
  96. if (!m)
  97. return [str];
  98. // no need to expand pre, since it is guaranteed to be free of brace-sets
  99. const pre = m.pre;
  100. const post = m.post.length ? expand_(m.post, false) : [''];
  101. if (/\$$/.test(m.pre)) {
  102. for (let k = 0; k < post.length; k++) {
  103. const expansion = pre + '{' + m.body + '}' + post[k];
  104. expansions.push(expansion);
  105. }
  106. }
  107. else {
  108. const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
  109. const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
  110. const isSequence = isNumericSequence || isAlphaSequence;
  111. const isOptions = m.body.indexOf(',') >= 0;
  112. if (!isSequence && !isOptions) {
  113. // {a},b}
  114. if (m.post.match(/,(?!,).*\}/)) {
  115. str = m.pre + '{' + m.body + escClose + m.post;
  116. return expand_(str);
  117. }
  118. return [str];
  119. }
  120. let n;
  121. if (isSequence) {
  122. n = m.body.split(/\.\./);
  123. }
  124. else {
  125. n = parseCommaParts(m.body);
  126. if (n.length === 1 && n[0] !== undefined) {
  127. // x{{a,b}}y ==> x{a}y x{b}y
  128. n = expand_(n[0], false).map(embrace);
  129. //XXX is this necessary? Can't seem to hit it in tests.
  130. /* c8 ignore start */
  131. if (n.length === 1) {
  132. return post.map(p => m.pre + n[0] + p);
  133. }
  134. /* c8 ignore stop */
  135. }
  136. }
  137. // at this point, n is the parts, and we know it's not a comma set
  138. // with a single entry.
  139. let N;
  140. if (isSequence && n[0] !== undefined && n[1] !== undefined) {
  141. const x = numeric(n[0]);
  142. const y = numeric(n[1]);
  143. const width = Math.max(n[0].length, n[1].length);
  144. let incr = n.length === 3 && n[2] !== undefined ? Math.abs(numeric(n[2])) : 1;
  145. let test = lte;
  146. const reverse = y < x;
  147. if (reverse) {
  148. incr *= -1;
  149. test = gte;
  150. }
  151. const pad = n.some(isPadded);
  152. N = [];
  153. for (let i = x; test(i, y); i += incr) {
  154. let c;
  155. if (isAlphaSequence) {
  156. c = String.fromCharCode(i);
  157. if (c === '\\') {
  158. c = '';
  159. }
  160. }
  161. else {
  162. c = String(i);
  163. if (pad) {
  164. const need = width - c.length;
  165. if (need > 0) {
  166. const z = new Array(need + 1).join('0');
  167. if (i < 0) {
  168. c = '-' + z + c.slice(1);
  169. }
  170. else {
  171. c = z + c;
  172. }
  173. }
  174. }
  175. }
  176. N.push(c);
  177. }
  178. }
  179. else {
  180. N = [];
  181. for (let j = 0; j < n.length; j++) {
  182. N.push.apply(N, expand_(n[j], false));
  183. }
  184. }
  185. for (let j = 0; j < N.length; j++) {
  186. for (let k = 0; k < post.length; k++) {
  187. const expansion = pre + N[j] + post[k];
  188. if (!isTop || isSequence || expansion) {
  189. expansions.push(expansion);
  190. }
  191. }
  192. }
  193. }
  194. return expansions;
  195. }
  196. //# sourceMappingURL=index.js.map