File size: 1,868 Bytes
d9494a5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import { type Nullable } from '@/types';

import { capitalize } from '../strings/capitalize';
import { isDefined } from '../validation/isDefined';

import { safeGetNestedProperty } from './safeGetNestedProperty';

const TEMPLATE_VARIABLE_REGEX = /\$\{([^{}]+)\}/g;
const HAS_TEMPLATE_VARIABLE_REGEX = /\$\{[^{}]+\}/;
const TRANSFORM_FUNCTION_CALL_REGEX = /^(\w+)\((.+)\)$/;

const LABEL_TRANSFORM_FUNCTIONS: Record<string, (value: string) => string> = {
  capitalize,
  lowercase: (value: string) => value.toLowerCase(),
};

const resolveTemplateExpression = ({
  expression,
  context,
}: {
  expression: string;
  context: Record<string, unknown>;
}): string => {
  const trimmedExpression = expression.trim();
  const transformFunctionMatch = trimmedExpression.match(
    TRANSFORM_FUNCTION_CALL_REGEX,
  );

  const expressionToEvaluate = transformFunctionMatch
    ? transformFunctionMatch[2].trim()
    : trimmedExpression;

  const transformFunction = transformFunctionMatch
    ? LABEL_TRANSFORM_FUNCTIONS[transformFunctionMatch[1]]
    : undefined;

  const resolvedPropertyValue = safeGetNestedProperty(
    context,
    expressionToEvaluate,
  );

  if (!isDefined(resolvedPropertyValue)) {
    return '';
  }

  const stringValue = String(resolvedPropertyValue);

  return isDefined(transformFunction)
    ? transformFunction(stringValue)
    : stringValue;
};

export const interpolateCommandMenuItemTemplate = ({
  label,
  context,
}: {
  label: Nullable<string>;
  context: Record<string, unknown>;
}): Nullable<string> => {
  if (!isDefined(label)) {
    return null;
  }

  if (!HAS_TEMPLATE_VARIABLE_REGEX.test(label)) {
    return label;
  }

  return label.replace(TEMPLATE_VARIABLE_REGEX, (match, expression: string) => {
    try {
      return resolveTemplateExpression({ expression, context });
    } catch {
      return match;
    }
  });
};