@readme/markdown 13.1.2 → 13.1.3

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.
@@ -0,0 +1,25 @@
1
+ export interface BlockHit {
2
+ key: string;
3
+ raw: string;
4
+ token: string;
5
+ }
6
+ /**
7
+ * The content matching in this regex captures everything between `[block:TYPE]`
8
+ * and `[/block]`, including new lines. Negative lookahead for the closing
9
+ * `[/block]` tag is required to prevent greedy matching to ensure it stops at
10
+ * the first closing tag it encounters preventing vulnerability to polynomial
11
+ * backtracking issues.
12
+ */
13
+ export declare const MAGIC_BLOCK_REGEX: RegExp;
14
+ /**
15
+ * Extract legacy magic block syntax from a markdown string.
16
+ * Returns the modified markdown and an array of extracted blocks.
17
+ */
18
+ export declare function extractMagicBlocks(markdown: string): {
19
+ replaced: string;
20
+ blocks: BlockHit[];
21
+ };
22
+ /**
23
+ * Restore extracted magic blocks back into a markdown string.
24
+ */
25
+ export declare function restoreMagicBlocks(replaced: string, blocks: BlockHit[]): string;
package/dist/main.js CHANGED
@@ -71360,6 +71360,10 @@ const types = {
71360
71360
  Recipe: NodeTypes.recipe,
71361
71361
  TutorialTile: NodeTypes.recipe, // coerce to recipe for backwards compatibility
71362
71362
  };
71363
+ // Node types that are phrasing (inline) content per the mdast spec. Phrasing
71364
+ // content at the document root violates the spec and causes mdx() to collapse
71365
+ // blank lines, so these must be wrapped in a paragraph when at root level.
71366
+ const phrasingTypes = new Set([NodeTypes.variable]);
71363
71367
  var TableNames;
71364
71368
  (function (TableNames) {
71365
71369
  TableNames["td"] = "td";
@@ -71515,7 +71519,16 @@ const coerceJsxToMd = ({ components = {}, html = false } = {}) => (node, index,
71515
71519
  type: types[node.name],
71516
71520
  position: node.position,
71517
71521
  };
71518
- parent.children[index] = mdNode;
71522
+ // Wrap in a paragraph if at root level. Links are phrasing content and
71523
+ // root children must all be the same category (per mdast spec). Mixing
71524
+ // phrasing with flow content (headings, paragraphs, etc.) causes mdx()
71525
+ // to collapse blank lines in the document.
71526
+ if (parent.type === 'root') {
71527
+ parent.children[index] = { type: 'paragraph', children: [mdNode], position: node.position };
71528
+ }
71529
+ else {
71530
+ parent.children[index] = mdNode;
71531
+ }
71519
71532
  }
71520
71533
  else if (node.name === 'Recipe' || node.name === 'TutorialTile') {
71521
71534
  const hProperties = getAttrs(node);
@@ -71541,7 +71554,13 @@ const coerceJsxToMd = ({ components = {}, html = false } = {}) => (node, index,
71541
71554
  },
71542
71555
  position: node.position,
71543
71556
  };
71544
- parent.children[index] = mdNode;
71557
+ if (parent.type === 'root' && phrasingTypes.has(types[node.name])) {
71558
+ // @ts-expect-error mdNode is typed as BlockContent but is actually phrasing content
71559
+ parent.children[index] = { type: 'paragraph', children: [mdNode], position: node.position };
71560
+ }
71561
+ else {
71562
+ parent.children[index] = mdNode;
71563
+ }
71545
71564
  }
71546
71565
  };
