index.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /**
  2. * @import {
  3. * Extension,
  4. * Handles,
  5. * HtmlExtension,
  6. * NormalizedExtension
  7. * } from 'micromark-util-types'
  8. */
  9. import {splice} from 'micromark-util-chunked'
  10. const hasOwnProperty = {}.hasOwnProperty
  11. /**
  12. * Combine multiple syntax extensions into one.
  13. *
  14. * @param {ReadonlyArray<Extension>} extensions
  15. * List of syntax extensions.
  16. * @returns {NormalizedExtension}
  17. * A single combined extension.
  18. */
  19. export function combineExtensions(extensions) {
  20. /** @type {NormalizedExtension} */
  21. const all = {}
  22. let index = -1
  23. while (++index < extensions.length) {
  24. syntaxExtension(all, extensions[index])
  25. }
  26. return all
  27. }
  28. /**
  29. * Merge `extension` into `all`.
  30. *
  31. * @param {NormalizedExtension} all
  32. * Extension to merge into.
  33. * @param {Extension} extension
  34. * Extension to merge.
  35. * @returns {undefined}
  36. * Nothing.
  37. */
  38. function syntaxExtension(all, extension) {
  39. /** @type {keyof Extension} */
  40. let hook
  41. for (hook in extension) {
  42. const maybe = hasOwnProperty.call(all, hook) ? all[hook] : undefined
  43. /** @type {Record<string, unknown>} */
  44. const left = maybe || (all[hook] = {})
  45. /** @type {Record<string, unknown> | undefined} */
  46. const right = extension[hook]
  47. /** @type {string} */
  48. let code
  49. if (right) {
  50. for (code in right) {
  51. if (!hasOwnProperty.call(left, code)) left[code] = []
  52. const value = right[code]
  53. constructs(
  54. // @ts-expect-error Looks like a list.
  55. left[code],
  56. Array.isArray(value) ? value : value ? [value] : []
  57. )
  58. }
  59. }
  60. }
  61. }
  62. /**
  63. * Merge `list` into `existing` (both lists of constructs).
  64. * Mutates `existing`.
  65. *
  66. * @param {Array<unknown>} existing
  67. * List of constructs to merge into.
  68. * @param {Array<unknown>} list
  69. * List of constructs to merge.
  70. * @returns {undefined}
  71. * Nothing.
  72. */
  73. function constructs(existing, list) {
  74. let index = -1
  75. /** @type {Array<unknown>} */
  76. const before = []
  77. while (++index < list.length) {
  78. // @ts-expect-error Looks like an object.
  79. ;(list[index].add === 'after' ? existing : before).push(list[index])
  80. }
  81. splice(existing, 0, 0, before)
  82. }
  83. /**
  84. * Combine multiple HTML extensions into one.
  85. *
  86. * @param {ReadonlyArray<HtmlExtension>} htmlExtensions
  87. * List of HTML extensions.
  88. * @returns {HtmlExtension}
  89. * Single combined HTML extension.
  90. */
  91. export function combineHtmlExtensions(htmlExtensions) {
  92. /** @type {HtmlExtension} */
  93. const handlers = {}
  94. let index = -1
  95. while (++index < htmlExtensions.length) {
  96. htmlExtension(handlers, htmlExtensions[index])
  97. }
  98. return handlers
  99. }
  100. /**
  101. * Merge `extension` into `all`.
  102. *
  103. * @param {HtmlExtension} all
  104. * Extension to merge into.
  105. * @param {HtmlExtension} extension
  106. * Extension to merge.
  107. * @returns {undefined}
  108. * Nothing.
  109. */
  110. function htmlExtension(all, extension) {
  111. /** @type {keyof HtmlExtension} */
  112. let hook
  113. for (hook in extension) {
  114. const maybe = hasOwnProperty.call(all, hook) ? all[hook] : undefined
  115. const left = maybe || (all[hook] = {})
  116. const right = extension[hook]
  117. /** @type {keyof Handles} */
  118. let type
  119. if (right) {
  120. for (type in right) {
  121. // @ts-expect-error assume document vs regular handler are managed correctly.
  122. left[type] = right[type]
  123. }
  124. }
  125. }
  126. }