container-phrasing.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. /**
  2. * @typedef {import('../types.js').Handle} Handle
  3. * @typedef {import('../types.js').Info} Info
  4. * @typedef {import('../types.js').PhrasingParents} PhrasingParents
  5. * @typedef {import('../types.js').State} State
  6. */
  7. /**
  8. * Serialize the children of a parent that contains phrasing children.
  9. *
  10. * These children will be joined flush together.
  11. *
  12. * @param {PhrasingParents} parent
  13. * Parent of flow nodes.
  14. * @param {State} state
  15. * Info passed around about the current state.
  16. * @param {Info} info
  17. * Info on where we are in the document we are generating.
  18. * @returns {string}
  19. * Serialized children, joined together.
  20. */
  21. export function containerPhrasing(parent, state, info) {
  22. const indexStack = state.indexStack
  23. const children = parent.children || []
  24. /** @type {Array<string>} */
  25. const results = []
  26. let index = -1
  27. let before = info.before
  28. indexStack.push(-1)
  29. let tracker = state.createTracker(info)
  30. while (++index < children.length) {
  31. const child = children[index]
  32. /** @type {string} */
  33. let after
  34. indexStack[indexStack.length - 1] = index
  35. if (index + 1 < children.length) {
  36. /** @type {Handle} */
  37. // @ts-expect-error: hush, it’s actually a `zwitch`.
  38. let handle = state.handle.handlers[children[index + 1].type]
  39. /** @type {Handle} */
  40. // @ts-expect-error: hush, it’s actually a `zwitch`.
  41. if (handle && handle.peek) handle = handle.peek
  42. after = handle
  43. ? handle(children[index + 1], parent, state, {
  44. before: '',
  45. after: '',
  46. ...tracker.current()
  47. }).charAt(0)
  48. : ''
  49. } else {
  50. after = info.after
  51. }
  52. // In some cases, html (text) can be found in phrasing right after an eol.
  53. // When we’d serialize that, in most cases that would be seen as html
  54. // (flow).
  55. // As we can’t escape or so to prevent it from happening, we take a somewhat
  56. // reasonable approach: replace that eol with a space.
  57. // See: <https://github.com/syntax-tree/mdast-util-to-markdown/issues/15>
  58. if (
  59. results.length > 0 &&
  60. (before === '\r' || before === '\n') &&
  61. child.type === 'html'
  62. ) {
  63. results[results.length - 1] = results[results.length - 1].replace(
  64. /(\r?\n|\r)$/,
  65. ' '
  66. )
  67. before = ' '
  68. // To do: does this work to reset tracker?
  69. tracker = state.createTracker(info)
  70. tracker.move(results.join(''))
  71. }
  72. results.push(
  73. tracker.move(
  74. state.handle(child, parent, state, {
  75. ...tracker.current(),
  76. before,
  77. after
  78. })
  79. )
  80. )
  81. before = results[results.length - 1].slice(-1)
  82. }
  83. indexStack.pop()
  84. return results.join('')
  85. }