@player-tools/dsl 0.5.0--canary.62.1176 → 0.5.0-next.0

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.
Files changed (52) hide show
  1. package/dist/cjs/index.cjs +973 -0
  2. package/dist/cjs/index.cjs.map +1 -0
  3. package/dist/index.legacy-esm.js +910 -0
  4. package/dist/index.mjs +910 -0
  5. package/dist/index.mjs.map +1 -0
  6. package/package.json +27 -33
  7. package/src/__tests__/asset-api.test.tsx +354 -0
  8. package/src/__tests__/edge-cases.test.tsx +81 -0
  9. package/src/__tests__/helpers/asset-library.tsx +371 -0
  10. package/src/__tests__/helpers/mock-data-refs.ts +21 -0
  11. package/src/__tests__/json.test.ts +12 -0
  12. package/src/__tests__/jsx.test.tsx +138 -0
  13. package/src/__tests__/schema.test.tsx +378 -0
  14. package/src/__tests__/switch.test.tsx +251 -0
  15. package/src/__tests__/template.test.tsx +294 -0
  16. package/src/__tests__/view-api.test.tsx +46 -0
  17. package/src/auto-id.tsx +10 -10
  18. package/src/compiler/__tests__/compiler.test.tsx +264 -0
  19. package/src/compiler/__tests__/schema.test.ts +127 -0
  20. package/src/compiler/compiler.ts +50 -46
  21. package/src/compiler/index.ts +3 -3
  22. package/src/compiler/schema.ts +29 -25
  23. package/src/compiler/types.ts +15 -12
  24. package/src/compiler/utils.ts +7 -7
  25. package/src/components.tsx +17 -12
  26. package/src/index.ts +11 -11
  27. package/src/string-templates/__tests__/binding.test.ts +87 -0
  28. package/src/string-templates/__tests__/edge-cases.test.ts +6 -0
  29. package/src/string-templates/__tests__/expression.test.ts +9 -0
  30. package/src/string-templates/__tests__/react.test.tsx +75 -0
  31. package/src/string-templates/index.ts +40 -22
  32. package/src/switch.tsx +12 -12
  33. package/src/template.tsx +38 -34
  34. package/src/types.ts +5 -5
  35. package/src/utils.tsx +8 -8
  36. package/types/auto-id.d.ts +33 -0
  37. package/types/compiler/compiler.d.ts +27 -0
  38. package/types/compiler/index.d.ts +4 -0
  39. package/types/compiler/schema.d.ts +61 -0
  40. package/types/compiler/types.d.ts +55 -0
  41. package/types/compiler/utils.d.ts +4 -0
  42. package/types/components.d.ts +90 -0
  43. package/types/index.d.ts +12 -0
  44. package/types/string-templates/index.d.ts +50 -0
  45. package/types/switch.d.ts +19 -0
  46. package/types/template.d.ts +20 -0
  47. package/types/types.d.ts +42 -0
  48. package/types/utils.d.ts +33 -0
  49. package/README.md +0 -9
  50. package/dist/index.cjs.js +0 -990
  51. package/dist/index.d.ts +0 -404
  52. package/dist/index.esm.js +0 -923
