arrmatura 6.3.1 → 6.3.2
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/README.md +6 -10
- package/dist/index.js +2 -0
- package/dist/index.js.map +7 -0
- package/docs/manual.md +84 -66
- package/index.ts +5 -4
- package/package.json +10 -4
- package/src/core/{Entitron.ts → Arrmatron.ts} +147 -136
- package/src/core/Component.ts +44 -21
- package/src/core/ManifestNode.ts +36 -25
- package/src/core/resolveExpression.ts +9 -4
- package/src/registry/composition.ts +9 -8
- package/src/registry/conditionals.ts +10 -8
- package/src/registry/connector.ts +47 -0
- package/src/registry/elementary.ts +7 -6
- package/src/registry/fragment.ts +7 -6
- package/src/registry/index.ts +21 -25
- package/src/registry/iterations.ts +30 -18
- package/src/registry/root.ts +8 -6
- package/src/registry/routing.ts +6 -5
- package/src/registry/selection.ts +11 -9
- package/src/registry/slot.ts +8 -7
- package/src/utils/FingerprintMashine.ts +128 -0
- package/src/utils/applyStateChangedToImpl.ts +18 -0
- package/src/utils/compileExpression.ts +136 -29
- package/src/utils/objectFingerprint.spec.ts +70 -0
- package/types.ts +38 -29
package/src/core/ManifestNode.ts
CHANGED
|
@@ -1,38 +1,46 @@
|
|
|
1
|
+
import type { IPlatform, IArrmatron, IManifestNode } from "arrmatura/types";
|
|
1
2
|
import { nextId } from "ultimus";
|
|
2
|
-
import { compilePipeExpression } from "../utils/compileExpression";
|
|
3
3
|
import type { Delta, ExpressionText, Hash, Uid } from "ultimus/types";
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
import { compilePipeExpression } from "../utils/compileExpression";
|
|
6
|
+
|
|
7
|
+
import { Arrmatron } from "./Arrmatron";
|
|
5
8
|
import { resolveExpression } from "./resolveExpression";
|
|
6
9
|
|
|
7
|
-
type PropertyResolver = (c: IEntitron, acc: Delta) => Delta;
|
|
8
10
|
|
|
9
|
-
type
|
|
11
|
+
type PropertyResolver = (c: IArrmatron, acc: Delta) => Delta;
|
|
12
|
+
|
|
13
|
+
type PropertyGetter = (c: IArrmatron) => unknown;
|
|
10
14
|
|
|
11
15
|
/**
|
|
12
|
-
* ManifestNode is a abstract factory for
|
|
16
|
+
* ManifestNode is a abstract factory for Arrmatron.
|
|
13
17
|
*
|
|
14
|
-
* Each specific kind of
|
|
18
|
+
* Each specific kind of Arrmatron has its own descending implementation of ManifestNode.
|
|
15
19
|
*/
|
|
16
20
|
export abstract class ManifestNode implements IManifestNode {
|
|
17
21
|
protected $content?: Map<Uid, IManifestNode> | undefined;
|
|
18
|
-
uid: Uid
|
|
22
|
+
uid: Uid;
|
|
19
23
|
refId?: string;
|
|
20
24
|
private readonly propertyResolvers: PropertyResolver[] = [];
|
|
21
|
-
private initialState: Hash<(c:
|
|
22
|
-
connectors?: Map<string, (c:
|
|
25
|
+
private initialState: Hash<(c: IArrmatron) => any> = {};
|
|
26
|
+
connectors?: Map<string, (c: IArrmatron, x: unknown) => unknown>;
|
|
23
27
|
tag: any;
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
constructor(xid: Uid) {
|
|
30
|
+
this.uid = `${nextId("N")}:${xid}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
abstract get EntitronConstructor(): new (platform: IPlatform, node: typeof this, parent?: Arrmatron, scope?: Arrmatron) => Arrmatron;
|
|
26
34
|
|
|
27
35
|
/**
|
|
28
36
|
* Create a context object.
|
|
29
37
|
*
|
|
30
38
|
* @param {IPlatform} platform - The platform object.
|
|
31
|
-
* @param {
|
|
32
|
-
* @param {
|
|
33
|
-
* @return {
|
|
39
|
+
* @param {IArrmatron} parent - The parent context object (optional).
|
|
40
|
+
* @param {IArrmatron} scope - The scope context object (optional).
|
|
41
|
+
* @return {IArrmatron} The created context object.
|
|
34
42
|
*/
|
|
35
|
-
|
|
43
|
+
createArrmatron(platform: IPlatform, parent?: Arrmatron, scope?: Arrmatron): Arrmatron {
|
|
36
44
|
return new this.EntitronConstructor(platform, this, parent, scope);
|
|
37
45
|
}
|
|
38
46
|
|
|
@@ -41,7 +49,7 @@ export abstract class ManifestNode implements IManifestNode {
|
|
|
41
49
|
}
|
|
42
50
|
|
|
43
51
|
addPropertyResolver(getter: PropertyGetter, propKey: string) {
|
|
44
|
-
this.propertyResolvers.push((c:
|
|
52
|
+
this.propertyResolvers.push((c: IArrmatron, delta: Delta) => {
|
|
45
53
|
delta[propKey] = getter(c);
|
|
46
54
|
return delta;
|
|
47
55
|
});
|
|
@@ -49,7 +57,7 @@ export abstract class ManifestNode implements IManifestNode {
|
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
private addDataPropertyResolver(getter: PropertyGetter, propKey: string) {
|
|
52
|
-
this.propertyResolvers.push((c:
|
|
60
|
+
this.propertyResolvers.push((c: IArrmatron, delta: Delta) => {
|
|
53
61
|
const val = getter(c);
|
|
54
62
|
delta.data = Object.assign((delta.data as object) || {}, {
|
|
55
63
|
[propKey]: val,
|
|
@@ -60,7 +68,7 @@ export abstract class ManifestNode implements IManifestNode {
|
|
|
60
68
|
}
|
|
61
69
|
|
|
62
70
|
private addPropertiesResolver(getter: PropertyGetter) {
|
|
63
|
-
this.propertyResolvers.push((c:
|
|
71
|
+
this.propertyResolvers.push((c: IArrmatron, delta: Delta) => {
|
|
64
72
|
const value = getter(c);
|
|
65
73
|
if (value && typeof value === "object") {
|
|
66
74
|
Object.entries(value).forEach(([key, val]) => {
|
|
@@ -79,10 +87,11 @@ export abstract class ManifestNode implements IManifestNode {
|
|
|
79
87
|
|
|
80
88
|
private addEmitter(expr: ExpressionText, k: string) {
|
|
81
89
|
const { key, pipec } = compilePipeExpression(expr);
|
|
82
|
-
|
|
90
|
+
const rkey = key + (expr.endsWith(")") ? "()" : '');
|
|
91
|
+
this.initialState[k] = (c) => (data: unknown) => c.scope?.emit(rkey, pipec(c, data) as Delta);
|
|
83
92
|
}
|
|
84
93
|
|
|
85
|
-
resolveInitialProps(c:
|
|
94
|
+
resolveInitialProps(c: IArrmatron) {
|
|
86
95
|
return this.propertyResolvers.reduce(
|
|
87
96
|
(delta, resolver) => resolver(c, delta),
|
|
88
97
|
Object.entries(this.initialState).reduce((acc, [key, fn]) => {
|
|
@@ -92,17 +101,17 @@ export abstract class ManifestNode implements IManifestNode {
|
|
|
92
101
|
);
|
|
93
102
|
}
|
|
94
103
|
|
|
95
|
-
resolveProps(c:
|
|
104
|
+
resolveProps(c: IArrmatron) {
|
|
96
105
|
return this.propertyResolvers.reduce((delta, resolver) => resolver(c, delta), {});
|
|
97
106
|
}
|
|
98
107
|
|
|
99
108
|
compileAttribute(k: string, v: unknown) {
|
|
100
109
|
if (k.startsWith("data-")) {
|
|
101
110
|
this.addDataPropertyResolver(resolveExpression(v), k.slice(5));
|
|
102
|
-
} else if (k === "
|
|
111
|
+
} else if (k === "Ref") {
|
|
103
112
|
this.refId = String(v);
|
|
104
|
-
} else if (k ===
|
|
105
|
-
const sv =
|
|
113
|
+
} else if (k === '(...)') {
|
|
114
|
+
const sv = String(v);
|
|
106
115
|
if (sv[0] === "<" && sv[1] === "-") {
|
|
107
116
|
this.addConnector(sv.slice(2), "");
|
|
108
117
|
} else {
|
|
@@ -116,11 +125,13 @@ export abstract class ManifestNode implements IManifestNode {
|
|
|
116
125
|
this.addConnector(v.slice(2), k);
|
|
117
126
|
} else if (v[0] === "-" && v[1] === ">") {
|
|
118
127
|
this.addEmitter(v.slice(2), k);
|
|
128
|
+
} else if (v[0] === "d" && v.startsWith("data->")) {
|
|
129
|
+
this.addEmitter(v.slice(6), k);
|
|
119
130
|
} else {
|
|
120
|
-
if (!v.includes("{")
|
|
131
|
+
if (!v.includes("{")) {
|
|
121
132
|
this.initialState[k] = () => v;
|
|
122
133
|
} else {
|
|
123
|
-
if (v[0] === "
|
|
134
|
+
if (v[0] === "R" && v[1] === "." && v.match(/^@@[a-z0-9\-_.]+$/i) != null) {
|
|
124
135
|
this.initialState[k] = (c) => c.platform.getResource(v.slice(2));
|
|
125
136
|
} else {
|
|
126
137
|
this.addPropertyResolver(resolveExpression(v), k);
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { createStringInterpolator } from "ultimus/src/parsing/createStringInterpolator";
|
|
2
|
+
|
|
2
3
|
import { compileConstant, compileJsExpression, compilePlaceholder } from "../utils/compileExpression";
|
|
4
|
+
// \{\{([\w\.]+)\}\}
|
|
3
5
|
|
|
4
6
|
const CACHE = new Map();
|
|
5
7
|
|
|
6
8
|
function compileExpression(v: unknown) {
|
|
7
9
|
if (typeof v !== "string") return () => v;
|
|
8
10
|
|
|
9
|
-
if (v[0] === "@") return compilePlaceholder(v);
|
|
10
|
-
|
|
11
11
|
if (v.startsWith("js:")) return compileJsExpression(v.slice(3));
|
|
12
12
|
|
|
13
|
-
if (v.includes("{"))
|
|
14
|
-
|
|
13
|
+
if (v.includes("{")) {
|
|
14
|
+
const vCutOne = v.slice(1, -1);
|
|
15
|
+
if (v[0] === "{" && v[v.length - 1] === "}" && !vCutOne.includes("{")) {
|
|
16
|
+
return compilePlaceholder(vCutOne)
|
|
17
|
+
}
|
|
18
|
+
return createStringInterpolator(v.replace(/\s+/g, " "), compilePlaceholder);
|
|
19
|
+
}
|
|
15
20
|
return compileConstant(v);
|
|
16
21
|
}
|
|
17
22
|
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { ManifestNode } from "../core/ManifestNode";
|
|
2
|
-
import { Entitron } from "../core/Entitron";
|
|
3
|
-
import type { Hash, XmlNode } from "ultimus/types";
|
|
4
1
|
import type { EContent, IManifestNode, IPlatform } from "arrmatura/types";
|
|
2
|
+
import type { Hash, XmlNode } from "ultimus/types";
|
|
3
|
+
|
|
4
|
+
import { Arrmatron } from "../core/Arrmatron";
|
|
5
|
+
import { ManifestNode } from "../core/ManifestNode";
|
|
5
6
|
|
|
6
|
-
export class Composite extends
|
|
7
|
+
export class Composite extends Arrmatron<CCompositeNode> {
|
|
7
8
|
get displayName(): string {
|
|
8
9
|
return `💠${this.manifest.tag}`;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
get recontentScope():
|
|
12
|
+
get recontentScope(): Arrmatron {
|
|
12
13
|
return this;
|
|
13
14
|
}
|
|
14
15
|
|
|
@@ -27,8 +28,8 @@ export class CCompositeNode extends ManifestNode {
|
|
|
27
28
|
nodes?: XmlNode["nodes"];
|
|
28
29
|
#content?: Map<string, IManifestNode>;
|
|
29
30
|
|
|
30
|
-
constructor({ tag, attrs, nodes }: XmlNode) {
|
|
31
|
-
super();
|
|
31
|
+
constructor({ id, tag, attrs, nodes }: XmlNode) {
|
|
32
|
+
super(id);
|
|
32
33
|
this.tag = tag;
|
|
33
34
|
this.nodes = nodes;
|
|
34
35
|
this.compileAttributes(attrs);
|
|
@@ -53,7 +54,7 @@ export class CCompositeNode extends ManifestNode {
|
|
|
53
54
|
}, {});
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
return this.slotContent[slotId
|
|
57
|
+
return this.slotContent[slotId || "default"] ?? undefined;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
getSubNodes(platform: IPlatform) {
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Composite } from "./composition";
|
|
3
|
-
import { Entitron } from "../core/Entitron";
|
|
4
|
-
import type { EContent, IEntitron, IPlatform } from "arrmatura/types";
|
|
1
|
+
import type { EContent, IArrmatron, IPlatform } from "arrmatura/types";
|
|
5
2
|
import type { XmlNode } from "ultimus/types";
|
|
3
|
+
|
|
4
|
+
import { Arrmatron } from "../core/Arrmatron";
|
|
5
|
+
import { ManifestNode } from "../core/ManifestNode";
|
|
6
6
|
import { compilePlaceholder } from "../utils/compileExpression";
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
import { Composite } from "./composition";
|
|
9
|
+
|
|
10
|
+
class Conditional extends Arrmatron<CIfNode> {
|
|
9
11
|
get contentManifests() {
|
|
10
12
|
const branches = this.manifest.getBranches(this.platform);
|
|
11
|
-
return this
|
|
13
|
+
return this.$component.condition ? branches.then : branches.else;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
get displayName(): string {
|
|
@@ -20,7 +22,7 @@ export class CIfNode extends ManifestNode {
|
|
|
20
22
|
#xml: XmlNode;
|
|
21
23
|
#branches?: { then?: EContent; else?: EContent };
|
|
22
24
|
constructor(xml: XmlNode, expr: string) {
|
|
23
|
-
super();
|
|
25
|
+
super(xml.id);
|
|
24
26
|
this.#xml = xml;
|
|
25
27
|
this.compileCondition(expr);
|
|
26
28
|
}
|
|
@@ -66,7 +68,7 @@ export class CIfNode extends ManifestNode {
|
|
|
66
68
|
} else if (expr.slice(0, 5) === "slot(") {
|
|
67
69
|
const slotId = expr.slice(5, -1);
|
|
68
70
|
// DO NOT place inside iterations.
|
|
69
|
-
this.addPropertyResolver((c:
|
|
71
|
+
this.addPropertyResolver((c: IArrmatron) => !!(c.scope as Composite).slotContent?.(slotId)?.size, "condition");
|
|
70
72
|
} else {
|
|
71
73
|
const resolver = compilePlaceholder(expr);
|
|
72
74
|
this.addPropertyResolver((c) => !!resolver(c), "condition");
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Fn, XmlNode } from "ultimus";
|
|
2
|
+
|
|
3
|
+
import { Arrmatron } from "../core/Arrmatron";
|
|
4
|
+
import { ManifestNode } from "../core/ManifestNode";
|
|
5
|
+
import { objectFingerprint } from "../utils/FingerprintMashine";
|
|
6
|
+
|
|
7
|
+
class Connector extends Arrmatron<ConnectorNode> {
|
|
8
|
+
prevkey: any;
|
|
9
|
+
|
|
10
|
+
counter = 0;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Triggers an with changes after a specified timeout.
|
|
14
|
+
*
|
|
15
|
+
* @return {void} No return value.
|
|
16
|
+
*/
|
|
17
|
+
touch() {
|
|
18
|
+
super.touch();
|
|
19
|
+
// if (this.isDone) return;
|
|
20
|
+
const { trigger, data, change } = this.$component;
|
|
21
|
+
const key = 'trigger' in this.$component ? trigger : data;
|
|
22
|
+
const fprint = objectFingerprint(key);
|
|
23
|
+
const changed = !('prevkey' in this) || this.prevkey != fprint;
|
|
24
|
+
|
|
25
|
+
if (!changed) return;
|
|
26
|
+
|
|
27
|
+
this.prevkey = fprint;
|
|
28
|
+
|
|
29
|
+
(change as Fn)?.(data);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get displayName(): string {
|
|
33
|
+
return "🔹Connector";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class ConnectorNode extends ManifestNode {
|
|
38
|
+
constructor({ attrs, id }: XmlNode) {
|
|
39
|
+
super(id);
|
|
40
|
+
this.compileAttributes(attrs);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get EntitronConstructor() {
|
|
44
|
+
return Connector;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { ManifestNode } from "../core/ManifestNode";
|
|
2
|
-
import { Entitron } from "../core/Entitron";
|
|
3
|
-
import type { Hash, XmlNode } from "ultimus/types";
|
|
4
1
|
import { IPlatform } from "arrmatura/types";
|
|
2
|
+
import type { Hash, XmlNode } from "ultimus/types";
|
|
3
|
+
|
|
4
|
+
import { Arrmatron } from "../core/Arrmatron";
|
|
5
|
+
import { ManifestNode } from "../core/ManifestNode";
|
|
5
6
|
|
|
6
|
-
class NativeElementEntitron extends
|
|
7
|
+
class NativeElementEntitron extends Arrmatron<CElementNode> {
|
|
7
8
|
get displayName(): string {
|
|
8
9
|
return this.manifest.tag;
|
|
9
10
|
}
|
|
@@ -16,8 +17,8 @@ class NativeElementEntitron extends Entitron<CElementNode> {
|
|
|
16
17
|
export class CElementNode extends ManifestNode {
|
|
17
18
|
#nodes: XmlNode[] | undefined;
|
|
18
19
|
|
|
19
|
-
constructor({ tag, attrs, nodes, text }: XmlNode) {
|
|
20
|
-
super();
|
|
20
|
+
constructor({ id, tag, attrs, nodes, text }: XmlNode) {
|
|
21
|
+
super(id);
|
|
21
22
|
this.tag = tag;
|
|
22
23
|
this.compileAttributes(attrs);
|
|
23
24
|
if (text) {
|
package/src/registry/fragment.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { ManifestNode } from "../core/ManifestNode";
|
|
2
|
-
import { Entitron } from "../core/Entitron";
|
|
3
|
-
import type { XmlNode } from "ultimus/types";
|
|
4
1
|
import type { IPlatform } from "arrmatura/types";
|
|
2
|
+
import type { XmlNode } from "ultimus/types";
|
|
3
|
+
|
|
4
|
+
import { Arrmatron } from "../core/Arrmatron";
|
|
5
|
+
import { ManifestNode } from "../core/ManifestNode";
|
|
5
6
|
|
|
6
7
|
export class CFragmentNode extends ManifestNode {
|
|
7
8
|
#nodes: XmlNode[] | undefined;
|
|
8
9
|
|
|
9
|
-
constructor({ nodes }: XmlNode) {
|
|
10
|
-
super();
|
|
10
|
+
constructor({ id, nodes }: XmlNode) {
|
|
11
|
+
super(id);
|
|
11
12
|
this.#nodes = nodes;
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -16,6 +17,6 @@ export class CFragmentNode extends ManifestNode {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
get EntitronConstructor() {
|
|
19
|
-
return
|
|
20
|
+
return Arrmatron;
|
|
20
21
|
}
|
|
21
22
|
}
|
package/src/registry/index.ts
CHANGED
|
@@ -1,25 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Uid, XmlNode } from "ultimus";
|
|
2
|
+
|
|
3
|
+
import { IManifestNode } from "../../types";
|
|
4
|
+
|
|
2
5
|
import { CCompositeNode } from "./composition";
|
|
3
6
|
import { CIfNode } from "./conditionals";
|
|
7
|
+
import { ConnectorNode } from "./connector";
|
|
4
8
|
import { CElementNode } from "./elementary";
|
|
5
|
-
import { CDynamicTagNode } from "./routing";
|
|
6
|
-
import { CSlotNode } from "./slot";
|
|
7
9
|
import { CFragmentNode } from "./fragment";
|
|
10
|
+
import { CForNode } from "./iterations";
|
|
11
|
+
import { CDynamicTagNode } from "./routing";
|
|
8
12
|
import { CSelectorNode } from "./selection";
|
|
13
|
+
import { CSlotNode } from "./slot";
|
|
9
14
|
|
|
10
|
-
import { Uid, XmlNode } from "ultimus";
|
|
11
|
-
import { IManifestNode } from "../../types";
|
|
12
15
|
|
|
13
|
-
export const Registry = {
|
|
14
|
-
CForNode,
|
|
15
|
-
CIfNode,
|
|
16
|
-
CDynamicTagNode,
|
|
17
|
-
CSlotNode,
|
|
18
|
-
CFragmentNode,
|
|
19
|
-
CCompositeNode,
|
|
20
|
-
CSelectorNode,
|
|
21
|
-
CElementNode,
|
|
22
|
-
};
|
|
23
16
|
|
|
24
17
|
const cloneNode = (x: XmlNode, eliminateAttr: string): XmlNode => {
|
|
25
18
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -35,28 +28,31 @@ const cloneNode = (x: XmlNode, eliminateAttr: string): XmlNode => {
|
|
|
35
28
|
|
|
36
29
|
const __compileManifestNode = (x: XmlNode): IManifestNode => {
|
|
37
30
|
const { tag = 'div', attrs } = x;
|
|
38
|
-
if (attrs?.["
|
|
39
|
-
return new
|
|
31
|
+
if (attrs?.["Each"]) {
|
|
32
|
+
return new CForNode(cloneNode(x, "Each"), String(attrs["Each"]).trim().split(" "));
|
|
40
33
|
}
|
|
41
|
-
if (attrs?.["
|
|
42
|
-
return new
|
|
34
|
+
if (attrs?.["If"]) {
|
|
35
|
+
return new CIfNode(cloneNode(x, "If"), String(attrs["If"]));
|
|
43
36
|
}
|
|
44
37
|
if (tag === "div" || tag[0]?.match(/[A-Z0-9]/) == null) {
|
|
45
|
-
return new
|
|
38
|
+
return new CElementNode(x);
|
|
46
39
|
}
|
|
47
40
|
if (tag === "Dynamic") {
|
|
48
|
-
return new
|
|
41
|
+
return new CDynamicTagNode(cloneNode(x, "As"), String(attrs?.As ?? "Error.Dynamic"));
|
|
49
42
|
}
|
|
50
43
|
if (tag === "Slot") {
|
|
51
|
-
return new
|
|
44
|
+
return new CSlotNode(x);
|
|
52
45
|
}
|
|
53
46
|
if (tag === "Selector") {
|
|
54
|
-
return new
|
|
47
|
+
return new CSelectorNode(x);
|
|
48
|
+
}
|
|
49
|
+
if (tag === "Connector") {
|
|
50
|
+
return new ConnectorNode(x);
|
|
55
51
|
}
|
|
56
52
|
if (tag === "Fragment" || tag === "Then" || tag === "Else") {
|
|
57
|
-
return new
|
|
53
|
+
return new CFragmentNode(x);
|
|
58
54
|
}
|
|
59
|
-
return new
|
|
55
|
+
return new CCompositeNode(x);
|
|
60
56
|
};
|
|
61
57
|
|
|
62
58
|
const CTXNODES = new Map();
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import { ManifestNode } from "../core/ManifestNode";
|
|
2
|
-
import { Entitron } from "../core/Entitron";
|
|
3
|
-
import type { Datum, Delta, Hash, Uid, XmlNode } from "ultimus/types";
|
|
4
1
|
import type { EContent, IManifestNode, IPlatform } from "arrmatura/types";
|
|
2
|
+
import { mapEntries } from "ultimus";
|
|
3
|
+
import type { Datum, Delta, Hash, Uid, XmlNode } from "ultimus/types";
|
|
4
|
+
|
|
5
|
+
import { Arrmatron } from "../core/Arrmatron";
|
|
6
|
+
import { ManifestNode } from "../core/ManifestNode";
|
|
5
7
|
import { compilePlaceholder } from "../utils/compileExpression";
|
|
6
8
|
|
|
9
|
+
|
|
7
10
|
const narrowData = (data: any) => {
|
|
8
11
|
if (!data) {
|
|
9
12
|
return [];
|
|
@@ -11,11 +14,11 @@ const narrowData = (data: any) => {
|
|
|
11
14
|
if (Array.isArray(data)) return data;
|
|
12
15
|
if (typeof data[Symbol.iterator] === 'function') return [...data];
|
|
13
16
|
if (typeof data === "string") return data.split(',').map(id => ({ id, name: id }));
|
|
14
|
-
if (typeof data === "object") return
|
|
17
|
+
if (typeof data === "object") return mapEntries(data, (key, value) => ({ ...value, id: key }));
|
|
15
18
|
return [{ id: String(data), name: String(data) }];
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
class Iterative extends
|
|
21
|
+
class Iterative extends Arrmatron<CForNode> {
|
|
19
22
|
pkHash: Hash<Datum> = {};
|
|
20
23
|
|
|
21
24
|
get displayName(): string {
|
|
@@ -23,30 +26,36 @@ class Iterative extends Entitron<CForNode> {
|
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
get contentManifests() {
|
|
26
|
-
const each: any = narrowData(this
|
|
29
|
+
const each: any = narrowData(this.$component.each);
|
|
27
30
|
const itemNode = this.manifest.getItemCtxNode(this.platform);
|
|
28
31
|
const nodes: Map<Uid, IManifestNode> = new Map();
|
|
29
32
|
this.pkHash = {};
|
|
30
33
|
if (each?.length) {
|
|
31
34
|
if (!each.forEach) {
|
|
32
|
-
|
|
35
|
+
|
|
33
36
|
throw new Error(`[each] Items has no forEach() ${each.toString()}`);
|
|
34
37
|
}
|
|
35
|
-
each.forEach((
|
|
38
|
+
each.forEach((_datum: Datum, index: number) => {
|
|
39
|
+
const datum = typeof _datum === "string" ? { id: _datum } : _datum;
|
|
36
40
|
let id = datum.id;
|
|
41
|
+
|
|
37
42
|
if (id == null) {
|
|
38
43
|
this.logError("ERROR: empty item id: ", datum);
|
|
39
44
|
id = String(index);
|
|
40
45
|
}
|
|
46
|
+
|
|
41
47
|
const pk = String(id);
|
|
42
48
|
if (this.pkHash[pk]) {
|
|
43
49
|
this.logError(`ERROR: duplicate item id : ${pk} (skipped)`, datum);
|
|
44
50
|
return;
|
|
45
51
|
}
|
|
52
|
+
|
|
46
53
|
this.pkHash[pk] = datum;
|
|
54
|
+
|
|
47
55
|
const inode = itemNode
|
|
48
56
|
.cloneWithDatum(datum, this.platform)
|
|
49
57
|
.addPropertyResolver(() => this.pkHash[pk], itemNode.itemName);
|
|
58
|
+
|
|
50
59
|
nodes.set(inode.uid, inode);
|
|
51
60
|
});
|
|
52
61
|
}
|
|
@@ -59,9 +68,12 @@ export class CForNode extends ManifestNode {
|
|
|
59
68
|
#itemName: string;
|
|
60
69
|
#xmlNode: XmlNode;
|
|
61
70
|
constructor(xmlNode: XmlNode, [itemName, prep, expr = prep]: string[]) {
|
|
62
|
-
super();
|
|
71
|
+
super(xmlNode.id);
|
|
72
|
+
|
|
63
73
|
this.#xmlNode = xmlNode;
|
|
64
|
-
|
|
74
|
+
|
|
75
|
+
this.#itemName = itemName.startsWith("@") ? itemName.slice(1) : itemName;
|
|
76
|
+
|
|
65
77
|
if (expr[0] === "<" && expr[1] === "-") {
|
|
66
78
|
this.addConnector(expr.slice(2), "each");
|
|
67
79
|
} else {
|
|
@@ -71,7 +83,7 @@ export class CForNode extends ManifestNode {
|
|
|
71
83
|
|
|
72
84
|
getItemCtxNode(platform: IPlatform) {
|
|
73
85
|
return (
|
|
74
|
-
this.#itemNode ?? (this.#itemNode = new CItemNode(this.#itemName, platform.getCompiledNodes([this.#xmlNode])))
|
|
86
|
+
this.#itemNode ?? (this.#itemNode = new CItemNode(this.#xmlNode.id, this.#itemName, platform.getCompiledNodes([this.#xmlNode])))
|
|
75
87
|
);
|
|
76
88
|
}
|
|
77
89
|
|
|
@@ -80,8 +92,8 @@ export class CForNode extends ManifestNode {
|
|
|
80
92
|
}
|
|
81
93
|
}
|
|
82
94
|
|
|
83
|
-
class IterativeItem extends
|
|
84
|
-
get recontentScope():
|
|
95
|
+
class IterativeItem extends Arrmatron<CItemNode> {
|
|
96
|
+
get recontentScope(): Arrmatron {
|
|
85
97
|
return this;
|
|
86
98
|
}
|
|
87
99
|
|
|
@@ -103,17 +115,17 @@ class IterativeItem extends Entitron<CItemNode> {
|
|
|
103
115
|
|
|
104
116
|
class CItemNode extends ManifestNode {
|
|
105
117
|
itemName: string;
|
|
106
|
-
constructor(itemName: string, content?: EContent) {
|
|
107
|
-
super();
|
|
118
|
+
constructor(id: string, itemName: string, content?: EContent) {
|
|
119
|
+
super(id);
|
|
108
120
|
this.itemName = itemName;
|
|
109
121
|
this.$content = content;
|
|
110
122
|
}
|
|
111
123
|
|
|
112
124
|
cloneWithDatum(datum: Datum, platform: IPlatform) {
|
|
113
125
|
const uid = `${String(this.uid)}#${datum.id}`;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
126
|
+
const c = new CItemNode(uid, this.itemName, this.getSubNodes(platform));
|
|
127
|
+
c.uid = uid;
|
|
128
|
+
return c;
|
|
117
129
|
}
|
|
118
130
|
|
|
119
131
|
get EntitronConstructor() {
|
package/src/registry/root.ts
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import { ManifestNode } from "../core/ManifestNode";
|
|
2
|
-
import { Entitron } from "../core/Entitron";
|
|
3
|
-
import type { Hash, XmlNode } from "ultimus/types";
|
|
4
1
|
import type { EContent, IPlatform } from "arrmatura/types";
|
|
5
2
|
import { xmlParse } from "ultimus";
|
|
3
|
+
import type { Hash, XmlNode } from "ultimus/types";
|
|
4
|
+
|
|
5
|
+
import { Arrmatron } from "../core/Arrmatron";
|
|
6
|
+
import { ManifestNode } from "../core/ManifestNode";
|
|
7
|
+
|
|
6
8
|
import { CCompositeNode } from "./composition";
|
|
7
9
|
|
|
8
|
-
class RootCtx extends
|
|
10
|
+
class RootCtx extends Arrmatron<CCompositeNode> {
|
|
9
11
|
get displayName(): string {
|
|
10
12
|
return `ROOT`;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
get recontentScope():
|
|
15
|
+
get recontentScope(): Arrmatron {
|
|
14
16
|
return this;
|
|
15
17
|
}
|
|
16
18
|
|
|
@@ -28,7 +30,7 @@ export class CRootNode extends ManifestNode {
|
|
|
28
30
|
nodes?: XmlNode["nodes"];
|
|
29
31
|
|
|
30
32
|
constructor(private template: string) {
|
|
31
|
-
super();
|
|
33
|
+
super('R0');
|
|
32
34
|
this.slotContent = {};
|
|
33
35
|
}
|
|
34
36
|
|
package/src/registry/routing.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { ManifestNode } from "../core/ManifestNode";
|
|
2
|
-
import { Entitron } from "../core/Entitron";
|
|
3
1
|
import type { XmlNode } from "ultimus/types";
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
import { Arrmatron } from "../core/Arrmatron";
|
|
4
|
+
import { ManifestNode } from "../core/ManifestNode";
|
|
5
|
+
|
|
6
|
+
class DynamicTag extends Arrmatron<CDynamicTagNode> {
|
|
6
7
|
get displayName(): string {
|
|
7
8
|
return "dynamics";
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
get contentManifests() {
|
|
11
|
-
const tag = this
|
|
12
|
+
const tag = this.$component.tag;
|
|
12
13
|
const uid = `${this.uid}:${String(tag ?? "")}`;
|
|
13
14
|
return new Map([
|
|
14
15
|
[uid, Object.assign(this.platform.getCompiledNodes({ ...this.manifest.xml, tag } as XmlNode), { uid })],
|
|
@@ -20,7 +21,7 @@ export class CDynamicTagNode extends ManifestNode {
|
|
|
20
21
|
xml: XmlNode & { tag: string };
|
|
21
22
|
|
|
22
23
|
constructor(xml: XmlNode, expr: string) {
|
|
23
|
-
super();
|
|
24
|
+
super(xml.id);
|
|
24
25
|
this.compileAttribute("tag", expr);
|
|
25
26
|
this.xml = xml;
|
|
26
27
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { ManifestNode } from "../core/ManifestNode";
|
|
2
|
-
import { Entitron } from "../core/Entitron";
|
|
3
|
-
import type { Hash, XmlNode } from "ultimus/types";
|
|
4
1
|
import type { EContent, IPlatform } from "arrmatura/types";
|
|
2
|
+
import type { Hash, XmlNode } from "ultimus/types";
|
|
3
|
+
|
|
4
|
+
import { Arrmatron } from "../core/Arrmatron";
|
|
5
|
+
import { ManifestNode } from "../core/ManifestNode";
|
|
5
6
|
|
|
6
|
-
export class Selector extends
|
|
7
|
+
export class Selector extends Arrmatron<CSelectorNode> {
|
|
7
8
|
get displayName(): string {
|
|
8
9
|
return `💠Selector`;
|
|
9
10
|
}
|
|
@@ -15,9 +16,10 @@ export class Selector extends Entitron<CSelectorNode> {
|
|
|
15
16
|
*/
|
|
16
17
|
get contentManifests() {
|
|
17
18
|
const cases = this.manifest.getCases(this.platform);
|
|
18
|
-
const
|
|
19
|
+
const onKey = this.$component.On as string;
|
|
20
|
+
const matchedCase = cases?.[onKey];
|
|
19
21
|
if (!matchedCase) {
|
|
20
|
-
this.log("unmatched case",
|
|
22
|
+
this.log("unmatched case", onKey, cases);
|
|
21
23
|
}
|
|
22
24
|
return matchedCase ?? cases?.["default"] ?? Object.values(cases)[0];
|
|
23
25
|
}
|
|
@@ -28,8 +30,8 @@ export class CSelectorNode extends ManifestNode {
|
|
|
28
30
|
key = "";
|
|
29
31
|
nodes?: XmlNode["nodes"];
|
|
30
32
|
|
|
31
|
-
constructor({ tag, attrs, nodes }: XmlNode) {
|
|
32
|
-
super();
|
|
33
|
+
constructor({ id, tag, attrs, nodes }: XmlNode) {
|
|
34
|
+
super(id);
|
|
33
35
|
this.tag = tag;
|
|
34
36
|
this.nodes = nodes;
|
|
35
37
|
this.compileAttributes(attrs);
|
|
@@ -45,7 +47,7 @@ export class CSelectorNode extends ManifestNode {
|
|
|
45
47
|
return (
|
|
46
48
|
this.cases ??
|
|
47
49
|
(this.cases = (this.nodes ?? []).reduce<Hash<EContent>>((groups, node) => {
|
|
48
|
-
groups[node.attrs?.
|
|
50
|
+
groups[node.attrs?.When as string] = platform.getCompiledNodes(node.nodes ?? []);
|
|
49
51
|
return groups;
|
|
50
52
|
}, {}))
|
|
51
53
|
);
|