Skip to content

Commit 13ed635

Browse files
committed
Refactor code-style
* Refactor to allow `null` in API input types * Add more docs to JSDoc * Refactor a bunch of code
1 parent d13d414 commit 13ed635

14 files changed

+180
-117
lines changed

lib/all.js

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
/**
2-
* @typedef {import('./index.js').Parent} Parent
3-
* @typedef {import('./index.js').Context} Context
4-
* @typedef {import('./index.js').Child} Child
2+
* @typedef {import('unist').Parent} UnistParent
3+
* @typedef {import('xast').Root} Root
4+
* @typedef {import('xast').RootChildMap} RootChildMap
5+
* @typedef {import('./index.js').State} State
6+
*/
7+
8+
/**
9+
* @typedef {Root | RootChildMap[keyof RootChildMap]} Node
10+
* @typedef {Extract<Node, UnistParent>} Parent
11+
* @typedef {Parent['children'][number]} Child
512
*/
613

714
import {one} from './one.js'
@@ -10,19 +17,21 @@ import {one} from './one.js'
1017
* Serialize all children of `parent`.
1118
*
1219
* @param {Parent} parent
13-
* @param {Context} ctx
20+
* xast parent node.
21+
* @param {State} state
22+
* Info passed around about the current state.
1423
* @returns {string}
15-
*
24+
* Serialized XML.
1625
*/
17-
export function all(parent, ctx) {
26+
export function all(parent, state) {
1827
/** @type {Array<Child>} */
1928
const children = (parent && parent.children) || []
2029
let index = -1
2130
/** @type {Array<string>} */
2231
const results = []
2332

2433
while (++index < children.length) {
25-
results[index] = one(children[index], ctx)
34+
results[index] = one(children[index], state)
2635
}
2736

2837
return results.join('')

lib/cdata.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/**
2-
* @typedef {import('./index.js').Handle} Handle
3-
* @typedef {import('./index.js').Cdata} Cdata
2+
* @typedef {import('xast').Cdata} Cdata
43
*/
54

65
import {escape} from './util-escape.js'
@@ -11,8 +10,10 @@ const subset = ['>']
1110
/**
1211
* Serialize a CDATA section.
1312
*
14-
* @type {Handle}
1513
* @param {Cdata} node
14+
* xast cdata node.
15+
* @returns {string}
16+
* Serialized XML.
1617
*/
1718
export function cdata(node) {
1819
return '<![CDATA[' + escape(node.value, subset, unsafe) + ']]>'

lib/comment.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
/**
2-
* @typedef {import('./index.js').Handle} Handle
3-
* @typedef {import('./index.js').Comment} Comment
2+
* @typedef {import('xast').Comment} Comment
43
*/
54

65
import {escape} from './util-escape.js'
76

87
/**
98
* Serialize a comment.
109
*
11-
* @type {Handle}
1210
* @param {Comment} node
11+
* xast comment node.
12+
* @returns {string}
13+
* Serialized XML.
1314
*/
1415
export function comment(node) {
1516
return '<!--' + escape(node.value, ['-']) + '-->'

lib/doctype.js

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
2-
* @typedef {import('./index.js').Handle} Handle
3-
* @typedef {import('./index.js').Doctype} Doctype
2+
* @typedef {import('xast').Doctype} Doctype
3+
* @typedef {import('./index.js').State} State
44
*/
55

66
import {name} from './name.js'
@@ -9,10 +9,14 @@ import {value} from './value.js'
99
/**
1010
* Serialize a doctype.
1111
*
12-
* @type {Handle}
1312
* @param {Doctype} node
13+
* xast doctype node.
14+
* @param {State} state
15+
* Info passed around about the current state.
16+
* @returns {string}
17+
* Serialized XML.
1418
*/
15-
export function doctype(node, ctx) {
19+
export function doctype(node, state) {
1620
const nodeName = name(node.name)
1721
const pub = node.public
1822
const sys = node.system
@@ -22,14 +26,14 @@ export function doctype(node, ctx) {
2226
result += ' ' + nodeName
2327
}
2428

25-
if (pub !== null && pub !== undefined && pub !== '') {
26-
result += ' PUBLIC ' + value(pub, ctx)
27-
} else if (sys !== null && sys !== undefined && sys !== '') {
29+
if (pub) {
30+
result += ' PUBLIC ' + value(pub, state)
31+
} else if (sys) {
2832
result += ' SYSTEM'
2933
}
3034

31-
if (sys !== null && sys !== undefined && sys !== '') {
32-
result += ' ' + value(sys, ctx)
35+
if (sys) {
36+
result += ' ' + value(sys, state)
3337
}
3438

3539
return result + '>'

lib/element.js

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/**
2-
* @typedef {import('./index.js').Handle} Handle
3-
* @typedef {import('./index.js').Element} Element
4-
* @typedef {import('./index.js').Attributes} Attributes
2+
* @typedef {import('xast').Element} Element
3+
* @typedef {import('./index.js').State} State
54
*/
65

76
import {all} from './all.js'
@@ -13,28 +12,29 @@ const own = {}.hasOwnProperty
1312
/**
1413
* Serialize an element.
1514
*
16-
* @type {Handle}
1715
* @param {Element} node
16+
* xast element node.
17+
* @param {State} state
18+
* Info passed around about the current state.
19+
* @returns {string}
20+
* Serialized XML.
1821
*/
19-
export function element(node, ctx) {
22+
export function element(node, state) {
2023
const nodeName = name(node.name)
21-
const content = all(node, ctx)
22-
/** @type {Attributes} */
24+
const content = all(node, state)
2325
const attributes = node.attributes || {}
24-
const close = content ? false : ctx.close
26+
const close = content ? false : state.options.closeEmptyElements
2527
/** @type {Array<string>} */
2628
const attrs = []
2729
/** @type {string} */
2830
let key
29-
/** @type {Attributes[keyof Attributes]} */
30-
let result
3131

3232
for (key in attributes) {
3333
if (own.call(attributes, key)) {
34-
result = attributes[key]
34+
const result = attributes[key]
3535

3636
if (result !== null && result !== undefined) {
37-
attrs.push(name(key) + '=' + value(result, ctx))
37+
attrs.push(name(key) + '=' + value(result, state))
3838
}
3939
}
4040
}
@@ -43,7 +43,7 @@ export function element(node, ctx) {
4343
'<' +
4444
nodeName +
4545
(attrs.length === 0 ? '' : ' ' + attrs.join(' ')) +
46-
(close ? (ctx.tight ? '' : ' ') + '/' : '') +
46+
(close ? (state.options.tightClose ? '' : ' ') + '/' : '') +
4747
'>' +
4848
content +
4949
(close ? '' : '</' + nodeName + '>')

lib/index.js

Lines changed: 52 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,75 @@
11
/**
2+
* @typedef {import('xast').Literal} Literal
23
* @typedef {import('xast').Root} Root
3-
* @typedef {import('xast').Element} Element
4-
* @typedef {import('xast').Cdata} Cdata
5-
* @typedef {import('xast').Comment} Comment
6-
* @typedef {import('xast').Doctype} Doctype
7-
* @typedef {import('xast').Instruction} Instruction
8-
* @typedef {import('xast').Text} Text
9-
* @typedef {import('xast').Literal & {type: 'raw'}} Raw
10-
* @typedef {Root|Element} Parent
11-
* @typedef {import('xast').Attributes} Attributes
12-
* @typedef {Root['children'][number]} Child
13-
* @typedef {Child|Root} Node
4+
* @typedef {import('xast').RootChildMap} RootChildMap
5+
*/
6+
7+
/**
8+
* @typedef {Literal & {type: 'raw'}} Raw
9+
* @typedef {Root | RootChildMap[keyof RootChildMap]} Node
1410
*
15-
* @typedef {'"'|"'"} Quote
11+
* @typedef {'"' | "'"} Quote
12+
* XML quotes for attribute values.
1613
*
1714
* @typedef Options
18-
* @property {Quote} [quote='"'] Preferred quote to use
19-
* @property {boolean} [quoteSmart=false] Use the other quote if that results in
20-
* less bytes
21-
* @property {boolean} [closeEmptyElements=false] Close elements without any
22-
* content with slash (/) on the opening tag instead of an end tag:
23-
* `<circle />` instead of `<circle></circle>`.
15+
* Configuration.
16+
* @property {boolean | null | undefined} [allowDangerousXml=false]
17+
* Allow `raw` nodes and insert them as raw XML.
18+
*
19+
* When `false`, `Raw` nodes are encoded.
20+
*
21+
* > ⚠️ **Danger**: only set this if you completely trust the content.
22+
* @property {boolean | null | undefined} [closeEmptyElements=false]
23+
* Close elements without any content with slash (`/`) on the opening tag
24+
* instead of an end tag: `<circle />` instead of `<circle></circle>`.
25+
*
2426
* See `tightClose` to control whether a space is used before the slash.
25-
* @property {boolean} [tightClose=false] Do not use an extra space when closing
26-
* self-closing elements: `<circle/>` instead of `<circle />`.
27-
* @property {boolean} [allowDangerousXml=false] Allow `raw` nodes and insert
28-
* them as raw XML. When falsey, encodes `raw` nodes.
29-
* Only set this if you completely trust the content!
27+
* @property {Quote | null | undefined} [quote='"']
28+
* Preferred quote to use.
29+
* @property {boolean | null | undefined} [quoteSmart=false]
30+
* Use the other quote if that results in less bytes.
31+
* @property {boolean | null | undefined} [tightClose=false]
32+
* Do not use an extra space when closing self-closing elements: `<circle/>`
33+
* instead of `<circle />`.
3034
*
31-
* @typedef Context
32-
* @property {Quote} quote
33-
* @property {Quote|undefined} alternative
34-
* @property {boolean} close
35-
* @property {boolean} tight
36-
* @property {boolean} dangerous
35+
* > 👉 **Note**: only used if `closeEmptyElements: true`.
3736
*
38-
* @callback Handle
39-
* @param {Node} node
40-
* @param {Context} context
41-
* @returns {string}
37+
* @typedef State
38+
* Info passed around about the current state.
39+
* @property {Options} options
40+
* Configuration.
4241
*/
4342

4443
import {one} from './one.js'
4544

4645
/**
47-
* Serialize the given xast tree (or list of nodes).
46+
* Serialize a xast tree to XML.
4847
*
49-
* @param {Node|Array<Node>} node
50-
* @param {Options} [options]
48+
* @param {Node | Array<Node>} node
49+
* xast node(s) to serialize.
50+
* @param {Options | null | undefined} [options]
51+
* Configuration.
5152
* @returns {string}
53+
* Serialized XML.
5254
*/
53-
export function toXml(node, options = {}) {
54-
const quote = options.quote || '"'
55-
/** @type {Quote} */
56-
const alternative = quote === '"' ? "'" : '"'
57-
const smart = options.quoteSmart
55+
export function toXml(node, options) {
5856
/** @type {Node} */
5957
// @ts-expect-error Assume no `root` in `node`.
6058
const value = Array.isArray(node) ? {type: 'root', children: node} : node
6159

62-
if (quote !== '"' && quote !== "'") {
63-
throw new Error('Invalid quote `' + quote + '`, expected `\'` or `"`')
60+
/** @type {State} */
61+
const state = {options: options || {}}
62+
63+
// Make sure the quote is valid.
64+
if (
65+
typeof state.options.quote === 'string' &&
66+
state.options.quote !== '"' &&
67+
state.options.quote !== "'"
68+
) {
69+
throw new Error(
70+
'Invalid quote `' + state.options.quote + '`, expected `\'` or `"`'
71+
)
6472
}
6573

66-
return one(value, {
67-
dangerous: options.allowDangerousXml || false,
68-
close: options.closeEmptyElements || false,
69-
tight: options.tightClose || false,
70-
quote,
71-
alternative: smart ? alternative : undefined
72-
})
74+
return one(value, state)
7375
}

lib/instruction.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/**
2-
* @typedef {import('./index.js').Handle} Handle
3-
* @typedef {import('./index.js').Instruction} Instruction
2+
* @typedef {import('xast').Instruction} Instruction
43
*/
54

65
import {escape} from './util-escape.js'
@@ -12,8 +11,10 @@ const subset = ['>']
1211
/**
1312
* Serialize an instruction.
1413
*
15-
* @type {Handle}
1614
* @param {Instruction} node
15+
* xast instruction node.
16+
* @returns {string}
17+
* Serialized XML.
1718
*/
1819
export function instruction(node) {
1920
const nodeName = name(node.name) || 'x'

lib/name.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import {escape} from './util-escape.js'
33
const subset = ['\t', '\n', ' ', '"', '&', "'", '/', '<', '=', '>']
44

55
/**
6-
* Serialize a node name.
6+
* Encode a node name.
77
*
88
* @param {string} value
9+
* Raw name.
910
* @returns {string}
11+
* Escaped name.
1012
*/
1113
export function name(value) {
1214
return escape(value, subset)

lib/one.js

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
/**
2-
* @typedef {import('./index.js').Handle} Handle
2+
* @typedef {import('xast').Root} Root
3+
* @typedef {import('xast').RootChildMap} RootChildMap
4+
* @typedef {import('./index.js').State} State
5+
*/
6+
7+
/**
8+
* @typedef {Root | RootChildMap[keyof RootChildMap]} Node
39
*/
410

511
import {all} from './all.js'
@@ -27,9 +33,14 @@ const handlers = {
2733
/**
2834
* Serialize a node.
2935
*
30-
* @type {Handle}
36+
* @param {Node} node
37+
* xast node.
38+
* @param {State} state
39+
* Info passed around about the current state.
40+
* @returns {string}
41+
* Serialized XML.
3142
*/
32-
export function one(node, ctx) {
43+
export function one(node, state) {
3344
const type = node && node.type
3445

3546
if (!type) {
@@ -40,6 +51,9 @@ export function one(node, ctx) {
4051
throw new Error('Cannot compile unknown node `' + type + '`')
4152
}
4253

43-
// @ts-expect-error Hush, it works.
44-
return handlers[type](node, ctx)
54+
const handle = handlers[type]
55+
// @ts-expect-error hush, node matches `type`.
56+
const result = handle(node, state)
57+
58+
return result
4559
}

0 commit comments

Comments
 (0)