71547
71566
  const readmeComponents = (opts) => () => tree => {
@@ -97742,8 +97761,59 @@ const mdxishTags_tags = (doc) => {
97742
97761
  };
97743
97762
  /* harmony default export */ const mdxishTags = (mdxishTags_tags);
97744
97763
 
97745
- ;// ./lib/stripComments.ts
97764
+ ;// ./lib/utils/extractMagicBlocks.ts
97765
+ /**
97766
+ * The content matching in this regex captures everything between `[block:TYPE]`
97767
+ * and `[/block]`, including new lines. Negative lookahead for the closing
97768
+ * `[/block]` tag is required to prevent greedy matching to ensure it stops at
97769
+ * the first closing tag it encounters preventing vulnerability to polynomial
97770
+ * backtracking issues.
97771
+ */
97772
+ const MAGIC_BLOCK_REGEX = /\[block:[^\]]{1,100}\](?:(?!\[block:)(?!\[\/block\])[\s\S])*\[\/block\]/g;
97773
+ /**
97774
+ * Extract legacy magic block syntax from a markdown string.
97775
+ * Returns the modified markdown and an array of extracted blocks.
97776
+ */
97777
+ function extractMagicBlocks(markdown) {
97778
+ const blocks = [];
97779
+ let index = 0;
97780
+ const replaced = markdown.replace(MAGIC_BLOCK_REGEX, match => {
97781
+ /**
97782
+ * Key is the unique identifier for the magic block
97783
+ */
97784
+ const key = `__MAGIC_BLOCK_${index}__`;
97785
+ /**
97786
+ * Token is a wrapper around the `key` to serialize & influence how the
97787
+ * magic block is parsed in the remark pipeline.
97788
+ * - Use backticks so it becomes a code span, preventing `remarkParse` from
97789
+ * parsing special characters in the token as markdown syntax
97790
+ * - Prepend a newline to ensure it is parsed as a block level node
97791
+ * - Append a newline to ensure it is separated from following content
97792
+ */
97793
+ const token = `\n\`${key}\`\n`;
97794
+ blocks.push({ key, raw: match, token });
97795
+ index += 1;
97796
+ return token;
97797
+ });
97798
+ return { replaced, blocks };
97799
+ }
97800
+ /**
97801
+ * Restore extracted magic blocks back into a markdown string.
97802
+ */
97803
+ function restoreMagicBlocks(replaced, blocks) {
97804
+ // If a magic block is at the start or end of the document, the extraction
97805
+ // token's newlines will have been trimmed during processing. We need to
97806
+ // account for that here to ensure the token is found and replaced correctly.
97807
+ // These extra newlines will be removed again when the final string is trimmed.
97808
+ const content = `\n${replaced}\n`;
97809
+ const restoredContent = blocks.reduce((acc, { token, raw }) => {
97810
+ // Ensure each magic block is separated by newlines when restored.
97811
+ return acc.split(token).join(`\n${raw}\n`);
97812
+ }, content);
97813
+ return restoredContent.trim();
97814
+ }
97746
97815
 
97816
+ ;// ./lib/stripComments.ts
97747
97817
 
97748
97818
 
97749
97819
 
@@ -97758,19 +97828,16 @@ const mdxishTags_tags = (doc) => {
97758
97828
  * Removes Markdown and MDX comments.
97759
97829
  */
97760
97830
  async function stripComments(doc, { mdx, mdxish } = {}) {
97761
- const micromarkExtensions = [magicBlock()];
97762
- const fromMarkdownExtensions = [magicBlockFromMarkdown()];
97831
+ const { replaced, blocks } = extractMagicBlocks(doc);
97832
+ const processor = unified();
97763
97833
  // we still require these two extensions because:
97764
- // 1. we cant rely on remarkMdx to parse MDXish
97834
+ // 1. we can rely on remarkMdx to parse MDXish
97765
97835
  // 2. we need to parse JSX comments into mdxTextExpression nodes so that the transformers can pick them up
97766
97836
  if (mdxish) {
97767
- micromarkExtensions.push(mdxExpression({ allowEmpty: true }));
97768
- fromMarkdownExtensions.push(mdxExpressionFromMarkdown());
97837
+ processor
97838
+ .data('micromarkExtensions', [mdxExpression({ allowEmpty: true })])
97839
+ .data('fromMarkdownExtensions', [mdxExpressionFromMarkdown()]);
97769
97840
  }
97770
- const processor = unified()
97771
- .data('micromarkExtensions', micromarkExtensions)
97772
- .data('fromMarkdownExtensions', fromMarkdownExtensions)
97773
- .data('toMarkdownExtensions', [magicBlockToMarkdown()]);
97774
97841
  processor
97775
97842
  .use(remarkParse)
97776
97843
  .use(normalize_malformed_md_syntax)
@@ -97804,8 +97871,10 @@ async function stripComments(doc, { mdx, mdxish } = {}) {
97804
97871
  },
97805
97872
  ],
97806
97873
  });
