@intlayer/core 9.4.4 → 9.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/README.md +0 -2
  2. package/dist/cjs/dictionaryManipulator/qualifiedDictionary.cjs.map +1 -1
  3. package/dist/cjs/index.cjs +5 -8
  4. package/dist/cjs/interpreter/getPlural.cjs.map +1 -1
  5. package/dist/cjs/localization/getPrefix.cjs +6 -6
  6. package/dist/cjs/localization/getPrefix.cjs.map +1 -1
  7. package/dist/cjs/markdown/compiler.cjs +437 -209
  8. package/dist/cjs/markdown/compiler.cjs.map +1 -1
  9. package/dist/cjs/markdown/constants.cjs +32 -50
  10. package/dist/cjs/markdown/constants.cjs.map +1 -1
  11. package/dist/cjs/markdown/elementScanner.cjs +191 -0
  12. package/dist/cjs/markdown/elementScanner.cjs.map +1 -0
  13. package/dist/cjs/markdown/index.cjs +5 -8
  14. package/dist/cjs/markdown/parser.cjs +90 -48
  15. package/dist/cjs/markdown/parser.cjs.map +1 -1
  16. package/dist/cjs/markdown/renderer.cjs +14 -11
  17. package/dist/cjs/markdown/renderer.cjs.map +1 -1
  18. package/dist/cjs/markdown/utils.cjs +123 -34
  19. package/dist/cjs/markdown/utils.cjs.map +1 -1
  20. package/dist/cjs/utils/getCookie.cjs +1 -1
  21. package/dist/cjs/utils/getCookie.cjs.map +1 -1
  22. package/dist/esm/dictionaryManipulator/qualifiedDictionary.mjs.map +1 -1
  23. package/dist/esm/index.mjs +3 -3
  24. package/dist/esm/interpreter/getPlural.mjs.map +1 -1
  25. package/dist/esm/localization/getPrefix.mjs +6 -6
  26. package/dist/esm/localization/getPrefix.mjs.map +1 -1
  27. package/dist/esm/markdown/compiler.mjs +438 -211
  28. package/dist/esm/markdown/compiler.mjs.map +1 -1
  29. package/dist/esm/markdown/constants.mjs +29 -43
  30. package/dist/esm/markdown/constants.mjs.map +1 -1
  31. package/dist/esm/markdown/elementScanner.mjs +183 -0
  32. package/dist/esm/markdown/elementScanner.mjs.map +1 -0
  33. package/dist/esm/markdown/index.mjs +3 -3
  34. package/dist/esm/markdown/parser.mjs +90 -48
  35. package/dist/esm/markdown/parser.mjs.map +1 -1
  36. package/dist/esm/markdown/renderer.mjs +14 -11
  37. package/dist/esm/markdown/renderer.mjs.map +1 -1
  38. package/dist/esm/markdown/utils.mjs +124 -35
  39. package/dist/esm/markdown/utils.mjs.map +1 -1
  40. package/dist/esm/utils/getCookie.mjs +1 -1
  41. package/dist/esm/utils/getCookie.mjs.map +1 -1
  42. package/dist/types/dictionaryManipulator/qualifiedDictionary.d.ts +3 -3
  43. package/dist/types/dictionaryManipulator/qualifiedDictionary.d.ts.map +1 -1
  44. package/dist/types/index.d.ts +4 -4
  45. package/dist/types/interpreter/getPlural.d.ts +3 -3
  46. package/dist/types/interpreter/getPlural.d.ts.map +1 -1
  47. package/dist/types/localization/getPrefix.d.ts.map +1 -1
  48. package/dist/types/markdown/compiler.d.ts +43 -0
  49. package/dist/types/markdown/compiler.d.ts.map +1 -1
  50. package/dist/types/markdown/constants.d.ts +26 -17
  51. package/dist/types/markdown/constants.d.ts.map +1 -1
  52. package/dist/types/markdown/elementScanner.d.ts +14 -0
  53. package/dist/types/markdown/elementScanner.d.ts.map +1 -0
  54. package/dist/types/markdown/index.d.ts +4 -4
  55. package/dist/types/markdown/parser.d.ts +0 -5
  56. package/dist/types/markdown/parser.d.ts.map +1 -1
  57. package/dist/types/markdown/renderer.d.ts +0 -4
  58. package/dist/types/markdown/renderer.d.ts.map +1 -1
  59. package/dist/types/markdown/types.d.ts +13 -0
  60. package/dist/types/markdown/types.d.ts.map +1 -1
  61. package/dist/types/markdown/utils.d.ts +7 -2
  62. package/dist/types/markdown/utils.d.ts.map +1 -1
  63. package/package.json +7 -7
  64. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/dictionaryManipulator/qualifiedDynamicLoader.d.ts +0 -1
  65. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getDictionaryAsync.d.ts +0 -1
  66. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getEnumeration.d.ts +0 -1
  67. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getGender.d.ts +0 -1
  68. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getInsertion.d.ts +0 -1
  69. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getPlural.d.ts +0 -1
  70. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getSelect.d.ts +0 -1
  71. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/index.d.ts +0 -7
  72. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/interpolableNode.d.ts +0 -1
  73. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/index.d.ts +0 -2
  74. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/nesting/index.d.ts +0 -1
  75. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/nesting/nesting.d.ts +0 -1
  76. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/dictionaryManipulator/qualifiedDynamicLoader.d.ts +0 -1
  77. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getDictionaryAsync.d.ts +0 -1
  78. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getEnumeration.d.ts +0 -1
  79. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getGender.d.ts +0 -1
  80. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getInsertion.d.ts +0 -1
  81. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getPlural.d.ts +0 -1
  82. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getSelect.d.ts +0 -1
  83. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/index.d.ts +0 -7
  84. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/interpolableNode.d.ts +0 -1
  85. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/intlayer/dist/types/index.d.ts +0 -6
  86. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/index.d.ts +0 -2
  87. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/nesting/index.d.ts +0 -1
  88. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/nesting/nesting.d.ts +0 -1
  89. package/dist/types/@intlayer/core/dist/types/dictionaryManipulator/plainDynamicLoader.d.ts +0 -2
  90. package/dist/types/@intlayer/core/dist/types/dictionaryManipulator/qualifiedDynamicLoader.d.ts +0 -2
  91. package/dist/types/@intlayer/core/dist/types/formatters/compact.d.ts +0 -1
  92. package/dist/types/@intlayer/core/dist/types/formatters/currency.d.ts +0 -1
  93. package/dist/types/@intlayer/core/dist/types/formatters/date.d.ts +0 -1
  94. package/dist/types/@intlayer/core/dist/types/formatters/index.d.ts +0 -9
  95. package/dist/types/@intlayer/core/dist/types/formatters/list.d.ts +0 -1
  96. package/dist/types/@intlayer/core/dist/types/formatters/number.d.ts +0 -1
  97. package/dist/types/@intlayer/core/dist/types/formatters/percentage.d.ts +0 -1
  98. package/dist/types/@intlayer/core/dist/types/formatters/relativeTime.d.ts +0 -1
  99. package/dist/types/@intlayer/core/dist/types/formatters/units.d.ts +0 -1
  100. package/dist/types/@intlayer/core/dist/types/interpreter/dictionaryTransformCache.d.ts +0 -4
  101. package/dist/types/@intlayer/core/dist/types/interpreter/getCondition.d.ts +0 -2
  102. package/dist/types/@intlayer/core/dist/types/interpreter/getContent/deepTransform.d.ts +0 -1
  103. package/dist/types/@intlayer/core/dist/types/interpreter/getContent/getContent.d.ts +0 -3
  104. package/dist/types/@intlayer/core/dist/types/interpreter/getContent/index.d.ts +0 -3
  105. package/dist/types/@intlayer/core/dist/types/interpreter/getContent/plugins.d.ts +0 -7
  106. package/dist/types/@intlayer/core/dist/types/interpreter/getDictionary.d.ts +0 -4
  107. package/dist/types/@intlayer/core/dist/types/interpreter/getDictionaryAsync.d.ts +0 -6
  108. package/dist/types/@intlayer/core/dist/types/interpreter/getEnumeration.d.ts +0 -2
  109. package/dist/types/@intlayer/core/dist/types/interpreter/getGender.d.ts +0 -2
  110. package/dist/types/@intlayer/core/dist/types/interpreter/getInsertion.d.ts +0 -2
  111. package/dist/types/@intlayer/core/dist/types/interpreter/getIntlayer.d.ts +0 -4
  112. package/dist/types/@intlayer/core/dist/types/interpreter/getIntlayerAsync.d.ts +0 -4
  113. package/dist/types/@intlayer/core/dist/types/interpreter/getNesting.d.ts +0 -6
  114. package/dist/types/@intlayer/core/dist/types/interpreter/getPlural.d.ts +0 -3
  115. package/dist/types/@intlayer/core/dist/types/interpreter/getSelect.d.ts +0 -2
  116. package/dist/types/@intlayer/core/dist/types/interpreter/getTranslation.d.ts +0 -1
  117. package/dist/types/@intlayer/core/dist/types/interpreter/index.d.ts +0 -18
  118. package/dist/types/@intlayer/core/dist/types/interpreter/interpolableNode.d.ts +0 -2
  119. package/dist/types/@intlayer/core/dist/types/intlayer/dist/types/index.d.ts +0 -6
  120. package/dist/types/@intlayer/core/dist/types/localization/comparePaths.d.ts +0 -1
  121. package/dist/types/@intlayer/core/dist/types/localization/domainUtils.d.ts +0 -2
  122. package/dist/types/@intlayer/core/dist/types/localization/generateSitemap.d.ts +0 -2
  123. package/dist/types/@intlayer/core/dist/types/localization/getBrowserLocale.d.ts +0 -1
  124. package/dist/types/@intlayer/core/dist/types/localization/getCanonicalPath.d.ts +0 -2
  125. package/dist/types/@intlayer/core/dist/types/localization/getHTMLTextDir.d.ts +0 -1
  126. package/dist/types/@intlayer/core/dist/types/localization/getLocale.d.ts +0 -1
  127. package/dist/types/@intlayer/core/dist/types/localization/getLocaleFromPath.d.ts +0 -2
  128. package/dist/types/@intlayer/core/dist/types/localization/getLocaleLang.d.ts +0 -1
  129. package/dist/types/@intlayer/core/dist/types/localization/getLocaleName.d.ts +0 -1
  130. package/dist/types/@intlayer/core/dist/types/localization/getLocalizedPath.d.ts +0 -2
  131. package/dist/types/@intlayer/core/dist/types/localization/getLocalizedUrl.d.ts +0 -2
  132. package/dist/types/@intlayer/core/dist/types/localization/getMultilingualUrls.d.ts +0 -2
  133. package/dist/types/@intlayer/core/dist/types/localization/getPathWithoutLocale.d.ts +0 -1
  134. package/dist/types/@intlayer/core/dist/types/localization/getPrefix.d.ts +0 -2
  135. package/dist/types/@intlayer/core/dist/types/localization/index.d.ts +0 -22
  136. package/dist/types/@intlayer/core/dist/types/localization/localeDetector.d.ts +0 -2
  137. package/dist/types/@intlayer/core/dist/types/localization/localeMapper.d.ts +0 -1
  138. package/dist/types/@intlayer/core/dist/types/localization/localeResolver.d.ts +0 -1
  139. package/dist/types/@intlayer/core/dist/types/localization/resolveBrowserLocale.d.ts +0 -2
  140. package/dist/types/@intlayer/core/dist/types/localization/rewriteMatch.d.ts +0 -1
  141. package/dist/types/@intlayer/core/dist/types/localization/rewriteUtils.d.ts +0 -2
  142. package/dist/types/@intlayer/core/dist/types/localization/validatePrefix.d.ts +0 -2
  143. package/dist/types/@intlayer/core/dist/types/markdown/index.d.ts +0 -2
  144. package/dist/types/@intlayer/core/dist/types/transpiler/condition/condition.d.ts +0 -1
  145. package/dist/types/@intlayer/core/dist/types/transpiler/condition/index.d.ts +0 -1
  146. package/dist/types/@intlayer/core/dist/types/transpiler/enumeration/enumeration.d.ts +0 -1
  147. package/dist/types/@intlayer/core/dist/types/transpiler/enumeration/index.d.ts +0 -1
  148. package/dist/types/@intlayer/core/dist/types/transpiler/file/file.d.ts +0 -1
  149. package/dist/types/@intlayer/core/dist/types/transpiler/file/index.d.ts +0 -1
  150. package/dist/types/@intlayer/core/dist/types/transpiler/gender/gender.d.ts +0 -1
  151. package/dist/types/@intlayer/core/dist/types/transpiler/gender/index.d.ts +0 -1
  152. package/dist/types/@intlayer/core/dist/types/transpiler/html/html.d.ts +0 -1
  153. package/dist/types/@intlayer/core/dist/types/transpiler/html/index.d.ts +0 -1
  154. package/dist/types/@intlayer/core/dist/types/transpiler/index.d.ts +0 -22
  155. package/dist/types/@intlayer/core/dist/types/transpiler/insertion/index.d.ts +0 -1
  156. package/dist/types/@intlayer/core/dist/types/transpiler/insertion/insertion.d.ts +0 -1
  157. package/dist/types/@intlayer/core/dist/types/transpiler/markdown/index.d.ts +0 -1
  158. package/dist/types/@intlayer/core/dist/types/transpiler/markdown/markdown.d.ts +0 -1
  159. package/dist/types/@intlayer/core/dist/types/transpiler/nesting/index.d.ts +0 -1
  160. package/dist/types/@intlayer/core/dist/types/transpiler/nesting/nesting.d.ts +0 -4
  161. package/dist/types/@intlayer/core/dist/types/transpiler/plural/index.d.ts +0 -1
  162. package/dist/types/@intlayer/core/dist/types/transpiler/plural/plural.d.ts +0 -1
  163. package/dist/types/@intlayer/core/dist/types/transpiler/select/index.d.ts +0 -1
  164. package/dist/types/@intlayer/core/dist/types/transpiler/select/select.d.ts +0 -1
  165. package/dist/types/@intlayer/core/dist/types/transpiler/translation/index.d.ts +0 -1
  166. package/dist/types/@intlayer/core/dist/types/transpiler/translation/translation.d.ts +0 -2
  167. package/dist/types/@intlayer/core/dist/types/utils/index.d.ts +0 -3
  168. package/dist/types/@intlayer/core/dist/types/utils/intl.d.ts +0 -1
  169. package/dist/types/@intlayer/core/dist/types/utils/isSameKeyPath.d.ts +0 -1
  170. package/dist/types/@intlayer/core/dist/types/utils/localeStorage.d.ts +0 -2
  171. package/dist/types/intlayer/dist/types/index.d.ts +0 -14
