splitAtTopLevelOnly.js 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. /**
  2. * This splits a string on a top-level character.
  3. *
  4. * Regex doesn't support recursion (at least not the JS-flavored version).
  5. * So we have to use a tiny state machine to keep track of paren placement.
  6. *
  7. * Expected behavior using commas:
  8. * var(--a, 0 0 1px rgb(0, 0, 0)), 0 0 1px rgb(0, 0, 0)
  9. * ─┬─ ┬ ┬ ┬
  10. * x x x ╰──────── Split because top-level
  11. * ╰──────────────┴──┴───────────── Ignored b/c inside >= 1 levels of parens
  12. *
  13. * @param {string} input
  14. * @param {string} separator
  15. */ "use strict";
  16. Object.defineProperty(exports, "__esModule", {
  17. value: true
  18. });
  19. Object.defineProperty(exports, "splitAtTopLevelOnly", {
  20. enumerable: true,
  21. get: function() {
  22. return splitAtTopLevelOnly;
  23. }
  24. });
  25. function splitAtTopLevelOnly(input, separator) {
  26. let stack = [];
  27. let parts = [];
  28. let lastPos = 0;
  29. let isEscaped = false;
  30. for(let idx = 0; idx < input.length; idx++){
  31. let char = input[idx];
  32. if (stack.length === 0 && char === separator[0] && !isEscaped) {
  33. if (separator.length === 1 || input.slice(idx, idx + separator.length) === separator) {
  34. parts.push(input.slice(lastPos, idx));
  35. lastPos = idx + separator.length;
  36. }
  37. }
  38. isEscaped = isEscaped ? false : char === "\\";
  39. if (char === "(" || char === "[" || char === "{") {
  40. stack.push(char);
  41. } else if (char === ")" && stack[stack.length - 1] === "(" || char === "]" && stack[stack.length - 1] === "[" || char === "}" && stack[stack.length - 1] === "{") {
  42. stack.pop();
  43. }
  44. }
  45. parts.push(input.slice(lastPos));
  46. return parts;
  47. }