97807
- const file = await processor.process(doc);
97808
- return String(file).trim();
97874
+ const file = await processor.process(replaced);
97875
+ const stringified = String(file).trim();
97876
+ const restored = restoreMagicBlocks(stringified, blocks);
97877
+ return restored;
97809
97878
  }
97810
97879
  /* harmony default export */ const lib_stripComments = (stripComments);
97811
97880
 
package/dist/main.node.js CHANGED
@@ -91564,6 +91564,10 @@ const readme_components_types = {
91564
91564
  Recipe: NodeTypes.recipe,
91565
91565
  TutorialTile: NodeTypes.recipe, // coerce to recipe for backwards compatibility
91566
91566
  };
91567
+ // Node types that are phrasing (inline) content per the mdast spec. Phrasing
91568
+ // content at the document root violates the spec and causes mdx() to collapse
91569
+ // blank lines, so these must be wrapped in a paragraph when at root level.
91570
+ const phrasingTypes = new Set([NodeTypes.variable]);
91567
91571
  var TableNames;
91568
91572
  (function (TableNames) {
91569
91573
  TableNames["td"] = "td";
@@ -91719,7 +91723,16 @@ const coerceJsxToMd = ({ components = {}, html = false } = {}) => (node, index,
91719
91723
  type: readme_components_types[node.name],
91720
91724
  position: node.position,
91721
91725
  };
91722
- parent.children[index] = mdNode;
91726
+ // Wrap in a paragraph if at root level. Links are phrasing content and
91727
+ // root children must all be the same category (per mdast spec). Mixing
91728
+ // phrasing with flow content (headings, paragraphs, etc.) causes mdx()
91729
+ // to collapse blank lines in the document.
91730
+ if (parent.type === 'root') {
91731
+ parent.children[index] = { type: 'paragraph', children: [mdNode], position: node.position };
91732
+ }
91733
+ else {
91734
+ parent.children[index] = mdNode;
91735
+ }
91723
91736
  }
91724
91737
  else if (node.name === 'Recipe' || node.name === 'TutorialTile') {
91725
91738
  const hProperties = getAttrs(node);
@@ -91745,7 +91758,13 @@ const coerceJsxToMd = ({ components = {}, html = false } = {}) => (node, index,
91745
91758
  },
91746
91759
  position: node.position,
91747
91760
  };
91748
- parent.children[index] = mdNode;
91761
+ if (parent.type === 'root' && phrasingTypes.has(readme_components_types[node.name])) {
91762
+ // @ts-expect-error mdNode is typed as BlockContent but is actually phrasing content
91763
+ parent.children[index] = { type: 'paragraph', children: [mdNode], position: node.position };
91764
+ }
91765
+ else {
91766
+ parent.children[index] = mdNode;
91767
+ }
91749
91768
  }
91750
91769
  };