@@ -1,4 +1,5 @@
1
- import { ATTRIBUTE_TO_NODE_PROP_MAP, ATTR_EXTRACTOR_R, BLOCKQUOTE_ALERT_R, BLOCKQUOTE_R, BLOCKQUOTE_TRIM_LEFT_MULTILINE_R, BLOCK_END_R, BREAK_LINE_R, BREAK_THEMATIC_R, CODE_BLOCK_FENCED_R, CODE_BLOCK_R, CODE_INLINE_R, CONSECUTIVE_NEWLINE_R, CUSTOM_COMPONENT_OPENING_TAG_R, CUSTOM_COMPONENT_R, DO_NOT_PROCESS_HTML_ELEMENTS, FOOTNOTE_R, FOOTNOTE_REFERENCE_R, FRONT_MATTER_R, GFM_TASK_R, HEADING_ATX_COMPLIANT_R, HEADING_R, HEADING_SETEXT_R, HTML_BLOCK_ELEMENT_R, HTML_BLOCK_OPENING_TAG_R, HTML_CHAR_CODE_R, HTML_COMMENT_R, HTML_LEFT_TRIM_AMOUNT_R, HTML_SELF_CLOSING_ELEMENT_R, HTML_SELF_CLOSING_OPENING_TAG_R, LINK_AUTOLINK_BARE_URL_R, LINK_AUTOLINK_R, NAMED_CODES_TO_UNICODE, NP_TABLE_R, ORDERED, ORDERED_LIST_ITEM_PREFIX_R, ORDERED_LIST_ITEM_R, ORDERED_LIST_R, PARAGRAPH_R, Priority, REFERENCE_IMAGE_OR_LINK, REFERENCE_IMAGE_R, REFERENCE_LINK_R, RuleType, SHORTCODE_R, SHOULD_RENDER_AS_BLOCK_R, TEXT_BOLD_R, TEXT_EMPHASIZED_R, TEXT_ESCAPED_R, TEXT_MARKED_R, TEXT_PLAIN_R, TEXT_STRIKETHROUGHED_R, TRIM_STARTING_NEWLINES, UNORDERED, UNORDERED_LIST_ITEM_PREFIX_R, UNORDERED_LIST_ITEM_R, UNORDERED_LIST_R } from "./constants.mjs";
1
+ import { ATTRIBUTE_TO_NODE_PROP_MAP, ATTR_EXTRACTOR_R, BLOCKQUOTE_ALERT_R, BLOCKQUOTE_R, BLOCKQUOTE_TRIM_LEFT_MULTILINE_R, BLOCK_END_R, BREAK_LINE_R, BREAK_THEMATIC_R, CODE_BLOCK_FENCED_R, CODE_BLOCK_R, CODE_INLINE_R, CONSECUTIVE_NEWLINE_R, DO_NOT_PROCESS_HTML_ELEMENTS, FOOTNOTE_R, FOOTNOTE_REFERENCE_R, FRONT_MATTER_R, GFM_TASK_R, HEADING_ATX_COMPLIANT_R, HEADING_R, HEADING_SETEXT_R, HTML_CHAR_CODE_R, HTML_COMMENT_R, HTML_LEFT_TRIM_AMOUNT_R, LINK_AUTOLINK_BARE_URL_R, LINK_AUTOLINK_R, NAMED_CODES_TO_UNICODE, NP_TABLE_R, ORDERED, ORDERED_LIST_ITEM_PREFIX_R, ORDERED_LIST_ITEM_R, ORDERED_LIST_R, PARAGRAPH_R, Priority, REFERENCE_IMAGE_OR_LINK, REFERENCE_IMAGE_R, REFERENCE_LINK_R, RuleType, SCOPE_BLOCK, SCOPE_BOTH, SCOPE_INLINE, SHORTCODE_R, SHOULD_RENDER_AS_BLOCK_R, TEXT_BOLD_R, TEXT_EMPHASIZED_R, TEXT_ESCAPED_R, TEXT_MARKED_R, TEXT_STRIKETHROUGHED_R, TRIM_STARTING_NEWLINES, UNORDERED, UNORDERED_LIST_ITEM_PREFIX_R, UNORDERED_LIST_ITEM_R, UNORDERED_LIST_R } from "./constants.mjs";
2
+ import { matchCustomComponent, matchHtmlBlockElement, matchSelfClosingElement, startsWithCustomComponent, startsWithElement, startsWithHtmlBlockElement, startsWithSelfClosingElement } from "./elementScanner.mjs";
2
3
  import { allowInline, anyScopeRegex, attributeValueToNodePropValue, blockRegex, captureNothing, cx, get, inlineRegex, normalizeAttributeKey, parseBlock, parseCaptureInline, parseInline, parseSimpleInline, parseTableAlign, parseTableCells, parseTableRow, renderNothing, sanitizer, simpleInlineRegex, slugify, some, startsWith, trimEnd, trimLeadingWhitespaceOutsideFences, unescapeString, unquote } from "./utils.mjs";
