mustache-replacer.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import objectPath from 'object-path';
  2. import dayjs from '@/lib/dayjs';
  3. const refKeys = {
  4. table: 'table',
  5. dataColumn: 'table',
  6. dataColumns: 'table',
  7. };
  8. /* eslint-disable prefer-destructuring */
  9. export const functions = {
  10. date(...args) {
  11. let date = new Date();
  12. let dateFormat = 'DD-MM-YYYY';
  13. if (args.length === 1) {
  14. dateFormat = args[0];
  15. } else if (args.length >= 2) {
  16. date = new Date(args[0]);
  17. dateFormat = args[1];
  18. }
  19. /* eslint-disable-next-line */
  20. const isValidDate = date instanceof Date && !isNaN(date);
  21. const dayjsDate = dayjs(isValidDate ? date : Date.now());
  22. const result =
  23. dateFormat === 'relative'
  24. ? dayjsDate.fromNow()
  25. : dayjsDate.format(dateFormat);
  26. return result;
  27. },
  28. randint(min = 0, max = 100) {
  29. return Math.round(Math.random() * (+max - +min) + +min);
  30. },
  31. getLength(str) {
  32. return str.length ?? str;
  33. },
  34. };
  35. export function extractStrFunction(str) {
  36. const extractedStr = /^\$\s*(\w+)\s*\((.*)\)/.exec(
  37. str.trim().replace(/\r?\n|\r/g, '')
  38. );
  39. if (!extractedStr) return null;
  40. const { 1: name, 2: funcParams } = extractedStr;
  41. const params = funcParams
  42. .split(/,(?=(?:[^"]*"[^"]*")*[^"]*$)/)
  43. .map((param) => param?.trim().replace(/^['"]|['"]$/g, '') || '');
  44. return {
  45. name,
  46. params,
  47. };
  48. }
  49. export function keyParser(key, data) {
  50. let [dataKey, path] = key.split(/@(.+)/);
  51. dataKey = refKeys[dataKey] ?? dataKey;
  52. if (!path) return { dataKey, path: '' };
  53. if (dataKey !== 'table') {
  54. if (dataKey === 'loopData' && !path.endsWith('.$index')) {
  55. const pathArr = path.split('.');
  56. pathArr.splice(1, 0, 'data');
  57. path = pathArr.join('.');
  58. }
  59. return { dataKey, path };
  60. }
  61. const [firstPath, restPath] = path.split(/\.(.+)/);
  62. if (firstPath === '$last') {
  63. const lastIndex = data.table.length - 1;
  64. path = `${lastIndex}.${restPath || ''}`;
  65. } else if (!restPath) {
  66. path = `0.${firstPath}`;
  67. } else if (typeof +firstPath !== 'number' || Number.isNaN(+firstPath)) {
  68. path = `0.${firstPath}.${restPath}`;
  69. }
  70. path = path.replace(/\.$/, '');
  71. return { dataKey: 'table', path };
  72. }
  73. function replacer(str, { regex, tagLen, modifyPath, data }) {
  74. return str.replace(regex, (match) => {
  75. let key = match.slice(tagLen, -tagLen).trim();
  76. if (!key) return '';
  77. if (modifyPath && typeof modifyPath === 'function') {
  78. key = modifyPath(key);
  79. }
  80. let result = '';
  81. const funcRef = extractStrFunction(key);
  82. if (funcRef && data.functions[funcRef.name]) {
  83. result = data.functions[funcRef.name]?.apply(
  84. { refData: data },
  85. funcRef.params
  86. );
  87. } else {
  88. const { dataKey, path } = keyParser(key, data);
  89. result = objectPath.get(data[dataKey], path) ?? match;
  90. }
  91. return typeof result === 'string' ? result : JSON.stringify(result);
  92. });
  93. }
  94. export default function (str, refData) {
  95. if (!str || typeof str !== 'string') return '';
  96. const data = { ...refData, functions };
  97. const replacedStr = replacer(`${str}`, {
  98. data,
  99. tagLen: 2,
  100. regex: /\{\{(.*?)\}\}/g,
  101. modifyPath: (path) =>
  102. replacer(path, {
  103. data,
  104. tagLen: 1,
  105. regex: /\[(.*?)\]/g,
  106. }),
  107. });
  108. return replacedStr;
  109. }