@readme/markdown 13.1.1 → 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.
- package/dist/lib/utils/extractMagicBlocks.d.ts +25 -0
- package/dist/main.js +443 -65
- package/dist/main.node.js +443 -65
- package/dist/main.node.js.map +1 -1
- package/dist/processor/transform/mdxish/magic-blocks/patterns.d.ts +10 -0
- package/dist/processor/transform/mdxish/terminate-html-flow-blocks.d.ts +21 -0
- package/package.json +6 -2
package/dist/main.node.js
CHANGED
|
@@ -25560,8 +25560,7 @@ function color(d) {
|
|
|
25560
25560
|
|
|
25561
25561
|
;// ./node_modules/unist-util-visit-parents/lib/index.js
|
|
25562
25562
|
/**
|
|
25563
|
-
* @
|
|
25564
|
-
* @typedef {import('unist').Parent} UnistParent
|
|
25563
|
+
* @import {Node as UnistNode, Parent as UnistParent} from 'unist'
|
|
25565
25564
|
*/
|
|
25566
25565
|
|
|
25567
25566
|
/**
|
|
@@ -25609,8 +25608,10 @@ function color(d) {
|
|
|
25609
25608
|
|
|
25610
25609
|
/**
|
|
25611
25610
|
* @typedef {(
|
|
25612
|
-
* Check extends
|
|
25613
|
-
* ? MatchesOne<Value,
|
|
25611
|
+
* Check extends ReadonlyArray<infer T>
|
|
25612
|
+
* ? MatchesOne<Value, T>
|
|
25613
|
+
* : Check extends Array<infer T>
|
|
25614
|
+
* ? MatchesOne<Value, T>
|
|
25614
25615
|
* : MatchesOne<Value, Check>
|
|
25615
25616
|
* )} Matches
|
|
25616
25617
|
* Check whether a node matches a check in the type system.
|
|
@@ -25880,9 +25881,9 @@ function visitParents(tree, test, visitor, reverse) {
|
|
|
25880
25881
|
typeof value.tagName === 'string'
|
|
25881
25882
|
? value.tagName
|
|
25882
25883
|
: // `xast`
|
|
25883
|
-
|
|
25884
|
-
|
|
25885
|
-
|
|
25884
|
+
typeof value.name === 'string'
|
|
25885
|
+
? value.name
|
|
25886
|
+
: undefined
|
|
25886
25887
|
|
|
25887
25888
|
Object.defineProperty(visit, 'name', {
|
|
25888
25889
|
value:
|
|
@@ -91563,6 +91564,10 @@ const readme_components_types = {
|
|
|
91563
91564
|
Recipe: NodeTypes.recipe,
|
|
91564
91565
|
TutorialTile: NodeTypes.recipe, // coerce to recipe for backwards compatibility
|
|
91565
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]);
|
|
91566
91571
|
var TableNames;
|
|
91567
91572
|
(function (TableNames) {
|
|
91568
91573
|
TableNames["td"] = "td";
|
|
@@ -91718,6 +91723,28 @@ const coerceJsxToMd = ({ components = {}, html = false } = {}) => (node, index,
|
|
|
91718
91723
|
type: readme_components_types[node.name],
|
|
91719
91724
|
position: node.position,
|
|
91720
91725
|
};
|
|
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
|
+
}
|
|
91736
|
+
}
|
|
91737
|
+
else if (node.name === 'Recipe' || node.name === 'TutorialTile') {
|
|
91738
|
+
const hProperties = getAttrs(node);
|
|
91739
|
+
const mdNode = {
|
|
91740
|
+
...hProperties,
|
|
91741
|
+
type: readme_components_types[node.name],
|
|
91742
|
+
data: {
|
|
91743
|
+
hName: node.name,
|
|
91744
|
+
...(Object.keys(hProperties).length && { hProperties }),
|
|
91745
|
+
},
|
|
91746
|
+
position: node.position,
|
|
91747
|
+
};
|
|
91721
91748
|
parent.children[index] = mdNode;
|
|
91722
91749
|
}
|
|
91723
91750
|
else if (node.name in readme_components_types) {
|
|
@@ -91731,7 +91758,13 @@ const coerceJsxToMd = ({ components = {}, html = false } = {}) => (node, index,
|
|
|
91731
91758
|
},
|
|
91732
91759
|
position: node.position,
|
|
91733
91760
|
};
|
|
91734
|
-
parent.
|
|
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
|
+
}
|
|
91735
91768
|
}
|
|
91736
91769
|
};
|
|
91737
91770
|
const readmeComponents = (opts) => () => tree => {
|
|
@@ -113448,6 +113481,63 @@ function rehypeStringify(options) {
|
|
|
113448
113481
|
}
|
|
113449
113482
|
}
|
|
113450
113483
|
|
|
113484
|
+
;// ./node_modules/mdast-util-newline-to-break/lib/index.js
|
|
113485
|
+
/**
|
|
113486
|
+
* @typedef {import('mdast').Nodes} Nodes
|
|
113487
|
+
* @typedef {import('mdast-util-find-and-replace').ReplaceFunction} ReplaceFunction
|
|
113488
|
+
*/
|
|
113489
|
+
|
|
113490
|
+
|
|
113491
|
+
|
|
113492
|
+
/**
|
|
113493
|
+
* Turn normal line endings into hard breaks.
|
|
113494
|
+
*
|
|
113495
|
+
* @param {Nodes} tree
|
|
113496
|
+
* Tree to change.
|
|
113497
|
+
* @returns {undefined}
|
|
113498
|
+
* Nothing.
|
|
113499
|
+
*/
|
|
113500
|
+
function newlineToBreak(tree) {
|
|
113501
|
+
findAndReplace(tree, [/\r?\n|\r/g, lib_replace])
|
|
113502
|
+
}
|
|
113503
|
+
|
|
113504
|
+
/**
|
|
113505
|
+
* Replace line endings.
|
|
113506
|
+
*
|
|
113507
|
+
* @type {ReplaceFunction}
|
|
113508
|
+
*/
|
|
113509
|
+
function lib_replace() {
|
|
113510
|
+
return {type: 'break'}
|
|
113511
|
+
}
|
|
113512
|
+
|
|
113513
|
+
;// ./node_modules/remark-breaks/lib/index.js
|
|
113514
|
+
/**
|
|
113515
|
+
* @typedef {import('mdast').Root} Root
|
|
113516
|
+
*/
|
|
113517
|
+
|
|
113518
|
+
|
|
113519
|
+
|
|
113520
|
+
/**
|
|
113521
|
+
* Support hard breaks without needing spaces or escapes (turns enters into
|
|
113522
|
+
* `<br>`s).
|
|
113523
|
+
*
|
|
113524
|
+
* @returns
|
|
113525
|
+
* Transform.
|
|
113526
|
+
*/
|
|
113527
|
+
function remarkBreaks() {
|
|
113528
|
+
/**
|
|
113529
|
+
* Transform.
|
|
113530
|
+
*
|
|
113531
|
+
* @param {Root} tree
|
|
113532
|
+
* Tree.
|
|
113533
|
+
* @returns {undefined}
|
|
113534
|
+
* Nothing.
|
|
113535
|
+
*/
|
|
113536
|
+
return function (tree) {
|
|
113537
|
+
newlineToBreak(tree)
|
|
113538
|
+
}
|
|
113539
|
+
}
|
|
113540
|
+
|
|
113451
113541
|
;// ./lib/utils/mdxish/mdxish-get-component-name.ts
|
|
113452
113542
|
/** Convert a string to PascalCase */
|
|
113453
113543
|
function toPascalCase(str) {
|
|
@@ -114126,6 +114216,71 @@ const evaluateExpressions = ({ context = {} } = {}) => tree => {
|
|
|
114126
114216
|
};
|
|
114127
114217
|
/* harmony default export */ const evaluate_expressions = (evaluateExpressions);
|
|
114128
114218
|
|
|
114219
|
+
;// ./node_modules/rehype-parse/lib/index.js
|
|
114220
|
+
/**
|
|
114221
|
+
* @import {Root} from 'hast'
|
|
114222
|
+
* @import {Options as FromHtmlOptions} from 'hast-util-from-html'
|
|
114223
|
+
* @import {Parser, Processor} from 'unified'
|
|
114224
|
+
*/
|
|
114225
|
+
|
|
114226
|
+
/**
|
|
114227
|
+
* @typedef {Omit<FromHtmlOptions, 'onerror'> & RehypeParseFields} Options
|
|
114228
|
+
* Configuration.
|
|
114229
|
+
*
|
|
114230
|
+
* @typedef RehypeParseFields
|
|
114231
|
+
* Extra fields.
|
|
114232
|
+
* @property {boolean | null | undefined} [emitParseErrors=false]
|
|
114233
|
+
* Whether to emit parse errors while parsing (default: `false`).
|
|
114234
|
+
*
|
|
114235
|
+
* > 👉 **Note**: parse errors are currently being added to HTML.
|
|
114236
|
+
* > Not all errors emitted by parse5 (or us) are specced yet.
|
|
114237
|
+
* > Some documentation may still be missing.
|
|
114238
|
+
*/
|
|
114239
|
+
|
|
114240
|
+
|
|
114241
|
+
|
|
114242
|
+
/**
|
|
114243
|
+
* Plugin to add support for parsing from HTML.
|
|
114244
|
+
*
|
|
114245
|
+
* > 👉 **Note**: this is not an XML parser.
|
|
114246
|
+
* > It supports SVG as embedded in HTML.
|
|
114247
|
+
* > It does not support the features available in XML.
|
|
114248
|
+
* > Passing SVG files might break but fragments of modern SVG should be fine.
|
|
114249
|
+
* > Use [`xast-util-from-xml`][xast-util-from-xml] to parse XML.
|
|
114250
|
+
*
|
|
114251
|
+
* @param {Options | null | undefined} [options]
|
|
114252
|
+
* Configuration (optional).
|
|
114253
|
+
* @returns {undefined}
|
|
114254
|
+
* Nothing.
|
|
114255
|
+
*/
|
|
114256
|
+
function rehypeParse(options) {
|
|
114257
|
+
/** @type {Processor<Root>} */
|
|
114258
|
+
// @ts-expect-error: TS in JSDoc generates wrong types if `this` is typed regularly.
|
|
114259
|
+
const self = this
|
|
114260
|
+
const {emitParseErrors, ...settings} = {...self.data('settings'), ...options}
|
|
114261
|
+
|
|
114262
|
+
self.parser = parser
|
|
114263
|
+
|
|
114264
|
+
/**
|
|
114265
|
+
* @type {Parser<Root>}
|
|
114266
|
+
*/
|
|
114267
|
+
function parser(document, file) {
|
|
114268
|
+
return fromHtml(document, {
|
|
114269
|
+
...settings,
|
|
114270
|
+
onerror: emitParseErrors
|
|
114271
|
+
? function (message) {
|
|
114272
|
+
if (file.path) {
|
|
114273
|
+
message.name = file.path + ':' + message.name
|
|
114274
|
+
message.file = file.path
|
|
114275
|
+
}
|
|
114276
|
+
|
|
114277
|
+
file.messages.push(message)
|
|
114278
|
+
}
|
|
114279
|
+
: undefined
|
|
114280
|
+
})
|
|
114281
|
+
}
|
|
114282
|
+
}
|
|
114283
|
+
|
|
114129
114284
|
;// ./processor/transform/mdxish/normalize-malformed-md-syntax.ts
|
|
114130
114285
|
|
|
114131
114286
|
// Marker patterns for multi-node emphasis detection
|
|
@@ -114469,6 +114624,18 @@ const normalizeEmphasisAST = () => (tree) => {
|
|
|
114469
114624
|
};
|
|
114470
114625
|
/* harmony default export */ const normalize_malformed_md_syntax = (normalizeEmphasisAST);
|
|
114471
114626
|
|
|
114627
|
+
;// ./processor/transform/mdxish/magic-blocks/patterns.ts
|
|
114628
|
+
/** Matches HTML tags (open, close, self-closing) with optional attributes. */
|
|
114629
|
+
const HTML_TAG_RE = /<\/?([a-zA-Z][a-zA-Z0-9-]*)((?:[^>"']*(?:"[^"]*"|'[^']*'))*[^>"']*)>/g;
|
|
114630
|
+
/** Matches an HTML element from its opening tag to the matching closing tag. */
|
|
114631
|
+
const HTML_ELEMENT_BLOCK_RE = /<([a-zA-Z][a-zA-Z0-9-]*)[\s>][\s\S]*?<\/\1>/g;
|
|
114632
|
+
/** Matches a newline with surrounding horizontal whitespace. */
|
|
114633
|
+
const NEWLINE_WITH_WHITESPACE_RE = /[^\S\n]*\n[^\S\n]*/g;
|
|
114634
|
+
/** Matches a closing block-level tag followed by non-tag text or by a newline then non-blank content. */
|
|
114635
|
+
const CLOSE_BLOCK_TAG_BOUNDARY_RE = /<\/([a-zA-Z][a-zA-Z0-9-]*)>\s*(?:(?!<)(\S)|\n([^\n]))/g;
|
|
114636
|
+
/** Tests whether a string contains a complete HTML element (open + close tag). */
|
|
114637
|
+
const COMPLETE_HTML_ELEMENT_RE = /<[a-zA-Z][^>]*>[\s\S]*<\/[a-zA-Z]/;
|
|
114638
|
+
|
|
114472
114639
|
;// ./processor/transform/mdxish/magic-blocks/placeholder.ts
|
|
114473
114640
|
const EMPTY_IMAGE_PLACEHOLDER = {
|
|
114474
114641
|
type: 'image',
|
|
@@ -114522,6 +114689,14 @@ const EMPTY_CODE_PLACEHOLDER = {
|
|
|
114522
114689
|
|
|
114523
114690
|
|
|
114524
114691
|
|
|
114692
|
+
|
|
114693
|
+
|
|
114694
|
+
|
|
114695
|
+
|
|
114696
|
+
|
|
114697
|
+
|
|
114698
|
+
|
|
114699
|
+
|
|
114525
114700
|
/**
|
|
114526
114701
|
* Wraps a node in a "pinned" container if sidebar: true is set.
|
|
114527
114702
|
*/
|
|
@@ -114549,12 +114724,125 @@ const imgWidthBySize = new Proxy(imgSizeValues, {
|
|
|
114549
114724
|
});
|
|
114550
114725
|
const textToInline = (text) => [{ type: 'text', value: text }];
|
|
114551
114726
|
const textToBlock = (text) => [{ children: textToInline(text), type: 'paragraph' }];
|
|
114552
|
-
/**
|
|
114553
|
-
|
|
114727
|
+
/**
|
|
114728
|
+
* Converts leading newlines in magic block content to `<br>` tags.
|
|
114729
|
+
* Leading newlines are stripped by remark-parse before they become soft break nodes,
|
|
114730
|
+
* so remark-breaks cannot handle them. We convert them to HTML `<br>` tags instead.
|
|
114731
|
+
*/
|
|
114732
|
+
const ensureLeadingBreaks = (text) => text.replace(/^\n+/, match => '<br>'.repeat(match.length));
|
|
114733
|
+
/** Preprocesses magic block body content before parsing. */
|
|
114734
|
+
const preprocessBody = (text) => {
|
|
114735
|
+
return ensureLeadingBreaks(text);
|
|
114736
|
+
};
|
|
114737
|
+
/** Markdown parser */
|
|
114738
|
+
const contentParser = unified().use(remarkParse).use(remarkBreaks).use(remarkGfm).use(normalize_malformed_md_syntax);
|
|
114739
|
+
/** Markdown to HTML processor (mdast → hast → HTML string) */
|
|
114740
|
+
const markdownToHtml = unified()
|
|
114741
|
+
.use(remarkParse)
|
|
114742
|
+
.use(remarkGfm)
|
|
114743
|
+
.use(normalize_malformed_md_syntax)
|
|
114744
|
+
.use(remarkRehype)
|
|
114745
|
+
.use(rehypeStringify);
|
|
114746
|
+
/** HTML parser (HTML string → hast) */
|
|
114747
|
+
const htmlParser = unified().use(rehypeParse, { fragment: true });
|
|
114748
|
+
/** HTML stringifier (hast → HTML string) */
|
|
114749
|
+
const htmlStringifier = unified().use(rehypeStringify);
|
|
114750
|
+
/** Process \|, \<, \> backslash escapes. Only < is entity-escaped; > is left literal to avoid double-encoding by rehype. */
|
|
114751
|
+
const processBackslashEscapes = (text) => text.replace(/\\<([^>]*)>/g, '<$1>').replace(/\\([<>|])/g, (_, c) => (c === '<' ? '<' : c === '>' ? '>' : c));
|
|
114752
|
+
/** Block-level HTML tags that trigger CommonMark type 6 HTML blocks (condition 6). */
|
|
114753
|
+
const BLOCK_LEVEL_TAGS = new Set(htmlBlockNames);
|
|
114754
|
+
const escapeInvalidTags = (str) => str.replace(HTML_TAG_RE, (match, tag, rest) => {
|
|
114755
|
+
const tagName = tag.replace(/^\//, '');
|
|
114756
|
+
if (STANDARD_HTML_TAGS.has(tagName.toLowerCase()))
|
|
114757
|
+
return match;
|
|
114758
|
+
// Preserve PascalCase tags (custom components like <Glossary>) for the main pipeline
|
|
114759
|
+
if (/^[A-Z]/.test(tagName))
|
|
114760
|
+
return match;
|
|
114761
|
+
return `<${tag}${rest}>`;
|
|
114762
|
+
});
|
|
114763
|
+
/**
|
|
114764
|
+
* Process markdown within HTML string.
|
|
114765
|
+
* 1. Parse HTML to HAST
|
|
114766
|
+
* 2. Find text nodes, parse as markdown, convert to HAST
|
|
114767
|
+
* 3. Stringify back to HTML
|
|
114768
|
+
*
|
|
114769
|
+
* PascalCase component tags (e.g. `<Glossary>`) are temporarily replaced with
|
|
114770
|
+
* placeholders before HTML parsing so `rehype-parse` doesn't mangle them
|
|
114771
|
+
* (it treats unknown tags as void elements, stripping their children).
|
|
114772
|
+
*/
|
|
114773
|
+
const processMarkdownInHtmlString = (html) => {
|
|
114774
|
+
const placeholders = [];
|
|
114775
|
+
let counter = 0;
|
|
114776
|
+
const safened = escapeInvalidTags(html).replace(HTML_TAG_RE, match => {
|
|
114777
|
+
if (!/^<\/?[A-Z]/.test(match))
|
|
114778
|
+
return match;
|
|
114779
|
+
const id = `<!--PC${(counter += 1)}-->`;
|
|
114780
|
+
placeholders.push([id, match]);
|
|
114781
|
+
return id;
|
|
114782
|
+
});
|
|
114783
|
+
const hast = htmlParser.parse(safened);
|
|
114784
|
+
const textToHast = (text) => {
|
|
114785
|
+
if (!text.trim())
|
|
114786
|
+
return [{ type: 'text', value: text }];
|
|
114787
|
+
const parsed = markdownToHtml.runSync(markdownToHtml.parse(escapeInvalidTags(text)));
|
|
114788
|
+
const nodes = parsed.children.flatMap(n => n.type === 'element' && n.tagName === 'p' ? n.children : [n]);
|
|
114789
|
+
const leading = text.match(/^\s+/)?.[0];
|
|
114790
|
+
const trailing = text.match(/\s+$/)?.[0];
|
|
114791
|
+
if (leading)
|
|
114792
|
+
nodes.unshift({ type: 'text', value: leading });
|
|
114793
|
+
if (trailing)
|
|
114794
|
+
nodes.push({ type: 'text', value: trailing });
|
|
114795
|
+
return nodes;
|
|
114796
|
+
};
|
|
114797
|
+
const processChildren = (children) => children.flatMap(child => (child.type === 'text' ? textToHast(child.value) : [child]));
|
|
114798
|
+
hast.children = processChildren(hast.children);
|
|
114799
|
+
visit(hast, 'element', (node) => {
|
|
114800
|
+
node.children = processChildren(node.children);
|
|
114801
|
+
});
|
|
114802
|
+
return placeholders.reduce((res, [id, original]) => res.replace(id, original), htmlStringifier.stringify(hast));
|
|
114803
|
+
};
|
|
114804
|
+
/**
|
|
114805
|
+
* Separate a closing block-level tag from the content that follows it.
|
|
114806
|
+
*
|
|
114807
|
+
* Each \n in the original text becomes a <br> tag to preserve spacing, then a
|
|
114808
|
+
* blank line (\n\n) is appended so CommonMark ends the HTML block and parses
|
|
114809
|
+
* the following content as markdown.
|
|
114810
|
+
*/
|
|
114811
|
+
const separateBlockTagFromContent = (match, tag, inlineChar, nextLineChar) => {
|
|
114812
|
+
if (!BLOCK_LEVEL_TAGS.has(tag.toLowerCase()))
|
|
114813
|
+
return match;
|
|
114814
|
+
const newlineCount = (match.match(/\n/g) ?? []).length;
|
|
114815
|
+
const breaks = '<br>'.repeat(newlineCount);
|
|
114816
|
+
return `</${tag}>${breaks}\n\n${inlineChar || nextLineChar}`;
|
|
114817
|
+
};
|
|
114818
|
+
/**
|
|
114819
|
+
* CommonMark doesn't process markdown inside HTML blocks -
|
|
114820
|
+
* so `<ul><li>_text_</li></ul>` won't convert underscores to emphasis.
|
|
114821
|
+
* We parse first, then visit html nodes and process their text content.
|
|
114822
|
+
*/
|
|
114554
114823
|
const parseTableCell = (text) => {
|
|
114555
114824
|
if (!text.trim())
|
|
114556
114825
|
return [{ type: 'text', value: '' }];
|
|
114557
|
-
|
|
114826
|
+
// Convert \n (and surrounding whitespace) to <br> inside HTML blocks so
|
|
114827
|
+
// CommonMark doesn't split them on blank lines.
|
|
114828
|
+
// Then strip leading whitespace to prevent indented code blocks.
|
|
114829
|
+
const escaped = processBackslashEscapes(text);
|
|
114830
|
+
const normalized = escaped
|
|
114831
|
+
.replace(HTML_ELEMENT_BLOCK_RE, match => match.replace(NEWLINE_WITH_WHITESPACE_RE, '<br>'))
|
|
114832
|
+
.replace(CLOSE_BLOCK_TAG_BOUNDARY_RE, separateBlockTagFromContent);
|
|
114833
|
+
const trimmedLines = normalized.split('\n').map(line => line.trimStart());
|
|
114834
|
+
const processed = trimmedLines.join('\n');
|
|
114835
|
+
const tree = contentParser.runSync(contentParser.parse(processed));
|
|
114836
|
+
// Process markdown inside complete HTML elements (e.g. _emphasis_ within <li>).
|
|
114837
|
+
// Bare tags like "<i>" are left for rehypeRaw since rehype-parse would mangle them.
|
|
114838
|
+
visit(tree, 'html', (node) => {
|
|
114839
|
+
if (COMPLETE_HTML_ELEMENT_RE.test(node.value)) {
|
|
114840
|
+
node.value = processMarkdownInHtmlString(node.value);
|
|
114841
|
+
}
|
|
114842
|
+
else {
|
|
114843
|
+
node.value = escapeInvalidTags(node.value);
|
|
114844
|
+
}
|
|
114845
|
+
});
|
|
114558
114846
|
if (tree.children.length > 1) {
|
|
114559
114847
|
return tree.children;
|
|
114560
114848
|
}
|
|
@@ -114709,7 +114997,7 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
|
114709
114997
|
});
|
|
114710
114998
|
}
|
|
114711
114999
|
if (hasBody) {
|
|
114712
|
-
const bodyBlocks = parseBlock(calloutJson.body || '');
|
|
115000
|
+
const bodyBlocks = parseBlock(preprocessBody(calloutJson.body || ''));
|
|
114713
115001
|
children.push(...bodyBlocks);
|
|
114714
115002
|
}
|
|
114715
115003
|
const calloutElement = {
|
|
@@ -114744,7 +115032,7 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
|
114744
115032
|
const tokenizeCell = compatibilityMode ? textToBlock : parseTableCell;
|
|
114745
115033
|
const tableChildren = Array.from({ length: rows + 1 }, (_, y) => ({
|
|
114746
115034
|
children: Array.from({ length: cols }, (__, x) => ({
|
|
114747
|
-
children: sparseData[y]?.[x] ? tokenizeCell(sparseData[y][x]) : [{ type: 'text', value: '' }],
|
|
115035
|
+
children: sparseData[y]?.[x] ? tokenizeCell(preprocessBody(sparseData[y][x])) : [{ type: 'text', value: '' }],
|
|
114748
115036
|
type: y === 0 ? 'tableHead' : 'tableCell',
|
|
114749
115037
|
})),
|
|
114750
115038
|
type: 'tableRow',
|
|
@@ -114845,79 +115133,63 @@ const isBlockNode = (node) => blockTypes.includes(node.type);
|
|
|
114845
115133
|
*/
|
|
114846
115134
|
const magicBlockTransformer = (options = {}) => tree => {
|
|
114847
115135
|
const replacements = [];
|
|
114848
|
-
|
|
114849
|
-
|
|
114850
|
-
|
|
115136
|
+
visitParents(tree, 'magicBlock', (node, ancestors) => {
|
|
115137
|
+
const parent = ancestors[ancestors.length - 1]; // direct parent of the current node
|
|
115138
|
+
const index = parent.children.indexOf(node);
|
|
115139
|
+
if (index === -1)
|
|
115140
|
+
return;
|
|
114851
115141
|
const children = transformMagicBlock(node.blockType, node.data, node.value, options);
|
|
114852
115142
|
if (!children.length) {
|
|
114853
|
-
//
|
|
115143
|
+
// `visitParents` doesn't support [Action, Index] returns like `visit` does;
|
|
115144
|
+
// a bare return after splicing is sufficient since `visitParents` walks by
|
|
115145
|
+
// tree structure rather than index.
|
|
114854
115146
|
parent.children.splice(index, 1);
|
|
114855
|
-
return
|
|
115147
|
+
return;
|
|
114856
115148
|
}
|
|
114857
115149
|
// If parent is a paragraph and we're inserting block nodes (which must not be in paragraphs), lift them out
|
|
114858
115150
|
if (parent.type === 'paragraph' && children.some(child => isBlockNode(child))) {
|
|
114859
115151
|
const blockNodes = [];
|
|
114860
115152
|
const inlineNodes = [];
|
|
114861
|
-
// Separate block and inline nodes
|
|
114862
115153
|
children.forEach(child => {
|
|
114863
|
-
|
|
114864
|
-
blockNodes.push(child);
|
|
114865
|
-
}
|
|
114866
|
-
else {
|
|
114867
|
-
inlineNodes.push(child);
|
|
114868
|
-
}
|
|
115154
|
+
(isBlockNode(child) ? blockNodes : inlineNodes).push(child);
|
|
114869
115155
|
});
|
|
114870
|
-
const before = parent.children.slice(0, index);
|
|
114871
|
-
const after = parent.children.slice(index + 1);
|
|
114872
115156
|
replacements.push({
|
|
115157
|
+
container: ancestors[ancestors.length - 2] || tree, // grandparent of the current node
|
|
114873
115158
|
parent,
|
|
114874
115159
|
blockNodes,
|
|
114875
115160
|
inlineNodes,
|
|
114876
|
-
before,
|
|
114877
|
-
after,
|
|
115161
|
+
before: parent.children.slice(0, index),
|
|
115162
|
+
after: parent.children.slice(index + 1),
|
|
114878
115163
|
});
|
|
114879
115164
|
}
|
|
114880
115165
|
else {
|
|
114881
|
-
// Normal case: just replace the inlineCode with the children
|
|
114882
115166
|
parent.children.splice(index, 1, ...children);
|
|
114883
115167
|
}
|
|
114884
|
-
return undefined;
|
|
114885
115168
|
});
|
|
114886
115169
|
// Second pass: apply replacements that require lifting block nodes out of paragraphs
|
|
114887
115170
|
// Process in reverse order to maintain correct indices
|
|
114888
115171
|
for (let i = replacements.length - 1; i >= 0; i -= 1) {
|
|
114889
|
-
const { after, before, blockNodes, inlineNodes, parent } = replacements[i];
|
|
114890
|
-
|
|
114891
|
-
const
|
|
114892
|
-
const paraIndex = rootChildren.findIndex(child => child === parent);
|
|
115172
|
+
const { after, before, blockNodes, container, inlineNodes, parent } = replacements[i];
|
|
115173
|
+
const containerChildren = container.children;
|
|
115174
|
+
const paraIndex = containerChildren.indexOf(parent);
|
|
114893
115175
|
if (paraIndex === -1) {
|
|
114894
|
-
|
|
114895
|
-
// This shouldn't happen normally, but handle it gracefully
|
|
114896
|
-
// Reconstruct the original index from before.length
|
|
114897
|
-
const originalIndex = before.length;
|
|
114898
|
-
parent.children.splice(originalIndex, 1, ...blockNodes, ...inlineNodes);
|
|
115176
|
+
parent.children.splice(before.length, 1, ...blockNodes, ...inlineNodes);
|
|
114899
115177
|
// eslint-disable-next-line no-continue
|
|
114900
115178
|
continue;
|
|
114901
115179
|
}
|
|
114902
|
-
// Update or remove the paragraph
|
|
114903
115180
|
if (inlineNodes.length > 0) {
|
|
114904
|
-
// Keep paragraph with inline nodes
|
|
114905
115181
|
parent.children = [...before, ...inlineNodes, ...after];
|
|
114906
|
-
// Insert block nodes after the paragraph
|
|
114907
115182
|
if (blockNodes.length > 0) {
|
|
114908
|
-
|
|
115183
|
+
containerChildren.splice(paraIndex + 1, 0, ...blockNodes);
|
|
114909
115184
|
}
|
|
114910
115185
|
}
|
|
114911
115186
|
else if (before.length === 0 && after.length === 0) {
|
|
114912
|
-
|
|
114913
|
-
rootChildren.splice(paraIndex, 1, ...blockNodes);
|
|
115187
|
+
containerChildren.splice(paraIndex, 1, ...blockNodes);
|
|
114914
115188
|
}
|
|
114915
115189
|
else {
|
|
114916
|
-
// Keep paragraph with remaining content
|
|
114917
115190
|
parent.children = [...before, ...after];
|
|
114918
|
-
// Insert block nodes after the paragraph
|
|
114919
115191
|
if (blockNodes.length > 0) {
|
|
114920
|
-
|
|
115192
|
+
containerChildren.splice(paraIndex + 1, 0, ...blockNodes);
|
|
114921
115193
|
}
|
|
114922
115194
|
}
|
|
114923
115195
|
}
|
|
@@ -115752,6 +116024,45 @@ const restoreBooleanProperties = () => tree => {
|
|
|
115752
116024
|
};
|
|
115753
116025
|
|
|
115754
116026
|
|
|
116027
|
+
;// ./processor/transform/mdxish/terminate-html-flow-blocks.ts
|
|
116028
|
+
|
|
116029
|
+
const STANDALONE_HTML_LINE_REGEX = /^(<[a-z][^<>]*>|<\/[a-z][^<>]*>)+\s*$/;
|
|
116030
|
+
const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/;
|
|
116031
|
+
/**
|
|
116032
|
+
* Preprocessor to terminate HTML flow blocks.
|
|
116033
|
+
*
|
|
116034
|
+
* In CommonMark, HTML blocks (types 6 and 7) only terminate on a blank line.
|
|
116035
|
+
* Without one, any content on the next line is consumed as part of the HTML block
|
|
116036
|
+
* and never parsed as its own construct. For example, a `[block:callout]` immediately
|
|
116037
|
+
* following `<div><p></p></div>` gets swallowed into the HTML flow token.
|
|
116038
|
+
*
|
|
116039
|
+
* @link https://spec.commonmark.org/0.29/#html-blocks
|
|
116040
|
+
*
|
|
116041
|
+
* This preprocessor inserts a blank line after standalone HTML lines when the
|
|
116042
|
+
* next line is non-blank, ensuring micromark's HTML flow tokenizer terminates
|
|
116043
|
+
* and subsequent content is parsed independently.
|
|
116044
|
+
*
|
|
116045
|
+
* Only targets non-indented lines with lowercase tag names. Uppercase tags
|
|
116046
|
+
* (e.g., `<Table>`, `<MyComponent>`) are JSX custom components and don't
|
|
116047
|
+
* trigger CommonMark HTML blocks, so they are left untouched.
|
|
116048
|
+
*
|
|
116049
|
+
* Lines inside fenced code blocks are skipped entirely.
|
|
116050
|
+
*/
|
|
116051
|
+
function terminateHtmlFlowBlocks(content) {
|
|
116052
|
+
const { protectedContent, protectedCode } = protectCodeBlocks(content);
|
|
116053
|
+
const lines = protectedContent.split('\n');
|
|
116054
|
+
const result = [];
|
|
116055
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
116056
|
+
result.push(lines[i]);
|
|
116057
|
+
if (i < lines.length - 1 &&
|
|
116058
|
+
(STANDALONE_HTML_LINE_REGEX.test(lines[i]) || HTML_LINE_WITH_CONTENT_REGEX.test(lines[i])) &&
|
|
116059
|
+
lines[i + 1].trim().length > 0) {
|
|
116060
|
+
result.push('');
|
|
116061
|
+
}
|
|
116062
|
+
}
|
|
116063
|
+
return restoreCodeBlocks(result.join('\n'), protectedCode);
|
|
116064
|
+
}
|
|
116065
|
+
|
|
115755
116066
|
;// ./processor/transform/mdxish/variables-text.ts
|
|
115756
116067
|
|
|
115757
116068
|
|
|
@@ -117054,10 +117365,29 @@ function loadComponents() {
|
|
|
117054
117365
|
|
|
117055
117366
|
|
|
117056
117367
|
|
|
117368
|
+
|
|
117369
|
+
|
|
117057
117370
|
|
|
117058
117371
|
|
|
117059
117372
|
|
|
117060
117373
|
const defaultTransformers = [callouts, code_tabs, gemoji_, transform_embeds];
|
|
117374
|
+
/**
|
|
117375
|
+
* Preprocessing pipeline: applies string-level transformations to work around
|
|
117376
|
+
* CommonMark/remark limitations and reach parity with legacy (rdmd) rendering.
|
|
117377
|
+
*
|
|
117378
|
+
* Runs a series of string-level transformations before micromark/remark parsing:
|
|
117379
|
+
* 1. Normalize malformed table separator syntax (e.g., `|: ---` → `| :---`)
|
|
117380
|
+
* 2. Terminate HTML flow blocks so subsequent content isn't swallowed
|
|
117381
|
+
* 3. Evaluate JSX expressions in attributes (unless safeMode)
|
|
117382
|
+
* 4. Replace snake_case component names with parser-safe placeholders
|
|
117383
|
+
*/
|
|
117384
|
+
function preprocessContent(content, opts) {
|
|
117385
|
+
const { safeMode, jsxContext, knownComponents } = opts;
|
|
117386
|
+
let result = normalizeTableSeparator(content);
|
|
117387
|
+
result = terminateHtmlFlowBlocks(result);
|
|
117388
|
+
result = safeMode ? result : preprocessJSXExpressions(result, jsxContext);
|
|
117389
|
+
return processSnakeCaseComponent(result, { knownComponents });
|
|
117390
|
+
}
|
|
117061
117391
|
function mdxishAstProcessor(mdContent, opts = {}) {
|
|
117062
117392
|
const { components: userComponents = {}, jsxContext = {}, newEditorTypes = false, safeMode = false, useTailwind, } = opts;
|
|
117063
117393
|
const components = {
|
|
@@ -117066,15 +117396,11 @@ function mdxishAstProcessor(mdContent, opts = {}) {
|
|
|
117066
117396
|
};
|
|
117067
117397
|
// Build set of known component names for snake_case filtering
|
|
117068
117398
|
const knownComponents = new Set(Object.keys(components));
|
|
117069
|
-
|
|
117070
|
-
|
|
117071
|
-
|
|
117072
|
-
|
|
117073
|
-
|
|
117074
|
-
? contentAfterTableNormalization
|
|
117075
|
-
: preprocessJSXExpressions(contentAfterTableNormalization, jsxContext);
|
|
117076
|
-
// Step 3: Replace snake_case component names with parser-safe placeholders
|
|
117077
|
-
const { content: parserReadyContent, mapping: snakeCaseMapping } = processSnakeCaseComponent(contentAfterJSXEvaluation, { knownComponents });
|
|
117399
|
+
const { content: parserReadyContent, mapping: snakeCaseMapping } = preprocessContent(mdContent, {
|
|
117400
|
+
safeMode,
|
|
117401
|
+
jsxContext,
|
|
117402
|
+
knownComponents,
|
|
117403
|
+
});
|
|
117078
117404
|
// Create string map for tailwind transformer
|
|
117079
117405
|
const tempComponentsMap = Object.entries(components).reduce((acc, [key, value]) => {
|
|
117080
117406
|
acc[key] = String(value);
|
|
@@ -117142,6 +117468,7 @@ function mdxish(mdContent, opts = {}) {
|
|
|
117142
117468
|
};
|
|
117143
117469
|
const { processor, parserReadyContent } = mdxishAstProcessor(mdContent, opts);
|
|
117144
117470
|
processor
|
|
117471
|
+
.use(remarkBreaks)
|
|
117145
117472
|
.use(remarkRehype, { allowDangerousHtml: true, handlers: mdxComponentHandlers })
|
|
117146
117473
|
.use(preserveBooleanProperties) // RehypeRaw converts boolean properties to empty strings
|
|
117147
117474
|
.use(rehypeRaw, { passThrough: ['html-block'] })
|
|
@@ -117638,8 +117965,59 @@ const mdxishTags_tags = (doc) => {
|
|
|
117638
117965
|
};
|
|
117639
117966
|
/* harmony default export */ const mdxishTags = (mdxishTags_tags);
|
|
117640
117967
|
|
|
117641
|
-
;// ./lib/
|
|
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
|
+
}
|
|
117642
118019
|
|
|
118020
|
+
;// ./lib/stripComments.ts
|
|
117643
118021
|
|
|
117644
118022
|
|
|
117645
118023
|
|
|
@@ -117654,10 +118032,8 @@ const mdxishTags_tags = (doc) => {
|
|
|
117654
118032
|
* Removes Markdown and MDX comments.
|
|
117655
118033
|
*/
|
|
117656
118034
|
async function stripComments(doc, { mdx, mdxish } = {}) {
|
|
117657
|
-
const
|
|
117658
|
-
|
|
117659
|
-
.data('fromMarkdownExtensions', [magicBlockFromMarkdown()])
|
|
117660
|
-
.data('toMarkdownExtensions', [magicBlockToMarkdown()]);
|
|
118035
|
+
const { replaced, blocks } = extractMagicBlocks(doc);
|
|
118036
|
+
const processor = unified();
|
|
117661
118037
|
// we still require these two extensions because:
|
|
117662
118038
|
// 1. we can rely on remarkMdx to parse MDXish
|
|
117663
118039
|
// 2. we need to parse JSX comments into mdxTextExpression nodes so that the transformers can pick them up
|
|
@@ -117699,8 +118075,10 @@ async function stripComments(doc, { mdx, mdxish } = {}) {
|
|
|
117699
118075
|
},
|
|
117700
118076
|
],
|
|
117701
118077
|
});
|
|
117702
|
-
const file = await processor.process(
|
|
117703
|
-
|
|
118078
|
+
const file = await processor.process(replaced);
|
|
118079
|
+
const stringified = String(file).trim();
|
|
118080
|
+
const restored = restoreMagicBlocks(stringified, blocks);
|
|
118081
|
+
return restored;
|
|
117704
118082
|
}
|
|
117705
118083
|
/* harmony default export */ const lib_stripComments = (stripComments);
|
|
117706
118084
|
|