Skip to content

Commit ba41ded

Browse files
committed
Add JSDoc based types
1 parent ed90b0e commit ba41ded

File tree

4 files changed

+113
-33
lines changed

4 files changed

+113
-33
lines changed

lib/index.js

Lines changed: 73 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
/**
2+
* @typedef {import('hast').Parent} HastParent
3+
* @typedef {import('hast').Root} HastRoot
4+
* @typedef {import('hast').DocType} HastDoctype
5+
* @typedef {import('hast').Element} HastElement
6+
* @typedef {import('hast').Text} HastText
7+
* @typedef {import('hast').Comment} HastComment
8+
* @typedef {HastParent['children'][number]} HastChild
9+
* @typedef {HastChild|HastRoot} HastNode
10+
*/
11+
112
import {webNamespaces} from 'web-namespaces'
213
import {h, s} from 'hastscript'
314

@@ -8,32 +19,49 @@ const DOCUMENT_NODE = 9
819
const DOCUMENT_TYPE_NODE = 10
920
const DOCUMENT_FRAGMENT_NODE = 11
1021

11-
function transform(value) {
12-
const node = value || {}
13-
22+
/**
23+
* @param {Node} node
24+
* @returns {HastNode|null}
25+
*/
26+
function transform(node) {
1427
switch (node.nodeType) {
1528
case ELEMENT_NODE:
29+
// @ts-ignore TypeScript is wrong.
1630
return element(node)
1731
case DOCUMENT_NODE:
1832
case DOCUMENT_FRAGMENT_NODE:
33+
// @ts-ignore TypeScript is wrong.
1934
return root(node)
2035
case TEXT_NODE:
36+
// @ts-ignore TypeScript is wrong.
2137
return text(node)
2238
case COMMENT_NODE:
39+
// @ts-ignore TypeScript is wrong.
2340
return comment(node)
2441
case DOCUMENT_TYPE_NODE:
42+
// @ts-ignore TypeScript is wrong.
2543
return doctype(node)
2644
default:
2745
return null
2846
}
2947
}
3048

31-
// Transform a document.
49+
/**
50+
* Transform a document.
51+
*
52+
* @param {Document|DocumentFragment} node
53+
* @returns {HastRoot}
54+
*/
3255
function root(node) {
3356
return {type: 'root', children: all(node)}
3457
}
3558

36-
// Transform a doctype.
59+
/**
60+
* Transform a doctype.
61+
*
62+
* @param {DocumentType} node
63+
* @returns {HastDoctype}
64+
*/
3765
function doctype(node) {
3866
return {
3967
type: 'doctype',
@@ -43,57 +71,82 @@ function doctype(node) {
4371
}
4472
}
4573

46-
// Transform text.
74+
/**
75+
* Transform a text.
76+
*
77+
* @param {Text} node
78+
* @returns {HastText}
79+
*/
4780
function text(node) {
4881
return {type: 'text', value: node.nodeValue}
4982
}
5083

51-
// Transform a comment.
84+
/**
85+
* Transform a comment.
86+
*
87+
* @param {Comment} node
88+
* @returns {HastComment}
89+
*/
5290
function comment(node) {
5391
return {type: 'comment', value: node.nodeValue}
5492
}
5593

56-
// Transform an element.
94+
/**
95+
* Transform an element.
96+
*
97+
* @param {Element} node
98+
* @returns {HastElement}
99+
*/
57100
function element(node) {
58101
const space = node.namespaceURI
59102
const fn = space === webNamespaces.svg ? s : h
60103
const tagName =
61104
space === webNamespaces.html ? node.tagName.toLowerCase() : node.tagName
105+
/** @type {DocumentFragment|Element} */
62106
const content =
107+
// @ts-ignore Types are wrong.
63108
space === webNamespaces.html && tagName === 'template' ? node.content : node
64109
const attributes = node.getAttributeNames()
65-
const {length} = attributes
110+
/** @type {Object.<string, string>} */
66111
const props = {}
67-
let index = 0
112+
let index = -1
68113

69-
while (index < length) {
70-
const key = attributes[index]
71-
props[key] = node.getAttribute(key)
72-
index += 1
114+
while (++index < attributes.length) {
115+
props[attributes[index]] = node.getAttribute(attributes[index])
73116
}
74117

75118
return fn(tagName, props, all(content))
76119
}
77120

121+
/**
122+
* Transform an element.
123+
*
124+
* @param {Document|DocumentFragment|Element} node
125+
* @returns {Array.<HastChild>}
126+
*/
78127
function all(node) {
79128
const nodes = node.childNodes
80-
const {length} = nodes
129+
/** @type {Array.<HastChild>} */
81130
const children = []
82-
let index = 0
131+
let index = -1
83132

84-
while (index < length) {
133+
while (++index < nodes.length) {
85134
const child = transform(nodes[index])
86135

87136
if (child !== null) {
137+
// @ts-ignore Assume no document inside document.
88138
children.push(child)
89139
}
90-
91-
index += 1
92140
}
93141

94142
return children
95143
}
96144

145+
/**
146+
* @param {Node} node
147+
* @returns {HastNode}
148+
*/
97149
export function fromDom(node) {
98-
return transform(node) || {type: 'root', children: []}
150+
// @ts-ignore Code can handle empty “node”.
151+
return transform(node || {}) || {type: 'root', children: []}
99152
}

package.json

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,33 +28,38 @@
2828
"module": "dist/hast-util-from-dom.mjs",
2929
"sideEffects": false,
3030
"type": "module",
31+
"types": "index.d.ts",
3132
"files": [
3233
"lib/",
34+
"index.d.ts",
3335
"index.js"
3436
],
3537
"dependencies": {
3638
"hastscript": "^7.0.0",
3739
"web-namespaces": "^2.0.0"
3840
},
3941
"devDependencies": {
40-
"@babel/core": "^7.0.0",
41-
"@babel/preset-env": "^7.0.0",
42-
"@rollup/plugin-babel": "^5.0.0",
42+
"@types/jsdom": "^16.0.0",
43+
"@types/tape": "^4.0.0",
4344
"c8": "^7.0.0",
4445
"glob": "^7.0.0",
45-
"jsdom": "^16.5.3",
46+
"jsdom": "^16.0.0",
4647
"prettier": "^2.0.0",
4748
"remark-cli": "^9.0.0",
4849
"remark-preset-wooorm": "^8.0.0",
49-
"rollup": "^2.0.0",
50-
"tape": "^5.2.2",
50+
"rimraf": "^3.0.0",
51+
"tape": "^5.0.0",
52+
"type-coverage": "^2.0.0",
53+
"typescript": "^4.0.0",
5154
"xo": "^0.39.0"
5255
},
5356
"scripts": {
57+
"prepack": "npm run build && npm run format",
58+
"build": "rimraf \"{lib/**,test/**,}*.d.ts\" && tsc && type-coverage",
5459
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
5560
"test-api": "node test/index.js",
5661
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node test/index.js",
57-
"test": "npm run format && npm run test-coverage"
62+
"test": "npm run build && npm run format && npm run test-coverage"
5863
},
5964
"prettier": {
6065
"tabWidth": 2,
@@ -71,5 +76,10 @@
7176
"plugins": [
7277
"preset-wooorm"
7378
]
79+
},
80+
"typeCoverage": {
81+
"atLeast": 100,
82+
"detail": true,
83+
"strict": true
7484
}
7585
}

test/index.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import glob from 'glob'
77
import {JSDOM} from 'jsdom'
88
import {fromDom} from '../index.js'
99

10-
globalThis.window = new JSDOM().window
10+
const window = new JSDOM().window
1111
globalThis.document = window.document
1212

1313
test('hast-util-from-dom', (t) => {
@@ -116,6 +116,7 @@ test('hast-util-from-dom', (t) => {
116116
)
117117

118118
t.deepEqual(
119+
// @ts-ignore runtime.
119120
fromDom(),
120121
{type: 'root', children: []},
121122
'should handle a missing DOM tree'
@@ -135,15 +136,16 @@ test('fixtures', (t) => {
135136

136137
t.end()
137138

138-
function each(fixturePath) {
139+
function each(/** @type {string} */ fixturePath) {
139140
const input = path.join(fixturePath, 'index.html')
140141
const output = path.join(fixturePath, 'index.json')
141-
const fixtureHtml = fs.readFileSync(input)
142+
const fixtureHtml = String(fs.readFileSync(input))
142143
const actual = fromDom(doc(fixtureHtml))
144+
/** @type {unknown} */
143145
let parsedExpected
144146

145147
try {
146-
parsedExpected = JSON.parse(fs.readFileSync(output))
148+
parsedExpected = JSON.parse(String(fs.readFileSync(output)))
147149
} catch {
148150
fs.writeFileSync(output, JSON.stringify(actual, null, 2))
149151
return
@@ -153,7 +155,7 @@ test('fixtures', (t) => {
153155
}
154156
})
155157

156-
function fragment(htmlString) {
158+
function fragment(/** @type {string} */ htmlString) {
157159
const node = document.createDocumentFragment()
158160
const temporary = document.createElement('body')
159161

@@ -170,6 +172,6 @@ function fragment(htmlString) {
170172
return node
171173
}
172174

173-
function doc(htmlString) {
175+
function doc(/** @type {string} */ htmlString) {
174176
return new JSDOM(htmlString).window.document
175177
}

tsconfig.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"include": ["*.js", "lib/**/*.js", "test/**/*.js"],
3+
"compilerOptions": {
4+
"target": "ES2020",
5+
"lib": ["ES2020", "DOM"],
6+
"module": "ES2020",
7+
"moduleResolution": "node",
8+
"allowJs": true,
9+
"checkJs": true,
10+
"declaration": true,
11+
"emitDeclarationOnly": true,
12+
"allowSyntheticDefaultImports": true,
13+
"skipLibCheck": true
14+
}
15+
}

0 commit comments

Comments
 (0)