@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,60 @@
1
+ import type { Formatting } from '@player-ui/types';
2
+
3
+ export type FormatOptions = Omit<Formatting.Reference, 'type'>;
4
+
5
+ /**
6
+ * The return types for the schema don't include options.
7
+ * These are already sent to the generic formatter function for that type
8
+ */
9
+ export type FormatFunction<From, To = From, Options = unknown> = (
10
+ val: From,
11
+ options?: Options
12
+ ) => To | undefined;
13
+
14
+ export type FormatHandler<From, To = From> = (val: From) => To;
15
+
16
+ export interface FormatDefinition<DataModelType, UserDisplayType> {
17
+ /**
18
+ * A function to format data (from the data-model to the user).
19
+ * Defaults to the identify function
20
+ */
21
+ format: FormatHandler<DataModelType, UserDisplayType>;
22
+
23
+ /**
24
+ * A function to invert the formatting (from the user to the data-model)
25
+ * Defaults to the identify function.
26
+ */
27
+ deformat: FormatHandler<UserDisplayType, DataModelType>;
28
+ }
29
+
30
+ export interface FormatType<
31
+ DataModelType,
32
+ UserDisplayType = DataModelType,
33
+ Options = undefined
34
+ > {
35
+ /**
36
+ * The name of the formatter.
37
+ * This corresponds to the 'type' format property when creating a DataType
38
+ */
39
+ name: string;
40
+
41
+ /**
42
+ * An optional function to format data for display to the user.
43
+ * This goes from dataModel -> UI display
44
+ */
45
+ format?: FormatFunction<
46
+ DataModelType | UserDisplayType,
47
+ UserDisplayType,
48
+ Options
49
+ >;
50
+
51
+ /**
52
+ * An optional function for undo the action of a format function for storage.
53
+ * This goes from UI -> dataModel
54
+ */
55
+ deformat?: FormatFunction<
56
+ UserDisplayType | DataModelType,
57
+ DataModelType,
58
+ Options
59
+ >;
60
+ }
@@ -0,0 +1,188 @@
1
+ import { setIn } from 'timm';
2
+ import type { Expression } from '@player-ui/types';
3
+ import type { DataModelWithParser } from '../data';
4
+
5
+ const DOUBLE_OPEN_CURLY = '{{';
6
+ const DOUBLE_CLOSE_CURLY = '}}';
7
+
8
+ export interface Options {
9
+ /** The model to use when resolving refs */
10
+ model: DataModelWithParser;
11
+
12
+ /** A function to evaluate an expression */
13
+ evaluate: (exp: Expression) => any;
14
+ }
15
+
16
+ /** Search the given string for the coordinates of the next expression to resolve */
17
+ export function findNextExp(str: string) {
18
+ const expStart = str.indexOf(DOUBLE_OPEN_CURLY);
19
+
20
+ if (expStart === -1) {
21
+ return undefined;
22
+ }
23
+
24
+ let count = 1;
25
+ let offset = expStart + DOUBLE_OPEN_CURLY.length;
26
+ let workingString = str.substring(expStart + DOUBLE_OPEN_CURLY.length);
27
+
28
+ while (count > 0 && workingString.length > 0) {
29
+ // Find the next open or close curly
30
+ const nextCloseCurly = workingString.indexOf(DOUBLE_CLOSE_CURLY);
31
+
32
+ // We can't close anything, so there's no point in going on with life.
33
+ if (nextCloseCurly === -1) {
34
+ break;
35
+ }
36
+
37
+ const nextOpenCurly = workingString.indexOf(DOUBLE_OPEN_CURLY);
38
+
39
+ if (nextOpenCurly !== -1 && nextOpenCurly < nextCloseCurly) {
40
+ // We've hit another open bracket before closing out the one we want
41
+ // Move everything over and bump our close count by 1
42
+ count++;
43
+ workingString = workingString.substring(
44
+ nextOpenCurly + DOUBLE_OPEN_CURLY.length
45
+ );
46
+ offset += nextOpenCurly + DOUBLE_OPEN_CURLY.length;
47
+ } else {
48
+ // We've hit another closing bracket
49
+ // Decrement our count and updates offsets
50
+ count--;
51
+ workingString = workingString.substring(
52
+ nextCloseCurly + DOUBLE_CLOSE_CURLY.length
53
+ );
54
+ offset += nextCloseCurly + DOUBLE_CLOSE_CURLY.length;
55
+ }
56
+ }
57
+
58
+ if (count !== 0) {
59
+ throw new Error(`Unbalanced {{ and }} in exp: ${str}`);
60
+ }
61
+
62
+ return {
63
+ start: expStart,
64
+ end: offset,
65
+ };
66
+ }
67
+
68
+ /** Finds any subset of the string wrapped in @[]@ and evaluates it as an expression */
69
+ export function resolveExpressionsInString(
70
+ val: string,
71
+ { evaluate }: Options
72
+ ): string {
73
+ const expMatch = /@\[.*?\]@/;
74
+ let newVal = val;
75
+ let match = newVal.match(expMatch);
76
+
77
+ while (match !== null) {
78
+ const expStrWithBrackets = match[0];
79
+ const matchStart = newVal.indexOf(expStrWithBrackets);
80
+
81
+ const expString = expStrWithBrackets.substr(
82
+ '@['.length,
83
+ expStrWithBrackets.length - '@['.length - ']@'.length
84
+ );
85
+ const expValue = evaluate(expString);
86
+
87
+ // The string is only the expression, return the raw value.
88
+ if (
89
+ matchStart === 0 &&
90
+ expStrWithBrackets === val &&
91
+ typeof expValue !== 'string'
92
+ ) {
93
+ return expValue;
94
+ }
95
+
96
+ newVal =
97
+ newVal.substr(0, matchStart) +
98
+ expValue +
99
+ newVal.substr(matchStart + expStrWithBrackets.length);
100
+ // remove the surrounding @[]@ to get the expression
101
+ match = newVal.match(expMatch);
102
+ }
103
+
104
+ return newVal;
105
+ }
106
+
107
+ /** Return a string with all data model references resolved */
108
+ export function resolveDataRefsInString(val: string, options: Options): string {
109
+ const { model } = options;
110
+ let workingString = resolveExpressionsInString(val, options);
111
+
112
+ if (
113
+ typeof workingString !== 'string' ||
114
+ workingString.indexOf(DOUBLE_OPEN_CURLY) === -1
115
+ ) {
116
+ return workingString;
117
+ }
118
+
119
+ while (workingString.indexOf(DOUBLE_OPEN_CURLY) !== -1) {
120
+ const expLocation = findNextExp(workingString);
121
+
122
+ if (!expLocation) {
123
+ return workingString;
124
+ }
125
+
126
+ const { start, end } = expLocation;
127
+
128
+ // Strip out the wrapping curlies from {{binding}} before passing to the model
129
+ const binding = workingString
130
+ .substring(
131
+ start + DOUBLE_OPEN_CURLY.length,
132
+ end - DOUBLE_OPEN_CURLY.length
133
+ )
134
+ .trim();
135
+
136
+ const evaledVal = model.get(binding, { formatted: true });
137
+
138
+ // Exit early if the string is _just_ a model lookup
139
+ // If the result is a string, we may need further processing for nested bindings
140
+ if (
141
+ start === 0 &&
142
+ end === workingString.length &&
143
+ typeof evaledVal !== 'string'
144
+ ) {
145
+ return evaledVal;
146
+ }
147
+
148
+ workingString =
149
+ workingString.substr(0, start) + evaledVal + workingString.substr(end);
150
+ }
151
+
152
+ return workingString;
153
+ }
154
+
155
+ /** Traverse the thing and replace any model refs */
156
+ function traverseObject<T>(val: T, options: Options): T {
157
+ switch (typeof val) {
158
+ case 'string': {
159
+ return resolveDataRefsInString(val as string, options) as unknown as T;
160
+ }
161
+
162
+ case 'object': {
163
+ // TODO: Do we care refs in keys?
164
+ const keys = Object.keys(val);
165
+ let newVal = val;
166
+
167
+ if (keys.length > 0) {
168
+ for (const key of keys) {
169
+ newVal = setIn(
170
+ newVal as any,
171
+ [key],
172
+ traverseObject((val as any)[key], options)
173
+ ) as any;
174
+ }
175
+ }
176
+
177
+ return newVal;
178
+ }
179
+
180
+ default:
181
+ return val;
182
+ }
183
+ }
184
+
185
+ /** Recursively resolve all model refs in whatever you pass in */
186
+ export function resolveDataRefs<T>(val: T, options: Options): T {
187
+ return traverseObject(val, options);
188
+ }
package/src/types.ts CHANGED
@@ -1,17 +1,15 @@
1
1
  import type { Flow, FlowResult } from '@player-ui/types';
