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
+
1
18
import { decodeEntity } from 'parse-entities/decode-entity.js'
2
19
import { stringifyEntitiesLight } from 'stringify-entities'
3
20
import { visitParents } from 'unist-util-visit-parents'
@@ -11,6 +28,7 @@ const shortcut = /^[^\t\n\r "#'.<=>`}]+$/
11
28
12
29
handleDirective . peek = peekDirective
13
30
31
+ /** @type {FromMarkdownExtension } */
14
32
export const directiveFromMarkdown = {
15
33
canContainEols : [ 'textDirective' ] ,
16
34
enter : {
@@ -52,6 +70,7 @@ export const directiveFromMarkdown = {
52
70
}
53
71
}
54
72
73
+ /** @type {ToMarkdownExtension } */
55
74
export const directiveToMarkdown = {
56
75
unsafe : [
57
76
{
@@ -77,74 +96,104 @@ export const directiveToMarkdown = {
77
96
}
78
97
}
79
98
99
+ /** @type {FromMarkdownHandle } */
80
100
function enterContainer ( token ) {
81
101
enter . call ( this , 'containerDirective' , token )
82
102
}
83
103
104
+ /** @type {FromMarkdownHandle } */
84
105
function enterLeaf ( token ) {
85
106
enter . call ( this , 'leafDirective' , token )
86
107
}
87
108
109
+ /** @type {FromMarkdownHandle } */
88
110
function enterText ( token ) {
89
111
enter . call ( this , 'textDirective' , token )
90
112
}
91
113
114
+ /**
115
+ * @this {ThisParameterType<FromMarkdownHandle>}
116
+ * @param {string } type
117
+ * @param {Parameters<FromMarkdownHandle>[0] } token
118
+ */
92
119
function enter ( type , token ) {
120
+ // @ts -expect-error: custom node.
93
121
this . enter ( { type, name : '' , attributes : { } , children : [ ] } , token )
94
122
}
95
123
124
+ /**
125
+ * @this {ThisParameterType<FromMarkdownHandle>}
126
+ * @param {Parameters<FromMarkdownHandle>[0] } token
127
+ */
96
128
function exitName ( token ) {
97
129
this . stack [ this . stack . length - 1 ] . name = this . sliceSerialize ( token )
98
130
}
99
131
132
+ /** @type {FromMarkdownHandle } */
100
133
function enterContainerLabel ( token ) {
101
134
this . enter (
102
135
{ type : 'paragraph' , data : { directiveLabel : true } , children : [ ] } ,
103
136
token
104
137
)
105
138
}
106
139
140
+ /** @type {FromMarkdownHandle } */
107
141
function exitContainerLabel ( token ) {
108
142
this . exit ( token )
109
143
}
110
144
145
+ /** @type {FromMarkdownHandle } */
111
146
function enterAttributes ( ) {
112
147
this . setData ( 'directiveAttributes' , [ ] )
113
148
this . buffer ( ) // Capture EOLs
114
149
}
115
150
151
+ /** @type {FromMarkdownHandle } */
116
152
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 ) ) ] )
121
157
}
122
158
159
+ /** @type {FromMarkdownHandle } */
123
160
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 ) ) ] )
128
165
}
129
166
167
+ /** @type {FromMarkdownHandle } */
130
168
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 ) )
133
173
}
134
174
175
+ /** @type {FromMarkdownHandle } */
135
176
function exitAttributeName ( token ) {
177
+ /** @type {Array.<[string, string]> } */
178
+ // @ts -expect-error: custom.
179
+ const list = this . getData ( 'directiveAttributes' )
180
+
136
181
// Attribute names in CommonMark are significantly limited, so character
137
182
// references can’t exist.
138
- this . getData ( 'directiveAttributes' ) . push ( [ this . sliceSerialize ( token ) , '' ] )
183
+ list . push ( [ this . sliceSerialize ( token ) , '' ] )
139
184
}
140
185
186
+ /** @type {FromMarkdownHandle } */
141
187
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> } */
143
192
const cleaned = { }
144
193
let index = - 1
145
194
146
- while ( ++ index < attributes . length ) {
147
- const attribute = attributes [ index ]
195
+ while ( ++ index < list . length ) {
196
+ const attribute = list [ index ]
148
197
149
198
if ( attribute [ 0 ] === 'class' && cleaned . class ) {
150
199
cleaned . class += ' ' + attribute [ 1 ]
@@ -158,10 +207,15 @@ function exitAttributes() {
158
207
this . stack [ this . stack . length - 1 ] . attributes = cleaned
159
208
}
160
209
210
+ /** @type {FromMarkdownHandle } */
161
211
function exit ( token ) {
162
212
this . exit ( token )
163
213
}
164
214
215
+ /**
216
+ * @type {ToMarkdownHandle }
217
+ * @param {TextDirective|LeafDirective|ContainerDirective } node
218
+ */
165
219
function handleDirective ( node , _ , context ) {
166
220
const prefix = fence ( node )
167
221
const exit = context . enter ( node . type )
@@ -181,15 +235,23 @@ function handleDirective(node, _, context) {
181
235
return value
182
236
}
183
237
238
+ /** @type {ToMarkdownHandle } */
184
239
function peekDirective ( ) {
185
240
return ':'
186
241
}
187
242
243
+ /**
244
+ * @param {TextDirective|LeafDirective|ContainerDirective } node
245
+ * @param {Context } context
246
+ * @returns {string }
247
+ */
188
248
function label ( node , context ) {
249
+ /** @type {Parent } */
189
250
let label = node
190
251
191
252
if ( node . type === 'containerDirective' ) {
192
253
if ( ! inlineDirectiveLabel ( node ) ) return ''
254
+ // @ts -expect-error: we just asserted it’s a parent.
193
255
label = node . children [ 0 ]
194
256
}
195
257
@@ -201,14 +263,24 @@ function label(node, context) {
201
263
return value ? '[' + value + ']' : ''
202
264
}
203
265
266
+ /**
267
+ * @param {TextDirective|LeafDirective|ContainerDirective } node
268
+ * @param {Context } context
269
+ * @returns {string }
270
+ */
204
271
function attributes ( node , context ) {
205
272
const quote = checkQuote ( context )
206
273
const subset = node . type === 'textDirective' ? [ quote ] : [ quote , '\n' , '\r' ]
207
274
const attrs = node . attributes || { }
275
+ /** @type {Array.<string> } */
208
276
const values = [ ]
277
+ /** @type {string|undefined } */
209
278
let classesFull
279
+ /** @type {string|undefined } */
210
280
let classes
281
+ /** @type {string|undefined } */
211
282
let id
283
+ /** @type {string } */
212
284
let key
213
285
214
286
for ( key in attrs ) {
@@ -217,25 +289,29 @@ function attributes(node, context) {
217
289
attrs [ key ] !== undefined &&
218
290
attrs [ key ] !== null
219
291
) {
220
- let value = String ( attrs [ key ] )
292
+ const value = String ( attrs [ key ] )
221
293
222
294
if ( key === 'id' ) {
223
295
id = shortcut . test ( value ) ? '#' + value : quoted ( 'id' , value )
224
296
} 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 = [ ]
228
302
let index = - 1
229
303
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 ]
233
307
)
234
308
}
235
309
236
310
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 ( '.' ) : ''
239
315
} else {
240
316
values . push ( quoted ( key , value ) )
241
317
}
@@ -256,6 +332,11 @@ function attributes(node, context) {
256
332
257
333
return values . length > 0 ? '{' + values . join ( ' ' ) + '}' : ''
258
334
335
+ /**
336
+ * @param {string } key
337
+ * @param {string } value
338
+ * @returns {string }
339
+ */
259
340
function quoted ( key , value ) {
260
341
return (
261
342
key +
@@ -266,6 +347,11 @@ function attributes(node, context) {
266
347
}
267
348
}
268
349
350
+ /**
351
+ * @param {TextDirective|LeafDirective|ContainerDirective } node
352
+ * @param {Context } context
353
+ * @returns {string }
354
+ */
269
355
function content ( node , context ) {
270
356
return containerFlow (
271
357
inlineDirectiveLabel ( node )
@@ -275,26 +361,43 @@ function content(node, context) {
275
361
)
276
362
}
277
363
364
+ /**
365
+ * @param {TextDirective|LeafDirective|ContainerDirective } node
366
+ * @returns {boolean }
367
+ */
278
368
function inlineDirectiveLabel ( node ) {
279
- return (
369
+ return Boolean (
280
370
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
284
374
)
285
375
}
286
376
377
+ /**
378
+ * @param {string } value
379
+ * @returns {string }
380
+ */
287
381
function decodeLight ( value ) {
288
382
return value . replace (
289
383
/ & ( # ( \d { 1 , 7 } | x [ \d a - f ] { 1 , 6 } ) | [ \d a - z ] { 1 , 31 } ) ; / gi,
290
384
decodeIfPossible
291
385
)
292
386
}
293
387
388
+ /**
389
+ * @param {string } $0
390
+ * @param {string } $1
391
+ * @returns {string }
392
+ */
294
393
function decodeIfPossible ( $0 , $1 ) {
295
394
return decodeEntity ( $1 ) || $0
296
395
}
297
396
397
+ /**
398
+ * @param {TextDirective|LeafDirective|ContainerDirective } node
399
+ * @returns {string }
400
+ */
298
401
function fence ( node ) {
299
402
let size = 0
300
403
@@ -309,7 +412,8 @@ function fence(node) {
309
412
310
413
return ':' . repeat ( size )
311
414
312
- function onvisit ( node , parents ) {
415
+ /** @type {import('unist-util-visit-parents').Visitor<TextDirective|LeafDirective|ContainerDirective> } */
416
+ function onvisit ( _ , parents ) {
313
417
let index = parents . length
314
418
let nesting = 0
315
419
0 commit comments