1
1
/**
2
- * @import {Directives, LeafDirective, TextDirective} from 'mdast-util-directive'
2
+ * @import {Directives, LeafDirective, TextDirective, ToMarkdownOptions } from 'mdast-util-directive'
3
3
* @import {
4
4
* CompileContext,
5
5
* Extension as FromMarkdownExtension,
@@ -22,9 +22,10 @@ import {visitParents} from 'unist-util-visit-parents'
22
22
23
23
const own = { } . hasOwnProperty
24
24
25
- const shortcut = / ^ [ ^ \t \n \r " # ' . < = > ` } ] + $ /
25
+ /** @type {Readonly<ToMarkdownOptions> } */
26
+ const emptyOptions = { }
26
27
27
- handleDirective . peek = peekDirective
28
+ const shortcut = / ^ [ ^ \t \n \r " # ' . < = > ` } ] + $ /
28
29
29
30
/**
30
31
* Create an extension for `mdast-util-from-markdown` to enable directives in
@@ -80,10 +81,16 @@ export function directiveFromMarkdown() {
80
81
* Create an extension for `mdast-util-to-markdown` to enable directives in
81
82
* markdown.
82
83
*
84
+ * @param {Readonly<ToMarkdownOptions> | null | undefined } [options]
85
+ * Configuration (optional).
83
86
* @returns {ToMarkdownExtension }
84
87
* Extension for `mdast-util-to-markdown` to enable directives.
85
88
*/
86
- export function directiveToMarkdown ( ) {
89
+ export function directiveToMarkdown ( options ) {
90
+ const settings = options || emptyOptions
91
+
92
+ handleDirective . peek = peekDirective
93
+
87
94
return {
88
95
handlers : {
89
96
containerDirective : handleDirective ,
@@ -108,6 +115,156 @@ export function directiveToMarkdown() {
108
115
{ atBreak : true , character : ':' , after : ':' }
109
116
]
110
117
}
118
+
119
+ /**
120
+ * @type {ToMarkdownHandle }
121
+ * @param {Directives } node
122
+ */
123
+ function handleDirective ( node , _ , state , info ) {
124
+ const tracker = state . createTracker ( info )
125
+ const sequence = fence ( node )
126
+ const exit = state . enter ( node . type )
127
+ let value = tracker . move ( sequence + ( node . name || '' ) )
128
+ /** @type {LeafDirective | Paragraph | TextDirective | undefined } */
129
+ let label
130
+
131
+ if ( node . type === 'containerDirective' ) {
132
+ const head = ( node . children || [ ] ) [ 0 ]
133
+ label = inlineDirectiveLabel ( head ) ? head : undefined
134
+ } else {
135
+ label = node
136
+ }
137
+
138
+ if ( label && label . children && label . children . length > 0 ) {
139
+ const exit = state . enter ( 'label' )
140
+ /** @type {ConstructName } */
141
+ const labelType = `${ node . type } Label`
142
+ const subexit = state . enter ( labelType )
143
+ value += tracker . move ( '[' )
144
+ value += tracker . move (
145
+ state . containerPhrasing ( label , {
146
+ ...tracker . current ( ) ,
147
+ before : value ,
148
+ after : ']'
149
+ } )
150
+ )
151
+ value += tracker . move ( ']' )
152
+ subexit ( )
153
+ exit ( )
154
+ }
155
+
156
+ value += tracker . move ( attributes ( node , state ) )
157
+
158
+ if ( node . type === 'containerDirective' ) {
159
+ const head = ( node . children || [ ] ) [ 0 ]
160
+ let shallow = node
161
+
162
+ if ( inlineDirectiveLabel ( head ) ) {
163
+ shallow = Object . assign ( { } , node , { children : node . children . slice ( 1 ) } )
164
+ }
165
+
166
+ if ( shallow && shallow . children && shallow . children . length > 0 ) {
167
+ value += tracker . move ( '\n' )
168
+ value += tracker . move ( state . containerFlow ( shallow , tracker . current ( ) ) )
169
+ }
170
+
171
+ value += tracker . move ( '\n' + sequence )
172
+ }
173
+
174
+ exit ( )
175
+ return value
176
+ }
177
+
178
+ /**
179
+ * @param {Directives } node
180
+ * @param {State } state
181
+ * @returns {string }
182
+ */
183
+ function attributes ( node , state ) {
184
+ // If the alternative is less common than `quote`, switch.
185
+ const appliedQuote = settings . quote || state . options . quote || '"'
186
+ const subset =
187
+ node . type === 'textDirective'
188
+ ? [ appliedQuote ]
189
+ : [ appliedQuote , '\n' , '\r' ]
190
+ const attributes = node . attributes || { }
191
+ /** @type {Array<string> } */
192
+ const values = [ ]
193
+ /** @type {string | undefined } */
194
+ let classesFull
195
+ /** @type {string | undefined } */
196
+ let classes
197
+ /** @type {string | undefined } */
198
+ let id
199
+ /** @type {string } */
200
+ let key
201
+
202
+ for ( key in attributes ) {
203
+ if (
204
+ own . call ( attributes , key ) &&
205
+ attributes [ key ] !== undefined &&
206
+ attributes [ key ] !== null
207
+ ) {
208
+ const value = String ( attributes [ key ] )
209
+
210
+ if ( key === 'id' ) {
211
+ id = shortcut . test ( value ) ? '#' + value : quoted ( 'id' , value )
212
+ } else if ( key === 'class' ) {
213
+ const list = value . split ( / [ \t \n \r ] + / g)
214
+ /** @type {Array<string> } */
215
+ const classesFullList = [ ]
216
+ /** @type {Array<string> } */
217
+ const classesList = [ ]
218
+ let index = - 1
219
+
220
+ while ( ++ index < list . length ) {
221
+ ; ( shortcut . test ( list [ index ] ) ? classesList : classesFullList ) . push (
222
+ list [ index ]
223
+ )
224
+ }
225
+
226
+ classesFull =
227
+ classesFullList . length > 0
228
+ ? quoted ( 'class' , classesFullList . join ( ' ' ) )
229
+ : ''
230
+ classes = classesList . length > 0 ? '.' + classesList . join ( '.' ) : ''
231
+ } else {
232
+ values . push ( quoted ( key , value ) )
233
+ }
234
+ }
235
+ }
236
+
237
+ if ( classesFull ) {
238
+ values . unshift ( classesFull )
239
+ }
240
+
241
+ if ( classes ) {
242
+ values . unshift ( classes )
243
+ }
244
+
245
+ if ( id ) {
246
+ values . unshift ( id )
247
+ }
248
+
249
+ return values . length > 0 ? '{' + values . join ( ' ' ) + '}' : ''
250
+
251
+ /**
252
+ * @param {string } key
253
+ * @param {string } value
254
+ * @returns {string }
255
+ */
256
+ function quoted ( key , value ) {
257
+ return (
258
+ key +
259
+ ( value
260
+ ? '=' +
261
+ appliedQuote +
262
+ stringifyEntitiesLight ( value , { subset} ) +
263
+ appliedQuote
264
+ : '' )
265
+ )
266
+ }
267
+ }
111
268
}
112
269
113
270
/**
@@ -276,154 +433,11 @@ function exit(token) {
276
433
this . exit ( token )
277
434
}
278
435
279
- /**
280
- * @type {ToMarkdownHandle }
281
- * @param {Directives } node
282
- */
283
- function handleDirective ( node , _ , state , info ) {
284
- const tracker = state . createTracker ( info )
285
- const sequence = fence ( node )
286
- const exit = state . enter ( node . type )
287
- let value = tracker . move ( sequence + ( node . name || '' ) )
288
- /** @type {LeafDirective | Paragraph | TextDirective | undefined } */
289
- let label
290
-
291
- if ( node . type === 'containerDirective' ) {
292
- const head = ( node . children || [ ] ) [ 0 ]
293
- label = inlineDirectiveLabel ( head ) ? head : undefined
294
- } else {
295
- label = node
296
- }
297
-
298
- if ( label && label . children && label . children . length > 0 ) {
299
- const exit = state . enter ( 'label' )
300
- /** @type {ConstructName } */
301
- const labelType = `${ node . type } Label`
302
- const subexit = state . enter ( labelType )
303
- value += tracker . move ( '[' )
304
- value += tracker . move (
305
- state . containerPhrasing ( label , {
306
- ...tracker . current ( ) ,
307
- before : value ,
308
- after : ']'
309
- } )
310
- )
311
- value += tracker . move ( ']' )
312
- subexit ( )
313
- exit ( )
314
- }
315
-
316
- value += tracker . move ( attributes ( node , state ) )
317
-
318
- if ( node . type === 'containerDirective' ) {
319
- const head = ( node . children || [ ] ) [ 0 ]
320
- let shallow = node
321
-
322
- if ( inlineDirectiveLabel ( head ) ) {
323
- shallow = Object . assign ( { } , node , { children : node . children . slice ( 1 ) } )
324
- }
325
-
326
- if ( shallow && shallow . children && shallow . children . length > 0 ) {
327
- value += tracker . move ( '\n' )
328
- value += tracker . move ( state . containerFlow ( shallow , tracker . current ( ) ) )
329
- }
330
-
331
- value += tracker . move ( '\n' + sequence )
332
- }
333
-
334
- exit ( )
335
- return value
336
- }
337
-
338
436
/** @type {ToMarkdownHandle } */
339
437
function peekDirective ( ) {
340
438
return ':'
341
439
}
342
440
343
- /**
344
- * @param {Directives } node
345
- * @param {State } state
346
- * @returns {string }
347
- */
348
- function attributes ( node , state ) {
349
- const quote = state . options . quote || '"'
350
- const subset = node . type === 'textDirective' ? [ quote ] : [ quote , '\n' , '\r' ]
351
- const attributes = node . attributes || { }
352
- /** @type {Array<string> } */
353
- const values = [ ]
354
- /** @type {string | undefined } */
355
- let classesFull
356
- /** @type {string | undefined } */
357
- let classes
358
- /** @type {string | undefined } */
359
- let id
360
- /** @type {string } */
361
- let key
362
-
363
- for ( key in attributes ) {
364
- if (
365
- own . call ( attributes , key ) &&
366
- attributes [ key ] !== undefined &&
367
- attributes [ key ] !== null
368
- ) {
369
- const value = String ( attributes [ key ] )
370
-
371
- if ( key === 'id' ) {
372
- id = shortcut . test ( value ) ? '#' + value : quoted ( 'id' , value )
373
- } else if ( key === 'class' ) {
374
- const list = value . split ( / [ \t \n \r ] + / g)
375
- /** @type {Array<string> } */
376
- const classesFullList = [ ]
377
- /** @type {Array<string> } */
378
- const classesList = [ ]
379
- let index = - 1
380
-
381
- while ( ++ index < list . length ) {
382
- ; ( shortcut . test ( list [ index ] ) ? classesList : classesFullList ) . push (
383
- list [ index ]
384
- )
385
- }
386
-
387
- classesFull =
388
- classesFullList . length > 0
389
- ? quoted ( 'class' , classesFullList . join ( ' ' ) )
390
- : ''
391
- classes = classesList . length > 0 ? '.' + classesList . join ( '.' ) : ''
392
- } else {
393
- values . push ( quoted ( key , value ) )
394
- }
395
- }
396
- }
397
-
398
- if ( classesFull ) {
399
- values . unshift ( classesFull )
400
- }
401
-
402
- if ( classes ) {
403
- values . unshift ( classes )
404
- }
405
-
406
- if ( id ) {
407
- values . unshift ( id )
408
- }
409
-
410
- return values . length > 0 ? '{' + values . join ( ' ' ) + '}' : ''
411
-
412
- /**
413
- * @param {string } key
414
- * @param {string } value
415
- * @returns {string }
416
- */
417
- function quoted ( key , value ) {
418
- return (
419
- key +
420
- ( value
421
- ? '=' + quote + stringifyEntitiesLight ( value , { subset} ) + quote
422
- : '' )
423
- )
424
- }
425
- }
426
-
427
441
/**
428
442
* @param {Nodes } node
429
443
* @returns {node is Paragraph & {data: {directiveLabel: true}} }
0 commit comments