@player-tools/dsl 0.11.0 → 0.12.1--canary.213.4811

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,20 @@
1
1
  import { test, expect, describe } from "vitest";
2
- import { getObjectReferences } from "../utils";
2
+ import {
3
+ getObjectReferences,
4
+ makeFunctionByName,
5
+ mapExpressionHandlersToFunctions,
6
+ wrapFunctionInType,
7
+ } from "../utils";
8
+ import { Binding, Expression, ExpressionHandler } from "@player-ui/player";
9
+ import { binding, expression } from "../string-templates";
10
+ import { LocalBazType } from "./helpers/mock-data-refs";
11
+ import { DataTypeRefs } from "../types";
12
+ import { makeBindingsForObject } from "../compiler/schema";
3
13
 
4
14
  describe("Testing the 'getObjectReferences' helper that creates same property references into a new object", () => {
5
15
  test("should return the object properties in referenced format", () => {
6
16
  const dataTypes = {
7
- BooleanType: {
8
- type: "BooleanType",
9
- },
17
+ LocalBazType,
10
18
  };
11
19
 
12
20
  const validators = {
@@ -15,12 +23,192 @@ describe("Testing the 'getObjectReferences' helper that creates same property re
15
23
  },
16
24
  };
17
25
 
18
- const dataReferences = getObjectReferences(dataTypes);
19
- const validatorReferences = getObjectReferences(validators);
26
+ type DTREF = DataTypeRefs<typeof dataTypes>;
27
+
28
+ const dataReferences = getObjectReferences<typeof dataTypes, DTREF>(
29
+ dataTypes,
30
+ );
20
31
 
21
- expect(dataReferences.BooleanTypeRef).toStrictEqual({
22
- type: "BooleanType",
32
+ type VALREF = DataTypeRefs<typeof validators>;
33
+ const validatorReferences = getObjectReferences<typeof validators, VALREF>(
34
+ validators,
35
+ );
36
+
37
+ expect(dataReferences.LocalBazTypeRef).toStrictEqual({
38
+ type: "LocalBazType",
23
39
  });
24
40
  expect(validatorReferences.requiredRef).toStrictEqual({ type: "required" });
25
41
  });
26
42
  });
43
+
44
+ describe("DSL Expression Generation Helper", () => {
45
+ test("Returns the correct serialization for bindings", () => {
46
+ const mockFunction: ExpressionHandler<[Binding], boolean> = (ctx, val) => {
47
+ return false;
48
+ };
49
+
50
+ const mockFunctionDSL = wrapFunctionInType(mockFunction);
51
+
52
+ const foo = binding<string>`some.binding`;
53
+ const test = expression`${mockFunctionDSL(foo)} == true`;
54
+
55
+ expect(test.toValue()).toEqual("mockFunction('some.binding') == true");
56
+ });
57
+
58
+ test("Returns the correct serialization for mixed values and bindings", () => {
59
+ const mockFunction: ExpressionHandler<[string, string, number], boolean> = (
60
+ ctx,
61
+ val,
62
+ ) => {
63
+ return false;
64
+ };
65
+
66
+ const mockFunctionDSL = wrapFunctionInType(mockFunction);
67
+
68
+ const foo = binding<string>`some.binding`;
69
+ const test = expression`${mockFunctionDSL(foo.toRefString(), "test", 1)} == true`;
70
+
71
+ expect(test.toValue()).toEqual(
72
+ "mockFunction('{{some.binding}}', 'test', 1) == true",
73
+ );
74
+ });
75
+
76
+ test("Returns the correct serialization for array items", () => {
77
+ const mockFunction: ExpressionHandler<[Array<unknown>], boolean> = (
78
+ ctx,
79
+ val,
80
+ ) => {
81
+ return false;
82
+ };
83
+
84
+ const mockFunctionDSL = wrapFunctionInType(mockFunction);
85
+
86
+ const foo = binding`some.binding`;
87
+ const test = expression`${mockFunctionDSL([1, "2", foo])}`;
88
+
89
+ expect(test.toValue()).toEqual(
90
+ "mockFunction([1, '2', '{{some.binding}}'])",
91
+ );
92
+ });
93
+
94
+ test("Can Dereferenced Binding", () => {
95
+ const mockFunction: ExpressionHandler<[boolean], boolean> = (ctx, val) => {
96
+ return false;
97
+ };
98
+
99
+ const mockFunctionDSL = wrapFunctionInType(mockFunction);
100
+
101
+ const foo = binding<boolean>`some.binding`;
102
+ const test = expression`${mockFunctionDSL(foo.toRefString())} == true`;
103
+
104
+ expect(test.toValue()).toEqual("mockFunction('{{some.binding}}') == true");
105
+ });
106
+
107
+ test("Can Pass Nested Expressions", () => {
108
+ const mockFunction: ExpressionHandler<[Expression, number], boolean> = (
109
+ ctx,
110
+ val,
111
+ ) => {
112
+ return false;
113
+ };
114
+
115
+ const mockFunctionDSL = wrapFunctionInType(mockFunction);
116
+
117
+ const foo = binding`foo.bar`;
118
+ const bar = expression<string>`${foo} != 1`;
119
+ const test = expression`${mockFunctionDSL(bar, 1)}`;
120
+
121
+ expect(test.toValue()).toEqual("mockFunction('{{foo.bar}} != 1', 1)");
122
+ });
123
+
124
+ test("Mock a function by name and args", () => {
125
+ const testFunction = makeFunctionByName<[string, number, boolean], void>(
126
+ "testFunction",
127
+ );
128
+
129
+ expect(testFunction("1", 0, false).toValue()).toMatch(
130
+ "testFunction('1', 0, false)",
131
+ );
132
+ });
133
+
134
+ test("Export Generator", () => {
135
+ const mockFunction: ExpressionHandler<[string, number], boolean> = (
136
+ ctx,
137
+ val,
138
+ ) => {
139
+ return false;
140
+ };
141
+ const expressionFunctions = { mockFunction };
142
+
143
+ const usableFunctions =
144
+ mapExpressionHandlersToFunctions<typeof expressionFunctions>(
145
+ expressionFunctions,
146
+ );
147
+
148
+ expect(usableFunctions.mockFunction("1", 0).toValue()).toMatch(
149
+ "mockFunction('1', 0)",
150
+ );
151
+ });
152
+
153
+ test("Return Type Chaining", () => {
154
+ const mockFunction: ExpressionHandler<[string, number], string> = (
155
+ ctx,
156
+ val,
157
+ ) => {
158
+ return "false";
159
+ };
160
+
161
+ const mockFunction2: ExpressionHandler<[string], boolean> = (ctx, val) => {
162
+ return false;
163
+ };
164
+ const expressionFunctions = { mockFunction, mockFunction2 };
165
+
166
+ const usableFunctions =
167
+ mapExpressionHandlersToFunctions<typeof expressionFunctions>(
168
+ expressionFunctions,
169
+ );
170
+
171
+ expect(
172
+ usableFunctions
173
+ .mockFunction2(usableFunctions.mockFunction("1", 0).toValue())
174
+ .toValue(),
175
+ ).toMatch("mockFunction2('mockFunction('1', 0)')");
176
+ });
177
+
178
+ test("Works with DSL Schema", () => {
179
+ const mockFunction: ExpressionHandler<[string, boolean], string> = (
180
+ ctx,
181
+ val,
182
+ ) => {
183
+ return "false";
184
+ };
185
+
186
+ const dataTypes = { LocalBazType };
187
+
188
+ type DTREF = DataTypeRefs<typeof dataTypes>;
189
+
190
+ const refableTypes = getObjectReferences<typeof dataTypes, DTREF>(
191
+ dataTypes,
192
+ );
193
+
194
+ const data = {
195
+ some: {
196
+ value: refableTypes.LocalBazTypeRef,
197
+ },
198
+ };
199
+
200
+ const schema = makeBindingsForObject(data);
201
+ const expressionFunctions = { mockFunction };
202
+
203
+ const usableFunctions =
204
+ mapExpressionHandlersToFunctions<typeof expressionFunctions>(
205
+ expressionFunctions,
206
+ );
207
+
208
+ expect(
209
+ usableFunctions
210
+ .mockFunction("1", schema.some.value.toRefString())
211
+ .toValue(),
212
+ ).toMatch("mockFunction('1', '{{some.value}}')");
213
+ });
214
+ });
@@ -8,7 +8,7 @@ import type { BindingTemplateInstance } from "../string-templates";
8
8
  const bindingSymbol = Symbol("binding");
9
9
 
10
10
  /** Symbol to indicate that a schema node should be generated with a different name */
11
- export const SchemaTypeName = Symbol("Schema Rename");
11
+ export const SchemaTypeName: unique symbol = Symbol("Schema Rename");
12
12
 
13
13
  interface SchemaChildren {
14
14
  /** Object property that will be used to create the intermediate type */
@@ -47,7 +47,15 @@ export class SchemaGenerator {
47
47
  private generatedDataTypes: Map<string, GeneratedDataType>;
48
48
  private logger: LoggingInterface;
49
49
 
50
- public hooks = {
50
+ public hooks: {
51
+ createSchemaNode: SyncWaterfallHook<
52
+ [
53
+ node: Schema.DataType<unknown>,
54
+ originalProperty: Record<string | symbol, unknown>,
55
+ ],
56
+ Record<string, any>
57
+ >;
58
+ } = {
51
59
  createSchemaNode: new SyncWaterfallHook<
52
60
  [
53
61
  node: Schema.DataType,
@@ -197,14 +205,14 @@ export type MakeBindingRefable<T> = {
197
205
  : T[P] extends unknown[]
198
206
  ? T[P]
199
207
  : MakeBindingRefable<T[P]>;
200
- } & BindingTemplateInstance;
208
+ } & BindingTemplateInstance<T>;
201
209
 
202
210
  /**
203
211
  * Adds bindings to an object so that the object can be directly used in JSX
204
212
  */
205
213
  export function makeBindingsForObject<Type>(
206
214
  obj: Type,
207
- arrayAccessorKeys = ["_index_"],
215
+ arrayAccessorKeys: string[] = ["_index_"],
208
216
  ): MakeBindingRefable<Type> {
209
217
  /** Proxy to track binding callbacks */
210
218
  const accessor = (paths: string[]) => {
@@ -258,7 +266,7 @@ export function makeBindingsForObject<Type>(
258
266
  /**
259
267
  * Generates binding for an object property
260
268
  */
261
- export const getBindingFromObject = (obj: any) => {
269
+ export const getBindingFromObject = (obj: any): BindingTemplateInstance => {
262
270
  const baseBindings = obj[bindingSymbol] as string[];
263
271
  if (!Array.isArray(baseBindings) || baseBindings.length === 0) {
264
272
  throw new Error(`Unable to get binding for ${obj}`);
@@ -270,13 +278,13 @@ export const getBindingFromObject = (obj: any) => {
270
278
  /**
271
279
  * Returns the binding string from an object path
272
280
  */
273
- export const getBindingStringFromObject = (obj: any) => {
281
+ export const getBindingStringFromObject = (obj: any): string => {
274
282
  return getBindingFromObject(obj).toString();
275
283
  };
276
284
 
277
285
  /**
278
286
  * Returns the ref string from an object path
279
287
  */
280
- export const getRefStringFromObject = (obj: any) => {
288
+ export const getRefStringFromObject = (obj: any): string => {
281
289
  return getBindingFromObject(obj).toRefString();
282
290
  };
@@ -0,0 +1,159 @@
1
+ import { test, expect, describe } from "vitest";
2
+ import {
3
+ and,
4
+ assign,
5
+ binding as b,
6
+ expression as e,
7
+ equals,
8
+ nand,
9
+ nor,
10
+ not,
11
+ or,
12
+ } from "../..";
13
+
14
+ describe("assign", () => {
15
+ test("string", () => {
16
+ const binding = b`foo.bar`;
17
+ expect(assign(binding, "1").toValue()).toStrictEqual("{{foo.bar}} = '1'");
18
+ });
19
+ test("number", () => {
20
+ const binding = b`foo.bar`;
21
+ expect(assign(binding, 1).toValue()).toStrictEqual("{{foo.bar}} = 1");
22
+ });
23
+ test("boolean", () => {
24
+ const binding = b`foo.bar`;
25
+ expect(assign(binding, true).toValue()).toStrictEqual("{{foo.bar}} = true");
26
+ });
27
+ test("undefined", () => {
28
+ const binding = b`foo.bar`;
29
+ expect(assign(binding, undefined).toValue()).toStrictEqual(
30
+ "{{foo.bar}} = null",
31
+ );
32
+ });
33
+ });
34
+
35
+ describe("not", () => {
36
+ test("binding", () => {
37
+ const binding = b<boolean>`foo.bar`;
38
+ expect(not(binding).toValue()).toStrictEqual("!({{foo.bar}})");
39
+ });
40
+ test("expression", () => {
41
+ const expression = e<boolean>`${b`foo.bar`} = true`;
42
+
43
+ expect(not(expression).toValue()).toStrictEqual("!({{foo.bar}} = true)");
44
+ });
45
+ });
46
+
47
+ describe("and", () => {
48
+ test("bindings", () => {
49
+ const binding = b<boolean>`foo.bar`;
50
+ const binding2 = b<boolean>`foo.baz`;
51
+ expect(and(binding, binding2).toValue()).toStrictEqual(
52
+ "{{foo.bar}} && {{foo.baz}}",
53
+ );
54
+ });
55
+
56
+ test("expressions", () => {
57
+ const expression = e<boolean>`${b`foo.bar`} == true`;
58
+ const expression2 = e<boolean>`${b`foo.baz`} == '1'`;
59
+ expect(and(expression, expression2).toValue()).toStrictEqual(
60
+ "({{foo.bar}} == true) && ({{foo.baz}} == '1')",
61
+ );
62
+ });
63
+ });
64
+
65
+ describe("or", () => {
66
+ test("bindings", () => {
67
+ const binding = b<boolean>`foo.bar`;
68
+ const binding2 = b<boolean>`foo.baz`;
69
+ expect(or(binding, binding2).toValue()).toStrictEqual(
70
+ "{{foo.bar}} || {{foo.baz}}",
71
+ );
72
+ });
73
+
74
+ test("expressions", () => {
75
+ const expression = e<boolean>`${b`foo.bar`} == true`;
76
+ const expression2 = e<boolean>`${b`foo.baz`} == '1'`;
77
+ expect(or(expression, expression2).toValue()).toStrictEqual(
78
+ "({{foo.bar}} == true) || ({{foo.baz}} == '1')",
79
+ );
80
+ });
81
+ });
82
+
83
+ describe("nand", () => {
84
+ test("bindings", () => {
85
+ const binding = b<boolean>`foo.bar`;
86
+ const binding2 = b<boolean>`foo.baz`;
87
+ expect(nand(binding, binding2).toValue()).toStrictEqual(
88
+ "!({{foo.bar}} && {{foo.baz}})",
89
+ );
90
+ });
91
+
92
+ test("expressions", () => {
93
+ const expression = e<boolean>`${b`foo.bar`} == true`;
94
+ const expression2 = e<boolean>`${b`foo.baz`} == '1'`;
95
+ expect(nand(expression, expression2).toValue()).toStrictEqual(
96
+ "!(({{foo.bar}} == true) && ({{foo.baz}} == '1'))",
97
+ );
98
+ });
99
+ });
100
+
101
+ describe("nor", () => {
102
+ test("bindings", () => {
103
+ const binding = b<boolean>`foo.bar`;
104
+ const binding2 = b<boolean>`foo.baz`;
105
+ expect(nor(binding, binding2).toValue()).toStrictEqual(
106
+ "!({{foo.bar}} || {{foo.baz}})",
107
+ );
108
+ });
109
+
110
+ test("expressions", () => {
111
+ const expression = e<boolean>`${b`foo.bar`} == true`;
112
+ const expression2 = e<boolean>`${b`foo.baz`} == '1'`;
113
+ expect(nor(expression, expression2).toValue()).toStrictEqual(
114
+ "!(({{foo.bar}} == true) || ({{foo.baz}} == '1'))",
115
+ );
116
+ });
117
+ });
118
+
119
+ describe("equals", () => {
120
+ test("binding-string", () => {
121
+ const binding = b`foo.bar`;
122
+ expect(equals(binding, "1").toValue()).toStrictEqual("{{foo.bar}} == '1'");
123
+ });
124
+
125
+ test("binding-number", () => {
126
+ const binding = b`foo.bar`;
127
+ expect(equals(binding, 1).toValue()).toStrictEqual("{{foo.bar}} == 1");
128
+ });
129
+
130
+ test("binding-boolean", () => {
131
+ const binding = b`foo.bar`;
132
+ expect(equals(binding, true).toValue()).toStrictEqual(
133
+ "{{foo.bar}} == true",
134
+ );
135
+ });
136
+
137
+ test("binding-undefined", () => {
138
+ const binding = b`foo.bar`;
139
+ expect(equals(binding, undefined).toValue()).toStrictEqual(
140
+ "{{foo.bar}} == null",
141
+ );
142
+ });
143
+
144
+ test("bindings", () => {
145
+ const binding = b<boolean>`foo.bar`;
146
+ const binding2 = b<boolean>`foo.baz`;
147
+ expect(equals(binding, binding2).toValue()).toStrictEqual(
148
+ "{{foo.bar}} == {{foo.baz}}",
149
+ );
150
+ });
151
+
152
+ test("expressions", () => {
153
+ const expression = e<boolean>`${b`foo.bar`} == true`;
154
+ const expression2 = e<boolean>`${b`foo.baz`} == '1'`;
155
+ expect(equals(expression, expression2).toValue()).toStrictEqual(
156
+ "({{foo.bar}} == true) == ({{foo.baz}} == '1')",
157
+ );
158
+ });
159
+ });
@@ -0,0 +1,38 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { expression as e } from "../../string-templates";
3
+ import { testExpression } from "../testing";
4
+ import { ExpressionHandler, withoutContext } from "@player-ui/player";
5
+
6
+ describe("expression testing", () => {
7
+ const initialModel = {
8
+ foo: {
9
+ bar: {
10
+ a: "1",
11
+ b: 2,
12
+ c: false,
13
+ },
14
+ },
15
+ };
16
+
17
+ test("basic operations", () => {
18
+ const exp = e`{{foo.bar.a}} = "test"`;
19
+ const result = testExpression(exp, initialModel);
20
+ expect(result).toStrictEqual({
21
+ foo: { bar: { a: "test", b: 2, c: false } },
22
+ });
23
+ });
24
+
25
+ test("custom function", () => {
26
+ const exp = e`{{foo.bar.c}} = not({{foo.bar.c}})`;
27
+
28
+ const mockExpression: ExpressionHandler<[boolean], boolean> =
29
+ withoutContext((arg) => {
30
+ return !arg;
31
+ });
32
+ const expressionMap = new Map([["not", mockExpression]]);
33
+ const result = testExpression(exp, initialModel, expressionMap);
34
+ expect(result).toStrictEqual({
35
+ foo: { bar: { a: "1", b: 2, c: true } },
36
+ });
37
+ });
38
+ });
@@ -0,0 +1,141 @@
1
+ import {
2
+ expression as e,
3
+ BindingTemplateInstance,
4
+ ExpressionTemplateInstance,
5
+ isTemplateStringInstance,
6
+ isBindingTemplateInstance,
7
+ } from "..";
8
+
9
+ type Argument<T> =
10
+ | string
11
+ | boolean
12
+ | number
13
+ | undefined
14
+ | BindingTemplateInstance<T>
15
+ | ExpressionTemplateInstance<T>;
16
+
17
+ const escapePrimitive = <T>(
18
+ val: Exclude<
19
+ Argument<T>,
20
+ BindingTemplateInstance<T> | ExpressionTemplateInstance<T>
21
+ >,
22
+ ): string => {
23
+ switch (typeof val) {
24
+ case "string": {
25
+ return `'${val}'`;
26
+ }
27
+ case "undefined": {
28
+ return "null";
29
+ }
30
+ default: {
31
+ return `${val}`;
32
+ }
33
+ }
34
+ };
35
+
36
+ const handleBindingOrExpression = (
37
+ val: BindingTemplateInstance | ExpressionTemplateInstance,
38
+ ) => {
39
+ if (isBindingTemplateInstance(val)) {
40
+ return val.toRefString();
41
+ } else {
42
+ return `(${val.toValue()})`;
43
+ }
44
+ };
45
+
46
+ const handleArgument = <T>(arg: Argument<T>): string => {
47
+ return isTemplateStringInstance(arg)
48
+ ? handleBindingOrExpression(arg)
49
+ : escapePrimitive(arg);
50
+ };
51
+
52
+ /**
53
+ * Performs an assigment of a value to a binding by returning the expression
54
+ * {{<binding>}} = <value>
55
+ * @param binding
56
+ * @param value
57
+ */
58
+ export const assign = <T>(
59
+ binding: BindingTemplateInstance<any>,
60
+ value: Argument<T>,
61
+ ): ExpressionTemplateInstance<void> => {
62
+ return e`${binding} = ${isTemplateStringInstance(value) ? value : escapePrimitive(value)}`;
63
+ };
64
+
65
+ /**
66
+ * Returns an equality comparison between the two values
67
+ */
68
+ export const equals = <A, B>(
69
+ a: Argument<A>,
70
+ b: Argument<B>,
71
+ ): ExpressionTemplateInstance<boolean> => {
72
+ return e`${handleArgument(a)} == ${handleArgument(b)}`;
73
+ };
74
+
75
+ /**
76
+ * Returns the negated version of the binding/expression
77
+ * by returning !(<value>)
78
+ * @param binding Binding/Expression to invert
79
+ * @returns Negated binding/expression
80
+ */
81
+ export const not = (
82
+ value: BindingTemplateInstance<boolean> | ExpressionTemplateInstance<boolean>,
83
+ ): ExpressionTemplateInstance<boolean> => {
84
+ return e<boolean>`!(${value})`;
85
+ };
86
+
87
+ /**
88
+ * Creates an expression for the logical or'ing of the provided values
89
+ * e.g: <exp1> || <exp2> || ...
90
+ * @param values Array of bindings/expressions to logically or
91
+ * @returns boolean
92
+ */
93
+ export const or = (
94
+ ...values: Array<
95
+ BindingTemplateInstance<boolean> | ExpressionTemplateInstance<boolean>
96
+ >
97
+ ): ExpressionTemplateInstance<boolean> => {
98
+ return e`${values.map(handleBindingOrExpression).join(" || ")}` as ExpressionTemplateInstance<boolean>;
99
+ };
100
+
101
+ /**
102
+ * Creates an expression for the logical nor'ing of the provided values
103
+ * e.g: !(<exp1> || <exp2> || ...)
104
+ * @param values Array of bindings/expressions to logically nor
105
+ * @returns boolean
106
+ */
107
+ export const nor = (
108
+ ...values: Array<
109
+ BindingTemplateInstance<boolean> | ExpressionTemplateInstance<boolean>
110
+ >
111
+ ): ExpressionTemplateInstance<boolean> => {
112
+ return not(or(...values));
113
+ };
114
+
115
+ /**
116
+ * Creates an expression for the logical and'ing of the provided values
117
+ * e.g: <exp1> && <exp2> && ...
118
+ * @param values Array of bindings/expressions to logically and
119
+ * @returns boolean
120
+ */
121
+ export const and = (
122
+ ...values: Array<
123
+ BindingTemplateInstance<boolean> | ExpressionTemplateInstance<boolean>
124
+ >
125
+ ): ExpressionTemplateInstance<boolean> => {
126
+ return e`${values.map(handleBindingOrExpression).join(" && ")}` as ExpressionTemplateInstance<boolean>;
127
+ };
128
+
129
+ /**
130
+ * Creates an expression for the logical nand'ing of the provided values
131
+ * e.g: !(<exp1> && <exp2> && ...)
132
+ * @param values Array of bindings/expressions to logically nand
133
+ * @returns boolean
134
+ */
135
+ export const nand = (
136
+ ...values: Array<
137
+ BindingTemplateInstance<boolean> | ExpressionTemplateInstance<boolean>
138
+ >
139
+ ): ExpressionTemplateInstance<boolean> => {
140
+ return not(and(...values));
141
+ };
@@ -0,0 +1,40 @@
1
+ import {
2
+ BindingParser,
3
+ ExpressionEvaluator,
4
+ ExpressionHandler,
5
+ ExpressionType,
6
+ LocalModel,
7
+ withParser,
8
+ } from "@player-ui/player";
9
+ import { ExpressionTemplateInstance } from "../string-templates";
10
+
11
+ /**
12
+ * Test harness to make testing expressions easier.
13
+ * Given an expreesion and an initial data model the harness will execute the expression
14
+ * on and return the new state of the data model.
15
+ * @param exp expression to execute
16
+ * @param initialData data model to operate on
17
+ * @param expressions expression handlers for functions that are called
18
+ * @returns Final data model state
19
+ */
20
+ export function testExpression(
21
+ exp: ExpressionTemplateInstance,
22
+ initialData: object,
23
+ expressions?: Map<string, ExpressionHandler<any[], any>>,
24
+ ): object {
25
+ // Setup Mock Player model and parsers
26
+ const localModel = new LocalModel(initialData);
27
+ const bindingParser = new BindingParser({
28
+ get: localModel.get,
29
+ set: localModel.set,
30
+ evaluate: (exp: ExpressionType) => {
31
+ return evaluator.evaluate(exp);
32
+ },
33
+ });
34
+
35
+ const model = withParser(localModel, bindingParser.parse);
36
+ const evaluator = new ExpressionEvaluator({ model });
37
+ expressions?.forEach((fn, name) => evaluator.addExpressionFunction(name, fn));
38
+ evaluator.evaluate(exp.toValue());
39
+ return localModel.get();
40
+ }
package/src/index.ts CHANGED
@@ -9,3 +9,4 @@ export * from "react-json-reconciler";
9
9
  export * from "./compiler/schema";
10
10
  export * from "./compiler";
11
11
  export * from "./compiler/types";
12
+ export * from "./expressions/native";