3
4
  import { parserFor } from "./parser.mjs";
4
5
  import { createRenderer, renderFor } from "./renderer.mjs";
@@ -13,14 +14,6 @@ const LINK_R = new RegExp(`^\\[((?:\\[[^\\[\\]]*(?:\\[[^\\[\\]]*\\][^\\[\\]]*)*\
13
14
  * lowercase the whole remaining document to look its closing tag up — copying
14
15
  * the rest of the file on each probe.
15
16
  */
16
- const closingTagRegexCache = /* @__PURE__ */ new Map();
17
- const getClosingTagRegex = (tag) => {
18
- const cached = closingTagRegexCache.get(tag);
19
- if (cached) return cached;
20
- const regex = new RegExp(`</${tag}>`, "i");
21
- closingTagRegexCache.set(tag, regex);
22
- return regex;
23
- };
24
17
  /**
25
18
  * Builds a tag -> component resolver for one set of overrides.
26
19
  *
@@ -29,10 +22,17 @@ const getClosingTagRegex = (tag) => {
29
22
  */
30
23
  const createTagResolver = (components) => {
31
24
  let lowercaseKeys = null;
25
+ if (Object.keys(components).length === 0) return (tag) => tag;
26
+ const resolved = /* @__PURE__ */ new Map();
32
27
  return (tag) => {
33
28
  if (typeof tag !== "string") return tag;
29
+ const memoized = resolved.get(tag);
30
+ if (memoized !== void 0) return memoized;
34
31
  const override = get(components, tag);
35
- if (override) return override;
32
+ if (override) {
33
+ resolved.set(tag, override);
34
+ return override;
35
+ }
36
36
  if (!lowercaseKeys) {
37
37
  lowercaseKeys = /* @__PURE__ */ new Map();
38
38
  for (const key of Object.keys(components)) {
@@ -41,7 +41,9 @@ const createTagResolver = (components) => {
41
41
  }
42
42
  }
43
43
  const key = lowercaseKeys.get(tag.toLowerCase());
44
- return (key ? get(components, key) : void 0) || tag;
44
+ const result = (key ? get(components, key) : void 0) || tag;
45
+ resolved.set(tag, result);
46
+ return result;
45
47
  };
46
48
  };
47
49
  /** Resolves the slugifier for a context, falling back to the built-in one. */
@@ -49,7 +51,7 @@ const createSlugger = (ctx) => (input) => ctx.slugify ? ctx.slugify(input, slugi
49
51
  const createElementFactory = (ctx, options) => {
50
52
  const { runtime, components = {} } = ctx;
51
53
  const resolveTag = createTagResolver(components);
52
- const filteredTags = options.tagfilter ? [
54
+ const filteredTags = options.tagfilter ? /* @__PURE__ */ new Set([
53
55
  "title",
54
56
  "textarea",
55
57
  "style",
@@ -59,59 +61,211 @@ const createElementFactory = (ctx, options) => {
59
61
  "noframes",
60
62
  "script",
61
63
  "plaintext"
62
- ] : [];
64
+ ]) : null;
65
+ /**
66
+ * True when `props` can be handed to the runtime untouched: no `class`
67
+ * alias to fold into `className`, and no empty value to drop. Rules build a
68
+ * fresh literal for every element, so reusing it is safe, and it saves an
69
+ * object per element — the single largest allocation in the render phase.
70
+ */
71
+ const needsPropsRewrite = (props) => {
72
+ if ("class" in props) return true;
73
+ for (const key in props) {
74
+ const value = props[key];
75
+ if (value === void 0 || value === null) return true;
76
+ }
77
+ return false;
78
+ };
63
79
  return (tag, props, ...children) => {
64
- if (typeof tag === "string" && filteredTags.includes(tag.toLowerCase())) return null;
65
80
  const isStringTag = typeof tag === "string";
66
- const className = cx(props?.className, props?.class);
67
- const mergedProps = {};
68
- let classNameHandled = false;
69
- if (props) for (const key in props) {
70
- const value = props[key];
71
- if (value === void 0 || value === null) continue;
72
- if (key === "className" || key === "class") {
73
- if (!classNameHandled) {
74
- if (className) mergedProps.className = className;
75
- classNameHandled = true;
76
- }
77
- } else mergedProps[key] = value;
81
+ if (filteredTags && isStringTag && (filteredTags.has(tag) || filteredTags.has(tag.toLowerCase()))) return null;
82
+ let finalProps;
83
+ if (!props) finalProps = {};
84
+ else if (!needsPropsRewrite(props)) finalProps = props;
85
+ else {
86
+ const className = cx(props.className, props.class);
87
+ const mergedProps = {};
88
+ let classNameHandled = false;
89
+ for (const key in props) {
90
+ const value = props[key];
91
+ if (value === void 0 || value === null) continue;
92
+ if (key === "className" || key === "class") {
93
+ if (!classNameHandled) {
94
+ if (className) mergedProps.className = className;
95
+ classNameHandled = true;
96
+ }
97
+ } else mergedProps[key] = value;
98
+ }
99
+ if (!classNameHandled && className) mergedProps.className = className;
100
+ finalProps = mergedProps;
78
101
  }
79
- if (!classNameHandled && className) mergedProps.className = className;
80
- let finalProps = mergedProps;
81
- if (runtime.normalizeProps && isStringTag) finalProps = runtime.normalizeProps(tag, mergedProps);
102
+ if (runtime.normalizeProps && isStringTag) finalProps = runtime.normalizeProps(tag, finalProps);
82
103
  const component = resolveTag(tag);
83
- return runtime.createElement(component, finalProps, ...children.length === 1 ? [children[0]] : children);
104
+ return runtime.createElement(component, finalProps, ...children);
105
+ };
106
+ };
107
+ const createDocumentScope = () => ({
108
+ footnotes: [],
109
+ references: {}
110
+ });
111
+ /**
112
+ * Small enough to scan linearly, large enough that an app mixing a few option
113
+ * sets — say prose and a dictionary rendered with different components — keeps
114
+ * hitting it.
115
+ */
116
+ const RULE_SET_CACHE_LIMIT = 8;
117
+ const ruleSetCache = [];
118
+ /**
119
+ * Every input that changes how rules are built. Compared directly rather than
120
+ * through a signature array, which would allocate on every compile.
121
+ */
122
+ const builtFromSame = (entry, ctx, options, parseOnly) => entry.parseOnly === parseOnly && entry.ctx.runtime === ctx.runtime && entry.ctx.components === ctx.components && entry.ctx.namedCodesToUnicode === ctx.namedCodesToUnicode && entry.ctx.sanitizer === ctx.sanitizer && entry.ctx.slugify === ctx.slugify && entry.options.disableAutoLink === options.disableAutoLink && entry.options.disableParsingRawHTML === options.disableParsingRawHTML && entry.options.enforceAtxHeadings === options.enforceAtxHeadings && entry.options.tagfilter === options.tagfilter && entry.options.renderRule === options.renderRule;
123
+ /**
124
+ * Returns the rule set for these options, building it only when no cached set
125
+ * was made from the same inputs.
126
+ */
127
+ const getRuleSet = (ctx, options, parseOnly) => {
128
+ for (let index = 0; index < ruleSetCache.length; index++) {
129
+ const entry = ruleSetCache[index];
130
+ if (!builtFromSame(entry, ctx, options, parseOnly)) continue;
131
+ if (index > 0) {
132
+ ruleSetCache.splice(index, 1);
133
+ ruleSetCache.unshift(entry);
134
+ }
135
+ return entry;
136
+ }
137
+ const scope = { current: createDocumentScope() };
138
+ const createElement = parseOnly ? noopCreateElement : createElementFactory(ctx, options);
139
+ const rules = createDocumentRules(createElement, ctx, options, scope);
140
+ const entry = {
141
+ ctx,
142
+ options,
143
+ parseOnly,
144
+ createElement,
145
+ scope,
146
+ parse: parserFor(rules),
147
+ emit: renderFor(createRenderer(rules, options.renderRule))
84
148
  };
149
+ ruleSetCache.unshift(entry);
150
+ if (ruleSetCache.length > RULE_SET_CACHE_LIMIT) ruleSetCache.pop();
151
+ return entry;
85
152
  };
86
- const createRules = (createElement, ctx, options, footnotes, refs, attrStringToMap, containsBlockSyntax, nonParagraphBlockSyntaxes) => {
153
+ /**
154
+ * Runs `operation` with `scope` installed as the current document, restoring
155
+ * whatever was there before. The save/restore keeps a nested compile — a custom
156
+ * component that renders markdown of its own — from clobbering its caller.
157
+ */
158
+ const withDocumentScope = (ref, scope, operation) => {
159
+ const previous = ref.current;
160
+ ref.current = scope;
161
+ try {
162
+ return operation();
163
+ } finally {
164
+ ref.current = previous;
165
+ }
166
+ };
167
+ const createRules = (createElement, ctx, options, scope, attrStringToMap, containsBlockSyntax, nonParagraphBlockSyntaxes) => {
87
168
  const slug = createSlugger(ctx);
88
169
  const sanitize = ctx.sanitizer ?? sanitizer;
89
170
  const namedCodesToUnicode = ctx.namedCodesToUnicode ? {
90
171
  ...NAMED_CODES_TO_UNICODE,
91
172
  ...ctx.namedCodesToUnicode
92
173
  } : NAMED_CODES_TO_UNICODE;
174
+ /**
175
+ * Raw HTML blocks and custom components differ only in how their tag is
176
+ * spelled: both trim the captured content, decide block or inline parsing
177
+ * from it, and render the tag with its attributes. `rawHtml` adds the two
178
+ * things only real HTML needs — the never-parse element list, and tracking
179
+ * whether we are inside an anchor.
180
+ */
181
+ const pairedElementRule = (match, order, rawHtml, scope = SCOPE_BOTH) => ({
182
+ _scope: scope,
183
+ _qualify: ["<"],
184
+ _match: anyScopeRegex(match),
185
+ _order: order,
186
+ _parse(capture, parse, state) {
187
+ const content = capture[3] ?? "";
188
+ const whitespace = content.match(HTML_LEFT_TRIM_AMOUNT_R)?.[1] ?? "";
189
+ const trimmed = trimLeadingWhitespaceOutsideFences(content, whitespace);
190
+ const parseFunc = containsBlockSyntax(trimmed) ? parseBlock : parseInline;
191
+ const tagName = (capture[1] ?? "").trim();
192
+ const lowercased = tagName.toLowerCase();
193
+ const noInnerParse = rawHtml && DO_NOT_PROCESS_HTML_ELEMENTS.indexOf(lowercased) !== -1;
194
+ const tag = noInnerParse ? lowercased : tagName;
195
+ const ast = {
196
+ attrs: attrStringToMap(tag, capture[2] ?? ""),
197
+ noInnerParse,
198
+ tag
199
+ };
200
+ if (rawHtml) state.inAnchor = state.inAnchor || lowercased === "a";
201
+ if (noInnerParse) ast.text = content;
202
+ else {
203
+ const prevInHTML = state.inHTML;
204
+ state.inHTML = true;
205
+ ast.children = parseFunc(parse, trimmed, state);
206
+ state.inHTML = prevInHTML;
207
+ }
208
+ if (rawHtml) state.inAnchor = false;
209
+ return ast;
210
+ },
211
+ _render(node, output, state = {}) {
212
+ return createElement(node.tag, {
213
+ key: state.key,
214
+ ...node.attrs ?? {}
215
+ }, node.text ?? (node.children ? output(node.children, state) : ""));
216
+ }
217
+ });
93
218
  const generateListRule = (type) => {
94
219
  const ordered = type === 1;
95
220
  const LIST_R = ordered ? ORDERED_LIST_R : UNORDERED_LIST_R;
96
221
  const LIST_ITEM_R = ordered ? ORDERED_LIST_ITEM_R : UNORDERED_LIST_ITEM_R;
97
222
  const LIST_ITEM_PREFIX_R = ordered ? ORDERED_LIST_ITEM_PREFIX_R : UNORDERED_LIST_ITEM_PREFIX_R;
98
223
  return {
99
- _qualify: (source) => LIST_ITEM_PREFIX_R.test(source),
224
+ _scope: SCOPE_BOTH,
225
+ _firstChars: ordered ? [
226
+ 32,
227
+ 9,
228
+ 48,
229
+ 49,
230
+ 50,
231
+ 51,
232
+ 52,
233
+ 53,
234
+ 54,
235
+ 55,
236
+ 56,
237
+ 57
238
+ ] : [
239
+ 32,
240
+ 9,
241
+ 42,
242
+ 43,
243
+ 45
244
+ ],
245
+ _qualify: (source, state) => state.prevCaptureIndent !== void 0 && Boolean(state.list ?? (!state.inline && !state.simple)) && LIST_ITEM_PREFIX_R.test(source),
100
246
  _match: allowInline((source, state) => {
101
247
  const lineIndent = state.prevCaptureIndent;
102
248
  const isStartOfLine = lineIndent !== void 0;
103
249
  const isListAllowed = state.list ?? (!state.inline && !state.simple);
104
250
  if (isStartOfLine && isListAllowed) {
105
251
  const matchSource = lineIndent ? lineIndent + source : source;
106
- return LIST_R.exec(matchSource);
252
+ const match = LIST_R.exec(matchSource);
253
+ if (!match) return null;
254
+ if (lineIndent && match[0].startsWith(lineIndent)) {
255
+ const adjusted = [...match];
256
+ adjusted[0] = match[0].slice(lineIndent.length);
257
+ adjusted.fullMatch = match[0];
258
+ return adjusted;
259
+ }
260
+ return match;
107
261
  }
108
262
  return null;
109
263
  }),
110
264
  _order: Priority.HIGH,
111
265
  _parse(capture, parse, state) {
112
- const bullet = capture[2];
266
+ const bullet = capture[2] ?? "";
113
267
  const startValue = ordered ? +bullet.slice(0, -1) : void 0;
114
- const items = capture[0].replace(BLOCK_END_R, "\n").match(LIST_ITEM_R);
268
+ const items = (capture.fullMatch ?? capture[0] ?? "").replace(BLOCK_END_R, "\n").match(LIST_ITEM_R);
115
269
  if (!items) return {
116
270
  items: [],
117
271
  ordered,
@@ -122,8 +276,8 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
122
276
  items: items.map((item, i) => {
123
277
  const prefixCapture = LIST_ITEM_PREFIX_R.exec(item);
124
278
  const space = prefixCapture ? prefixCapture[0].length : 0;
125
- const spaceRegex = new RegExp(`^ {1,${space}}`, "gm");
126
- const content = item.replace(spaceRegex, "").replace(LIST_ITEM_PREFIX_R, "");
279
+ const spaceRegex = space <= 8 ? STRIP_INDENT_REGEXES[space] : new RegExp(`^ {1,${space}}`, "gm");
280
+ const content = item.slice(space).replace(spaceRegex, "");
127
281
  const isLastItem = i === items.length - 1;
128
282
  const thisItemIsAParagraph = content.indexOf("\n\n") !== -1 || isLastItem && lastItemWasAParagraph;
129
283
  lastItemWasAParagraph = thisItemIsAParagraph;
@@ -148,21 +302,33 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
148
302
  };
149
303
  },
150
304
  _render(node, output, state = {}) {
151
- const Tag = node.ordered ? "ol" : "ul";
152
- const props = { key: state.key };
153
- if (node.ordered && node.start != null) props.start = node.start;
154
- return createElement(Tag, props, ...node.items.map((item, i) => createElement("li", { key: i }, output(item, state))));
305
+ return createElement(ordered ? "ol" : "ul", {
306
+ key: state.key,
307
+ start: node.start
308
+ }, ...node.items.map((item, i) => createElement("li", { key: i }, output(item, state))));
155
309
  }
156
310
  };
157
311
  };
158
312
  const matchParagraph = (source, state) => {
159
313
  if (state.inline || state.simple || state.inHTML && source.indexOf("\n\n") === -1 && !state.prevCaptureHasBlankLine) return null;
160
314
  let start = 0;
161
- while (true) {
315
+ const srcLen = source.length;
316
+ while (start < srcLen) {
162
317
  const newlineIndex = source.indexOf("\n", start);
163
- const line = source.slice(start, newlineIndex === -1 ? void 0 : newlineIndex + 1);
164
- if (some(nonParagraphBlockSyntaxes, line)) break;
165
- if (newlineIndex === -1 || !line.trim()) break;
318
+ const lineEnd = newlineIndex === -1 ? srcLen : newlineIndex;
319
+ let p = start;
320
+ while (p < lineEnd && (source.charCodeAt(p) === 32 || source.charCodeAt(p) === 9)) p++;
321
+ if (p >= lineEnd) break;
322
+ if (p - start >= 4 || source.charCodeAt(start) === 9) break;
323
+ const ch = source.charCodeAt(p);
324
+ if (ch === 62 || ch === 96 || ch === 126 || ch === 35 || ch === 124 || ch === 42 || ch === 43 || ch === 45 || ch === 61 || ch === 60 || ch >= 48 && ch <= 57) {
325
+ const line = source.slice(start, newlineIndex === -1 ? void 0 : newlineIndex + 1);
326
+ if (some(nonParagraphBlockSyntaxes, line)) break;
327
+ }
328
+ if (newlineIndex === -1) {
329
+ start = srcLen;
330
+ break;
331
+ }
166
332
  start = newlineIndex + 1;
167
333
  }
168
334
  const match = source.slice(0, start);
@@ -177,11 +343,12 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
177
343
  };
178
344
  return {
179
345
  [RuleType.blockQuote]: {
346
+ _scope: SCOPE_BLOCK,
180
347
  _qualify: [">"],
181
348
  _match: blockRegex(BLOCKQUOTE_R),
182
349
  _order: Priority.HIGH,
183
350
  _parse(capture, parse, state) {
184
- const matchAlert = capture[0].replace(BLOCKQUOTE_TRIM_LEFT_MULTILINE_R, "").match(BLOCKQUOTE_ALERT_R);
351
+ const matchAlert = (capture[0] ?? "").replace(BLOCKQUOTE_TRIM_LEFT_MULTILINE_R, "").match(BLOCKQUOTE_ALERT_R);
185
352
  const alert = matchAlert?.[1];
186
353
  const content = matchAlert?.[2] ?? "";
187
354
  return {
@@ -208,6 +375,7 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
208
375
  }
209
376
  },
210
377
  [RuleType.breakLine]: {
378
+ _scope: SCOPE_BOTH,
211
379
  _qualify: [" "],
212
380
  _match: anyScopeRegex(BREAK_LINE_R),
213
381
  _order: Priority.HIGH,
@@ -217,6 +385,7 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
217
385
  }
218
386
  },
219
387
  [RuleType.breakThematic]: {
388
+ _scope: SCOPE_BLOCK,
220
389
  _qualify: [
221
390
  "--",
222
391
  "__",
@@ -233,6 +402,7 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
233
402
  }
234
403
  },
235
404
  [RuleType.codeBlock]: {
405
+ _scope: SCOPE_BLOCK,
236
406
  _qualify: [" "],
237
407
  _match: blockRegex(CODE_BLOCK_R),
238
408
  _order: Priority.MAX,
@@ -240,7 +410,7 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
240
410
  return {
241
411
  type: RuleType.codeBlock,
242
412
  lang: void 0,
243
- text: unescapeString(trimEnd(capture[0].replace(/^ {4}/gm, "")))
413
+ text: unescapeString(trimEnd((capture[0] ?? "").replace(/^ {4}/gm, "")))
244
414
  };
245
415
  },
246
416
  _render(node, _, state = {}) {
@@ -251,6 +421,7 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
251
421
  }
252
422
  },
253
423
  [RuleType.codeFenced]: {
424
+ _scope: SCOPE_BLOCK,
254
425
  _qualify: ["```", "~~~"],
255
426
  _match: blockRegex(CODE_BLOCK_FENCED_R),
256
427
  _order: Priority.MAX,
@@ -258,43 +429,48 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
258
429
  return {
259
430
  attrs: attrStringToMap("code", capture[3] ?? ""),
260
431
  lang: capture[2] || void 0,
261
- text: capture[4],
432
+ text: capture[4] ?? "",
262
433
  type: RuleType.codeBlock
263
434
  };
264
435
  }
