Skip to content

Commit 0316760

Browse files
authored
fix(xml)!: rename options to parse_options/stringify_options and add additional typings for each sub-options for clarity (#103)
1 parent dabe1f1 commit 0316760

File tree

6 files changed

+124
-84
lines changed

6 files changed

+124
-84
lines changed

xml/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,22 @@ console.log(stringify({
6464
- Support for custom `reviver` and `replacer` functions
6565
- Support for metadata stored into non-enumerable properties (advanced usage).
6666

67+
## 🕊️ Migrating from `6.x.x` to `7.x.x`
68+
69+
For both `stringify` and `parse`, the type `options` has been renamed and prefixed by their scope for more clarity.
70+
71+
```diff ts
72+
- import type { options } from "jsr:@libs/xml@6/stringify"
73+
+ import type { stringify_options } from "jsr:@libs/xml@7/stringify"
74+
```
75+
76+
```diff ts
77+
- import type { options } from "jsr:@libs/xml@6/parse"
78+
+ import type { parse_options } from "jsr:@libs/xml@7/parse"
79+
```
80+
81+
If you didn't use these typings, no further changes are required.
82+
6783
## 🕊️ Migrating from `5.x.x` to `6.x.x`
6884

6985
Version `6.x.x` and onwards require Deno `2.x.x` or later.

xml/_types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ export type xml_document = xml_node & {
4040
}
4141

4242
/** Synchronous reader. */
43-
export type ReaderSync = { readSync(buffer: Uint8Array): Nullable<number> }
43+
export type ReaderSync = {
44+
/** Read synchronously some data into a buffer and returns the number of bytes read. */
45+
readSync(buffer: Uint8Array): Nullable<number>
46+
}
4447

4548
/** A laxer type for what can be stringified. We won’t ever create this, but we’ll accept it. */
4649
export type stringifyable = Partial<Omit<xml_document, "@version" | "@standalone"> & { "@version": string; "@standalone": string }>

xml/mod.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export { type options as parse_options, parse } from "./parse.ts"
2-
export { cdata, comment, type options as stringify_options, stringify } from "./stringify.ts"
1+
export * from "./parse.ts"
2+
export * from "./stringify.ts"
33
export type * from "./_types.ts"

xml/parse.ts

Lines changed: 64 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,58 +6,17 @@
66
// Imports
77
import { initSync, JsReader, source, Token, tokenize } from "./wasm_xml_parser/wasm_xml_parser.js"
88
import type { Nullable, ReaderSync, xml_document, xml_node, xml_text } from "./_types.ts"
9-
export type { Nullable, ReaderSync, xml_document, xml_node, xml_text }
9+
export type * from "./_types.ts"
1010
initSync(source())
1111

1212
/** XML parser options. */
13-
export type options = {
13+
export type parse_options = {
1414
/** Remove elements from result. */
15-
clean?: {
16-
/** Remove attributes from result. */
17-
attributes?: boolean
18-
/** Remove comments from result. */
19-
comments?: boolean
20-
/** Remove XML doctype from result. */
21-
doctype?: boolean
22-
/** Remove XML processing instructions from result. */
23-
instructions?: boolean
24-
}
15+
clean?: clean_options
2516
/** Flatten result depending on node content. */
26-
flatten?: {
27-
/** If node only contains attributes values (i.e. with key starting with `@`), it'll be flattened as a regular object without `@` prefixes. */
28-
attributes?: boolean
29-
/** If node only contains a `#text` value, it'll be flattened as a string (defaults to `true`). */
30-
text?: boolean
31-
/** If node does not contains any attribute or text, it'll be flattened to `null` (defaults to `true`). */
32-
empty?: boolean
33-
}
17+
flatten?: flatten_options
3418
/** Revive result. */
35-
revive?: {
36-
/**
37-
* Trim texts (this is applied before other revivals, defaults to `true`).
38-
* It honors `xml:space="preserve"` attribute.
39-
*/
40-
trim?: boolean
41-
/**
42-
* Revive XML entities (defaults to `true`).
43-
* Automatically unescape XML entities and replace common entities with their respective characters.
44-
*/
45-
entities?: boolean
46-
/** Revive booleans (matching `/^(?:[Tt]rue|[Ff]alse)$/`).*/
47-
booleans?: boolean
48-
/**
49-
* Revive finite numbers.
50-
* Note that the version of the XML prolog is always treated as a string to avoid breaking documents.
51-
*/
52-
numbers?: boolean
53-
/**
54-
* Custom reviver (this is applied after other revivals).
55-
* When it is applied on an attribute, `key` and `value` will be given.
56-
* When it is applied on a node, both `key` and `value` will be `null`.
57-
* Return `undefined` to delete either the attribute or the tag.
58-
*/
59-
custom?: (args: { name: string; key: Nullable<string>; value: Nullable<string>; node: Readonly<xml_node> }) => unknown
60-
}
19+
revive?: revive_options
6120
/**
6221
* Parsing mode.
6322
* Using `html` is more permissive and will not throw on some invalid XML syntax.
@@ -66,6 +25,62 @@ export type options = {
6625
mode?: "xml" | "html"
6726
}
6827

28+
/** XML parser {@linkcode parse_options}`.clean` */
29+
export type clean_options = {
30+
/** Remove attributes from result. */
31+
attributes?: boolean
32+
/** Remove comments from result. */
33+
comments?: boolean
34+
/** Remove XML doctype from result. */
35+
doctype?: boolean
36+
/** Remove XML processing instructions from result. */
37+
instructions?: boolean
38+
}
39+
40+
/** XML parser {@linkcode parse_options}`.flatten` */
41+
export type flatten_options = {
42+
/** If node only contains attributes values (i.e. with key starting with `@`), it'll be flattened as a regular object without `@` prefixes. */
43+
attributes?: boolean
44+
/** If node only contains a `#text` value, it'll be flattened as a string (defaults to `true`). */
45+
text?: boolean
46+
/** If node does not contains any attribute or text, it'll be flattened to `null` (defaults to `true`). */
47+
empty?: boolean
48+
}
49+
50+
/** XML parser {@linkcode parse_options}`.revive` */
51+
export type revive_options = {
52+
/**
53+
* Trim texts (this is applied before other revivals, defaults to `true`).
54+
* It honors `xml:space="preserve"` attribute.
55+
*/
56+
trim?: boolean
57+
/**
58+
* Revive XML entities (defaults to `true`).
59+
* Automatically unescape XML entities and replace common entities with their respective characters.
60+
*/
61+
entities?: boolean
62+
/** Revive booleans (matching `/^(?:[Tt]rue|[Ff]alse)$/`).*/
63+
booleans?: boolean
64+
/**
65+
* Revive finite numbers.
66+
* Note that the version of the XML prolog is always treated as a string to avoid breaking documents.
67+
*/
68+
numbers?: boolean
69+
/**
70+
* Custom reviver (this is applied after other revivals).
71+
* When it is applied on an attribute, `key` and `value` will be given.
72+
* When it is applied on a node, both `key` and `value` will be `null`.
73+
* Return `undefined` to delete either the attribute or the tag.
74+
*/
75+
custom?: reviver
76+
}
77+
78+
/**
79+
* Custom XML parser reviver.
80+
* It can be used to change the way some nodes are parsed.
81+
*/
82+
export type reviver = (args: { name: string; key: Nullable<string>; value: Nullable<string>; node: Readonly<xml_node> }) => unknown
83+
6984
/**
7085
* Parse a XML string into an object.
7186
*
@@ -115,7 +130,7 @@ export type options = {
115130
* console.log(parse(file))
116131
* ```
117132
*/
118-
export function parse(content: string | ReaderSync, options?: options): xml_document {
133+
export function parse(content: string | ReaderSync, options?: parse_options): xml_document {
119134
const xml = xml_node("~xml") as xml_document
120135
const stack = [xml] as Array<xml_node>
121136
const tokens = [] as Array<[number, string, string?]>
@@ -322,7 +337,7 @@ function xml_node(name: string, { parent = null as Nullable<xml_node> } = {}): x
322337
}
323338

324339
/** Post-process xml node. */
325-
function postprocess(node: xml_node, options: options) {
340+
function postprocess(node: xml_node, options: parse_options) {
326341
// Clean XML document if required
327342
if (node["~name"] === "~xml") {
328343
if (options?.clean?.doctype) {
@@ -419,7 +434,7 @@ const entities = {
419434
} as const
420435

421436
/** Revive value. */
422-
function revive(node: xml_node | xml_text, key: string, options: options) {
437+
function revive(node: xml_node | xml_text, key: string, options: parse_options) {
423438
let value = (node as xml_node)[key] as string
424439
if (options?.revive?.trim) {
425440
value = value.trim()

xml/stringify.ts

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,45 @@
55

66
// Imports
77
import type { Nullable, stringifyable, xml_document, xml_node, xml_text } from "./_types.ts"
8-
export type { Nullable, stringifyable, xml_document, xml_node, xml_text }
8+
export type * from "./_types.ts"
99

1010
/** XML stringifier options. */
11-
export type options = {
11+
export type stringify_options = {
1212
/** Format options. */
13-
format?: {
14-
/**
15-
* Indent string (defaults to `" "`).
16-
* Set to empty string to disable indentation and enable minification.
17-
*/
18-
indent?: string
19-
/** Break text node if its length is greater than this value (defaults to `128`). */
20-
breakline?: number
21-
}
13+
format?: format_options
2214
/** Replace options. */
23-
replace?: {
24-
/**
25-
* Force escape all XML entities.
26-
* By default, only the ones that would break the XML structure are escaped.
27-
*/
28-
entities?: boolean
29-
/**
30-
* Custom replacer (this is applied after other revivals).
31-
* When it is applied on an attribute, `key` and `value` will be given.
32-
* When it is applied on a node, both `key` and `value` will be `null`.
33-
* Return `undefined` to delete either the attribute or the tag.
34-
*/
35-
custom?: (args: { name: string; key: Nullable<string>; value: Nullable<string>; node: Readonly<xml_node> }) => unknown
36-
}
15+
replace?: replace_options
16+
}
17+
18+
/** XML stringifier {@linkcode stringify_options}`.format` */
19+
export type format_options = {
20+
/**
21+
* Indent string (defaults to `" "`).
22+
* Set to empty string to disable indentation and enable minification.
23+
*/
24+
indent?: string
25+
/** Break text node if its length is greater than this value (defaults to `128`). */
26+
breakline?: number
27+
}
28+
29+
/** XML stringifier {@linkcode stringify_options}`.replace` */
30+
export type replace_options = {
31+
/**
32+
* Force escape all XML entities.
33+
* By default, only the ones that would break the XML structure are escaped.
34+
*/
35+
entities?: boolean
36+
/**
37+
* Custom replacer (this is applied after other revivals).
38+
* When it is applied on an attribute, `key` and `value` will be given.
39+
* When it is applied on a node, both `key` and `value` will be `null`.
40+
* Return `undefined` to delete either the attribute or the tag.
41+
*/
42+
custom?: (args: { name: string; key: Nullable<string>; value: Nullable<string>; node: Readonly<xml_node> }) => unknown
3743
}
3844

3945
/** XML stringifier options (with non-nullable format options). */
40-
type _options = options & { format: NonNullable<options["format"]> }
46+
type _options = stringify_options & { format: NonNullable<stringify_options["format"]> }
4147

4248
/** Internal symbol to store properties without erasing user-provided ones. */
4349
const internal = Symbol("internal")
@@ -66,7 +72,7 @@ const internal = Symbol("internal")
6672
* }))
6773
* ```
6874
*/
69-
export function stringify(document: stringifyable, options?: options): string {
75+
export function stringify(document: stringifyable, options?: stringify_options): string {
7076
options ??= {}
7177
options.format ??= {}
7278
options.format.indent ??= " "
@@ -237,7 +243,7 @@ function xml_node(node: xml_node, { format: { breakline = 0, indent = "" }, repl
237243
}
238244

239245
/** Extract children from node. */
240-
function xml_children(node: xml_node, options: options): Array<xml_node> {
246+
function xml_children(node: xml_node, options: stringify_options): Array<xml_node> {
241247
const children = Object.keys(node)
242248
.filter((key) => /^[A-Za-z_]/.test(key))
243249
.flatMap((key) =>
@@ -274,7 +280,7 @@ function xml_children(node: xml_node, options: options): Array<xml_node> {
274280
}
275281

276282
/** Extract attributes from node. */
277-
function xml_attributes(node: xml_node, options: options): Array<[string, string]> {
283+
function xml_attributes(node: xml_node, options: stringify_options): Array<[string, string]> {
278284
return Object.entries(node!)
279285
.filter(([key]) => key.startsWith("@"))
280286
.map(([key]) => [key.slice(1), replace(node!, key, { ...options, escape: ["&", '"', "'"] })])
@@ -291,7 +297,7 @@ const entities = {
291297
} as const
292298

293299
/** Replace value. */
294-
function replace(node: xml_node | xml_text, key: string, options: options & { escape?: Array<keyof typeof entities> }) {
300+
function replace(node: xml_node | xml_text, key: string, options: stringify_options & { escape?: Array<keyof typeof entities> }) {
295301
let value = `${(node as xml_node)[key]}` as string
296302
if (options?.escape) {
297303
if (options?.replace?.entities) {

xml/stringify_test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { type options as parse_options, parse } from "./parse.ts"
2-
import { cdata, comment, type options as stringify_options, stringify } from "./stringify.ts"
1+
import { parse, type parse_options } from "./parse.ts"
2+
import { cdata, comment, stringify, type stringify_options } from "./stringify.ts"
33
import { expect, test } from "@libs/testing"
44

55
// This operation ensure that reforming a parsed XML will still yield same data

0 commit comments

Comments
 (0)