@eksml/xml 0.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.
Files changed (63) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +588 -0
  3. package/dist/converters/fromLossless.d.mts +14 -0
  4. package/dist/converters/fromLossless.d.mts.map +1 -0
  5. package/dist/converters/fromLossless.mjs +35 -0
  6. package/dist/converters/fromLossless.mjs.map +1 -0
  7. package/dist/converters/fromLossy.d.mts +18 -0
  8. package/dist/converters/fromLossy.d.mts.map +1 -0
  9. package/dist/converters/fromLossy.mjs +91 -0
  10. package/dist/converters/fromLossy.mjs.map +1 -0
  11. package/dist/converters/lossless.d.mts +39 -0
  12. package/dist/converters/lossless.d.mts.map +1 -0
  13. package/dist/converters/lossless.mjs +74 -0
  14. package/dist/converters/lossless.mjs.map +1 -0
  15. package/dist/converters/lossy.d.mts +42 -0
  16. package/dist/converters/lossy.d.mts.map +1 -0
  17. package/dist/converters/lossy.mjs +158 -0
  18. package/dist/converters/lossy.mjs.map +1 -0
  19. package/dist/htmlConstants-D6fsKbZ-.mjs +30 -0
  20. package/dist/htmlConstants-D6fsKbZ-.mjs.map +1 -0
  21. package/dist/parser-BfdEfWDg.d.mts +95 -0
  22. package/dist/parser-BfdEfWDg.d.mts.map +1 -0
  23. package/dist/parser-CYq309aR.mjs +479 -0
  24. package/dist/parser-CYq309aR.mjs.map +1 -0
  25. package/dist/parser.d.mts +2 -0
  26. package/dist/parser.mjs +2 -0
  27. package/dist/sax.d.mts +64 -0
  28. package/dist/sax.d.mts.map +1 -0
  29. package/dist/sax.mjs +70 -0
  30. package/dist/sax.mjs.map +1 -0
  31. package/dist/saxEngine-BDnD7ruG.mjs +750 -0
  32. package/dist/saxEngine-BDnD7ruG.mjs.map +1 -0
  33. package/dist/utilities/index.d.mts +88 -0
  34. package/dist/utilities/index.d.mts.map +1 -0
  35. package/dist/utilities/index.mjs +87 -0
  36. package/dist/utilities/index.mjs.map +1 -0
  37. package/dist/writer.d.mts +58 -0
  38. package/dist/writer.d.mts.map +1 -0
  39. package/dist/writer.mjs +357 -0
  40. package/dist/writer.mjs.map +1 -0
  41. package/dist/xmlParseStream.d.mts +138 -0
  42. package/dist/xmlParseStream.d.mts.map +1 -0
  43. package/dist/xmlParseStream.mjs +313 -0
  44. package/dist/xmlParseStream.mjs.map +1 -0
  45. package/package.json +100 -0
  46. package/src/converters/fromLossless.ts +80 -0
  47. package/src/converters/fromLossy.ts +180 -0
  48. package/src/converters/lossless.ts +116 -0
  49. package/src/converters/lossy.ts +274 -0
  50. package/src/parser.ts +728 -0
  51. package/src/sax.ts +157 -0
  52. package/src/saxEngine.ts +1157 -0
  53. package/src/utilities/escapeRegExp.ts +19 -0
  54. package/src/utilities/filter.ts +63 -0
  55. package/src/utilities/getElementById.ts +21 -0
  56. package/src/utilities/getElementsByClassName.ts +22 -0
  57. package/src/utilities/htmlConstants.ts +26 -0
  58. package/src/utilities/index.ts +7 -0
  59. package/src/utilities/isElementNode.ts +19 -0
  60. package/src/utilities/isTextNode.ts +19 -0
  61. package/src/utilities/toContentString.ts +23 -0
  62. package/src/writer.ts +650 -0
  63. package/src/xmlParseStream.ts +597 -0
