@player-tools/fluent 0.12.1--canary.241.6077
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.
- package/dist/cjs/index.cjs +2396 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/index.legacy-esm.js +2276 -0
- package/dist/index.mjs +2276 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +38 -0
- package/src/core/base-builder/__tests__/fluent-builder-base.test.ts +2423 -0
- package/src/core/base-builder/__tests__/fluent-partial.test.ts +179 -0
- package/src/core/base-builder/__tests__/id-generator.test.ts +658 -0
- package/src/core/base-builder/__tests__/registry.test.ts +534 -0
- package/src/core/base-builder/__tests__/resolution-mixed-arrays.test.ts +319 -0
- package/src/core/base-builder/__tests__/resolution-pipeline.test.ts +416 -0
- package/src/core/base-builder/__tests__/resolution-switches.test.ts +468 -0
- package/src/core/base-builder/__tests__/resolution-templates.test.ts +255 -0
- package/src/core/base-builder/__tests__/switch.test.ts +815 -0
- package/src/core/base-builder/__tests__/template.test.ts +596 -0
- package/src/core/base-builder/__tests__/value-extraction.test.ts +200 -0
- package/src/core/base-builder/__tests__/value-storage.test.ts +459 -0
- package/src/core/base-builder/conditional/index.ts +64 -0
- package/src/core/base-builder/context.ts +152 -0
- package/src/core/base-builder/errors.ts +69 -0
- package/src/core/base-builder/fluent-builder-base.ts +308 -0
- package/src/core/base-builder/guards.ts +137 -0
- package/src/core/base-builder/id/generator.ts +290 -0
- package/src/core/base-builder/id/registry.ts +152 -0
- package/src/core/base-builder/index.ts +72 -0
- package/src/core/base-builder/resolution/path-resolver.ts +116 -0
- package/src/core/base-builder/resolution/pipeline.ts +103 -0
- package/src/core/base-builder/resolution/steps/__tests__/nested-asset-wrappers.test.ts +206 -0
- package/src/core/base-builder/resolution/steps/asset-id.ts +77 -0
- package/src/core/base-builder/resolution/steps/asset-wrappers.ts +64 -0
- package/src/core/base-builder/resolution/steps/builders.ts +84 -0
- package/src/core/base-builder/resolution/steps/mixed-arrays.ts +95 -0
- package/src/core/base-builder/resolution/steps/nested-asset-wrappers.ts +124 -0
- package/src/core/base-builder/resolution/steps/static-values.ts +35 -0
- package/src/core/base-builder/resolution/steps/switches.ts +71 -0
- package/src/core/base-builder/resolution/steps/templates.ts +40 -0
- package/src/core/base-builder/resolution/value-resolver.ts +333 -0
- package/src/core/base-builder/storage/auxiliary-storage.ts +82 -0
- package/src/core/base-builder/storage/value-storage.ts +282 -0
- package/src/core/base-builder/types.ts +266 -0
- package/src/core/base-builder/utils.ts +10 -0
- package/src/core/flow/__tests__/index.test.ts +292 -0
- package/src/core/flow/index.ts +118 -0
- package/src/core/index.ts +8 -0
- package/src/core/mocks/generated/action.builder.ts +92 -0
- package/src/core/mocks/generated/choice-item.builder.ts +120 -0
- package/src/core/mocks/generated/choice.builder.ts +134 -0
- package/src/core/mocks/generated/collection.builder.ts +93 -0
- package/src/core/mocks/generated/field-collection.builder.ts +86 -0
- package/src/core/mocks/generated/index.ts +10 -0
- package/src/core/mocks/generated/info.builder.ts +64 -0
- package/src/core/mocks/generated/input.builder.ts +63 -0
- package/src/core/mocks/generated/overview-collection.builder.ts +65 -0
- package/src/core/mocks/generated/splash-collection.builder.ts +93 -0
- package/src/core/mocks/generated/text.builder.ts +47 -0
- package/src/core/mocks/index.ts +1 -0
- package/src/core/mocks/types/action.ts +92 -0
- package/src/core/mocks/types/choice.ts +129 -0
- package/src/core/mocks/types/collection.ts +140 -0
- package/src/core/mocks/types/info.ts +7 -0
- package/src/core/mocks/types/input.ts +7 -0
- package/src/core/mocks/types/text.ts +5 -0
- package/src/core/schema/__tests__/index.test.ts +127 -0
- package/src/core/schema/index.ts +195 -0
- package/src/core/schema/types.ts +7 -0
- package/src/core/switch/__tests__/index.test.ts +156 -0
- package/src/core/switch/index.ts +81 -0
- package/src/core/tagged-template/README.md +448 -0
- package/src/core/tagged-template/__tests__/extract-bindings-from-schema.test.ts +207 -0
- package/src/core/tagged-template/__tests__/index.test.ts +190 -0
- package/src/core/tagged-template/__tests__/schema-std-integration.test.ts +580 -0
- package/src/core/tagged-template/binding.ts +95 -0
- package/src/core/tagged-template/expression.ts +92 -0
- package/src/core/tagged-template/extract-bindings-from-schema.ts +120 -0
- package/src/core/tagged-template/index.ts +5 -0
- package/src/core/tagged-template/std.ts +472 -0
- package/src/core/tagged-template/types.ts +123 -0
- package/src/core/template/__tests__/index.test.ts +380 -0
- package/src/core/template/index.ts +196 -0
- package/src/core/utils/index.ts +160 -0
- package/src/fp/README.md +411 -0
- package/src/fp/__tests__/index.test.ts +1178 -0
- package/src/fp/index.ts +386 -0
- package/src/gen/common.ts +15 -0
- package/src/index.ts +5 -0
- package/src/types.ts +203 -0
- package/types/core/base-builder/conditional/index.d.ts +21 -0
- package/types/core/base-builder/context.d.ts +39 -0
- package/types/core/base-builder/errors.d.ts +45 -0
- package/types/core/base-builder/fluent-builder-base.d.ts +147 -0
- package/types/core/base-builder/guards.d.ts +58 -0
- package/types/core/base-builder/id/generator.d.ts +69 -0
- package/types/core/base-builder/id/registry.d.ts +93 -0
- package/types/core/base-builder/index.d.ts +9 -0
- package/types/core/base-builder/resolution/path-resolver.d.ts +15 -0
- package/types/core/base-builder/resolution/pipeline.d.ts +27 -0
- package/types/core/base-builder/resolution/steps/asset-id.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/asset-wrappers.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/builders.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/mixed-arrays.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/nested-asset-wrappers.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/static-values.d.ts +14 -0
- package/types/core/base-builder/resolution/steps/switches.d.ts +15 -0
- package/types/core/base-builder/resolution/steps/templates.d.ts +14 -0
- package/types/core/base-builder/resolution/value-resolver.d.ts +62 -0
- package/types/core/base-builder/storage/auxiliary-storage.d.ts +50 -0
- package/types/core/base-builder/storage/value-storage.d.ts +82 -0
- package/types/core/base-builder/types.d.ts +183 -0
- package/types/core/base-builder/utils.d.ts +2 -0
- package/types/core/flow/index.d.ts +23 -0
- package/types/core/index.d.ts +8 -0
- package/types/core/mocks/index.d.ts +2 -0
- package/types/core/mocks/types/action.d.ts +58 -0
- package/types/core/mocks/types/choice.d.ts +95 -0
- package/types/core/mocks/types/collection.d.ts +102 -0
- package/types/core/mocks/types/info.d.ts +7 -0
- package/types/core/mocks/types/input.d.ts +7 -0
- package/types/core/mocks/types/text.d.ts +5 -0
- package/types/core/schema/index.d.ts +34 -0
- package/types/core/schema/types.d.ts +5 -0
- package/types/core/switch/index.d.ts +21 -0
- package/types/core/tagged-template/binding.d.ts +19 -0
- package/types/core/tagged-template/expression.d.ts +11 -0
- package/types/core/tagged-template/extract-bindings-from-schema.d.ts +7 -0
- package/types/core/tagged-template/index.d.ts +6 -0
- package/types/core/tagged-template/std.d.ts +174 -0
- package/types/core/tagged-template/types.d.ts +69 -0
- package/types/core/template/index.d.ts +97 -0
- package/types/core/utils/index.d.ts +47 -0
- package/types/fp/index.d.ts +149 -0
- package/types/gen/common.d.ts +6 -0
- package/types/index.d.ts +3 -0
- package/types/types.d.ts +163 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import type { Schema, Language } from "@player-ui/types";
|
|
2
|
+
import { SyncWaterfallHook } from "tapable-ts";
|
|
3
|
+
import { dequal } from "dequal";
|
|
4
|
+
import { SchemaGeneratorInput } from "./types";
|
|
5
|
+
|
|
6
|
+
/** Symbol to indicate that a schema node should be generated with a different name */
|
|
7
|
+
export const SchemaTypeName = Symbol.for("Schema Rename");
|
|
8
|
+
|
|
9
|
+
export type LoggingInterface = Pick<Console, "warn" | "error" | "log">;
|
|
10
|
+
|
|
11
|
+
interface SchemaChildren {
|
|
12
|
+
/** Object property that will be used to create the intermediate type */
|
|
13
|
+
name: string;
|
|
14
|
+
|
|
15
|
+
/** Object properties children that will be parsed */
|
|
16
|
+
child: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type SchemaNode = (Schema.DataType | Language.DataTypeRef) & {
|
|
20
|
+
/** Overwrite the name of the generated type */
|
|
21
|
+
[SchemaTypeName]?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
interface GeneratedDataType {
|
|
25
|
+
/** The SchemaNode that was generated */
|
|
26
|
+
node: SchemaNode;
|
|
27
|
+
/** How many times it has been generated */
|
|
28
|
+
count: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Type Guard for the `Schema.DataType` and `Language.DataTypeRef` type
|
|
33
|
+
* A bit hacky but since `Schema.Schema` must have a `Schema.DataType` as
|
|
34
|
+
* the final product we have to call it that even if it is a `Language.DataTypeRef`
|
|
35
|
+
*/
|
|
36
|
+
const isTypeDef = (property: SchemaNode): property is Schema.DataType => {
|
|
37
|
+
return (property as Schema.DataType).type !== undefined;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Generator for `Schema.Schema` Objects
|
|
42
|
+
*/
|
|
43
|
+
export class SchemaGenerator {
|
|
44
|
+
private children: SchemaChildren[] = [];
|
|
45
|
+
private generatedDataTypes: Map<string, GeneratedDataType> = new Map();
|
|
46
|
+
private typeNameCache: Map<string, string> = new Map();
|
|
47
|
+
private logger: LoggingInterface;
|
|
48
|
+
|
|
49
|
+
public hooks = {
|
|
50
|
+
createSchemaNode: new SyncWaterfallHook<
|
|
51
|
+
[
|
|
52
|
+
node: Schema.DataType,
|
|
53
|
+
originalProperty: Record<string | symbol, unknown>,
|
|
54
|
+
]
|
|
55
|
+
>(),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
constructor(logger?: LoggingInterface) {
|
|
59
|
+
this.logger = logger ?? console;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Converts an object to a `Schema.Schema` representation
|
|
64
|
+
* Optimized to minimize object operations and memory allocations
|
|
65
|
+
*/
|
|
66
|
+
public toSchema = (schema: SchemaGeneratorInput): Schema.Schema => {
|
|
67
|
+
// Clear state efficiently
|
|
68
|
+
this.children.length = 0;
|
|
69
|
+
this.generatedDataTypes.clear();
|
|
70
|
+
this.typeNameCache.clear();
|
|
71
|
+
|
|
72
|
+
const newSchema: Schema.Schema = {
|
|
73
|
+
ROOT: {},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Pre-allocate arrays and use for...in for better performance
|
|
77
|
+
const rootKeys = Object.keys(schema);
|
|
78
|
+
for (let i = 0; i < rootKeys.length; i++) {
|
|
79
|
+
const property = rootKeys[i];
|
|
80
|
+
const subType = schema[property] as SchemaNode;
|
|
81
|
+
newSchema.ROOT[property] = this.hooks.createSchemaNode.call(
|
|
82
|
+
this.processChild(property, subType),
|
|
83
|
+
subType as unknown as Record<string | symbol, unknown>,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Process children using optimized iteration
|
|
88
|
+
while (this.children.length > 0) {
|
|
89
|
+
const { name, child } = this.children.pop()!;
|
|
90
|
+
const typeDef: Record<string, Schema.DataType> = {};
|
|
91
|
+
|
|
92
|
+
const childKeys = Object.keys(child);
|
|
93
|
+
for (let i = 0; i < childKeys.length; i++) {
|
|
94
|
+
const property = childKeys[i];
|
|
95
|
+
const subType = child[property] as SchemaNode;
|
|
96
|
+
typeDef[property] = this.hooks.createSchemaNode.call(
|
|
97
|
+
this.processChild(property, subType),
|
|
98
|
+
subType as unknown as Record<string | symbol, unknown>,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
newSchema[name] = typeDef;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return newSchema;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
private processChild(property: string, subType: SchemaNode): Schema.DataType {
|
|
108
|
+
if (isTypeDef(subType)) {
|
|
109
|
+
return subType;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let intermediateType: Schema.DataType;
|
|
113
|
+
let child: Record<string, unknown>;
|
|
114
|
+
|
|
115
|
+
if (Array.isArray(subType)) {
|
|
116
|
+
if (subType.length > 1) {
|
|
117
|
+
this.logger.warn(
|
|
118
|
+
`Type ${property} has multiple types in array, should only contain one top level object type. Only taking first defined type`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const subTypeName = subType[0][SchemaTypeName] ?? property;
|
|
123
|
+
intermediateType = this.makePlaceholderArrayType(subTypeName);
|
|
124
|
+
child = subType[0] as Record<string, unknown>;
|
|
125
|
+
} else {
|
|
126
|
+
const subTypeName = subType[SchemaTypeName] ?? property;
|
|
127
|
+
intermediateType = this.makePlaceholderType(subTypeName);
|
|
128
|
+
child = subType as unknown as Record<string, unknown>;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const typeName = intermediateType.type;
|
|
132
|
+
|
|
133
|
+
if (this.generatedDataTypes.has(typeName)) {
|
|
134
|
+
const generatedType = this.generatedDataTypes.get(typeName)!;
|
|
135
|
+
|
|
136
|
+
// Use deep equality check to ensure types are actually different
|
|
137
|
+
if (
|
|
138
|
+
!dequal(child, this.generatedDataTypes.get(typeName)?.node as object)
|
|
139
|
+
) {
|
|
140
|
+
generatedType.count += 1;
|
|
141
|
+
const newTypeName = `${typeName}${generatedType.count}`;
|
|
142
|
+
intermediateType = {
|
|
143
|
+
...intermediateType,
|
|
144
|
+
type: newTypeName,
|
|
145
|
+
};
|
|
146
|
+
this.logger.warn(
|
|
147
|
+
`WARNING: Generated two intermediate types with the name: ${typeName} that are of different shapes, using artificial type ${newTypeName}`,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
// Add new type mapping for the new artificial type
|
|
151
|
+
this.generatedDataTypes.set(newTypeName, {
|
|
152
|
+
node: subType,
|
|
153
|
+
count: 1,
|
|
154
|
+
});
|
|
155
|
+
this.children.push({ name: newTypeName, child });
|
|
156
|
+
return intermediateType;
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
this.generatedDataTypes.set(typeName, {
|
|
160
|
+
node: subType,
|
|
161
|
+
count: 1,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
this.children.push({ name: intermediateType.type, child });
|
|
166
|
+
return intermediateType;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Cached type name generation
|
|
171
|
+
*/
|
|
172
|
+
private makePlaceholderType = (typeName: string): Schema.DataType => {
|
|
173
|
+
let cachedName = this.typeNameCache.get(typeName);
|
|
174
|
+
if (!cachedName) {
|
|
175
|
+
cachedName = `${typeName}Type`;
|
|
176
|
+
this.typeNameCache.set(typeName, cachedName);
|
|
177
|
+
}
|
|
178
|
+
return { type: cachedName };
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Cached array type name generation
|
|
183
|
+
*/
|
|
184
|
+
private makePlaceholderArrayType(typeName: string): Schema.DataType {
|
|
185
|
+
let cachedName = this.typeNameCache.get(typeName);
|
|
186
|
+
if (!cachedName) {
|
|
187
|
+
cachedName = `${typeName}Type`;
|
|
188
|
+
this.typeNameCache.set(typeName, cachedName);
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
type: cachedName,
|
|
192
|
+
isArray: true,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach } from "vitest";
|
|
2
|
+
import { switch_ } from "../index";
|
|
3
|
+
import type { AssetWrapperOrSwitch } from "@player-ui/types";
|
|
4
|
+
import { text } from "../../mocks";
|
|
5
|
+
import { expression } from "../../tagged-template";
|
|
6
|
+
import { resetGlobalIdSet } from "../../base-builder";
|
|
7
|
+
|
|
8
|
+
describe("Switch", () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
resetGlobalIdSet();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("create a static switch by default", () => {
|
|
14
|
+
const cases = [{ case: true, asset: text().withValue("Hello") }];
|
|
15
|
+
const result = switch_({ cases })({ parentId: "parent" });
|
|
16
|
+
|
|
17
|
+
const expected: AssetWrapperOrSwitch = {
|
|
18
|
+
staticSwitch: [
|
|
19
|
+
{
|
|
20
|
+
case: true,
|
|
21
|
+
asset: {
|
|
22
|
+
value: "Hello",
|
|
23
|
+
type: "text",
|
|
24
|
+
id: "parent-staticSwitch-0-text",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
expect(result).toEqual(expected);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("create a dynamic switch when isDynamic is true", () => {
|
|
34
|
+
const cases = [{ case: true, asset: text().withValue("Hello") }];
|
|
35
|
+
const result = switch_({ cases, isDynamic: true })({ parentId: "parent" });
|
|
36
|
+
|
|
37
|
+
const expected: AssetWrapperOrSwitch = {
|
|
38
|
+
dynamicSwitch: [
|
|
39
|
+
{
|
|
40
|
+
case: true,
|
|
41
|
+
asset: {
|
|
42
|
+
value: "Hello",
|
|
43
|
+
type: "text",
|
|
44
|
+
id: "parent-dynamicSwitch-0-text",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
expect(result).toEqual(expected);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("convert string case expressions to strings", () => {
|
|
54
|
+
const cases = [{ case: "foo == true", asset: text().withValue("Hello") }];
|
|
55
|
+
const result = switch_({ cases })({ parentId: "parent" });
|
|
56
|
+
|
|
57
|
+
const expected: AssetWrapperOrSwitch = {
|
|
58
|
+
staticSwitch: [
|
|
59
|
+
{
|
|
60
|
+
case: "foo == true",
|
|
61
|
+
asset: {
|
|
62
|
+
value: "Hello",
|
|
63
|
+
type: "text",
|
|
64
|
+
id: "parent-staticSwitch-0-text",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
expect(result).toEqual(expected);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("handle TaggedTemplateValue case expressions", () => {
|
|
74
|
+
const templateValue = expression`foo == true`;
|
|
75
|
+
const cases = [{ case: templateValue, asset: text().withValue("Hello") }];
|
|
76
|
+
const result = switch_({ cases })({ parentId: "parent" });
|
|
77
|
+
|
|
78
|
+
const expected: AssetWrapperOrSwitch = {
|
|
79
|
+
staticSwitch: [
|
|
80
|
+
{
|
|
81
|
+
case: "@[foo == true]@",
|
|
82
|
+
asset: {
|
|
83
|
+
value: "Hello",
|
|
84
|
+
type: "text",
|
|
85
|
+
id: "parent-staticSwitch-0-text",
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
expect(result).toEqual(expected);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("preserve ids for assets with ids", () => {
|
|
95
|
+
const assetWithId = text().withId("custom-id").withValue("Hello");
|
|
96
|
+
const result = switch_({
|
|
97
|
+
cases: [{ case: true, asset: assetWithId }],
|
|
98
|
+
})({ parentId: "parent" });
|
|
99
|
+
|
|
100
|
+
const expected: AssetWrapperOrSwitch = {
|
|
101
|
+
staticSwitch: [
|
|
102
|
+
{
|
|
103
|
+
case: true,
|
|
104
|
+
asset: { value: "Hello", type: "text", id: "custom-id" },
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
expect(result).toEqual(expected);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("handle multiple cases", () => {
|
|
113
|
+
const firstAsset = text().withValue("First");
|
|
114
|
+
const secondAsset = text().withValue("Second");
|
|
115
|
+
const defaultAsset = text().withValue("Default");
|
|
116
|
+
|
|
117
|
+
const result = switch_({
|
|
118
|
+
cases: [
|
|
119
|
+
{ case: '{{name.first}} == "John"', asset: firstAsset },
|
|
120
|
+
{ case: '{{name.first}} == "Jane"', asset: secondAsset },
|
|
121
|
+
{ case: true, asset: defaultAsset },
|
|
122
|
+
],
|
|
123
|
+
})({ parentId: "parent" });
|
|
124
|
+
|
|
125
|
+
const expected: AssetWrapperOrSwitch = {
|
|
126
|
+
staticSwitch: [
|
|
127
|
+
{
|
|
128
|
+
case: '{{name.first}} == "John"',
|
|
129
|
+
asset: {
|
|
130
|
+
value: "First",
|
|
131
|
+
type: "text",
|
|
132
|
+
id: "parent-staticSwitch-0-text",
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
case: '{{name.first}} == "Jane"',
|
|
137
|
+
asset: {
|
|
138
|
+
value: "Second",
|
|
139
|
+
type: "text",
|
|
140
|
+
id: "parent-staticSwitch-1-text",
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
case: true,
|
|
145
|
+
asset: {
|
|
146
|
+
value: "Default",
|
|
147
|
+
type: "text",
|
|
148
|
+
id: "parent-staticSwitch-2-text",
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
expect(result).toEqual(expected);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { Asset, AssetWrapper } from "@player-ui/types";
|
|
2
|
+
import {
|
|
3
|
+
type BaseBuildContext,
|
|
4
|
+
genId,
|
|
5
|
+
isFluentBuilder,
|
|
6
|
+
BranchTypes,
|
|
7
|
+
} from "../base-builder";
|
|
8
|
+
import {
|
|
9
|
+
isTaggedTemplateValue,
|
|
10
|
+
type TaggedTemplateValue,
|
|
11
|
+
} from "../tagged-template";
|
|
12
|
+
|
|
13
|
+
type CaseExpression = boolean | string | TaggedTemplateValue;
|
|
14
|
+
|
|
15
|
+
interface SwitchCase<
|
|
16
|
+
T extends Asset,
|
|
17
|
+
C extends BaseBuildContext = BaseBuildContext,
|
|
18
|
+
> {
|
|
19
|
+
readonly case: CaseExpression;
|
|
20
|
+
readonly asset: T | { build(context?: C): T };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface SwitchArgs<
|
|
24
|
+
T extends Asset,
|
|
25
|
+
C extends BaseBuildContext = BaseBuildContext,
|
|
26
|
+
> {
|
|
27
|
+
readonly cases: ReadonlyArray<SwitchCase<T, C>>;
|
|
28
|
+
readonly isDynamic?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function processCaseExpression(exp: CaseExpression): string | boolean {
|
|
32
|
+
if (typeof exp === "boolean") {
|
|
33
|
+
return exp;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (isTaggedTemplateValue(exp)) {
|
|
37
|
+
return exp.toString();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return String(exp);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Creates a switch configuration for conditionally selecting an asset
|
|
45
|
+
* @see https://player-ui.github.io/next/content/assets-views/#switches
|
|
46
|
+
*/
|
|
47
|
+
export const switch_ =
|
|
48
|
+
<T extends Asset, C extends BaseBuildContext = BaseBuildContext>({
|
|
49
|
+
cases,
|
|
50
|
+
isDynamic = false,
|
|
51
|
+
}: SwitchArgs<T, C>) =>
|
|
52
|
+
(ctx: C, caseOffset: number = 0): AssetWrapper => {
|
|
53
|
+
const switchType = isDynamic ? "dynamic" : "static";
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
[`${switchType}Switch`]: cases.map((c, index) => {
|
|
57
|
+
const caseParentCtx: C = {
|
|
58
|
+
...ctx,
|
|
59
|
+
parentId: ctx.parentId,
|
|
60
|
+
branch: {
|
|
61
|
+
type: BranchTypes.SWITCH,
|
|
62
|
+
kind: switchType,
|
|
63
|
+
index: caseOffset + index,
|
|
64
|
+
},
|
|
65
|
+
} as C;
|
|
66
|
+
|
|
67
|
+
const asset: T =
|
|
68
|
+
isFluentBuilder(c.asset) && "build" in c.asset
|
|
69
|
+
? (c.asset.build(caseParentCtx) as T)
|
|
70
|
+
: (c.asset as T);
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
case: processCaseExpression(c.case),
|
|
74
|
+
asset: {
|
|
75
|
+
...asset,
|
|
76
|
+
id: asset.id ?? genId(caseParentCtx),
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}),
|
|
80
|
+
} as AssetWrapper;
|
|
81
|
+
};
|