@@ -0,0 +1,294 @@
1
+ import { test, expect, describe } from "vitest";
2
+ import React from "react";
3
+ import { render } from "react-json-reconciler";
4
+ import { binding as b } from "..";
5
+ import { Template } from "../template";
6
+ import { Text, Collection } from "./helpers/asset-library";
7
+
8
+ test("finds output property based on array context", async () => {
9
+ const element = (
10
+ <obj>
11
+ <property name="foo">
12
+ <array>
13
+ <value>Foo</value>
14
+ <Template data={b`foo.output`}>
15
+ <value>bar</value>
16
+ </Template>
17
+ </array>
18
+ </property>
19
+ </obj>
20
+ );
21
+
22
+ expect((await render(element)).jsonValue).toStrictEqual({
23
+ foo: ["Foo"],
24
+ template: [
25
+ {
26
+ data: "foo.output",
27
+ value: "bar",
28
+ output: "foo",
29
+ },
30
+ ],
31
+ });
32
+ });
33
+
34
+ test("finds dynamic property in a template", async () => {
35
+ const element = (
36
+ <obj>
37
+ <property name="foo">
38
+ <array>
39
+ <value>Foo</value>
40
+ <Template dynamic data={b`foo.output`}>
41
+ <value>bar</value>
42
+ </Template>
43
+ </array>
44
+ </property>
45
+ </obj>
46
+ );
47
+
48
+ expect((await render(element)).jsonValue).toStrictEqual({
49
+ foo: ["Foo"],
50
+ template: [
51
+ {
52
+ data: "foo.output",
53
+ dynamic: true,
54
+ value: "bar",
55
+ output: "foo",
56
+ },
57
+ ],
58
+ });
59
+ });
60
+
61
+ test("works if already in a template array", async () => {
62
+ const element = (
63
+ <obj>
64
+ <property name="template">
65
+ <array>
66
+ <Template output="output" data={b`foo.output`}>
67
+ <value>bar</value>
68
+ </Template>
69
+ </array>
70
+ </property>
71
+ </obj>
72
+ );
73
+ expect((await render(element)).jsonValue).toStrictEqual({
74
+ template: [{ output: "output", data: "foo.output", value: "bar" }],
75
+ });
76
+ });
77
+
78
+ test("template will delete empty arrays related to the template only", async () => {
79
+ const element = (
80
+ <Collection>
81
+ <Collection.Actions>
82
+ <Template data={b`foo.bar`}>
83
+ <Text>Template action</Text>
84
+ </Template>
85
+ </Collection.Actions>
86
+ <Collection.Values>
87
+ <Template data={b`foo.bar`}>
88
+ <Text>Template Value 1</Text>
89
+ </Template>
90
+ <Collection>
91
+ <Collection.Actions />
92
+ {/* "actions": [] will not be deleted since it's not related to template */}
93
+ <Collection.Values>
94
+ {/* "values": [] will not be deleted */}
95
+ <Template data={b`foo.bar`}>
96
+ <Text>Template Value 2</Text>
97
+ </Template>
98
+ </Collection.Values>
99
+ </Collection>
100
+ <Collection>
101
+ <Collection.Actions />
102
+ <Collection.Values>
103
+ <Text>This should not be deleted by template</Text>
104
+ {/* "values" array will have text so it will not be deleted, only empty arrays are deleted */}
105
+ <Template data={b`foo.bar`}>
106
+ <Text>Template Value 3</Text>
107
+ </Template>
108
+ </Collection.Values>
109
+ </Collection>
110
+ </Collection.Values>
111
+ </Collection>
112
+ );
113
+ expect((await render(element)).jsonValue).toStrictEqual({
114
+ id: "root",
115
+ type: "collection",
116
+ template: [
117
+ {
118
+ data: "foo.bar",
119
+ output: "actions",
120
+ value: {
121
+ id: "actions-_index_",
122
+ type: "text",
123
+ value: "Template action",
124
+ },
125
+ },
126
+ {
127
+ data: "foo.bar",
128
+ output: "values",
129
+ value: {
130
+ asset: {
131
+ id: "values-_index_",
132
+ type: "text",
133
+ value: "Template Value 1",
134
+ },
135
+ },
136
+ },
137
+ ],
138
+ values: [
139
+ {
140
+ asset: {
141
+ actions: [],
142
+ id: "values-1",
143
+ type: "collection",
144
+ template: [
145
+ {
146
+ data: "foo.bar",
147
+ output: "values",
148
+ value: {
149
+ asset: {
150
+ id: "values-1-values-_index_",
151
+ type: "text",
152
+ value: "Template Value 2",
153
+ },
154
+ },
155
+ },
156
+ ],
157
+ },
158
+ },
159
+ {
160
+ asset: {
161
+ actions: [],
162
+ id: "values-2",
163
+ type: "collection",
164
+ template: [
165
+ {
166
+ data: "foo.bar",
167
+ output: "values",
168
+ value: {
169
+ asset: {
170
+ id: "values-2-values-_index_",
171
+ type: "text",
172
+ value: "Template Value 3",
173
+ },
174
+ },
175
+ },
176
+ ],
177
+ values: [
178
+ {
179
+ asset: {
180
+ id: "values-2-values-0",
181
+ type: "text",
182
+ value: "This should not be deleted by template",
183
+ },
184
+ },
185
+ ],
186
+ },
187
+ },
188
+ ],
189
+ });
190
+ });
191
+
192
+ describe("template auto id", () => {
193
+ test("s1mple", async () => {
194
+ const element = (
195
+ <Collection>
196
+ <Collection.Values>
197
+ <Template data={b`foo.bar`}>
198
+ <Text>Template Value</Text>
199
+ </Template>
200
+ <Text id="static">Value 1</Text>
201
+ </Collection.Values>
202
+ </Collection>
203
+ );
204
+
205
+ const actual = (await render(element)).jsonValue;
206
+
207
+ expect(actual).toStrictEqual({
208
+ id: "root",
209
+ type: "collection",
210
+ values: [
211
+ {
212
+ asset: {
213
+ id: "static",
214
+ type: "text",
215
+ value: "Value 1",
216
+ },
217
+ },
218
+ ],
219
+ template: [
220
+ {
221
+ data: "foo.bar",
222
+ output: "values",
223
+ value: {
224
+ asset: {
225
+ id: "values-_index_",
226
+ type: "text",
227
+ value: "Template Value",
228
+ },
229
+ },
230
+ },
231
+ ],
232
+ });
233
+ });
234
+
235
+ test("nested", async () => {
236
+ const element = (
237
+ <Collection>
238
+ <Collection.Values>
239
+ <Template data={b`foo.bar`}>
240
+ <Collection>
241
+ <Collection.Values>
242
+ <Template data={b`foo.baz`}>
243
+ <Text>Nested Templates</Text>
244
+ </Template>
245
+ </Collection.Values>
246
+ </Collection>
247
+ </Template>
248
+ <Text id="static">Value 1</Text>
249
+ </Collection.Values>
250
+ </Collection>
251
+ );
252
+
253
+ const actual = (await render(element)).jsonValue;
254
+
255
+ expect(actual).toStrictEqual({
256
+ id: "root",
257
+ type: "collection",
258
+ values: [
259
+ {
260
+ asset: {
261
+ id: "static",
262
+ type: "text",
263
+ value: "Value 1",
264
+ },
265
+ },
266
+ ],
267
+ template: [
268
+ {
269
+ data: "foo.bar",
270
+ output: "values",
271
+ value: {
272
+ asset: {
273
+ id: "values-_index_",
274
+ template: [
275
+ {
276
+ output: "values",
277
+ data: "foo.baz",
278
+ value: {
279
+ asset: {
280
+ id: "values-_index_-values-_index1_",
281
+ type: "text",
282
+ value: "Nested Templates",
283
+ },
284
+ },
285
+ },
286
+ ],
287
+ type: "collection",
288
+ },
289
+ },
290
+ },
291
+ ],
292
+ });
293
+ });
294
+ });
@@ -0,0 +1,46 @@
1
+ import { test, expect, describe } from "vitest";
2
+ import React from "react";
3
+ import { render } from "react-json-reconciler";
4
+ import { binding as b } from "../string-templates";
5
+ import { Info } from "./helpers/asset-library";
6
+
7
+ describe("View", () => {
8
+ test("Does not convert ref property to template", async () => {
9
+ const validationBinding = b`some.binding`;
10
+ const element = (
11
+ await render(
12
+ <Info
13
+ id="custom-id"
14
+ validation={[
15
+ {
16
+ type: "expression",
17
+ ref: validationBinding,
18
+ message: "some validation message",
19
+ },
20
+ ]}
21
+ >
22
+ <Info.Title>Cool Title</Info.Title>
23
+ </Info>
24
+ )
25
+ ).jsonValue;
26
+
27
+ expect(element).toStrictEqual({
28
+ id: "custom-id",
29
+ title: {
30
+ asset: {
31
+ id: "custom-id-title",
32
+ type: "text",
33
+ value: "Cool Title",
34
+ },
35
+ },
36
+ type: "info",
37
+ validation: [
38
+ {
39
+ message: "some validation message",
40
+ ref: "some.binding",
41
+ type: "expression",
42
+ },
43
+ ],
44
+ });
45
+ });
46
+ });
package/src/auto-id.tsx CHANGED
@@ -1,10 +1,10 @@
1
- import React from 'react';
2
- import type { JsonNode } from 'react-json-reconciler';
3
- import { flattenNodes } from 'react-json-reconciler';
4
- import { SlotContext } from './components';
5
- import type { WithChildren } from './types';
1
+ import React from "react";
2
+ import type { JsonNode } from "react-json-reconciler";
3
+ import { flattenNodes } from "react-json-reconciler";
4
+ import { SlotContext } from "./components";
5
+ import type { WithChildren } from "./types";
6
6
 
