8
8
* @typedef {import('mdast-util-from-markdown').OnEnterError } OnEnterError
9
9
* @typedef {import('mdast-util-from-markdown').OnExitError } OnExitError
10
10
*
11
- * @typedef {import('mdast-util-to-markdown').Options } ToMarkdownExtension
12
11
* @typedef {import('mdast-util-to-markdown').Handle } ToMarkdownHandle
13
- * @typedef {import('mdast-util-to-markdown').Map } ToMarkdownMap
12
+ * @typedef {import('mdast-util-to-markdown').Options } ToMarkdownExtension
13
+ * @typedef {import('mdast-util-to-markdown').State } State
14
+ * @typedef {import('mdast-util-to-markdown').Tracker } Tracker
14
15
*
15
16
* @typedef {import('../index.js').MdxJsxAttributeValueExpression } MdxJsxAttributeValueExpression
16
17
* @typedef {import('../index.js').MdxJsxAttribute } MdxJsxAttribute
@@ -61,13 +62,14 @@ import {parseEntities} from 'parse-entities'
61
62
import { stringifyPosition } from 'unist-util-stringify-position'
62
63
import { VFileMessage } from 'vfile-message'
63
64
import { stringifyEntitiesLight } from 'stringify-entities'
64
- import { containerFlow } from 'mdast-util-to-markdown/lib/util/container-flow.js'
65
65
import { containerPhrasing } from 'mdast-util-to-markdown/lib/util/container-phrasing.js'
66
66
import { indentLines } from 'mdast-util-to-markdown/lib/util/indent-lines.js'
67
67
import { track } from 'mdast-util-to-markdown/lib/util/track.js'
68
68
69
69
// To do: next major: use `state`, use utilities from state, rename `safeOptions` to `info`.
70
70
71
+ const indent = ' '
72
+
71
73
/**
72
74
* Create an extension for `mdast-util-from-markdown` to enable MDX JSX.
73
75
*
@@ -521,21 +523,28 @@ export function mdxJsxToMarkdown(options) {
521
523
*/
522
524
// eslint-disable-next-line complexity
523
525
function mdxElement ( node , _ , context , safeOptions ) {
524
- const tracker = track ( safeOptions )
525
- const selfClosing =
526
- node . name && ( ! node . children || node . children . length === 0 )
527
- const exit = context . enter ( node . type )
528
- let index = - 1
526
+ const selfClosing = node . name
527
+ ? ! node . children || node . children . length === 0
528
+ : false
529
+ const depth = inferDepth ( context )
530
+ const currentIndent = createIndent ( depth )
531
+ const trackerOneLine = track ( safeOptions )
532
+ const trackerMultiLine = track ( safeOptions )
529
533
/** @type {Array<string> } */
530
534
const serializedAttributes = [ ]
531
- let value = tracker . move ( '<' + ( node . name || '' ) )
535
+ const prefix = currentIndent + '<' + ( node . name || '' )
536
+ const exit = context . enter ( node . type )
537
+
538
+ trackerOneLine . move ( prefix )
539
+ trackerMultiLine . move ( prefix )
532
540
533
541
// None.
534
542
if ( node . attributes && node . attributes . length > 0 ) {
535
543
if ( ! node . name ) {
536
544
throw new Error ( 'Cannot serialize fragment w/ attributes' )
537
545
}
538
546
547
+ let index = - 1
539
548
while ( ++ index < node . attributes . length ) {
540
549
const attribute = node . attributes [ index ]
541
550
/** @type {string } */
@@ -585,7 +594,7 @@ export function mdxJsxToMarkdown(options) {
585
594
// Including a line ending (expressions).
586
595
( / \r ? \n | \r / . test ( attributesOnOneLine ) ||
587
596
// Current position (including `<tag`).
588
- tracker . current ( ) . now . column +
597
+ trackerOneLine . current ( ) . now . column +
589
598
// -1 because columns, +1 for ` ` before attributes.
590
599
// Attributes joined by spaces.
591
600
attributesOnOneLine . length +
@@ -596,18 +605,28 @@ export function mdxJsxToMarkdown(options) {
596
605
attributesOnTheirOwnLine = true
597
606
}
598
607
608
+ let tracker = trackerOneLine
609
+ let value = prefix
610
+
599
611
if ( attributesOnTheirOwnLine ) {
612
+ tracker = trackerMultiLine
613
+
614
+ let index = - 1
615
+
616
+ while ( ++ index < serializedAttributes . length ) {
617
+ // Only indent first line of of attributes, we can’t indent attribute
618
+ // values.
619
+ serializedAttributes [ index ] =
620
+ currentIndent + indent + serializedAttributes [ index ]
621
+ }
622
+
600
623
value += tracker . move (
601
- '\n' + indentLines ( serializedAttributes . join ( '\n' ) , map )
624
+ '\n' + serializedAttributes . join ( '\n' ) + '\n' + currentIndent
602
625
)
603
626
} else if ( attributesOnOneLine ) {
604
627
value += tracker . move ( ' ' + attributesOnOneLine )
605
628
}
606
629
607
- if ( attributesOnTheirOwnLine ) {
608
- value += tracker . move ( '\n' )
609
- }
610
-
611
630
if ( selfClosing ) {
612
631
value += tracker . move (
613
632
( tightSelfClosing || attributesOnTheirOwnLine ? '' : ' ' ) + '/'
@@ -617,41 +636,118 @@ export function mdxJsxToMarkdown(options) {
617
636
value += tracker . move ( '>' )
618
637
619
638
if ( node . children && node . children . length > 0 ) {
620
- if ( node . type === 'mdxJsxFlowElement' ) {
621
- tracker . shift ( 2 )
622
- value += tracker . move ( '\n' )
623
- value += tracker . move (
624
- indentLines ( containerFlow ( node , context , tracker . current ( ) ) , map )
625
- )
626
- value += tracker . move ( '\n' )
627
- } else {
639
+ if ( node . type === 'mdxJsxTextElement' ) {
628
640
value += tracker . move (
629
641
containerPhrasing ( node , context , {
630
642
...tracker . current ( ) ,
631
- before : '< ' ,
632
- after : '> '
643
+ before : '> ' ,
644
+ after : '< '
633
645
} )
634
646
)
647
+ } else {
648
+ tracker . shift ( 2 )
649
+ value += tracker . move ( '\n' )
650
+ value += tracker . move ( containerFlow ( node , context , tracker . current ( ) ) )
651
+ value += tracker . move ( '\n' )
635
652
}
636
653
}
637
654
638
655
if ( ! selfClosing ) {
639
- value += tracker . move ( '</' + ( node . name || '' ) + '>' )
656
+ value += tracker . move ( currentIndent + '</' + ( node . name || '' ) + '>' )
640
657
}
641
658
642
659
exit ( )
643
660
return value
644
661
}
662
+ }
663
+
664
+ // Modified copy of:
665
+ // <https://github.com/syntax-tree/mdast-util-to-markdown/blob/a381cbc/lib/util/container-flow.js>.
666
+ //
667
+ // To do: add `indent` support to `mdast-util-to-markdown`.
668
+ // As indents are only used for JSX, it’s fine for now, but perhaps better
669
+ // there.
670
+ /**
671
+ * @param {MdxJsxFlowElement } parent
672
+ * Parent of flow nodes.
673
+ * @param {State } state
674
+ * Info passed around about the current state.
675
+ * @param {ReturnType<Tracker['current']> } info
676
+ * Info on where we are in the document we are generating.
677
+ * @returns {string }
678
+ * Serialized children, joined by (blank) lines.
679
+ */
680
+ function containerFlow ( parent , state , info ) {
681
+ const indexStack = state . indexStack
682
+ const children = parent . children
683
+ const tracker = state . createTracker ( info )
684
+ const currentIndent = createIndent ( inferDepth ( state ) )
685
+ /** @type {Array<string> } */
686
+ const results = [ ]
687
+ let index = - 1
688
+
689
+ indexStack . push ( - 1 )
690
+
691
+ while ( ++ index < children . length ) {
692
+ const child = children [ index ]
645
693
646
- /** @type {ToMarkdownMap } */
647
- function map ( line , _ , blank ) {
648
- return ( blank ? '' : ' ' ) + line
694
+ indexStack [ indexStack . length - 1 ] = index
695
+
696
+ const childInfo = { before : '\n' , after : '\n' , ...tracker . current ( ) }
697
+
698
+ const result = state . handle ( child , parent , state , childInfo )
699
+
700
+ const serializedChild =
701
+ child . type === 'mdxJsxFlowElement'
702
+ ? result
703
+ : indentLines ( result , function ( line , _ , blank ) {
704
+ return ( blank ? '' : currentIndent ) + line
705
+ } )
706
+
707
+ results . push ( tracker . move ( serializedChild ) )
708
+
709
+ if ( child . type !== 'list' ) {
710
+ state . bulletLastUsed = undefined
711
+ }
712
+
713
+ if ( index < children . length - 1 ) {
714
+ results . push ( tracker . move ( '\n\n' ) )
715
+ }
649
716
}
650
717
651
- /**
652
- * @type {ToMarkdownHandle }
653
- */
654
- function peekElement ( ) {
655
- return '<'
718
+ indexStack . pop ( )
719
+
720
+ return results . join ( '' )
721
+ }
722
+
723
+ /**
724
+ *
725
+ * @param {State } context
726
+ * @returns {number }
727
+ */
728
+ function inferDepth ( context ) {
729
+ let depth = 0
730
+
731
+ for ( const x of context . stack ) {
732
+ if ( x === 'mdxJsxFlowElement' ) {
733
+ depth ++
734
+ }
656
735
}
736
+
737
+ return depth
738
+ }
739
+
740
+ /**
741
+ * @param {number } depth
742
+ * @returns {string }
743
+ */
744
+ function createIndent ( depth ) {
745
+ return indent . repeat ( depth )
746
+ }
747
+
748
+ /**
749
+ * @type {ToMarkdownHandle }
750
+ */
751
+ function peekElement ( ) {
752
+ return '<'
657
753
}
0 commit comments