@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.js
CHANGED
|
@@ -13007,8 +13007,7 @@ function color(d) {
|
|
|
13007
13007
|
|
|
13008
13008
|
;// ./node_modules/unist-util-visit-parents/lib/index.js
|
|
13009
13009
|
/**
|
|
13010
|
-
* @
|
|
13011
|
-
* @typedef {import('unist').Parent} UnistParent
|
|
13010
|
+
* @import {Node as UnistNode, Parent as UnistParent} from 'unist'
|
|
13012
13011
|
*/
|
|
13013
13012
|
|
|
13014
13013
|
/**
|
|
@@ -13056,8 +13055,10 @@ function color(d) {
|
|
|
13056
13055
|
|
|
13057
13056
|
/**
|
|
13058
13057
|
* @typedef {(
|
|
13059
|
-
* Check extends
|
|
13060
|
-
* ? MatchesOne<Value,
|
|
13058
|
+
* Check extends ReadonlyArray<infer T>
|
|
13059
|
+
* ? MatchesOne<Value, T>
|
|
13060
|
+
* : Check extends Array<infer T>
|
|
13061
|
+
* ? MatchesOne<Value, T>
|
|
13061
13062
|
* : MatchesOne<Value, Check>
|
|
13062
13063
|
* )} Matches
|
|
13063
13064
|
* Check whether a node matches a check in the type system.
|
|
@@ -13327,9 +13328,9 @@ function visitParents(tree, test, visitor, reverse) {
|
|
|
13327
13328
|
typeof value.tagName === 'string'
|
|
13328
13329
|
? value.tagName
|
|
13329
13330
|
: // `xast`
|
|
13330
|
-
|
|
13331
|
-
|
|
13332
|
-
|
|
13331
|
+
typeof value.name === 'string'
|
|
13332
|
+
? value.name
|
|
13333
|
+
: undefined
|
|
13333
13334
|
|
|
13334
13335
|
Object.defineProperty(visit, 'name', {
|
|
13335
13336
|
value:
|
|
@@ -71359,6 +71360,10 @@ const types = {
|
|
|
71359
71360
|
Recipe: NodeTypes.recipe,
|
|
71360
71361
|
TutorialTile: NodeTypes.recipe, // coerce to recipe for backwards compatibility
|
|
71361
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]);
|
|
71362
71367
|
var TableNames;
|
|
71363
71368
|
(function (TableNames) {
|
|
71364
71369
|
TableNames["td"] = "td";
|
|
@@ -71514,6 +71519,28 @@ const coerceJsxToMd = ({ components = {}, html = false } = {}) => (node, index,
|
|
|
71514
71519
|
type: types[node.name],
|
|
71515
71520
|
position: node.position,
|
|
71516
71521
|
};
|
|
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
|
+
}
|
|
71532
|
+
}
|
|
71533
|
+
else if (node.name === 'Recipe' || node.name === 'TutorialTile') {
|
|
71534
|
+
const hProperties = getAttrs(node);
|
|
71535
|
+
const mdNode = {
|
|
71536
|
+
...hProperties,
|
|
71537
|
+
type: types[node.name],
|
|
71538
|
+
data: {
|
|
71539
|
+
hName: node.name,
|
|
71540
|
+
...(Object.keys(hProperties).length && { hProperties }),
|
|
71541
|
+
},
|
|
71542
|
+
position: node.position,
|
|
71543
|
+
};
|
|
71517
71544
|
parent.children[index] = mdNode;
|
|
71518
71545
|
}
|
|
71519
71546
|
else if (node.name in types) {
|
|
@@ -71527,7 +71554,13 @@ const coerceJsxToMd = ({ components = {}, html = false } = {}) => (node, index,
|
|
|
71527
71554
|
},
|
|
71528
71555
|
position: node.position,
|
|
71529
71556
|
};
|
|
71530
|
-
parent.
|
|
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
|
+
}
|
|
71531
71564
|
}
|
|
71532
71565
|
};
|
|
71533
71566
|
const readmeComponents = (opts) => () => tree => {
|
|
@@ -93244,6 +93277,63 @@ function rehypeStringify(options) {
|
|
|
93244
93277
|
}
|
|
93245
93278
|
}
|
|
93246
93279
|
|
|
93280
|
+
;// ./node_modules/mdast-util-newline-to-break/lib/index.js
|
|
93281
|
+
/**
|
|
93282
|
+
* @typedef {import('mdast').Nodes} Nodes
|
|
93283
|
+
* @typedef {import('mdast-util-find-and-replace').ReplaceFunction} ReplaceFunction
|
|
93284
|
+
*/
|
|
93285
|
+
|
|
93286
|
+
|
|
93287
|
+
|
|
93288
|
+
/**
|
|
93289
|
+
* Turn normal line endings into hard breaks.
|
|
93290
|
+
*
|
|
93291
|
+
* @param {Nodes} tree
|
|
93292
|
+
* Tree to change.
|
|
93293
|
+
* @returns {undefined}
|
|
93294
|
+
* Nothing.
|
|
93295
|
+
*/
|
|
93296
|
+
function newlineToBreak(tree) {
|
|
93297
|
+
findAndReplace(tree, [/\r?\n|\r/g, lib_replace])
|
|
93298
|
+
}
|
|
93299
|
+
|
|
93300
|
+
/**
|
|
93301
|
+
* Replace line endings.
|
|
93302
|
+
*
|
|
93303
|
+
* @type {ReplaceFunction}
|
|
93304
|
+
*/
|
|
93305
|
+
function lib_replace() {
|
|
93306
|
+
return {type: 'break'}
|
|
93307
|
+
}
|
|
93308
|
+
|
|
93309
|
+
;// ./node_modules/remark-breaks/lib/index.js
|
|
93310
|
+
/**
|
|
93311
|
+
* @typedef {import('mdast').Root} Root
|
|
93312
|
+
*/
|
|
93313
|
+
|
|
93314
|
+
|
|
93315
|
+
|
|
93316
|
+
/**
|
|
93317
|
+
* Support hard breaks without needing spaces or escapes (turns enters into
|
|
93318
|
+
* `<br>`s).
|
|
93319
|
+
*
|
|
93320
|
+
* @returns
|
|
93321
|
+
* Transform.
|
|
93322
|
+
*/
|
|
93323
|
+
function remarkBreaks() {
|
|
93324
|
+
/**
|
|
93325
|
+
* Transform.
|
|
93326
|
+
*
|
|
93327
|
+
* @param {Root} tree
|
|
93328
|
+
* Tree.
|
|
93329
|
+
* @returns {undefined}
|
|
93330
|
+
* Nothing.
|
|
93331
|
+
*/
|
|
93332
|
+
return function (tree) {
|
|
93333
|
+
newlineToBreak(tree)
|
|
93334
|
+
}
|
|
93335
|
+
}
|
|
93336
|
+
|
|
93247
93337
|
;// ./lib/utils/mdxish/mdxish-get-component-name.ts
|
|
93248
93338
|
/** Convert a string to PascalCase */
|
|
93249
93339
|
function toPascalCase(str) {
|
|
@@ -93922,6 +94012,71 @@ const evaluateExpressions = ({ context = {} } = {}) => tree => {
|
|
|
93922
94012
|
};
|
|
93923
94013
|
/* harmony default export */ const evaluate_expressions = (evaluateExpressions);
|
|
93924
94014
|
|
|
94015
|
+
;// ./node_modules/rehype-parse/lib/index.js
|
|
94016
|
+
/**
|
|
94017
|
+
* @import {Root} from 'hast'
|
|
94018
|
+
* @import {Options as FromHtmlOptions} from 'hast-util-from-html'
|
|
94019
|
+
* @import {Parser, Processor} from 'unified'
|
|
94020
|
+
*/
|
|
94021
|
+
|
|
94022
|
+
/**
|
|
94023
|
+
* @typedef {Omit<FromHtmlOptions, 'onerror'> & RehypeParseFields} Options
|
|
94024
|
+
* Configuration.
|
|
94025
|
+
*
|
|
94026
|
+
* @typedef RehypeParseFields
|
|
94027
|
+
* Extra fields.
|
|
94028
|
+
* @property {boolean | null | undefined} [emitParseErrors=false]
|
|
94029
|
+
* Whether to emit parse errors while parsing (default: `false`).
|
|
94030
|
+
*
|
|
94031
|
+
* > 👉 **Note**: parse errors are currently being added to HTML.
|
|
94032
|
+
* > Not all errors emitted by parse5 (or us) are specced yet.
|
|
94033
|
+
* > Some documentation may still be missing.
|
|
94034
|
+
*/
|
|
94035
|
+
|
|
94036
|
+
|
|
94037
|
+
|
|
94038
|
+
/**
|
|
94039
|
+
* Plugin to add support for parsing from HTML.
|
|
94040
|
+
*
|
|
94041
|
+
* > 👉 **Note**: this is not an XML parser.
|
|
94042
|
+
* > It supports SVG as embedded in HTML.
|
|
94043
|
+
* > It does not support the features available in XML.
|
|
94044
|
+
* > Passing SVG files might break but fragments of modern SVG should be fine.
|
|
94045
|
+
* > Use [`xast-util-from-xml`][xast-util-from-xml] to parse XML.
|
|
94046
|
+
*
|
|
94047
|
+
* @param {Options | null | undefined} [options]
|
|
94048
|
+
* Configuration (optional).
|
|
94049
|
+
* @returns {undefined}
|
|
94050
|
+
* Nothing.
|
|
94051
|
+
*/
|
|
94052
|
+
function rehypeParse(options) {
|
|
94053
|
+
/** @type {Processor<Root>} */
|
|
94054
|
+
// @ts-expect-error: TS in JSDoc generates wrong types if `this` is typed regularly.
|
|
94055
|
+
const self = this
|
|
94056
|
+
const {emitParseErrors, ...settings} = {...self.data('settings'), ...options}
|
|
94057
|
+
|
|
94058
|
+
self.parser = parser
|
|
94059
|
+
|
|
94060
|
+
/**
|
|
94061
|
+
* @type {Parser<Root>}
|
|
94062
|
+
*/
|
|
94063
|
+
function parser(document, file) {
|
|
94064
|
+
return fromHtml(document, {
|
|
94065
|
+
...settings,
|
|
94066
|
+
onerror: emitParseErrors
|
|
94067
|
+
? function (message) {
|
|
94068
|
+
if (file.path) {
|
|
94069
|
+
message.name = file.path + ':' + message.name
|
|
94070
|
+
message.file = file.path
|
|
94071
|
+
}
|
|
94072
|
+
|
|
94073
|
+
file.messages.push(message)
|
|
94074
|
+
}
|
|
94075
|
+
: undefined
|
|
94076
|
+
})
|
|
94077
|
+
}
|
|
94078
|
+
}
|
|
94079
|
+
|
|
93925
94080
|
;// ./processor/transform/mdxish/normalize-malformed-md-syntax.ts
|
|
93926
94081
|
|
|
93927
94082
|
// Marker patterns for multi-node emphasis detection
|
|
@@ -94265,6 +94420,18 @@ const normalizeEmphasisAST = () => (tree) => {
|
|
|
94265
94420
|
};
|
|
94266
94421
|
/* harmony default export */ const normalize_malformed_md_syntax = (normalizeEmphasisAST);
|
|
94267
94422
|
|
|
94423
|
+
;// ./processor/transform/mdxish/magic-blocks/patterns.ts
|
|
94424
|
+
/** Matches HTML tags (open, close, self-closing) with optional attributes. */
|
|
94425
|
+
const HTML_TAG_RE = /<\/?([a-zA-Z][a-zA-Z0-9-]*)((?:[^>"']*(?:"[^"]*"|'[^']*'))*[^>"']*)>/g;
|
|
94426
|
+
/** Matches an HTML element from its opening tag to the matching closing tag. */
|
|
94427
|
+
const HTML_ELEMENT_BLOCK_RE = /<([a-zA-Z][a-zA-Z0-9-]*)[\s>][\s\S]*?<\/\1>/g;
|
|
94428
|
+
/** Matches a newline with surrounding horizontal whitespace. */
|
|
94429
|
+
const NEWLINE_WITH_WHITESPACE_RE = /[^\S\n]*\n[^\S\n]*/g;
|
|
94430
|
+
/** Matches a closing block-level tag followed by non-tag text or by a newline then non-blank content. */
|
|
94431
|
+
const CLOSE_BLOCK_TAG_BOUNDARY_RE = /<\/([a-zA-Z][a-zA-Z0-9-]*)>\s*(?:(?!<)(\S)|\n([^\n]))/g;
|
|
94432
|
+
/** Tests whether a string contains a complete HTML element (open + close tag). */
|
|
94433
|
+
const COMPLETE_HTML_ELEMENT_RE = /<[a-zA-Z][^>]*>[\s\S]*<\/[a-zA-Z]/;
|
|
94434
|
+
|
|
94268
94435
|
;// ./processor/transform/mdxish/magic-blocks/placeholder.ts
|
|
94269
94436
|
const EMPTY_IMAGE_PLACEHOLDER = {
|
|
94270
94437
|
type: 'image',
|
|
@@ -94318,6 +94485,14 @@ const EMPTY_CODE_PLACEHOLDER = {
|
|
|
94318
94485
|
|
|
94319
94486
|
|
|
94320
94487
|
|
|
94488
|
+
|
|
94489
|
+
|
|
94490
|
+
|
|
94491
|
+
|
|
94492
|
+
|
|
94493
|
+
|
|
94494
|
+
|
|
94495
|
+
|
|
94321
94496
|
/**
|
|
94322
94497
|
* Wraps a node in a "pinned" container if sidebar: true is set.
|
|
94323
94498
|
*/
|
|
@@ -94345,12 +94520,125 @@ const imgWidthBySize = new Proxy(imgSizeValues, {
|
|
|
94345
94520
|
});
|
|
94346
94521
|
const textToInline = (text) => [{ type: 'text', value: text }];
|
|
94347
94522
|
const textToBlock = (text) => [{ children: textToInline(text), type: 'paragraph' }];
|
|
94348
|
-
/**
|
|
94349
|
-
|
|
94523
|
+
/**
|
|
94524
|
+
* Converts leading newlines in magic block content to `<br>` tags.
|
|
94525
|
+
* Leading newlines are stripped by remark-parse before they become soft break nodes,
|
|
94526
|
+
* so remark-breaks cannot handle them. We convert them to HTML `<br>` tags instead.
|
|
94527
|
+
*/
|
|
94528
|
+
const ensureLeadingBreaks = (text) => text.replace(/^\n+/, match => '<br>'.repeat(match.length));
|
|
94529
|
+
/** Preprocesses magic block body content before parsing. */
|
|
94530
|
+
const preprocessBody = (text) => {
|
|
94531
|
+
return ensureLeadingBreaks(text);
|
|
94532
|
+
};
|
|
94533
|
+
/** Markdown parser */
|
|
94534
|
+
const contentParser = unified().use(remarkParse).use(remarkBreaks).use(remarkGfm).use(normalize_malformed_md_syntax);
|
|
94535
|
+
/** Markdown to HTML processor (mdast → hast → HTML string) */
|
|
94536
|
+
const markdownToHtml = unified()
|
|
94537
|
+
.use(remarkParse)
|
|
94538
|
+
.use(remarkGfm)
|
|
94539
|
+
.use(normalize_malformed_md_syntax)
|
|
94540
|
+
.use(remarkRehype)
|
|
94541
|
+
.use(rehypeStringify);
|
|
94542
|
+
/** HTML parser (HTML string → hast) */
|
|
94543
|
+
const htmlParser = unified().use(rehypeParse, { fragment: true });
|
|
94544
|
+
/** HTML stringifier (hast → HTML string) */
|
|
94545
|
+
const htmlStringifier = unified().use(rehypeStringify);
|
|
94546
|
+
/** Process \|, \<, \> backslash escapes. Only < is entity-escaped; > is left literal to avoid double-encoding by rehype. */
|
|
94547
|
+
const processBackslashEscapes = (text) => text.replace(/\\<([^>]*)>/g, '<$1>').replace(/\\([<>|])/g, (_, c) => (c === '<' ? '<' : c === '>' ? '>' : c));
|
|
94548
|
+
/** Block-level HTML tags that trigger CommonMark type 6 HTML blocks (condition 6). */
|
|
94549
|
+
const BLOCK_LEVEL_TAGS = new Set(htmlBlockNames);
|
|
94550
|
+
const escapeInvalidTags = (str) => str.replace(HTML_TAG_RE, (match, tag, rest) => {
|
|
94551
|
+
const tagName = tag.replace(/^\//, '');
|
|
94552
|
+
if (STANDARD_HTML_TAGS.has(tagName.toLowerCase()))
|
|
94553
|
+
return match;
|
|
94554
|
+
// Preserve PascalCase tags (custom components like <Glossary>) for the main pipeline
|
|
94555
|
+
if (/^[A-Z]/.test(tagName))
|
|
94556
|
+
return match;
|
|
94557
|
+
return `<${tag}${rest}>`;
|
|
94558
|
+
});
|
|
94559
|
+
/**
|
|
94560
|
+
* Process markdown within HTML string.
|
|
94561
|
+
* 1. Parse HTML to HAST
|
|
94562
|
+
* 2. Find text nodes, parse as markdown, convert to HAST
|
|
94563
|
+
* 3. Stringify back to HTML
|
|
94564
|
+
*
|
|
94565
|
+
* PascalCase component tags (e.g. `<Glossary>`) are temporarily replaced with
|
|
94566
|
+
* placeholders before HTML parsing so `rehype-parse` doesn't mangle them
|
|
94567
|
+
* (it treats unknown tags as void elements, stripping their children).
|
|
94568
|
+
*/
|
|
94569
|
+
const processMarkdownInHtmlString = (html) => {
|
|
94570
|
+
const placeholders = [];
|
|
94571
|
+
let counter = 0;
|
|
94572
|
+
const safened = escapeInvalidTags(html).replace(HTML_TAG_RE, match => {
|
|
94573
|
+
if (!/^<\/?[A-Z]/.test(match))
|
|
94574
|
+
return match;
|
|
94575
|
+
const id = `<!--PC${(counter += 1)}-->`;
|
|
94576
|
+
placeholders.push([id, match]);
|
|
94577
|
+
return id;
|
|
94578
|
+
});
|
|
94579
|
+
const hast = htmlParser.parse(safened);
|
|
94580
|
+
const textToHast = (text) => {
|
|
94581
|
+
if (!text.trim())
|
|
94582
|
+
return [{ type: 'text', value: text }];
|
|
94583
|
+
const parsed = markdownToHtml.runSync(markdownToHtml.parse(escapeInvalidTags(text)));
|
|
94584
|
+
const nodes = parsed.children.flatMap(n => n.type === 'element' && n.tagName === 'p' ? n.children : [n]);
|
|
94585
|
+
const leading = text.match(/^\s+/)?.[0];
|
|
94586
|
+
const trailing = text.match(/\s+$/)?.[0];
|
|
94587
|
+
if (leading)
|
|
94588
|
+
nodes.unshift({ type: 'text', value: leading });
|
|
94589
|
+
if (trailing)
|
|
94590
|
+
nodes.push({ type: 'text', value: trailing });
|
|
94591
|
+
return nodes;
|
|
94592
|
+
};
|
|
94593
|
+
const processChildren = (children) => children.flatMap(child => (child.type === 'text' ? textToHast(child.value) : [child]));
|
|
94594
|
+
hast.children = processChildren(hast.children);
|
|
94595
|
+
visit(hast, 'element', (node) => {
|
|
94596
|
+
node.children = processChildren(node.children);
|
|
94597
|
+
});
|
|
94598
|
+
return placeholders.reduce((res, [id, original]) => res.replace(id, original), htmlStringifier.stringify(hast));
|
|
94599
|
+
};
|
|
94600
|
+
/**
|
|
94601
|
+
* Separate a closing block-level tag from the content that follows it.
|
|
94602
|
+
*
|
|
94603
|
+
* Each \n in the original text becomes a <br> tag to preserve spacing, then a
|
|
94604
|
+
* blank line (\n\n) is appended so CommonMark ends the HTML block and parses
|
|
94605
|
+
* the following content as markdown.
|
|
94606
|
+
*/
|
|
94607
|
+
const separateBlockTagFromContent = (match, tag, inlineChar, nextLineChar) => {
|
|
94608
|
+
if (!BLOCK_LEVEL_TAGS.has(tag.toLowerCase()))
|
|
94609
|
+
return match;
|
|
94610
|
+
const newlineCount = (match.match(/\n/g) ?? []).length;
|
|
94611
|
+
const breaks = '<br>'.repeat(newlineCount);
|
|
94612
|
+
return `</${tag}>${breaks}\n\n${inlineChar || nextLineChar}`;
|
|
94613
|
+
};
|
|
94614
|
+
/**
|
|
94615
|
+
* CommonMark doesn't process markdown inside HTML blocks -
|
|
94616
|
+
* so `<ul><li>_text_</li></ul>` won't convert underscores to emphasis.
|
|
94617
|
+
* We parse first, then visit html nodes and process their text content.
|
|
94618
|
+
*/
|
|
94350
94619
|
const parseTableCell = (text) => {
|
|
94351
94620
|
if (!text.trim())
|
|
94352
94621
|
return [{ type: 'text', value: '' }];
|
|
94353
|
-
|
|
94622
|
+
// Convert \n (and surrounding whitespace) to <br> inside HTML blocks so
|
|
94623
|
+
// CommonMark doesn't split them on blank lines.
|
|
94624
|
+
// Then strip leading whitespace to prevent indented code blocks.
|
|
94625
|
+
const escaped = processBackslashEscapes(text);
|
|
94626
|
+
const normalized = escaped
|
|
94627
|
+
.replace(HTML_ELEMENT_BLOCK_RE, match => match.replace(NEWLINE_WITH_WHITESPACE_RE, '<br>'))
|
|
94628
|
+
.replace(CLOSE_BLOCK_TAG_BOUNDARY_RE, separateBlockTagFromContent);
|
|
94629
|
+
const trimmedLines = normalized.split('\n').map(line => line.trimStart());
|
|
94630
|
+
const processed = trimmedLines.join('\n');
|
|
94631
|
+
const tree = contentParser.runSync(contentParser.parse(processed));
|
|
94632
|
+
// Process markdown inside complete HTML elements (e.g. _emphasis_ within <li>).
|
|
94633
|
+
// Bare tags like "<i>" are left for rehypeRaw since rehype-parse would mangle them.
|
|
94634
|
+
visit(tree, 'html', (node) => {
|
|
94635
|
+
if (COMPLETE_HTML_ELEMENT_RE.test(node.value)) {
|
|
94636
|
+
node.value = processMarkdownInHtmlString(node.value);
|
|
94637
|
+
}
|
|
94638
|
+
else {
|
|
94639
|
+
node.value = escapeInvalidTags(node.value);
|
|
94640
|
+
}
|
|
94641
|
+
});
|
|
94354
94642
|
if (tree.children.length > 1) {
|
|
94355
94643
|
return tree.children;
|
|
94356
94644
|
}
|
|
@@ -94505,7 +94793,7 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
|
94505
94793
|
});
|
|
94506
94794
|
}
|
|
94507
94795
|
if (hasBody) {
|
|
94508
|
-
const bodyBlocks = parseBlock(calloutJson.body || '');
|
|
94796
|
+
const bodyBlocks = parseBlock(preprocessBody(calloutJson.body || ''));
|
|
94509
94797
|
children.push(...bodyBlocks);
|
|
94510
94798
|
}
|
|
94511
94799
|
const calloutElement = {
|
|
@@ -94540,7 +94828,7 @@ function transformMagicBlock(blockType, data, rawValue, options = {}) {
|
|
|
94540
94828
|
const tokenizeCell = compatibilityMode ? textToBlock : parseTableCell;
|
|
94541
94829
|
const tableChildren = Array.from({ length: rows + 1 }, (_, y) => ({
|
|
94542
94830
|
children: Array.from({ length: cols }, (__, x) => ({
|
|
94543
|
-
children: sparseData[y]?.[x] ? tokenizeCell(sparseData[y][x]) : [{ type: 'text', value: '' }],
|
|
94831
|
+
children: sparseData[y]?.[x] ? tokenizeCell(preprocessBody(sparseData[y][x])) : [{ type: 'text', value: '' }],
|
|
94544
94832
|
type: y === 0 ? 'tableHead' : 'tableCell',
|
|
94545
94833
|
})),
|
|
94546
94834
|
type: 'tableRow',
|
|
@@ -94641,79 +94929,63 @@ const isBlockNode = (node) => blockTypes.includes(node.type);
|
|
|
94641
94929
|
*/
|
|
94642
94930
|
const magicBlockTransformer = (options = {}) => tree => {
|
|
94643
94931
|
const replacements = [];
|
|
94644
|
-
|
|
94645
|
-
|
|
94646
|
-
|
|
94932
|
+
visitParents(tree, 'magicBlock', (node, ancestors) => {
|
|
94933
|
+
const parent = ancestors[ancestors.length - 1]; // direct parent of the current node
|
|
94934
|
+
const index = parent.children.indexOf(node);
|
|
94935
|
+
if (index === -1)
|
|
94936
|
+
return;
|
|
94647
94937
|
const children = transformMagicBlock(node.blockType, node.data, node.value, options);
|
|
94648
94938
|
if (!children.length) {
|
|
94649
|
-
//
|
|
94939
|
+
// `visitParents` doesn't support [Action, Index] returns like `visit` does;
|
|
94940
|
+
// a bare return after splicing is sufficient since `visitParents` walks by
|
|
94941
|
+
// tree structure rather than index.
|
|
94650
94942
|
parent.children.splice(index, 1);
|
|
94651
|
-
return
|
|
94943
|
+
return;
|
|
94652
94944
|
}
|
|
94653
94945
|
// If parent is a paragraph and we're inserting block nodes (which must not be in paragraphs), lift them out
|
|
94654
94946
|
if (parent.type === 'paragraph' && children.some(child => isBlockNode(child))) {
|
|
94655
94947
|
const blockNodes = [];
|
|
94656
94948
|
const inlineNodes = [];
|
|
94657
|
-
// Separate block and inline nodes
|
|
94658
94949
|
children.forEach(child => {
|
|
94659
|
-
|
|
94660
|
-
blockNodes.push(child);
|
|
94661
|
-
}
|
|
94662
|
-
else {
|
|
94663
|
-
inlineNodes.push(child);
|
|
94664
|
-
}
|
|
94950
|
+
(isBlockNode(child) ? blockNodes : inlineNodes).push(child);
|
|
94665
94951
|
});
|
|
94666
|
-
const before = parent.children.slice(0, index);
|
|
94667
|
-
const after = parent.children.slice(index + 1);
|
|
94668
94952
|
replacements.push({
|
|
94953
|
+
container: ancestors[ancestors.length - 2] || tree, // grandparent of the current node
|
|
94669
94954
|
parent,
|
|
94670
94955
|
blockNodes,
|
|
94671
94956
|
inlineNodes,
|
|
94672
|
-
before,
|
|
94673
|
-
after,
|
|
94957
|
+
before: parent.children.slice(0, index),
|
|
94958
|
+
after: parent.children.slice(index + 1),
|
|
94674
94959
|
});
|
|
94675
94960
|
}
|
|
94676
94961
|
else {
|
|
94677
|
-
// Normal case: just replace the inlineCode with the children
|
|
94678
94962
|
parent.children.splice(index, 1, ...children);
|
|
94679
94963
|
}
|
|
94680
|
-
return undefined;
|
|
94681
94964
|
});
|
|
94682
94965
|
// Second pass: apply replacements that require lifting block nodes out of paragraphs
|
|
94683
94966
|
// Process in reverse order to maintain correct indices
|
|
94684
94967
|
for (let i = replacements.length - 1; i >= 0; i -= 1) {
|
|
94685
|
-
const { after, before, blockNodes, inlineNodes, parent } = replacements[i];
|
|
94686
|
-
|
|
94687
|
-
const
|
|
94688
|
-
const paraIndex = rootChildren.findIndex(child => child === parent);
|
|
94968
|
+
const { after, before, blockNodes, container, inlineNodes, parent } = replacements[i];
|
|
94969
|
+
const containerChildren = container.children;
|
|
94970
|
+
const paraIndex = containerChildren.indexOf(parent);
|
|
94689
94971
|
if (paraIndex === -1) {
|
|
94690
|
-
|
|
94691
|
-
// This shouldn't happen normally, but handle it gracefully
|
|
94692
|
-
// Reconstruct the original index from before.length
|
|
94693
|
-
const originalIndex = before.length;
|
|
94694
|
-
parent.children.splice(originalIndex, 1, ...blockNodes, ...inlineNodes);
|
|
94972
|
+
parent.children.splice(before.length, 1, ...blockNodes, ...inlineNodes);
|
|
94695
94973
|
// eslint-disable-next-line no-continue
|
|
94696
94974
|
continue;
|
|
94697
94975
|
}
|
|
94698
|
-
// Update or remove the paragraph
|
|
94699
94976
|
if (inlineNodes.length > 0) {
|
|
94700
|
-
// Keep paragraph with inline nodes
|
|
94701
94977
|
parent.children = [...before, ...inlineNodes, ...after];
|
|
94702
|
-
// Insert block nodes after the paragraph
|
|
94703
94978
|
if (blockNodes.length > 0) {
|
|
94704
|
-
|
|
94979
|
+
containerChildren.splice(paraIndex + 1, 0, ...blockNodes);
|
|
94705
94980
|
}
|
|
94706
94981
|
}
|
|
94707
94982
|
else if (before.length === 0 && after.length === 0) {
|
|
94708
|
-
|
|
94709
|
-
rootChildren.splice(paraIndex, 1, ...blockNodes);
|
|
94983
|
+
containerChildren.splice(paraIndex, 1, ...blockNodes);
|
|
94710
94984
|
}
|
|
94711
94985
|
else {
|
|
94712
|
-
// Keep paragraph with remaining content
|
|
94713
94986
|
parent.children = [...before, ...after];
|
|
94714
|
-
// Insert block nodes after the paragraph
|
|
94715
94987
|
if (blockNodes.length > 0) {
|
|
94716
|
-
|
|
94988
|
+
containerChildren.splice(paraIndex + 1, 0, ...blockNodes);
|
|
94717
94989
|
}
|
|
94718
94990
|
}
|
|
94719
94991
|
}
|
|
@@ -95548,6 +95820,45 @@ const restoreBooleanProperties = () => tree => {
|
|
|
95548
95820
|
};
|
|
95549
95821
|
|
|
95550
95822
|
|
|
95823
|
+
;// ./processor/transform/mdxish/terminate-html-flow-blocks.ts
|
|
95824
|
+
|
|
95825
|
+
const STANDALONE_HTML_LINE_REGEX = /^(<[a-z][^<>]*>|<\/[a-z][^<>]*>)+\s*$/;
|
|
95826
|
+
const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/;
|
|
95827
|
+
/**
|
|
95828
|
+
* Preprocessor to terminate HTML flow blocks.
|
|
95829
|
+
*
|
|
95830
|
+
* In CommonMark, HTML blocks (types 6 and 7) only terminate on a blank line.
|
|
95831
|
+
* Without one, any content on the next line is consumed as part of the HTML block
|
|
95832
|
+
* and never parsed as its own construct. For example, a `[block:callout]` immediately
|
|
95833
|
+
* following `<div><p></p></div>` gets swallowed into the HTML flow token.
|
|
95834
|
+
*
|
|
95835
|
+
* @link https://spec.commonmark.org/0.29/#html-blocks
|
|
95836
|
+
*
|
|
95837
|
+
* This preprocessor inserts a blank line after standalone HTML lines when the
|
|
95838
|
+
* next line is non-blank, ensuring micromark's HTML flow tokenizer terminates
|
|
95839
|
+
* and subsequent content is parsed independently.
|
|
95840
|
+
*
|
|
95841
|
+
* Only targets non-indented lines with lowercase tag names. Uppercase tags
|
|
95842
|
+
* (e.g., `<Table>`, `<MyComponent>`) are JSX custom components and don't
|
|
95843
|
+
* trigger CommonMark HTML blocks, so they are left untouched.
|
|
95844
|
+
*
|
|
95845
|
+
* Lines inside fenced code blocks are skipped entirely.
|
|
95846
|
+
*/
|
|
95847
|
+
function terminateHtmlFlowBlocks(content) {
|
|
95848
|
+
const { protectedContent, protectedCode } = protectCodeBlocks(content);
|
|
95849
|
+
const lines = protectedContent.split('\n');
|
|
95850
|
+
const result = [];
|
|
95851
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
95852
|
+
result.push(lines[i]);
|
|
95853
|
+
if (i < lines.length - 1 &&
|
|
95854
|
+
(STANDALONE_HTML_LINE_REGEX.test(lines[i]) || HTML_LINE_WITH_CONTENT_REGEX.test(lines[i])) &&
|
|
95855
|
+
lines[i + 1].trim().length > 0) {
|
|
95856
|
+
result.push('');
|
|
95857
|
+
}
|
|
95858
|
+
}
|
|
95859
|
+
return restoreCodeBlocks(result.join('\n'), protectedCode);
|
|
95860
|
+
}
|
|
95861
|
+
|
|
95551
95862
|
;// ./processor/transform/mdxish/variables-text.ts
|
|
95552
95863
|
|
|
95553
95864
|
|
|
@@ -96850,10 +97161,29 @@ function loadComponents() {
|
|
|
96850
97161
|
|
|
96851
97162
|
|
|
96852
97163
|
|
|
97164
|
+
|
|
97165
|
+
|
|
96853
97166
|
|
|
96854
97167
|
|
|
96855
97168
|
|
|
96856
97169
|
const defaultTransformers = [callouts, code_tabs, gemoji_, transform_embeds];
|
|
97170
|
+
/**
|
|
97171
|
+
* Preprocessing pipeline: applies string-level transformations to work around
|
|
97172
|
+
* CommonMark/remark limitations and reach parity with legacy (rdmd) rendering.
|
|
97173
|
+
*
|
|
97174
|
+
* Runs a series of string-level transformations before micromark/remark parsing:
|
|
97175
|
+
* 1. Normalize malformed table separator syntax (e.g., `|: ---` → `| :---`)
|
|
97176
|
+
* 2. Terminate HTML flow blocks so subsequent content isn't swallowed
|
|
97177
|
+
* 3. Evaluate JSX expressions in attributes (unless safeMode)
|
|
97178
|
+
* 4. Replace snake_case component names with parser-safe placeholders
|
|
97179
|
+
*/
|
|
97180
|
+
function preprocessContent(content, opts) {
|
|
97181
|
+
const { safeMode, jsxContext, knownComponents } = opts;
|
|
97182
|
+
let result = normalizeTableSeparator(content);
|
|
97183
|
+
result = terminateHtmlFlowBlocks(result);
|
|
97184
|
+
result = safeMode ? result : preprocessJSXExpressions(result, jsxContext);
|
|
97185
|
+
return processSnakeCaseComponent(result, { knownComponents });
|
|
97186
|
+
}
|
|
96857
97187
|
function mdxishAstProcessor(mdContent, opts = {}) {
|
|
96858
97188
|
const { components: userComponents = {}, jsxContext = {}, newEditorTypes = false, safeMode = false, useTailwind, } = opts;
|
|
96859
97189
|
const components = {
|
|
@@ -96862,15 +97192,11 @@ function mdxishAstProcessor(mdContent, opts = {}) {
|
|
|
96862
97192
|
};
|
|
96863
97193
|
// Build set of known component names for snake_case filtering
|
|
96864
97194
|
const knownComponents = new Set(Object.keys(components));
|
|
96865
|
-
|
|
96866
|
-
|
|
96867
|
-
|
|
96868
|
-
|
|
96869
|
-
|
|
96870
|
-
? contentAfterTableNormalization
|
|
96871
|
-
: preprocessJSXExpressions(contentAfterTableNormalization, jsxContext);
|
|
96872
|
-
// Step 3: Replace snake_case component names with parser-safe placeholders
|
|
96873
|
-
const { content: parserReadyContent, mapping: snakeCaseMapping } = processSnakeCaseComponent(contentAfterJSXEvaluation, { knownComponents });
|
|
97195
|
+
const { content: parserReadyContent, mapping: snakeCaseMapping } = preprocessContent(mdContent, {
|
|
97196
|
+
safeMode,
|
|
97197
|
+
jsxContext,
|
|
97198
|
+
knownComponents,
|
|
97199
|
+
});
|
|
96874
97200
|
// Create string map for tailwind transformer
|
|
96875
97201
|
const tempComponentsMap = Object.entries(components).reduce((acc, [key, value]) => {
|
|
96876
97202
|
acc[key] = String(value);
|
|
@@ -96938,6 +97264,7 @@ function mdxish(mdContent, opts = {}) {
|
|
|
96938
97264
|
};
|
|
96939
97265
|
const { processor, parserReadyContent } = mdxishAstProcessor(mdContent, opts);
|
|
96940
97266
|
processor
|
|
97267
|
+
.use(remarkBreaks)
|
|
96941
97268
|
.use(remarkRehype, { allowDangerousHtml: true, handlers: mdxComponentHandlers })
|
|
96942
97269
|
.use(preserveBooleanProperties) // RehypeRaw converts boolean properties to empty strings
|
|
96943
97270
|
.use(rehypeRaw, { passThrough: ['html-block'] })
|
|
@@ -97434,8 +97761,59 @@ const mdxishTags_tags = (doc) => {
|
|
|
97434
97761
|
};
|
|
97435
97762
|
/* harmony default export */ const mdxishTags = (mdxishTags_tags);
|
|
97436
97763
|
|
|
97437
|
-
;// ./lib/
|
|
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
|
+
}
|
|
97438
97815
|
|
|
97816
|
+
;// ./lib/stripComments.ts
|
|
97439
97817
|
|
|
97440
97818
|
|
|
97441
97819
|
|
|
@@ -97450,10 +97828,8 @@ const mdxishTags_tags = (doc) => {
|
|
|
97450
97828
|
* Removes Markdown and MDX comments.
|
|
97451
97829
|
*/
|
|
97452
97830
|
async function stripComments(doc, { mdx, mdxish } = {}) {
|
|
97453
|
-
const
|
|
97454
|
-
|
|
97455
|
-
.data('fromMarkdownExtensions', [magicBlockFromMarkdown()])
|
|
97456
|
-
.data('toMarkdownExtensions', [magicBlockToMarkdown()]);
|
|
97831
|
+
const { replaced, blocks } = extractMagicBlocks(doc);
|
|
97832
|
+
const processor = unified();
|
|
97457
97833
|
// we still require these two extensions because:
|
|
97458
97834
|
// 1. we can rely on remarkMdx to parse MDXish
|
|
97459
97835
|
// 2. we need to parse JSX comments into mdxTextExpression nodes so that the transformers can pick them up
|
|
@@ -97495,8 +97871,10 @@ async function stripComments(doc, { mdx, mdxish } = {}) {
|
|
|
97495
97871
|
},
|
|
97496
97872
|
],
|
|
97497
97873
|
});
|
|
97498
|
-
const file = await processor.process(
|
|
97499
|
-
|
|
97874
|
+
const file = await processor.process(replaced);
|
|
97875
|
+
const stringified = String(file).trim();
|
|
97876
|
+
const restored = restoreMagicBlocks(stringified, blocks);
|
|
97877
|
+
return restored;
|
|
97500
97878
|
}
|
|
97501
97879
|
/* harmony default export */ const lib_stripComments = (stripComments);
|
|
97502
97880
|
|