@thi.ng/sax 2.2.61 → 2.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 (4) hide show
  1. package/README.md +53 -41
  2. package/index.d.ts +36 -0
  3. package/index.js +28 -6
  4. package/package.json +6 -6
package/README.md CHANGED
@@ -35,7 +35,7 @@
35
35
  [@thi.ng/transducers](https://github.com/thi-ng/umbrella/tree/develop/packages/transducers)-based,
36
36
  [SAX](https://en.wikipedia.org/wiki/Simple_API_for_XML)-like,
37
37
  non-validating, [configurable](#parser-options), speedy & tiny XML
38
- parser (~1.8KB gzipped).
38
+ parser (~1.4KB brotli).
39
39
 
40
40
  Unlike the classic event-driven approach of SAX, this parser is
41
41
  implemented as a transducer function, transforming an XML input into a
@@ -88,7 +88,7 @@ For Node.js REPL:
88
88
  const sax = await import("@thi.ng/sax");
89
89
  ```
90
90
 
91
- Package sizes (brotli'd, pre-treeshake): ESM: 1.41 KB
91
+ Package sizes (brotli'd, pre-treeshake): ESM: 1.65 KB
92
92
 
93
93
  ## Dependencies
94
94
 
@@ -132,12 +132,12 @@ src=`<?xml version="1.0" encoding="utf-8"?>
132
132
  <b2 foo="bar" />
133
133
  </a>`
134
134
 
135
- // sax.parse() returns a transducer
136
- doc = [...tx.iterator(sax.parse(), src)]
137
-
138
- // ...or returns iterator if input is given
135
+ // sax.parse() returns iterator if input is given
139
136
  doc = [...sax.parse(src)]
140
137
 
138
+ // ...or returns a transducer to be used with thi.ng/transducers
139
+ doc = [...tx.iterator(sax.parse(), src)]
140
+
141
141
  // (see description of `type` values and parse options further below)
142
142
 
143
143
  // [ { type: 0,
@@ -188,11 +188,8 @@ first `<g>` element is complete. This is because the `matchFirst()`
188
188
  transducer will cause early termination once that element has been
189
189
  processed.
190
190
 
191
- ```ts
192
- import { parse, Type } from "@thi.ng/sax";
193
- import * as tx from "@thi.ng/transducers";
194
-
195
- svg=`
191
+ ```ts id:svgdoc
192
+ const svg = `
196
193
  <?xml version="1.0"?>
197
194
  <svg version="1.1" height="300" width="300" xmlns="http://www.w3.org/2000/svg">
198
195
  <g fill="yellow">
@@ -206,27 +203,37 @@ svg=`
206
203
  <circle cx="150.00" cy="150.00" r="25.00" />
207
204
  </g>
208
205
  </svg>`;
206
+ ```
209
207
 
210
- [...tx.iterator(
211
- tx.comp(
212
- // transform into parse events (see parser options below)
213
- parse({ children: true }),
214
- // match 1st group end
215
- tx.matchFirst((e) => e.type == Type.ELEM_END && e.tag == "g"),
216
- // extract group's children
217
- tx.mapcat((e) => e.children),
218
- // select circles only
219
- tx.filter((e) => e.tag == "circle"),
220
- // transform attributes
221
- tx.map((e)=> [e.tag, {
222
- ...e.attribs,
223
- cx: parseFloat(e.attribs.cx),
224
- cy: parseFloat(e.attribs.cy),
225
- r: parseFloat(e.attribs.r),
226
- }])
227
- ),
228
- svg
229
- )]
208
+ ```ts tangle:export/readme-parse-svg.ts
209
+ import { parse, Type } from "@thi.ng/sax";
210
+ import * as tx from "@thi.ng/transducers";
211
+
212
+ // using the SVG example doc defined above
213
+ <<svgdoc>>
214
+
215
+ console.log(
216
+ [...tx.iterator(
217
+ tx.comp(
218
+ // transform into parse events (see parser options below)
219
+ parse({ children: true }),
220
+ // match 1st group end
221
+ tx.matchFirst((e) => e.type == Type.ELEM_END && e.tag == "g"),
222
+ // extract group's children
223
+ tx.mapcat((e) => e.children),
224
+ // select circles only
225
+ tx.filter((e) => e.tag == "circle"),
226
+ // transform attributes
227
+ tx.map((e)=> [e.tag, {
228
+ ...e.attribs,
229
+ cx: parseFloat(e.attribs.cx),
230
+ cy: parseFloat(e.attribs.cy),
231
+ r: parseFloat(e.attribs.r),
232
+ }])
233
+ ),
234
+ svg
235
+ )]
236
+ );
230
237
  // [ [ 'circle', { cx: 50, cy: 150, r: 50 } ],
231
238
  // [ 'circle', { cx: 250, cy: 150, r: 50 } ],
232
239
  // [ 'circle', { cx: 150, cy: 150, fill: 'rgba(0,255,255,0.25)', r: 100, stroke: '#ff0000' } ] ]
@@ -238,22 +245,25 @@ This example shows how SVG can be parsed into
238
245
  [@thi.ng/hiccup](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup)
239
246
  format.
240
247
 
241
- ```ts
248
+ ```ts tangle:export/readme-parse-elements.ts
242
249
  import { defmulti, DEFAULT } from "@thi.ng/defmulti";
243
- import { parse } from "@thi.ng/sax";
250
+ import { parse, type ParseElement } from "@thi.ng/sax";
244
251
  import * as tx from "@thi.ng/transducers";
245
252
 
253
+ // using the SVG example doc defined above
254
+ <<svgdoc>>
255
+
246
256
  // coerces given attribute IDs into numeric values and
247
257
  // keeps all other attribs
248
- const numericAttribs = (e, ...ids: string[]) =>
258
+ const numericAttribs = (e: ParseElement, ...ids: string[]) =>
249
259
  ids.reduce(
250
260
  (acc, id) => (acc[id] = parseFloat(e.attribs[id]), acc),
251
- { ...e.attribs }
261
+ <Record<string,any>>{ ...e.attribs }
252
262
  );
253
263
 
254
264
  // returns iterator of parsed & filtered children of given element
255
265
  // (iterator is used to avoid extraneous copying at call sites)
256
- const parsedChildren = (e) =>
266
+ const parsedChildren = (e: ParseElement) =>
257
267
  tx.iterator(
258
268
  tx.comp(
259
269
  tx.map(parseElement),
@@ -281,11 +291,13 @@ parseElement.add("svg", (e) =>
281
291
  // implementation for unhandled elements
282
292
  parseElement.add(DEFAULT, () => null);
283
293
 
284
- // using the same SVG source as in previous example:
294
+ // finally parse & transform results:
285
295
  // the `last()` reducer just returns the ultimate value
286
296
  // which in this case is the SVG root element's ELEM_END parse event
287
297
  // this also contains all children (by default)
288
- parseElement(tx.transduce(parse(), tx.last(), svg));
298
+ console.log(
299
+ parseElement(tx.transduce(parse(), tx.last(), svg))
300
+ );
289
301
 
290
302
  // ["svg",
291
303
  // {
@@ -327,14 +339,14 @@ If the parser encounters a syntax error, an error event value incl. a
327
339
  description and input position will be produced (but no JS error will be
328
340
  thrown) and the entire transducer pipeline stopped.
329
341
 
330
- ```ts
342
+ ```ts tangle:export/readme-error-handling.ts
331
343
  import { parse } from "@thi.ng/sax";
332
344
  import { iterator } from "@thi.ng/transducers";
333
345
 
334
- [...iterator(parse(), `a`)]
346
+ console.log([...iterator(parse(), `a`)]);
335
347
  // [ { type: 7, body: 'unexpected char: \'a\' @ pos 1' } ]
336
348
 
337
- [...iterator(parse(), `<a><b></c></a>`)]
349
+ console.log([...iterator(parse(), `<a><b></c></a>`)]);
338
350
  // [ { type: 4, tag: 'a', attribs: {} },
339
351
  // { type: 4, tag: 'b', attribs: {} },
340
352
  // { type: 7, body: 'unmatched tag: c @ pos 7' } ]
package/index.d.ts CHANGED
@@ -84,4 +84,40 @@ export declare enum Type {
84
84
  export declare function parse(opts?: Partial<ParseOpts>): Transducer<string, ParseEvent>;
85
85
  export declare function parse(src: string): IterableIterator<ParseEvent>;
86
86
  export declare function parse(opts: Partial<ParseOpts>, src: string): IterableIterator<ParseEvent>;
87
+ /**
88
+ * Convenience helper for certain applications of {@link parse} to produce a
89
+ * tree of the parsed document.
90
+ *
91
+ * @remarks
92
+ * This functions always enables the {@link ParseOpts.children} option, only
93
+ * keeps {@link Type.ELEM_END} events and only returns the last one, i.e that of
94
+ * the root element (including all children).
95
+ *
96
+ * Also see {@link parseAsHiccup}.
97
+ *
98
+ * @param src
99
+ * @param opts
100
+ */
101
+ export declare const parseAsTree: (src: string, opts?: Partial<ParseOpts>) => ParseEvent;
102
+ /**
103
+ * Convenience helper for certain applications of {@link parse} to produce a
104
+ * tree of the parsed document in thi.ng/hiccup format.
105
+ *
106
+ * @remarks
107
+ * Uses {@link parseAsTree} and then transforms result.
108
+ *
109
+ * @example
110
+ * ```ts tangle:../export/parse-as-hiccup.ts
111
+ * import { parseAsHiccup } from "@thi.ng/sax";
112
+ *
113
+ * const doc = `<svg><g id="foo"><circle x="0" y="0" r="100"/></g></svg>`;
114
+ *
115
+ * console.log(parseAsHiccup(doc));
116
+ * // ["svg", {}, ["g", { id: "foo" }, ["circle", { x: "0", y: "0", r: "100" }]]]
117
+ * ```
118
+ *
119
+ * @param src
120
+ * @param opts
121
+ */
122
+ export declare const parseAsHiccup: (src: string, opts?: Partial<ParseOpts>) => [string, IObjectOf<string>, ...any[]];
87
123
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,6 +1,10 @@
1
1
  import { NO_OP } from "@thi.ng/api/api";
2
2
  import { unescapeEntities } from "@thi.ng/strings/entities";
3
3
  import { ESCAPES } from "@thi.ng/strings/escape";
4
+ import { transduce } from "@thi.ng/transducers/transduce";
5
+ import { comp } from "@thi.ng/transducers/comp";
6
+ import { filter } from "@thi.ng/transducers/filter";
7
+ import { last } from "@thi.ng/transducers/last";
4
8
  import { fsm } from "@thi.ng/transducers-fsm";
5
9
  import { __iter, iterator } from "@thi.ng/transducers/iterator";
6
10
  const VOID_TAGS = new Set(
@@ -61,6 +65,20 @@ function parse(...args) {
61
65
  terminate: 1 /* ERROR */
62
66
  });
63
67
  }
68
+ const parseAsTree = (src, opts) => transduce(
69
+ comp(
70
+ parse({ ...opts, children: true }),
71
+ filter((x) => x.type === 5 /* ELEM_END */)
72
+ ),
73
+ last(),
74
+ src
75
+ );
76
+ const parseAsHiccup = (src, opts) => __toHiccup(parseAsTree(src, opts));
77
+ const __toHiccup = (root) => [
78
+ root.tag,
79
+ root.attribs ?? {},
80
+ ...root.children?.length ? root.children.map(__toHiccup) : root.body ? [root.body] : []
81
+ ];
64
82
  const __isWS = (x) => {
65
83
  const c = x.charCodeAt(0);
66
84
  return c === 32 || // space
@@ -70,12 +88,14 @@ const __isWS = (x) => {
70
88
  };
71
89
  const __isTagChar = (x) => {
72
90
  const c = x.charCodeAt(0);
73
- return c >= 65 && c <= 90 || // A-Z
74
- c >= 97 && c <= 122 || // a-z
91
+ return c >= 97 && c <= 122 || // a-z
75
92
  c >= 48 && c <= 57 || // 0-9
76
- c == 45 || // -
77
- c == 95 || // _
78
- c == 58;
93
+ c >= 65 && c <= 90 || // A-Z
94
+ c === 58 || // :
95
+ c === 45 || // -
96
+ c === 95 || // _
97
+ c === 46 || // .
98
+ c === 183 || c >= 192 && c <= 214 || c >= 248 && c <= 767 || c >= 768 && c <= 879 || c >= 880 && c <= 893 || c >= 895 && c <= 8191 || c >= 895 && c <= 8191 || c === 8204 || c === 8205 || c === 8255 || c === 8256 || c >= 8304 && c <= 8591 || c >= 11264 && c <= 12271 || c >= 12289 && c <= 55295 || c >= 63744 && c <= 64975 || c >= 65008 && c <= 65533;
79
99
  };
80
100
  const __error = (s, body) => {
81
101
  s.state = 1 /* ERROR */;
@@ -411,5 +431,7 @@ const __beginElementBody = (state) => {
411
431
  export {
412
432
  Type,
413
433
  VOID_TAGS,
414
- parse
434
+ parse,
435
+ parseAsHiccup,
436
+ parseAsTree
415
437
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/sax",
3
- "version": "2.2.61",
3
+ "version": "2.3.0",
4
4
  "description": "Transducer-based, SAX-like, non-validating, speedy & tiny XML parser",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -39,10 +39,10 @@
39
39
  "tool:tangle": "../../node_modules/.bin/tangle src/**/*.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@thi.ng/api": "^8.12.6",
43
- "@thi.ng/strings": "^3.9.26",
44
- "@thi.ng/transducers": "^9.6.14",
45
- "@thi.ng/transducers-fsm": "^2.2.147"
42
+ "@thi.ng/api": "^8.12.7",
43
+ "@thi.ng/strings": "^3.9.28",
44
+ "@thi.ng/transducers": "^9.6.16",
45
+ "@thi.ng/transducers-fsm": "^2.2.149"
46
46
  },
47
47
  "devDependencies": {
48
48
  "esbuild": "^0.25.11",
@@ -82,5 +82,5 @@
82
82
  ],
83
83
  "year": 2018
84
84
  },
85
- "gitHead": "136a5e5ef0b69e82329db00d806c3c4e8f1aa063\n"
85
+ "gitHead": "74b6f319d8f52c9266d97f616be5298a6a5b96e4\n"
86
86
  }