@player-ui/player 0.3.0-next.2 → 0.3.0-next.4

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 (77) hide show
  1. package/dist/index.cjs.js +4128 -891
  2. package/dist/index.d.ts +1227 -50
  3. package/dist/index.esm.js +4065 -836
  4. package/package.json +9 -15
  5. package/src/binding/binding.ts +108 -0
  6. package/src/binding/index.ts +188 -0
  7. package/src/binding/resolver.ts +157 -0
  8. package/src/binding/utils.ts +51 -0
  9. package/src/binding-grammar/ast.ts +113 -0
  10. package/src/binding-grammar/custom/index.ts +304 -0
  11. package/src/binding-grammar/ebnf/binding.ebnf +22 -0
  12. package/src/binding-grammar/ebnf/index.ts +186 -0
  13. package/src/binding-grammar/ebnf/types.ts +104 -0
  14. package/src/binding-grammar/index.ts +4 -0
  15. package/src/binding-grammar/parsimmon/index.ts +78 -0
  16. package/src/controllers/constants/index.ts +85 -0
  17. package/src/controllers/constants/utils.ts +37 -0
  18. package/src/{data.ts → controllers/data.ts} +6 -6
  19. package/src/controllers/flow/controller.ts +95 -0
  20. package/src/controllers/flow/flow.ts +205 -0
  21. package/src/controllers/flow/index.ts +2 -0
  22. package/src/controllers/index.ts +5 -0
  23. package/src/{validation → controllers/validation}/binding-tracker.ts +5 -5
  24. package/src/{validation → controllers/validation}/controller.ts +15 -14
  25. package/src/{validation → controllers/validation}/index.ts +0 -0
  26. package/src/{view → controllers/view}/asset-transform.ts +2 -3
  27. package/src/{view → controllers/view}/controller.ts +9 -8
  28. package/src/controllers/view/index.ts +4 -0
  29. package/src/{view → controllers/view}/store.ts +0 -0
  30. package/src/{view → controllers/view}/types.ts +2 -1
  31. package/src/data/dependency-tracker.ts +187 -0
  32. package/src/data/index.ts +4 -0
  33. package/src/data/local-model.ts +41 -0
  34. package/src/data/model.ts +216 -0
  35. package/src/data/noop-model.ts +18 -0
  36. package/src/expressions/evaluator-functions.ts +29 -0
  37. package/src/expressions/evaluator.ts +405 -0
  38. package/src/expressions/index.ts +3 -0
  39. package/src/expressions/parser.ts +889 -0
  40. package/src/expressions/types.ts +200 -0
  41. package/src/expressions/utils.ts +8 -0
  42. package/src/index.ts +9 -12
  43. package/src/logger/consoleLogger.ts +49 -0
  44. package/src/logger/index.ts +5 -0
  45. package/src/logger/noopLogger.ts +13 -0
  46. package/src/logger/proxyLogger.ts +25 -0
  47. package/src/logger/tapableLogger.ts +38 -0
  48. package/src/logger/types.ts +6 -0
  49. package/src/player.ts +21 -18
  50. package/src/plugins/flow-exp-plugin.ts +2 -3
  51. package/src/schema/index.ts +2 -0
  52. package/src/schema/schema.ts +220 -0
  53. package/src/schema/types.ts +60 -0
  54. package/src/string-resolver/index.ts +188 -0
  55. package/src/types.ts +11 -13
  56. package/src/utils/index.ts +1 -0
  57. package/src/utils/replaceParams.ts +17 -0
  58. package/src/validator/index.ts +3 -0
  59. package/src/validator/registry.ts +20 -0
  60. package/src/validator/types.ts +75 -0
  61. package/src/validator/validation-middleware.ts +114 -0
  62. package/src/view/builder/index.ts +81 -0
  63. package/src/view/index.ts +5 -4
  64. package/src/view/parser/index.ts +318 -0
  65. package/src/view/parser/types.ts +141 -0
  66. package/src/view/plugins/applicability.ts +78 -0
  67. package/src/view/plugins/index.ts +5 -0
  68. package/src/view/plugins/options.ts +4 -0
  69. package/src/view/plugins/plugin.ts +21 -0
  70. package/src/view/plugins/string-resolver.ts +149 -0
  71. package/src/view/plugins/switch.ts +120 -0
  72. package/src/view/plugins/template-plugin.ts +172 -0
  73. package/src/view/resolver/index.ts +397 -0
  74. package/src/view/resolver/types.ts +161 -0
  75. package/src/view/resolver/utils.ts +57 -0
  76. package/src/view/view.ts +149 -0
  77. package/src/utils/desc.d.ts +0 -2