265
436
  },
266
437
  [RuleType.codeInline]: {
267
- _qualify: ["`"],
438
+ _scope: SCOPE_INLINE,
439
+ _firstChars: [96],
440
+ _qualify: (source) => source.charCodeAt(0) === 96 && source.indexOf("`", 1) !== -1,
268
441
  _match: simpleInlineRegex(CODE_INLINE_R),
269
442
  _order: Priority.LOW,
270
443
  _parse(capture) {
271
- return { text: unescapeString(capture[2]) };
444
+ return { text: unescapeString(capture[2] ?? "") };
272
445
  },
273
446
  _render(node, _, state = {}) {
274
447
  return createElement("code", { key: state.key }, node.text);
275
448
  }
276
449
  },
277
450
  [RuleType.footnote]: {
451
+ _scope: SCOPE_INLINE,
278
452
  _qualify: ["[^"],
279
453
  _match: blockRegex(FOOTNOTE_R),
280
454
  _order: Priority.MAX,
281
455
  _parse(capture) {
282
- footnotes.push({
283
- footnote: capture[2],
284
- identifier: capture[1]
456
+ scope.current.footnotes.push({
457
+ footnote: capture[2] ?? "",
458
+ identifier: capture[1] ?? ""
285
459
  });
286
460
  return {};
287
461
  },
288
462
  _render: renderNothing
289
463
  },
290
464
  [RuleType.footnoteReference]: {
465
+ _scope: SCOPE_INLINE,
291
466
  _qualify: ["[^"],
292
467
  _match: inlineRegex(FOOTNOTE_REFERENCE_R),
293
468
  _order: Priority.HIGH,
294
469
  _parse(capture) {
470
+ const text = capture[1] ?? "";
295
471
  return {
296
- target: `#${slug(capture[1])}`,
297
- text: capture[1]
472
+ target: `#${slug(text)}`,
473
+ text
298
474
  };
299
475
  },
300
476
  _render(node, _, state = {}) {
@@ -305,11 +481,12 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
305
481
  }
306
482
  },
307
483
  [RuleType.gfmTask]: {
484
+ _scope: SCOPE_INLINE,
308
485
  _qualify: ["[ ]", "[x]"],
309
486
  _match: inlineRegex(GFM_TASK_R),
310
487
  _order: Priority.HIGH,
311
488
  _parse(capture) {
312
- return { completed: capture[1].toLowerCase() === "x" };
489
+ return { completed: (capture[1] ?? "").toLowerCase() === "x" };
313
490
  },
314
491
  _render(node, _, state = {}) {
315
492
  return createElement("input", {
@@ -321,14 +498,16 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
321
498
  }
322
499
  },
323
500
  [RuleType.heading]: {
501
+ _scope: SCOPE_BLOCK,
324
502
  _qualify: ["#"],
325
503
  _match: blockRegex(options.enforceAtxHeadings ? HEADING_ATX_COMPLIANT_R : HEADING_R),
326
504
  _order: Priority.HIGH,
327
505
  _parse(capture, parse, state) {
506
+ const text = capture[2] ?? "";
328
507
  return {
329
- children: parseInline(parse, capture[2], state),
330
- id: slug(capture[2]),
331
- level: capture[1].length
508
+ children: parseInline(parse, text, state),
509
+ id: slug(text),
510
+ level: capture[1]?.length ?? 1
332
511
  };
333
512
  },
334
513
  _render(node, output, state = {}) {
@@ -339,7 +518,9 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
339
518
  }
340
519
  },
341
520
  [RuleType.headingSetext]: {
342
- _qualify: (source) => {
521
+ _scope: SCOPE_BLOCK,
522
+ _qualify: (source, state) => {
523
+ if (state.prevCaptureIndent === void 0 || state.inline || state.simple) return false;
343
524
  const nlIndex = source.indexOf("\n");
344
525
  return nlIndex > 0 && nlIndex < source.length - 1 && (source[nlIndex + 1] === "=" || source[nlIndex + 1] === "-");
345
526
  },
@@ -347,52 +528,15 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
347
528
  _order: Priority.MAX,
348
529
  _parse(capture, parse, state) {
349
530
  return {
350
- children: parseInline(parse, capture[1], state),
531
+ children: parseInline(parse, capture[1] ?? "", state),
351
532
  level: capture[2] === "=" ? 1 : 2,
352
533
  type: RuleType.heading
353
534
  };
354
535
  }
355
536
  },
356
- [RuleType.htmlBlock]: {
357
- _qualify: (source) => {
358
- if (options.disableParsingRawHTML) return false;
359
- const match = HTML_BLOCK_OPENING_TAG_R.exec(source);
360
- if (!match) return false;
361
- return getClosingTagRegex(match[1]).test(source);
362
- },
363
- _match: anyScopeRegex(HTML_BLOCK_ELEMENT_R),
364
- _order: Priority.HIGH,
365
- _parse(capture, parse, state) {
366
- const whitespace = capture[3].match(HTML_LEFT_TRIM_AMOUNT_R)?.[1] ?? "";
367
- const trimmed = trimLeadingWhitespaceOutsideFences(capture[3], whitespace);
368
- const parseFunc = containsBlockSyntax(trimmed) ? parseBlock : parseInline;
369
- const tagName = capture[1].trim();
370
- const noInnerParse = DO_NOT_PROCESS_HTML_ELEMENTS.indexOf(tagName.toLowerCase()) !== -1;
371
- const tag = noInnerParse ? tagName.toLowerCase() : tagName;
372
- const ast = {
373
- attrs: attrStringToMap(tag, capture[2] ?? ""),
374
- noInnerParse,
375
- tag
376
- };
377
- state.inAnchor = state.inAnchor || tagName.toLowerCase() === "a";
378
- if (noInnerParse) ast.text = capture[3];
379
- else {
380
- const prevInHTML = state.inHTML;
381
- state.inHTML = true;
382
- ast.children = parseFunc(parse, trimmed, state);
383
- state.inHTML = prevInHTML;
384
- }
385
- state.inAnchor = false;
386
- return ast;
387
- },
388
- _render(node, output, state = {}) {
389
- return createElement(node.tag, {
390
- key: state.key,
391
- ...node.attrs ?? {}
392
- }, node.text ?? (node.children ? output(node.children, state) : ""));
393
- }
394
- },
537
+ [RuleType.htmlBlock]: pairedElementRule(matchHtmlBlockElement, Priority.HIGH, true, SCOPE_BOTH),
395
538
  [RuleType.htmlComment]: {
539
+ _scope: SCOPE_BOTH,
396
540
  _qualify: ["<!"],
397
541
  _match: anyScopeRegex(HTML_COMMENT_R),
398
542
  _order: Priority.HIGH,
@@ -400,14 +544,12 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
400
544
  _render: renderNothing
401
545
  },
402
546
  [RuleType.htmlSelfClosing]: {
403
- _qualify: (source) => {
404
- if (options.disableParsingRawHTML) return false;
405
- return HTML_SELF_CLOSING_OPENING_TAG_R.test(source);
406
- },
407
- _match: anyScopeRegex(HTML_SELF_CLOSING_ELEMENT_R),
547
+ _scope: SCOPE_BOTH,
548
+ _qualify: ["<"],
549
+ _match: anyScopeRegex(matchSelfClosingElement),
408
550
  _order: Priority.HIGH,
409
551
  _parse(capture) {
410
- const tag = capture[1].trim();
552
+ const tag = (capture[1] ?? "").trim();
411
553
  return {
412
554
  attrs: attrStringToMap(tag, capture[2] || ""),
413
555
  tag
@@ -420,34 +562,10 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
420
562
  });
421
563
  }
422
564
  },
423
- [RuleType.customComponent]: {
424
- _qualify: (source) => CUSTOM_COMPONENT_OPENING_TAG_R.test(source),
425
- _match: anyScopeRegex(CUSTOM_COMPONENT_R),
426
- _order: Priority.MAX,
427
- _parse(capture, parse, state) {
428
- const whitespace = capture[3].match(HTML_LEFT_TRIM_AMOUNT_R)?.[1] ?? "";
429
- const trimmed = trimLeadingWhitespaceOutsideFences(capture[3], whitespace);
430
- const parseFunc = containsBlockSyntax(trimmed) ? parseBlock : parseInline;
431
- const tag = capture[1].trim();
432
- const ast = {
433
- attrs: attrStringToMap(tag, capture[2] ?? ""),
434
- noInnerParse: false,
435
- tag
436
- };
437
- const prevInHTML = state.inHTML;
438
- state.inHTML = true;
439
- ast.children = parseFunc(parse, trimmed, state);
440
- state.inHTML = prevInHTML;
441
- return ast;
442
- },
443
- _render(node, output, state = {}) {
444
- return createElement(node.tag, {
445
- key: state.key,
446
- ...node.attrs ?? {}
447
- }, node.text ?? (node.children ? output(node.children, state) : ""));
448
- }
449
- },
565
+ [RuleType.customComponent]: pairedElementRule(matchCustomComponent, Priority.MAX, false, SCOPE_BOTH),
450
566
  [RuleType.paragraph]: {
567
+ _scope: SCOPE_BLOCK,
568
+ _qualify: (_source, state) => !state.inline && !state.simple,
451
569
  _match: matchParagraph,
452
570
  _order: Priority.LOW,
453
571
  _parse: parseCaptureInline,
@@ -456,14 +574,16 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
456
574
  }
457
575
  },
458
576
  [RuleType.image]: {
459
- _qualify: ["!["],
577
+ _scope: SCOPE_INLINE,
578
+ _firstChars: [33],
579
+ _qualify: (source) => source.startsWith("![") && source.indexOf("](") !== -1,
460
580
  _match: simpleInlineRegex(IMAGE_R),
461
581
  _order: Priority.HIGH,
462
582
  _parse(capture) {
463
583
  return {
464
- alt: unescapeString(capture[1]),
465
- target: unescapeString(capture[2]),
466
- title: unescapeString(capture[3])
584
+ alt: unescapeString(capture[1] ?? ""),
585
+ target: unescapeString(capture[2] ?? ""),
586
+ title: capture[3] ? unescapeString(capture[3]) : void 0
467
587
  };
468
588
  },
469
589
  _render(node, _, state = {}) {
@@ -476,14 +596,16 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
476
596
  }
477
597
  },
478
598
  [RuleType.link]: {
479
- _qualify: ["["],
599
+ _scope: SCOPE_INLINE,
600
+ _firstChars: [91],
601
+ _qualify: (source) => source.charCodeAt(0) === 91 && source.indexOf("](") !== -1,
480
602
  _match: inlineRegex(LINK_R),
481
603
  _order: Priority.LOW,
482
604
  _parse(capture, parse, state) {
483
605
  return {
484
- children: parseSimpleInline(parse, capture[1], state),
485
- target: unescapeString(capture[2]),
486
- title: unescapeString(capture[3])
606
+ children: parseSimpleInline(parse, capture[1] ?? "", state),
607
+ target: unescapeString(capture[2] ?? ""),
608
+ title: capture[3] ? unescapeString(capture[3]) : void 0
487
609
  };
488
610
  },
489
611
  _render(node, output, state = {}) {
@@ -496,11 +618,12 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
496
618
  }
497
619
  },
498
620
  [RuleType.linkAngleBraceStyleDetector]: {
621
+ _scope: SCOPE_INLINE,
499
622
  _qualify: ["<"],
500
623
  _match: inlineRegex(LINK_AUTOLINK_R),
501
624
  _order: Priority.MAX,
502
625
  _parse(capture) {
503
- let target = capture[1];
626
+ let target = capture[1] ?? "";
504
627
  let isEmail = false;
505
628
  if (target.indexOf("@") !== -1 && target.indexOf("//") === -1) {
506
629
  isEmail = true;
@@ -517,21 +640,26 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
517
640
  }
518
641
  },
519
642
  [RuleType.linkBareUrlDetector]: {
520
- _qualify: (source, state) => !!(state.inline && !state.inAnchor && !options.disableAutoLink && (startsWith(source, "http://") || startsWith(source, "https://"))),
643
+ _scope: SCOPE_INLINE,
644
+ _firstChars: [104],
645
+ _qualify: (source, state) => Boolean(state.inline && !state.inAnchor && !options.disableAutoLink && (startsWith(source, "http://") || startsWith(source, "https://"))),
521
646
  _match: inlineRegex(LINK_AUTOLINK_BARE_URL_R),
522
647
  _order: Priority.MAX,
523
648
  _parse(capture) {
649
+ const url = capture[1] ?? "";
524
650
  return {
525
651
  children: [{
526
- text: capture[1],
652
+ text: url,
527
653
  type: RuleType.text
528
654
  }],
529
- target: capture[1],
655
+ target: url,
530
656
  type: RuleType.link
531
657
  };
532
658
  }
533
659
  },
534
660
  [RuleType.newlineCoalescer]: {
661
+ _scope: SCOPE_BOTH,
662
+ _qualify: ["\n"],
535
663
  _match: blockRegex(CONSECUTIVE_NEWLINE_R),
536
664
  _order: Priority.LOW,
537
665
  _parse: captureNothing,
@@ -542,12 +670,14 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
542
670
  [RuleType.orderedList]: generateListRule(1),
543
671
  [RuleType.unorderedList]: generateListRule(2),
544
672
  [RuleType.ref]: {
673
+ _scope: SCOPE_BOTH,
545
674
  _qualify: ["["],
546
675
  _match: anyScopeRegex(REFERENCE_IMAGE_OR_LINK),
547
676
  _order: Priority.MAX,
548
677
  _parse(capture) {
549
- refs[capture[1]] = {
550
- target: capture[2],
678
+ const identifier = capture[1];
679
+ if (identifier !== void 0) scope.current.references[identifier] = {
680
+ target: capture[2] ?? "",
551
681
  title: capture[4]
552
682
  };
553
683
  return {};
@@ -555,17 +685,19 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
555
685
  _render: renderNothing
556
686
  },
557
687
  [RuleType.refImage]: {
558
- _qualify: ["!["],
688
+ _scope: SCOPE_INLINE,
689
+ _firstChars: [33],
690
+ _qualify: (source) => source.startsWith("![") && source.indexOf("](") === -1,
559
691
  _match: simpleInlineRegex(REFERENCE_IMAGE_R),
560
692
  _order: Priority.MAX,
561
693
  _parse(capture) {
562
694
  return {
563
695
  alt: capture[1] ? unescapeString(capture[1]) : void 0,
564
- ref: capture[2]
696
+ ref: capture[2] ?? ""
565
697
  };
566
698
  },
567
699
  _render(node, _, state = {}) {
568
- const ref = refs[node.ref];
700
+ const ref = scope.current.references[node.ref];
569
701
  if (!ref) return null;
570
702
  return createElement("img", {
571
703
  key: state.key,
@@ -576,18 +708,20 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
576
708
  }
577
709
  },
578
710
  [RuleType.refLink]: {
579
- _qualify: (source) => source[0] === "[" && source.indexOf("](") === -1,
711
+ _scope: SCOPE_INLINE,
712
+ _firstChars: [91],
713
+ _qualify: (source) => source.indexOf("](") === -1,
580
714
  _match: inlineRegex(REFERENCE_LINK_R),
581
715
  _order: Priority.MAX,
582
716
  _parse(capture, parse, state) {
583
717
  return {
584
- children: parseSimpleInline(parse, capture[1], state),
585
- fallbackChildren: capture[0],
586
- ref: capture[2]
718
+ children: parseSimpleInline(parse, capture[1] ?? "", state),
719
+ fallbackChildren: capture[0] ?? "",
720
+ ref: capture[2] ?? ""
587
721
  };
588
722
  },
589
723
  _render(node, output, state = {}) {
590
- const ref = refs[node.ref];
724
+ const ref = scope.current.references[node.ref];
591
725
  if (!ref) return createElement("span", { key: state.key }, node.fallbackChildren);
592
726
  return createElement("a", {
593
727
  key: state.key,
@@ -597,6 +731,7 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
597
731
  }
598
732
  },
599
733
  [RuleType.table]: {
734
+ _scope: SCOPE_BLOCK,
600
735
  _qualify: ["|"],
601
736
  _match: blockRegex(NP_TABLE_R),
602
737
  _order: Priority.HIGH,
@@ -604,7 +739,7 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
604
739
  state.inline = true;
605
740
  const align = capture[2] ? parseTableAlign(capture[2]) : [];
606
741
  const cells = capture[3] ? parseTableCells(capture[3], parse, state) : [];
607
- const header = parseTableRow(capture[1], parse, state, !!cells.length);
742
+ const header = parseTableRow(capture[1] ?? "", parse, state, !!cells.length);
608
743
  state.inline = false;
609
744
  return cells.length ? {
610
745
  align,
@@ -619,17 +754,19 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
619
754
  _render(node, output, state = {}) {
620
755
  const table = node;
621
756
  const getStyle = (i) => table.align[i] && table.align[i] !== "left" ? { textAlign: table.align[i] } : {};
622
- return createElement("table", { key: state.key }, createElement("thead", null, createElement("tr", null, ...table.header.map((c, i) => createElement("th", {
757
+ return createElement("table", { key: state.key }, createElement("thead", { key: "thead" }, createElement("tr", null, ...table.header.map((c, i) => createElement("th", {
623
758
  key: i,
624
759
  style: getStyle(i)
625
- }, output(c, state))))), createElement("tbody", null, ...table.cells.map((row, i) => createElement("tr", { key: i }, ...row.map((c, j) => createElement("td", {
760
+ }, output(c, state))))), createElement("tbody", { key: "tbody" }, ...table.cells.map((row, i) => createElement("tr", { key: i }, ...row.map((c, j) => createElement("td", {
626
761
  key: j,
627
762
  style: getStyle(j)
628
763
  }, output(c, state)))))));
629
764
  }
630
765
  },
631
766
  [RuleType.tableSeparator]: {
632
- _match: (source, state) => state.inTable && source[0] === "|" ? /^\|/.exec(source) : null,
767
+ _scope: SCOPE_INLINE,
768
+ _qualify: ["|"],
769
+ _match: (source, state) => state.inTable ? /^\|/.exec(source) : null,
633
770
  _order: Priority.HIGH,
634
771
  _parse() {
635
772
  return { type: RuleType.tableSeparator };
@@ -639,90 +776,180 @@ const createRules = (createElement, ctx, options, footnotes, refs, attrStringToM
639
776
  }
640
777
  },
641
778
  [RuleType.text]: {
779
+ _scope: SCOPE_INLINE,
642
780
  _match: allowInline((source, _state) => {
643
- const shortMatch = SHORTCODE_R.exec(source);
644
- if (shortMatch) return shortMatch;
645
- return TEXT_PLAIN_R.exec(source) || /^[\s\S]/.exec(source);
781
+ const len = source.length;
782
+ if (source.charCodeAt(0) === 10) {
783
+ let j = 1;
784
+ while (j < len && source.charCodeAt(j) === 32) j++;
785
+ if (j < len && (source.charCodeAt(j) === 42 || source.charCodeAt(j) === 43 || source.charCodeAt(j) === 45 || source.charCodeAt(j) >= 48 && source.charCodeAt(j) <= 57)) return [source.slice(0, j)];
786
+ return ["\n"];
787
+ }
788
+ if (source.charCodeAt(0) === 58) {
789
+ const shortMatch = SHORTCODE_R.exec(source);
790
+ if (shortMatch) return shortMatch;
791
+ }
792
+ TEXT_RUN_R.lastIndex = 1;
793
+ TEXT_RUN_R.test(source);
794
+ TEXT_CAPTURE[0] = source.slice(0, trimHardBreak(source, TEXT_RUN_R.lastIndex));
795
+ return TEXT_CAPTURE;
646
796
  }),
647
797
  _order: Priority.MIN,
648
798
  _parse(capture) {
649
- const text = capture[0];
650
- return { text: text.indexOf("&") === -1 ? text : text.replace(HTML_CHAR_CODE_R, (f, i) => {
651
- if (i.startsWith("#x")) return String.fromCharCode(parseInt(i.slice(2), 16));
652
- if (i.startsWith("#")) return String.fromCharCode(parseInt(i.slice(1), 10));
653
- return namedCodesToUnicode[i] || f;
654
- }) };
799
+ const text = capture[0] ?? "";
800
+ return {
801
+ type: RuleType.text,
802
+ text: text.charCodeAt(0) !== 38 ? text : text.replace(HTML_CHAR_CODE_R, (f, i) => {
803
+ if (i.startsWith("#x")) return String.fromCharCode(parseInt(i.slice(2), 16));
804
+ if (i.startsWith("#")) return String.fromCharCode(parseInt(i.slice(1), 10));
805
+ return namedCodesToUnicode[i] || f;
806
+ })
807
+ };
655
808
  },
656
809
  _render(node) {
657
810
  return node.text;
658
811
  }
659
812
  },
660
813
  [RuleType.textBolded]: {
661
- _qualify: ["**", "__"],
814
+ _scope: SCOPE_INLINE,
815
+ _firstChars: [42, 95],
816
+ _qualify: (source) => {
817
+ const c = source.charCodeAt(0);
818
+ if (c === 42) {
819
+ const next = source.charCodeAt(2);
820
+ if (next === 32 || next === 10 || next === 9) return false;
821
+ return source.charCodeAt(1) === 42 && source.indexOf("**", 2) !== -1;
822
+ }
823
+ if (c === 95) {
824
+ const next = source.charCodeAt(2);
825
+ if (next === 32 || next === 10 || next === 9) return false;
826
+ return source.charCodeAt(1) === 95 && source.indexOf("__", 2) !== -1;
827
+ }
828
+ return false;
829
+ },
662
830
  _match: simpleInlineRegex(TEXT_BOLD_R),
663
831
  _order: Priority.MED,
664
832
  _parse(capture, parse, state) {
665
- return { children: parse(capture[2], state) };
833
+ return { children: parse(capture[2] ?? "", state) };
666
834
  },
667
835
  _render(node, output, state = {}) {
668
836
  return createElement("strong", { key: state.key }, output(node.children, state));
669
837
  }
670
838
  },
671
839
  [RuleType.textEmphasized]: {
672
- _qualify: ["*", "_"],
840
+ _scope: SCOPE_INLINE,
841
+ _firstChars: [42, 95],
842
+ _qualify: (source) => {
843
+ const c = source.charCodeAt(0);
844
+ const next = source.charCodeAt(1);
845
+ if (next === 32 || next === 10 || next === 9) return false;
846
+ return c === 42 && source.indexOf("*", 1) !== -1 || c === 95 && source.indexOf("_", 1) !== -1;
847
+ },
673
848
  _match: simpleInlineRegex(TEXT_EMPHASIZED_R),
674
849
  _order: Priority.LOW,
675
850
  _parse(capture, parse, state) {
676
- return { children: parse(capture[2], state) };
851
+ return { children: parse(capture[2] ?? "", state) };
677
852
  },
678
853
  _render(node, output, state = {}) {
679
854
  return createElement("em", { key: state.key }, output(node.children, state));
680
855
  }
681
856
  },
682
857
  [RuleType.textEscaped]: {
858
+ _scope: SCOPE_INLINE,
683
859
  _qualify: ["\\"],
684
860
  _match: simpleInlineRegex(TEXT_ESCAPED_R),
685
861
  _order: Priority.HIGH,
686
862
  _parse(capture) {
687
863
  return {
688
- text: capture[1],
864
+ text: capture[1] ?? "",
689
865
  type: RuleType.text
690
866
  };
691
867
  }
692
868
  },
693
869
  [RuleType.textMarked]: {
694
- _qualify: ["=="],
870
+ _scope: SCOPE_INLINE,
871
+ _firstChars: [61],
872
+ _qualify: (source) => {
873
+ const next = source.charCodeAt(2);
874
+ if (next === 32 || next === 10 || next === 9) return false;
875
+ return source.charCodeAt(1) === 61 && source.indexOf("==", 2) !== -1;
876
+ },
695
877
  _match: simpleInlineRegex(TEXT_MARKED_R),
696
878
  _order: Priority.LOW,
697
- _parse: parseCaptureInline,
879
+ _parse(capture, parse, state) {
880
+ return { children: parseCaptureInline(capture, parse, state).children };
881
+ },
698
882
  _render(node, output, state = {}) {
699
883
  return createElement("mark", { key: state.key }, output(node.children, state));
700
884
  }
701
885
  },
702
886
  [RuleType.textStrikethroughed]: {
703
- _qualify: ["~~"],
887
+ _scope: SCOPE_INLINE,
888
+ _firstChars: [126],
889
+ _qualify: (source) => {
890
+ const next = source.charCodeAt(2);
891
+ if (next === 32 || next === 10 || next === 9) return false;
892
+ return source.charCodeAt(1) === 126 && source.indexOf("~~", 2) !== -1;
893
+ },
704
894
  _match: simpleInlineRegex(TEXT_STRIKETHROUGHED_R),
705
895
  _order: Priority.LOW,
706
- _parse: parseCaptureInline,
896
+ _parse(capture, parse, state) {
897
+ return { children: parseCaptureInline(capture, parse, state).children };
898
+ },
707
899
  _render(node, output, state = {}) {
708
900
  return createElement("del", { key: state.key }, output(node.children, state));
709
901
  }
710
902
  }
711
903
  };
712
904
  };
905
+ const STRIP_INDENT_REGEXES = [
906
+ null,
907
+ /^ {1,1}/gm,
908
+ /^ {1,2}/gm,
909
+ /^ {1,3}/gm,
910
+ /^ {1,4}/gm,
911
+ /^ {1,5}/gm,
912
+ /^ {1,6}/gm,
913
+ /^ {1,7}/gm,
914
+ /^ {1,8}/gm
915
+ ];
916
+ /** Scratch capture reused by the text rule; see its `_match`. */
917
+ const TEXT_CAPTURE = [""];
713
918
  /**
714
- * `createElement` is only ever reached from a rule's `_render`, so a parse-only
715
- * caller can hand over this stub instead of building an element factory.
919
+ * Consumes a run of plain text, stopping before the next character that could
920
+ * open another inline rule or a bare URL. Anchored so `test` leaves the stop
921
+ * offset in `lastIndex` without allocating a match array, and so the scan
922
+ * itself runs in the regex engine.
923
+ *
924
+ * The bare-URL guard is an alternation branch rather than a per-character
925
+ * lookahead, because a lookahead is re-evaluated at every offset while this
926
+ * branch is only reached on an `h`. `&` stops a run purely so that an HTML
927
+ * entity can only ever sit at offset 0 of a text node; adjacent text nodes are
928
+ * merged by the parser, so the tree is unchanged.
716
929
  */
717
- const noopCreateElement = () => null;
930
+ const TEXT_RUN_R = /(?:[^\n&h!*:<=[\\_`~]+|h(?!ttp))*/y;
931
+ /**
932
+ * Pull a run's stop offset back before a hard line break it just swallowed.
933
+ *
934
+ * A run never contains a newline, so ` \n` can only sit immediately before the
935
+ * stop offset — which makes this O(1) check equivalent to the per-character
936
+ * `(?! {2}\n)` lookahead the run scanner used to carry.
937
+ */
938
+ const trimHardBreak = (source, end) => end >= 3 && source.charCodeAt(end) === 10 && source.charCodeAt(end - 1) === 32 && source.charCodeAt(end - 2) === 32 ? end - 2 : end;
939
+ const noopCreateElement = (type, props, ...children) => ({
940
+ children,
941
+ props,
942
+ type
943
+ });
718
944
  /**
719
945
  * Builds the rule set backing one document.
720
946
  *
721
- * Rules close over the document's footnote and reference tables, so they cannot
722
- * be shared across documents — but a single document's parse and render phases
723
- * can share one set, which is what `compile` does.
947
+ * Rules read the document's footnote and reference tables through `scope`
948
+ * rather than closing over them, so one set serves every document sharing the
949
+ * same options. Building this graph dominated the cost of compiling the many
950
+ * short strings a dictionary holds; `getRuleSet` caches it.
724
951
  */
725
- const createDocumentRules = (createElement, ctx, options, footnotes, refs) => {
952
+ const createDocumentRules = (createElement, ctx, options, scope) => {
726
953
  const attrStringToMap = (tag, str) => {
727
954
  if (!str?.trim()) return null;
728
955
  const attributes = str.match(ATTR_EXTRACTOR_R);
@@ -735,7 +962,7 @@ const createDocumentRules = (createElement, ctx, options, footnotes, refs) => {
735
962
  const mappedKey = ATTRIBUTE_TO_NODE_PROP_MAP[key] ?? key;
736
963
  if (mappedKey === "ref") return map;
737
964
  map[mappedKey] = attributeValueToNodePropValue(tag, key, value, ctx.sanitizer ?? sanitizer);
738
- if (typeof map[mappedKey] === "string" && (HTML_BLOCK_ELEMENT_R.test(map[mappedKey]) || HTML_SELF_CLOSING_ELEMENT_R.test(map[mappedKey]))) map[mappedKey] = parseMarkdown(map[mappedKey].trim(), ctx, options).ast;
965
+ if (typeof map[mappedKey] === "string" && startsWithElement(map[mappedKey])) map[mappedKey] = parseMarkdown(map[mappedKey].trim(), ctx, options).ast;
739
966
  } else if (raw !== "style") map[ATTRIBUTE_TO_NODE_PROP_MAP[raw] ?? raw] = true;
740
967
  return map;
741
968
  }, {});
@@ -745,19 +972,18 @@ const createDocumentRules = (createElement, ctx, options, footnotes, refs) => {
745
972
  CODE_BLOCK_FENCED_R,
746
973
  CODE_BLOCK_R,
747
974
  options.enforceAtxHeadings ? HEADING_ATX_COMPLIANT_R : HEADING_R,
748
- HEADING_SETEXT_R,
749
975
  NP_TABLE_R,
750
976
  ORDERED_LIST_R,
751
977
  UNORDERED_LIST_R,
752
- CUSTOM_COMPONENT_R
978
+ startsWithCustomComponent
753
979
  ];
754
980
  const blockSyntaxes = options.disableParsingRawHTML ? nonParagraphBlockSyntaxes : [
755
981
  ...nonParagraphBlockSyntaxes,
756
982
  PARAGRAPH_R,
757
- HTML_BLOCK_ELEMENT_R,
983
+ startsWithHtmlBlockElement,
758
984
  HTML_COMMENT_R,
759
- HTML_SELF_CLOSING_ELEMENT_R,
760
- CUSTOM_COMPONENT_R
985
+ startsWithSelfClosingElement,
986
+ startsWithCustomComponent
761
987
  ];
762
988
  const containsBlockSyntax = (input) => {
763
989
  const cleaned = input.replace(TRIM_STARTING_NEWLINES, "");
@@ -765,17 +991,16 @@ const createDocumentRules = (createElement, ctx, options, footnotes, refs) => {
765
991
  if (slice.indexOf("\n\n") !== -1) return true;
766
992
  return some(blockSyntaxes, slice);
767
993
  };
768
- const baseRules = createRules(createElement, ctx, options, footnotes, refs, attrStringToMap, containsBlockSyntax, nonParagraphBlockSyntaxes);
994
+ const baseRules = createRules(createElement, ctx, options, scope, attrStringToMap, containsBlockSyntax, nonParagraphBlockSyntaxes);
769
995
  return options.disableParsingRawHTML ? Object.keys(baseRules).reduce((acc, key) => {
770
996
  if (key !== RuleType.htmlBlock && key !== RuleType.htmlSelfClosing) acc[key] = baseRules[key];
771
997
  return acc;
772
998
  }, {}) : baseRules;
773
999
  };
774
1000
  /** Runs the parse phase against an already-built rule set. */
775
- const parseWithRules = (markdown, options, rules, footnotes, refs) => {
776
- const parser = parserFor(rules);
777
- const result = options.preserveFrontmatter ? markdown : markdown.replace(FRONT_MATTER_R, "");
778
- const trimmedStart = result.replace(TRIM_STARTING_NEWLINES, "");
1001
+ const parseWithRules = (markdown, options, parser, footnotes, refs) => {
1002
+ const result = !options.preserveFrontmatter && markdown.charCodeAt(0) === 45 ? markdown.replace(FRONT_MATTER_R, "") : markdown;
1003
+ const trimmedStart = result.charCodeAt(0) === 10 ? result.replace(TRIM_STARTING_NEWLINES, "") : result;
779
1004
  const inline = options.forceInline || !options.forceBlock && SHOULD_RENDER_AS_BLOCK_R.test(trimmedStart) === false;
780
1005
  const ast = parser(inline ? result : `${trimEnd(trimmedStart)}\n\n`, { inline });
781
1006
  if (footnotes.length > 0) for (const def of footnotes) def.parsedAst = parser(def.footnote, { inline: true });
@@ -787,11 +1012,10 @@ const parseWithRules = (markdown, options, rules, footnotes, refs) => {
787
1012
  };
788
1013
  };
789
1014
  /** Runs the render phase against an already-built rule set. */
790
- const renderWithRules = (parsed, ctx, options, rules, createElement) => {
1015
+ const renderWithRules = (parsed, ctx, options, emitter, createElement) => {
791
1016
  const components = ctx.components ?? {};
792
1017
  const slug = createSlugger(ctx);
793
1018
  const footnotes = parsed.footnotes || [];
794
- const emitter = renderFor(createRenderer(rules, options.renderRule));
795
1019
  const inline = parsed.inline;
796
1020
  const arr = emitter(parsed.ast, { inline });
797
1021
  while (typeof arr[arr.length - 1] === "string" && !arr[arr.length - 1].trim()) arr.pop();
@@ -801,6 +1025,7 @@ const renderWithRules = (parsed, ctx, options, rules, createElement) => {
801
1025
  if (arr.length > 1 || options.forceWrapper) return createElement(wrapper, { key: "outer" }, arr);
802
1026
  if (arr.length === 1) {
803
1027
  const node = arr[0];
1028
+ if (Array.isArray(node)) return createElement(wrapper, { key: "outer" }, node);
804
1029
  if (typeof node === "string") {
805
1030
  const spanProps = { key: "outer" };
806
1031
  if (!inline && components) {
@@ -826,27 +1051,29 @@ const renderWithRules = (parsed, ctx, options, rules, createElement) => {
826
1051
  }, def.identifier, emitter(def.parsedAst || def.footnote, { inline: true }))))) : node;
827
1052
  };
828
1053
  const parseMarkdown = (markdown = "", ctx, options = {}) => {
829
- const footnotes = [];
830
- const refs = {};
831
- const rules = createDocumentRules(noopCreateElement, ctx, options, footnotes, refs);
832
- return parseWithRules(markdown, options, rules, footnotes, refs);
1054
+ const { parse, scope } = getRuleSet(ctx, options, true);
1055
+ const documentScope = createDocumentScope();
1056
+ return withDocumentScope(scope, documentScope, () => parseWithRules(markdown, options, parse, documentScope.footnotes, documentScope.references));
833
1057
  };
834
1058
  const renderMarkdownAst = (parsed, ctx, options = {}) => {
835
- const createElement = createElementFactory(ctx, options);
836
- const rules = createDocumentRules(createElement, ctx, options, parsed.footnotes ?? [], parsed.references ?? {});
837
- return renderWithRules(parsed, ctx, options, rules, createElement);
1059
+ const { emit, createElement, scope } = getRuleSet(ctx, options, false);
1060
+ const documentScope = {
1061
+ footnotes: parsed.footnotes ?? [],
1062
+ references: parsed.references ?? {}
1063
+ };
1064
+ return withDocumentScope(scope, documentScope, () => renderWithRules(parsed, ctx, options, emit, createElement));
838
1065
  };
839
1066
  const compile = (markdown = "", ctx, options = {}) => {
840
1067
  if (typeof markdown !== "string") {
841
1068
  console.error("intlayer: the first argument must be a string. Received", typeof markdown);
842
1069
  throw new Error("intlayer: the first argument must be a string");
843
1070
  }
844
- const footnotes = [];
845
- const refs = {};
846
- const createElement = createElementFactory(ctx, options);
847
- const rules = createDocumentRules(createElement, ctx, options, footnotes, refs);
848
- const parsed = parseWithRules(markdown, options, rules, footnotes, refs);
849
- return renderWithRules(parsed, ctx, options, rules, createElement);
1071
+ const { parse, emit, createElement, scope } = getRuleSet(ctx, options, false);
1072
+ const documentScope = createDocumentScope();
1073
+ return withDocumentScope(scope, documentScope, () => {
1074
+ const parsed = parseWithRules(markdown, options, parse, documentScope.footnotes, documentScope.references);
1075
+ return renderWithRules(parsed, ctx, options, emit, createElement);
1076
+ });
850
1077
  };
851
1078
  const createCompiler = (ctx) => (markdown, options) => compile(markdown, ctx, options);
852
1079
  const compileWithOptions = (markdown, runtime, options = {}) => {
@@ -861,5 +1088,5 @@ const compileWithOptions = (markdown, runtime, options = {}) => {
861
1088
  };
862
1089
 
863
1090
  //#endregion
864
- export { compile, compileWithOptions, createCompiler, parseMarkdown, renderMarkdownAst };
1091
+ export { compile, compileWithOptions, createCompiler, getRuleSet, parseMarkdown, renderMarkdownAst };
865
1092
  //# sourceMappingURL=compiler.mjs.map