@player-ui/player 0.8.0-next.3 → 0.8.0-next.5

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 (37) hide show
  1. package/dist/Player.native.js +240 -252
  2. package/dist/Player.native.js.map +1 -1
  3. package/dist/cjs/index.cjs +208 -268
  4. package/dist/cjs/index.cjs.map +1 -1
  5. package/dist/index.legacy-esm.js +206 -270
  6. package/dist/index.mjs +206 -270
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +4 -4
  9. package/src/plugins/default-view-plugin.ts +4 -0
  10. package/src/view/__tests__/view.immutable.test.ts +8 -1
  11. package/src/view/__tests__/view.test.ts +4 -0
  12. package/src/view/parser/__tests__/parser.test.ts +16 -1
  13. package/src/view/parser/index.ts +46 -262
  14. package/src/view/parser/utils.ts +23 -3
  15. package/src/view/plugins/__tests__/__snapshots__/asset.test.ts.snap +215 -0
  16. package/src/view/plugins/__tests__/__snapshots__/multi-node.test.ts.snap +67 -0
  17. package/src/view/plugins/__tests__/__snapshots__/template.test.ts.snap +56 -54
  18. package/src/view/plugins/__tests__/applicability.test.ts +24 -16
  19. package/src/view/plugins/__tests__/asset.test.ts +140 -0
  20. package/src/view/plugins/__tests__/multi-node.test.ts +38 -0
  21. package/src/view/plugins/__tests__/template.test.ts +48 -388
  22. package/src/view/plugins/applicability.ts +39 -23
  23. package/src/view/plugins/asset.ts +42 -0
  24. package/src/view/plugins/index.ts +3 -1
  25. package/src/view/plugins/multi-node.ts +73 -0
  26. package/src/view/plugins/switch.ts +81 -50
  27. package/src/view/plugins/{template-plugin.ts → template.ts} +38 -24
  28. package/src/view/resolver/__tests__/edgecases.test.ts +14 -1
  29. package/src/view/view.ts +2 -7
  30. package/types/view/parser/index.d.ts +6 -11
  31. package/types/view/parser/utils.d.ts +11 -2
  32. package/types/view/plugins/applicability.d.ts +1 -0
  33. package/types/view/plugins/asset.d.ts +8 -0
  34. package/types/view/plugins/index.d.ts +3 -1
  35. package/types/view/plugins/multi-node.d.ts +8 -0
  36. package/types/view/plugins/switch.d.ts +2 -1
  37. package/types/view/plugins/{template-plugin.d.ts → template.d.ts} +2 -2
@@ -0,0 +1,73 @@
1
+ import { ViewInstance, ViewPlugin } from "../view";
2
+ import type {
3
+ Parser,
4
+ Node,
5
+ ParseObjectOptions,
6
+ ParseObjectChildOptions,
7
+ } from "../parser";
8
+ import { NodeType } from "../parser";
9
+ import { hasTemplateValues, hasTemplateKey } from "../parser/utils";
10
+
11
+ /** A view plugin to resolve multi nodes */
12
+ export default class MultiNodePlugin implements ViewPlugin {
13
+ applyParser(parser: Parser) {
14
+ parser.hooks.parseNode.tap(
15
+ "multi-node",
16
+ (
17
+ obj: any,
18
+ nodeType: Node.ChildrenTypes,
19
+ options: ParseObjectOptions,
20
+ childOptions?: ParseObjectChildOptions,
21
+ ) => {
22
+ if (
23
+ childOptions &&
24
+ !hasTemplateKey(childOptions.key) &&
25
+ Array.isArray(obj)
26
+ ) {
27
+ const values = obj
28
+ .map((childVal) =>
29
+ parser.parseObject(childVal, NodeType.Value, options),
30
+ )
31
+ .filter((child): child is Node.Node => !!child);
32
+
33
+ if (!values.length) {
34
+ return [];
35
+ }
36
+
37
+ const multiNode = parser.createASTNode(
38
+ {
39
+ type: NodeType.MultiNode,
40
+ override: !hasTemplateValues(
41
+ childOptions.parentObj,
42
+ childOptions.key,
43
+ ),
44
+ values,
45
+ },
46
+ obj,
47
+ );
48
+
49
+ if (!multiNode) {
50
+ return [];
51
+ }
52
+
53
+ if (multiNode.type === NodeType.MultiNode) {
54
+ multiNode.values.forEach((v) => {
55
+ v.parent = multiNode;
56
+ });
57
+ }
58
+
59
+ return [
60
+ {
61
+ path: [...childOptions.path, childOptions.key],
62
+ value: multiNode,
63
+ },
64
+ ];
65
+ }
66
+ },
67
+ );
68
+ }
69
+
70
+ apply(view: ViewInstance) {
71
+ view.hooks.parser.tap("multi-node", this.applyParser.bind(this));
72
+ }
73
+ }
@@ -1,8 +1,14 @@
1
+ import { ViewInstance, ViewPlugin } from "../view";
1
2
  import type { Options } from "./options";