7
- const IDSuffixContext = React.createContext<string>('root');
7
+ const IDSuffixContext = React.createContext<string>("root");
8
8
 
9
9
  export const IndexSuffixStopContext = React.createContext<boolean>(false);
10
10
 
@@ -25,11 +25,11 @@ export const IDSuffixProvider = (
25
25
  return (
26
26
  <IDSuffixContext.Provider
27
27
  value={[
28
- currentPrefix === 'root' ? undefined : currentPrefix,
28
+ currentPrefix === "root" ? undefined : currentPrefix,
29
29
  props.suffix,
30
30
  ]
31
31
  .filter(Boolean)
32
- .join('-')}
32
+ .join("-")}
33
33
  >
34
34
  {props.children}
35
35
  </IDSuffixContext.Provider>
@@ -62,10 +62,10 @@ export const useIndexInSlot = (ref: React.RefObject<JsonNode>) => {
62
62
 
63
63
  React.useEffect(() => {
64
64
  if (!slotContext?.isArray) {
65
- throw new Error('Cannot get index in non-array slot');
65
+ throw new Error("Cannot get index in non-array slot");
66
66
  }
67
67
 
68
- if (ref.current && slotContext?.ref.current?.valueNode?.type === 'array') {
68
+ if (ref.current && slotContext?.ref.current?.valueNode?.type === "array") {
69
69
  const allChildren = flattenNodes(
70
70
  slotContext.ref.current.valueNode.children
71
71
  );
@@ -0,0 +1,264 @@
1
+ import { test, expect } from "vitest";
2
+ import React from "react";
3
+ import { expression as e } from "../../string-templates";
4
+ import { DSLCompiler } from "../compiler";
5
+ import type { Navigation } from "../../types";
6
+
7
+ test("treats jsx as view", async () => {
8
+ const compiler = new DSLCompiler();
9
+
10
+ const result = await compiler.serialize(
11
+ <object>
12
+ <property name="foo">bar</property>
13
+ </object>
14
+ );
15
+ expect(result).toBeDefined();
16
+ expect(result?.value).toStrictEqual({
17
+ foo: "bar",
18
+ });
19
+ });
20
+
21
+ test("should treat schema type objects as schema", async () => {
22
+ const compiler = new DSLCompiler();
23
+ const result = await compiler.serialize({
24
+ foo: { bar: { type: "StringType" } },
25
+ });
26
+
27
+ expect(result.value).toStrictEqual({
28
+ ROOT: {
29
+ foo: {
30
+ type: "fooType",
31
+ },
32
+ },
33
+ fooType: {
34
+ bar: {
35
+ type: "StringType",
36
+ },
37
+ },
38
+ });
39
+ });
40
+
41
+ test("expressions in navigation", async () => {
42
+ const compiler = new DSLCompiler();
43
+ const navigation: Navigation = {
44
+ BEGIN: "Flow",
45
+ onStart: e`foo`,
46
+ Flow: {
47
+ startState: "VIEW_page",
48
+ onStart: [e`foo`, e`foo`],
49
+ VIEW_page: {
50
+ onStart: {
51
+ exp: e`foo`,
52
+ },
53
+ state_type: `VIEW`,
54
+ ref: "test",
55
+ transitions: {
56
+ "*": "ShowView1Or2",
57
+ },
58
+ },
59
+ ShowView1Or2: {
60
+ state_type: "ACTION",
61
+ exp: e`foo`,
62
+ transitions: {
63
+ "*": "VIEW_Other",
64
+ },
65
+ },
66
+ END_back: {
67
+ state_type: "END",
68
+ outcome: "BACK",
69
+ },
70
+ END_done: {
71
+ state_type: "END",
72
+ outcome: "doneWithFlow",
73
+ },
74
+ },
75
+ };
76
+ const result = await compiler.serialize({ navigation });
77
+ expect(result.value).toStrictEqual({
78
+ navigation: {
79
+ BEGIN: "Flow",
80
+ onStart: `foo`,
81
+ Flow: {
82
+ startState: "VIEW_page",
83
+ onStart: [`foo`, `foo`],
84
+ VIEW_page: {
85
+ onStart: {
86
+ exp: `foo`,
87
+ },
88
+ state_type: `VIEW`,
89
+ ref: "test",
90
+ transitions: {
91
+ "*": "ShowView1Or2",
92
+ },
93
+ },
94
+ ShowView1Or2: {
95
+ state_type: "ACTION",
96
+ exp: `foo`,
97
+ transitions: {
98
+ "*": "VIEW_Other",
99
+ },
100
+ },
101
+ END_back: {
102
+ state_type: "END",
103
+ outcome: "BACK",
104
+ },
105
+ END_done: {
106
+ state_type: "END",
107
+ outcome: "doneWithFlow",
108
+ },
109
+ },
110
+ },
111
+ views: [],
112
+ });
113
+ });
114
+
115
+ test("compiles schema when added to flow", async () => {
116
+ const compiler = new DSLCompiler();
117
+ const result = await compiler.serialize({
118
+ id: "test-flow",
119
+ views: [],
120
+ navigation: {
121
+ BEGIN: "FLOW_1",
122
+ FLOW_1: {
123
+ startState: "VIEW_1",
124
+ VIEW_1: {
125
+ state_type: "VIEW",
126
+ ref: "test",
127
+ transitions: {
128
+ "*": "END_Done",
129
+ },
130
+ },
131
+ },
132
+ },
133
+ schema: {
134
+ foo: {
135
+ bar: {
136
+ baz: {
137
+ type: "StringType",
138
+ validation: [
139
+ {
140
+ type: "required",
141
+ },
142
+ ],
143
+ },
144
+ },
145
+ },
146
+ },
147
+ });
148
+
149
+ expect(result).toBeDefined();
150
+ expect(result?.value).toMatchInlineSnapshot(`
151
+ {
152
+ "id": "test-flow",
153
+ "navigation": {
154
+ "BEGIN": "FLOW_1",
155
+ "FLOW_1": {
156
+ "VIEW_1": {
157
+ "ref": "test",
158
+ "state_type": "VIEW",
159
+ "transitions": {
160
+ "*": "END_Done",
161
+ },
162
+ },
163
+ "startState": "VIEW_1",
164
+ },
165
+ },
166
+ "schema": {
167
+ "ROOT": {
168
+ "foo": {
169
+ "type": "fooType",
170
+ },
171
+ },
172
+ "barType": {
173
+ "baz": {
174
+ "type": "StringType",
175
+ "validation": [
176
+ {
177
+ "type": "required",
178
+ },
179
+ ],
180
+ },
181
+ },
182
+ "fooType": {
183
+ "bar": {
184
+ "type": "barType",
185
+ },
186
+ },
187
+ },
188
+ "views": [],
189
+ }
190
+ `);
191
+ });
192
+
193
+ test("compiles mixed DSL and non-DSL views", async () => {
194
+ const compiler = new DSLCompiler();
195
+ const dslView = (
196
+ <object>
197
+ <property name="foo">bar</property>
198
+ </object>
199
+ );
200
+ const result = await compiler.serialize({
201
+ id: "test-flow",
202
+ views: [
203
+ {
204
+ id: "foo",
205
+ type: "bar",
206
+ info: {
207
+ asset: {
208
+ id: "info",
209
+ type: "baz",
210
+ },
211
+ },
212
+ },
213
+ dslView,
214
+ ],
215
+ navigation: {
216
+ BEGIN: "FLOW_1",
217
+ FLOW_1: {
218
+ startState: "VIEW_1",
219
+ VIEW_1: {
220
+ state_type: "VIEW",
221
+ ref: "test",
222
+ transitions: {
223
+ "*": "END_Done",
224
+ },
225
+ },
226
+ },
227
+ },
228
+ });
229
+
230
+ expect(result).toBeDefined();
231
+ expect(result?.value).toMatchInlineSnapshot(`
232
+ {
233
+ "id": "test-flow",
234
+ "navigation": {
235
+ "BEGIN": "FLOW_1",
236
+ "FLOW_1": {
237
+ "VIEW_1": {
238
+ "ref": "test",
239
+ "state_type": "VIEW",
240
+ "transitions": {
241
+ "*": "END_Done",
242
+ },
243
+ },
244
+ "startState": "VIEW_1",
245
+ },
246
+ },
247
+ "views": [
248
+ {
249
+ "id": "foo",
250
+ "info": {
251
+ "asset": {
252
+ "id": "info",
253
+ "type": "baz",
254
+ },
255
+ },
256
+ "type": "bar",
257
+ },
258
+ {
259
+ "foo": "bar",
260
+ },
261
+ ],
262
+ }
263
+ `);
264
+ });