@readme/markdown 14.4.0 → 14.4.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.
package/dist/main.js CHANGED
@@ -11594,6 +11594,7 @@ __webpack_require__.r(__webpack_exports__);
11594
11594
  __webpack_require__.d(__webpack_exports__, {
11595
11595
  Components: () => (/* reexport */ components_namespaceObject),
11596
11596
  FLOW_TYPES: () => (/* reexport */ FLOW_TYPES),
11597
+ INLINE_ONLY_PARENT_TYPES: () => (/* reexport */ INLINE_ONLY_PARENT_TYPES),
11597
11598
  NodeTypes: () => (/* reexport */ NodeTypes),
11598
11599
  Owlmoji: () => (/* reexport */ Owlmoji),
11599
11600
  compile: () => (/* reexport */ lib_compile),
@@ -13061,6 +13062,34 @@ const FLOW_TYPES = new Set([
13061
13062
  'table',
13062
13063
  'thematicBreak',
13063
13064
  ]);
13065
+ /**
13066
+ * MDAST parent types whose children are restricted to inline content. A block-level
13067
+ * node placed inside any of these violates the parent's content model and breaks
13068
+ * downstream consumers
13069
+ *
13070
+ * Derived from the mdast spec. Seven of these are direct "phrasing content" parents:
13071
+ * - paragraph, heading, link, linkReference, emphasis, strong
13072
+ * - tableCell (phrasing minus Break)
13073
+ *
13074
+ * @link {https://github.com/syntax-tree/mdast}
13075
+ *
13076
+ * `delete` is GFM and has *transparent* content, see:
13077
+ * @link {https://github.com/syntax-tree/mdast-util-gfm-strikethrough}
13078
+ *
13079
+ * its children must also be phrasing, so it belongs here too.
13080
+ *
13081
+ * NOTE: exported so downstream consumers can reuse and not drift away
13082
+ */
13083
+ const INLINE_ONLY_PARENT_TYPES = new Set([
13084
+ 'paragraph',
13085
+ 'heading',
13086
+ 'tableCell',
13087
+ 'link',
13088
+ 'linkReference',
13089
+ 'emphasis',
13090
+ 'strong',
13091
+ 'delete',
13092
+ ]);
13064
13093
  // HELPER CONSTANTS FOR MDXISH RENDERING
13065
13094
  /**
13066
13095
  * Inline-only custom components that appear as phrasing content within
@@ -75543,7 +75572,15 @@ const walkTags = (html, handlers) => {
75543
75572
  const tagEnd = (parser) => (parser.endIndex ?? parser.startIndex) + 1;
75544
75573
  const parser = new Parser_Parser({
75545
75574
  onopentag(name) {
75546
- handlers.onOpen?.({ name, start: parser.startIndex, end: tagEnd(parser) });
75575
+ const start = parser.startIndex;
75576
+ const end = tagEnd(parser);
75577
+ handlers.onOpen?.({
75578
+ name,
75579
+ start,
75580
+ end,
75581
+ isSelfClosing: html[end - 2] === '/',
75582
+ isStrayCloser: html[start + 1] === '/',
75583
+ });
75547
75584
  },
75548
75585
  onclosetag(name, implicit) {
75549
75586
  handlers.onClose?.({ name, start: parser.startIndex, end: tagEnd(parser), implicit });
@@ -75912,13 +75949,6 @@ const isStandardHtmlTag = (name) => STANDARD_HTML_TAGS.has(name.toLowerCase());
75912
75949
  // (htmlparser2) handles attribute parsing; this scan only needs to locate
75913
75950
  // orphan closers, which can't appear inside an attribute value anyway.
75914
75951
  const HTML_TAG_TOKEN_RE = /<(\/)?([a-zA-Z][a-zA-Z0-9-]*)\b[^>]*>/g;
75915
- /**
75916
- * htmlparser2 silently drops closing tags that have no matching opener
75917
- * (e.g. the trailing `</li>` in `<ul><li>x</ul></li>`), leaving them in the
75918
- * source makes mdxjs choke on the dangling closer. Scan the masked
75919
- * source for `</name>` tokens that don't pair with any prior unmatched
75920
- * `<name>` and return their spans so the caller can drop them.
75921
- */
75922
75952
  const findOrphanClosers = (html) => {
75923
75953
  const masked = maskNonTagRegions(html);
75924
75954
  const stack = [];
@@ -75927,18 +75957,30 @@ const findOrphanClosers = (html) => {
75927
75957
  let match;
75928
75958
  while ((match = HTML_TAG_TOKEN_RE.exec(masked)) !== null) {
75929
75959
  const name = match[2].toLowerCase();
75930
- // Non-HTML names and void elements are handled by the main walker.
75931
75960
  // eslint-disable-next-line no-continue
75932
- if (!isStandardHtmlTag(name) || HTML_VOID_ELEMENTS.has(name))
75961
+ if (!isStandardHtmlTag(name))
75962
+ continue;
75963
+ // Special case for <br>: htmlparser2 will normalize orphan </br> to <br>
75964
+ // so we don't need to handle it here.
75965
+ // eslint-disable-next-line no-continue
75966
+ if (name === 'br' && match[1] === '/')
75933
75967
  continue;
75968
+ const isVoid = HTML_VOID_ELEMENTS.has(name);
75934
75969
  if (match[1] === '/') {
75970
+ // Other void closers (`</hr>`, `</img>`, ...) have no HTML5 rewrite rule
75971
+ // and must be stripped before the strict mdxjs parse runs.
75972
+ if (isVoid) {
75973
+ orphans.push({ offset: match.index, length: match[0].length });
75974
+ // eslint-disable-next-line no-continue
75975
+ continue;
75976
+ }
75935
75977
  const idx = stack.lastIndexOf(name);
75936
75978
  if (idx === -1)
75937
75979
  orphans.push({ offset: match.index, length: match[0].length });
75938
75980
  else
75939
75981
  stack.length = idx;
75940
75982
  }
75941
- else if (!match[0].endsWith('/>')) {
75983
+ else if (!isVoid && !match[0].endsWith('/>')) {
75942
75984
  stack.push(name);
75943
75985
  }
75944
75986
  }
@@ -75971,7 +76013,7 @@ const repairUnclosedTags = (html) => {
75971
76013
  const inserts = [];
75972
76014
  const openTags = [];
75973
76015
  walkTags(html, {
75974
- onOpen({ name, start, end }) {
76016
+ onOpen({ name, start, end, isSelfClosing, isStrayCloser }) {
75975
76017
  // Escape non-HTML names (custom components, typos, `<arbitrary-tag>`)
75976
76018
  // so MDX treats them as literal text instead of expecting a closer
75977
76019
  if (!isStandardHtmlTag(name)) {
@@ -75979,12 +76021,19 @@ const repairUnclosedTags = (html) => {
75979
76021
  return;
75980
76022
  }
75981
76023
  if (HTML_VOID_ELEMENTS.has(name.toLowerCase())) {
75982
- // MDX requires void elements to be self-closing (`<br/>`, not `<br>`).
75983
- // If the source open tag doesn't end with `/`, inject one before the
75984
- // `>` so it parses. `end` is one past `>`, so `end - 2` is the char
75985
- // immediately before `>`.
75986
- if (html[end - 2] !== '/')
76024
+ // MDX requires void elements to be self-closing (`<br/>`, not `<br>`)
76025
+ // so we need to rewrite them to self closing tags.
76026
+ if (isStrayCloser) {
76027
+ // htmlparser2 may normalize some stray closers like `</br>` into opener
76028
+ // events <br> per the HTML5 spec. In this case remove the closing /
76029
+ // and fully rewrite the tag to self closing.
76030
+ inserts.push({ offset: start, text: `<${name}/>`, consumes: end - start });
76031
+ return;
76032
+ }
76033
+ else if (!isSelfClosing) {
76034
+ // Slots in the / right before the closing >
75987
76035
  inserts.push({ offset: end - 1, text: '/' });
76036
+ }
75988
76037
  return;
75989
76038
  }
75990
76039
  openTags.push({ name, start, end });
@@ -103402,6 +103451,7 @@ const mdxishHtmlBlocks = () => tree => {
103402
103451
 
103403
103452
 
103404
103453
 
103454
+
103405
103455
  function toImageAlign(value) {
103406
103456
  if (value === 'left' || value === 'center' || value === 'right') {
103407
103457
  return value;
@@ -103954,16 +104004,12 @@ const mdxishJsxToMdast = () => tree => {
103954
104004
  parent.children[index] = newNode;
103955
104005
  }
103956
104006
  });
103957
- // Transform magic block images (type: 'image') to image-block
103958
- // Images inside paragraphs are standard markdown handled by imageTransformer, normalized below
104007
+ // Promote magic-block images (type: 'image') to image-block, except inside inline-only
104008
+ // parents where the image must stay inline (authors use `<Image caption="…" />` instead).
103959
104009
  visit(tree, 'image', (node, index, parent) => {
103960
104010
  if (!parent || index === undefined)
103961
104011
  return SKIP;
103962
- if (parent.type === 'paragraph')
103963
- return SKIP;
103964
- // `![](url)` in any tableCell stays inline. Authors who want a captioned figure use
103965
- // `<Image caption="…" />` JSX, which becomes `image-block` via `COMPONENT_MAP` above.
103966
- if (parent.type === 'tableCell')
104012
+ if (INLINE_ONLY_PARENT_TYPES.has(parent.type))
103967
104013
  return SKIP;
103968
104014
  const newNode = transformMagicBlockImage(node);
103969
104015
  parent.children[index] = newNode;
package/dist/main.node.js CHANGED
@@ -19278,6 +19278,7 @@ __webpack_require__.r(__webpack_exports__);
19278
19278
  __webpack_require__.d(__webpack_exports__, {
19279
19279
  Components: () => (/* reexport */ components_namespaceObject),
19280
19280
  FLOW_TYPES: () => (/* reexport */ FLOW_TYPES),
19281
+ INLINE_ONLY_PARENT_TYPES: () => (/* reexport */ INLINE_ONLY_PARENT_TYPES),
19281
19282
  NodeTypes: () => (/* reexport */ NodeTypes),
19282
19283
  Owlmoji: () => (/* reexport */ Owlmoji),
19283
19284
  compile: () => (/* reexport */ lib_compile),
@@ -25634,6 +25635,34 @@ const FLOW_TYPES = new Set([
25634
25635
  'table',
25635
25636
  'thematicBreak',
25636
25637
  ]);
25638
+ /**
25639
+ * MDAST parent types whose children are restricted to inline content. A block-level
25640
+ * node placed inside any of these violates the parent's content model and breaks
25641
+ * downstream consumers
25642
+ *
25643
+ * Derived from the mdast spec. Seven of these are direct "phrasing content" parents:
25644
+ * - paragraph, heading, link, linkReference, emphasis, strong
25645
+ * - tableCell (phrasing minus Break)
25646
+ *
25647
+ * @link {https://github.com/syntax-tree/mdast}
25648
+ *
25649
+ * `delete` is GFM and has *transparent* content, see:
25650
+ * @link {https://github.com/syntax-tree/mdast-util-gfm-strikethrough}
25651
+ *
25652
+ * its children must also be phrasing, so it belongs here too.
25653
+ *
25654
+ * NOTE: exported so downstream consumers can reuse and not drift away
25655
+ */
25656
+ const INLINE_ONLY_PARENT_TYPES = new Set([
25657
+ 'paragraph',
25658
+ 'heading',
25659
+ 'tableCell',
25660
+ 'link',
25661
+ 'linkReference',
25662
+ 'emphasis',
25663
+ 'strong',
25664
+ 'delete',
25665
+ ]);
25637
25666
  // HELPER CONSTANTS FOR MDXISH RENDERING
25638
25667
  /**
25639
25668
  * Inline-only custom components that appear as phrasing content within
@@ -95767,7 +95796,15 @@ const walkTags = (html, handlers) => {
95767
95796
  const tagEnd = (parser) => (parser.endIndex ?? parser.startIndex) + 1;
95768
95797
  const parser = new Parser_Parser({
95769
95798
  onopentag(name) {
95770
- handlers.onOpen?.({ name, start: parser.startIndex, end: tagEnd(parser) });
95799
+ const start = parser.startIndex;
95800
+ const end = tagEnd(parser);
95801
+ handlers.onOpen?.({
95802
+ name,
95803
+ start,
95804
+ end,
95805
+ isSelfClosing: html[end - 2] === '/',
95806
+ isStrayCloser: html[start + 1] === '/',
95807
+ });
95771
95808
  },
95772
95809
  onclosetag(name, implicit) {
95773
95810
  handlers.onClose?.({ name, start: parser.startIndex, end: tagEnd(parser), implicit });
@@ -96136,13 +96173,6 @@ const isStandardHtmlTag = (name) => STANDARD_HTML_TAGS.has(name.toLowerCase());
96136
96173
  // (htmlparser2) handles attribute parsing; this scan only needs to locate
96137
96174
  // orphan closers, which can't appear inside an attribute value anyway.
96138
96175
  const HTML_TAG_TOKEN_RE = /<(\/)?([a-zA-Z][a-zA-Z0-9-]*)\b[^>]*>/g;
96139
- /**
96140
- * htmlparser2 silently drops closing tags that have no matching opener
96141
- * (e.g. the trailing `</li>` in `<ul><li>x</ul></li>`), leaving them in the
96142
- * source makes mdxjs choke on the dangling closer. Scan the masked
96143
- * source for `</name>` tokens that don't pair with any prior unmatched
96144
- * `<name>` and return their spans so the caller can drop them.
96145
- */
96146
96176
  const findOrphanClosers = (html) => {
96147
96177
  const masked = maskNonTagRegions(html);
96148
96178
  const stack = [];
@@ -96151,18 +96181,30 @@ const findOrphanClosers = (html) => {
96151
96181
  let match;
96152
96182
  while ((match = HTML_TAG_TOKEN_RE.exec(masked)) !== null) {
96153
96183
  const name = match[2].toLowerCase();
96154
- // Non-HTML names and void elements are handled by the main walker.
96155
96184
  // eslint-disable-next-line no-continue
96156
- if (!isStandardHtmlTag(name) || HTML_VOID_ELEMENTS.has(name))
96185
+ if (!isStandardHtmlTag(name))
96186
+ continue;
96187
+ // Special case for <br>: htmlparser2 will normalize orphan </br> to <br>
96188
+ // so we don't need to handle it here.
96189
+ // eslint-disable-next-line no-continue
96190
+ if (name === 'br' && match[1] === '/')
96157
96191
  continue;
96192
+ const isVoid = HTML_VOID_ELEMENTS.has(name);
96158
96193
  if (match[1] === '/') {
96194
+ // Other void closers (`</hr>`, `</img>`, ...) have no HTML5 rewrite rule
96195
+ // and must be stripped before the strict mdxjs parse runs.
96196
+ if (isVoid) {
96197
+ orphans.push({ offset: match.index, length: match[0].length });
96198
+ // eslint-disable-next-line no-continue
96199
+ continue;
96200
+ }
96159
96201
  const idx = stack.lastIndexOf(name);
96160
96202
  if (idx === -1)
96161
96203
  orphans.push({ offset: match.index, length: match[0].length });
96162
96204
  else
96163
96205
  stack.length = idx;
96164
96206
  }
96165
- else if (!match[0].endsWith('/>')) {
96207
+ else if (!isVoid && !match[0].endsWith('/>')) {
96166
96208
  stack.push(name);
96167
96209
  }
96168
96210
  }
@@ -96195,7 +96237,7 @@ const repairUnclosedTags = (html) => {
96195
96237
  const inserts = [];
96196
96238
  const openTags = [];
96197
96239
  walkTags(html, {
96198
- onOpen({ name, start, end }) {
96240
+ onOpen({ name, start, end, isSelfClosing, isStrayCloser }) {
96199
96241
  // Escape non-HTML names (custom components, typos, `<arbitrary-tag>`)
96200
96242
  // so MDX treats them as literal text instead of expecting a closer
96201
96243
  if (!isStandardHtmlTag(name)) {
@@ -96203,12 +96245,19 @@ const repairUnclosedTags = (html) => {
96203
96245
  return;
96204
96246
  }
96205
96247
  if (HTML_VOID_ELEMENTS.has(name.toLowerCase())) {
96206
- // MDX requires void elements to be self-closing (`<br/>`, not `<br>`).
96207
- // If the source open tag doesn't end with `/`, inject one before the
96208
- // `>` so it parses. `end` is one past `>`, so `end - 2` is the char
96209
- // immediately before `>`.
96210
- if (html[end - 2] !== '/')
96248
+ // MDX requires void elements to be self-closing (`<br/>`, not `<br>`)
96249
+ // so we need to rewrite them to self closing tags.
96250
+ if (isStrayCloser) {
96251
+ // htmlparser2 may normalize some stray closers like `</br>` into opener
96252
+ // events <br> per the HTML5 spec. In this case remove the closing /
96253
+ // and fully rewrite the tag to self closing.
96254
+ inserts.push({ offset: start, text: `<${name}/>`, consumes: end - start });
96255
+ return;
96256
+ }
96257
+ else if (!isSelfClosing) {
96258
+ // Slots in the / right before the closing >
96211
96259
  inserts.push({ offset: end - 1, text: '/' });
96260
+ }
96212
96261
  return;
96213
96262
  }
96214
96263
  openTags.push({ name, start, end });
@@ -123626,6 +123675,7 @@ const mdxishHtmlBlocks = () => tree => {
123626
123675
 
123627
123676
 
123628
123677
 
123678
+
123629
123679
  function toImageAlign(value) {
123630
123680
  if (value === 'left' || value === 'center' || value === 'right') {
123631
123681
  return value;
@@ -124178,16 +124228,12 @@ const mdxishJsxToMdast = () => tree => {
124178
124228
  parent.children[index] = newNode;
124179
124229
  }
124180
124230
  });
124181
- // Transform magic block images (type: 'image') to image-block
124182
- // Images inside paragraphs are standard markdown handled by imageTransformer, normalized below
124231
+ // Promote magic-block images (type: 'image') to image-block, except inside inline-only
124232
+ // parents where the image must stay inline (authors use `<Image caption="…" />` instead).
124183
124233
  visit(tree, 'image', (node, index, parent) => {
124184
124234
  if (!parent || index === undefined)
124185
124235
  return SKIP;
124186
- if (parent.type === 'paragraph')
124187
- return SKIP;
124188
- // `![](url)` in any tableCell stays inline. Authors who want a captioned figure use
124189
- // `<Image caption="…" />` JSX, which becomes `image-block` via `COMPONENT_MAP` above.
124190
- if (parent.type === 'tableCell')
124236
+ if (INLINE_ONLY_PARENT_TYPES.has(parent.type))
124191
124237
  return SKIP;
124192
124238
  const newNode = transformMagicBlockImage(node);
124193
124239
  parent.children[index] = newNode;