2
- import { Expression } from '@player-ui/types';
3
- import type { DataModelWithParser } from '@player-ui/data';
4
- import { DataModelOptions } from '@player-ui/data';
5
- import type { FlowController } from '@player-ui/flow';
6
- import { NamedState, TransitionFunction } from '@player-ui/flow';
7
- import { ViewInstance } from '@player-ui/view';
8
- import type { BindingParser, BindingLike } from '@player-ui/binding';
9
- import type { SchemaController } from '@player-ui/schema';
10
- import type { ExpressionEvaluator } from '@player-ui/expressions';
11
- import type { Logger } from '@player-ui/logger';
12
- import type { ViewController } from './view';
13
- import type { DataController } from './data';
14
- import type { ValidationController } from './validation';
2
+ import type { DataModelWithParser } from './data';
3
+ import type { BindingParser, BindingLike } from './binding';
4
+ import type { SchemaController } from './schema';
5
+ import type { ExpressionEvaluator } from './expressions';
6
+ import type { Logger } from './logger';
7
+ import type {
8
+ ViewController,
9
+ DataController,
10
+ ValidationController,
11
+ FlowController,
12
+ } from './controllers';
15
13
 
16
14
  /** The status for a flow's execution state */
17
15
  export type PlayerFlowStatus =