package/src/sax.ts ADDED
@@ -0,0 +1,157 @@
1
+ /**
2
+ * createSaxParser — EventEmitter-style SAX parser built on the internal SAX engine.
3
+ *
4
+ * Provides dynamic `.on()` / `.off()` handler management on top of the core
5
+ * SAX tokenizer.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { createSaxParser } from '@eksml/xml/sax';
10
+ *
11
+ * const parser = createSaxParser({ html: true });
12
+ *
13
+ * parser.on('openTag', (tagName, attrs) => console.log(tagName, attrs));
14
+ * parser.on('text', (text) => console.log(text));
15
+ *
16
+ * parser.write('<div><br><p>Hello</p></div>');
17
+ * parser.close();
18
+ * ```
19
+ */
20
+
21
+ import type { SaxEngineHandlers, Attributes } from '#src/saxEngine.ts';
22
+ import { saxEngine } from '#src/saxEngine.ts';
23
+ import {
24
+ HTML_VOID_ELEMENTS,
25
+ HTML_RAW_CONTENT_TAGS,
26
+ } from '#src/utilities/htmlConstants.ts';
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // SAX event types
30
+ // ---------------------------------------------------------------------------
31
+
32
+ /** Event name → handler signature map for the SAX parser. */
33
+ export interface SaxEventMap {
34
+ openTag: (tagName: string, attributes: Attributes) => void;
35
+ closeTag: (tagName: string) => void;
36
+ text: (text: string) => void;
37
+ cdata: (data: string) => void;
38
+ comment: (comment: string) => void;
39
+ processingInstruction: (name: string, body: string) => void;
40
+ doctype: (tagName: string, attributes: Attributes) => void;
41
+ }
42
+
43
+ /** SAX event names. */
44
+ export type SaxEventName = keyof SaxEventMap;
45
+
46
+ /** EventEmitter-style SAX parser returned by `createSaxParser()`. */
47
+ export interface SaxParser {
48
+ /** Register an event handler. */
49
+ on<E extends SaxEventName>(event: E, handler: SaxEventMap[E]): void;
50
+ /** Remove an event handler. */
51
+ off<E extends SaxEventName>(event: E, handler: SaxEventMap[E]): void;
52
+ /** Feed a chunk of XML to the parser. */
53
+ write(chunk: string): void;
54
+ /** Signal end-of-input and flush any remaining buffered data. */
55
+ close(): void;
56
+ }
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // SAX parser options
60
+ // ---------------------------------------------------------------------------
61
+
62
+ /** Options for `createSaxParser()`. */
63
+ export interface SaxParserOptions {
64
+ /**
65
+ * Enable HTML mode. Sets `selfClosingTags` and `rawContentTags` to their
66
+ * HTML defaults unless explicitly provided.
67
+ */
68
+ html?: boolean;
69
+ /** Tag names that are self-closing (void). Defaults to HTML voids when `html: true`. */
70
+ selfClosingTags?: string[];
71
+ /** Tag names whose content is raw text. Defaults to `['script', 'style']` when `html: true`. */
72
+ rawContentTags?: string[];
73
+ }
74
+
75
+ // ---------------------------------------------------------------------------
76
+ // createSaxParser
77
+ // ---------------------------------------------------------------------------
78
+
79
+ /**
80
+ * Create an EventEmitter-style SAX parser.
81
+ *
82
+ * Uses the internal SAX engine, but wraps it with `.on()` / `.off()` methods
83
+ * so handlers can be added and removed dynamically.
84
+ *
85
+ * @param options - Parser options (html mode, selfClosingTags, rawContentTags).
86
+ * @returns A SaxParser with `.on()`, `.off()`, `.write()`, `.close()`.
87
+ */
88
+ export function createSaxParser(options?: SaxParserOptions): SaxParser {
89
+ const opts = options ?? {};
90
+
91
+ // Resolve HTML-mode defaults (same logic as parse() and XmlParseStream)
92
+ const isHtml = opts.html === true;
93
+ const selfClosingTags =
94
+ opts.selfClosingTags ?? (isHtml ? HTML_VOID_ELEMENTS : []);
95
+ const rawContentTags =
96
+ opts.rawContentTags ?? (isHtml ? HTML_RAW_CONTENT_TAGS : []);
97
+
98
+ // Listener sets — one Set per event type
99
+ const listeners: {
100
+ [E in SaxEventName]: Set<SaxEventMap[E]>;
101
+ } = {
102
+ openTag: new Set(),
103
+ closeTag: new Set(),
104
+ text: new Set(),
105
+ cdata: new Set(),
106
+ comment: new Set(),
107
+ processingInstruction: new Set(),
108
+ doctype: new Set(),
109
+ };
110
+
111
+ // Bridge handlers: call all registered listeners for each event
112
+ const handlers: SaxEngineHandlers = {
113
+ onOpenTag(tagName: string, attributes: Attributes) {
114
+ for (const handler of listeners.openTag) handler(tagName, attributes);
115
+ },
116
+ onCloseTag(tagName: string) {
117
+ for (const handler of listeners.closeTag) handler(tagName);
118
+ },
119
+ onText(text: string) {
120
+ for (const handler of listeners.text) handler(text);
121
+ },
122
+ onCdata(data: string) {
123
+ for (const handler of listeners.cdata) handler(data);
124
+ },
125
+ onComment(comment: string) {
126
+ for (const handler of listeners.comment) handler(comment);
127
+ },
128
+ onProcessingInstruction(name: string, body: string) {
129
+ for (const handler of listeners.processingInstruction)
130
+ handler(name, body);
131
+ },
132
+ onDoctype(tagName: string, attributes: Attributes) {
133
+ for (const handler of listeners.doctype) handler(tagName, attributes);
134
+ },
135
+ };
136
+
137
+ const parser = saxEngine({
138
+ ...handlers,
139
+ selfClosingTags,
140
+ rawContentTags,
141
+ });
142
+
143
+ return {
144
+ on<E extends SaxEventName>(event: E, handler: SaxEventMap[E]): void {
145
+ listeners[event].add(handler);
146
+ },
147
+ off<E extends SaxEventName>(event: E, handler: SaxEventMap[E]): void {
148
+ listeners[event].delete(handler);
149
+ },
150
+ write(chunk: string): void {
151
+ parser.write(chunk);
152
+ },
153
+ close(): void {
154
+ parser.close();
155
+ },
156
+ };
157
+ }