@player-ui/async-node-plugin 0.8.0--canary.307.9645 → 0.8.0-next.1
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/AsyncNodePlugin.native.js +7706 -0
- package/dist/AsyncNodePlugin.native.js.map +1 -0
- package/dist/cjs/index.cjs +166 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/index.legacy-esm.js +130 -0
- package/dist/index.mjs +130 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +24 -60
- package/src/index.test.ts +453 -0
- package/src/index.ts +156 -78
- package/src/types.ts +2 -2
- package/types/index.d.ts +38 -0
- package/types/types.d.ts +3 -0
- package/dist/async-node-plugin.dev.js +0 -10678
- package/dist/async-node-plugin.prod.js +0 -2
- package/dist/index.cjs.js +0 -102
- package/dist/index.d.ts +0 -20
- package/dist/index.esm.js +0 -94
package/src/index.ts
CHANGED
|
@@ -1,107 +1,185 @@
|
|
|
1
|
-
import { NodeType, getNodeID } from
|
|
1
|
+
import { NodeType, getNodeID } from "@player-ui/player";
|
|
2
2
|
import type {
|
|
3
3
|
Player,
|
|
4
4
|
PlayerPlugin,
|
|
5
5
|
Node,
|
|
6
6
|
ParseObjectOptions,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
ViewInstance,
|
|
8
|
+
Parser,
|
|
9
|
+
ViewPlugin,
|
|
10
|
+
Resolver,
|
|
11
|
+
} from "@player-ui/player";
|
|
12
|
+
import { AsyncParallelBailHook } from "tapable-ts";
|
|
13
|
+
import queueMicrotask from "queue-microtask";
|
|
14
|
+
import { omit } from "timm";
|
|
11
15
|
|
|
12
|
-
export * from
|
|
16
|
+
export * from "./types";
|
|
17
|
+
|
|
18
|
+
export interface AsyncNodePluginOptions {
|
|
19
|
+
/** A set of plugins to load */
|
|
20
|
+
plugins?: AsyncNodeViewPlugin[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface AsyncNodeViewPlugin extends ViewPlugin {
|
|
24
|
+
/** Use this to tap into the async node plugin hooks */
|
|
25
|
+
applyPlugin: (asyncNodePlugin: AsyncNodePlugin) => void;
|
|
26
|
+
|
|
27
|
+
asyncNode: AsyncParallelBailHook<[Node.Async], any>;
|
|
28
|
+
}
|
|
13
29
|
|
|
14
30
|
/**
|
|
15
31
|
* Async node plugin used to resolve async nodes in the content
|
|
16
32
|
* If an async node is present, allow users to provide a replacement node to be rendered when ready
|
|
17
33
|
*/
|
|
18
34
|
export class AsyncNodePlugin implements PlayerPlugin {
|
|
35
|
+
private plugins: AsyncNodeViewPlugin[] | undefined;
|
|
36
|
+
|
|
37
|
+
constructor(options: AsyncNodePluginOptions) {
|
|
38
|
+
if (options?.plugins) {
|
|
39
|
+
this.plugins = options.plugins;
|
|
40
|
+
options.plugins.forEach((plugin) => {
|
|
41
|
+
plugin.applyPlugin(this);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
19
46
|
public readonly hooks = {
|
|
20
|
-
onAsyncNode: new AsyncParallelBailHook<[Node.
|
|
47
|
+
onAsyncNode: new AsyncParallelBailHook<[Node.Async], any>(),
|
|
21
48
|
};
|
|
22
49
|
|
|
23
|
-
name =
|
|
50
|
+
name = "AsyncNode";
|
|
51
|
+
|
|
52
|
+
apply(player: Player) {
|
|
53
|
+
player.hooks.viewController.tap(this.name, (viewController) => {
|
|
54
|
+
viewController.hooks.view.tap(this.name, (view) => {
|
|
55
|
+
this.plugins?.forEach((plugin) => {
|
|
56
|
+
plugin.apply(view);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {
|
|
64
|
+
public asyncNode = new AsyncParallelBailHook<[Node.Async], any>();
|
|
65
|
+
private basePlugin: AsyncNodePlugin | undefined;
|
|
66
|
+
|
|
67
|
+
name = "AsyncNode";
|
|
68
|
+
|
|
69
|
+
private resolvedMapping = new Map<string, any>();
|
|
24
70
|
|
|
25
|
-
private
|
|
71
|
+
private currentView: ViewInstance | undefined;
|
|
26
72
|
|
|
27
73
|
private isAsync(node: Node.Node | null): node is Node.Async {
|
|
28
74
|
return node?.type === NodeType.Async;
|
|
29
75
|
}
|
|
30
76
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
omit(obj, 'async'),
|
|
51
|
-
nodeType,
|
|
52
|
-
options
|
|
53
|
-
);
|
|
54
|
-
const parsedNodeId = getNodeID(parsedAsync);
|
|
55
|
-
if (parsedAsync !== null && parsedNodeId) {
|
|
56
|
-
return parser.createASTNode(
|
|
57
|
-
{
|
|
58
|
-
id: parsedNodeId,
|
|
59
|
-
type: NodeType.Async,
|
|
60
|
-
value: parsedAsync,
|
|
61
|
-
},
|
|
62
|
-
obj
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
77
|
+
applyParser(parser: Parser) {
|
|
78
|
+
parser.hooks.determineNodeType.tap(this.name, (obj) => {
|
|
79
|
+
if (Object.prototype.hasOwnProperty.call(obj, "async")) {
|
|
80
|
+
return NodeType.Async;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
parser.hooks.parseNode.tap(
|
|
84
|
+
this.name,
|
|
85
|
+
(
|
|
86
|
+
obj: any,
|
|
87
|
+
nodeType: Node.ChildrenTypes,
|
|
88
|
+
options: ParseObjectOptions,
|
|
89
|
+
determinedNodeType: null | NodeType,
|
|
90
|
+
) => {
|
|
91
|
+
if (determinedNodeType === NodeType.Async) {
|
|
92
|
+
const parsedAsync = parser.parseObject(
|
|
93
|
+
omit(obj, "async"),
|
|
94
|
+
nodeType,
|
|
95
|
+
options,
|
|
69
96
|
);
|
|
97
|
+
const parsedNodeId = getNodeID(parsedAsync);
|
|
98
|
+
if (parsedAsync !== null && parsedNodeId) {
|
|
99
|
+
return parser.createASTNode(
|
|
100
|
+
{
|
|
101
|
+
id: parsedNodeId,
|
|
102
|
+
type: NodeType.Async,
|
|
103
|
+
value: parsedAsync,
|
|
104
|
+
},
|
|
105
|
+
obj,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
applyResolverHooks(resolver: Resolver) {
|
|
116
|
+
resolver.hooks.beforeResolve.tap(this.name, (node, options) => {
|
|
117
|
+
let resolvedNode;
|
|
118
|
+
if (this.isAsync(node)) {
|
|
119
|
+
const mappedValue = this.resolvedMapping.get(node.id);
|
|
120
|
+
if (mappedValue) {
|
|
121
|
+
resolvedNode = mappedValue;
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
resolvedNode = null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const newNode = resolvedNode || node;
|
|
128
|
+
if (!resolvedNode && node?.type === NodeType.Async) {
|
|
129
|
+
queueMicrotask(async () => {
|
|
130
|
+
const result = await this.basePlugin?.hooks.onAsyncNode.call(node);
|
|
131
|
+
const parsedNode =
|
|
132
|
+
options.parseNode && result ? options.parseNode(result) : undefined;
|
|
133
|
+
|
|
134
|
+
if (parsedNode) {
|
|
135
|
+
this.resolvedMapping.set(node.id, parsedNode);
|
|
136
|
+
this.currentView?.updateAsync();
|
|
137
|
+
}
|
|
70
138
|
});
|
|
71
139
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
140
|
+
return node;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return newNode;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
apply(view: ViewInstance): void {
|
|
148
|
+
view.hooks.parser.tap("template", this.applyParser.bind(this));
|
|
149
|
+
view.hooks.resolver.tap("template", (resolver) => {
|
|
150
|
+
resolver.hooks.beforeResolve.tap(this.name, (node, options) => {
|
|
151
|
+
let resolvedNode;
|
|
152
|
+
if (this.isAsync(node)) {
|
|
153
|
+
const mappedValue = this.resolvedMapping.get(node.id);
|
|
154
|
+
if (mappedValue) {
|
|
155
|
+
resolvedNode = mappedValue;
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
resolvedNode = null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const newNode = resolvedNode || node;
|
|
162
|
+
if (!resolvedNode && node?.type === NodeType.Async) {
|
|
163
|
+
queueMicrotask(async () => {
|
|
164
|
+
const result = await this.basePlugin?.hooks.onAsyncNode.call(node);
|
|
165
|
+
const parsedNode =
|
|
166
|
+
options.parseNode && result
|
|
167
|
+
? options.parseNode(result)
|
|
168
|
+
: undefined;
|
|
169
|
+
|
|
170
|
+
this.resolvedMapping.set(node.id, parsedNode ? parsedNode : node);
|
|
171
|
+
view.updateAsync();
|
|
102
172
|
});
|
|
103
|
-
|
|
173
|
+
|
|
174
|
+
return node;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return newNode;
|
|
104
178
|
});
|
|
105
179
|
});
|
|
106
180
|
}
|
|
181
|
+
|
|
182
|
+
applyPlugin(asyncNodePlugin: AsyncNodePlugin): void {
|
|
183
|
+
this.basePlugin = asyncNodePlugin;
|
|
184
|
+
}
|
|
107
185
|
}
|
package/src/types.ts
CHANGED
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Player, PlayerPlugin, Node, ViewInstance, Parser, ViewPlugin, Resolver } from "@player-ui/player";
|
|
2
|
+
import { AsyncParallelBailHook } from "tapable-ts";
|
|
3
|
+
export * from "./types";
|
|
4
|
+
export interface AsyncNodePluginOptions {
|
|
5
|
+
/** A set of plugins to load */
|
|
6
|
+
plugins?: AsyncNodeViewPlugin[];
|
|
7
|
+
}
|
|
8
|
+
export interface AsyncNodeViewPlugin extends ViewPlugin {
|
|
9
|
+
/** Use this to tap into the async node plugin hooks */
|
|
10
|
+
applyPlugin: (asyncNodePlugin: AsyncNodePlugin) => void;
|
|
11
|
+
asyncNode: AsyncParallelBailHook<[Node.Async], any>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Async node plugin used to resolve async nodes in the content
|
|
15
|
+
* If an async node is present, allow users to provide a replacement node to be rendered when ready
|
|
16
|
+
*/
|
|
17
|
+
export declare class AsyncNodePlugin implements PlayerPlugin {
|
|
18
|
+
private plugins;
|
|
19
|
+
constructor(options: AsyncNodePluginOptions);
|
|
20
|
+
readonly hooks: {
|
|
21
|
+
onAsyncNode: AsyncParallelBailHook<[Node.Async], any, Record<string, any>>;
|
|
22
|
+
};
|
|
23
|
+
name: string;
|
|
24
|
+
apply(player: Player): void;
|
|
25
|
+
}
|
|
26
|
+
export declare class AsyncNodePluginPlugin implements AsyncNodeViewPlugin {
|
|
27
|
+
asyncNode: AsyncParallelBailHook<[Node.Async], any, Record<string, any>>;
|
|
28
|
+
private basePlugin;
|
|
29
|
+
name: string;
|
|
30
|
+
private resolvedMapping;
|
|
31
|
+
private currentView;
|
|
32
|
+
private isAsync;
|
|
33
|
+
applyParser(parser: Parser): void;
|
|
34
|
+
applyResolverHooks(resolver: Resolver): void;
|
|
35
|
+
apply(view: ViewInstance): void;
|
|
36
|
+
applyPlugin(asyncNodePlugin: AsyncNodePlugin): void;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|
package/types/types.d.ts
ADDED