@@ -0,0 +1 @@
1
+ export * from './replaceParams';
@@ -0,0 +1,17 @@
1
+ const ANY_CHAR_REGEX = /%([a-zA-Z]+)/g;
2
+
3
+ /**
4
+ * Replaces %num in message with the provided parameters in order.
5
+ *
6
+ * @param message - Parameterized string like "This is a %1"
7
+ * @param params - Parameters to replace in message E.g. ['tax2021.amount']
8
+ * @returns A message with the parameters replaced.
9
+ */
10
+ export function replaceParams(
11
+ message: string,
12
+ params: Record<string, any>
13
+ ): string {
14
+ return message
15
+ .slice()
16
+ .replace(ANY_CHAR_REGEX, (keyExpr) => params[keyExpr.slice(1)] || keyExpr);
17
+ }
@@ -0,0 +1,3 @@
1
+ export * from './validation-middleware';
2
+ export * from './types';
3
+ export * from './registry';
@@ -0,0 +1,20 @@
1
+ import type { ValidatorFunction } from './types';
2
+
3
+ /** A registry that tracks validators */
4
+ export class ValidatorRegistry {
5
+ private registry: Map<string, ValidatorFunction<any>>;
6
+
7
+ constructor() {
8
+ this.registry = new Map();
9
+ }
10
+
11
+ /** Use the given validator name to fetch the handler */
12
+ public get(name: string): ValidatorFunction | undefined {
13
+ return this.registry.get(name);
14
+ }
15
+
16
+ /** Register a new validator */
17
+ public register<T>(name: string, handler: ValidatorFunction<T>) {
18
+ this.registry.set(name, handler);
19
+ }
20
+ }
@@ -0,0 +1,75 @@
1
+ import type { Validation } from '@player-ui/types';
2
+
3
+ import type { BindingInstance, BindingFactory } from '../binding';
4
+ import type { DataModelWithParser } from '../data';
5
+ import type { ExpressionEvaluatorFunction } from '../expressions';
6
+ import type { Logger } from '../logger';
7
+ import type { ConstantsProvider } from '../controllers';
8
+
9
+ interface BaseValidationResponse<T = Validation.Severity> {
10
+ /** The validation message to show to the user */
11
+ message: string;
12
+
13
+ /** List of parameters associated with a validation. These can be replaced into a templatized message string. */
14
+ parameters?: Record<string, any>;
15
+
16
+ /** How serious is this violation */
17
+ severity: T;
18
+
19
+ /** Where this validation should be displayed */
20
+ displayTarget?: Validation.DisplayTarget;
21
+
22
+ /** The blocking state of this validation */
23
+ blocking?: boolean | 'once';
24
+ }
25
+
26
+ export interface WarningValidationResponse
27
+ extends BaseValidationResponse<'warning'> {
28
+ /** Warning validations can be dismissed without correcting the error */
29
+ dismiss?: () => void;
30
+ }
31
+
32
+ export type ErrorValidationResponse = BaseValidationResponse<'error'>;
33
+
34
+ export type ValidationResponse =
35
+ | ErrorValidationResponse
36
+ | WarningValidationResponse;
37
+
38
+ type RequiredValidationKeys = 'severity' | 'trigger';
39
+
40
+ export type ValidationObject = Validation.Reference &
41
+ Required<Pick<Validation.Reference, RequiredValidationKeys>>;
42
+
43
+ export interface ValidationProvider {
44
+ getValidationsForBinding?(
45
+ binding: BindingInstance
46
+ ): Array<ValidationObject> | undefined;
47
+
48
+ getValidationsForView?(): Array<ValidationObject> | undefined;
49
+ }
50
+
51
+ export interface ValidatorContext {
52
+ /** The data to set or get data from */
53
+ model: DataModelWithParser;
54
+
55
+ /** A means of parsing a binding */
56
+ parseBinding: BindingFactory;
57
+
58
+ /** Execute the expression and return it's result */
59
+ evaluate: ExpressionEvaluatorFunction;
60
+
61
+ /** Logger instance to use */
62
+ logger: Logger;
63
+
64
+ /** The validation object that triggered this function */
65
+ validation: ValidationObject;
66
+
67
+ /** The constants for messages */
68
+ constants: ConstantsProvider;
69
+ }
70
+
71
+ export type ValidatorFunction<Options = unknown> = (
72
+ context: ValidatorContext,
73
+ value: any,
74
+ options?: Options
75
+ ) => Omit<BaseValidationResponse, 'severity'> | undefined;
@@ -0,0 +1,114 @@
1
+ import { setIn } from 'timm';
2
+ import type { BindingInstance } from '../binding';
3
+ import type {
4
+ BatchSetTransaction,
5
+ DataModelImpl,
6
+ DataModelOptions,
7
+ DataModelMiddleware,
8
+ Updates,
9
+ } from '../data';
10
+ import { toModel } from '../data';
11
+ import type { Logger } from '../logger';
12
+
13
+ import type { ValidationResponse } from './types';
14
+
15
+ /**
16
+ * Returns a validation object if the data is invalid or an set of BindingsInstances if the binding itself is a weak ref of another invalid validation
17
+ */
18
+ export type MiddlewareChecker = (
19
+ binding: BindingInstance,
20
+ model: DataModelImpl
21
+ ) => ValidationResponse | Set<BindingInstance> | undefined;
22
+
23
+ /**
24
+ * Middleware for the data-model that caches the results of invalid data
25
+ */
26
+ export class ValidationMiddleware implements DataModelMiddleware {
27
+ public validator: MiddlewareChecker;
28
+ public shadowModelPaths: Map<BindingInstance, any>;
29
+ private logger?: Logger;
30
+
31
+ constructor(
32
+ validator: MiddlewareChecker,
33
+ options?: {
34
+ /** A logger instance */
35
+ logger?: Logger;
36
+ }
37
+ ) {
38
+ this.validator = validator;
39
+ this.shadowModelPaths = new Map();
40
+ this.logger = options?.logger;
41
+ }
42
+
43
+ public set(
44
+ transaction: BatchSetTransaction,
45
+ options?: DataModelOptions,
46
+ next?: DataModelImpl
47
+ ): Updates {
48
+ const asModel = toModel(this, { ...options, includeInvalid: true }, next);
49
+ const nextTransaction: BatchSetTransaction = [];
50
+
51
+ transaction.forEach(([binding, value]) => {
52
+ this.shadowModelPaths.set(binding, value);
53
+ });
54
+
55
+ const invalidBindings: Array<BindingInstance> = [];
56
+
57
+ this.shadowModelPaths.forEach((value, binding) => {
58
+ const validations = this.validator(binding, asModel);
59
+
60
+ if (validations === undefined) {
61
+ nextTransaction.push([binding, value]);
62
+ } else if (validations instanceof Set) {
63
+ invalidBindings.push(...validations);
64
+ } else {
65
+ this.logger?.debug(
66
+ `Invalid value for path: ${binding.asString()} - ${
67
+ validations.severity
68
+ } - ${validations.message}`
69
+ );
70
+ }
71
+ });
72
+
73
+ if (next && nextTransaction.length > 0) {
74
+ // defer clearing the shadow model to prevent validations that are run twice due to weak binding refs still needing the data
75
+ nextTransaction.forEach(([binding]) =>
76
+ this.shadowModelPaths.delete(binding)
77
+ );
78
+ return next.set(nextTransaction, options);
79
+ }
80
+
81
+ return invalidBindings.map((binding) => {
82
+ return {
83
+ binding,
84
+ oldValue: asModel.get(binding),
85
+ newValue: asModel.get(binding),
86
+ force: true,
87
+ };
88
+ });
89
+ }
90
+
91
+ public get(
92
+ binding: BindingInstance,
93
+ options?: DataModelOptions,
94
+ next?: DataModelImpl
95
+ ) {
96
+ let val = next?.get(binding, options);
97
+
98
+ if (options?.includeInvalid === true) {
99
+ this.shadowModelPaths.forEach((shadowValue, shadowBinding) => {
100
+ if (shadowBinding === binding) {
101
+ val = shadowValue;
102
+
103
+ return;
104
+ }
105
+
106
+ if (binding.contains(shadowBinding)) {
107
+ val = setIn(val, shadowBinding.relative(binding), shadowValue);
108
+ }
109
+ });
110
+ }
111
+
112
+ return val;
113
+ }
114
+ }
@@ -0,0 +1,81 @@
1
+ import type { Node, AnyAssetType } from '../parser';
2
+ import { NodeType } from '../parser';
3
+
4
+ /**
5
+ * Functions for building AST nodes (relatively) easily
6
+ */
7
+ export class Builder {
8
+ /**
9
+ * Creates an asset node
10
+ *
11
+ * @param value - the value to put in the asset node
12
+ */
13
+ static asset<T extends AnyAssetType>(value: T): Node.Asset<T> {
14
+ return {
15
+ type: NodeType.Asset,
16
+ value,
17
+ };
18
+ }
19
+
20
+ /**
21
+ * Creates a value node
22
+ *
23
+ * @param v - The object to put in the value node
24
+ */
25
+ static value(v?: object): Node.Value {
26
+ return {
27
+ type: NodeType.Value,
28
+ value: v,
29
+ };
30
+ }
31
+
32
+ /**
33
+ * Creates a multiNode and associates the multiNode as the parent
34
+ * of all the value nodes
35
+ *
36
+ * @param values - the value or applicability nodes to put in the multinode
37
+ */
38
+ static multiNode(
39
+ ...values: (Node.Value | Node.Applicability)[]
40
+ ): Node.MultiNode {
41
+ const m: Node.MultiNode = {
42
+ type: NodeType.MultiNode,
43
+ override: true,
44
+ values,
45
+ };
46
+
47
+ values.forEach((v) => {
48
+ // eslint-disable-next-line no-param-reassign
49
+ v.parent = m;
50
+ });
51
+
52
+ return m;
53
+ }
54
+
55
+ /**
56
+ * Adds a child node to a node
57
+ *
58
+ * @param node - The node to add a child to
59
+ * @param path - The path at which to add the child
60
+ * @param child - The child node
61
+ */
62
+ static addChild<N extends Node.BaseWithChildren<NT>, NT extends NodeType>(
63
+ node: N,
64
+ path: Node.PathSegment | Node.PathSegment[],
65
+ child: Node.Node
66
+ ) {
67
+ // eslint-disable-next-line no-param-reassign
68
+ child.parent = node as Node.Node;
69
+
70
+ const newChild: Node.Child = {
71
+ path: Array.isArray(path) ? path : [path],
72
+ value: child,
73
+ };
74
+
75
+ // eslint-disable-next-line no-param-reassign
76
+ node.children = node.children || [];
77
+ node.children.push(newChild);
78
+
79
+ return node;
80
+ }
81
+ }
package/src/view/index.ts CHANGED
@@ -1,4 +1,5 @@
1
- export * from './asset-transform';
2
- export * from './controller';
3
- export * from './store';
4
- export * from './types';
1
+ export * from './view';
2
+ export * from './resolver';
3
+ export * from './parser';
4
+ export * from './builder';
5
+ export * from './plugins';