Skip to content
This repository was archived by the owner on Aug 9, 2023. It is now read-only.

Commit bc205bc

Browse files
committed
Add JSDoc based types
1 parent 442274c commit bc205bc

File tree

9 files changed

+323
-220
lines changed

9 files changed

+323
-220
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.DS_Store
2+
*.d.ts
23
*.log
34
coverage/
45
node_modules/

index.js

Lines changed: 145 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,32 @@
1+
/**
2+
* @typedef {import('hast').Element} Element
3+
* @typedef {import('hast').Root} Root
4+
* @typedef {import('hast').Text} Text
5+
*
6+
* @typedef {import('unist-util-is').AssertPredicate<Element>} AssertElement
7+
* @typedef {import('unist-util-is').AssertPredicate<Text>} AssertText
8+
* @typedef {import('unist-util-is').AssertPredicate<Root>} AssertRoot
9+
*
10+
* @callback CreateElementLike
11+
* @param {string} name
12+
* @param {Object<string, any>} [attributes]
13+
* @param {Array.<string|any>} [children]
14+
* @returns {any}
15+
*
16+
* @typedef Context
17+
* @property {html|svg} schema
18+
* @property {string|null} prefix
19+
* @property {number} key
20+
* @property {boolean} react
21+
* @property {boolean} vue
22+
* @property {boolean} vdom
23+
* @property {boolean} hyperscript
24+
*
25+
* @typedef Options
26+
* @property {string|null} [prefix]
27+
* @property {'html'|'svg'} [space]
28+
*/
29+
130
import {html, svg, find, hastToReact} from 'property-information'
231
import {stringify as spaces} from 'space-separated-tokens'
332
import {stringify as commas} from 'comma-separated-tokens'
@@ -7,52 +36,76 @@ import {convert} from 'unist-util-is'
736

837
var own = {}.hasOwnProperty
938

39+
/** @type {AssertRoot} */
40+
// @ts-ignore it’s correct.
1041
var root = convert('root')
42+
/** @type {AssertElement} */
43+
// @ts-ignore it’s correct.
1144
var element = convert('element')
45+
/** @type {AssertText} */
46+
// @ts-ignore it’s correct.
1247
var text = convert('text')
1348

