xml-twig 1.7.13 → 1.9.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 (4) hide show
  1. package/README.md +18 -18
  2. package/doc/twig.md +98 -32
  3. package/package.json +6 -7
  4. package/twig.js +21 -1
package/README.md CHANGED
@@ -4,27 +4,29 @@ Node module for processing huge XML documents in tree mode
4
4
  Inspired by Perl module [XML::Twig](https://metacpan.org/pod/XML::Twig)
5
5
 
6
6
 
7
- ## When should I use this, motivation of this module
8
- When you need to read a XML file, then you have two principles:
7
+ ## When to Use This Module and Its Motivation
8
+ When you need to read an XML file, there are two primary approaches:
9
9
 
10
- * The **Document Object Model (DOM)** style. These parser read the entire XML document into memory. Usually they provide easy methods to navigate in the document tree or make modifications.
10
+ 1. **The Document Object Model (DOM) Style**
11
11
 
12
- DOM parsers are perfect for rather small files, for example configuration files or (X-)HTML pages. However, for bigger XML files you may run into memory limits. When you parse a XML-File as DOM, then the footprint in RAM can be easily 10-20 times the size of the raw XML-String. If the XML-File is greater than [Buffer.constants.MAX_STRING_LENGTH](https://nodejs.org/api/buffer.html#bufferconstantsmax_string_length) (typically 512 MiB), then a DOM parser may throw error "Cannot create a string longer than 0x1fffffe8 characters".
12
+ These parsers read the entire XML document into memory. They usually provide convenient methods for navigating the document tree or making modifications. DOM parsers are ideal for smaller files, such as configuration files or (X-)HTML pages. However, for larger XML files, you may run into memory limitations. Parsing an XML file using the DOM method can cause memory usage to increase by 10-20 times the size of the raw XML string. If the XML file exceeds the size of [Buffer.constants.MAX_STRING_LENGTH](https://nodejs.org/api/buffer.html#bufferconstantsmax_string_length) (typically 512 MB), the DOM parser may throw an error: "Cannot create a string longer than 0x1fffffe8 characters."
13
13
 
14
- * The **stream** or **event** based parsers. These parser read the XML file "line by line". The biggest advantage of such a parser is, there is no limit in the size of the XML file. You can read XML files having a size of many terabytes, because you read always just a single node.
14
+ 1. **Stream or Event-Based Parsers**
15
15
 
16
- The backside: By default you cannot navigate in the document tree, you know only the current node.
16
+ These parsers read the XML file "line by line" or node by node. The main advantage of this approach is that there is no size limitation for the XML file. You can read XML files of several terabytes because only a single node is read into memory at a time.
17
+
18
+ The downside is that, by default, you cannot navigate the document tree - you can only access the current node.
17
19
 
18
- This module tries to combine both principles. The XML document can be read in chunks and within a chunk you have all the nice features and functions you know from a DOM based parser.
20
+ This module aims to combine both approaches. It reads the XML document in chunks, and within each chunk, you can utilize the familiar features and functions of a DOM-based parser.
19
21
 
20
22
  ## Dependencies
21
- XML documents are read either with [sax](https://www.npmjs.com/package/sax) or [node-expat](https://www.npmjs.com/package/node-expat) parser. More parser may be added in future releases. By default the `sax` parser is used. However, I clearly recommend using the `node-expat` parser. All other parsers I tested, are not compliant to XML standards.
23
+ XML documents are parsed using either the [sax](https://www.npmjs.com/package/sax) or [node-expat](https://www.npmjs.com/package/node-expat) parser. parsers. Additional parsers may be added in future releases. By default, the `sax` parser is used. However, I strongly recommend using the `node-expat` parser, as other parsers I tested are not fully compliant with XML standards.
22
24
 
23
- **NOTE: The `node-expat` module is not automatically installed with this module. Install the parser by yourself, if you like to use it**
25
+ **NOTE: The `node-expat` module is not automatically installed with this module. If you wish to use it, you must install it manually.**
24
26
 
25
27
  ## Installation
26
28
 
27
- Install module like any other node module and optionally `node-expat`:
29
+ To install the module, use the standard Node.js installation process. Optionally, you can also install the `node-expat` parser:
28
30
  ```bash
29
31
  npm install xml-twig
30
32
 
@@ -32,7 +34,7 @@ npm install xml-twig
32
34
  npm install node-expat
33
35
 
34
36
  ```
35
- In my tests I parsed a 900 MB big XML file, the `node-expat` is faster than `sax` (node-expat: around 2:30 Minutes, sax: around 3:40 Minutes). However, you may run into problems when you try to install the `node-expat` parser. That's the reason why `node-expat` parser is not installed automatically.
37
+ In my tests, I parsed a 900 MB XML file, and the `node-expat`t parser was faster than `sax` (`node-expat`: around 2:30 minutes, `sax`: around 3:40 minutes). However, you may encounter issues when installing the `node-expat` parser, which is why it's not installed automatically.
36
38
 
37
39
  ## How to use it
38
40
 
@@ -72,10 +74,9 @@ API Documentation: see [Twig](./doc/twig.md)
72
74
 
73
75
  - **Read XML Document in chucks**
74
76
 
75
- The key feature of this module is to read and process XML files in chunks. You need to create handler functions for elements you like to process.<br>
76
- The most notable difference to other parsers is the `purge()` and `purgeUpTo()` method. The parser reads the element and you decide how long you need to keep it in the memory.
77
- In many cases you will purge it immediately after you have used it but in some cases you may keep the element for later use. The parser knows the element position in the XML-Tree.
77
+ The key feature of this module is the ability to read and process XML files in chunks. You need to define handler functions for the elements you want to process.
78
78
 
79
+ A major difference compared to other parsers is the `purge()` and `purgeUpTo()` methods. The parser reads an element, and you decide how long to keep it in memory. In many cases, you will purge the element immediately after processing it, but in some situations, you might want to retain it for later use. The parser keeps track of the element’s position within the XML tree.
79
80
 
80
81
  ```js
81
82
  function bookHandler(elt, parserObj) {
@@ -146,10 +147,6 @@ API Documentation: see [Twig](./doc/twig.md)
146
147
 
147
148
  ```
148
149
 
149
- Be aware if you run methods like `elt.followingSibling()`, `elt.descendant()`, `elt.next()`, etc. on the current element. Such calls return empty result, because following element are not yet read from the XML file. You must navigate to an earlier element, e.g.<br>
150
- `elt.root().children()[0].followingSibling()`
151
-
152
-
153
150
  - **Read only parts from XML Document**
154
151
 
155
152
  If you like to read only certain elements, use option `partial: true`. The `root` element is always read.
@@ -294,6 +291,9 @@ Here are some examples the get attribute and values:
294
291
 
295
292
  `.writer(indented|xw)` - **XMLWriter**: Returns a [XMLWriter](https://www.npmjs.com/package/xml-writer) object you can use to print the currently loaded XML tree.<br>Instead of providing an indented parameter (`true`, `false` or indent character) you can also provide an `XMLWriter` object which adds more flexibility.
296
293
 
294
+ Be aware if you call methods like `elt.followingSibling()`, `elt.descendant()`, `elt.next()`, etc. on the current element, they will return empty results. This is because the following elements have not yet been read from the XML file. To navigate to an earlier element, you can use a method like:<br>
295
+ `elt.root().children()[0].followingSibling()`
296
+
297
297
  **condition** Parameter
298
298
 
299
299
  You can specify condition on above methods. You can filter elements by following conditions:
package/doc/twig.md CHANGED
@@ -156,6 +156,8 @@ You can specify a <code>function</code> or a <code>event</code> name</p>
156
156
  * [.parent](#Twig+parent) ⇒ [<code>Twig</code>](#Twig)
157
157
  * [.self](#Twig+self) ⇒ [<code>Twig</code>](#Twig)
158
158
  * [.children](#Twig+children) ⇒ [<code>Array.&lt;Twig&gt;</code>](#Twig)
159
+ * [.firstChild](#Twig+firstChild) ⇒ [<code>Twig</code>](#Twig)
160
+ * [.lastChild](#Twig+lastChild) ⇒ [<code>Twig</code>](#Twig)
159
161
  * [.next](#Twig+next) ⇒ [<code>Twig</code>](#Twig)
160
162
  * [.previous](#Twig+previous) ⇒ [<code>Twig</code>](#Twig)
161
163
  * [.first](#Twig+first) ⇒ [<code>Twig</code>](#Twig)
@@ -249,7 +251,8 @@ Purges the current, typically used after element has been processed.<br>The root
249
251
  <a name="Twig+purgeUpTo"></a>
250
252
 
251
253
  ### twig.purgeUpTo
252
- Purges up to the elt element. This allows you to keep part of the tree in memory when you purge.<br>
254
+ Purges up to the elt element. This allows you to keep part of the tree in memory when you purge.<br>
255
+ The `elt` object is not purged. If you like to purge including `elt`, use `.purgeUpTo(elt.previous())`
253
256
 
254
257
  **Kind**: instance property of [<code>Twig</code>](#Twig)
255
258
 
@@ -271,7 +274,8 @@ Escapes special XML characters. According W3C specification these are only `&, <
271
274
  <a name="Twig+isEmpty"></a>
272
275
 
273
276
  ### twig.isEmpty ⇒ <code>boolean</code>
274
- Returns `true` if the element is empty, otherwise `false`.
277
+ Returns `true` if the element is empty, otherwise `false`.
278
+ An empty element has no text nor any child elements, however empty elements can have attributes.
275
279
 
276
280
  **Kind**: instance property of [<code>Twig</code>](#Twig)
277
281
  **Returns**: <code>boolean</code> - true if empty element
@@ -306,7 +310,8 @@ The position in `#children` array. For root object 0
306
310
  <a name="Twig+path"></a>
307
311
 
308
312
  ### twig.path ⇒ <code>string</code>
309
- The X-Path position of the element
313
+ The X-Path position of the element
314
+ NOTE: Applies only to currently loaded elements.
310
315
 
311
316
  **Kind**: instance property of [<code>Twig</code>](#Twig)
312
317
  **Returns**: <code>string</code> - X-Path
@@ -406,7 +411,8 @@ Creates xml-writer from current element
406
411
  <a name="Twig+attr"></a>
407
412
 
408
413
  ### twig.attr ⇒ <code>string</code> \| <code>number</code> \| <code>object</code>
409
- Returns attribute value or `null` if not found.<br>
414
+ Returns attribute value or `null` if not found.<br>
415
+ If more than one matches the condition, then it returns object as [attribute()](#attribute)
410
416
 
411
417
  **Kind**: instance property of [<code>Twig</code>](#Twig)
412
418
  **Returns**: <code>string</code> \| <code>number</code> \| <code>object</code> - - The value of the attribute or `null` if the does not exist
@@ -449,7 +455,10 @@ Retrieve or update XML attribute. For update, the condition must be a string, i.
449
455
 
450
456
  **Example**
451
457
  ```js
452
- attribute((name, val) => { return name === 'age' && val > 50})
458
+ attribute((name, val) => { return name === 'age' && val > 50})
459
+ attribute((name) => { return ['firstName', 'lastName'].includes(name) })
460
+ attribute('firstName')
461
+ attribute(/name/i)
453
462
  ```
454
463
  <a name="Twig+deleteAttribute"></a>
455
464
 
@@ -492,6 +501,28 @@ All children, optionally matching `condition` of the current element or empty ar
492
501
  | --- | --- | --- |
493
502
  | condition | [<code>ElementCondition</code>](#ElementCondition) | Optional condition |
494
503
 
504
+ <a name="Twig+firstChild"></a>
505
+
506
+ ### twig.firstChild ⇒ [<code>Twig</code>](#Twig)
507
+ The first matching child, optionally matching `condition` of the current element or null
508
+
509
+ **Kind**: instance property of [<code>Twig</code>](#Twig)
510
+
511
+ | Param | Type | Description |
512
+ | --- | --- | --- |
513
+ | condition | [<code>ElementCondition</code>](#ElementCondition) | Optional condition |
514
+
515
+ <a name="Twig+lastChild"></a>
516
+
517
+ ### twig.lastChild ⇒ [<code>Twig</code>](#Twig)
518
+ The last matching child, optionally matching `condition` of the current element or null
519
+
520
+ **Kind**: instance property of [<code>Twig</code>](#Twig)
521
+
522
+ | Param | Type | Description |
523
+ | --- | --- | --- |
524
+ | condition | [<code>ElementCondition</code>](#ElementCondition) | Optional condition |
525
+
495
526
  <a name="Twig+next"></a>
496
527
 
497
528
  ### twig.next ⇒ [<code>Twig</code>](#Twig)
@@ -712,7 +743,8 @@ Deletes the current element from tree, same as `purge()`. The root object cannot
712
743
  <a name="Twig+setRoot"></a>
713
744
 
714
745
  ### twig.setRoot(name) ℗
715
- Sets the name of root element. In some cases the root is created before the XML-Root element is available<br>
746
+ Sets the name of root element. In some cases the root is created before the XML-Root element is available<br>
747
+ Used internally!
716
748
 
717
749
  **Kind**: instance method of [<code>Twig</code>](#Twig)
718
750
  **Access**: private
@@ -790,6 +822,8 @@ Common function to filter Twig element
790
822
  * [.parent](#Twig+parent) ⇒ [<code>Twig</code>](#Twig)
791
823
  * [.self](#Twig+self) ⇒ [<code>Twig</code>](#Twig)
792
824
  * [.children](#Twig+children) ⇒ [<code>Array.&lt;Twig&gt;</code>](#Twig)
825
+ * [.firstChild](#Twig+firstChild) ⇒ [<code>Twig</code>](#Twig)
826
+ * [.lastChild](#Twig+lastChild) ⇒ [<code>Twig</code>](#Twig)
793
827
  * [.next](#Twig+next) ⇒ [<code>Twig</code>](#Twig)
794
828
  * [.previous](#Twig+previous) ⇒ [<code>Twig</code>](#Twig)
795
829
  * [.first](#Twig+first) ⇒ [<code>Twig</code>](#Twig)
@@ -883,7 +917,8 @@ Purges the current, typically used after element has been processed.<br>The root
883
917
  <a name="Twig+purgeUpTo"></a>
884
918
 
885
919
  ### twig.purgeUpTo
886
- Purges up to the elt element. This allows you to keep part of the tree in memory when you purge.<br>
920
+ Purges up to the elt element. This allows you to keep part of the tree in memory when you purge.<br>
921
+ The `elt` object is not purged. If you like to purge including `elt`, use `.purgeUpTo(elt.previous())`
887
922
 
888
923
  **Kind**: instance property of [<code>Twig</code>](#Twig)
889
924
 
@@ -905,7 +940,8 @@ Escapes special XML characters. According W3C specification these are only `&, <
905
940
  <a name="Twig+isEmpty"></a>
906
941
 
907
942
  ### twig.isEmpty ⇒ <code>boolean</code>
908
- Returns `true` if the element is empty, otherwise `false`.
943
+ Returns `true` if the element is empty, otherwise `false`.
944
+ An empty element has no text nor any child elements, however empty elements can have attributes.
909
945
 
910
946
  **Kind**: instance property of [<code>Twig</code>](#Twig)
911
947
  **Returns**: <code>boolean</code> - true if empty element
@@ -940,7 +976,8 @@ The position in `#children` array. For root object 0
940
976
  <a name="Twig+path"></a>
941
977
 
942
978
  ### twig.path ⇒ <code>string</code>
943
- The X-Path position of the element
979
+ The X-Path position of the element
980
+ NOTE: Applies only to currently loaded elements.
944
981
 
945
982
  **Kind**: instance property of [<code>Twig</code>](#Twig)
946
983
  **Returns**: <code>string</code> - X-Path
@@ -1040,7 +1077,8 @@ Creates xml-writer from current element
1040
1077
  <a name="Twig+attr"></a>
1041
1078
 
1042
1079
  ### twig.attr ⇒ <code>string</code> \| <code>number</code> \| <code>object</code>
1043
- Returns attribute value or `null` if not found.<br>
1080
+ Returns attribute value or `null` if not found.<br>
1081
+ If more than one matches the condition, then it returns object as [attribute()](#attribute)
1044
1082
 
1045
1083
  **Kind**: instance property of [<code>Twig</code>](#Twig)
1046
1084
  **Returns**: <code>string</code> \| <code>number</code> \| <code>object</code> - - The value of the attribute or `null` if the does not exist
@@ -1083,7 +1121,10 @@ Retrieve or update XML attribute. For update, the condition must be a string, i.
1083
1121
 
1084
1122
  **Example**
1085
1123
  ```js
1086
- attribute((name, val) => { return name === 'age' && val > 50})
1124
+ attribute((name, val) => { return name === 'age' && val > 50})
1125
+ attribute((name) => { return ['firstName', 'lastName'].includes(name) })
1126
+ attribute('firstName')
1127
+ attribute(/name/i)
1087
1128
  ```
1088
1129
  <a name="Twig+deleteAttribute"></a>
1089
1130
 
@@ -1126,6 +1167,28 @@ All children, optionally matching `condition` of the current element or empty ar
1126
1167
  | --- | --- | --- |
1127
1168
  | condition | [<code>ElementCondition</code>](#ElementCondition) | Optional condition |
1128
1169
 
1170
+ <a name="Twig+firstChild"></a>
1171
+
1172
+ ### twig.firstChild ⇒ [<code>Twig</code>](#Twig)
1173
+ The first matching child, optionally matching `condition` of the current element or null
1174
+
1175
+ **Kind**: instance property of [<code>Twig</code>](#Twig)
1176
+
1177
+ | Param | Type | Description |
1178
+ | --- | --- | --- |
1179
+ | condition | [<code>ElementCondition</code>](#ElementCondition) | Optional condition |
1180
+
1181
+ <a name="Twig+lastChild"></a>
1182
+
1183
+ ### twig.lastChild ⇒ [<code>Twig</code>](#Twig)
1184
+ The last matching child, optionally matching `condition` of the current element or null
1185
+
1186
+ **Kind**: instance property of [<code>Twig</code>](#Twig)
1187
+
1188
+ | Param | Type | Description |
1189
+ | --- | --- | --- |
1190
+ | condition | [<code>ElementCondition</code>](#ElementCondition) | Optional condition |
1191
+
1129
1192
  <a name="Twig+next"></a>
1130
1193
 
1131
1194
  ### twig.next ⇒ [<code>Twig</code>](#Twig)
@@ -1346,7 +1409,8 @@ Deletes the current element from tree, same as `purge()`. The root object cannot
1346
1409
  <a name="Twig+setRoot"></a>
1347
1410
 
1348
1411
  ### twig.setRoot(name) ℗
1349
- Sets the name of root element. In some cases the root is created before the XML-Root element is available<br>
1412
+ Sets the name of root element. In some cases the root is created before the XML-Root element is available<br>
1413
+ Used internally!
1350
1414
 
1351
1415
  **Kind**: instance method of [<code>Twig</code>](#Twig)
1352
1416
  **Access**: private
@@ -1446,7 +1510,7 @@ Generic error for unsupported condition
1446
1510
 
1447
1511
  ## SAX
1448
1512
  **Kind**: global constant
1449
- **Version:**: 1.7.12
1513
+ **Version:**: 1.9.0
1450
1514
  **Author:**: Wernfried Domscheit
1451
1515
  **Copyright:**: Copyright (c) 2025 Wernfried Domscheit. All rights reserved.
1452
1516
  **Website:**: https://www.npmjs.com/package/xml-twig
@@ -1529,7 +1593,9 @@ Optional settings for the Twig parser
1529
1593
  <a name="TwigHandler"></a>
1530
1594
 
1531
1595
  ## TwigHandler
1532
- Reference to handler functions for Twig objects.<br>
1596
+ Reference to handler functions for Twig objects.<br>
1597
+ Element can be specified as string, Regular Expression, custom function, `Twig.Root` or `Twig.Any`<br>
1598
+ You can specify a `function` or a `event` name
1533
1599
 
1534
1600
  **Kind**: global typedef
1535
1601
  **Properties**
@@ -1543,7 +1609,13 @@ Reference to handler functions for Twig objects.<br>
1543
1609
  <a name="HandlerCondition"></a>
1544
1610
 
1545
1611
  ## HandlerCondition : <code>string</code> \| <code>Array.&lt;string&gt;</code> \| <code>RegExp</code> \| [<code>HandlerConditionFilter</code>](#HandlerConditionFilter) \| [<code>Root</code>](#Root) \| [<code>Any</code>](#Any)
1546
- Condition to specify when handler shall be called<br>
1547
- If `string` then the element name must be equal to the string
1548
- If `string[]` then the element name must be included in string array
1549
- If `RegExp` then the element name must match the Regular Expression
1550
- If [HandlerConditionFilter](#HandlerConditionFilter) then function must return `true`
1551
- Use `Twig.Root` to call the handler on root element, i.e. when the end of document is reached
1552
- Use `Twig.Any` to call the handler on every element
1612
+ Condition to specify when handler shall be called<br>
1613
+ - If `string` then the element name must be equal to the string
1614
+ - If `string[]` then the element name must be included in string array
1615
+ - If `RegExp` then the element name must match the Regular Expression
1616
+ - If [HandlerConditionFilter](#HandlerConditionFilter) then function must return `true`
1617
+ - Use `Twig.Root` to call the handler on root element, i.e. when the end of document is reached
1618
+ - Use `Twig.Any` to call the handler on every element
1553
1619
 
1554
1620
  **Kind**: global typedef
1555
1621
  <a name="HandlerFunction"></a>
@@ -1572,7 +1644,12 @@ Custom filter function to specify when handler shall be called
1572
1644
  <a name="ElementCondition"></a>
1573
1645
 
1574
1646
  ## ElementCondition : <code>string</code> \| <code>RegExp</code> \| [<code>ElementConditionFilter</code>](#ElementConditionFilter) \| [<code>Twig</code>](#Twig) \| <code>undefined</code>
1575
- Optional condition to get elements<br>
1576
- If `undefined`, then all elements are returned.<br>
1577
- If `string` then the element name must be equal to the string
1578
- If `RegExp` then the element name must match the Regular Expression
1579
- If [ElementConditionFilter](#ElementConditionFilter) then function must return `true`
1580
- Use [Twig](#Twig) object to find a specific element
1647
+ Optional condition to get elements<br>
1648
+ - If `undefined`, then all elements are returned.<br>
1649
+ - If `string` then the element name must be equal to the string
1650
+ - If `RegExp` then the element name must match the Regular Expression
1651
+ - If [ElementConditionFilter](#ElementConditionFilter) then function must return `true`
1652
+ - Use [Twig](#Twig) object to find a specific element
1581
1653
 
1582
1654
  **Kind**: global typedef
1583
1655
  <a name="ElementConditionFilter"></a>
@@ -1606,7 +1683,11 @@ Custom filter function to select desired elements
1606
1683
  <a name="AttributeCondition"></a>
1607
1684
 
1608
1685
  ## AttributeCondition : <code>string</code> \| <code>RegExp</code> \| [<code>AttributeConditionFilter</code>](#AttributeConditionFilter)
1609
- Optional condition to get attributes<br>
1610
- If `undefined`, then all attributes are returned.<br>
1611
- If `string` then the attribute name must be equal to the string
1612
- If `RegExp` then the attribute name must match the Regular Expression
1613
- If [AttributeConditionFilter](#AttributeConditionFilter) then the attribute must filter function
1686
+ Optional condition to get attributes<br>
1687
+ - If `undefined`, then all attributes are returned.<br>
1688
+ - If `string` then the attribute name must be equal to the string
1689
+ - If `RegExp` then the attribute name must match the Regular Expression
1690
+ - If [AttributeConditionFilter](#AttributeConditionFilter) then the attribute must filter function
1614
1691
 
1615
1692
  **Kind**: global typedef
1616
1693
  <a name="AttributeConditionFilter"></a>
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  },
6
6
  "name": "xml-twig",
7
7
  "description": "Node module for processing huge XML documents in tree mode",
8
- "version": "1.7.13",
8
+ "version": "1.9.0",
9
9
  "main": "twig.js",
10
10
  "directories": {
11
11
  "doc": "doc"
@@ -15,16 +15,15 @@
15
15
  "doc/*.md"
16
16
  ],
17
17
  "devDependencies": {
18
- "jsdoc-to-markdown": "^9.0.0",
19
- "luxon": "^3.5.0",
18
+ "jsdoc-to-markdown": "^9.1.1",
19
+ "luxon": "^3.6.1",
20
20
  "node-expat": "^2.4.1"
21
21
  },
22
22
  "scripts": {
23
23
  "test": "node demo.js",
24
- "preversion": "jsdoc2md --private twig.js > doc/twig.md",
25
- "postversion": "sed -i -e \"s/@version: .*/@version: %npm_package_version%/\" twig.js",
26
- "prepack": "unix2dos twig.js doc/twig.md",
27
- "prepare": "git commit -a -m \"Updated doc and version\""
24
+ "postversion": "sed -bi -e \"s/@version: .*/@version: %npm_package_version%/\" twig.js",
25
+ "prepare": "jsdoc2md --EOL win32 --private twig.js > doc/twig.md",
26
+ "postpack": "git commit -a -m \"Updated doc and version\""
28
27
  },
29
28
  "repository": {
30
29
  "type": "git",
package/twig.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @version: 1.7.13
2
+ * @version: 1.9.0
3
3
  * @author: Wernfried Domscheit
4
4
  * @copyright: Copyright (c) 2025 Wernfried Domscheit. All rights reserved.
5
5
  * @website: https://www.npmjs.com/package/xml-twig
@@ -958,6 +958,26 @@ class Twig {
958
958
  return this.filterElements(this.#children, condition);
959
959
  };
960
960
 
961
+ /**
962
+ * The first matching child, optionally matching `condition` of the current element or null
963
+ * @param {ElementCondition} condition - Optional condition
964
+ * @returns {?Twig}
965
+ */
966
+ firstChild = function (condition) {
967
+ let _children = this.children(condition);
968
+ return _children.length == 0 ? null : _children[0];
969
+ };
970
+
971
+ /**
972
+ * The last matching child, optionally matching `condition` of the current element or null
973
+ * @param {ElementCondition} condition - Optional condition
974
+ * @returns {?Twig}
975
+ */
976
+ lastChild = function (condition) {
977
+ let _children = this.children(condition);
978
+ return _children.length == 0 ? null : _children[_children.length - 1];
979
+ };
980
+
961
981
  /**
962
982
  * Returns the next matching element.
963
983
  * @param {ElementCondition} condition - Optional condition