@getodk/xforms-engine 0.2.0 → 0.3.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.
Files changed (184) hide show
  1. package/dist/body/BodyElementDefinition.d.ts +4 -3
  2. package/dist/body/RepeatElementDefinition.d.ts +2 -2
  3. package/dist/body/control/ControlDefinition.d.ts +2 -2
  4. package/dist/body/control/select/ItemDefinition.d.ts +2 -2
  5. package/dist/body/control/select/ItemsetDefinition.d.ts +5 -4
  6. package/dist/body/group/BaseGroupDefinition.d.ts +1 -1
  7. package/dist/body/group/PresentationGroupDefinition.d.ts +1 -1
  8. package/dist/client/BaseNode.d.ts +68 -2
  9. package/dist/client/GroupNode.d.ts +2 -0
  10. package/dist/client/ModelValueNode.d.ts +37 -0
  11. package/dist/client/NoteNode.d.ts +53 -0
  12. package/dist/client/RootNode.d.ts +2 -0
  13. package/dist/client/SelectNode.d.ts +5 -3
  14. package/dist/client/StringNode.d.ts +5 -3
  15. package/dist/client/SubtreeNode.d.ts +2 -0
  16. package/dist/client/TextRange.d.ts +85 -2
  17. package/dist/client/constants.d.ts +9 -0
  18. package/dist/client/hierarchy.d.ts +14 -9
  19. package/dist/client/node-types.d.ts +2 -1
  20. package/dist/client/{RepeatRangeNode.d.ts → repeat/BaseRepeatRangeNode.d.ts} +18 -17
  21. package/dist/client/{RepeatInstanceNode.d.ts → repeat/RepeatInstanceNode.d.ts} +9 -8
  22. package/dist/client/repeat/RepeatRangeControlledNode.d.ts +19 -0
  23. package/dist/client/repeat/RepeatRangeUncontrolledNode.d.ts +20 -0
  24. package/dist/client/validation.d.ts +163 -0
  25. package/dist/expression/DependentExpression.d.ts +12 -8
  26. package/dist/index.d.ts +9 -4
  27. package/dist/index.js +2635 -678
  28. package/dist/index.js.map +1 -1
  29. package/dist/instance/Group.d.ts +3 -1
  30. package/dist/instance/ModelValue.d.ts +40 -0
  31. package/dist/instance/Note.d.ts +42 -0
  32. package/dist/instance/Root.d.ts +2 -0
  33. package/dist/instance/SelectField.d.ts +10 -4
  34. package/dist/instance/StringField.d.ts +11 -5
  35. package/dist/instance/Subtree.d.ts +2 -0
  36. package/dist/instance/abstract/DescendantNode.d.ts +5 -6
  37. package/dist/instance/abstract/InstanceNode.d.ts +2 -0
  38. package/dist/instance/hierarchy.d.ts +10 -5
  39. package/dist/instance/internal-api/ValidationContext.d.ts +21 -0
  40. package/dist/instance/{RepeatRange.d.ts → repeat/BaseRepeatRange.d.ts} +46 -45
  41. package/dist/instance/{RepeatInstance.d.ts → repeat/RepeatInstance.d.ts} +13 -12
  42. package/dist/instance/repeat/RepeatRangeControlled.d.ts +16 -0
  43. package/dist/instance/repeat/RepeatRangeUncontrolled.d.ts +35 -0
  44. package/dist/instance/text/TextRange.d.ts +4 -4
  45. package/dist/lib/reactivity/createComputedExpression.d.ts +6 -1
  46. package/dist/lib/reactivity/createNoteReadonlyThunk.d.ts +5 -0
  47. package/dist/lib/reactivity/node-state/createSharedNodeState.d.ts +1 -1
  48. package/dist/lib/reactivity/node-state/createSpecifiedState.d.ts +1 -1
  49. package/dist/lib/reactivity/text/createFieldHint.d.ts +3 -3
  50. package/dist/lib/reactivity/text/createNodeLabel.d.ts +2 -2
  51. package/dist/lib/reactivity/text/createNoteText.d.ts +25 -0
  52. package/dist/lib/reactivity/text/createTextRange.d.ts +5 -7
  53. package/dist/lib/reactivity/validation/createAggregatedViolations.d.ts +9 -0
  54. package/dist/lib/reactivity/validation/createValidation.d.ts +18 -0
  55. package/dist/model/BindDefinition.d.ts +4 -2
  56. package/dist/model/BindElement.d.ts +1 -0
  57. package/dist/model/{ValueNodeDefinition.d.ts → LeafNodeDefinition.d.ts} +2 -2
  58. package/dist/model/NodeDefinition.d.ts +8 -8
  59. package/dist/model/RepeatInstanceDefinition.d.ts +2 -2
  60. package/dist/model/RepeatRangeDefinition.d.ts +14 -4
  61. package/dist/parse/NoteNodeDefinition.d.ts +31 -0
  62. package/dist/parse/expression/RepeatCountControlExpression.d.ts +19 -0
  63. package/dist/parse/text/HintDefinition.d.ts +9 -0
  64. package/dist/parse/text/ItemLabelDefinition.d.ts +9 -0
  65. package/dist/parse/text/ItemsetLabelDefinition.d.ts +13 -0
  66. package/dist/parse/text/LabelDefinition.d.ts +15 -0
  67. package/dist/parse/text/MessageDefinition.d.ts +15 -0
  68. package/dist/parse/text/OutputChunkDefinition.d.ts +8 -0
  69. package/dist/parse/text/ReferenceChunkDefinition.d.ts +8 -0
  70. package/dist/parse/text/StaticTextChunkDefinition.d.ts +10 -0
  71. package/dist/parse/text/TranslationChunkDefinition.d.ts +9 -0
  72. package/dist/parse/text/abstract/TextChunkDefinition.d.ts +18 -0
  73. package/dist/parse/text/abstract/TextElementDefinition.d.ts +23 -0
  74. package/dist/parse/text/abstract/TextRangeDefinition.d.ts +35 -0
  75. package/dist/parse/xpath/dependency-analysis.d.ts +40 -0
  76. package/dist/parse/xpath/path-resolution.d.ts +70 -0
  77. package/dist/parse/xpath/predicate-analysis.d.ts +30 -0
  78. package/dist/parse/xpath/reference-parsing.d.ts +18 -0
  79. package/dist/parse/xpath/semantic-analysis.d.ts +98 -0
  80. package/dist/parse/xpath/syntax-traversal.d.ts +69 -0
  81. package/dist/solid.js +2636 -679
  82. package/dist/solid.js.map +1 -1
  83. package/package.json +14 -15
  84. package/src/body/BodyElementDefinition.ts +4 -3
  85. package/src/body/RepeatElementDefinition.ts +5 -17
  86. package/src/body/control/ControlDefinition.ts +4 -3
  87. package/src/body/control/select/ItemDefinition.ts +3 -3
  88. package/src/body/control/select/ItemsetDefinition.ts +29 -12
  89. package/src/body/control/select/ItemsetNodesetExpression.ts +1 -1
  90. package/src/body/group/BaseGroupDefinition.ts +3 -2
  91. package/src/body/group/PresentationGroupDefinition.ts +1 -1
  92. package/src/client/BaseNode.ts +73 -7
  93. package/src/client/GroupNode.ts +2 -0
  94. package/src/client/ModelValueNode.ts +40 -0
  95. package/src/client/NoteNode.ts +74 -0
  96. package/src/client/README.md +1 -0
  97. package/src/client/RootNode.ts +2 -0
  98. package/src/client/SelectNode.ts +5 -3
  99. package/src/client/StringNode.ts +5 -3
  100. package/src/client/SubtreeNode.ts +2 -0
  101. package/src/client/TextRange.ts +99 -2
  102. package/src/client/constants.ts +10 -0
  103. package/src/client/hierarchy.ts +30 -14
  104. package/src/client/node-types.ts +8 -1
  105. package/src/client/{RepeatRangeNode.ts → repeat/BaseRepeatRangeNode.ts} +18 -19
  106. package/src/client/{RepeatInstanceNode.ts → repeat/RepeatInstanceNode.ts} +10 -8
  107. package/src/client/repeat/RepeatRangeControlledNode.ts +20 -0
  108. package/src/client/repeat/RepeatRangeUncontrolledNode.ts +21 -0
  109. package/src/client/validation.ts +199 -0
  110. package/src/expression/DependentExpression.ts +45 -27
  111. package/src/index.ts +15 -8
  112. package/src/instance/Group.ts +10 -4
  113. package/src/instance/ModelValue.ts +104 -0
  114. package/src/instance/Note.ts +142 -0
  115. package/src/instance/Root.ts +9 -3
  116. package/src/instance/SelectField.ts +28 -6
  117. package/src/instance/StringField.ts +35 -9
  118. package/src/instance/Subtree.ts +9 -3
  119. package/src/instance/abstract/DescendantNode.ts +6 -7
  120. package/src/instance/abstract/InstanceNode.ts +20 -6
  121. package/src/instance/children.ts +42 -15
  122. package/src/instance/hierarchy.ts +21 -2
  123. package/src/instance/internal-api/ValidationContext.ts +23 -0
  124. package/src/instance/{RepeatRange.ts → repeat/BaseRepeatRange.ts} +114 -99
  125. package/src/instance/{RepeatInstance.ts → repeat/RepeatInstance.ts} +27 -22
  126. package/src/instance/repeat/RepeatRangeControlled.ts +82 -0
  127. package/src/instance/repeat/RepeatRangeUncontrolled.ts +67 -0
  128. package/src/instance/text/TextRange.ts +10 -4
  129. package/src/lib/reactivity/createComputedExpression.ts +22 -24
  130. package/src/lib/reactivity/createNoteReadonlyThunk.ts +33 -0
  131. package/src/lib/reactivity/createSelectItems.ts +21 -14
  132. package/src/lib/reactivity/node-state/createSharedNodeState.ts +1 -1
  133. package/src/lib/reactivity/text/createFieldHint.ts +9 -7
  134. package/src/lib/reactivity/text/createNodeLabel.ts +7 -5
  135. package/src/lib/reactivity/text/createNoteText.ts +72 -0
  136. package/src/lib/reactivity/text/createTextRange.ts +17 -90
  137. package/src/lib/reactivity/validation/createAggregatedViolations.ts +70 -0
  138. package/src/lib/reactivity/validation/createValidation.ts +196 -0
  139. package/src/model/BindComputation.ts +0 -4
  140. package/src/model/BindDefinition.ts +8 -6
  141. package/src/model/BindElement.ts +1 -0
  142. package/src/model/{ValueNodeDefinition.ts → LeafNodeDefinition.ts} +4 -4
  143. package/src/model/ModelBindMap.ts +4 -0
  144. package/src/model/NodeDefinition.ts +12 -12
  145. package/src/model/RepeatInstanceDefinition.ts +2 -2
  146. package/src/model/RepeatRangeDefinition.ts +49 -8
  147. package/src/model/RootDefinition.ts +7 -3
  148. package/src/parse/NoteNodeDefinition.ts +70 -0
  149. package/src/parse/TODO.md +3 -0
  150. package/src/parse/expression/RepeatCountControlExpression.ts +44 -0
  151. package/src/parse/text/HintDefinition.ts +25 -0
  152. package/src/parse/text/ItemLabelDefinition.ts +25 -0
  153. package/src/parse/text/ItemsetLabelDefinition.ts +44 -0
  154. package/src/parse/text/LabelDefinition.ts +61 -0
  155. package/src/parse/text/MessageDefinition.ts +49 -0
  156. package/src/parse/text/OutputChunkDefinition.ts +25 -0
  157. package/src/parse/text/ReferenceChunkDefinition.ts +14 -0
  158. package/src/parse/text/StaticTextChunkDefinition.ts +19 -0
  159. package/src/parse/text/TranslationChunkDefinition.ts +38 -0
  160. package/src/parse/text/abstract/TextChunkDefinition.ts +38 -0
  161. package/src/parse/text/abstract/TextElementDefinition.ts +71 -0
  162. package/src/parse/text/abstract/TextRangeDefinition.ts +70 -0
  163. package/src/parse/xpath/dependency-analysis.ts +105 -0
  164. package/src/parse/xpath/path-resolution.ts +475 -0
  165. package/src/parse/xpath/predicate-analysis.ts +61 -0
  166. package/src/parse/xpath/reference-parsing.ts +90 -0
  167. package/src/parse/xpath/semantic-analysis.ts +466 -0
  168. package/src/parse/xpath/syntax-traversal.ts +129 -0
  169. package/dist/body/text/HintDefinition.d.ts +0 -11
  170. package/dist/body/text/LabelDefinition.d.ts +0 -22
  171. package/dist/body/text/TextElementDefinition.d.ts +0 -33
  172. package/dist/body/text/TextElementOutputPart.d.ts +0 -13
  173. package/dist/body/text/TextElementPart.d.ts +0 -13
  174. package/dist/body/text/TextElementReferencePart.d.ts +0 -7
  175. package/dist/body/text/TextElementStaticPart.d.ts +0 -7
  176. package/dist/lib/xpath/analysis.d.ts +0 -23
  177. package/src/body/text/HintDefinition.ts +0 -26
  178. package/src/body/text/LabelDefinition.ts +0 -68
  179. package/src/body/text/TextElementDefinition.ts +0 -97
  180. package/src/body/text/TextElementOutputPart.ts +0 -27
  181. package/src/body/text/TextElementPart.ts +0 -31
  182. package/src/body/text/TextElementReferencePart.ts +0 -21
  183. package/src/body/text/TextElementStaticPart.ts +0 -26
  184. package/src/lib/xpath/analysis.ts +0 -241
