index.js 3.3 KB

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