2
- import type { Parser, Node, ParseObjectOptions } from "../parser";
3
+ import type {
4
+ Parser,
5
+ Node,
6
+ ParseObjectOptions,
7
+ ParseObjectChildOptions,
8
+ } from "../parser";
3
9
  import { EMPTY_NODE, NodeType } from "../parser";
4
10
  import type { Resolver } from "../resolver";
5
- import { ViewInstance, ViewPlugin } from "../view";
11
+ import { hasSwitchKey } from "../parser/utils";
6
12
 
7
13
  /** A view plugin to resolve switches */
8
14
  export default class SwitchPlugin implements ViewPlugin {
@@ -23,6 +29,14 @@ export default class SwitchPlugin implements ViewPlugin {
23
29
  return EMPTY_NODE;
24
30
  }
25
31
 
32
+ private isSwitch(obj: any) {
33
+ return (
34
+ obj &&
35
+ (Object.prototype.hasOwnProperty.call(obj, "dynamicSwitch") ||
36
+ Object.prototype.hasOwnProperty.call(obj, "staticSwitch"))
37
+ );
38
+ }
39
+
26
40
  applyParser(parser: Parser) {
27
41
  /** Switches resolved during the parsing phase are static */
28
42
  parser.hooks.onCreateASTNode.tap("switch", (node) => {
@@ -33,75 +47,92 @@ export default class SwitchPlugin implements ViewPlugin {
33
47
  return node;
34
48
  });
35
49
 
36
- parser.hooks.determineNodeType.tap("switch", (obj) => {
37
- if (
38
- Object.prototype.hasOwnProperty.call(obj, "dynamicSwitch") ||
39
- Object.prototype.hasOwnProperty.call(obj, "staticSwitch")
40
- ) {
41
- return NodeType.Switch;
42
- }
43
- });
44
-
45
50
  parser.hooks.parseNode.tap(
46
51
  "switch",
47
52
  (
48
53
  obj: any,
49
54
  _nodeType: Node.ChildrenTypes,
50
55
  options: ParseObjectOptions,
51
- determinedNodeType: null | NodeType,
56
+ childOptions?: ParseObjectChildOptions,
52
57
  ) => {
53
- if (determinedNodeType === NodeType.Switch) {
54
- const dynamic = "dynamicSwitch" in obj;
55
- const switchContent =
56
- "dynamicSwitch" in obj ? obj.dynamicSwitch : obj.staticSwitch;
57
-
58
- const cases: Node.SwitchCase[] = [];
59
-
60
- switchContent.forEach(
61
- (switchCase: {
62
- [x: string]: any;
63
- /**
64
- *
65
- */
66
- case: any;
67
- }) => {
68
- const { case: switchCaseExpr, ...switchBody } = switchCase;
69
- const value = parser.parseObject(
70
- switchBody,
71
- NodeType.Value,
72
- options,
73
- );
74
-
75
- if (value) {
76
- cases.push({
77
- case: switchCaseExpr,
78
- value: value as Node.Value,
79
- });
80
- }
81
- },
82
- );
83
-
84
- const switchAST = parser.hooks.onCreateASTNode.call(
58
+ if (
59
+ this.isSwitch(obj) ||
60
+ (childOptions && hasSwitchKey(childOptions.key))
61
+ ) {
62
+ const objToParse =
63
+ childOptions && hasSwitchKey(childOptions.key)
64
+ ? { [childOptions.key]: obj }
65
+ : obj;
66
+ const dynamic = "dynamicSwitch" in objToParse;
67
+ const switchContent = dynamic
68
+ ? objToParse.dynamicSwitch
69
+ : objToParse.staticSwitch;
70
+
71
+ const cases: Node.SwitchCase[] = switchContent
72
+ .map(
73
+ (switchCase: {
74
+ [x: string]: any;
75
+ /**
76
+ *
77
+ */
78
+ case: any;
79
+ }) => {
80
+ const { case: switchCaseExpr, ...switchBody } = switchCase;
81
+ const value = parser.parseObject(
82
+ switchBody,
83
+ NodeType.Value,
84
+ options,
85
+ );
86
+
87
+ if (value) {
88
+ return {
89
+ case: switchCaseExpr,
90
+ value: value as Node.Value,
91
+ };
92
+ }
93
+
94
+ return;
95
+ },
96
+ )
97
+ .filter(Boolean);
98
+
99
+ const switchAST = parser.createASTNode(
85
100
  {
86
101
  type: NodeType.Switch,
87
102
  dynamic,
88
103
  cases,
89
104
  },
90
- obj,
105
+ objToParse,
91
106
  );
92
107
 
93
- if (switchAST?.type === NodeType.Switch) {
108
+ if (!switchAST || switchAST.type === NodeType.Empty) {
109
+ return childOptions ? [] : null;
110
+ }
111
+
112
+ if (switchAST.type === NodeType.Switch) {
94
113
  switchAST.cases.forEach((sCase) => {
95
- // eslint-disable-next-line no-param-reassign
96
114
  sCase.value.parent = switchAST;
97
115
  });
98
116
  }
99
117
 
100
- if (switchAST?.type === NodeType.Empty) {
101
- return null;
118
+ if (childOptions) {
119
+ let path = [...childOptions.path, childOptions.key];
120
+ let value: any = switchAST;
121
+
122
+ if (
123
+ switchAST.type === NodeType.Value &&
124
+ switchAST.children?.length === 1 &&
125
+ switchAST.value === undefined
126
+ ) {
127
+ const firstChild = switchAST.children[0];
128
+ path = [...path, ...firstChild.path];
129
+ value = firstChild.value;
130
+ }
131
+
132
+ return [{ path, value }];
102
133
  }
103
134
 
104
- return switchAST ?? null;
135
+ return switchAST;
105
136
  }
106
137
  },
107
138
  );
@@ -1,9 +1,16 @@
1
1
  import { SyncWaterfallHook } from "tapable-ts";
2
- import type { Node, ParseObjectOptions, Parser } from "../parser";
2
+ import type { Template } from "@player-ui/types";
3
+ import type {
4
+ Node,
5
+ ParseObjectOptions,
6
+ ParseObjectChildOptions,
7
+ Parser,
8
+ } from "../parser";
3
9
  import { NodeType } from "../parser";
10
+ import { ViewInstance, ViewPlugin } from "../view";
4
11
  import type { Options } from "./options";
5
12
  import type { Resolver } from "../resolver";
6
- import { ViewInstance, ViewPlugin } from "../view";
13
+ import { hasTemplateKey } from "../parser/utils";
7
14
 
8
15
  export interface TemplateItemInfo {
9
16
  /** The index of the data for the current iteration of the template */
@@ -115,35 +122,42 @@ export default class TemplatePlugin implements ViewPlugin {
115
122
  return node;
116
123
  });
117
124
 
118
- parser.hooks.determineNodeType.tap("template", (obj: any) => {
119
- if (obj === "template") {
120
- return NodeType.Template;
121
- }
122
- });
123
-
124
125
  parser.hooks.parseNode.tap(
125
126
  "template",
126
127
  (
127
128
  obj: any,
128
129
  _nodeType: Node.ChildrenTypes,
129
130
  options: ParseObjectOptions,
130
- determinedNodeType: null | NodeType,
131
+ childOptions?: ParseObjectChildOptions,
131
132
  ) => {
132
- if (determinedNodeType === NodeType.Template) {
133
- const templateNode = parser.createASTNode(
134
- {
135
- type: NodeType.Template,
136
- depth: options.templateDepth ?? 0,
137
- data: obj.data,
138
- template: obj.value,
139
- dynamic: obj.dynamic ?? false,
140
- },
141
- obj,
142
- );
143
-
144
- if (templateNode) {
145
- return templateNode;
146
- }
133
+ if (childOptions && hasTemplateKey(childOptions.key)) {
134
+ return obj
135
+ .map((template: Template) => {
136
+ const templateAST = parser.createASTNode(
137
+ {
138
+ type: NodeType.Template,
139
+ depth: options.templateDepth ?? 0,
140
+ data: template.data,
141
+ template: template.value,
142
+ dynamic: template.dynamic ?? false,
143
+ },
144
+ template,
145
+ );
146
+
147
+ if (!templateAST) return;
148
+
149
+ if (templateAST.type === NodeType.MultiNode) {
150
+ templateAST.values.forEach((v) => {
151
+ v.parent = templateAST;
152
+ });
153
+ }
154
+
155
+ return {
156
+ path: [...childOptions.path, template.output],
157
+ value: templateAST,
158
+ };
159
+ })
160
+ .filter(Boolean);
147
161
  }
148
162
  },
149
163
  );
@@ -9,7 +9,11 @@ import { TapableLogger } from "../../../logger";
9
9
  import { Resolver } from "..";
10
10
  import type { Node } from "../../parser";
11
11
  import { NodeType, Parser } from "../../parser";
12
- import { StringResolverPlugin } from "../../plugins";
12
+ import {
13
+ StringResolverPlugin,
14
+ MultiNodePlugin,
15
+ AssetPlugin,
16
+ } from "../../plugins";
13
17
 
14
18
  describe("Dynamic AST Transforms", () => {
15
19
  const content = {
@@ -40,6 +44,8 @@ describe("Dynamic AST Transforms", () => {
40
44
  year: "2021",
41
45
  });
42
46
  const parser = new Parser();
47
+ new MultiNodePlugin().applyParser(parser);
48
+
43
49
  const bindingParser = new BindingParser();
44
50
  const inputBinding = bindingParser.parse("year");
45
51
  const rootNode = parser.parseObject(content);
@@ -218,6 +224,7 @@ describe("Dynamic AST Transforms", () => {
218
224
  year: "2021",
219
225
  });
220
226
  const parser = new Parser();
227
+ new AssetPlugin().applyParser(parser);
221
228
  const bindingParser = new BindingParser();
222
229
  const inputBinding = bindingParser.parse("year");
223
230
  const rootNode = parser.parseObject(view);
@@ -271,6 +278,7 @@ describe("Dynamic AST Transforms", () => {
271
278
  year: "2021",
272
279
  });
273
280
  const parser = new Parser();
281
+ new AssetPlugin().applyParser(parser);
274
282
  const bindingParser = new BindingParser();
275
283
  const rootNode = parser.parseObject(content);
276
284
 
@@ -354,6 +362,7 @@ describe("Duplicate IDs", () => {
354
362
  count2: 0,
355
363
  });
356
364
  const parser = new Parser();
365
+ new AssetPlugin().applyParser(parser);
357
366
  const bindingParser = new BindingParser();
358
367
  const rootNode = parser.parseObject(content, NodeType.View);
359
368
 
@@ -449,6 +458,8 @@ describe("Duplicate IDs", () => {
449
458
  count2: 0,
450
459
  });
451
460
  const parser = new Parser();
461
+ new MultiNodePlugin().applyParser(parser);
462
+
452
463
  const bindingParser = new BindingParser();
453
464
  const rootNode = parser.parseObject(content, NodeType.View);
454
465
 
@@ -512,6 +523,8 @@ describe("AST caching", () => {
512
523
 
513
524
  const model = new LocalModel();
514
525
  const parser = new Parser();
526
+ new MultiNodePlugin().applyParser(parser);
527
+
515
528
  const bindingParser = new BindingParser();
516
529
  const rootNode = parser.parseObject(content, NodeType.View);
517
530
  const resolver = new Resolver(rootNode!, {
package/src/view/view.ts CHANGED
@@ -4,15 +4,10 @@ import type { BindingInstance, BindingFactory } from "../binding";
4
4
  import type { ValidationProvider, ValidationObject } from "../validator";
5
5
  import type { Logger } from "../logger";
6
6
  import type { Resolve } from "./resolver";
7
- import { Resolver, toNodeResolveOptions } from "./resolver";
7
+ import { Resolver } from "./resolver";
8
8
  import type { Node } from "./parser";
9
9
  import { Parser } from "./parser";
10
- import {
11
- TemplatePlugin,
12
- StringResolverPlugin,
13
- ApplicabilityPlugin,
14
- SwitchPlugin,
15
- } from "./plugins";
10
+ import { TemplatePlugin } from "./plugins";
16
11
 
17
12
  /**
18
13
  * Manages the view level validations
@@ -8,6 +8,11 @@ export interface ParseObjectOptions {
8
8
  /** how nested the templated is */
9
9
  templateDepth?: number;
10
10
  }
11
+ export interface ParseObjectChildOptions {
12
+ key: string;
13
+ path: Node.PathSegment[];
14
+ parentObj: object;
15
+ }
11
16
  /**
12
17
  * The Parser is the way to take an incoming view from the user and parse it into an AST.
13
18
  * It provides a few ways to interact with the parsing, including mutating an object before and after creation of an AST node
@@ -33,20 +38,10 @@ export declare class Parser {
33
38
  * If null, we ignore this node all together
34
39
  */
35
40
  onCreateASTNode: SyncWaterfallHook<[Node.Node | null | undefined, object], Record<string, any>>;
36
- determineNodeType: SyncBailHook<[string | object], NodeType, Record<string, any>>;
37
- parseNode: SyncBailHook<[obj: object, nodeType: Node.ChildrenTypes, parseOptions: ParseObjectOptions, determinedNodeType: NodeType | null], Node.Node, Record<string, any>>;
41
+ parseNode: SyncBailHook<[obj: object, nodeType: Node.ChildrenTypes, parseOptions: ParseObjectOptions, childOptions?: ParseObjectChildOptions | undefined], Node.Node | Node.Child[], Record<string, any>>;
38
42
  };
39
43
  parseView(value: AnyAssetType): Node.View;
40
- private parseAsync;
41
44
  createASTNode(node: Node.Node | null, value: any): Node.Node | null;
42
- /**
43
- * Checks if there are templated values in the object
44
- *
45
- * @param obj - The Parsed Object to check to see if we have a template array type for
46
- * @param localKey - The key being checked
47
- */
48
- private hasTemplateValues;
49
- private hasSwitchKey;
50
45
  parseObject(obj: object, type?: Node.ChildrenTypes, options?: ParseObjectOptions): Node.Node | null;
51
46
  }
52
47
  //# sourceMappingURL=index.d.ts.map
@@ -1,6 +1,15 @@
1
1
  import type { Node } from "./types";
2
- /** Check to see if the object contains async */
3
- export declare function hasAsync(obj: object): boolean;
2
+ /**
3
+ * Checks if there are templated values in the object
4
+ *
5
+ * @param obj - The Parsed Object to check to see if we have a template array type for
6
+ * @param localKey - The key being checked
7
+ */
8
+ export declare function hasTemplateValues(obj: any, localKey: string): any;
9
+ /** Check to see if the string is a valid switch key */
10
+ export declare function hasSwitchKey(localKey: string): boolean;
11
+ /** Check to see if the string is a valid template key */
12
+ export declare function hasTemplateKey(localKey: string): boolean;
4
13
  /** Get the ID of the Node if there is one */
5
14
  export declare function getNodeID(node?: Node.Node | null): string | undefined;
6
15
  //# sourceMappingURL=utils.d.ts.map
@@ -3,6 +3,7 @@ import type { Parser } from "../parser";
3
3
  import { ViewInstance, ViewPlugin } from "../view";
4
4
  /** A view plugin to remove inapplicable assets from the tree */
5
5
  export default class ApplicabilityPlugin implements ViewPlugin {
6
+ private isApplicability;
6
7
  applyResolver(resolver: Resolver): void;
7
8
  applyParser(parser: Parser): void;
8
9
  apply(view: ViewInstance): void;
@@ -0,0 +1,8 @@
1
+ import { ViewInstance, ViewPlugin } from "../view";
2
+ import type { Parser } from "../parser";
3
+ /** A view plugin to resolve assets */
4
+ export default class AssetPlugin implements ViewPlugin {
5
+ applyParser(parser: Parser): void;
6
+ apply(view: ViewInstance): void;
7
+ }
8
+ //# sourceMappingURL=asset.d.ts.map
@@ -1,5 +1,7 @@
1
- export { default as TemplatePlugin } from "./template-plugin";
1
+ export { default as TemplatePlugin } from "./template";
2
2
  export { default as StringResolverPlugin } from "./string-resolver";
3
3
  export { default as ApplicabilityPlugin } from "./applicability";
4
4
  export { default as SwitchPlugin } from "./switch";
5
+ export { default as MultiNodePlugin } from "./multi-node";
6
+ export { default as AssetPlugin } from "./asset";
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,8 @@
1
+ import { ViewInstance, ViewPlugin } from "../view";
2
+ import type { Parser } from "../parser";
3
+ /** A view plugin to resolve multi nodes */
4
+ export default class MultiNodePlugin implements ViewPlugin {
5
+ applyParser(parser: Parser): void;
6
+ apply(view: ViewInstance): void;
7
+ }
8
+ //# sourceMappingURL=multi-node.d.ts.map
@@ -1,12 +1,13 @@
1
+ import { ViewInstance, ViewPlugin } from "../view";
1
2
  import type { Options } from "./options";
2
3
  import type { Parser } from "../parser";
3
4
  import type { Resolver } from "../resolver";
4
- import { ViewInstance, ViewPlugin } from "../view";
5
5
  /** A view plugin to resolve switches */
6
6
  export default class SwitchPlugin implements ViewPlugin {
7
7
  private readonly options;
8
8
  constructor(options: Options);
9
9
  private resolveSwitch;
10
+ private isSwitch;
10
11
  applyParser(parser: Parser): void;
11
12
  applyResolver(resolver: Resolver): void;
12
13
  apply(view: ViewInstance): void;
@@ -1,8 +1,8 @@
1
1
  import { SyncWaterfallHook } from "tapable-ts";
2
2
  import type { Parser } from "../parser";
3
+ import { ViewInstance, ViewPlugin } from "../view";
3
4
  import type { Options } from "./options";
4
5
  import type { Resolver } from "../resolver";
5
- import { ViewInstance, ViewPlugin } from "../view";
6
6
  export interface TemplateItemInfo {
7
7
  /** The index of the data for the current iteration of the template */
8
8
  index: number;
@@ -30,4 +30,4 @@ export default class TemplatePlugin implements ViewPlugin {
30
30
  applyResolverHooks(resolver: Resolver): void;
31
31
  apply(view: ViewInstance): void;
32
32
  }
33
- //# sourceMappingURL=template-plugin.d.ts.map
33
+ //# sourceMappingURL=template.d.ts.map