@siteimprove/alfa-dom 0.108.2 → 0.109.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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @siteimprove/alfa-dom
2
2
 
3
+ ## 0.109.0
4
+
5
+ ### Patch Changes
6
+
7
+ - **Added:** A new function `getTextDescendants` has been added to the `Query` namespace. It retrieves all text descendants of a node, with an option to group text under matching sub-trees into labeled `TextGroup` objects. ([#1972](https://github.com/Siteimprove/alfa/pull/1972))
8
+
9
+ - **Changed:** The `index()` method on nodes now accepts an optional predicate parameter to filter siblings when calculating the node's index position. ([#1980](https://github.com/Siteimprove/alfa/pull/1980))
10
+
3
11
  ## 0.108.2
4
12
 
5
13
  ## 0.108.1
@@ -28,7 +28,7 @@ export class Comment extends Node {
28
28
  .getOr("/");
29
29
  path += path === "/" ? "" : "/";
30
30
  path += "comment()";
31
- const index = this.preceding(options).count(Comment.isComment);
31
+ const index = this.index(options, Comment.isComment);
32
32
  path += `[${index + 1}]`;
33
33
  return path;
34
34
  }
@@ -205,9 +205,7 @@ export class Element extends Node {
205
205
  .getOr("/");
206
206
  path += path === "/" ? "" : "/";
207
207
  path += this._name;
208
- const index = this.preceding(options)
209
- .filter(Element.isElement)
210
- .count((element) => element._name === this._name);
208
+ const index = this.index(options, (node) => Element.isElement(node) && node._name === this._name);
211
209
  path += `[${index + 1}]`;
212
210
  return path;
213
211
  }
@@ -1,8 +1,9 @@
1
1
  import type { Predicate } from "@siteimprove/alfa-predicate";
2
2
  import type { Refinement } from "@siteimprove/alfa-refinement";
3
- import type { Sequence } from "@siteimprove/alfa-sequence";
3
+ import { Sequence } from "@siteimprove/alfa-sequence";
4
4
  import { Node } from "../../node.js";
5
5
  import { Element } from "../element.js";
6
+ import { Text } from "../text.js";
6
7
  /**
7
8
  * Get all descendants of a node that satisfy a given refinement.
8
9
  *
@@ -31,4 +32,36 @@ export declare const getElementDescendants: (node: Node, options?: Node.Traversa
31
32
  * @public
32
33
  */
33
34
  export declare function getInclusiveElementDescendants(node: Element, options?: Node.Traversal): Sequence<Element>;
35
+ /**
36
+ * A group of text nodes with an associated label.
37
+ *
38
+ * @public
39
+ */
40
+ export interface TextGroup {
41
+ label: string;
42
+ text: Sequence<Text>;
43
+ }
44
+ /**
45
+ * Options for grouping text descendants.
46
+ *
47
+ * @public
48
+ */
49
+ export interface TextGroupOptions<N extends Node = Node> {
50
+ startsGroup: Refinement<Node, N>;
51
+ getLabel: (node: N) => string;
52
+ }
53
+ /**
54
+ * Get all text descendants of a node, optionally grouping some into labeled groups.
55
+ *
56
+ * @remarks
57
+ * When a descendant matches `startsGroup`, all of its text descendants are collected
58
+ * into a {@link TextGroup} with a label from `getLabel`. Text nodes outside such
59
+ * sub-trees are returned as plain {@link Text} nodes.
60
+ *
61
+ * Groups are not nested: if a `startsGroup` node contains another `startsGroup` node,
62
+ * the inner node's text is included in the outer group, not as a separate group.
63
+ *
64
+ * @public
65
+ */
66
+ export declare function getTextDescendants<N extends Node = Node>(textOptions?: TextGroupOptions<N>): (node: Node, options?: Node.Traversal) => Sequence<Text | TextGroup>;
34
67
  //# sourceMappingURL=descendants.d.ts.map
@@ -1,6 +1,8 @@
1
1
  import { Cache } from "@siteimprove/alfa-cache";
2
+ import { Sequence } from "@siteimprove/alfa-sequence";
2
3
  import { Node } from "../../node.js";
3
4
  import { Element } from "../element.js";
5
+ import { Text } from "../text.js";
4
6
  const _descendantsCache = Cache.empty();
5
7
  export function getDescendants(predicate) {
6
8
  return (node, options = Node.Traversal.empty) => {
@@ -23,4 +25,51 @@ export const getElementDescendants = getDescendants(Element.isElement);
23
25
  export function getInclusiveElementDescendants(node, options = Node.Traversal.empty) {
24
26
  return getElementDescendants(node, options).prepend(node);
25
27
  }
28
+ const _textCache = Cache.empty();
29
+ const defaultTextOptions = {
30
+ startsGroup: (node) => false,
31
+ getLabel: () => "",
32
+ };
33
+ /**
34
+ * Get all text descendants of a node, optionally grouping some into labeled groups.
35
+ *
36
+ * @remarks
37
+ * When a descendant matches `startsGroup`, all of its text descendants are collected
38
+ * into a {@link TextGroup} with a label from `getLabel`. Text nodes outside such
39
+ * sub-trees are returned as plain {@link Text} nodes.
40
+ *
41
+ * Groups are not nested: if a `startsGroup` node contains another `startsGroup` node,
42
+ * the inner node's text is included in the outer group, not as a separate group.
43
+ *
44
+ * @public
45
+ */
46
+ export function getTextDescendants(textOptions = defaultTextOptions) {
47
+ return (node, options = Node.Traversal.empty) => {
48
+ const optionsMap = _textCache
49
+ .get(textOptions, Cache.empty)
50
+ .get(node, () => []);
51
+ if (optionsMap[options.value] === undefined) {
52
+ optionsMap[options.value] = Sequence.from(_getTextDescendants(node, textOptions, options));
53
+ }
54
+ return optionsMap[options.value];
55
+ };
56
+ }
57
+ function* _getTextDescendants(node, textOptions, traversalOptions) {
58
+ const { startsGroup, getLabel } = textOptions;
59
+ for (const child of node.children(traversalOptions)) {
60
+ if (startsGroup(child)) {
61
+ const groupText = getDescendants(Text.isText)(child, traversalOptions);
62
+ yield {
63
+ label: getLabel(child),
64
+ text: groupText,
65
+ };
66
+ }
67
+ else if (Text.isText(child)) {
68
+ yield child;
69
+ }
70
+ else {
71
+ yield* _getTextDescendants(child, textOptions, traversalOptions);
72
+ }
73
+ }
74
+ }
26
75
  //# sourceMappingURL=descendants.js.map
@@ -1,12 +1,16 @@
1
1
  import * as descendants from "./descendants.js";
2
2
  import * as elementIdMap from "./element-id-map.js";
3
+ import type { Node } from "../../node.js";
3
4
  /**
4
5
  * @public
5
6
  */
6
7
  export declare namespace Query {
7
8
  const getDescendants: typeof descendants.getDescendants;
8
- const getElementDescendants: (node: import("../../node.js").Node, options?: import("../../node.js").Node.Traversal) => import("@siteimprove/alfa-sequence").Sequence<import("../element.js").Element<string>>;
9
- const getElementIdMap: typeof elementIdMap.getElementIdMap;
9
+ const getElementDescendants: (node: Node, options?: Node.Traversal) => import("@siteimprove/alfa-sequence").Sequence<import("../element.js").Element<string>>;
10
10
  const getInclusiveElementDescendants: typeof descendants.getInclusiveElementDescendants;
11
+ const getTextDescendants: typeof descendants.getTextDescendants;
12
+ const getElementIdMap: typeof elementIdMap.getElementIdMap;
13
+ type TextGroup = descendants.TextGroup;
14
+ type TextGroupOptions<N extends Node = Node> = descendants.TextGroupOptions<N>;
11
15
  }
12
16
  //# sourceMappingURL=index.d.ts.map
@@ -7,7 +7,8 @@ export var Query;
7
7
  (function (Query) {
8
8
  Query.getDescendants = descendants.getDescendants;
9
9
  Query.getElementDescendants = descendants.getElementDescendants;
10
- Query.getElementIdMap = elementIdMap.getElementIdMap;
11
10
  Query.getInclusiveElementDescendants = descendants.getInclusiveElementDescendants;
11
+ Query.getTextDescendants = descendants.getTextDescendants;
12
+ Query.getElementIdMap = elementIdMap.getElementIdMap;
12
13
  })(Query || (Query = {}));
13
14
  //# sourceMappingURL=index.js.map
package/dist/node/text.js CHANGED
@@ -44,7 +44,7 @@ export class Text extends Node {
44
44
  .getOr("/");
45
45
  path += path === "/" ? "" : "/";
46
46
  path += "text()";
47
- const index = this.preceding(options).count(Text.isText);
47
+ const index = this.index(options, Text.isText);
48
48
  path += `[${index + 1}]`;
49
49
  return path;
50
50
  }
package/dist/node.d.ts CHANGED
@@ -73,7 +73,7 @@ export interface Node {
73
73
  last(options?: Node.Traversal): Option<Node>;
74
74
  previous(options?: Node.Traversal): Option<Node>;
75
75
  next(options?: Node.Traversal): Option<Node>;
76
- index(options?: Node.Traversal): number;
76
+ index(options?: Node.Traversal, predicate?: Predicate<Node>): number;
77
77
  closest<T extends Node>(refinement: Refinement<Node, T>, options?: Node.Traversal): Option<T>;
78
78
  closest(predicate: Predicate<Node>, options?: Node.Traversal): Option<Node>;
79
79
  }
package/dist/node.js CHANGED
@@ -183,13 +183,10 @@ export class Node extends tree.Node {
183
183
  // we accept the risk of caching the value assuming that it will only be
184
184
  // computed on fully frozen trees.
185
185
  path(options = Node.Traversal.empty) {
186
- if (this._path[options.value] !== undefined) {
187
- return this._path[options.value];
188
- }
189
- else {
186
+ if (this._path[options.value] == undefined) {
190
187
  this._path[options.value] = this._internalPath(options);
191
- return this._internalPath(options);
192
188
  }
189
+ return this._path[options.value];
193
190
  }
194
191
  equals(value) {
195
192
  return value === this;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "http://json.schemastore.org/package",
3
3
  "name": "@siteimprove/alfa-dom",
4
4
  "homepage": "https://alfa.siteimprove.com",
5
- "version": "0.108.2",
5
+ "version": "0.109.0",
6
6
  "license": "MIT",
7
7
  "description": "Implementations of the core DOM and CSSOM node types",
8
8
  "repository": {
@@ -35,37 +35,37 @@
35
35
  "dist/**/*.d.ts"
36
36
  ],
37
37
  "dependencies": {
38
- "@siteimprove/alfa-array": "^0.108.2",
39
- "@siteimprove/alfa-cache": "^0.108.2",
40
- "@siteimprove/alfa-comparable": "^0.108.2",
41
- "@siteimprove/alfa-css": "^0.108.2",
42
- "@siteimprove/alfa-css-feature": "^0.108.2",
43
- "@siteimprove/alfa-device": "^0.108.2",
44
- "@siteimprove/alfa-earl": "^0.108.2",
45
- "@siteimprove/alfa-equatable": "^0.108.2",
46
- "@siteimprove/alfa-flags": "^0.108.2",
47
- "@siteimprove/alfa-iterable": "^0.108.2",
48
- "@siteimprove/alfa-json": "^0.108.2",
49
- "@siteimprove/alfa-lazy": "^0.108.2",
50
- "@siteimprove/alfa-map": "^0.108.2",
51
- "@siteimprove/alfa-option": "^0.108.2",
52
- "@siteimprove/alfa-parser": "^0.108.2",
53
- "@siteimprove/alfa-predicate": "^0.108.2",
54
- "@siteimprove/alfa-rectangle": "^0.108.2",
55
- "@siteimprove/alfa-refinement": "^0.108.2",
56
- "@siteimprove/alfa-result": "^0.108.2",
57
- "@siteimprove/alfa-sarif": "^0.108.2",
58
- "@siteimprove/alfa-selective": "^0.108.2",
59
- "@siteimprove/alfa-sequence": "^0.108.2",
60
- "@siteimprove/alfa-slice": "^0.108.2",
61
- "@siteimprove/alfa-string": "^0.108.2",
62
- "@siteimprove/alfa-trampoline": "^0.108.2",
63
- "@siteimprove/alfa-tree": "^0.108.2"
38
+ "@siteimprove/alfa-array": "^0.109.0",
39
+ "@siteimprove/alfa-cache": "^0.109.0",
40
+ "@siteimprove/alfa-comparable": "^0.109.0",
41
+ "@siteimprove/alfa-css": "^0.109.0",
42
+ "@siteimprove/alfa-css-feature": "^0.109.0",
43
+ "@siteimprove/alfa-device": "^0.109.0",
44
+ "@siteimprove/alfa-earl": "^0.109.0",
45
+ "@siteimprove/alfa-equatable": "^0.109.0",
46
+ "@siteimprove/alfa-flags": "^0.109.0",
47
+ "@siteimprove/alfa-iterable": "^0.109.0",
48
+ "@siteimprove/alfa-json": "^0.109.0",
49
+ "@siteimprove/alfa-lazy": "^0.109.0",
50
+ "@siteimprove/alfa-map": "^0.109.0",
51
+ "@siteimprove/alfa-option": "^0.109.0",
52
+ "@siteimprove/alfa-parser": "^0.109.0",
53
+ "@siteimprove/alfa-predicate": "^0.109.0",
54
+ "@siteimprove/alfa-rectangle": "^0.109.0",
55
+ "@siteimprove/alfa-refinement": "^0.109.0",
56
+ "@siteimprove/alfa-result": "^0.109.0",
57
+ "@siteimprove/alfa-sarif": "^0.109.0",
58
+ "@siteimprove/alfa-selective": "^0.109.0",
59
+ "@siteimprove/alfa-sequence": "^0.109.0",
60
+ "@siteimprove/alfa-slice": "^0.109.0",
61
+ "@siteimprove/alfa-string": "^0.109.0",
62
+ "@siteimprove/alfa-trampoline": "^0.109.0",
63
+ "@siteimprove/alfa-tree": "^0.109.0"
64
64
  },
65
65
  "devDependencies": {
66
- "@siteimprove/alfa-test": "^0.108.2",
66
+ "@siteimprove/alfa-test": "^0.109.0",
67
67
  "@types/jsdom": "^27.0.0",
68
- "jsdom": "^27.2.0"
68
+ "jsdom": "^27.4.0"
69
69
  },
70
70
  "publishConfig": {
71
71
  "access": "public",