@readme/markdown 15.2.1 → 15.3.0
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/README.md +1 -0
- package/dist/lib/mdxish.d.ts +9 -0
- package/dist/main.js +63 -28
- package/dist/main.node.js +63 -28
- package/dist/main.node.js.map +1 -1
- package/dist/processor/transform/mdxish/magic-blocks/patterns.d.ts +3 -0
- package/dist/processor/transform/mdxish/magic-blocks/types.d.ts +1 -0
- package/dist/render-fixture.node.js +63 -28
- package/dist/render-fixture.node.js.map +1 -1
- package/dist/utils/common-html-words.d.ts +9 -2
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -99,6 +99,7 @@ Extends [`CompileOptions`](https://mdxjs.com/packages/mdx/#compileoptions)
|
|
|
99
99
|
- **`safeMode`** (`boolean`, optional)—extract script tags from `HTMLBlock`s
|
|
100
100
|
- **`components`** (`Record<string, string>`, optional)—an object of tag names to mdx.
|
|
101
101
|
- **`copyButtons`** (`Boolean`, optional) — add a copy button to code blocks
|
|
102
|
+
- **`hardBreaks`** (`boolean`, optional)—render every newline as a `<br>`. `mdxish` defaults to `true`
|
|
102
103
|
|
|
103
104
|
### `RunOpts`
|
|
104
105
|
|
package/dist/lib/mdxish.d.ts
CHANGED
|
@@ -3,6 +3,15 @@ import type { Root } from 'hast';
|
|
|
3
3
|
import type { Root as MdastRoot } from 'mdast';
|
|
4
4
|
export interface MdxishOpts {
|
|
5
5
|
components?: CustomComponents;
|
|
6
|
+
/**
|
|
7
|
+
* Whether a single newline (\n) renders as a `<br>`. Defaults to `true`, matching legacy rdmd.
|
|
8
|
+
* Turn it off for CommonMark semantics, where only a blank line breaks — what content
|
|
9
|
+
* soft-wrapped to a line-length limit (OpenAPI descriptions, linted markdown) expects.
|
|
10
|
+
*
|
|
11
|
+
* Only applies to `mdxish()`; `mdxishAstProcessor` never hard-breaks its MDAST.
|
|
12
|
+
* There's no use for it right now but it can be revisited if needed.
|
|
13
|
+
*/
|
|
14
|
+
hardBreaks?: boolean;
|
|
6
15
|
newEditorTypes?: boolean;
|
|
7
16
|
/**
|
|
8
17
|
* When enabled, the pipeline ignores all expression syntax `{...}`.
|
package/dist/main.js
CHANGED
|
@@ -75799,6 +75799,7 @@ var allCSS2RNProps = __webpack_unused_export__ = flatten([allProps, CSS2RNProps]
|
|
|
75799
75799
|
|
|
75800
75800
|
|
|
75801
75801
|
|
|
75802
|
+
|
|
75802
75803
|
/**
|
|
75803
75804
|
* Extract word boundaries from camelCase strings (e.g., "borderWidth" -> ["border", "width"])
|
|
75804
75805
|
*/
|
|
@@ -75878,10 +75879,43 @@ const CUSTOM_PROP_BOUNDARIES = [
|
|
|
75878
75879
|
*/
|
|
75879
75880
|
const RUNTIME_COMPONENT_TAGS = new Set(['Variable', 'variable', 'html-block', 'rdme-pin']);
|
|
75880
75881
|
/**
|
|
75881
|
-
*
|
|
75882
|
-
*
|
|
75882
|
+
* Elements that are not actually standard HTML tags, or those that we intentionally
|
|
75883
|
+
* don't want to treat as one & is mute to check with. These include:
|
|
75884
|
+
* - SVG/MathML descendants
|
|
75885
|
+
* - Namespaced foreign content
|
|
75886
|
+
* - `image`, which the HTML tree builder rewrites to `img`
|
|
75887
|
+
* Most of these are bundled in parse5's `TAG_NAMES` set, but we can also add a few more.
|
|
75883
75888
|
*/
|
|
75884
|
-
const
|
|
75889
|
+
const NON_STANDARD_TAGS = new Set([
|
|
75890
|
+
'annotation-xml',
|
|
75891
|
+
'desc',
|
|
75892
|
+
'foreignObject',
|
|
75893
|
+
'image',
|
|
75894
|
+
'malignmark',
|
|
75895
|
+
'mglyph',
|
|
75896
|
+
'mi',
|
|
75897
|
+
'mn',
|
|
75898
|
+
'mo',
|
|
75899
|
+
'ms',
|
|
75900
|
+
'mtext',
|
|
75901
|
+
]);
|
|
75902
|
+
/**
|
|
75903
|
+
* Standard HTML tags list.
|
|
75904
|
+
* A use case of this is to differentiate custom components tag vs standard HTML tags.
|
|
75905
|
+
*
|
|
75906
|
+
* Unioned from:
|
|
75907
|
+
* - `html-tags`: All modern spec HTML elements
|
|
75908
|
+
* - parse5's `TAG_NAMES`: Elements the HTML tree-construction algorithm has to special-case
|
|
75909
|
+
* This includes obsolete tags that are still rendered by browsers.
|
|
75910
|
+
* - NON_STANDARD_TAGS: We've had to filter because currently parse5's `TAG_NAMES` set over-enumerates
|
|
75911
|
+
* tags that are not actually standard HTML tags. It also allows custom tags to be filtered out.
|
|
75912
|
+
*/
|
|
75913
|
+
const STANDARD_HTML_TAGS = new Set([
|
|
75914
|
+
...html_tags_namespaceObject,
|
|
75915
|
+
...Object.values(TAG_NAMES),
|
|
75916
|
+
'acronym', // Obselete tag not included either of the above sources. Add here if we have missed any.
|
|
75917
|
+
'blink',
|
|
75918
|
+
].filter(tag => !NON_STANDARD_TAGS.has(tag)));
|
|
75885
75919
|
/**
|
|
75886
75920
|
* Table structural tags. Blank lines inside these carry deliberate meaning for
|
|
75887
75921
|
* `mdxishTables` (e.g. splitting cell content into paragraphs, or deciding
|
|
@@ -105351,8 +105385,11 @@ var variable_default = /*#__PURE__*/__webpack_require__.n(variable_);
|
|
|
105351
105385
|
const HTML_TAG_RE = /<\/?([a-zA-Z][a-zA-Z0-9-]*)((?:[^>"']*(?:"[^"]*"|'[^']*'))*[^>"']*)>/g;
|
|
105352
105386
|
/** Matches an HTML element from its opening tag to the matching closing tag. */
|
|
105353
105387
|
const HTML_ELEMENT_BLOCK_RE = /<([a-zA-Z][a-zA-Z0-9-]*)[\s>][\s\S]*?<\/\1>/g;
|
|
105388
|
+
const NEWLINE_RE = /\n/g;
|
|
105354
105389
|
/** Matches a newline with surrounding horizontal whitespace. */
|
|
105355
105390
|
const NEWLINE_WITH_WHITESPACE_RE = /[^\S\n]*\n[^\S\n]*/g;
|
|
105391
|
+
/** Matches a run of two or more newlines (a blank line) with surrounding horizontal whitespace. */
|
|
105392
|
+
const BLANK_LINE_RE = /[^\S\n]*\n(?:[^\S\n]*\n)+[^\S\n]*/g;
|
|
105356
105393
|
/** Matches a closing block-level tag followed by non-tag text or by a newline then non-blank content. */
|
|
105357
105394
|
const CLOSE_BLOCK_TAG_BOUNDARY_RE = /<\/([a-zA-Z][a-zA-Z0-9-]*)>\s*(?:(?!<)(\S)|\n([^\n]))/g;
|
|
105358
105395
|
/** Strips HTML open/close tags. Used to detect non-tag inner text content. */
|
|
@@ -105422,7 +105459,6 @@ const EMPTY_CODE_PLACEHOLDER = {
|
|
|
105422
105459
|
|
|
105423
105460
|
|
|
105424
105461
|
|
|
105425
|
-
|
|
105426
105462
|
/**
|
|
105427
105463
|
* Wraps a node in a "pinned" container if sidebar: true is set.
|
|
105428
105464
|
*/
|
|
@@ -105457,16 +105493,13 @@ const textToBlock = (text) => [{ children: textToInline(text), type: 'paragraph'
|
|
|
105457
105493
|
*/
|
|
105458
105494
|
const ensureLeadingBreaks = (text) => text.replace(/^\n+/, match => '<br>'.repeat(match.length));
|
|
105459
105495
|
/** Preprocesses magic block body content before parsing. */
|
|
105460
|
-
const preprocessBody = (text) =>
|
|
105461
|
-
return ensureLeadingBreaks(text);
|
|
105462
|
-
};
|
|
105496
|
+
const preprocessBody = (text, hardBreaks) => (hardBreaks ? ensureLeadingBreaks(text) : text);
|
|
105463
105497
|
const bodyExtensions = mdxishExtensions(FEATURES.magicBlockBody);
|
|
105464
105498
|
/** Markdown parser */
|
|
105465
105499
|
const contentParser = unified()
|
|
105466
105500
|
.data('micromarkExtensions', bodyExtensions.micromarkExtensions)
|
|
105467
105501
|
.data('fromMarkdownExtensions', bodyExtensions.fromMarkdownExtensions)
|
|
105468
105502
|
.use(remarkParse)
|
|
105469
|
-
.use(hard_breaks)
|
|
105470
105503
|
.use(remarkGfm)
|
|
105471
105504
|
.use(normalize_malformed_md_syntax);
|
|
105472
105505
|
/**
|
|
@@ -105563,17 +105596,21 @@ const processMarkdownInHtmlString = (html) => {
|
|
|
105563
105596
|
/**
|
|
105564
105597
|
* Separate a closing block-level tag from the content that follows it.
|
|
105565
105598
|
*
|
|
105566
|
-
*
|
|
105567
|
-
*
|
|
105568
|
-
* the following content as markdown.
|
|
105599
|
+
* A blank line (\n\n) is appended so CommonMark ends the HTML block and parses the
|
|
105600
|
+
* following content as markdown; with hard breaks each \n also becomes a <br> to keep its spacing.
|
|
105569
105601
|
*/
|
|
105570
|
-
const separateBlockTagFromContent = (match, tag, inlineChar, nextLineChar) => {
|
|
105602
|
+
const separateBlockTagFromContent = (hardBreaks, match, tag, inlineChar, nextLineChar) => {
|
|
105571
105603
|
if (!BLOCK_LEVEL_TAGS.has(tag.toLowerCase()))
|
|
105572
105604
|
return match;
|
|
105573
|
-
const newlineCount = (match.match(
|
|
105605
|
+
const newlineCount = hardBreaks ? (match.match(NEWLINE_RE) ?? []).length : 0;
|
|
105574
105606
|
const breaks = '<br>'.repeat(newlineCount);
|
|
105575
105607
|
return `</${tag}>${breaks}\n\n${inlineChar || nextLineChar}`;
|
|
105576
105608
|
};
|
|
105609
|
+
/**
|
|
105610
|
+
* Newlines inside an HTML block become <br> so CommonMark doesn't end the block on a blank
|
|
105611
|
+
* line. Without hard breaks only blank lines break, but they still have to be replaced.
|
|
105612
|
+
*/
|
|
105613
|
+
const collapseHtmlBlockNewlines = (html, hardBreaks) => html.replace(hardBreaks ? NEWLINE_WITH_WHITESPACE_RE : BLANK_LINE_RE, '<br>');
|
|
105577
105614
|
/** Escape a leading (possibly indented) `-`/`*`/`+` so cells don't become bullet lists. */
|
|
105578
105615
|
const escapeLeadingListMarkers = (text) => text.replace(/^([ \t]*)([-*+])(?=[ \t]|$)/gm, '$1\\$2');
|
|
105579
105616
|
/**
|
|
@@ -105581,15 +105618,13 @@ const escapeLeadingListMarkers = (text) => text.replace(/^([ \t]*)([-*+])(?=[ \t
|
|
|
105581
105618
|
* so `<ul><li>_text_</li></ul>` won't convert underscores to emphasis.
|
|
105582
105619
|
* We parse first, then visit html nodes and process their text content.
|
|
105583
105620
|
*/
|
|
105584
|
-
const parseTableCell = (text) => {
|
|
105621
|
+
const parseTableCell = (text, hardBreaks) => {
|
|
105585
105622
|
if (!text.trim())
|
|
105586
105623
|
return [{ type: 'text', value: '' }];
|
|
105587
|
-
// Convert \n (and surrounding whitespace) to <br> inside HTML blocks so
|
|
105588
|
-
// CommonMark doesn't split them on blank lines.
|
|
105589
105624
|
const escaped = processBackslashEscapes(text);
|
|
105590
105625
|
const normalized = escaped
|
|
105591
|
-
.replace(HTML_ELEMENT_BLOCK_RE, match => match
|
|
105592
|
-
.replace(CLOSE_BLOCK_TAG_BOUNDARY_RE, separateBlockTagFromContent);
|
|
105626
|
+
.replace(HTML_ELEMENT_BLOCK_RE, match => collapseHtmlBlockNewlines(match, hardBreaks))
|
|
105627
|
+
.replace(CLOSE_BLOCK_TAG_BOUNDARY_RE, (match, tag, inlineChar, nextLineChar) => separateBlockTagFromContent(hardBreaks, match, tag, inlineChar, nextLineChar));
|
|
105593
105628
|
const processed = escapeLeadingListMarkers(normalized);
|
|
105594
105629
|
const tree = contentParser.runSync(contentParser.parse(processed));
|
|
105595
105630
|
// Process markdown inside HTML blocks that have non-tag inner text (e.g. `<div>**x**`
|
|
@@ -105638,7 +105673,7 @@ const parseApiHeaderTitle = (text) => {
|
|
|
105638
105673
|
* Transform a magicBlock node into final MDAST nodes.
|
|
105639
105674
|
*/
|
|
105640
105675
|
function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
105641
|
-
const { compatibilityMode = false, safeMode = false } = options;
|
|
105676
|
+
const { compatibilityMode = false, hardBreaks = true, safeMode = false } = options;
|
|
105642
105677
|
// Handle empty data by returning placeholder nodes for known block types
|
|
105643
105678
|
// This allows the editor to show appropriate placeholder UI instead of nothing
|
|
105644
105679
|
if (Object.keys(data).length < 1) {
|
|
@@ -105777,7 +105812,7 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
|
105777
105812
|
});
|
|
105778
105813
|
}
|
|
105779
105814
|
if (hasBody) {
|
|
105780
|
-
const bodyBlocks = parseBlock(preprocessBody(calloutJson.body || ''));
|
|
105815
|
+
const bodyBlocks = parseBlock(preprocessBody(calloutJson.body || '', hardBreaks));
|
|
105781
105816
|
children.push(...bodyBlocks);
|
|
105782
105817
|
}
|
|
105783
105818
|
const calloutElement = {
|
|
@@ -105809,12 +105844,12 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
|
105809
105844
|
mapped[rowIndex][colIndex] = v;
|
|
105810
105845
|
return mapped;
|
|
105811
105846
|
}, []);
|
|
105812
|
-
const tokenizeCell = compatibilityMode
|
|
105813
|
-
? textToBlock
|
|
105814
|
-
: parseTableCell;
|
|
105847
|
+
const tokenizeCell = compatibilityMode ? textToBlock : (text) => parseTableCell(text, hardBreaks);
|
|
105815
105848
|
const tableChildren = Array.from({ length: rows + 1 }, (_, y) => ({
|
|
105816
105849
|
children: Array.from({ length: cols }, (__, x) => ({
|
|
105817
|
-
children: sparseData[y]?.[x]
|
|
105850
|
+
children: sparseData[y]?.[x]
|
|
105851
|
+
? tokenizeCell(preprocessBody(sparseData[y][x], hardBreaks))
|
|
105852
|
+
: [{ type: 'text', value: '' }],
|
|
105818
105853
|
type: y === 0 ? 'tableHead' : 'tableCell',
|
|
105819
105854
|
})),
|
|
105820
105855
|
type: 'tableRow',
|
|
@@ -107690,7 +107725,7 @@ function preprocessContent(content, opts) {
|
|
|
107690
107725
|
return processSnakeCaseComponent(result, { knownComponents });
|
|
107691
107726
|
}
|
|
107692
107727
|
function mdxishAstProcessor(mdContent, opts = {}) {
|
|
107693
|
-
const { components: userComponents = {}, newEditorTypes = false, safeMode = false, useTailwind } = opts;
|
|
107728
|
+
const { components: userComponents = {}, hardBreaks: enableHardBreaks = true, newEditorTypes = false, safeMode = false, useTailwind, } = opts;
|
|
107694
107729
|
const components = {
|
|
107695
107730
|
...loadComponents(),
|
|
107696
107731
|
...userComponents,
|
|
@@ -107719,7 +107754,7 @@ function mdxishAstProcessor(mdContent, opts = {}) {
|
|
|
107719
107754
|
// The next few transformers must appear after mdxishMdxComponentBlocks
|
|
107720
107755
|
// so nodes produced by the inline re-parse of component bodies
|
|
107721
107756
|
// (e.g. code/image/embed inside <Tabs>) get visited too
|
|
107722
|
-
.use(magic_block_transformer)
|
|
107757
|
+
.use(magic_block_transformer, { hardBreaks: enableHardBreaks })
|
|
107723
107758
|
.use(transform_images, { isMdxish: true })
|
|
107724
107759
|
.use(defaultTransformers)
|
|
107725
107760
|
.use(newEditorTypes ? inline_mdx_blocks : undefined) // Merge inline html components (e.g. <Anchor>) into MDAST nodes
|
|
@@ -107776,7 +107811,7 @@ function mdxishMdastToMd(mdast) {
|
|
|
107776
107811
|
* @see .claude/context/MDXish/Processor Overview.md
|
|
107777
107812
|
*/
|
|
107778
107813
|
function mdxish(mdContent, opts = {}) {
|
|
107779
|
-
const { components: userComponents = {}, safeMode = false, variables } = opts;
|
|
107814
|
+
const { components: userComponents = {}, hardBreaks: enableHardBreaks = true, safeMode = false, variables } = opts;
|
|
107780
107815
|
const components = {
|
|
107781
107816
|
...loadComponents(),
|
|
107782
107817
|
...userComponents,
|
|
@@ -107788,7 +107823,7 @@ function mdxish(mdContent, opts = {}) {
|
|
|
107788
107823
|
const { processor, parserReadyContent } = mdxishAstProcessor(contentWithoutComments, opts);
|
|
107789
107824
|
processor
|
|
107790
107825
|
.use(safeMode ? undefined : evaluate_exports) // Evaluate `export const/function` and stash scope on file.data.mdxishScope
|
|
107791
|
-
.use(hard_breaks) // Must precede evaluateExpressions to avoid splitting the \n in an evaluated template literal into a <br> node
|
|
107826
|
+
.use(enableHardBreaks ? hard_breaks : undefined) // Must precede evaluateExpressions to avoid splitting the \n in an evaluated template literal into a <br> node
|
|
107792
107827
|
.use(safeMode ? undefined : evaluate_expressions) // Evaluate self-contained MDX expressions (e.g. `{1+1}`)
|
|
107793
107828
|
.use(safeMode ? undefined : evaluate_style_block_expressions) // Evaluate `<style>{`...`}</style>` template literals into plain CSS
|
|
107794
107829
|
.use(variables_code, { variables }) // Resolve <<...>> and {user.*} inside code and inline code nodes
|
package/dist/main.node.js
CHANGED
|
@@ -95982,6 +95982,7 @@ var allCSS2RNProps = __webpack_unused_export__ = flatten([allProps, CSS2RNProps]
|
|
|
95982
95982
|
|
|
95983
95983
|
|
|
95984
95984
|
|
|
95985
|
+
|
|
95985
95986
|
/**
|
|
95986
95987
|
* Extract word boundaries from camelCase strings (e.g., "borderWidth" -> ["border", "width"])
|
|
95987
95988
|
*/
|
|
@@ -96061,10 +96062,43 @@ const CUSTOM_PROP_BOUNDARIES = [
|
|
|
96061
96062
|
*/
|
|
96062
96063
|
const RUNTIME_COMPONENT_TAGS = new Set(['Variable', 'variable', 'html-block', 'rdme-pin']);
|
|
96063
96064
|
/**
|
|
96064
|
-
*
|
|
96065
|
-
*
|
|
96065
|
+
* Elements that are not actually standard HTML tags, or those that we intentionally
|
|
96066
|
+
* don't want to treat as one & is mute to check with. These include:
|
|
96067
|
+
* - SVG/MathML descendants
|
|
96068
|
+
* - Namespaced foreign content
|
|
96069
|
+
* - `image`, which the HTML tree builder rewrites to `img`
|
|
96070
|
+
* Most of these are bundled in parse5's `TAG_NAMES` set, but we can also add a few more.
|
|
96066
96071
|
*/
|
|
96067
|
-
const
|
|
96072
|
+
const NON_STANDARD_TAGS = new Set([
|
|
96073
|
+
'annotation-xml',
|
|
96074
|
+
'desc',
|
|
96075
|
+
'foreignObject',
|
|
96076
|
+
'image',
|
|
96077
|
+
'malignmark',
|
|
96078
|
+
'mglyph',
|
|
96079
|
+
'mi',
|
|
96080
|
+
'mn',
|
|
96081
|
+
'mo',
|
|
96082
|
+
'ms',
|
|
96083
|
+
'mtext',
|
|
96084
|
+
]);
|
|
96085
|
+
/**
|
|
96086
|
+
* Standard HTML tags list.
|
|
96087
|
+
* A use case of this is to differentiate custom components tag vs standard HTML tags.
|
|
96088
|
+
*
|
|
96089
|
+
* Unioned from:
|
|
96090
|
+
* - `html-tags`: All modern spec HTML elements
|
|
96091
|
+
* - parse5's `TAG_NAMES`: Elements the HTML tree-construction algorithm has to special-case
|
|
96092
|
+
* This includes obsolete tags that are still rendered by browsers.
|
|
96093
|
+
* - NON_STANDARD_TAGS: We've had to filter because currently parse5's `TAG_NAMES` set over-enumerates
|
|
96094
|
+
* tags that are not actually standard HTML tags. It also allows custom tags to be filtered out.
|
|
96095
|
+
*/
|
|
96096
|
+
const STANDARD_HTML_TAGS = new Set([
|
|
96097
|
+
...html_tags_namespaceObject,
|
|
96098
|
+
...Object.values(TAG_NAMES),
|
|
96099
|
+
'acronym', // Obselete tag not included either of the above sources. Add here if we have missed any.
|
|
96100
|
+
'blink',
|
|
96101
|
+
].filter(tag => !NON_STANDARD_TAGS.has(tag)));
|
|
96068
96102
|
/**
|
|
96069
96103
|
* Table structural tags. Blank lines inside these carry deliberate meaning for
|
|
96070
96104
|
* `mdxishTables` (e.g. splitting cell content into paragraphs, or deciding
|
|
@@ -125534,8 +125568,11 @@ var variable_dist_default = /*#__PURE__*/__webpack_require__.n(variable_dist);
|
|
|
125534
125568
|
const HTML_TAG_RE = /<\/?([a-zA-Z][a-zA-Z0-9-]*)((?:[^>"']*(?:"[^"]*"|'[^']*'))*[^>"']*)>/g;
|
|
125535
125569
|
/** Matches an HTML element from its opening tag to the matching closing tag. */
|
|
125536
125570
|
const HTML_ELEMENT_BLOCK_RE = /<([a-zA-Z][a-zA-Z0-9-]*)[\s>][\s\S]*?<\/\1>/g;
|
|
125571
|
+
const NEWLINE_RE = /\n/g;
|
|
125537
125572
|
/** Matches a newline with surrounding horizontal whitespace. */
|
|
125538
125573
|
const NEWLINE_WITH_WHITESPACE_RE = /[^\S\n]*\n[^\S\n]*/g;
|
|
125574
|
+
/** Matches a run of two or more newlines (a blank line) with surrounding horizontal whitespace. */
|
|
125575
|
+
const BLANK_LINE_RE = /[^\S\n]*\n(?:[^\S\n]*\n)+[^\S\n]*/g;
|
|
125539
125576
|
/** Matches a closing block-level tag followed by non-tag text or by a newline then non-blank content. */
|
|
125540
125577
|
const CLOSE_BLOCK_TAG_BOUNDARY_RE = /<\/([a-zA-Z][a-zA-Z0-9-]*)>\s*(?:(?!<)(\S)|\n([^\n]))/g;
|
|
125541
125578
|
/** Strips HTML open/close tags. Used to detect non-tag inner text content. */
|
|
@@ -125605,7 +125642,6 @@ const EMPTY_CODE_PLACEHOLDER = {
|
|
|
125605
125642
|
|
|
125606
125643
|
|
|
125607
125644
|
|
|
125608
|
-
|
|
125609
125645
|
/**
|
|
125610
125646
|
* Wraps a node in a "pinned" container if sidebar: true is set.
|
|
125611
125647
|
*/
|
|
@@ -125640,16 +125676,13 @@ const textToBlock = (text) => [{ children: textToInline(text), type: 'paragraph'
|
|
|
125640
125676
|
*/
|
|
125641
125677
|
const ensureLeadingBreaks = (text) => text.replace(/^\n+/, match => '<br>'.repeat(match.length));
|
|
125642
125678
|
/** Preprocesses magic block body content before parsing. */
|
|
125643
|
-
const preprocessBody = (text) =>
|
|
125644
|
-
return ensureLeadingBreaks(text);
|
|
125645
|
-
};
|
|
125679
|
+
const preprocessBody = (text, hardBreaks) => (hardBreaks ? ensureLeadingBreaks(text) : text);
|
|
125646
125680
|
const bodyExtensions = mdxishExtensions(FEATURES.magicBlockBody);
|
|
125647
125681
|
/** Markdown parser */
|
|
125648
125682
|
const contentParser = unified()
|
|
125649
125683
|
.data('micromarkExtensions', bodyExtensions.micromarkExtensions)
|
|
125650
125684
|
.data('fromMarkdownExtensions', bodyExtensions.fromMarkdownExtensions)
|
|
125651
125685
|
.use(remarkParse)
|
|
125652
|
-
.use(hard_breaks)
|
|
125653
125686
|
.use(remarkGfm)
|
|
125654
125687
|
.use(normalize_malformed_md_syntax);
|
|
125655
125688
|
/**
|
|
@@ -125746,17 +125779,21 @@ const processMarkdownInHtmlString = (html) => {
|
|
|
125746
125779
|
/**
|
|
125747
125780
|
* Separate a closing block-level tag from the content that follows it.
|
|
125748
125781
|
*
|
|
125749
|
-
*
|
|
125750
|
-
*
|
|
125751
|
-
* the following content as markdown.
|
|
125782
|
+
* A blank line (\n\n) is appended so CommonMark ends the HTML block and parses the
|
|
125783
|
+
* following content as markdown; with hard breaks each \n also becomes a <br> to keep its spacing.
|
|
125752
125784
|
*/
|
|
125753
|
-
const separateBlockTagFromContent = (match, tag, inlineChar, nextLineChar) => {
|
|
125785
|
+
const separateBlockTagFromContent = (hardBreaks, match, tag, inlineChar, nextLineChar) => {
|
|
125754
125786
|
if (!BLOCK_LEVEL_TAGS.has(tag.toLowerCase()))
|
|
125755
125787
|
return match;
|
|
125756
|
-
const newlineCount = (match.match(
|
|
125788
|
+
const newlineCount = hardBreaks ? (match.match(NEWLINE_RE) ?? []).length : 0;
|
|
125757
125789
|
const breaks = '<br>'.repeat(newlineCount);
|
|
125758
125790
|
return `</${tag}>${breaks}\n\n${inlineChar || nextLineChar}`;
|
|
125759
125791
|
};
|
|
125792
|
+
/**
|
|
125793
|
+
* Newlines inside an HTML block become <br> so CommonMark doesn't end the block on a blank
|
|
125794
|
+
* line. Without hard breaks only blank lines break, but they still have to be replaced.
|
|
125795
|
+
*/
|
|
125796
|
+
const collapseHtmlBlockNewlines = (html, hardBreaks) => html.replace(hardBreaks ? NEWLINE_WITH_WHITESPACE_RE : BLANK_LINE_RE, '<br>');
|
|
125760
125797
|
/** Escape a leading (possibly indented) `-`/`*`/`+` so cells don't become bullet lists. */
|
|
125761
125798
|
const escapeLeadingListMarkers = (text) => text.replace(/^([ \t]*)([-*+])(?=[ \t]|$)/gm, '$1\\$2');
|
|
125762
125799
|
/**
|
|
@@ -125764,15 +125801,13 @@ const escapeLeadingListMarkers = (text) => text.replace(/^([ \t]*)([-*+])(?=[ \t
|
|
|
125764
125801
|
* so `<ul><li>_text_</li></ul>` won't convert underscores to emphasis.
|
|
125765
125802
|
* We parse first, then visit html nodes and process their text content.
|
|
125766
125803
|
*/
|
|
125767
|
-
const parseTableCell = (text) => {
|
|
125804
|
+
const parseTableCell = (text, hardBreaks) => {
|
|
125768
125805
|
if (!text.trim())
|
|
125769
125806
|
return [{ type: 'text', value: '' }];
|
|
125770
|
-
// Convert \n (and surrounding whitespace) to <br> inside HTML blocks so
|
|
125771
|
-
// CommonMark doesn't split them on blank lines.
|
|
125772
125807
|
const escaped = processBackslashEscapes(text);
|
|
125773
125808
|
const normalized = escaped
|
|
125774
|
-
.replace(HTML_ELEMENT_BLOCK_RE, match => match
|
|
125775
|
-
.replace(CLOSE_BLOCK_TAG_BOUNDARY_RE, separateBlockTagFromContent);
|
|
125809
|
+
.replace(HTML_ELEMENT_BLOCK_RE, match => collapseHtmlBlockNewlines(match, hardBreaks))
|
|
125810
|
+
.replace(CLOSE_BLOCK_TAG_BOUNDARY_RE, (match, tag, inlineChar, nextLineChar) => separateBlockTagFromContent(hardBreaks, match, tag, inlineChar, nextLineChar));
|
|
125776
125811
|
const processed = escapeLeadingListMarkers(normalized);
|
|
125777
125812
|
const tree = contentParser.runSync(contentParser.parse(processed));
|
|
125778
125813
|
// Process markdown inside HTML blocks that have non-tag inner text (e.g. `<div>**x**`
|
|
@@ -125821,7 +125856,7 @@ const parseApiHeaderTitle = (text) => {
|
|
|
125821
125856
|
* Transform a magicBlock node into final MDAST nodes.
|
|
125822
125857
|
*/
|
|
125823
125858
|
function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
125824
|
-
const { compatibilityMode = false, safeMode = false } = options;
|
|
125859
|
+
const { compatibilityMode = false, hardBreaks = true, safeMode = false } = options;
|
|
125825
125860
|
// Handle empty data by returning placeholder nodes for known block types
|
|
125826
125861
|
// This allows the editor to show appropriate placeholder UI instead of nothing
|
|
125827
125862
|
if (Object.keys(data).length < 1) {
|
|
@@ -125960,7 +125995,7 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
|
125960
125995
|
});
|
|
125961
125996
|
}
|
|
125962
125997
|
if (hasBody) {
|
|
125963
|
-
const bodyBlocks = parseBlock(preprocessBody(calloutJson.body || ''));
|
|
125998
|
+
const bodyBlocks = parseBlock(preprocessBody(calloutJson.body || '', hardBreaks));
|
|
125964
125999
|
children.push(...bodyBlocks);
|
|
125965
126000
|
}
|
|
125966
126001
|
const calloutElement = {
|
|
@@ -125992,12 +126027,12 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
|
125992
126027
|
mapped[rowIndex][colIndex] = v;
|
|
125993
126028
|
return mapped;
|
|
125994
126029
|
}, []);
|
|
125995
|
-
const tokenizeCell = compatibilityMode
|
|
125996
|
-
? textToBlock
|
|
125997
|
-
: parseTableCell;
|
|
126030
|
+
const tokenizeCell = compatibilityMode ? textToBlock : (text) => parseTableCell(text, hardBreaks);
|
|
125998
126031
|
const tableChildren = Array.from({ length: rows + 1 }, (_, y) => ({
|
|
125999
126032
|
children: Array.from({ length: cols }, (__, x) => ({
|
|
126000
|
-
children: sparseData[y]?.[x]
|
|
126033
|
+
children: sparseData[y]?.[x]
|
|
126034
|
+
? tokenizeCell(preprocessBody(sparseData[y][x], hardBreaks))
|
|
126035
|
+
: [{ type: 'text', value: '' }],
|
|
126001
126036
|
type: y === 0 ? 'tableHead' : 'tableCell',
|
|
126002
126037
|
})),
|
|
126003
126038
|
type: 'tableRow',
|
|
@@ -127873,7 +127908,7 @@ function preprocessContent(content, opts) {
|
|
|
127873
127908
|
return processSnakeCaseComponent(result, { knownComponents });
|
|
127874
127909
|
}
|
|
127875
127910
|
function mdxishAstProcessor(mdContent, opts = {}) {
|
|
127876
|
-
const { components: userComponents = {}, newEditorTypes = false, safeMode = false, useTailwind } = opts;
|
|
127911
|
+
const { components: userComponents = {}, hardBreaks: enableHardBreaks = true, newEditorTypes = false, safeMode = false, useTailwind, } = opts;
|
|
127877
127912
|
const components = {
|
|
127878
127913
|
...loadComponents(),
|
|
127879
127914
|
...userComponents,
|
|
@@ -127902,7 +127937,7 @@ function mdxishAstProcessor(mdContent, opts = {}) {
|
|
|
127902
127937
|
// The next few transformers must appear after mdxishMdxComponentBlocks
|
|
127903
127938
|
// so nodes produced by the inline re-parse of component bodies
|
|
127904
127939
|
// (e.g. code/image/embed inside <Tabs>) get visited too
|
|
127905
|
-
.use(magic_block_transformer)
|
|
127940
|
+
.use(magic_block_transformer, { hardBreaks: enableHardBreaks })
|
|
127906
127941
|
.use(transform_images, { isMdxish: true })
|
|
127907
127942
|
.use(defaultTransformers)
|
|
127908
127943
|
.use(newEditorTypes ? inline_mdx_blocks : undefined) // Merge inline html components (e.g. <Anchor>) into MDAST nodes
|
|
@@ -127959,7 +127994,7 @@ function mdxishMdastToMd(mdast) {
|
|
|
127959
127994
|
* @see .claude/context/MDXish/Processor Overview.md
|
|
127960
127995
|
*/
|
|
127961
127996
|
function mdxish(mdContent, opts = {}) {
|
|
127962
|
-
const { components: userComponents = {}, safeMode = false, variables } = opts;
|
|
127997
|
+
const { components: userComponents = {}, hardBreaks: enableHardBreaks = true, safeMode = false, variables } = opts;
|
|
127963
127998
|
const components = {
|
|
127964
127999
|
...loadComponents(),
|
|
127965
128000
|
...userComponents,
|
|
@@ -127971,7 +128006,7 @@ function mdxish(mdContent, opts = {}) {
|
|
|
127971
128006
|
const { processor, parserReadyContent } = mdxishAstProcessor(contentWithoutComments, opts);
|
|
127972
128007
|
processor
|
|
127973
128008
|
.use(safeMode ? undefined : evaluate_exports) // Evaluate `export const/function` and stash scope on file.data.mdxishScope
|
|
127974
|
-
.use(hard_breaks) // Must precede evaluateExpressions to avoid splitting the \n in an evaluated template literal into a <br> node
|
|
128009
|
+
.use(enableHardBreaks ? hard_breaks : undefined) // Must precede evaluateExpressions to avoid splitting the \n in an evaluated template literal into a <br> node
|
|
127975
128010
|
.use(safeMode ? undefined : evaluate_expressions) // Evaluate self-contained MDX expressions (e.g. `{1+1}`)
|
|
127976
128011
|
.use(safeMode ? undefined : evaluate_style_block_expressions) // Evaluate `<style>{`...`}</style>` template literals into plain CSS
|
|
127977
128012
|
.use(variables_code, { variables }) // Resolve <<...>> and {user.*} inside code and inline code nodes
|