Skip to content

Commit 1a4f304

Browse files
committed
Refactor code-style
1 parent 5b87105 commit 1a4f304

File tree

2 files changed

+148
-146
lines changed

2 files changed

+148
-146
lines changed

lib/index.js

+20-18
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/**
2-
* @typedef {import('hast').Root} HastRoot
3-
* @typedef {import('hast').RootContent} HastRootContent
2+
* @typedef {import('hast').Comment} HastComment
43
* @typedef {import('hast').Doctype} HastDoctype
54
* @typedef {import('hast').Element} HastElement
6-
* @typedef {import('hast').Text} HastText
7-
* @typedef {import('hast').Comment} HastComment
85
* @typedef {import('hast').Nodes} HastNodes
6+
* @typedef {import('hast').Root} HastRoot
7+
* @typedef {import('hast').RootContent} HastRootContent
8+
* @typedef {import('hast').Text} HastText
99
*/
1010

1111
/**
@@ -15,17 +15,19 @@
1515
* DOM node that was handled.
1616
* @param {HastNodes} hastNode
1717
* Corresponding hast node.
18-
* @returns {void}
18+
* @returns {undefined | void}
1919
* Nothing.
2020
*
21+
* Note: `void` included until TS infers `undefined` nicely.
22+
*
2123
* @typedef Options
2224
* Configuration.
2325
* @property {AfterTransform | null | undefined} [afterTransform]
24-
* Callback called when each node is transformed.
26+
* Callback called when each node is transformed (optional).
2527
*/
2628

27-
import {webNamespaces} from 'web-namespaces'
2829
import {h, s} from 'hastscript'
30+
import {webNamespaces} from 'web-namespaces'
2931

3032
/**
3133
* Transform a DOM tree to a hast tree.
@@ -70,15 +72,15 @@ function transform(node, options) {
7072
function one(node, options) {
7173
switch (node.nodeType) {
7274
case 1 /* Element */: {
73-
// @ts-expect-error TypeScript is wrong.
74-
return element(node, options)
75+
const domNode = /** @type {Element} */ (node)
76+
return element(domNode, options)
7577
}
7678

7779
// Ignore: Attr (2).
7880

7981
case 3 /* Text */: {
80-
// @ts-expect-error TypeScript is wrong.
81-
return text(node)
82+
const domNode = /** @type {Text} */ (node)
83+
return text(domNode)
8284
}
8385