@@ -1,7 +0,0 @@
1
- import { AnyTextElementDefinition, TextElement } from './TextElementDefinition.ts';
2
- import { TextElementPart } from './TextElementPart.ts';
3
-
4
- export declare class TextElementReferencePart extends TextElementPart<'reference'> {
5
- static from(context: AnyTextElementDefinition, element: TextElement): TextElementReferencePart | null;
6
- protected constructor(context: AnyTextElementDefinition, expression: string);
7
- }
@@ -1,7 +0,0 @@
1
- import { AnyTextElementDefinition } from './TextElementDefinition.ts';
2
- import { TextElementPart } from './TextElementPart.ts';
3
-
4
- export declare class TextElementStaticPart extends TextElementPart<'static'> {
5
- readonly stringValue: string;
6
- constructor(context: AnyTextElementDefinition, node: Text);
7
- }
@@ -1,23 +0,0 @@
1
- import { AnySyntaxNode } from '@getodk/xpath/static/grammar/SyntaxNode.js';
2
-
3
- export type SingleChildNode = Extract<AnySyntaxNode, {
4
- readonly children: readonly [AnySyntaxNode];
5
- }>;
6
- export declare const isItextFunctionCalled: (expression: string) => boolean;
7
- interface GetNodesetDependenciesOptions {
8
- readonly contextReference?: string | null;
9
- /**
10
- * @default false
11
- */
12
- readonly ignoreContextReference?: boolean;
13
- /**
14
- * Ignores location path sub-expressions whose full text is `null`. While this
15
- * is technically a valid relative name test step, it seems that real forms in
16
- * the wild use it as if XPath had an actual `null` token/value.
17
- *
18
- * @default true
19
- */
20
- readonly ignoreNullExpressions?: boolean;
21
- }
22
- export declare const getNodesetDependencies: (expression: string, options?: GetNodesetDependenciesOptions) => Set<string>;
23
- export {};
@@ -1,26 +0,0 @@
1
- import { getHintElement } from '../../lib/dom/query.ts';
2
- import type { XFormDefinition } from '../../XFormDefinition.ts';
3
- import type { AnyControlDefinition } from '../control/ControlDefinition.ts';
4
- import type { TextElement } from './TextElementDefinition.ts';
5
- import { TextElementDefinition } from './TextElementDefinition.ts';
6
-
7
- export interface HintElement extends TextElement {
8
- readonly localName: 'hint';
9
- }
10
-
11
- export class HintDefinition extends TextElementDefinition<'hint'> {
12
- static forElement(
13
- form: XFormDefinition,
14
- definition: AnyControlDefinition
15
- ): HintDefinition | null {
16
- const hintElement = getHintElement(definition.element);
17
-
18
- if (hintElement == null) {
19
- return null;
20
- }
21
-
22
- return new this(form, definition, hintElement);
23
- }
24
-
25
- readonly type = 'hint';
26
- }
@@ -1,68 +0,0 @@
1
- import { getLabelElement, getRepeatGroupLabelElement } from '../../lib/dom/query.ts';
2
- import type { XFormDefinition } from '../../XFormDefinition.ts';
3
- import type { AnyControlDefinition } from '../control/ControlDefinition.ts';
4
- import type { ItemDefinition } from '../control/select/ItemDefinition.ts';
5
- import type { ItemsetDefinition } from '../control/select/ItemsetDefinition.ts';
6
- import type { BaseGroupDefinition } from '../group/BaseGroupDefinition.ts';
7
- import type { RepeatElementDefinition } from '../RepeatElementDefinition.ts';
8
- import type { TextElement, TextElementOwner } from './TextElementDefinition.ts';
9
- import { TextElementDefinition } from './TextElementDefinition.ts';
10
-
11
- export interface LabelElement extends TextElement {
12
- readonly localName: 'label';
13
- }
14
-
15
- type StaticLabelContext = Exclude<TextElementOwner, ItemsetDefinition>;
16
-
17
- export class LabelDefinition extends TextElementDefinition<'label'> {
18
- protected static staticDefinition(
19
- form: XFormDefinition,
20
- definition: StaticLabelContext
21
- ): LabelDefinition | null {
22
- const labelElement = getLabelElement(definition.element);
23
-
24
- if (labelElement == null) {
25
- return null;
26
- }
27
-
28
- return new this(form, definition, labelElement);
29
- }
30
-
31
- static forControl(form: XFormDefinition, control: AnyControlDefinition): LabelDefinition | null {
32
- return this.staticDefinition(form, control);
33
- }
34
-
35
- static forRepeatGroup(
36
- form: XFormDefinition,
37
- repeat: RepeatElementDefinition
38
- ): LabelDefinition | null {
39
- const repeatGroupLabel = getRepeatGroupLabelElement(repeat.element);
40
-
41
- if (repeatGroupLabel == null) {
42
- return null;
43
- }
44
-
45
- return new this(form, repeat, repeatGroupLabel);
46
- }
47
-
48
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
- static forGroup(form: XFormDefinition, group: BaseGroupDefinition<any>): LabelDefinition | null {
50
- return this.staticDefinition(form, group);
51
- }
52
-
53
- static forItem(form: XFormDefinition, item: ItemDefinition): LabelDefinition | null {
54
- return this.staticDefinition(form, item);
55
- }
56
-
57
- static forItemset(form: XFormDefinition, itemset: ItemsetDefinition): LabelDefinition | null {
58
- const labelElement = getLabelElement(itemset.element);
59
-
60
- if (labelElement == null) {
61
- return null;
62
- }
63
-
64
- return new this(form, itemset, labelElement);
65
- }
66
-
67
- readonly type = 'label';
68
- }
@@ -1,97 +0,0 @@
1
- import { isCommentNode, isElementNode, isTextNode } from '@getodk/common/lib/dom/predicates.ts';
2
- import type { XFormDefinition } from '../../XFormDefinition.ts';
3
- import { type AnyDependentExpression } from '../../expression/DependentExpression.ts';
4
- import type { AnyGroupElementDefinition } from '../BodyDefinition.ts';
5
- import { BodyElementDefinition } from '../BodyElementDefinition.ts';
6
- import type { RepeatElementDefinition } from '../RepeatElementDefinition.ts';
7
- import type { AnyControlDefinition } from '../control/ControlDefinition.ts';
8
- import type { ItemDefinition } from '../control/select/ItemDefinition.ts';
9
- import type { ItemsetDefinition } from '../control/select/ItemsetDefinition.ts';
10
- import { TextElementOutputPart } from './TextElementOutputPart.ts';
11
- import { TextElementReferencePart } from './TextElementReferencePart.ts';
12
- import { TextElementStaticPart } from './TextElementStaticPart.ts';
13
-
14
- export type TextElementType = 'hint' | 'label';
15
-
16
- export interface TextElement extends Element {
17
- readonly localName: TextElementType;
18
- }
19
-
20
- export type TextElementOwner =
21
- | AnyControlDefinition
22
- | AnyGroupElementDefinition
23
- | ItemDefinition
24
- | ItemsetDefinition
25
- | RepeatElementDefinition;
26
-
27
- export type TextElementChild = TextElementOutputPart | TextElementStaticPart;
28
-
29
- export abstract class TextElementDefinition<
30
- Type extends TextElementType,
31
- > extends BodyElementDefinition<Type> {
32
- readonly category = 'support';
33
- abstract override readonly type: Type;
34
-
35
- override readonly reference: string | null;
36
- override readonly parentReference: string | null;
37
-
38
- readonly referenceExpression: TextElementReferencePart | null;
39
- readonly children: readonly TextElementChild[];
40
-
41
- override get isTranslated(): boolean {
42
- return this.owner.isTranslated;
43
- }
44
-
45
- override set isTranslated(value: true) {
46
- this.owner.isTranslated = value;
47
- }
48
-
49
- protected constructor(
50
- form: XFormDefinition,
51
- readonly owner: TextElementOwner,
52
- element: TextElement
53
- ) {
54
- super(form, owner, element);
55
-
56
- this.reference = owner.reference;
57
- this.parentReference = owner.parentReference;
58
- this.referenceExpression = TextElementReferencePart.from(this, element);
59
-
60
- const children = Array.from(element.childNodes).flatMap((node) => {
61
- if (isTextNode(node)) {
62
- return new TextElementStaticPart(this, node);
63
- }
64
-
65
- if (isElementNode(node)) {
66
- const output = TextElementOutputPart.from(this, node);
67
-
68
- if (output != null) {
69
- return output;
70
- }
71
- }
72
-
73
- if (isCommentNode(node)) {
74
- return [];
75
- }
76
-
77
- // eslint-disable-next-line no-console
78
- console.error('Unexpected text element child', node);
79
-
80
- throw new Error(`Unexpected <${element.nodeName}> child element`);
81
- });
82
-
83
- this.children = children;
84
- }
85
-
86
- override registerDependentExpression(expression: AnyDependentExpression): void {
87
- this.owner.registerDependentExpression(expression);
88
- }
89
-
90
- override toJSON(): object {
91
- const { form, owner, parent, ...rest } = this;
92
-
93
- return rest;
94
- }
95
- }
96
-
97
- export type AnyTextElementDefinition = TextElementDefinition<TextElementType>;
@@ -1,27 +0,0 @@
1
- import type { AnyTextElementDefinition } from './TextElementDefinition.ts';
2
- import { TextElementPart } from './TextElementPart.ts';
3
-
4
- interface OutputElement extends Element {
5
- readonly localName: 'output';
6
-
7
- getAttribute(name: 'value'): string;
8
- getAttribute(name: string): string | null;
9
- }
10
-
11
- const isOutputElement = (element: Element): element is OutputElement => {
12
- return element.localName === 'output' && element.hasAttribute('value');
13
- };
14
-
15
- export class TextElementOutputPart extends TextElementPart<'output'> {
16
- static from(context: AnyTextElementDefinition, element: Element): TextElementOutputPart | null {
17
- if (isOutputElement(element)) {
18
- return new this(context, element);
19
- }
20
-
21
- return null;
22
- }
23
-
24
- protected constructor(context: AnyTextElementDefinition, element: OutputElement) {
25
- super('output', context, element.getAttribute('value'));
26
- }
27
- }
@@ -1,31 +0,0 @@
1
- import { DependentExpression } from '../../expression/DependentExpression.ts';
2
- import type { AnyTextElementDefinition } from './TextElementDefinition.ts';
3
- import type { TextElementOutputPart } from './TextElementOutputPart.ts';
4
- import type { TextElementReferencePart } from './TextElementReferencePart.ts';
5
- import type { TextElementStaticPart } from './TextElementStaticPart.ts';
6
-
7
- export type TextElementPartType = 'output' | 'reference' | 'static';
8
-
9
- export abstract class TextElementPart<
10
- Type extends TextElementPartType,
11
- > extends DependentExpression<'string'> {
12
- readonly stringValue?: string;
13
-
14
- constructor(
15
- readonly type: Type,
16
- context: AnyTextElementDefinition,
17
- expression: string
18
- ) {
19
- super(context, 'string', expression, {
20
- semanticDependencies: {
21
- translations: type !== 'static',
22
- },
23
- ignoreContextReference: true,
24
- });
25
- }
26
- }
27
-
28
- export type AnyTextElementPart =
29
- | TextElementOutputPart
30
- | TextElementReferencePart
31
- | TextElementStaticPart;
@@ -1,21 +0,0 @@
1
- import type { AnyTextElementDefinition, TextElement } from './TextElementDefinition.ts';
2
- import { TextElementPart } from './TextElementPart.ts';
3
-
4
- export class TextElementReferencePart extends TextElementPart<'reference'> {
5
- static from(
6
- context: AnyTextElementDefinition,
7
- element: TextElement
8
- ): TextElementReferencePart | null {
9
- const expression = element.getAttribute('ref');
10
-
11
- if (expression == null) {
12
- return null;
13
- }
14
-
15
- return new this(context, expression);
16
- }
17
-
18
- protected constructor(context: AnyTextElementDefinition, expression: string) {
19
- super('reference', context, expression);
20
- }
21
- }
@@ -1,26 +0,0 @@
1
- import type { AnyTextElementDefinition } from './TextElementDefinition.ts';
2
- import { TextElementPart } from './TextElementPart.ts';
3
-
4
- const toStaticXPathExpression = (staticTextValue: string): string => {
5
- const quote = staticTextValue.includes('"') ? "'" : '"';
6
-
7
- if (staticTextValue.includes(quote)) {
8
- // throw new Error('todo concat()');
9
- return 'todo(concat())';
10
- }
11
-
12
- return `${quote}${staticTextValue}${quote}`;
13
- };
14
-
15
- export class TextElementStaticPart extends TextElementPart<'static'> {
16
- override readonly stringValue: string;
17
-
18
- constructor(context: AnyTextElementDefinition, node: Text) {
19
- const stringValue = node.data;
20
- const expression = toStaticXPathExpression(stringValue);
21
-
22
- super('static', context, expression);
23
-
24
- this.stringValue = stringValue;
25
- }
26
- }
@@ -1,241 +0,0 @@
1
- import { UnreachableError } from '@getodk/common/lib/error/UnreachableError';
2
- import { expressionParser } from '@getodk/xpath/expressionParser.js';
3
- import type {
4
- AbsoluteLocationPathNode,
5
- AnyBinaryExprNode,
6
- AnySyntaxNode,
7
- FilterPathExprNode,
8
- LocalPartNode,
9
- PrefixedNameNode,
10
- RelativeLocationPathNode,
11
- UnprefixedNameNode,
12
- } from '@getodk/xpath/static/grammar/SyntaxNode.js';
13
- import type { AnyBinaryExprType } from '@getodk/xpath/static/grammar/type-names.js';
14
-
15
- export type SingleChildNode = Extract<
16
- AnySyntaxNode,
17
- { readonly children: readonly [AnySyntaxNode] }
18
- >;
19
-
20
- const isSingleNodeChild = (node: AnySyntaxNode): node is SingleChildNode => {
21
- return node.childCount === 1;
22
- };
23
-
24
- const binaryExprNodeTypes = new Set<AnyBinaryExprType>([
25
- 'addition_expr',
26
- 'and_expr',
27
- 'division_expr',
28
- 'eq_expr',
29
- 'gt_expr',
30
- 'gte_expr',
31
- 'lt_expr',
32
- 'lte_expr',
33
- 'modulo_expr',
34
- 'multiplication_expr',
35
- 'ne_expr',
36
- 'or_expr',
37
- 'subtraction_expr',
38
- 'union_expr',
39
- ]);
40
-
41
- const isBinaryExprNode = (node: AnySyntaxNode): node is AnyBinaryExprNode => {
42
- return binaryExprNodeTypes.has(node.type as AnyBinaryExprType);
43
- };
44
-
45
- const matchesLocalName = (
46
- localName: string,
47
- nameNode: PrefixedNameNode | UnprefixedNameNode
48
- ): boolean => {
49
- const localPartNode: LocalPartNode | UnprefixedNameNode =
50
- nameNode.type === 'prefixed_name' ? nameNode.children[1] : nameNode;
51
-
52
- return localPartNode.text === localName;
53
- };
54
-
55
- const isFunctionCalled = (localName: string, node: AnySyntaxNode): boolean => {
56
- if (!node.text.includes(localName)) {
57
- return false;
58
- }
59
-
60
- if (isSingleNodeChild(node)) {
61
- const [child] = node.children;
62
-
63
- return isFunctionCalled(localName, child);
64
- }
65
-
66
- if (isBinaryExprNode(node)) {
67
- const [lhs, rhs] = node.children;
68
-
69
- return isFunctionCalled(localName, lhs) || isFunctionCalled(localName, rhs);
70
- }
71
-
72
- switch (node.type) {
73
- // Terminal nodes
74
- case 'number':
75
- case 'string_literal':
76
- case 'variable_reference':
77
-
78
- // Path sub-nodes which could not have a function call child
79
- //
80
- // This only errors because this set of cases is commented:
81
- // eslint-disable-next-line no-fallthrough
82
- case '//':
83
- case 'absolute_root_location_path':
84
- case 'axis_name':
85
- case 'node_type_test':
86
- case 'parent':
87
- case 'self':
88
-
89
- // Name nodes are also not function call parents
90
- //
91
- // This only errors because this set of cases is commented:
92
- // eslint-disable-next-line no-fallthrough
93
- case 'local_part':
94
- case 'prefixed_name':
95
- case 'prefix':
96
- case 'unprefixed_name':
97
- case 'unprefixed_wildcard_name_test':
98
- return false;
99
-
100
- // Path sub-nodes which could have a function call child
101
- case 'abbreviated_absolute_location_path':
102
- case 'abbreviated_step':
103
- case 'absolute_location_path':
104
- case 'axis_test':
105
- case 'filter_path_expr':
106
- case 'relative_location_path':
107
- case 'step':
108
- return node.children.some((childNode) => isFunctionCalled(localName, childNode));
109
-
110
- case 'function_call': {
111
- const [functionNameNode] = node.children;
112
- const [nameNode] = functionNameNode.children;
113
-
114
- return matchesLocalName(localName, nameNode);
115
- }
116
-
117
- default:
118
- throw new UnreachableError(node);
119
- }
120
- };
121
-
122
- export const isItextFunctionCalled = (expression: string): boolean => {
123
- const { rootNode } = expressionParser.parse(expression);
124
-
125
- return isFunctionCalled('itext', rootNode);
126
- };
127
-
128
- type LocationPathSubExpressionNode =
129
- | AbsoluteLocationPathNode
130
- | FilterPathExprNode
131
- | RelativeLocationPathNode;
132
-
133
- const isAnyLocationPathExprNode = (node: AnySyntaxNode): node is LocationPathSubExpressionNode => {
134
- const { type } = node;
135
-
136
- if (type === 'absolute_location_path' || type === 'relative_location_path') {
137
- return true;
138
- }
139
-
140
- return false;
141
- };
142
-
143
- // TODO: this does not currently even attempt to find sub-expressions nested
144
- // within sub-expressions.
145
- const findLocationPathExprNodes = (
146
- node: AnySyntaxNode
147
- ): readonly LocationPathSubExpressionNode[] => {
148
- const results: LocationPathSubExpressionNode[] = [];
149
-
150
- if (isAnyLocationPathExprNode(node)) {
151
- results.push(node);
152
- }
153
-
154
- results.push(
155
- ...node.children.flatMap((childNode) => {
156
- return findLocationPathExprNodes(childNode);
157
- })
158
- );
159
-
160
- return results;
161
- };
162
-
163
- // TODO: this is a very small subset of resolution that needs to be supported,
164
- // and it's a hamfisted hack. **This is temporary** to unblock progress on
165
- // computations, but a longer term solution will need to address:
166
- //
167
- // - non-abbreviation axes (parent, child, self) according to XForms spec
168
- // - non-leading axes
169
- // - context expressions which are more complex than a series of explicit
170
- // element name test steps (this may be fine for binds!)
171
- const resolveRelativeSubExpression = (contextReference: string | null, expression: string) => {
172
- if (contextReference == null) {
173
- return expression;
174
- }
175
-
176
- const [, axisAbbreviation, relativeExpression = ''] = expression.match(/^(\.{1,2})(\/.*$)?/) ?? [
177
- null,
178
- '',
179
- expression,
180
- ];
181
-
182
- switch (axisAbbreviation) {
183
- case '':
184
- return expression;
185
-
186
- case '.':
187
- return `${contextReference}${relativeExpression}`;
188
-
189
- case '..':
190
- return `${contextReference.replace(/\/[^/]+$/, '')}${relativeExpression}`;
191
- }
192
-
193
- throw new Error(`Unexpected relative expression: ${expression}`);
194
- };
195
-
196
- interface GetNodesetDependenciesOptions {
197
- readonly contextReference?: string | null;
198
-
199
- /**
200
- * @default false
201
- */
202
- readonly ignoreContextReference?: boolean;
203
-
204
- /**
205
- * Ignores location path sub-expressions whose full text is `null`. While this
206
- * is technically a valid relative name test step, it seems that real forms in
207
- * the wild use it as if XPath had an actual `null` token/value.
208
- *
209
- * @default true
210
- */
211
- readonly ignoreNullExpressions?: boolean;
212
- }
213
-
214
- export const getNodesetDependencies = (
215
- expression: string,
216
- options: GetNodesetDependenciesOptions = {}
217
- ): Set<string> => {
218
- const { rootNode } = expressionParser.parse(expression);
219
- const subExpressionNodes = findLocationPathExprNodes(rootNode);
220
- const {
221
- contextReference = null,
222
- ignoreContextReference = false,
223
- ignoreNullExpressions = true,
224
- } = options;
225
-
226
- const subExpressions = subExpressionNodes
227
- .map((syntaxNode) => resolveRelativeSubExpression(contextReference, syntaxNode.text))
228
- .filter((subExpression) => {
229
- if (ignoreContextReference && subExpression === contextReference) {
230
- return false;
231
- }
232
-
233
- if (ignoreNullExpressions && subExpression === 'null') {
234
- return false;
235
- }
236
-
237
- return true;
238
- });
239
-
240
- return new Set(subExpressions);
241
- };