91751
91770
  const readmeComponents = (opts) => () => tree => {
@@ -117946,8 +117965,59 @@ const mdxishTags_tags = (doc) => {
117946
117965
  };
117947
117966
  /* harmony default export */ const mdxishTags = (mdxishTags_tags);
117948
117967
 
117949
- ;// ./lib/stripComments.ts
117968
+ ;// ./lib/utils/extractMagicBlocks.ts
117969
+ /**
117970
+ * The content matching in this regex captures everything between `[block:TYPE]`
117971
+ * and `[/block]`, including new lines. Negative lookahead for the closing
117972
+ * `[/block]` tag is required to prevent greedy matching to ensure it stops at
117973
+ * the first closing tag it encounters preventing vulnerability to polynomial
117974
+ * backtracking issues.
117975
+ */
117976
+ const MAGIC_BLOCK_REGEX = /\[block:[^\]]{1,100}\](?:(?!\[block:)(?!\[\/block\])[\s\S])*\[\/block\]/g;
117977
+ /**
117978
+ * Extract legacy magic block syntax from a markdown string.
117979
+ * Returns the modified markdown and an array of extracted blocks.
117980
+ */
117981
+ function extractMagicBlocks(markdown) {
117982
+ const blocks = [];
117983
+ let index = 0;
117984
+ const replaced = markdown.replace(MAGIC_BLOCK_REGEX, match => {
117985
+ /**
117986
+ * Key is the unique identifier for the magic block
117987
+ */
117988
+ const key = `__MAGIC_BLOCK_${index}__`;
117989
+ /**
117990
+ * Token is a wrapper around the `key` to serialize & influence how the
117991
+ * magic block is parsed in the remark pipeline.
117992
+ * - Use backticks so it becomes a code span, preventing `remarkParse` from
117993
+ * parsing special characters in the token as markdown syntax
117994
+ * - Prepend a newline to ensure it is parsed as a block level node
117995
+ * - Append a newline to ensure it is separated from following content
117996
+ */
117997
+ const token = `\n\`${key}\`\n`;
117998
+ blocks.push({ key, raw: match, token });
117999
+ index += 1;
118000
+ return token;
118001
+ });
118002
+ return { replaced, blocks };
118003
+ }
118004
+ /**
118005
+ * Restore extracted magic blocks back into a markdown string.
118006
+ */
118007
+ function restoreMagicBlocks(replaced, blocks) {
118008
+ // If a magic block is at the start or end of the document, the extraction
118009
+ // token's newlines will have been trimmed during processing. We need to
118010
+ // account for that here to ensure the token is found and replaced correctly.
118011
+ // These extra newlines will be removed again when the final string is trimmed.
118012
+ const content = `\n${replaced}\n`;
118013
+ const restoredContent = blocks.reduce((acc, { token, raw }) => {
118014
+ // Ensure each magic block is separated by newlines when restored.
118015
+ return acc.split(token).join(`\n${raw}\n`);
118016
+ }, content);
118017
+ return restoredContent.trim();
118018
+ }
117950
118019
 
118020
+ ;// ./lib/stripComments.ts
117951
118021
 
117952
118022
 
117953
118023
 
@@ -117962,19 +118032,16 @@ const mdxishTags_tags = (doc) => {
117962
118032
  * Removes Markdown and MDX comments.
117963
118033
  */
117964
118034
  async function stripComments(doc, { mdx, mdxish } = {}) {
117965
- const micromarkExtensions = [magicBlock()];
117966
- const fromMarkdownExtensions = [magicBlockFromMarkdown()];
118035
+ const { replaced, blocks } = extractMagicBlocks(doc);
118036
+ const processor = unified();
117967
118037
  // we still require these two extensions because:
117968
- // 1. we cant rely on remarkMdx to parse MDXish
118038
+ // 1. we can rely on remarkMdx to parse MDXish
117969
118039
  // 2. we need to parse JSX comments into mdxTextExpression nodes so that the transformers can pick them up
117970
118040
  if (mdxish) {
117971
- micromarkExtensions.push(mdxExpression({ allowEmpty: true }));
117972
- fromMarkdownExtensions.push(mdxExpressionFromMarkdown());
118041
+ processor
118042
+ .data('micromarkExtensions', [mdxExpression({ allowEmpty: true })])
118043
+ .data('fromMarkdownExtensions', [mdxExpressionFromMarkdown()]);
117973
118044
  }
117974
- const processor = unified()
117975
- .data('micromarkExtensions', micromarkExtensions)
117976
- .data('fromMarkdownExtensions', fromMarkdownExtensions)
117977
- .data('toMarkdownExtensions', [magicBlockToMarkdown()]);
117978
118045
  processor
117979
118046
  .use(remarkParse)
117980
118047
  .use(normalize_malformed_md_syntax)
@@ -118008,8 +118075,10 @@ async function stripComments(doc, { mdx, mdxish } = {}) {
118008
118075
  },
118009
118076
  ],
118010
118077
  });
118011
- const file = await processor.process(doc);
118012
- return String(file).trim();
118078
+ const file = await processor.process(replaced);
118079
+ const stringified = String(file).trim();
118080
+ const restored = restoreMagicBlocks(stringified, blocks);
118081
+ return restored;
118013
118082
  }
118014
118083
  /* harmony default export */ const lib_stripComments = (stripComments);
118015
118084