8486
// Ignore: CDATA (4).
@@ -87,22 +89,22 @@ function one(node, options) {
8789
// Ignore: Processing instruction (7).
8890

8991
case 8 /* Comment */: {
90-
// @ts-expect-error TypeScript is wrong.
91-
return comment(node)
92+
const domNode = /** @type {Comment} */ (node)
93+
return comment(domNode)
9294
}
9395

9496
case 9 /* Document */: {
95-
// @ts-expect-error TypeScript is wrong.
96-
return root(node, options)
97+
const domNode = /** @type {Document} */ (node)
98+
return root(domNode, options)
9799
}
98100

99101
case 10 /* Document type */: {
100102
return doctype()
101103
}
102104

103105
case 11 /* Document fragment */: {
104-
// @ts-expect-error TypeScript is wrong.
105-
return root(node, options)
106+
const domNode = /** @type {DocumentFragment} */ (node)
107+
return root(domNode, options)
106108
}
107109

108110
default: {
@@ -176,7 +178,7 @@ function element(node, options) {
176178
space === webNamespaces.html ? node.tagName.toLowerCase() : node.tagName
177179
/** @type {DocumentFragment | Element} */
178180
const content =
179-
// @ts-expect-error Types are wrong.
181+
// @ts-expect-error: DOM types are wrong, content can exist.
180182
space === webNamespaces.html && tagName === 'template' ? node.content : node
181183
const attributes = node.getAttributeNames()
182184
/** @type {Record<string, string>} */

test/index.js

+128-128
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,19 @@ import process from 'node:process'
1010
import test from 'node:test'
1111
import {JSDOM} from 'jsdom'
1212
import {fromDom} from '../index.js'
13-
import * as mod from '../index.js'
1413

1514
const window = new JSDOM().window
1615
globalThis.document = window.document
1716

18-
test('fromDom', () => {
19-
assert.deepEqual(
20-
Object.keys(mod).sort(),
21-
['fromDom'],
22-
'should expose the public api'
23-
)
17+
test('fromDom', async function (t) {
18+
await t.test('should expose the public api', async function () {
19+
assert.deepEqual(Object.keys(await import('../index.js')).sort(), [
20+
'fromDom'
21+
])
22+
})
2423

25-
assert.deepEqual(
26-
fromDom(doc('<title>Hello!</title><h1>World!')),
27-
{
24+
await t.test('should transform a complete document', async function () {
25+
assert.deepEqual(fromDom(doc('<title>Hello!</title><h1>World!')), {
2826
type: 'root',
2927
children: [
3028
{
@@ -61,13 +59,11 @@ test('fromDom', () => {
6159
]
6260
}
6361
]
64-
},
65-
'should transform a complete document'
66-
)
62+
})
63+
})
6764

68-
assert.deepEqual(
69-
fromDom(fragment('<title>Hello!</title><h1>World!')),
70-
{
65+
await t.test('should transform a fragment', async function () {
66+
assert.deepEqual(fromDom(fragment('<title>Hello!</title><h1>World!')), {
7167
type: 'root',
7268
children: [
7369
{
@@ -83,98 +79,101 @@ test('fromDom', () => {
8379
children: [{type: 'text', value: 'World!'}]
8480
}
8581
]
86-
},
87-
'should transform a fragment'
88-
)
89-
90-
assert.deepEqual(
91-
fromDom(document.createDocumentFragment()),
92-
{type: 'root', children: []},
93-
'should support an empty fragment'
94-
)
95-
96-
assert.deepEqual(
97-
fromDom(document.createComment('alpha')),
98-
{type: 'comment', value: 'alpha'},
99-
'should support a comment'
100-
)
101-
102-
assert.deepEqual(
103-
fromDom(document.createTextNode('bravo')),
104-
{type: 'text', value: 'bravo'},
105-
'should support a text'
106-
)
107-
108-
const cdata = new JSDOM('<xml></xml>', {
109-
contentType: 'application/xml'
110-
}).window.document.createCDATASection('charlie')
111-
112-
assert.deepEqual(
113-
fromDom(cdata),
114-
{type: 'root', children: []},
115-
'should handle CDATA'
116-
)
117-
118-
const frag = document.createDocumentFragment()
119-
120-
// eslint-disable-next-line unicorn/prefer-dom-node-append
121-
frag.appendChild(cdata)
122-
123-
assert.deepEqual(
124-
fromDom(frag),
125-
{type: 'root', children: []},
126-
'should handle CDATA in HTML'
127-
)
128-
129-
assert.deepEqual(
130-
// @ts-expect-error runtime.
131-
fromDom(),
132-
{type: 'root', children: []},
133-
'should handle a missing DOM tree'
134-
)
135-
136-
assert.deepEqual(
137-
fromDom(document.createTextNode('')),
138-
{type: 'text', value: ''},
139-
'should support a text w/o value'
140-
)
141-
142-
assert.deepEqual(
143-
fromDom(document.createComment('')),
144-
{type: 'comment', value: ''},
145-
'should support a comment w/o value'
146-
)
147-
148-
const attribute = document.createAttribute('title')
149-
const element = document.createElement('div')
150-
element.setAttributeNode(attribute)
151-
152-
assert.deepEqual(
153-
fromDom(element),
154-
{type: 'element', tagName: 'div', properties: {title: ''}, children: []},
155-
'should support an attribute w/o value'
156-
)
157-
158-
const heading = document.createElement('h2')
159-
const text = document.createTextNode('Hello')
160-
heading.append(text)
161-
162-
assert.deepEqual(
163-
(() => {
164-
/** @type {Array<[Node, HastNodes|undefined]>} */
165-
const calls = []
166-
fromDom(heading, {
167-
/**
168-
* @param {Node} node
169-
* @param {HastNodes|undefined} transformed
170-
*/
171-
afterTransform(node, transformed) {
172-
calls.push([node, transformed])
173-
}
174-
})
175-
return calls
176-
})(),
177-
[
82+
})
83+
})
84+
85+
await t.test('should support an empty fragment', async function () {
86+
assert.deepEqual(fromDom(document.createDocumentFragment()), {
87+
type: 'root',
88+
children: []
89+
})
90+
})
91+
92+
await t.test('should support a comment', async function () {
93+
assert.deepEqual(fromDom(document.createComment('alpha')), {
94+
type: 'comment',
95+
value: 'alpha'
96+
})
97+
})
98+
99+
await t.test('should support a text', async function () {
100+
assert.deepEqual(fromDom(document.createTextNode('bravo')), {
101+
type: 'text',
102+
value: 'bravo'
103+
})
104+
})
105+
106+
await t.test('should handle CDATA', async function () {
107+
const cdata = new JSDOM('<xml></xml>', {
108+
contentType: 'application/xml'
109+
}).window.document.createCDATASection('charlie')
110+
111+
assert.deepEqual(fromDom(cdata), {type: 'root', children: []})
112+
})
113+
114+
await t.test('should handle CDATA in HTML', async function () {
115+
const cdata = new JSDOM('<xml></xml>', {
116+
contentType: 'application/xml'
117+
}).window.document.createCDATASection('charlie')
118+
const frag = document.createDocumentFragment()
119+
120+
// eslint-disable-next-line unicorn/prefer-dom-node-append
121+
frag.appendChild(cdata)
122+
123+
assert.deepEqual(fromDom(frag), {type: 'root', children: []})
124+
})
125+
126+
await t.test('should handle a missing DOM tree', async function () {
127+
assert.deepEqual(
128+
// To do: remove.
129+
// @ts-expect-error runtime.
130+
fromDom(),
131+
{type: 'root', children: []}
132+
)
133+
})
134+
135+
await t.test('should support a text w/o value', async function () {
136+
assert.deepEqual(fromDom(document.createTextNode('')), {
137+
type: 'text',
138+
value: ''
139+
})
140+
})
141+
142+
await t.test('should support a comment w/o value', async function () {
143+
assert.deepEqual(fromDom(document.createComment('')), {
144+
type: 'comment',
145+
value: ''
146+
})
147+
})
148+
149+
await t.test('should support an attribute w/o value', async function () {
150+
const attribute = document.createAttribute('title')
151+
const element = document.createElement('div')
152+
element.setAttributeNode(attribute)
153+
154+
assert.deepEqual(fromDom(element), {
155+
type: 'element',
156+
tagName: 'div',
157+
properties: {title: ''},
158+
children: []
159+
})
160+
})
161+
162+
await t.test('should call `afterTransform`', async function () {
163+
const heading = document.createElement('h2')
164+
const text = document.createTextNode('Hello')
165+
heading.append(text)
166+
167+
/** @type {Array<[unknown, unknown]>} */
168+
const calls = []
169+
170+
fromDom(heading, {
171+
afterTransform(node, transformed) {
172+
calls.push([node, transformed])
173+
}
174+
})
175+
176+
assert.deepEqual(calls, [
178177
[text, {type: 'text', value: 'Hello'}],
179178
[
180179
heading,
@@ -185,12 +184,11 @@ test('fromDom', () => {
185184
children: [{type: 'text', value: 'Hello'}]
186185
}
187186
]
188-
],
189-
'should invoke afterTransform'
190-
)
187+
])
188+
})
191189
})
192190

193-
test('fixtures', async () => {
191+
test('fixtures', async function (t) {
194192
const base = new URL('fixtures/', import.meta.url)
195193
const folders = await fs.readdir(base)
196194

@@ -199,25 +197,27 @@ test('fixtures', async () => {
199197
continue
200198
}
201199

202-
const treeUrl = new URL(folder + '/index.json', base)
203-
const fixtureUrl = new URL(folder + '/index.html', base)
204-
const input = String(await fs.readFile(fixtureUrl))
205-
const actual = fromDom(doc(input))
206-
/** @type {HastNodes} */
207-
let expected
200+
await t.test(folder, async function () {
201+
const treeUrl = new URL(folder + '/index.json', base)
202+
const fixtureUrl = new URL(folder + '/index.html', base)
203+
const input = String(await fs.readFile(fixtureUrl))
204+
const actual = fromDom(doc(input))
205+
/** @type {HastNodes} */
206+
let expected
207+
208+
try {
209+
if ('UPDATE' in process.env) {
210+
throw new Error('Updating')
211+
}
208212

209-
try {
210-
if ('UPDATE' in process.env) {
211-
throw new Error('Updating')
213+
expected = JSON.parse(String(await fs.readFile(treeUrl)))
214+
} catch {
215+
await fs.writeFile(treeUrl, JSON.stringify(actual, undefined, 2))
216+
return
212217
}
213218

214-
expected = JSON.parse(String(await fs.readFile(treeUrl)))
215-
} catch {
216-
await fs.writeFile(treeUrl, JSON.stringify(actual, null, 2))
217-
continue
218-
}
219-
220-
assert.deepEqual(actual, expected, folder)
219+
assert.deepEqual(actual, expected, folder)
220+
})
221221
}
222222
})
223223

0 commit comments

Comments
 (0)