14-
export function toH(h, node, options) {
15-
var settings = options || {}
49+
/**
50+
* @template {CreateElementLike} H
51+
* @param {H} h
52+
* @param {Element|Root} tree
53+
* @param {string|boolean|Options} [options]
54+
* @returns {ReturnType<H>}
55+
*/
56+
export function toH(h, tree, options) {
57+
if (typeof h !== 'function') {
58+
throw new TypeError('h is not a function')
59+
}
60+
1661
var r = react(h)
1762
var v = vue(h)
1863
var vd = vdom(h)
64+
/** @type {string|boolean} */
1965
var prefix
66+
/** @type {Element} */
67+
var node
2068

21-
if (typeof h !== 'function') {
22-
throw new TypeError('h is not a function')
23-
}
24-
25-
if (typeof settings === 'string' || typeof settings === 'boolean') {
26-
prefix = settings
27-
settings = {}
69+
if (typeof options === 'string' || typeof options === 'boolean') {
70+
prefix = options
71+
options = {}
2872
} else {
29-
prefix = settings.prefix
73+
if (!options) options = {}
74+
prefix = options.prefix
3075
}
3176

32-
if (root(node)) {
77+
if (root(tree)) {
78+
// @ts-ignore Allow `doctypes` in there, we’ll filter them out later.
3379
node =
34-
node.children.length === 1 && element(node.children[0])
35-
? node.children[0]
80+
tree.children.length === 1 && element(tree.children[0])
81+
? tree.children[0]
3682
: {
3783
type: 'element',
3884
tagName: 'div',
3985
properties: {},
40-
children: node.children
86+
children: tree.children
4187
}
42-
} else if (!element(node)) {
88+
} else if (element(tree)) {
89+
node = tree
90+
} else {
4391
throw new Error(
44-
'Expected root or element, not `' + ((node && node.type) || node) + '`'
92+
// @ts-ignore runtime.
93+
'Expected root or element, not `' + ((tree && tree.type) || tree) + '`'
4594
)
4695
}
4796

4897
return transform(h, node, {
49-
schema: settings.space === 'svg' ? svg : html,
98+
schema: options.space === 'svg' ? svg : html,
5099
prefix:
51100
prefix === undefined || prefix === null
52101
? r || v || vd
53102
? 'h-'
54103
: null
55-
: prefix,
104+
: typeof prefix === 'string'
105+
? prefix
106+
: prefix
107+
? 'h-'
108+
: null,
56109
key: 0,
57110
react: r,
58111
vue: v,
@@ -61,15 +114,26 @@ export function toH(h, node, options) {
61114
})
62115
}
63116

64-
// Transform a hast node through a hyperscript interface to *anything*!
117+
/**
118+
* Transform a hast node through a hyperscript interface to *anything*!
119+
*
120+
* @template {CreateElementLike} H
121+
* @param {H} h
122+
* @param {Element} node
123+
* @param {Context} ctx
124+
*/
65125
function transform(h, node, ctx) {
66126
var parentSchema = ctx.schema
67127
var schema = parentSchema
68128
var name = node.tagName
129+
/** @type {Object.<string, unknown>} */
69130
var attributes = {}
131+
/** @type {Array.<ReturnType<H>|string>} */
70132
var nodes = []
71133
var index = -1
134+
/** @type {string} */
72135
var key
136+
/** @type {Element['children'][number]} */
73137
var value
74138

75139
if (parentSchema.space === 'html' && name.toLowerCase() === 'svg') {
@@ -118,9 +182,17 @@ function transform(h, node, ctx) {
118182
: h.call(node, name, attributes)
119183
}
120184

185+
/**
186+
* @param {Object.<string, unknown>} props
187+
* @param {string} prop
188+
* @param {unknown} value
189+
* @param {Context} ctx
190+
* @param {string} name
191+
*/
121192
// eslint-disable-next-line complexity, max-params
122193
function addAttribute(props, prop, value, ctx, name) {
123194
var info = find(ctx.schema, prop)
195+
/** @type {string} */
124196
var subprop
125197

126198
// Ignore nullish and `NaN` values.
@@ -135,7 +207,7 @@ function addAttribute(props, prop, value, ctx, name) {
135207
return
136208
}
137209

138-
if (value && typeof value === 'object' && 'length' in value) {
210+
if (Array.isArray(value)) {
139211
// Accept `array`.
140212
// Most props are space-separated.
141213
value = info.commaSeparated ? commas(value) : spaces(value)
@@ -175,32 +247,67 @@ function addAttribute(props, prop, value, ctx, name) {
175247
}
176248
}
177249

178-
// Check if `h` is `react.createElement`.
250+
/**
251+
* Check if `h` is `react.createElement`.
252+
*
253+
* @param {CreateElementLike} h
254+
* @returns {boolean}
255+
*/
179256
function react(h) {
180-
var node = h && h('div')
257+
/** @type {unknown} */
258+
var node = h('div')
181259
return Boolean(
182260
node &&
261+
// @ts-ignore Looks like a React node.
183262
('_owner' in node || '_store' in node) &&
263+
// @ts-ignore Looks like a React node.
184264
(node.key === undefined || node.key === null)
185265
)
186266
}
187267

188-
// Check if `h` is `hyperscript`.
268+
/**
269+
* Check if `h` is `hyperscript`.
270+
*
271+
* @param {CreateElementLike} h
272+
* @returns {boolean}
273+
*/
189274
function hyperscript(h) {
190-
return Boolean(h && h.context && h.cleanup)
275+
return 'context' in h && 'cleanup' in h
191276
}
192277

193-
// Check if `h` is `virtual-dom/h`.
278+
/**
279+
* Check if `h` is `virtual-dom/h`.
280+
*
281+
* @param {CreateElementLike} h
282+
* @returns {boolean}
283+
*/
194284
function vdom(h) {
195-
return h && h('div').type === 'VirtualNode'
285+
/** @type {unknown} */
286+
var node = h('div')
287+
// @ts-ignore Looks like a vnode.
288+
return node.type === 'VirtualNode'
196289
}
197290

291+
/**
292+
* Check if `h` is Vue.
293+
*
294+
* @param {CreateElementLike} h
295+
* @returns {boolean}
296+
*/
198297
function vue(h) {
199-
var node = h && h('div')
298+
/** @type {unknown} */
299+
var node = h('div')
300+
// @ts-ignore Looks like a Vue node.
200301
return Boolean(node && node.context && node.context._isVue)
201302
}
202303

304+
/**
305+
* @param {string} value
306+
* @param {string} tagName
307+
* @returns {Object.<string, string>}
308+
*/
203309
function parseStyle(value, tagName) {
310+
/** @type {Object.<string, string>} */
204311
var result = {}
205312

206313
try {
@@ -213,12 +320,22 @@ function parseStyle(value, tagName) {
213320

214321
return result
215322

323+
/**
324+
* @param {string} name
325+
* @param {string} value
326+
* @returns {void}
327+
*/
216328
function iterator(name, value) {
217329
if (name.slice(0, 4) === '-ms-') name = 'ms-' + name.slice(4)
218330
result[name.replace(/-([a-z])/g, styleReplacer)] = value
219331
}
220332
}
221333

222-
function styleReplacer($0, $1) {
334+
/**
335+
* @param {string} _
336+
* @param {string} $1
337+
* @returns {string}
338+
*/
339+
function styleReplacer(_, $1) {
223340
return $1.toUpperCase()
224341
}

package.json

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@
3535
"sideEffects": false,
3636
"type": "module",
3737
"main": "index.js",
38-
"types": "types/index.d.ts",
38+
"types": "index.d.ts",
3939
"files": [
40-
"types/index.d.ts",
40+
"index.d.ts",
4141
"index.js"
4242
],
4343
"dependencies": {
44-
"@types/unist": "^2.0.3",
44+
"@types/unist": "^2.0.0",
4545
"comma-separated-tokens": "^2.0.0",
4646
"property-information": "^6.0.0",
4747
"space-separated-tokens": "^2.0.0",
@@ -52,6 +52,8 @@
5252
"devDependencies": {
5353
"@types/hyperscript": "0.0.4",
5454
"@types/react": "^17.0.0",
55+
"@types/react-dom": "^17.0.0",
56+
"@types/tape": "^4.0.0",
5557
"@types/virtual-dom": "^2.0.0",
5658
"c8": "^7.0.0",
5759
"hyperscript": "^2.0.0",
@@ -61,7 +63,10 @@
6163
"rehype": "^11.0.0",
6264
"remark-cli": "^9.0.0",
6365
"remark-preset-wooorm": "^8.0.0",
66+
"rimraf": "^3.0.0",
6467
"tape": "^5.0.0",
68+
"type-coverage": "^2.0.0",
69+
"typescript": "^4.0.0",
6570
"unist-builder": "^3.0.0",
6671
"vdom-to-html": "^2.0.0",
6772
"virtual-dom": "^2.0.0",
@@ -70,10 +75,12 @@
7075
"xo": "^0.39.0"
7176
},
7277
"scripts": {
78+
"prepack": "npm run build && npm run format",
79+
"build": "rimraf \"*.d.ts\" && tsc && type-coverage",
7380
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
7481
"test-api": "node test.js",
7582
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node test.js",
76-
"test": "npm run format && npm run test-coverage"
83+
"test": "npm run build && npm run format && npm run test-coverage"
7784
},
7885
"prettier": {
7986
"tabWidth": 2,
@@ -88,14 +95,20 @@
8895
"rules": {
8996
"no-var": "off",
9097
"prefer-arrow-callback": "off"
91-
},
92-
"ignore": [
93-
"types/"
94-
]
98+
}
9599
},
96100
"remarkConfig": {
97101
"plugins": [
98102
"preset-wooorm"
99103
]
104+
},
105+
"typeCoverage": {
106+
"atLeast": 100,
107+
"detail": true,
108+
"strict": true,
109+
"ignoreCatch": true,
110+
"ignoreFiles": [
111+
"index.d.ts"
112+
]
100113
}
101114
}

0 commit comments

Comments
 (0)