@shbernal/ts-xlsx 2.1.0 → 3.1.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/README.md +4 -1
- package/dist/bytes.d.ts +7 -0
- package/dist/bytes.js +22 -0
- package/dist/core/address.d.ts +60 -2
- package/dist/core/address.js +40 -24
- package/dist/core/autofilter.d.ts +5 -4
- package/dist/core/autofilter.js +34 -35
- package/dist/core/axis-handle.d.ts +10 -0
- package/dist/core/axis-handle.js +17 -0
- package/dist/core/cell.d.ts +25 -0
- package/dist/core/cell.js +9 -3
- package/dist/core/clone.d.ts +29 -0
- package/dist/core/clone.js +24 -0
- package/dist/core/color-resolution.js +2 -4
- package/dist/core/column.d.ts +5 -1
- package/dist/core/column.js +34 -35
- package/dist/core/comment-thread.js +2 -1
- package/dist/core/conditional-formatting-overlay.d.ts +2 -1
- package/dist/core/conditional-formatting-overlay.js +2 -2
- package/dist/core/conditional-formatting.d.ts +14 -6
- package/dist/core/conditional-formatting.js +45 -43
- package/dist/core/containers.d.ts +12 -0
- package/dist/core/containers.js +5 -0
- package/dist/core/data-validation-overlay.d.ts +2 -1
- package/dist/core/data-validation-overlay.js +4 -4
- package/dist/core/data-validation.d.ts +9 -3
- package/dist/core/data-validation.js +21 -18
- package/dist/core/date-format.d.ts +20 -0
- package/dist/core/date-format.js +160 -0
- package/dist/core/date.d.ts +43 -8
- package/dist/core/date.js +18 -2
- package/dist/core/formula.d.ts +26 -0
- package/dist/core/formula.js +20 -4
- package/dist/core/grid-edits.d.ts +2 -3
- package/dist/core/grid-edits.js +69 -70
- package/dist/core/grid-shift.d.ts +58 -9
- package/dist/core/grid-shift.js +26 -4
- package/dist/core/image.d.ts +14 -10
- package/dist/core/image.js +9 -9
- package/dist/core/internal.d.ts +8 -2
- package/dist/core/limits.d.ts +23 -0
- package/dist/core/limits.js +4 -0
- package/dist/core/merge-index.d.ts +31 -0
- package/dist/core/merge-index.js +60 -0
- package/dist/core/merge.d.ts +5 -14
- package/dist/core/merge.js +18 -27
- package/dist/core/page-setup.d.ts +72 -2
- package/dist/core/page-setup.js +34 -8
- package/dist/core/pivot-table.d.ts +1 -1
- package/dist/core/pivot-table.js +67 -53
- package/dist/core/protection.js +2 -11
- package/dist/core/range.js +34 -30
- package/dist/core/row-input.js +2 -2
- package/dist/core/row.d.ts +5 -1
- package/dist/core/row.js +22 -23
- package/dist/core/style.d.ts +134 -75
- package/dist/core/style.js +112 -53
- package/dist/core/table-style.d.ts +1 -1
- package/dist/core/table-style.js +4 -4
- package/dist/core/table.d.ts +28 -10
- package/dist/core/table.js +54 -39
- package/dist/core/theme.js +6 -5
- package/dist/core/used-extent.d.ts +44 -0
- package/dist/core/used-extent.js +102 -0
- package/dist/core/value.d.ts +9 -0
- package/dist/core/value.js +36 -34
- package/dist/core/workbook-media.d.ts +34 -0
- package/dist/core/workbook-media.js +44 -0
- package/dist/core/workbook-styles.d.ts +67 -1
- package/dist/core/workbook-vba.js +3 -2
- package/dist/core/workbook.d.ts +23 -2
- package/dist/core/workbook.js +24 -40
- package/dist/core/worksheet-comments.d.ts +2 -1
- package/dist/core/worksheet-comments.js +20 -13
- package/dist/core/worksheet-merges.d.ts +44 -0
- package/dist/core/worksheet-merges.js +59 -0
- package/dist/core/worksheet-model.d.ts +9 -1
- package/dist/core/worksheet-model.js +24 -9
- package/dist/core/worksheet.d.ts +72 -12
- package/dist/core/worksheet.js +68 -70
- package/dist/customui/ribbon.js +6 -7
- package/dist/entries/core.d.ts +18 -13
- package/dist/entries/core.js +12 -6
- package/dist/entries/xlsx.d.ts +1 -1
- package/dist/errors.d.ts +64 -0
- package/dist/errors.js +22 -0
- package/dist/hex.d.ts +8 -0
- package/dist/hex.js +3 -0
- package/dist/io/csv/delimiter.d.ts +9 -0
- package/dist/io/csv/delimiter.js +6 -0
- package/dist/io/csv/read.js +11 -4
- package/dist/io/csv/write.d.ts +9 -2
- package/dist/io/csv/write.js +9 -38
- package/dist/io/opc/inflate.js +15 -1
- package/dist/io/opc/namespaces.d.ts +16 -0
- package/dist/io/opc/namespaces.js +4 -0
- package/dist/io/opc/part-paths.d.ts +10 -0
- package/dist/io/opc/part-paths.js +16 -1
- package/dist/io/opc/read-opc.d.ts +69 -10
- package/dist/io/opc/read-opc.js +49 -55
- package/dist/io/opc/read-options.d.ts +5 -1
- package/dist/io/opc/rels.d.ts +1 -6
- package/dist/io/opc/rels.js +1 -4
- package/dist/io/opc/sniff-format.d.ts +3 -3
- package/dist/io/opc/sniff-format.js +2 -2
- package/dist/io/style/xf-style.d.ts +48 -2
- package/dist/io/style/xf-style.js +14 -0
- package/dist/io/xlsb/formula.d.ts +2 -2
- package/dist/io/xlsb/formula.js +49 -34
- package/dist/io/xlsb/primitives.js +8 -14
- package/dist/io/xlsb/ptg-functions.d.ts +19 -10
- package/dist/io/xlsb/ptg-functions.js +385 -596
- package/dist/io/xlsb/read-styles.js +50 -83
- package/dist/io/xlsb/read-worksheet.d.ts +20 -1
- package/dist/io/xlsb/read-worksheet.js +73 -89
- package/dist/io/xlsb/read.d.ts +6 -3
- package/dist/io/xlsb/read.js +47 -29
- package/dist/io/xlsb/record-stream.d.ts +30 -0
- package/dist/io/xlsb/record-stream.js +20 -0
- package/dist/io/xlsb/record-types.d.ts +1 -5
- package/dist/io/xlsb/record-types.js +1 -5
- package/dist/io/xlsx/cell-accumulator.d.ts +9 -2
- package/dist/io/xlsx/cell-accumulator.js +32 -9
- package/dist/io/xlsx/cell-style-resolution.d.ts +25 -0
- package/dist/io/xlsx/cell-style-resolution.js +29 -0
- package/dist/io/xlsx/cell-value.d.ts +5 -3
- package/dist/io/xlsx/cell-value.js +13 -15
- package/dist/io/xlsx/color-xml.js +3 -2
- package/dist/io/xlsx/column-budget.d.ts +26 -0
- package/dist/io/xlsx/column-budget.js +22 -0
- package/dist/io/xlsx/comments.d.ts +6 -2
- package/dist/io/xlsx/comments.js +36 -31
- package/dist/io/xlsx/conditional-formatting.d.ts +9 -5
- package/dist/io/xlsx/conditional-formatting.js +51 -46
- package/dist/io/xlsx/data-validation.d.ts +9 -7
- package/dist/io/xlsx/data-validation.js +38 -35
- package/dist/io/xlsx/edit-vba.d.ts +7 -2
- package/dist/io/xlsx/edit-vba.js +15 -29
- package/dist/io/xlsx/font-xml.d.ts +7 -0
- package/dist/io/xlsx/font-xml.js +104 -0
- package/dist/io/xlsx/hyperlinks.d.ts +41 -7
- package/dist/io/xlsx/hyperlinks.js +40 -28
- package/dist/io/xlsx/images.js +24 -25
- package/dist/io/xlsx/namespaces.d.ts +16 -0
- package/dist/io/xlsx/namespaces.js +7 -0
- package/dist/io/xlsx/package-plan.d.ts +23 -3
- package/dist/io/xlsx/package-plan.js +66 -25
- package/dist/io/xlsx/part-names.d.ts +41 -0
- package/dist/io/xlsx/part-names.js +46 -0
- package/dist/io/xlsx/pivot.js +8 -7
- package/dist/io/xlsx/read-parts.d.ts +59 -0
- package/dist/io/xlsx/read-parts.js +252 -0
- package/dist/io/xlsx/read-pivot.js +3 -1
- package/dist/io/xlsx/read-repair.d.ts +26 -0
- package/dist/io/xlsx/read-repair.js +39 -0
- package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +1 -1
- package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +7 -3
- package/dist/io/xlsx/read-rows.d.ts +18 -5
- package/dist/io/xlsx/read-rows.js +50 -37
- package/dist/io/xlsx/read-shared-strings.js +5 -3
- package/dist/io/xlsx/read-styles.d.ts +1 -6
- package/dist/io/xlsx/read-styles.js +65 -109
- package/dist/io/xlsx/read-workbook-xml.d.ts +36 -0
- package/dist/io/xlsx/read-workbook-xml.js +170 -0
- package/dist/io/xlsx/read-worksheet.d.ts +11 -1
- package/dist/io/xlsx/read-worksheet.js +51 -193
- package/dist/io/xlsx/read.d.ts +5 -13
- package/dist/io/xlsx/read.js +111 -436
- package/dist/io/xlsx/rich-text.js +1 -1
- package/dist/io/xlsx/row-position.d.ts +18 -0
- package/dist/io/xlsx/row-position.js +11 -0
- package/dist/io/xlsx/row-xml.d.ts +128 -0
- package/dist/io/xlsx/row-xml.js +203 -0
- package/dist/io/xlsx/shared-formulas.js +2 -2
- package/dist/io/xlsx/sheet-properties.d.ts +10 -1
- package/dist/io/xlsx/sheet-properties.js +166 -37
- package/dist/io/xlsx/style-elements.d.ts +43 -0
- package/dist/io/xlsx/style-elements.js +156 -0
- package/dist/io/xlsx/styles.d.ts +12 -3
- package/dist/io/xlsx/styles.js +5 -204
- package/dist/io/xlsx/tables.js +11 -14
- package/dist/io/xlsx/theme-xml.d.ts +26 -4
- package/dist/io/xlsx/theme-xml.js +107 -69
- package/dist/io/xlsx/threaded-comments.d.ts +8 -10
- package/dist/io/xlsx/threaded-comments.js +3 -1
- package/dist/io/xlsx/workbook-xml.d.ts +54 -4
- package/dist/io/xlsx/workbook-xml.js +84 -57
- package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
- package/dist/io/xlsx/worksheet-xml.js +59 -242
- package/dist/io/xlsx/write-stream.d.ts +71 -16
- package/dist/io/xlsx/write-stream.js +79 -41
- package/dist/io/xlsx/write.d.ts +6 -2
- package/dist/io/xlsx/write.js +217 -153
- package/dist/rel-type.d.ts +16 -0
- package/dist/rel-type.js +6 -0
- package/dist/token-set.d.ts +28 -0
- package/dist/token-set.js +7 -0
- package/dist/vba/bytes.d.ts +14 -5
- package/dist/vba/bytes.js +16 -6
- package/dist/vba/cfb-format.d.ts +32 -0
- package/dist/vba/cfb-format.js +13 -0
- package/dist/vba/cfb-writer.d.ts +9 -0
- package/dist/vba/cfb-writer.js +28 -25
- package/dist/vba/cfb.d.ts +13 -1
- package/dist/vba/cfb.js +73 -57
- package/dist/vba/codepage.js +2 -1
- package/dist/vba/errors.d.ts +7 -0
- package/dist/vba/ms-ovba.js +79 -23
- package/dist/vba/project-editor.js +35 -40
- package/dist/vba/project.d.ts +11 -1
- package/dist/vba/project.js +40 -21
- package/dist/vba/vba-encoding.js +5 -6
- package/dist/xml/xml-attrs.d.ts +37 -0
- package/dist/xml/xml-attrs.js +30 -0
- package/dist/xml/xml-chars.d.ts +40 -0
- package/dist/xml/xml-chars.js +12 -0
- package/dist/xml/xml-namespaces.d.ts +31 -0
- package/dist/xml/xml-namespaces.js +50 -0
- package/dist/xml/xml-read.d.ts +128 -81
- package/dist/xml/xml-read.js +161 -178
- package/dist/xml/xml-scan.d.ts +78 -0
- package/dist/xml/xml-scan.js +180 -0
- package/dist/xml/xml.d.ts +29 -28
- package/dist/xml/xml.js +24 -24
- package/package.json +13 -9
package/dist/xml/xml-read.d.ts
CHANGED
|
@@ -1,75 +1,112 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
}
|
|
1
|
+
import { NamespaceScope } from './xml-namespaces.ts';
|
|
2
|
+
import { type XmlAttributes, type XmlEvent } from './xml-scan.ts';
|
|
4
3
|
export interface SaxHandlers {
|
|
5
|
-
/**
|
|
6
|
-
|
|
4
|
+
/**
|
|
5
|
+
* An element start. `selfClosing` is true for `<x/>`; no matching {@link onClose} fires for it.
|
|
6
|
+
*
|
|
7
|
+
* `scope` carries the namespace bindings in force here, for the handful of readings whose identity
|
|
8
|
+
* is a namespace rather than a prefix: an `r:id` under whatever prefix the file bound the
|
|
9
|
+
* relationships namespace to, or telling an extension element from a main-namespace one in a file
|
|
10
|
+
* that prefixes the main namespace. Most handlers match on {@link localName} and ignore it.
|
|
11
|
+
*/
|
|
12
|
+
onOpen(name: string, attrs: XmlAttributes, selfClosing: boolean, scope: NamespaceScope): void;
|
|
7
13
|
/** A run of character data (already entity-decoded; CDATA delivered verbatim). Omit to ignore text. */
|
|
8
14
|
onText?(text: string): void;
|
|
9
15
|
/** An element end (`</x>`); the synthetic end of a self-closing element is *not* reported here.
|
|
10
|
-
* Omit to ignore closes.
|
|
11
|
-
|
|
16
|
+
* Omit to ignore closes. `scope` is the element's own bindings, still in force: it is popped after
|
|
17
|
+
* this returns, so a close handler resolves the same namespaces its open handler did. */
|
|
18
|
+
onClose?(name: string, scope: NamespaceScope): void;
|
|
12
19
|
}
|
|
13
20
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
21
|
+
* What {@link elementSubtrees} is to capture: for each container element's local name, the local name
|
|
22
|
+
* of the children to take verbatim inside it (`'dxfs' -> 'dxf'`). Scoping the child to a container is
|
|
23
|
+
* what keeps a `<color>` in `<mruColors>` from being confused with the many other `<color>` elements
|
|
24
|
+
* a stylesheet carries.
|
|
18
25
|
*/
|
|
19
|
-
export type
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
readonly
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
readonly text: string;
|
|
27
|
-
} | {
|
|
28
|
-
readonly kind: 'close';
|
|
29
|
-
readonly name: string;
|
|
30
|
-
};
|
|
26
|
+
export type SubtreeSelection = ReadonlyMap<string, string>;
|
|
27
|
+
/** What {@link elementSubtrees} captured: the verbatim source of each selected child, keyed by its
|
|
28
|
+
* container's local name, and each container's own attributes as written. */
|
|
29
|
+
export interface SubtreeCapture {
|
|
30
|
+
readonly fragments: ReadonlyMap<string, readonly string[]>;
|
|
31
|
+
readonly attributes: ReadonlyMap<string, XmlAttributes>;
|
|
32
|
+
}
|
|
31
33
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
34
|
+
* Capture the verbatim source text of selected elements, in one scan.
|
|
35
|
+
*
|
|
36
|
+
* Some content is re-emitted byte for byte rather than modelled: a differential style, a custom
|
|
37
|
+
* indexed palette, an author's recent-colour swatches, a table-style definition. Preserving the raw
|
|
38
|
+
* text is what keeps a foreign `<dxf>`'s number format a real format code across a re-write instead
|
|
39
|
+
* of a coerced `"[object Object]"`, so the reader needs a subtree's *source*, which an event stream
|
|
40
|
+
* by definition cannot hand back.
|
|
41
|
+
*
|
|
42
|
+
* That gap is why four callers each grew a `<container>([\s\S]*?)</container>` scanner of their own,
|
|
43
|
+
* which is a regular expression parsing XML, over untrusted input, in the same directory as the
|
|
44
|
+
* reader written specifically to avoid that (ADR-0004). This is the same capability done properly:
|
|
45
|
+
* one linear scan with comments, CDATA, processing instructions and declarations skipped as markup
|
|
46
|
+
* rather than matched as text, and the nesting depth counted so a same-named descendant does not end
|
|
47
|
+
* a capture early.
|
|
48
|
+
*
|
|
49
|
+
* Only a container's *first* occurrence is read, matching the single block these documents declare;
|
|
50
|
+
* a second is ignored rather than merged. A captured element that never closes throws
|
|
51
|
+
* {@link XmlParseError}, like the reader's other truncation cases: a partial subtree re-emitted
|
|
52
|
+
* verbatim is broken markup handed on as though it were content.
|
|
36
53
|
*/
|
|
37
|
-
export declare function
|
|
54
|
+
export declare function elementSubtrees(source: string, selection: SubtreeSelection): SubtreeCapture;
|
|
38
55
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* **One left-to-right pass, and that is load-bearing.** `005F` maps to `_` like any other code
|
|
46
|
-
* point, with no special case, because a single pass already gives the underscore escape its
|
|
47
|
-
* meaning: in `_x005F_x0041_` the match at 0 yields `_` and scanning resumes at `x0041_`, which has
|
|
48
|
-
* no leading underscore left to start an escape. So the value reads back as the literal seven
|
|
49
|
-
* characters `_x0041_` the author wrote. Decoding `_x005F_` in a pass of its own, before or after
|
|
50
|
-
* the rest, collapses that to `A` and loses the distinction the encoder went to trouble to keep.
|
|
51
|
-
* Excel agrees: it reads that cell as `_x0041_`.
|
|
52
|
-
*
|
|
53
|
-
* The decode is unconditional, not a repair of characters XML cannot carry. Excel reads
|
|
54
|
-
* `a_x0009_b` as a tab even though a literal tab would have been perfectly legal there, so a
|
|
55
|
-
* decoder that only handled the illegal range would disagree with Excel on files Excel wrote.
|
|
56
|
+
* Where an element sits in the source: the offsets an editor splices at, and the parsed attributes of
|
|
57
|
+
* its opening tag.
|
|
58
|
+
*
|
|
59
|
+
* `contentStart`/`contentEnd` bound the element's children; for an empty element they are equal and
|
|
60
|
+
* both sit just past the `/>`, so replacing that range turns `<latin/>` into `<latin>…</latin>` and
|
|
61
|
+
* a caller that means to replace the *element* uses `start`/`end` instead.
|
|
56
62
|
*/
|
|
57
|
-
export
|
|
63
|
+
export interface ElementRange {
|
|
64
|
+
/** Offset of the element's `<`. */
|
|
65
|
+
readonly start: number;
|
|
66
|
+
/** One past the element's final `>`. */
|
|
67
|
+
readonly end: number;
|
|
68
|
+
/** One past the opening tag's `>`. */
|
|
69
|
+
readonly contentStart: number;
|
|
70
|
+
/** Offset of the closing tag's `<`, or `end` for an empty element. */
|
|
71
|
+
readonly contentEnd: number;
|
|
72
|
+
/** The name as written, namespace prefix included. */
|
|
73
|
+
readonly name: string;
|
|
74
|
+
readonly attrs: XmlAttributes;
|
|
75
|
+
}
|
|
58
76
|
/**
|
|
59
|
-
*
|
|
60
|
-
* recursion. This is the parser's core; {@link parseXml} is a thin push adapter over it. A
|
|
61
|
-
* consumer that must produce output incrementally (the streaming row reader) pulls events and
|
|
62
|
-
* yields as it goes, holding only its own running state; a push callback cannot.
|
|
77
|
+
* Locate the first element reachable by a path of local names, as offsets into the source.
|
|
63
78
|
*
|
|
64
|
-
*
|
|
79
|
+
* The primitive for *editing* a part rather than reading one: everything outside the returned range
|
|
80
|
+
* is spliced through byte for byte, so an edit changes what it names and nothing else -- the
|
|
81
|
+
* whitespace, the comments, the attribute order, the prefix the source chose. That is a guarantee no
|
|
82
|
+
* re-serialisation can make, and it is why this returns offsets instead of text.
|
|
83
|
+
*
|
|
84
|
+
* The alternative it replaces is a `<container>[\s\S]*?</container>` regular expression, which is a
|
|
85
|
+
* regular expression parsing XML over untrusted input: it terminates on a `</container>` inside a
|
|
86
|
+
* comment or a CDATA section, it cannot see an element written `<x></x>` where it expected `<x/>`,
|
|
87
|
+
* and its container name is interpolated into a pattern where an NCName's legal `.` and `-` are
|
|
88
|
+
* metacharacters. This scan classifies markup with {@link markupAt}, so a comment is skipped rather
|
|
89
|
+
* than matched, and compares parsed names rather than raw text.
|
|
90
|
+
*
|
|
91
|
+
* Each step of `path` matches at any depth below the previous one, and the *first* match wins, which
|
|
92
|
+
* is the single block these documents declare. An element that never closes throws
|
|
93
|
+
* {@link XmlParseError}, on the same grounds as the other truncation cases here: an unterminated
|
|
94
|
+
* range is not a range.
|
|
65
95
|
*/
|
|
66
|
-
export declare function
|
|
96
|
+
export declare function elementRange(source: string, path: readonly string[]): ElementRange | undefined;
|
|
67
97
|
/** An element start surfaced by {@link openElements}: its qualified `name`, the namespace-stripped
|
|
68
98
|
* `local` name the filter matched on, and its already-decoded `attrs`. */
|
|
69
99
|
export interface OpenElement {
|
|
70
100
|
readonly name: string;
|
|
71
101
|
readonly local: string;
|
|
72
102
|
readonly attrs: XmlAttributes;
|
|
103
|
+
/**
|
|
104
|
+
* The namespace bindings in force at this element, for the attributes and elements whose identity
|
|
105
|
+
* is a namespace rather than a prefix (`r:id`, the x14 extension elements). One shared, mutating
|
|
106
|
+
* instance rather than a snapshot: it is valid while this element is the current one, which is the
|
|
107
|
+
* whole of a `for..of` body, and copying it per element would cost every scan for the few that ask.
|
|
108
|
+
*/
|
|
109
|
+
readonly scope: NamespaceScope;
|
|
73
110
|
}
|
|
74
111
|
/**
|
|
75
112
|
* Yield each element start in `source` as an {@link OpenElement}, optionally restricted to the given
|
|
@@ -101,41 +138,41 @@ export interface ParseXmlOptions {
|
|
|
101
138
|
*/
|
|
102
139
|
readonly closeEmptyElements?: ReadonlySet<string>;
|
|
103
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* One reader's share of a parse: the handlers it wants the events delivered to, and the self-closing
|
|
143
|
+
* elements it needs expanded. Named separately from {@link ParseXmlOptions} because several readers
|
|
144
|
+
* of the same part run over a single parse of it, and each has to bring its own requirements rather
|
|
145
|
+
* than have the caller remember them; see {@link parseXmlPasses}.
|
|
146
|
+
*/
|
|
147
|
+
export interface SaxPass {
|
|
148
|
+
readonly handlers: SaxHandlers;
|
|
149
|
+
/** As {@link ParseXmlOptions.closeEmptyElements}, for this reader's elements. */
|
|
150
|
+
readonly closeEmptyElements?: ReadonlySet<string>;
|
|
151
|
+
}
|
|
152
|
+
/** A {@link SaxPass} that gathers something during the parse rather than committing as it goes. */
|
|
153
|
+
export interface CollectingPass<T> extends SaxPass {
|
|
154
|
+
/** What the pass collected. Meaningful only once the parse driving it has finished. */
|
|
155
|
+
result(): T;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Parse `source` once, delivering every event to each pass in turn. The alternative, a parse per
|
|
159
|
+
* reader, costs a full scan of the document per reader and finds nothing in most of them: the
|
|
160
|
+
* worksheet part is the largest in a package, and reading it five times over spent 45% of a large
|
|
161
|
+
* file's read on four scans that matched no element.
|
|
162
|
+
*
|
|
163
|
+
* The expansions the passes ask for are unioned, so *every* pass sees `<x/>` as an open plus a close
|
|
164
|
+
* for any name *any* of them named. That is the one way a pass can observe that it is sharing a
|
|
165
|
+
* parse, and it is why the option is a set of element names rather than a flag: a pass sees an extra
|
|
166
|
+
* close only for elements another pass had to name, and reaching a close for an element a reader
|
|
167
|
+
* does not handle is already the ordinary case.
|
|
168
|
+
*/
|
|
169
|
+
export declare function parseXmlPasses(source: string, passes: readonly SaxPass[]): void;
|
|
104
170
|
/**
|
|
105
171
|
* Parse an XML document, dispatching SAX events to `handlers`. A thin push adapter over
|
|
106
172
|
* {@link xmlEvents}: one scanning core serves both the callback and the pull consumers.
|
|
107
173
|
* Throws {@link XmlParseError} on malformed markup.
|
|
108
174
|
*/
|
|
109
175
|
export declare function parseXml(source: string, handlers: SaxHandlers, options?: ParseXmlOptions): void;
|
|
110
|
-
/** Strip a namespace prefix from a qualified name (`r:id` → `id`, `sheet` → `sheet`). */
|
|
111
|
-
export declare function localName(qualified: string): string;
|
|
112
|
-
/** An OOXML boolean that is on when present with no value (`<b/>` is bold) and off only on an
|
|
113
|
-
* explicit `"0"`/`"false"`; absence reads as on. */
|
|
114
|
-
export declare function boolPresent(val: string | undefined): boolean;
|
|
115
|
-
/** An OOXML boolean that is on only when explicitly `"1"`/`"true"`; anything else, including
|
|
116
|
-
* absence and a truthy-looking `"0"`, is off. */
|
|
117
|
-
export declare function boolStrict(val: string | undefined): boolean;
|
|
118
|
-
/** An optional OOXML boolean: `undefined` when the attribute is absent or carries an unrecognised
|
|
119
|
-
* token, otherwise its `"1"`/`"true"` vs `"0"`/`"false"` value. Lets a caller store only the
|
|
120
|
-
* attributes the source actually carried, so a re-write stays byte-clean. */
|
|
121
|
-
export declare function boolTristate(val: string | undefined): boolean | undefined;
|
|
122
|
-
/** An OOXML integer attribute at or above `min` (default: unbounded below); `undefined` when the
|
|
123
|
-
* attribute is absent, blank, fractional, not a number, or below the floor. Integers past
|
|
124
|
-
* `Number.MAX_SAFE_INTEGER` read as `undefined` too: no index or count is usable out there, and
|
|
125
|
-
* arithmetic on one silently lies. */
|
|
126
|
-
export declare function numInteger(val: string | undefined, min?: number): number | undefined;
|
|
127
|
-
/** An OOXML decimal attribute at or above `min` (default: unbounded below); `undefined` when the
|
|
128
|
-
* attribute is absent, blank, not a number, or below the floor. Infinities are not finite numbers
|
|
129
|
-
* and read as `undefined`. */
|
|
130
|
-
export declare function numFinite(val: string | undefined, min?: number): number | undefined;
|
|
131
|
-
/** Read an operand's text as a number only when it is a canonical decimal literal (optional sign,
|
|
132
|
-
* digits, optional fraction). A cell reference, defined name, expression, or exotically-spelled
|
|
133
|
-
* number (`1E5`, hex) keeps its verbatim text, so it is neither coerced to `NaN` and lost nor
|
|
134
|
-
* re-spelled into a number that would not re-write byte-clean. Callers layer their own type rules
|
|
135
|
-
* (a data-validation `list`/`custom` operand stays a string regardless of what it looks like). */
|
|
136
|
-
export declare function coerceNumericLiteral(text: string): string | number;
|
|
137
|
-
/** Narrow an enumerated attribute through its guard; `undefined` when absent or not a member. */
|
|
138
|
-
export declare function enumToken<T extends string>(val: string | undefined, isMember: (candidate: string) => candidate is T): T | undefined;
|
|
139
176
|
/**
|
|
140
177
|
* Gathers one element's character data across the open/text/close events a SAX parse delivers it in.
|
|
141
178
|
*
|
|
@@ -169,6 +206,16 @@ export declare class TextCapture {
|
|
|
169
206
|
text(chunk: string): void;
|
|
170
207
|
/** The gathered text when `local` closes the captured element, else `undefined`. Unlatches. */
|
|
171
208
|
close(local: string): string | undefined;
|
|
209
|
+
/**
|
|
210
|
+
* Abandon any capture in progress, for a caller whose own container has just ended or begun.
|
|
211
|
+
*
|
|
212
|
+
* {@link close} unlatches only for the element that opened the capture, which is the right answer
|
|
213
|
+
* within one element but not across a boundary the caller owns: markup that opens a `<t>` and is
|
|
214
|
+
* then truncated leaves the capture armed, and the next container's text lands in the abandoned
|
|
215
|
+
* buffer instead of where that container wanted it. A caller that resets its own per-container
|
|
216
|
+
* state resets this with it.
|
|
217
|
+
*/
|
|
218
|
+
reset(): void;
|
|
172
219
|
}
|
|
173
220
|
/**
|
|
174
221
|
* Yield each named element's text as that element closes, as `{local, text}`.
|
package/dist/xml/xml-read.js
CHANGED
|
@@ -1,147 +1,154 @@
|
|
|
1
1
|
import { XmlParseError } from './errors.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
gt: '>',
|
|
6
|
-
quot: '"',
|
|
7
|
-
apos: "'",
|
|
8
|
-
};
|
|
9
|
-
const ENTITY = /&(#x[0-9a-fA-F]+|#[0-9]+|[a-zA-Z][a-zA-Z0-9]*);/g;
|
|
10
|
-
export function decodeEntities(value) {
|
|
11
|
-
if (!value.includes('&'))
|
|
12
|
-
return value;
|
|
13
|
-
return value.replace(ENTITY, (match, body) => {
|
|
14
|
-
if (body.charCodeAt(0) === 0x23) {
|
|
15
|
-
const codePoint = body.charCodeAt(1) === 0x78
|
|
16
|
-
? parseInt(body.slice(2), 16)
|
|
17
|
-
: parseInt(body.slice(1), 10);
|
|
18
|
-
if (!Number.isInteger(codePoint) || codePoint < 0 || codePoint > 0x10ffff)
|
|
19
|
-
return match;
|
|
20
|
-
try {
|
|
21
|
-
return String.fromCodePoint(codePoint);
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
return match;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
const named = PREDEFINED_ENTITIES[body];
|
|
28
|
-
return named ?? match;
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
export function decodeSpreadsheetText(value) {
|
|
32
|
-
if (!value.includes('_'))
|
|
33
|
-
return value;
|
|
34
|
-
return value.replace(/_x([0-9A-Fa-f]{4})_/g, (_match, hex) => String.fromCharCode(parseInt(hex, 16)));
|
|
35
|
-
}
|
|
36
|
-
const ATTRIBUTE = /([^\s=/>]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
|
|
37
|
-
function parseAttributes(source) {
|
|
38
|
-
const attrs = {};
|
|
39
|
-
ATTRIBUTE.lastIndex = 0;
|
|
40
|
-
let match = ATTRIBUTE.exec(source);
|
|
41
|
-
while (match !== null) {
|
|
42
|
-
const value = match[2] ?? match[3] ?? '';
|
|
43
|
-
attrs[match[1]] = decodeEntities(value);
|
|
44
|
-
match = ATTRIBUTE.exec(source);
|
|
45
|
-
}
|
|
46
|
-
return attrs;
|
|
47
|
-
}
|
|
48
|
-
function findTagEnd(source, start) {
|
|
49
|
-
let quote = '';
|
|
50
|
-
for (let i = start + 1; i < source.length; i++) {
|
|
51
|
-
const ch = source[i];
|
|
52
|
-
if (quote !== '') {
|
|
53
|
-
if (ch === quote)
|
|
54
|
-
quote = '';
|
|
55
|
-
}
|
|
56
|
-
else if (ch === '"' || ch === "'") {
|
|
57
|
-
quote = ch;
|
|
58
|
-
}
|
|
59
|
-
else if (ch === '>') {
|
|
60
|
-
return i;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
throw new XmlParseError('unterminated tag: missing ">"');
|
|
64
|
-
}
|
|
65
|
-
function skipDeclaration(source, start) {
|
|
66
|
-
let depth = 0;
|
|
67
|
-
for (let i = start; i < source.length; i++) {
|
|
68
|
-
const ch = source[i];
|
|
69
|
-
if (ch === '[')
|
|
70
|
-
depth++;
|
|
71
|
-
else if (ch === ']')
|
|
72
|
-
depth--;
|
|
73
|
-
else if (ch === '>' && depth <= 0)
|
|
74
|
-
return i + 1;
|
|
75
|
-
}
|
|
76
|
-
throw new XmlParseError('unterminated markup declaration: missing ">"');
|
|
77
|
-
}
|
|
78
|
-
export function* xmlEvents(source) {
|
|
2
|
+
import { NamespaceScope } from './xml-namespaces.js';
|
|
3
|
+
import { localName, markupAt, parseAttributes, tagAt, xmlEvents, } from './xml-scan.js';
|
|
4
|
+
function* rawTags(source) {
|
|
79
5
|
const length = source.length;
|
|
80
6
|
let i = 0;
|
|
81
7
|
while (i < length) {
|
|
82
8
|
const lt = source.indexOf('<', i);
|
|
83
|
-
if (lt === -1)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
if (lt > i) {
|
|
90
|
-
const chunk = source.slice(i, lt);
|
|
91
|
-
if (chunk.length > 0)
|
|
92
|
-
yield { kind: 'text', text: decodeEntities(normalizeLineEndings(chunk)) };
|
|
93
|
-
}
|
|
94
|
-
if (source.startsWith('<!--', lt)) {
|
|
95
|
-
const end = source.indexOf('-->', lt + 4);
|
|
96
|
-
if (end === -1)
|
|
97
|
-
throw new XmlParseError('unterminated comment');
|
|
98
|
-
i = end + 3;
|
|
9
|
+
if (lt === -1)
|
|
10
|
+
break;
|
|
11
|
+
const markup = markupAt(source, lt);
|
|
12
|
+
if (markup !== undefined) {
|
|
13
|
+
i = markup.next;
|
|
99
14
|
continue;
|
|
100
15
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
16
|
+
const tag = tagAt(source, lt);
|
|
17
|
+
i = tag.next;
|
|
18
|
+
yield { lt, tag, local: localName(tag.name) };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function elementSubtrees(source, selection) {
|
|
22
|
+
const fragments = new Map();
|
|
23
|
+
const attributes = new Map();
|
|
24
|
+
const finished = new Set();
|
|
25
|
+
let container;
|
|
26
|
+
let capture;
|
|
27
|
+
for (const { lt, tag, local } of rawTags(source)) {
|
|
28
|
+
if (tag.close) {
|
|
29
|
+
if (capture !== undefined && local === capture.local) {
|
|
30
|
+
if (capture.depth > 0)
|
|
31
|
+
capture.depth -= 1;
|
|
32
|
+
else {
|
|
33
|
+
fragments.get(container?.local ?? '')?.push(source.slice(capture.start, tag.next));
|
|
34
|
+
capture = undefined;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else if (capture === undefined && container !== undefined && local === container.local) {
|
|
38
|
+
if (container.depth > 0)
|
|
39
|
+
container.depth -= 1;
|
|
40
|
+
else {
|
|
41
|
+
finished.add(container.local);
|
|
42
|
+
container = undefined;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
107
45
|
continue;
|
|
108
46
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
i = end + 2;
|
|
114
|
-
continue;
|
|
47
|
+
const { selfClosing } = tag;
|
|
48
|
+
if (capture !== undefined) {
|
|
49
|
+
if (!selfClosing && local === capture.local)
|
|
50
|
+
capture.depth += 1;
|
|
115
51
|
}
|
|
116
|
-
if (
|
|
117
|
-
|
|
52
|
+
else if (container !== undefined) {
|
|
53
|
+
if (!selfClosing && local === container.local)
|
|
54
|
+
container.depth += 1;
|
|
55
|
+
else if (local === container.child) {
|
|
56
|
+
if (selfClosing)
|
|
57
|
+
fragments.get(container.local)?.push(source.slice(lt, tag.next));
|
|
58
|
+
else
|
|
59
|
+
capture = { local, start: lt, depth: 0 };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const child = selection.get(local);
|
|
64
|
+
if (child !== undefined && !finished.has(local)) {
|
|
65
|
+
attributes.set(local, parseAttributes(tag.attrSource));
|
|
66
|
+
if (selfClosing)
|
|
67
|
+
finished.add(local);
|
|
68
|
+
else {
|
|
69
|
+
fragments.set(local, fragments.get(local) ?? []);
|
|
70
|
+
container = { local, child, depth: 0 };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (capture !== undefined) {
|
|
76
|
+
throw new XmlParseError(`unterminated <${capture.local}> element`);
|
|
77
|
+
}
|
|
78
|
+
return { fragments, attributes };
|
|
79
|
+
}
|
|
80
|
+
export function elementRange(source, path) {
|
|
81
|
+
const open = [];
|
|
82
|
+
let pending;
|
|
83
|
+
for (const { lt, tag, local } of rawTags(source)) {
|
|
84
|
+
if (tag.close) {
|
|
85
|
+
const innermost = open.at(-1);
|
|
86
|
+
if (innermost !== undefined && innermost.local === local) {
|
|
87
|
+
if (innermost.depth > 0)
|
|
88
|
+
innermost.depth -= 1;
|
|
89
|
+
else {
|
|
90
|
+
open.pop();
|
|
91
|
+
if (pending !== undefined && open.length === path.length - 1) {
|
|
92
|
+
return { ...pending, end: tag.next, contentEnd: lt };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
118
96
|
continue;
|
|
119
97
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
98
|
+
if (pending === undefined && local === path[open.length]) {
|
|
99
|
+
const last = open.length === path.length - 1;
|
|
100
|
+
if (last) {
|
|
101
|
+
if (tag.selfClosing) {
|
|
102
|
+
return {
|
|
103
|
+
start: lt,
|
|
104
|
+
end: tag.next,
|
|
105
|
+
contentStart: tag.next,
|
|
106
|
+
contentEnd: tag.next,
|
|
107
|
+
name: tag.name,
|
|
108
|
+
attrs: parseAttributes(tag.attrSource),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
pending = {
|
|
112
|
+
start: lt,
|
|
113
|
+
contentStart: tag.next,
|
|
114
|
+
name: tag.name,
|
|
115
|
+
attrs: parseAttributes(tag.attrSource),
|
|
116
|
+
};
|
|
117
|
+
open.push({ local, depth: 0 });
|
|
118
|
+
}
|
|
119
|
+
else if (!tag.selfClosing) {
|
|
120
|
+
open.push({ local, depth: 0 });
|
|
121
|
+
}
|
|
124
122
|
}
|
|
125
123
|
else {
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const attrs = nameEnd === -1 ? {} : parseAttributes(body.slice(nameEnd));
|
|
131
|
-
yield { kind: 'open', name, attrs, selfClosing };
|
|
124
|
+
const innermost = open.at(-1);
|
|
125
|
+
if (innermost !== undefined && !tag.selfClosing && innermost.local === local) {
|
|
126
|
+
innermost.depth += 1;
|
|
127
|
+
}
|
|
132
128
|
}
|
|
133
|
-
i = gt + 1;
|
|
134
129
|
}
|
|
130
|
+
if (pending !== undefined) {
|
|
131
|
+
throw new XmlParseError(`unterminated <${pending.name}> element`);
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
135
134
|
}
|
|
136
135
|
export function* openElements(source, ...localNames) {
|
|
137
136
|
const filter = localNames.length > 0 ? new Set(localNames) : undefined;
|
|
137
|
+
const scope = new NamespaceScope();
|
|
138
138
|
for (const event of xmlEvents(source)) {
|
|
139
|
+
if (event.kind === 'close') {
|
|
140
|
+
scope.close();
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
139
143
|
if (event.kind !== 'open')
|
|
140
144
|
continue;
|
|
145
|
+
scope.open(event.attrs);
|
|
141
146
|
const local = localName(event.name);
|
|
142
|
-
if (filter
|
|
143
|
-
|
|
144
|
-
|
|
147
|
+
if (filter === undefined || filter.has(local)) {
|
|
148
|
+
yield { name: event.name, local, attrs: event.attrs, scope };
|
|
149
|
+
}
|
|
150
|
+
if (event.selfClosing)
|
|
151
|
+
scope.close();
|
|
145
152
|
}
|
|
146
153
|
}
|
|
147
154
|
export function* closeEmptyElements(events, names) {
|
|
@@ -155,79 +162,51 @@ export function* closeEmptyElements(events, names) {
|
|
|
155
162
|
}
|
|
156
163
|
}
|
|
157
164
|
}
|
|
165
|
+
export function parseXmlPasses(source, passes) {
|
|
166
|
+
const expanded = new Set();
|
|
167
|
+
for (const pass of passes) {
|
|
168
|
+
for (const name of pass.closeEmptyElements ?? [])
|
|
169
|
+
expanded.add(name);
|
|
170
|
+
}
|
|
171
|
+
const handlers = passes.map((pass) => pass.handlers);
|
|
172
|
+
parseXml(source, {
|
|
173
|
+
onOpen(name, attrs, selfClosing, scope) {
|
|
174
|
+
for (const handler of handlers)
|
|
175
|
+
handler.onOpen(name, attrs, selfClosing, scope);
|
|
176
|
+
},
|
|
177
|
+
onText(text) {
|
|
178
|
+
for (const handler of handlers)
|
|
179
|
+
handler.onText?.(text);
|
|
180
|
+
},
|
|
181
|
+
onClose(name, scope) {
|
|
182
|
+
for (const handler of handlers)
|
|
183
|
+
handler.onClose?.(name, scope);
|
|
184
|
+
},
|
|
185
|
+
}, expanded.size > 0 ? { closeEmptyElements: expanded } : undefined);
|
|
186
|
+
}
|
|
158
187
|
export function parseXml(source, handlers, options) {
|
|
159
188
|
const events = options?.closeEmptyElements
|
|
160
189
|
? closeEmptyElements(xmlEvents(source), options.closeEmptyElements)
|
|
161
190
|
: xmlEvents(source);
|
|
191
|
+
const scope = new NamespaceScope();
|
|
162
192
|
for (const event of events) {
|
|
163
193
|
switch (event.kind) {
|
|
164
194
|
case 'open':
|
|
165
|
-
|
|
195
|
+
scope.open(event.attrs);
|
|
196
|
+
handlers.onOpen(event.name, event.attrs, event.selfClosing, scope);
|
|
197
|
+
if (event.selfClosing)
|
|
198
|
+
scope.close();
|
|
166
199
|
break;
|
|
167
200
|
case 'text':
|
|
168
201
|
handlers.onText?.(event.text);
|
|
169
202
|
break;
|
|
170
203
|
case 'close':
|
|
171
|
-
handlers.onClose?.(event.name);
|
|
204
|
+
handlers.onClose?.(event.name, scope);
|
|
205
|
+
scope.close();
|
|
172
206
|
break;
|
|
173
207
|
}
|
|
174
208
|
}
|
|
175
209
|
}
|
|
176
|
-
function normalizeLineEndings(chunk) {
|
|
177
|
-
if (!chunk.includes('\r'))
|
|
178
|
-
return chunk;
|
|
179
|
-
return chunk.replace(/\r\n?/g, '\n');
|
|
180
|
-
}
|
|
181
|
-
function firstWhitespace(source) {
|
|
182
|
-
for (let i = 0; i < source.length; i++) {
|
|
183
|
-
const code = source.charCodeAt(i);
|
|
184
|
-
if (code === 0x20 || code === 0x09 || code === 0x0a || code === 0x0d)
|
|
185
|
-
return i;
|
|
186
|
-
}
|
|
187
|
-
return -1;
|
|
188
|
-
}
|
|
189
|
-
export function localName(qualified) {
|
|
190
|
-
const colon = qualified.indexOf(':');
|
|
191
|
-
return colon === -1 ? qualified : qualified.slice(colon + 1);
|
|
192
|
-
}
|
|
193
|
-
export function boolPresent(val) {
|
|
194
|
-
return val === undefined || (val !== '0' && val !== 'false');
|
|
195
|
-
}
|
|
196
|
-
export function boolStrict(val) {
|
|
197
|
-
return val === '1' || val === 'true';
|
|
198
|
-
}
|
|
199
|
-
export function boolTristate(val) {
|
|
200
|
-
if (val === '1' || val === 'true')
|
|
201
|
-
return true;
|
|
202
|
-
if (val === '0' || val === 'false')
|
|
203
|
-
return false;
|
|
204
|
-
return undefined;
|
|
205
|
-
}
|
|
206
|
-
export function numInteger(val, min = -Number.MAX_SAFE_INTEGER) {
|
|
207
|
-
const n = parseAttrNumber(val);
|
|
208
|
-
if (n === undefined || !Number.isSafeInteger(n) || n < min)
|
|
209
|
-
return undefined;
|
|
210
|
-
return n;
|
|
211
|
-
}
|
|
212
|
-
export function numFinite(val, min = -Infinity) {
|
|
213
|
-
const n = parseAttrNumber(val);
|
|
214
|
-
if (n === undefined || n < min)
|
|
215
|
-
return undefined;
|
|
216
|
-
return n;
|
|
217
|
-
}
|
|
218
|
-
function parseAttrNumber(val) {
|
|
219
|
-
if (val === undefined || val.trim() === '')
|
|
220
|
-
return undefined;
|
|
221
|
-
const n = Number(val);
|
|
222
|
-
return Number.isFinite(n) ? n : undefined;
|
|
223
|
-
}
|
|
224
|
-
export function coerceNumericLiteral(text) {
|
|
225
|
-
const trimmed = text.trim();
|
|
226
|
-
return /^-?\d+(?:\.\d+)?$/.test(trimmed) ? Number(trimmed) : text;
|
|
227
|
-
}
|
|
228
|
-
export function enumToken(val, isMember) {
|
|
229
|
-
return val !== undefined && isMember(val) ? val : undefined;
|
|
230
|
-
}
|
|
231
210
|
export class TextCapture {
|
|
232
211
|
#names;
|
|
233
212
|
#capturing;
|
|
@@ -254,6 +233,10 @@ export class TextCapture {
|
|
|
254
233
|
this.#capturing = undefined;
|
|
255
234
|
return this.#text;
|
|
256
235
|
}
|
|
236
|
+
reset() {
|
|
237
|
+
this.#capturing = undefined;
|
|
238
|
+
this.#text = '';
|
|
239
|
+
}
|
|
257
240
|
}
|
|
258
241
|
export function* capturedText(source, names) {
|
|
259
242
|
const capture = new TextCapture(names);
|