Skip to content

Commit d16164d

Browse files
committed
Add JSDoc based types
1 parent 3bc2920 commit d16164d

File tree

5 files changed

+220
-60
lines changed

5 files changed

+220
-60
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
coverage/
22
node_modules/
33
.DS_Store
4+
*.d.ts
45
*.log
56
yarn.lock

index.js

+132-28
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
/**
2+
* @typedef {import('mdast-util-from-markdown').Handle} FromMarkdownHandle
3+
* @typedef {import('mdast-util-from-markdown').Extension} FromMarkdownExtension
4+
* @typedef {import('mdast-util-to-markdown/lib/types.js').Node} Node
5+
* @typedef {import('mdast-util-to-markdown/lib/types.js').Parent} Parent
6+
* @typedef {import('mdast-util-to-markdown/lib/types.js').Handle} ToMarkdownHandle
7+
* @typedef {import('mdast-util-to-markdown/lib/types.js').Context} Context
8+
* @typedef {import('mdast-util-to-markdown/lib/types.js').Options} ToMarkdownExtension
9+
*
10+
* @typedef {Record<string, string>} Attributes
11+
* @typedef {{name: string, attributes?: Attributes}} Directive
12+
*
13+
* @typedef {Parent & Directive & {type: 'textDirective', children: Array.<import('mdast').PhrasingContent>}} TextDirective
14+
* @typedef {Parent & Directive & {type: 'leafDirective', children: Array.<import('mdast').PhrasingContent>}} LeafDirective
15+
* @typedef {Parent & Directive & {type: 'containerDirective', children: Array.<import('mdast').BlockContent>}} ContainerDirective
16+
*/
17+
118
import {decodeEntity} from 'parse-entities/decode-entity.js'
219
import {stringifyEntitiesLight} from 'stringify-entities'
320
import {visitParents} from 'unist-util-visit-parents'
@@ -11,6 +28,7 @@ const shortcut = /^[^\t\n\r "#'.<=>`}]+$/
1128

1229
handleDirective.peek = peekDirective
1330

31+
/** @type {FromMarkdownExtension} */
1432
export const directiveFromMarkdown = {
1533
canContainEols: ['textDirective'],
1634
enter: {
@@ -52,6 +70,7 @@ export const directiveFromMarkdown = {
5270
}
5371
}
5472

73+
/** @type {ToMarkdownExtension} */
5574
export const directiveToMarkdown = {
5675
unsafe: [
5776
{
@@ -77,74 +96,104 @@ export const directiveToMarkdown = {
7796
}
7897
}
7998

99+
/** @type {FromMarkdownHandle} */
80100
function enterContainer(token) {
81101
enter.call(this, 'containerDirective', token)
82102
}
83103

104+
/** @type {FromMarkdownHandle} */
84105
function enterLeaf(token) {
85106
enter.call(this, 'leafDirective', token)
86107
}
87108

109+
/** @type {FromMarkdownHandle} */
88110
function enterText(token) {
89111
enter.call(this, 'textDirective', token)
90112
}
91113

114+
/**
115+
* @this {ThisParameterType<FromMarkdownHandle>}
116+
* @param {string} type
117+
* @param {Parameters<FromMarkdownHandle>[0]} token
118+
*/
92119
function enter(type, token) {
120+
// @ts-expect-error: custom node.
93121
this.enter({type, name: '', attributes: {}, children: []}, token)
94122
}
95123

124+
/**
125+
* @this {ThisParameterType<FromMarkdownHandle>}
126+
* @param {Parameters<FromMarkdownHandle>[0]} token
127+
*/
96128
function exitName(token) {
97129
this.stack[this.stack.length - 1].name = this.sliceSerialize(token)
98130
}
99131

132+
/** @type {FromMarkdownHandle} */
100133
function enterContainerLabel(token) {
101134
this.enter(
102135
{type: 'paragraph', data: {directiveLabel: true}, children: []},
103136
token
104137
)
105138
}
106139

140+
/** @type {FromMarkdownHandle} */
107141
function exitContainerLabel(token) {
108142
this.exit(token)
109143
}
110144

145+
/** @type {FromMarkdownHandle} */
111146
function enterAttributes() {
112147
this.setData('directiveAttributes', [])
113148
this.buffer() // Capture EOLs
114149
}
115150

151+
/** @type {FromMarkdownHandle} */
116152
function exitAttributeIdValue(token) {
117-
this.getData('directiveAttributes').push([
118-
'id',
119-
decodeLight(this.sliceSerialize(token))
120-
])
153+
/** @type {Array.<[string, string]>} */
154+
// @ts-expect-error: custom.
155+
const list = this.getData('directiveAttributes')
156+
list.push(['id', decodeLight(this.sliceSerialize(token))])
121157
}
122158

159+
/** @type {FromMarkdownHandle} */
123160
function exitAttributeClassValue(token) {
124-
this.getData('directiveAttributes').push([
125-
'class',
126-
decodeLight(this.sliceSerialize(token))
127-
])
161+
/** @type {Array.<[string, string]>} */
162+
// @ts-expect-error: custom.
163+
const list = this.getData('directiveAttributes')
164+
list.push(['class', decodeLight(this.sliceSerialize(token))])
128165
}
129166

167+
/** @type {FromMarkdownHandle} */
130168
function exitAttributeValue(token) {
131-
const attributes = this.getData('directiveAttributes')
132-
attributes[attributes.length - 1][1] = decodeLight(this.sliceSerialize(token))
169+
/** @type {Array.<[string, string]>} */
170+
// @ts-expect-error: custom.
171+
const list = this.getData('directiveAttributes')
172+
list[list.length - 1][1] = decodeLight(this.sliceSerialize(token))
133173
}
134174

175+
/** @type {FromMarkdownHandle} */
135176
function exitAttributeName(token) {
177+
/** @type {Array.<[string, string]>} */
178+
// @ts-expect-error: custom.
179+
const list = this.getData('directiveAttributes')
180+
136181
// Attribute names in CommonMark are significantly limited, so character
137182
// references can’t exist.
138-
this.getData('directiveAttributes').push([this.sliceSerialize(token), ''])
183+
list.push([this.sliceSerialize(token), ''])
139184
}
140185

186+
/** @type {FromMarkdownHandle} */
141187
function exitAttributes() {
142-
const attributes = this.getData('directiveAttributes')
188+
/** @type {Array.<[string, string]>} */
189+
// @ts-expect-error: custom.
190+
const list = this.getData('directiveAttributes')
191+
/** @type {Record.<string, string>} */
143192
const cleaned = {}
144193
let index = -1
145194

146-
while (++index < attributes.length) {
147-
const attribute = attributes[index]
195+
while (++index < list.length) {
196+
const attribute = list[index]
148197

149198
if (attribute[0] === 'class' && cleaned.class) {
150199
cleaned.class += ' ' + attribute[1]
@@ -158,10 +207,15 @@ function exitAttributes() {
158207
this.stack[this.stack.length - 1].attributes = cleaned
159208
}
160209

210+
/** @type {FromMarkdownHandle} */
161211
function exit(token) {
162212
this.exit(token)
163213
}
164214

215+
/**
216+
* @type {ToMarkdownHandle}
217+
* @param {TextDirective|LeafDirective|ContainerDirective} node
218+
*/
165219
function handleDirective(node, _, context) {
166220
const prefix = fence(node)
167221
const exit = context.enter(node.type)
@@ -181,15 +235,23 @@ function handleDirective(node, _, context) {
181235
return value
182236
}
183237

238+
/** @type {ToMarkdownHandle} */
184239
function peekDirective() {
185240
return ':'
186241
}
187242

243+
/**
244+
* @param {TextDirective|LeafDirective|ContainerDirective} node
245+
* @param {Context} context
246+
* @returns {string}
247+
*/
188248
function label(node, context) {
249+
/** @type {Parent} */
189250
let label = node
190251

191252
if (node.type === 'containerDirective') {
192253
if (!inlineDirectiveLabel(node)) return ''
254+
// @ts-expect-error: we just asserted it’s a parent.
193255
label = node.children[0]
194256
}
195257

@@ -201,14 +263,24 @@ function label(node, context) {
201263
return value ? '[' + value + ']' : ''
202264
}
203265

266+
/**
267+
* @param {TextDirective|LeafDirective|ContainerDirective} node
268+
* @param {Context} context
269+
* @returns {string}
270+
*/
204271
function attributes(node, context) {
205272
const quote = checkQuote(context)
206273
const subset = node.type === 'textDirective' ? [quote] : [quote, '\n', '\r']
207274
const attrs = node.attributes || {}
275+
/** @type {Array.<string>} */
208276
const values = []
277+
/** @type {string|undefined} */
209278
let classesFull
279+
/** @type {string|undefined} */
210280
let classes
281+
/** @type {string|undefined} */
211282
let id
283+
/** @type {string} */
212284
let key
213285

214286
for (key in attrs) {
@@ -217,25 +289,29 @@ function attributes(node, context) {
217289
attrs[key] !== undefined &&
218290
attrs[key] !== null
219291
) {
220-
let value = String(attrs[key])
292+
const value = String(attrs[key])
221293

222294
if (key === 'id') {
223295
id = shortcut.test(value) ? '#' + value : quoted('id', value)
224296
} else if (key === 'class') {
225-
value = value.split(/[\t\n\r ]+/g)
226-
classesFull = []
227-
classes = []
297+
const list = value.split(/[\t\n\r ]+/g)
298+
/** @type {Array.<string>} */
299+
const classesFullList = []
300+
/** @type {Array.<string>} */
301+
const classesList = []
228302
let index = -1
229303

230-
while (++index < value.length) {
231-
;(shortcut.test(value[index]) ? classes : classesFull).push(
232-
value[index]
304+
while (++index < list.length) {
305+
;(shortcut.test(list[index]) ? classesList : classesFullList).push(
306+
list[index]
233307
)
234308
}
235309

236310
classesFull =
237-
classesFull.length > 0 ? quoted('class', classesFull.join(' ')) : ''
238-
classes = classes.length > 0 ? '.' + classes.join('.') : ''
311+
classesFullList.length > 0
312+
? quoted('class', classesFullList.join(' '))
313+
: ''
314+
classes = classesList.length > 0 ? '.' + classesList.join('.') : ''
239315
} else {
240316
values.push(quoted(key, value))
241317
}
@@ -256,6 +332,11 @@ function attributes(node, context) {
256332

257333
return values.length > 0 ? '{' + values.join(' ') + '}' : ''
258334

335+
/**
336+
* @param {string} key
337+
* @param {string} value
338+
* @returns {string}
339+
*/
259340
function quoted(key, value) {
260341
return (
261342
key +
@@ -266,6 +347,11 @@ function attributes(node, context) {
266347
}
267348
}
268349

350+
/**
351+
* @param {TextDirective|LeafDirective|ContainerDirective} node
352+
* @param {Context} context
353+
* @returns {string}
354+
*/
269355
function content(node, context) {
270356
return containerFlow(
271357
inlineDirectiveLabel(node)
@@ -275,26 +361,43 @@ function content(node, context) {
275361
)
276362
}
277363

364+
/**
365+
* @param {TextDirective|LeafDirective|ContainerDirective} node
366+
* @returns {boolean}
367+
*/
278368
function inlineDirectiveLabel(node) {
279-
return (
369+
return Boolean(
280370
node.children &&
281-
node.children[0] &&
282-
node.children[0].data &&
283-
node.children[0].data.directiveLabel
371+
node.children[0] &&
372+
node.children[0].data &&
373+
node.children[0].data.directiveLabel
284374
)
285375
}
286376

377+
/**
378+
* @param {string} value
379+
* @returns {string}
380+
*/
287381
function decodeLight(value) {
288382
return value.replace(
289383
/&(#(\d{1,7}|x[\da-f]{1,6})|[\da-z]{1,31});/gi,
290384
decodeIfPossible
291385
)
292386
}
293387

388+
/**
389+
* @param {string} $0
390+
* @param {string} $1
391+
* @returns {string}
392+
*/
294393
function decodeIfPossible($0, $1) {
295394
return decodeEntity($1) || $0
296395
}
297396

397+
/**
398+
* @param {TextDirective|LeafDirective|ContainerDirective} node
399+
* @returns {string}
400+
*/
298401
function fence(node) {
299402
let size = 0
300403

@@ -309,7 +412,8 @@ function fence(node) {
309412

310413
return ':'.repeat(size)
311414

312-
function onvisit(node, parents) {
415+
/** @type {import('unist-util-visit-parents').Visitor<TextDirective|LeafDirective|ContainerDirective>} */
416+
function onvisit(_, parents) {
313417
let index = parents.length
314418
let nesting = 0
315419

0 commit comments

Comments
 (0)