@@ -0,0 +1,397 @@
1
+ import { SyncWaterfallHook, SyncHook } from 'tapable-ts';
2
+ import { setIn, addLast } from 'timm';
3
+ import dlv from 'dlv';
4
+ import { dequal } from 'dequal';
5
+ import type { BindingInstance, BindingLike } from '../../binding';
6
+ import type {
7
+ DataModelOptions,
8
+ DataModelWithParser,
9
+ Updates,
10
+ } from '../../data';
11
+ import { DependencyModel, withParser } from '../../data';
12
+ import type { Logger } from '../../logger';
13
+ import type { Node } from '../parser';
14
+ import { NodeType } from '../parser';
15
+ import { caresAboutDataChanges, toNodeResolveOptions } from './utils';
16
+ import type { Resolve } from './types';
17
+
18
+ export * from './types';
19
+ export * from './utils';
20
+
21
+ interface NodeUpdate extends Resolve.ResolvedNode {
22
+ /** A flag to track if a node has changed since the last resolution */
23
+ updated: boolean;
24
+ }
25
+
26
+ /** Add model context to the data model */
27
+ const withContext = (model: DataModelWithParser): DataModelWithParser => {
28
+ return {
29
+ get: (binding: BindingLike, options?: DataModelOptions): any => {
30
+ return model.get(binding, {
31
+ context: { model },
32
+ ...options,
33
+ });
34
+ },
35
+
36
+ set: (
37
+ transaction: [BindingLike, any][],
38
+ options?: DataModelOptions
39
+ ): Updates => {
40
+ return model.set(transaction, {
41
+ context: { model },
42
+ ...options,
43
+ });
44
+ },
45
+ };
46
+ };
47
+
48
+ /**
49
+ * The Resolver is the way to take a parsed AST graph of a view and resolve it to a concrete representation of the current user state
50
+ * It combines the ability to mutate ast nodes before resolving, as well as the mutating the resolved objects while parsing
51
+ */
52
+ export class Resolver {
53
+ public readonly hooks = {
54
+ /** A hook to allow skipping of the resolution tree for a specific node */
55
+ skipResolve: new SyncWaterfallHook<
56
+ [boolean, Node.Node, Resolve.NodeResolveOptions]
57
+ >(),
58
+
59
+ /** An event emitted before calculating the next update */
60
+ beforeUpdate: new SyncHook<[Set<BindingInstance> | undefined]>(),
61
+
62
+ /** An event emitted after calculating the next update */
63
+ afterUpdate: new SyncHook<[any]>(),
64
+
65
+ /** The options passed to a node to resolve it to an object */
66
+ resolveOptions: new SyncWaterfallHook<
67
+ [Resolve.NodeResolveOptions, Node.Node]
68
+ >(),
69
+
70
+ /** A hook to transform the AST node into a new AST node before resolving it */
71
+ beforeResolve: new SyncWaterfallHook<
72
+ [Node.Node | null, Resolve.NodeResolveOptions]
73
+ >(),
74
+
75
+ /**
76
+ * A hook to transform an AST node into it's resolved value.
77
+ * This runs _before_ any children are resolved
78
+ */
79
+ resolve: new SyncWaterfallHook<
80
+ [any, Node.Node, Resolve.NodeResolveOptions]
81
+ >(),
82
+
83
+ /**
84
+ * A hook to transform the resolved value of an AST node.
85
+ * This runs _after_ all children nodes are resolved
86
+ */
87
+ afterResolve: new SyncWaterfallHook<
88
+ [any, Node.Node, Resolve.NodeResolveOptions]
89
+ >(),
90
+
91
+ /** Called at the very end of a node's tree being updated */
92
+ afterNodeUpdate: new SyncHook<
93
+ [Node.Node, Node.Node | undefined, NodeUpdate]
94
+ >(),
95
+ };
96
+
97
+ /**
98
+ * The AST tree after beforeResolve is ran mapped to the AST before beforeResolve is ran
99
+ */
100
+ private readonly ASTMap: Map<Node.Node, Node.Node>;
101
+ /**
102
+ * The root node in the AST tree we want to resolve
103
+ */
104
+ public readonly root: Node.Node;
105
+
106
+ /**
107
+ * The cache of the last resolved values when walking the tree.
108
+ * This gets recycled every update to avoid stale data if a node is unused in an update
109
+ */
110
+ private resolveCache: Map<Node.Node, Resolve.ResolvedNode>;
111
+
112
+ /**
113
+ * Cache of node IDs that have been processed to track if nodes have duplicate IDs
114
+ */
115
+ private idCache: Set<string>;
116
+
117
+ /**
118
+ * The parameters required to resolve AST nodes
119
+ */
120
+ private readonly options: Resolve.ResolverOptions;
121
+
122
+ /**
123
+ * Tapable logger for logging errors encountered during view resolution
124
+ */
125
+ private logger?: Logger;
126
+
127
+ constructor(root: Node.Node, options: Resolve.ResolverOptions) {
128
+ this.root = root;
129
+ this.options = options;
130
+ this.resolveCache = new Map();
131
+ this.ASTMap = new Map();
132
+ this.logger = options.logger;
133
+ this.idCache = new Set();
134
+ }
135
+
136
+ public getSourceNode(convertedAST: Node.Node) {
137
+ return this.ASTMap.get(convertedAST);
138
+ }
139
+
140
+ public update(changes?: Set<BindingInstance>): any {
141
+ this.hooks.beforeUpdate.call(changes);
142
+ const resolveCache = new Map<Node.Node, Resolve.ResolvedNode>();
143
+ this.idCache.clear();
144
+ this.ASTMap.clear();
145
+
146
+ const updated = this.computeTree(
147
+ this.root,
148
+ undefined,
149
+ changes,
150
+ resolveCache,
151
+ toNodeResolveOptions(this.options)
152
+ );
153
+ this.resolveCache = resolveCache;
154
+ this.hooks.afterUpdate.call(updated.value);
155
+
156
+ return updated.value;
157
+ }
158
+
159
+ private getNodeID(node?: Node.Node): string | undefined {
160
+ if (!node) {
161
+ return;
162
+ }
163
+
164
+ if (
165
+ (node.type === NodeType.Asset ||
166
+ node.type === NodeType.View ||
167
+ node.type === NodeType.Value) &&
168
+ typeof node.value === 'object' &&
169
+ typeof node.value?.id === 'string'
170
+ ) {
171
+ return node.value.id;
172
+ }
173
+ }
174
+
175
+ private getPreviousResult(node: Node.Node): Resolve.ResolvedNode | undefined {
176
+ if (!node) {
177
+ return;
178
+ }
179
+
180
+ const isFirstUpdate = this.resolveCache.size === 0;
181
+ const id = this.getNodeID(node);
182
+
183
+ if (id) {
184
+ if (this.idCache.has(id)) {
185
+ // Only log this conflict once to cut down on noise
186
+ // May want to swap this to logging when we first see the id -- which may not be the first render
187
+ if (isFirstUpdate) {
188
+ if (node.type === NodeType.Asset || node.type === NodeType.View) {
189
+ this.logger?.error(
190
+ `Cache conflict: Found Asset/View nodes that have conflicting ids: ${id}, may cause cache issues.`
191
+ );
192
+ } else if (node.type === NodeType.Value) {
193
+ this.logger?.info(
194
+ `Cache conflict: Found Value nodes that have conflicting ids: ${id}, may cause cache issues. To improve performance make value node IDs globally unique.`
195
+ );
196
+ }
197
+ }
198
+
199
+ // Don't use anything from a prev result if there's a duplicate id detected
200
+ return;
201
+ }
202
+
203
+ this.idCache.add(id);
204
+ }
205
+
206
+ return this.resolveCache.get(node);
207
+ }
208
+
209
+ private computeTree(
210
+ node: Node.Node,
211
+ parent: Node.Node | undefined,
212
+ dataChanges: Set<BindingInstance> | undefined,
213
+ cacheUpdate: Map<Node.Node, Resolve.ResolvedNode>,
214
+ options: Resolve.NodeResolveOptions
215
+ ): NodeUpdate {
216
+ const dependencyModel = new DependencyModel(options.data.model);
217
+
218
+ dependencyModel.trackSubset('core');
219
+ const depModelWithParser = withContext(
220
+ withParser(dependencyModel, this.options.parseBinding)
221
+ );
222
+
223
+ const resolveOptions = this.hooks.resolveOptions.call(
224
+ {
225
+ ...options,
226
+ data: {
227
+ ...options.data,
228
+ model: depModelWithParser,
229
+ },
230
+ evaluate: (exp) =>
231
+ this.options.evaluator.evaluate(exp, { model: depModelWithParser }),
232
+ node,
233
+ },
234
+ node
235
+ );
236
+
237
+ const previousResult = this.getPreviousResult(node);
238
+ const previousDeps = previousResult?.dependencies;
239
+
240
+ const dataChanged = caresAboutDataChanges(dataChanges, previousDeps);
241
+ const shouldUseLastValue = this.hooks.skipResolve.call(
242
+ !dataChanged,
243
+ node,
244
+ resolveOptions
245
+ );
246
+
247
+ if (shouldUseLastValue && previousResult) {
248
+ const update = {
249
+ ...previousResult,
250
+ updated: false,
251
+ };
252
+
253
+ cacheUpdate.set(node, update);
254
+
255
+ /** Recursively repopulate the AST map given some AST Node and it's resolved AST representation */
256
+ const repopulateASTMapFromCache = (
257
+ resolvedAST: Node.Node,
258
+ AST: Node.Node
259
+ ) => {
260
+ this.ASTMap.set(resolvedAST, AST);
261
+ if ('children' in resolvedAST) {
262
+ resolvedAST.children?.forEach(({ value: childAST }) => {
263
+ const { node: childResolvedAST } =
264
+ this.getPreviousResult(childAST) || {};
265
+ if (!childResolvedAST) return;
266
+
267
+ repopulateASTMapFromCache(childResolvedAST, childAST);
268
+
269
+ if (childResolvedAST.type === NodeType.MultiNode) {
270
+ childResolvedAST.values.forEach((mChildAST) => {
271
+ const { node: mChildResolvedAST } =
272
+ this.getPreviousResult(mChildAST) || {};
273
+ if (!mChildResolvedAST) return;
274
+
275
+ repopulateASTMapFromCache(mChildResolvedAST, mChildAST);
276
+ });
277
+ }
278
+ });
279
+ }
280
+ };
281
+
282
+ const resolvedAST = previousResult.node;
283
+ repopulateASTMapFromCache(resolvedAST, node);
284
+
285
+ this.hooks.afterNodeUpdate.call(node, parent, update);
286
+
287
+ return update;
288
+ }
289
+
290
+ const resolvedAST = this.hooks.beforeResolve.call(node, resolveOptions) ?? {
291
+ type: NodeType.Empty,
292
+ };
293
+
294
+ resolveOptions.node = resolvedAST;
295
+
296
+ this.ASTMap.set(resolvedAST, node);
297
+
298
+ let resolved = this.hooks.resolve.call(
299
+ undefined,
300
+ resolvedAST,
301
+ resolveOptions
302
+ );
303
+
304
+ let updated = !dequal(previousResult?.value, resolved);
305
+
306
+ if (previousResult && !updated) {
307
+ resolved = previousResult?.value;
308
+ }
309
+
310
+ const childDependencies = new Set<BindingInstance>();
311
+ dependencyModel.trackSubset('children');
312
+
313
+ if ('children' in resolvedAST) {
314
+ resolvedAST.children?.forEach((child) => {
315
+ const computedChildTree = this.computeTree(
316
+ child.value,
317
+ node,
318
+ dataChanges,
319
+ cacheUpdate,
320
+ resolveOptions
321
+ );
322
+ let { updated: childUpdated, value: childValue } = computedChildTree;
323
+ const { node: childNode, dependencies: childTreeDeps } =
324
+ computedChildTree;
325
+
326
+ childTreeDeps.forEach((binding) => childDependencies.add(binding));
327
+
328
+ if (childNode.type === NodeType.MultiNode) {
329
+ childValue = [];
330
+ childNode.values.forEach((mValue) => {
331
+ const mTree = this.computeTree(
332
+ mValue,
333
+ node,
334
+ dataChanges,
335
+ cacheUpdate,
336
+ resolveOptions
337
+ );
338
+
339
+ if (mTree.value !== undefined && mTree.value !== null) {
340
+ childValue.push(mTree.value);
341
+ }
342
+
343
+ mTree.dependencies.forEach((bindingDep) =>
344
+ childDependencies.add(bindingDep)
345
+ );
346
+
347
+ childUpdated = childUpdated || mTree.updated;
348
+ });
349
+ }
350
+
351
+ if (childValue) {
352
+ if (childNode.type === NodeType.MultiNode && !childNode.override) {
353
+ const arr = addLast(
354
+ dlv(resolved, child.path as any[], []),
355
+ childValue
356
+ );
357
+ resolved = setIn(resolved, child.path, arr);
358
+ } else {
359
+ resolved = setIn(resolved, child.path, childValue);
360
+ }
361
+ }
362
+
363
+ updated = updated || childUpdated;
364
+ });
365
+ }
366
+
367
+ childDependencies.forEach((bindingDep) =>
368
+ dependencyModel.addChildReadDep(bindingDep)
369
+ );
370
+
371
+ dependencyModel.trackSubset('core');
372
+ if (previousResult && !updated) {
373
+ resolved = previousResult?.value;
374
+ }
375
+
376
+ resolved = this.hooks.afterResolve.call(resolved, resolvedAST, {
377
+ ...resolveOptions,
378
+ getDependencies: (scope?: 'core' | 'children') =>
379
+ dependencyModel.getDependencies(scope),
380
+ });
381
+
382
+ const update: NodeUpdate = {
383
+ node: resolvedAST,
384
+ updated,
385
+ value: resolved,
386
+ dependencies: new Set([
387
+ ...dependencyModel.getDependencies(),
388
+ ...childDependencies,
389
+ ]),
390
+ };
391
+
392
+ this.hooks.afterNodeUpdate.call(node, parent, update);
393
+ cacheUpdate.set(node, update);
394
+
395
+ return update;
396
+ }
397
+ }
@@ -0,0 +1,161 @@
1
+ import type {
2
+ Schema,
3
+ Formatting,
4
+ Validation as ValidationTypes,
5
+ } from '@player-ui/types';
6
+ import type {
7
+ BindingInstance,
8
+ BindingLike,
9
+ BindingFactory,
10
+ } from '../../binding';
11
+ import type {
12
+ DataModelWithParser,
13
+ DataModelImpl,
14
+ DataModelOptions,
15
+ } from '../../data';
16
+ import type { TransitionFunction } from '../../controllers';
17
+ import type { ExpressionEvaluator, ExpressionType } from '../../expressions';
18
+ import type { ValidationResponse } from '../../validator';
19
+ import type { Logger } from '../../logger';
20
+ import type { SchemaController } from '../../schema';
21
+ import type { Node } from '../parser';
22
+
23
+ export declare namespace Resolve {
24
+ export interface Validation {
25
+ /** Fetch the data-type for the given binding */
26
+ type(binding: BindingLike): Schema.DataType | undefined;
27
+
28
+ /** Get all currently applicable validation errors */
29
+ getAll(): Map<BindingInstance, ValidationResponse> | undefined;
30
+
31
+ /** Internal Method to lookup if there is a validation for the given binding */
32
+ _getValidationForBinding(
33
+ binding: BindingLike
34
+ ): ValidationResponse | undefined;
35
+
36
+ /** Get field level error for the specific binding */
37
+ get(
38
+ binding: BindingLike,
39
+ options?: {
40
+ /** If this binding should also be tracked for validations */
41
+ track: boolean;
42
+ }
43
+ ): ValidationResponse | undefined;
44
+
45
+ /** Get errors for all children regardless of section */
46
+ getChildren(type: ValidationTypes.DisplayTarget): Array<ValidationResponse>;
47
+
48
+ /** Get errors for all children solely in this section */
49
+ getValidationsForSection(): Array<ValidationResponse>;
50
+
51
+ /** Track errors for this binding, and notify the node of changes */
52
+ track: (binding: BindingLike) => void;
53
+
54
+ /** Register node as a section */
55
+ register: (options?: {
56
+ /** While type of Display Target group it should register as */
57
+ type: Exclude<ValidationTypes.DisplayTarget, 'field'>;
58
+ }) => void;
59
+ }
60
+
61
+ export interface BaseOptions {
62
+ /** A logger to use */
63
+ logger?: Logger;
64
+
65
+ /** An optional set of validation features */
66
+ validation?: Validation;
67
+
68
+ /** Parse a raw valy into an AST node */
69
+ parseNode?: (node: any) => Node.Node | null;
70
+
71
+ /** A function to move the state to a new place */
72
+ transition?: TransitionFunction;
73
+
74
+ /** The hub for data invariants and metaData associated with the data model */
75
+ schema: SchemaController;
76
+ }
77
+
78
+ export interface NodeDataOptions {
79
+ /** The data to set or get data from */
80
+ model: DataModelWithParser<DataModelOptions>;
81
+
82
+ /**
83
+ * A function to format a given a value (given a binding) for display to the user
84
+ * Note: this doesn't persist any changes in the model.
85
+ */
86
+ format: (binding: BindingLike, value: any) => any;
87
+
88
+ /**
89
+ * A function to format a given value using a formatting reference.
90
+ * The default behavior is the identity function.
91
+ */
92
+ formatValue: (formatReference: Formatting.Reference, value: any) => any;
93
+ }
94
+
95
+ export type NodeResolveOptions = BaseOptions & {
96
+ /** Execute the expression and return it's result */
97
+ evaluate: (exp: ExpressionType) => any;
98
+
99
+ /** All parameters for how to process data */
100
+ data: NodeDataOptions;
101
+
102
+ /** The data dependencies that were requested during the resolution */
103
+ getDependencies?(scope?: 'core' | 'children'): Set<BindingInstance>;
104
+
105
+ /** original node */
106
+ node?: Node.Node;
107
+ };
108
+
109
+ export type ResolverOptions = BaseOptions & {
110
+ /** The data model to set or get data from */
111
+ model: DataModelImpl<DataModelOptions>;
112
+
113
+ /** A formatter function to call */
114
+ format?: (binding: BindingInstance, value: any) => any;
115
+
116
+ /**
117
+ * A function to format a given value using a formatting reference.
118
+ * The default behavior is the identity function.
119
+ */
120
+ formatValue?: (formatReference: Formatting.Reference, value: any) => any;
121
+
122
+ /** An evaluator to execute an expression */
123
+ evaluator: ExpressionEvaluator;
124
+
125
+ /** A fn to parse a raw binding into a binding object */
126
+ parseBinding: BindingFactory;
127
+ };
128
+
129
+ export interface ResolvedNode {
130
+ /** The original node */
131
+ node: Node.Node;
132
+
133
+ /** The data dependencies that were requested during the resolution */
134
+ dependencies: Set<BindingInstance>;
135
+
136
+ /** The final value */
137
+ value: any;
138
+ }
139
+
140
+ export type NodeTransformFunction = (
141
+ node: Node.Node,
142
+ options: NodeResolveOptions
143
+ ) => Node.Node | null;
144
+
145
+ export type NodeResolveFunction = (
146
+ value: any,
147
+ node: Node.Node,
148
+ options: NodeResolveOptions
149
+ ) => any;
150
+
151
+ export interface Plugin {
152
+ /** A transform function to migrate an AST to another AST */
153
+ beforeResolve?: NodeTransformFunction;
154
+
155
+ /** A function to transform an AST to a resolved value */
156
+ resolve?: NodeResolveFunction;
157
+
158
+ /** A function to process a resolved value before completing the node */
159
+ afterResolve?: NodeResolveFunction;
160
+ }
161
+ }
@@ -0,0 +1,57 @@
1
+ import type { BindingInstance, BindingLike } from '../../binding';
2
+ import { isBinding } from '../../binding';
3
+ import type { ExpressionType } from '../../expressions';
4
+ import type { Resolve } from './types';
5
+
6
+ /** Check to see if and of the data-changes affect the given dependencies */
7
+ export function caresAboutDataChanges(
8
+ dataChanges?: Set<BindingInstance>,
9
+ dependencies?: Set<BindingInstance>
10
+ ) {
11
+ if (!dataChanges || !dependencies) {
12
+ return true;
13
+ }
14
+
15
+ const depArray = Array.from(dependencies.values());
16
+ const dataChangeArray = Array.from(dataChanges.values());
17
+
18
+ return (
19
+ depArray.find(
20
+ (dep) =>
21
+ !!dataChangeArray.find(
22
+ (change) =>
23
+ change === dep || change.contains(dep) || dep.contains(change)
24
+ )
25
+ ) !== undefined
26
+ );
27
+ }
28
+
29
+ /** Convert the options object for a resolver to one for a node */
30
+ export function toNodeResolveOptions(
31
+ resolverOptions: Resolve.ResolverOptions
32
+ ): Resolve.NodeResolveOptions {
33
+ return {
34
+ ...resolverOptions,
35
+ data: {
36
+ model: resolverOptions.model,
37
+ formatValue: (ref, value) => {
38
+ if (resolverOptions.formatValue) {
39
+ return resolverOptions.formatValue(ref, value);
40
+ }
41
+
42
+ return value;
43
+ },
44
+ format: (bindingLike: BindingLike, value: any) =>
45
+ resolverOptions.format
46
+ ? resolverOptions.format(
47
+ isBinding(bindingLike)
48
+ ? bindingLike
49
+ : resolverOptions.parseBinding(bindingLike),
50
+ value
51
+ )
52
+ : value,
53
+ },
54
+ evaluate: (exp: ExpressionType) =>
55
+ resolverOptions.evaluator.evaluate(exp, resolverOptions),
56
+ };
57
+ }