join-sql-fragments.js 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. 'use strict';
  2. function doesNotWantLeadingSpace(str) {
  3. return /^[;,)]/.test(str);
  4. }
  5. function doesNotWantTrailingSpace(str) {
  6. return /\($/.test(str);
  7. }
  8. /**
  9. * Joins an array of strings with a single space between them,
  10. * except for:
  11. *
  12. * - Strings starting with ';', ',' and ')', which do not get a leading space.
  13. * - Strings ending with '(', which do not get a trailing space.
  14. *
  15. * @param {string[]} parts
  16. * @returns {string}
  17. * @private
  18. */
  19. function singleSpaceJoinHelper(parts) {
  20. return parts.reduce(({ skipNextLeadingSpace, result }, part) => {
  21. if (skipNextLeadingSpace || doesNotWantLeadingSpace(part)) {
  22. result += part.trim();
  23. } else {
  24. result += ` ${part.trim()}`;
  25. }
  26. return {
  27. skipNextLeadingSpace: doesNotWantTrailingSpace(part),
  28. result
  29. };
  30. }, {
  31. skipNextLeadingSpace: true,
  32. result: ''
  33. }).result;
  34. }
  35. /**
  36. * Joins an array with a single space, auto trimming when needed.
  37. *
  38. * Certain elements do not get leading/trailing spaces.
  39. *
  40. * @param {any[]} array The array to be joined. Falsy values are skipped. If an
  41. * element is another array, this function will be called recursively on that array.
  42. * Otherwise, if a non-string, non-falsy value is present, a TypeError will be thrown.
  43. *
  44. * @returns {string} The joined string.
  45. *
  46. * @private
  47. */
  48. function joinSQLFragments(array) {
  49. if (array.length === 0) return '';
  50. // Skip falsy fragments
  51. array = array.filter(x => x);
  52. // Resolve recursive calls
  53. array = array.map(fragment => {
  54. if (Array.isArray(fragment)) {
  55. return joinSQLFragments(fragment);
  56. }
  57. return fragment;
  58. });
  59. // Ensure strings
  60. for (const fragment of array) {
  61. if (fragment && typeof fragment !== 'string') {
  62. const error = new TypeError(`Tried to construct a SQL string with a non-string, non-falsy fragment (${fragment}).`);
  63. error.args = array;
  64. error.fragment = fragment;
  65. throw error;
  66. }
  67. }
  68. // Trim fragments
  69. array = array.map(x => x.trim());
  70. // Skip full-whitespace fragments (empty after the above trim)
  71. array = array.filter(x => x !== '');
  72. return singleSpaceJoinHelper(array);
  73. }
  74. exports.joinSQLFragments = joinSQLFragments;