@player-ui/player 0.8.0--canary.307.9621 → 0.8.0-next.0
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/Player.native.js +11630 -0
- package/dist/Player.native.js.map +1 -0
- package/dist/cjs/index.cjs +5626 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/{index.esm.js → index.legacy-esm.js} +2044 -1667
- package/dist/{index.cjs.js → index.mjs} +2052 -1761
- package/dist/index.mjs.map +1 -0
- package/package.json +29 -63
- package/src/__tests__/data.test.ts +498 -0
- package/src/__tests__/flow.test.ts +312 -0
- package/src/__tests__/helpers/action-exp.plugin.ts +22 -0
- package/src/__tests__/helpers/actions.flow.ts +67 -0
- package/src/__tests__/helpers/binding.plugin.ts +125 -0
- package/src/__tests__/helpers/expression.plugin.ts +88 -0
- package/src/__tests__/helpers/transform-plugin.ts +19 -0
- package/src/__tests__/helpers/validation.flow.ts +56 -0
- package/src/__tests__/player.test.ts +597 -0
- package/src/__tests__/string-resolver.test.ts +186 -0
- package/src/__tests__/validation.test.ts +3555 -0
- package/src/__tests__/view.test.ts +715 -0
- package/src/binding/__tests__/binding.test.ts +113 -0
- package/src/binding/__tests__/index.test.ts +208 -0
- package/src/binding/__tests__/resolver.test.ts +83 -0
- package/src/binding/binding.ts +6 -6
- package/src/binding/index.ts +34 -34
- package/src/binding/resolver.ts +19 -19
- package/src/binding/utils.ts +7 -7
- package/src/binding-grammar/__tests__/parser.test.ts +64 -0
- package/src/binding-grammar/__tests__/test-utils/ast-cases.ts +198 -0
- package/src/binding-grammar/__tests__/test-utils/perf-test.ts +66 -0
- package/src/binding-grammar/ast.ts +11 -11
- package/src/binding-grammar/custom/index.ts +19 -22
- package/src/binding-grammar/ebnf/index.ts +20 -21
- package/src/binding-grammar/ebnf/types.ts +13 -13
- package/src/binding-grammar/index.ts +4 -4
- package/src/binding-grammar/parsimmon/index.ts +14 -14
- package/src/controllers/constants/__tests__/index.test.ts +106 -0
- package/src/controllers/constants/index.ts +3 -3
- package/src/controllers/constants/utils.ts +4 -4
- package/src/controllers/data/controller.ts +22 -22
- package/src/controllers/data/index.ts +1 -1
- package/src/controllers/data/utils.ts +7 -7
- package/src/controllers/flow/__tests__/controller.test.ts +195 -0
- package/src/controllers/flow/__tests__/flow.test.ts +381 -0
- package/src/controllers/flow/controller.ts +13 -13
- package/src/controllers/flow/flow.ts +23 -23
- package/src/controllers/flow/index.ts +2 -2
- package/src/controllers/index.ts +5 -5
- package/src/controllers/validation/binding-tracker.ts +71 -59
- package/src/controllers/validation/controller.ts +104 -104
- package/src/controllers/validation/index.ts +2 -2
- package/src/controllers/view/asset-transform.ts +20 -20
- package/src/controllers/view/controller.ts +27 -27
- package/src/controllers/view/index.ts +4 -4
- package/src/controllers/view/store.ts +3 -3
- package/src/controllers/view/types.ts +7 -7
- package/src/data/__tests__/__snapshots__/dependency-tracker.test.ts.snap +64 -0
- package/src/data/__tests__/dependency-tracker.test.ts +146 -0
- package/src/data/__tests__/local-model.test.ts +46 -0
- package/src/data/__tests__/model.test.ts +78 -0
- package/src/data/dependency-tracker.ts +16 -16
- package/src/data/index.ts +4 -4
- package/src/data/local-model.ts +6 -6
- package/src/data/model.ts +17 -17
- package/src/data/noop-model.ts +1 -1
- package/src/expressions/__tests__/__snapshots__/parser.test.ts.snap +854 -0
- package/src/expressions/__tests__/evaluator-functions.test.ts +47 -0
- package/src/expressions/__tests__/evaluator.test.ts +410 -0
- package/src/expressions/__tests__/parser.test.ts +115 -0
- package/src/expressions/__tests__/utils.test.ts +44 -0
- package/src/expressions/evaluator-functions.ts +6 -6
- package/src/expressions/evaluator.ts +71 -67
- package/src/expressions/index.ts +4 -4
- package/src/expressions/parser.ts +102 -105
- package/src/expressions/types.ts +29 -21
- package/src/expressions/utils.ts +32 -21
- package/src/index.ts +13 -13
- package/src/logger/__tests__/consoleLogger.test.ts +46 -0
- package/src/logger/__tests__/noopLogger.test.ts +13 -0
- package/src/logger/__tests__/proxyLogger.test.ts +31 -0
- package/src/logger/__tests__/tapableLogger.test.ts +41 -0
- package/src/logger/consoleLogger.ts +9 -9
- package/src/logger/index.ts +5 -5
- package/src/logger/noopLogger.ts +1 -1
- package/src/logger/proxyLogger.ts +6 -6
- package/src/logger/tapableLogger.ts +7 -7
- package/src/logger/types.ts +2 -2
- package/src/player.ts +60 -58
- package/src/plugins/default-exp-plugin.ts +10 -10
- package/src/plugins/default-view-plugin.ts +29 -0
- package/src/plugins/flow-exp-plugin.ts +6 -6
- package/src/schema/__tests__/schema.test.ts +243 -0
- package/src/schema/index.ts +2 -2
- package/src/schema/schema.ts +24 -24
- package/src/schema/types.ts +4 -4
- package/src/string-resolver/__tests__/index.test.ts +361 -0
- package/src/string-resolver/index.ts +17 -17
- package/src/types.ts +17 -17
- package/src/utils/__tests__/replaceParams.test.ts +33 -0
- package/src/utils/index.ts +1 -1
- package/src/utils/replaceParams.ts +1 -1
- package/src/validator/__tests__/binding-map-splice.test.ts +53 -0
- package/src/validator/__tests__/validation-middleware.test.ts +127 -0
- package/src/validator/binding-map-splice.ts +5 -5
- package/src/validator/index.ts +4 -4
- package/src/validator/registry.ts +1 -1
- package/src/validator/types.ts +13 -13
- package/src/validator/validation-middleware.ts +15 -15
- package/src/view/__tests__/view.immutable.test.ts +269 -0
- package/src/view/__tests__/view.test.ts +959 -0
- package/src/view/builder/index.test.ts +69 -0
- package/src/view/builder/index.ts +3 -3
- package/src/view/index.ts +5 -5
- package/src/view/parser/__tests__/__snapshots__/parser.test.ts.snap +394 -0
- package/src/view/parser/__tests__/parser.test.ts +264 -0
- package/src/view/parser/index.ts +43 -33
- package/src/view/parser/types.ts +11 -11
- package/src/view/parser/utils.ts +5 -5
- package/src/view/plugins/__tests__/__snapshots__/template.test.ts.snap +278 -0
- package/src/view/plugins/__tests__/applicability.test.ts +265 -0
- package/src/view/plugins/__tests__/string.test.ts +122 -0
- package/src/view/plugins/__tests__/template.test.ts +724 -0
- package/src/view/plugins/applicability.ts +19 -19
- package/src/view/plugins/index.ts +4 -5
- package/src/view/plugins/options.ts +1 -1
- package/src/view/plugins/string-resolver.ts +22 -22
- package/src/view/plugins/switch.ts +22 -23
- package/src/view/plugins/template-plugin.ts +26 -27
- package/src/view/resolver/__tests__/dependencies.test.ts +321 -0
- package/src/view/resolver/__tests__/edgecases.test.ts +626 -0
- package/src/view/resolver/index.ts +42 -42
- package/src/view/resolver/types.ts +21 -20
- package/src/view/resolver/utils.ts +9 -9
- package/src/view/view.ts +32 -22
- package/types/binding/binding.d.ts +50 -0
- package/types/binding/index.d.ts +29 -0
- package/types/binding/resolver.d.ts +26 -0
- package/types/binding/utils.d.ts +12 -0
- package/types/binding-grammar/ast.d.ts +67 -0
- package/types/binding-grammar/custom/index.d.ts +4 -0
- package/types/binding-grammar/ebnf/index.d.ts +4 -0
- package/types/binding-grammar/ebnf/types.d.ts +75 -0
- package/types/binding-grammar/index.d.ts +5 -0
- package/types/binding-grammar/parsimmon/index.d.ts +4 -0
- package/types/controllers/constants/index.d.ts +45 -0
- package/types/controllers/constants/utils.d.ts +6 -0
- package/types/controllers/data/controller.d.ts +45 -0
- package/types/controllers/data/index.d.ts +2 -0
- package/types/controllers/data/utils.d.ts +14 -0
- package/types/controllers/flow/controller.d.ts +25 -0
- package/types/controllers/flow/flow.d.ts +50 -0
- package/types/controllers/flow/index.d.ts +3 -0
- package/types/controllers/index.d.ts +6 -0
- package/types/controllers/validation/binding-tracker.d.ts +32 -0
- package/types/controllers/validation/controller.d.ts +151 -0
- package/types/controllers/validation/index.d.ts +3 -0
- package/types/controllers/view/asset-transform.d.ts +19 -0
- package/types/controllers/view/controller.d.ts +37 -0
- package/types/controllers/view/index.d.ts +5 -0
- package/types/controllers/view/store.d.ts +20 -0
- package/types/controllers/view/types.d.ts +16 -0
- package/types/data/dependency-tracker.d.ts +49 -0
- package/types/data/index.d.ts +5 -0
- package/types/data/local-model.d.ts +16 -0
- package/types/data/model.d.ts +86 -0
- package/types/data/noop-model.d.ts +13 -0
- package/types/expressions/evaluator-functions.d.ts +15 -0
- package/types/expressions/evaluator.d.ts +52 -0
- package/types/expressions/index.d.ts +5 -0
- package/types/expressions/parser.d.ts +10 -0
- package/types/expressions/types.d.ts +144 -0
- package/types/expressions/utils.d.ts +12 -0
- package/types/index.d.ts +14 -0
- package/types/logger/consoleLogger.d.ts +17 -0
- package/types/logger/index.d.ts +6 -0
- package/types/logger/noopLogger.d.ts +10 -0
- package/types/logger/proxyLogger.d.ts +15 -0
- package/types/logger/tapableLogger.d.ts +23 -0
- package/types/logger/types.d.ts +6 -0
- package/types/player.d.ts +101 -0
- package/types/plugins/default-exp-plugin.d.ts +9 -0
- package/types/plugins/default-view-plugin.d.ts +9 -0
- package/types/plugins/flow-exp-plugin.d.ts +11 -0
- package/types/schema/index.d.ts +3 -0
- package/types/schema/schema.d.ts +36 -0
- package/types/schema/types.d.ts +38 -0
- package/types/string-resolver/index.d.ts +30 -0
- package/types/types.d.ts +73 -0
- package/types/utils/index.d.ts +2 -0
- package/types/utils/replaceParams.d.ts +9 -0
- package/types/validator/binding-map-splice.d.ts +10 -0
- package/types/validator/index.d.ts +5 -0
- package/types/validator/registry.d.ts +11 -0
- package/types/validator/types.d.ts +53 -0
- package/types/validator/validation-middleware.d.ts +36 -0
- package/types/view/builder/index.d.ts +35 -0
- package/types/view/index.d.ts +6 -0
- package/types/view/parser/index.d.ts +52 -0
- package/types/view/parser/types.d.ts +109 -0
- package/types/view/parser/utils.d.ts +6 -0
- package/types/view/plugins/applicability.d.ts +10 -0
- package/types/view/plugins/index.d.ts +5 -0
- package/types/view/plugins/options.d.ts +4 -0
- package/types/view/plugins/string-resolver.d.ts +13 -0
- package/types/view/plugins/switch.d.ts +14 -0
- package/types/view/plugins/template-plugin.d.ts +33 -0
- package/types/view/resolver/index.d.ts +73 -0
- package/types/view/resolver/types.d.ts +129 -0
- package/types/view/resolver/utils.d.ts +11 -0
- package/types/view/view.d.ts +37 -0
- package/dist/index.d.ts +0 -1814
- package/dist/player.dev.js +0 -11472
- package/dist/player.prod.js +0 -2
- package/src/view/plugins/plugin.ts +0 -21
package/src/binding/resolver.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { NestedError } from
|
|
2
|
-
import type { SyncWaterfallHook } from
|
|
3
|
-
import type { PathNode, AnyNode } from
|
|
4
|
-
import { findInArray, maybeConvertToNum } from
|
|
1
|
+
import { NestedError } from "ts-nested-error";
|
|
2
|
+
import type { SyncWaterfallHook } from "tapable-ts";
|
|
3
|
+
import type { PathNode, AnyNode } from "../binding-grammar";
|
|
4
|
+
import { findInArray, maybeConvertToNum } from "./utils";
|
|
5
5
|
|
|
6
6
|
export interface NormalizedResult {
|
|
7
7
|
/** The normalized path */
|
|
@@ -33,7 +33,7 @@ export interface ResolveBindingASTHooks {
|
|
|
33
33
|
export function resolveBindingAST(
|
|
34
34
|
bindingPathNode: PathNode,
|
|
35
35
|
options: ResolveBindingASTOptions,
|
|
36
|
-
hooks?: ResolveBindingASTHooks
|
|
36
|
+
hooks?: ResolveBindingASTHooks,
|
|
37
37
|
): NormalizedResult {
|
|
38
38
|
const context: Required<NormalizedResult> = {
|
|
39
39
|
updates: {},
|
|
@@ -45,11 +45,11 @@ export function resolveBindingAST(
|
|
|
45
45
|
|
|
46
46
|
/** Get the value for any child node */
|
|
47
47
|
function getValueForNode(node: AnyNode): any {
|
|
48
|
-
if (node.name ===
|
|
48
|
+
if (node.name === "Value") {
|
|
49
49
|
return node.value;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
if (node.name ===
|
|
52
|
+
if (node.name === "PathNode") {
|
|
53
53
|
const nestedResolvedValue = resolveBindingAST(node, options);
|
|
54
54
|
|
|
55
55
|
if (nestedResolvedValue.updates) {
|
|
@@ -61,17 +61,17 @@ export function resolveBindingAST(
|
|
|
61
61
|
|
|
62
62
|
try {
|
|
63
63
|
return options.convertToPath(
|
|
64
|
-
options.getValue(nestedResolvedValue.path)
|
|
64
|
+
options.getValue(nestedResolvedValue.path),
|
|
65
65
|
);
|
|
66
66
|
} catch (e: any) {
|
|
67
67
|
throw new NestedError(
|
|
68
68
|
`Unable to resolve path segment: ${nestedResolvedValue.path}`,
|
|
69
|
-
e
|
|
69
|
+
e,
|
|
70
70
|
);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
if (node.name ===
|
|
74
|
+
if (node.name === "Expression") {
|
|
75
75
|
try {
|
|
76
76
|
const actualValue = options.evaluate(node.value);
|
|
77
77
|
|
|
@@ -86,8 +86,8 @@ export function resolveBindingAST(
|
|
|
86
86
|
|
|
87
87
|
/** Handle when path segments are binding paths (foo.bar) or single segments (foo) */
|
|
88
88
|
function appendPathSegments(segment: string | number) {
|
|
89
|
-
if (typeof segment ===
|
|
90
|
-
segment.split(
|
|
89
|
+
if (typeof segment === "string" && segment.indexOf(".") > -1) {
|
|
90
|
+
segment.split(".").forEach((i) => {
|
|
91
91
|
context.path.push(maybeConvertToNum(i));
|
|
92
92
|
});
|
|
93
93
|
} else {
|
|
@@ -101,16 +101,16 @@ export function resolveBindingAST(
|
|
|
101
101
|
hooks?.beforeResolveNode.call(_node, { ...context, ...options }) ?? _node;
|
|
102
102
|
|
|
103
103
|
switch (resolvedNode.name) {
|
|
104
|
-
case
|
|
105
|
-
case
|
|
104
|
+
case "Expression":
|
|
105
|
+
case "PathNode":
|
|
106
106
|
appendPathSegments(getValueForNode(resolvedNode));
|
|
107
107
|
break;
|
|
108
108
|
|
|
109
|
-
case
|
|
109
|
+
case "Value":
|
|
110
110
|
appendPathSegments(resolvedNode.value);
|
|
111
111
|
break;
|
|
112
112
|
|
|
113
|
-
case
|
|
113
|
+
case "Query": {
|
|
114
114
|
// Look for an object at the path with the given key/val criteria
|
|
115
115
|
const objToQuery: Record<string, any>[] =
|
|
116
116
|
options.getValue(context.path) ?? [];
|
|
@@ -124,7 +124,7 @@ export function resolveBindingAST(
|
|
|
124
124
|
|
|
125
125
|
if (index === undefined || index === -1) {
|
|
126
126
|
context.updates[
|
|
127
|
-
[...context.path, objToQuery.length, resolvedKey].join(
|
|
127
|
+
[...context.path, objToQuery.length, resolvedKey].join(".")
|
|
128
128
|
] = resolvedValue;
|
|
129
129
|
context.path.push(objToQuery.length);
|
|
130
130
|
} else {
|
|
@@ -134,8 +134,8 @@ export function resolveBindingAST(
|
|
|
134
134
|
break;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
case
|
|
138
|
-
context.path.push(resolvedNode.value.map(getValueForNode).join(
|
|
137
|
+
case "Concatenated":
|
|
138
|
+
context.path.push(resolvedNode.value.map(getValueForNode).join(""));
|
|
139
139
|
break;
|
|
140
140
|
|
|
141
141
|
default:
|
package/src/binding/utils.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { BindingLike, BindingInstance } from
|
|
1
|
+
import type { BindingLike, BindingInstance } from "./binding";
|
|
2
2
|
|
|
3
3
|
/** Check if the parameter representing a binding is already of the Binding class */
|
|
4
4
|
export function isBinding(binding: BindingLike): binding is BindingInstance {
|
|
5
|
-
return !(typeof binding ===
|
|
5
|
+
return !(typeof binding === "string" || Array.isArray(binding));
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
/** Convert the string to an int if you can, otherwise just return the original string */
|
|
@@ -20,14 +20,14 @@ export function maybeConvertToNum(i: string): string | number {
|
|
|
20
20
|
* utility to convert binding into binding segments.
|
|
21
21
|
*/
|
|
22
22
|
export function getBindingSegments(
|
|
23
|
-
binding: BindingLike
|
|
23
|
+
binding: BindingLike,
|
|
24
24
|
): Array<string | number> {
|
|
25
25
|
if (Array.isArray(binding)) {
|
|
26
26
|
return binding;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
if (typeof binding ===
|
|
30
|
-
return binding.split(
|
|
29
|
+
if (typeof binding === "string") {
|
|
30
|
+
return binding.split(".");
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
return binding.asArray();
|
|
@@ -37,10 +37,10 @@ export function getBindingSegments(
|
|
|
37
37
|
export function findInArray<T extends Record<string | number, object>>(
|
|
38
38
|
array: Array<T>,
|
|
39
39
|
key: string | number,
|
|
40
|
-
value: T
|
|
40
|
+
value: T,
|
|
41
41
|
): number | undefined {
|
|
42
42
|
return array.findIndex((obj) => {
|
|
43
|
-
if (obj && typeof obj ===
|
|
43
|
+
if (obj && typeof obj === "object") {
|
|
44
44
|
// Intentional double-equals because we want '4' to be coerced to 4
|
|
45
45
|
// eslint-disable-next-line eqeqeq
|
|
46
46
|
return obj[key] == value;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
VALID_AST_PARSER_TESTS,
|
|
5
|
+
INVALID_AST_PARSER_TESTS,
|
|
6
|
+
VALID_AST_PARSER_CUSTOM_TESTS,
|
|
7
|
+
} from "./test-utils/ast-cases";
|
|
8
|
+
import type { ParserSuccessResult, ParserFailureResult } from "../ast";
|
|
9
|
+
import { parse as parseParsimmon } from "../parsimmon";
|
|
10
|
+
import { parse as parseEBNF } from "../ebnf";
|
|
11
|
+
import { parse as parseCustom } from "../custom";
|
|
12
|
+
|
|
13
|
+
describe("parsimmon", () => {
|
|
14
|
+
test.each(VALID_AST_PARSER_TESTS)("Parsimmon Valid: %s", (binding, AST) => {
|
|
15
|
+
const result = parseParsimmon(binding);
|
|
16
|
+
|
|
17
|
+
expect(result.status).toBe(true);
|
|
18
|
+
expect((result as ParserSuccessResult).path).toStrictEqual(AST);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test.each(INVALID_AST_PARSER_TESTS)("Parsimmon Invalid: %s", (binding) => {
|
|
22
|
+
const result = parseParsimmon(binding);
|
|
23
|
+
expect(result.status).toBe(false);
|
|
24
|
+
expect((result as ParserFailureResult).error.length > 0).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("ebnf", () => {
|
|
29
|
+
test.each(VALID_AST_PARSER_TESTS)("EBNF Valid: %s", (binding, AST) => {
|
|
30
|
+
const result = parseEBNF(binding);
|
|
31
|
+
|
|
32
|
+
expect(result.status).toBe(true);
|
|
33
|
+
expect((result as ParserSuccessResult).path).toStrictEqual(AST);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test.each(INVALID_AST_PARSER_TESTS)("EBNF Invalid: %s", (binding) => {
|
|
37
|
+
const result = parseEBNF(binding);
|
|
38
|
+
expect(result.status).toBe(false);
|
|
39
|
+
expect((result as ParserFailureResult).error.length > 0).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe("custom", () => {
|
|
44
|
+
test.each(VALID_AST_PARSER_TESTS)("Custom Valid: %s", (binding, AST) => {
|
|
45
|
+
const result = parseCustom(binding);
|
|
46
|
+
expect(result.status).toBe(true);
|
|
47
|
+
expect((result as ParserSuccessResult).path).toStrictEqual(AST);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test.each(INVALID_AST_PARSER_TESTS)("Custom Invalid: %s", (binding) => {
|
|
51
|
+
const result = parseCustom(binding);
|
|
52
|
+
expect(result.status).toBe(false);
|
|
53
|
+
expect((result as ParserFailureResult).error.length > 0).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test.each(VALID_AST_PARSER_CUSTOM_TESTS)(
|
|
57
|
+
"Custom Unicode Valid: %s",
|
|
58
|
+
(binding, AST) => {
|
|
59
|
+
const result = parseCustom(binding);
|
|
60
|
+
expect(result.status).toBe(true);
|
|
61
|
+
expect((result as ParserSuccessResult).path).toStrictEqual(AST);
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
});
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import type { PathNode } from "../../ast";
|
|
2
|
+
import {
|
|
3
|
+
toValue,
|
|
4
|
+
toQuery,
|
|
5
|
+
toConcatenatedNode,
|
|
6
|
+
toPath,
|
|
7
|
+
toExpression,
|
|
8
|
+
} from "../../ast";
|
|
9
|
+
|
|
10
|
+
export const VALID_AST_PARSER_TESTS: Array<[string, PathNode]> = [
|
|
11
|
+
// Basic ones
|
|
12
|
+
["", toPath([])],
|
|
13
|
+
["foo", toPath([toValue("foo")])],
|
|
14
|
+
["foo.bar", toPath([toValue("foo"), toValue("bar")])],
|
|
15
|
+
["1234567890_ABC_abc", toPath([toValue("1234567890_ABC_abc")])],
|
|
16
|
+
["a-b-c-d-", toPath([toValue("a-b-c-d-")])],
|
|
17
|
+
|
|
18
|
+
// Queries
|
|
19
|
+
[
|
|
20
|
+
"foo[x=something]",
|
|
21
|
+
toPath([toValue("foo"), toQuery(toValue("x"), toValue("something"))]),
|
|
22
|
+
],
|
|
23
|
+
[
|
|
24
|
+
"foo[x == something]",
|
|
25
|
+
toPath([toValue("foo"), toQuery(toValue("x"), toValue("something"))]),
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
"foo[x === something]",
|
|
29
|
+
toPath([toValue("foo"), toQuery(toValue("x"), toValue("something"))]),
|
|
30
|
+
],
|
|
31
|
+
[
|
|
32
|
+
`foo[x = 'something']`,
|
|
33
|
+
toPath([toValue("foo"), toQuery(toValue("x"), toValue("something"))]),
|
|
34
|
+
],
|
|
35
|
+
[
|
|
36
|
+
'foo[x == "something"]',
|
|
37
|
+
toPath([toValue("foo"), toQuery(toValue("x"), toValue("something"))]),
|
|
38
|
+
],
|
|
39
|
+
[
|
|
40
|
+
'foo["x" = "something"]',
|
|
41
|
+
toPath([toValue("foo"), toQuery(toValue("x"), toValue("something"))]),
|
|
42
|
+
],
|
|
43
|
+
[
|
|
44
|
+
`foo['x'='hello [!@#$%^&*().]']`,
|
|
45
|
+
toPath([
|
|
46
|
+
toValue("foo"),
|
|
47
|
+
toQuery(toValue("x"), toValue("hello [!@#$%^&*().]")),
|
|
48
|
+
]),
|
|
49
|
+
],
|
|
50
|
+
[
|
|
51
|
+
`foo['Month'='New[.]]Mo,nth.']`,
|
|
52
|
+
toPath([
|
|
53
|
+
toValue("foo"),
|
|
54
|
+
toQuery(toValue("Month"), toValue("New[.]]Mo,nth.")),
|
|
55
|
+
]),
|
|
56
|
+
],
|
|
57
|
+
|
|
58
|
+
// Nested Paths
|
|
59
|
+
["{{foo}}", toPath([toPath([toValue("foo")])])],
|
|
60
|
+
["{{hello.world}}", toPath([toPath([toValue("hello"), toValue("world")])])],
|
|
61
|
+
[
|
|
62
|
+
"foo.bar.{{nested}}.baz",
|
|
63
|
+
toPath([
|
|
64
|
+
toValue("foo"),
|
|
65
|
+
toValue("bar"),
|
|
66
|
+
toPath([toValue("nested")]),
|
|
67
|
+
toValue("baz"),
|
|
68
|
+
]),
|
|
69
|
+
],
|
|
70
|
+
[
|
|
71
|
+
"foo[{{bar}}].baz",
|
|
72
|
+
toPath([toValue("foo"), toPath([toValue("bar")]), toValue("baz")]),
|
|
73
|
+
],
|
|
74
|
+
|
|
75
|
+
// Brackets
|
|
76
|
+
[
|
|
77
|
+
"foo[a][b][c]",
|
|
78
|
+
toPath([toValue("foo"), toValue("a"), toValue("b"), toValue("c")]),
|
|
79
|
+
],
|
|
80
|
+
[
|
|
81
|
+
`foo['a']["b"][c="bar"]`,
|
|
82
|
+
toPath([
|
|
83
|
+
toValue("foo"),
|
|
84
|
+
toValue("a"),
|
|
85
|
+
toValue("b"),
|
|
86
|
+
toQuery(toValue("c"), toValue("bar")),
|
|
87
|
+
]),
|
|
88
|
+
],
|
|
89
|
+
[
|
|
90
|
+
'foo[a][b == " [x ]" ][c]',
|
|
91
|
+
toPath([
|
|
92
|
+
toValue("foo"),
|
|
93
|
+
toValue("a"),
|
|
94
|
+
toQuery(toValue("b"), toValue(" [x ]")),
|
|
95
|
+
toValue("c"),
|
|
96
|
+
]),
|
|
97
|
+
],
|
|
98
|
+
|
|
99
|
+
// Multi-Nodes
|
|
100
|
+
[
|
|
101
|
+
"_hello_{{world}}",
|
|
102
|
+
toPath([
|
|
103
|
+
toConcatenatedNode([toValue("_hello_"), toPath([toValue("world")])]),
|
|
104
|
+
]),
|
|
105
|
+
],
|
|
106
|
+
[
|
|
107
|
+
"_hello_{{world}}_foo_",
|
|
108
|
+
toPath([
|
|
109
|
+
toConcatenatedNode([
|
|
110
|
+
toValue("_hello_"),
|
|
111
|
+
toPath([toValue("world")]),
|
|
112
|
+
toValue("_foo_"),
|
|
113
|
+
]),
|
|
114
|
+
]),
|
|
115
|
+
],
|
|
116
|
+
[
|
|
117
|
+
"{{hello}}{{world}}",
|
|
118
|
+
toPath([
|
|
119
|
+
toConcatenatedNode([
|
|
120
|
+
toPath([toValue("hello")]),
|
|
121
|
+
toPath([toValue("world")]),
|
|
122
|
+
]),
|
|
123
|
+
]),
|
|
124
|
+
],
|
|
125
|
+
[
|
|
126
|
+
"{{hello}}_foobar_{{world}}",
|
|
127
|
+
toPath([
|
|
128
|
+
toConcatenatedNode([
|
|
129
|
+
toPath([toValue("hello")]),
|
|
130
|
+
toValue("_foobar_"),
|
|
131
|
+
toPath([toValue("world")]),
|
|
132
|
+
]),
|
|
133
|
+
]),
|
|
134
|
+
],
|
|
135
|
+
[
|
|
136
|
+
"{{hello}}_world_",
|
|
137
|
+
toPath([
|
|
138
|
+
toConcatenatedNode([toPath([toValue("hello")]), toValue("_world_")]),
|
|
139
|
+
]),
|
|
140
|
+
],
|
|
141
|
+
[
|
|
142
|
+
"{{hello}}_world",
|
|
143
|
+
toPath([
|
|
144
|
+
toConcatenatedNode([toPath([toValue("hello")]), toValue("_world")]),
|
|
145
|
+
]),
|
|
146
|
+
],
|
|
147
|
+
[
|
|
148
|
+
"foo.{{hello}}_world",
|
|
149
|
+
toPath([
|
|
150
|
+
toValue("foo"),
|
|
151
|
+
toConcatenatedNode([toPath([toValue("hello")]), toValue("_world")]),
|
|
152
|
+
]),
|
|
153
|
+
],
|
|
154
|
+
|
|
155
|
+
// Expressions
|
|
156
|
+
[
|
|
157
|
+
"foo.`bar()`.baz",
|
|
158
|
+
toPath([toValue("foo"), toExpression("bar()"), toValue("baz")]),
|
|
159
|
+
],
|
|
160
|
+
[
|
|
161
|
+
"foo`bar()`.baz",
|
|
162
|
+
toPath([
|
|
163
|
+
toConcatenatedNode([toValue("foo"), toExpression("bar()")]),
|
|
164
|
+
toValue("baz"),
|
|
165
|
+
]),
|
|
166
|
+
],
|
|
167
|
+
[
|
|
168
|
+
"foo[`bar()`].baz",
|
|
169
|
+
toPath([toValue("foo"), toExpression("bar()"), toValue("baz")]),
|
|
170
|
+
],
|
|
171
|
+
[
|
|
172
|
+
'foo["readonly" = `foo() == bar()`].baz',
|
|
173
|
+
toPath([
|
|
174
|
+
toValue("foo"),
|
|
175
|
+
toQuery(toValue("readonly"), toExpression("foo() == bar()")),
|
|
176
|
+
toValue("baz"),
|
|
177
|
+
]),
|
|
178
|
+
],
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
export const INVALID_AST_PARSER_TESTS: Array<string> = [
|
|
182
|
+
" ",
|
|
183
|
+
"@#$%^&*()",
|
|
184
|
+
".",
|
|
185
|
+
"foo.bar[",
|
|
186
|
+
"foo.bar.{{nested.}",
|
|
187
|
+
"foo.bar`not done()",
|
|
188
|
+
"{foo.bar",
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
export const VALID_AST_PARSER_CUSTOM_TESTS: Array<[string, PathNode]> = [
|
|
192
|
+
["foo‑<>~¡¢£", toPath([toValue("foo‑<>~¡¢£")])],
|
|
193
|
+
["foo.bar<>~¡¢£", toPath([toValue("foo"), toValue("bar<>~¡¢£")])],
|
|
194
|
+
[
|
|
195
|
+
"foo[{{b‑ar}}].baz",
|
|
196
|
+
toPath([toValue("foo"), toPath([toValue("b‑ar")]), toValue("baz")]),
|
|
197
|
+
],
|
|
198
|
+
];
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { VALID_AST_PARSER_TESTS } from "./ast-cases";
|
|
2
|
+
import { parse as parseParsimmon } from "../../parsimmon";
|
|
3
|
+
import { parse as parseEBNF } from "../../ebnf";
|
|
4
|
+
import { parse as parseCustom } from "../../custom";
|
|
5
|
+
|
|
6
|
+
import type { Parser } from "../../ast";
|
|
7
|
+
|
|
8
|
+
const parsers: Array<{
|
|
9
|
+
/** The name of the parser being tested */
|
|
10
|
+
name: string;
|
|
11
|
+
|
|
12
|
+
/** The parse function */
|
|
13
|
+
parser: Parser;
|
|
14
|
+
}> = [
|
|
15
|
+
{ name: "parsimmon", parser: parseParsimmon },
|
|
16
|
+
{ name: "ebnf", parser: parseEBNF },
|
|
17
|
+
{ name: "custom", parser: parseCustom },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const VALID_ITERATIONS = 1000;
|
|
21
|
+
|
|
22
|
+
/** Execute 1 iteration of the parser test */
|
|
23
|
+
const runOnce = (parser: Parser): number => {
|
|
24
|
+
const start = Date.now();
|
|
25
|
+
|
|
26
|
+
for (const testCase of VALID_AST_PARSER_TESTS) {
|
|
27
|
+
parser(testCase[0]);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const end = Date.now();
|
|
31
|
+
|
|
32
|
+
return end - start;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** Run all parser perf tests */
|
|
36
|
+
export const testAll = () => {
|
|
37
|
+
const results: Array<{
|
|
38
|
+
/** The name of the parser */
|
|
39
|
+
name: string;
|
|
40
|
+
|
|
41
|
+
/** How long it took overall */
|
|
42
|
+
time: number;
|
|
43
|
+
|
|
44
|
+
/** Average number of bindings parsed per sec */
|
|
45
|
+
opsPerSec: number;
|
|
46
|
+
}> = [];
|
|
47
|
+
|
|
48
|
+
for (const parser of parsers) {
|
|
49
|
+
const runs = [];
|
|
50
|
+
|
|
51
|
+
for (let i = 0; i < VALID_ITERATIONS; i++) {
|
|
52
|
+
runs.push(runOnce(parser.parser));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const total = runs.reduce((s, n) => s + n);
|
|
56
|
+
const opsPerSec =
|
|
57
|
+
(VALID_ITERATIONS * VALID_AST_PARSER_TESTS.length) / (total / 1000);
|
|
58
|
+
results.push({
|
|
59
|
+
name: parser.name,
|
|
60
|
+
time: total,
|
|
61
|
+
opsPerSec,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.table(results);
|
|
66
|
+
};
|
|
@@ -7,7 +7,7 @@ export interface Node<T extends string> {
|
|
|
7
7
|
* An AST node that represents a nested path in the model
|
|
8
8
|
* foo.{{bar}}.baz (this is {{bar}})
|
|
9
9
|
*/
|
|
10
|
-
export interface PathNode extends Node<
|
|
10
|
+
export interface PathNode extends Node<"PathNode"> {
|
|
11
11
|
/** The path in the model that this node represents */
|
|
12
12
|
path: Array<AnyNode>;
|
|
13
13
|
}
|
|
@@ -16,7 +16,7 @@ export interface PathNode extends Node<'PathNode'> {
|
|
|
16
16
|
* A segment representing a query
|
|
17
17
|
* [foo=bar]
|
|
18
18
|
*/
|
|
19
|
-
export interface QueryNode extends Node<
|
|
19
|
+
export interface QueryNode extends Node<"Query"> {
|
|
20
20
|
/** The key to query */
|
|
21
21
|
key: AnyNode;
|
|
22
22
|
|
|
@@ -25,52 +25,52 @@ export interface QueryNode extends Node<'Query'> {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/** A simple segment */
|
|
28
|
-
export interface ValueNode extends Node<
|
|
28
|
+
export interface ValueNode extends Node<"Value"> {
|
|
29
29
|
/** The segment value */
|
|
30
30
|
value: string | number;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/** A nested expression */
|
|
34
|
-
export interface ExpressionNode extends Node<
|
|
34
|
+
export interface ExpressionNode extends Node<"Expression"> {
|
|
35
35
|
/** The expression */
|
|
36
36
|
value: string;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/** Helper to create a value node */
|
|
40
40
|
export const toValue = (value: string | number): ValueNode => ({
|
|
41
|
-
name:
|
|
41
|
+
name: "Value",
|
|
42
42
|
value,
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
/** Helper to create an expression node */
|
|
46
46
|
export const toExpression = (value: string): ExpressionNode => ({
|
|
47
|
-
name:
|
|
47
|
+
name: "Expression",
|
|
48
48
|
value,
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
/** Helper to create a nested path node */
|
|
52
52
|
export const toPath = (path: Array<AnyNode>): PathNode => ({
|
|
53
|
-
name:
|
|
53
|
+
name: "PathNode",
|
|
54
54
|
path,
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
/** Helper to create a query node */
|
|
58
58
|
export const toQuery = (key: AnyNode, value?: AnyNode): QueryNode => ({
|
|
59
|
-
name:
|
|
59
|
+
name: "Query",
|
|
60
60
|
key,
|
|
61
61
|
value,
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
/** Create a concat node */
|
|
65
65
|
export const toConcatenatedNode = (
|
|
66
|
-
values: Array<PathNode | ValueNode | ExpressionNode
|
|
66
|
+
values: Array<PathNode | ValueNode | ExpressionNode>,
|
|
67
67
|
): PathNode | ValueNode | ConcatenatedNode | ExpressionNode => {
|
|
68
68
|
if (values.length === 1) {
|
|
69
69
|
return values[0];
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
return {
|
|
73
|
-
name:
|
|
73
|
+
name: "Concatenated",
|
|
74
74
|
value: values,
|
|
75
75
|
};
|
|
76
76
|
};
|
|
@@ -79,7 +79,7 @@ export const toConcatenatedNode = (
|
|
|
79
79
|
* A binding segment that's multiple smaller ones
|
|
80
80
|
* {{foo}}_bar_{{baz}}
|
|
81
81
|
*/
|
|
82
|
-
export interface ConcatenatedNode extends Node<
|
|
82
|
+
export interface ConcatenatedNode extends Node<"Concatenated"> {
|
|
83
83
|
/** A list of nested paths, or value nodes to concat together to form a segment */
|
|
84
84
|
value: Array<PathNode | ValueNode | ExpressionNode>;
|
|
85
85
|
}
|
|
@@ -6,24 +6,24 @@ import type {
|
|
|
6
6
|
ValueNode,
|
|
7
7
|
QueryNode,
|
|
8
8
|
ExpressionNode,
|
|
9
|
-
} from
|
|
9
|
+
} from "../ast";
|
|
10
10
|
import {
|
|
11
11
|
toValue,
|
|
12
12
|
toPath,
|
|
13
13
|
toConcatenatedNode,
|
|
14
14
|
toQuery,
|
|
15
15
|
toExpression,
|
|
16
|
-
} from
|
|
17
|
-
|
|
18
|
-
const SEGMENT_SEPARATOR =
|
|
19
|
-
const OPEN_CURL =
|
|
20
|
-
const CLOSE_CURL =
|
|
21
|
-
const OPEN_BRACKET =
|
|
22
|
-
const CLOSE_BRACKET =
|
|
23
|
-
const EQUALS =
|
|
16
|
+
} from "../ast";
|
|
17
|
+
|
|
18
|
+
const SEGMENT_SEPARATOR = ".";
|
|
19
|
+
const OPEN_CURL = "{";
|
|
20
|
+
const CLOSE_CURL = "}";
|
|
21
|
+
const OPEN_BRACKET = "[";
|
|
22
|
+
const CLOSE_BRACKET = "]";
|
|
23
|
+
const EQUALS = "=";
|
|
24
24
|
const SINGLE_QUOTE = "'";
|
|
25
25
|
const DOUBLE_QUOTE = '"';
|
|
26
|
-
const BACK_TICK =
|
|
26
|
+
const BACK_TICK = "`";
|
|
27
27
|
// const IDENTIFIER_REGEX = /[\w\-@]+/;
|
|
28
28
|
|
|
29
29
|
/** A _faster_ way to match chars instead of a regex. */
|
|
@@ -65,14 +65,13 @@ export const parse: Parser = (path) => {
|
|
|
65
65
|
|
|
66
66
|
ch = path.charAt(index);
|
|
67
67
|
index += 1;
|
|
68
|
-
// console.log(`Index: ${index} Char: ${ch}`);
|
|
69
68
|
return ch;
|
|
70
69
|
};
|
|
71
70
|
|
|
72
71
|
/** gobble all whitespace */
|
|
73
72
|
const whitespace = () => {
|
|
74
73
|
/* eslint-disable no-unmodified-loop-condition */
|
|
75
|
-
while (ch ===
|
|
74
|
+
while (ch === " ") {
|
|
76
75
|
next();
|
|
77
76
|
}
|
|
78
77
|
};
|
|
@@ -146,15 +145,13 @@ export const parse: Parser = (path) => {
|
|
|
146
145
|
const nestedPath = (): PathNode | undefined => {
|
|
147
146
|
if (ch === OPEN_CURL) {
|
|
148
147
|
next(OPEN_CURL);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return modelRef;
|
|
157
|
-
}
|
|
148
|
+
next(OPEN_CURL);
|
|
149
|
+
|
|
150
|
+
/* eslint-disable-next-line @typescript-eslint/no-use-before-define */
|
|
151
|
+
const modelRef = parsePath();
|
|
152
|
+
next(CLOSE_CURL);
|
|
153
|
+
next(CLOSE_CURL);
|
|
154
|
+
return modelRef;
|
|
158
155
|
}
|
|
159
156
|
};
|
|
160
157
|
|
|
@@ -254,7 +251,7 @@ export const parse: Parser = (path) => {
|
|
|
254
251
|
const parseSegmentAndBrackets = (): Array<AnyNode> => {
|
|
255
252
|
// try to parse a segment first
|
|
256
253
|
|
|
257
|
-
const parsed = [];
|
|
254
|
+
const parsed: Array<AnyNode> = [];
|
|
258
255
|
|
|
259
256
|
const firstSegment = segment();
|
|
260
257
|
|