@readme/markdown 12.1.0 → 12.2.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/dist/lib/stripComments.d.ts +2 -1
- package/dist/main.js +149 -13
- package/dist/main.node.js +149 -13
- package/dist/main.node.js.map +1 -1
- package/dist/processor/plugin/toc.d.ts +3 -3
- package/package.json +1 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
interface Opts {
|
|
2
2
|
mdx?: boolean;
|
|
3
|
+
mdxish?: boolean;
|
|
3
4
|
}
|
|
4
5
|
/**
|
|
5
6
|
* Removes Markdown and MDX comments.
|
|
6
7
|
*/
|
|
7
|
-
declare function stripComments(doc: string, { mdx }?: Opts): Promise<string>;
|
|
8
|
+
declare function stripComments(doc: string, { mdx, mdxish }?: Opts): Promise<string>;
|
|
8
9
|
export default stripComments;
|
package/dist/main.js
CHANGED
|
@@ -52923,6 +52923,37 @@ const stripCommentsTransformer = () => {
|
|
|
52923
52923
|
|
|
52924
52924
|
|
|
52925
52925
|
const STRIP_TAGS = ['script', 'style'];
|
|
52926
|
+
/**
|
|
52927
|
+
* Extract variable key from MDX expression AST (e.g., {user.name} → 'name')
|
|
52928
|
+
* Uses ESTree AST inspection, matching the approach in processor/transform/variables.ts
|
|
52929
|
+
*
|
|
52930
|
+
* @see https://github.com/syntax-tree/mdast-util-mdx-expression - MdxTextExpressionHast type
|
|
52931
|
+
* @see https://github.com/estree/estree/blob/master/es5.md - ESTree spec for expression types
|
|
52932
|
+
*/
|
|
52933
|
+
function extractMdxVariableKey(node) {
|
|
52934
|
+
const estree = node.data?.estree;
|
|
52935
|
+
if (!estree || estree.type !== 'Program' || estree.body.length === 0)
|
|
52936
|
+
return undefined;
|
|
52937
|
+
const statement = estree.body[0];
|
|
52938
|
+
if (statement.type !== 'ExpressionStatement')
|
|
52939
|
+
return undefined;
|
|
52940
|
+
const expr = statement.expression;
|
|
52941
|
+
if (expr.type !== 'MemberExpression')
|
|
52942
|
+
return undefined;
|
|
52943
|
+
const memberExpr = expr;
|
|
52944
|
+
const obj = memberExpr.object;
|
|
52945
|
+
if (obj.type !== 'Identifier' || obj.name !== 'user')
|
|
52946
|
+
return undefined;
|
|
52947
|
+
const prop = memberExpr.property;
|
|
52948
|
+
if (prop.type === 'Identifier') {
|
|
52949
|
+
return prop.name;
|
|
52950
|
+
}
|
|
52951
|
+
if (prop.type === 'Literal') {
|
|
52952
|
+
const val = prop.value;
|
|
52953
|
+
return typeof val === 'string' ? val : undefined;
|
|
52954
|
+
}
|
|
52955
|
+
return undefined;
|
|
52956
|
+
}
|
|
52926
52957
|
function plain_one(node, opts) {
|
|
52927
52958
|
if (node.type === 'comment')
|
|
52928
52959
|
return '';
|
|
@@ -52946,6 +52977,8 @@ function plain_one(node, opts) {
|
|
|
52946
52977
|
const body = children ? plain_all({ type: 'root', children }, opts) : '';
|
|
52947
52978
|
return [icon, ' ', title, title && body && ': ', body].filter(Boolean).join('');
|
|
52948
52979
|
}
|
|
52980
|
+
// 'variable' (lowercase) comes from mdxish() after rehypeRaw normalizes HTML tag names
|
|
52981
|
+
case 'variable':
|
|
52949
52982
|
case 'Variable': {
|
|
52950
52983
|
const key = node.properties.name.toString();
|
|
52951
52984
|
const val = 'variables' in opts && opts.variables[key];
|
|
@@ -52967,6 +53000,13 @@ function plain_one(node, opts) {
|
|
|
52967
53000
|
break;
|
|
52968
53001
|
}
|
|
52969
53002
|
}
|
|
53003
|
+
// Handle MDX expressions like {user.name}
|
|
53004
|
+
if (node.type === 'mdxTextExpression') {
|
|
53005
|
+
const key = extractMdxVariableKey(node);
|
|
53006
|
+
if (key) {
|
|
53007
|
+
return ('variables' in opts && opts.variables[key]) || key;
|
|
53008
|
+
}
|
|
53009
|
+
}
|
|
52970
53010
|
if ('value' in node) {
|
|
52971
53011
|
return node.value;
|
|
52972
53012
|
}
|
|
@@ -86628,11 +86668,27 @@ const getDepth = (el) => {
|
|
|
86628
86668
|
return Infinity;
|
|
86629
86669
|
return parseInt(el.tagName?.match(/^h(\d)/)[1], 10);
|
|
86630
86670
|
};
|
|
86671
|
+
/**
|
|
86672
|
+
* Flatten Variables into a simple key-value map for static resolution.
|
|
86673
|
+
* Merges user values with defaults (user values take precedence).
|
|
86674
|
+
*/
|
|
86675
|
+
const flattenVariables = (variables) => {
|
|
86676
|
+
if (!variables)
|
|
86677
|
+
return {};
|
|
86678
|
+
return {
|
|
86679
|
+
...variables.user,
|
|
86680
|
+
...Object.fromEntries((variables.defaults || []).filter(d => !(d.name in variables.user)).map(d => [d.name, d.default])),
|
|
86681
|
+
};
|
|
86682
|
+
};
|
|
86631
86683
|
/*
|
|
86632
86684
|
* `tocToHast` consumes the list generated by `rehypeToc` and produces a hast
|
|
86633
86685
|
* of nested lists to be rendered as a table of contents.
|
|
86686
|
+
*
|
|
86687
|
+
* @param headings - The list of heading elements to render
|
|
86688
|
+
* @param variables - Optional user variables for resolving Variable nodes in headings
|
|
86634
86689
|
*/
|
|
86635
|
-
const tocToHast = (headings = []) => {
|
|
86690
|
+
const tocToHast = (headings = [], variables) => {
|
|
86691
|
+
const flatVars = flattenVariables(variables);
|
|
86636
86692
|
const headingDepths = headings.map(getDepth);
|
|
86637
86693
|
const min = Math.min(...headingDepths);
|
|
86638
86694
|
const ast = hastscript_lib_h('ul');
|
|
@@ -86650,7 +86706,7 @@ const tocToHast = (headings = []) => {
|
|
|
86650
86706
|
stack.pop();
|
|
86651
86707
|
}
|
|
86652
86708
|
if (heading.properties) {
|
|
86653
|
-
const content = lib_plain({ type: 'root', children: heading.children });
|
|
86709
|
+
const content = lib_plain({ type: 'root', children: heading.children }, { variables: flatVars });
|
|
86654
86710
|
stack[stack.length - 1].children.push(hastscript_lib_h('li', null, hastscript_lib_h('a', { href: `#${heading.properties.id}` }, content)));
|
|
86655
86711
|
}
|
|
86656
86712
|
});
|
|
@@ -86660,8 +86716,12 @@ const tocToHast = (headings = []) => {
|
|
|
86660
86716
|
* `tocHastToMdx` is a utility for combining `TocList`s of a root document and
|
|
86661
86717
|
* any child components it may have. Once combined it will generate a markdown
|
|
86662
86718
|
* doc representing a table of contents.
|
|
86719
|
+
*
|
|
86720
|
+
* @param toc - The list of TOC elements
|
|
86721
|
+
* @param components - Custom components that may contain headings
|
|
86722
|
+
* @param variables - Optional user variables for resolving Variable nodes in headings
|
|
86663
86723
|
*/
|
|
86664
|
-
const tocHastToMdx = (toc, components) => {
|
|
86724
|
+
const tocHastToMdx = (toc, components, variables) => {
|
|
86665
86725
|
if (typeof toc === 'undefined')
|
|
86666
86726
|
return '';
|
|
86667
86727
|
const injected = toc.flatMap(node => {
|
|
@@ -86670,7 +86730,7 @@ const tocHastToMdx = (toc, components) => {
|
|
|
86670
86730
|
}
|
|
86671
86731
|
return node;
|
|
86672
86732
|
});
|
|
86673
|
-
const tocHast = tocToHast(injected);
|
|
86733
|
+
const tocHast = tocToHast(injected, variables);
|
|
86674
86734
|
return lib_mdx({ type: 'root', children: [tocHast] }, { hast: true });
|
|
86675
86735
|
};
|
|
86676
86736
|
|
|
@@ -93529,6 +93589,61 @@ function restoreCodeBlocks(content, protectedCode) {
|
|
|
93529
93589
|
return protectedCode.codeBlocks[parseInt(idx, 10)];
|
|
93530
93590
|
});
|
|
93531
93591
|
}
|
|
93592
|
+
/**
|
|
93593
|
+
* Escapes unbalanced braces in content to prevent MDX expression parsing errors.
|
|
93594
|
+
* Handles: already-escaped braces, string literals inside expressions, nested balanced braces.
|
|
93595
|
+
*/
|
|
93596
|
+
function escapeUnbalancedBraces(content) {
|
|
93597
|
+
const opens = [];
|
|
93598
|
+
const unbalanced = new Set();
|
|
93599
|
+
let strDelim = null;
|
|
93600
|
+
let strEscaped = false;
|
|
93601
|
+
for (let i = 0; i < content.length; i += 1) {
|
|
93602
|
+
const ch = content[i];
|
|
93603
|
+
// Track strings inside expressions to ignore braces within them
|
|
93604
|
+
if (opens.length > 0) {
|
|
93605
|
+
if (strDelim) {
|
|
93606
|
+
if (strEscaped)
|
|
93607
|
+
strEscaped = false;
|
|
93608
|
+
else if (ch === '\\')
|
|
93609
|
+
strEscaped = true;
|
|
93610
|
+
else if (ch === strDelim)
|
|
93611
|
+
strDelim = null;
|
|
93612
|
+
// eslint-disable-next-line no-continue
|
|
93613
|
+
continue;
|
|
93614
|
+
}
|
|
93615
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
93616
|
+
strDelim = ch;
|
|
93617
|
+
// eslint-disable-next-line no-continue
|
|
93618
|
+
continue;
|
|
93619
|
+
}
|
|
93620
|
+
}
|
|
93621
|
+
// Skip already-escaped braces (count preceding backslashes)
|
|
93622
|
+
if (ch === '{' || ch === '}') {
|
|
93623
|
+
let bs = 0;
|
|
93624
|
+
for (let j = i - 1; j >= 0 && content[j] === '\\'; j -= 1)
|
|
93625
|
+
bs += 1;
|
|
93626
|
+
if (bs % 2 === 1) {
|
|
93627
|
+
// eslint-disable-next-line no-continue
|
|
93628
|
+
continue;
|
|
93629
|
+
}
|
|
93630
|
+
}
|
|
93631
|
+
if (ch === '{')
|
|
93632
|
+
opens.push(i);
|
|
93633
|
+
else if (ch === '}') {
|
|
93634
|
+
if (opens.length > 0)
|
|
93635
|
+
opens.pop();
|
|
93636
|
+
else
|
|
93637
|
+
unbalanced.add(i);
|
|
93638
|
+
}
|
|
93639
|
+
}
|
|
93640
|
+
opens.forEach(pos => unbalanced.add(pos));
|
|
93641
|
+
if (unbalanced.size === 0)
|
|
93642
|
+
return content;
|
|
93643
|
+
return Array.from(content)
|
|
93644
|
+
.map((ch, i) => (unbalanced.has(i) ? `\\${ch}` : ch))
|
|
93645
|
+
.join('');
|
|
93646
|
+
}
|
|
93532
93647
|
/**
|
|
93533
93648
|
* Converts JSX attribute expressions (attribute={expression}) to HTML attributes (attribute="value").
|
|
93534
93649
|
* Handles style objects (camelCase → kebab-case), className → class, and JSON stringifies objects.
|
|
@@ -93645,7 +93760,9 @@ function preprocessJSXExpressions(content, context = {}) {
|
|
|
93645
93760
|
// For inline expressions, we use a library to parse the expression & evaluate it later
|
|
93646
93761
|
// For attribute expressions, it was difficult to use a library to parse them, so do it manually
|
|
93647
93762
|
processed = evaluateAttributeExpressions(processed, context, protectedCode);
|
|
93648
|
-
// Step 4:
|
|
93763
|
+
// Step 4: Escape unbalanced braces to prevent MDX expression parsing errors
|
|
93764
|
+
processed = escapeUnbalancedBraces(processed);
|
|
93765
|
+
// Step 5: Restore protected code blocks
|
|
93649
93766
|
processed = restoreProtectedCodes(processed, protectedCode);
|
|
93650
93767
|
return processed;
|
|
93651
93768
|
}
|
|
@@ -95440,7 +95557,7 @@ function buildRMDXModule(content, headings, tocHast, opts) {
|
|
|
95440
95557
|
* @see {@link https://github.com/readmeio/rmdx/blob/main/docs/mdxish-flow.md}
|
|
95441
95558
|
*/
|
|
95442
95559
|
const renderMdxish = (tree, opts = {}) => {
|
|
95443
|
-
const { components: userComponents = {}, ...contextOpts } = opts;
|
|
95560
|
+
const { components: userComponents = {}, variables, ...contextOpts } = opts;
|
|
95444
95561
|
const components = {
|
|
95445
95562
|
...loadComponents(),
|
|
95446
95563
|
...userComponents,
|
|
@@ -95449,8 +95566,8 @@ const renderMdxish = (tree, opts = {}) => {
|
|
|
95449
95566
|
const componentsForRehype = exportComponentsForRehype(components);
|
|
95450
95567
|
const processor = createRehypeReactProcessor(componentsForRehype);
|
|
95451
95568
|
const content = processor.stringify(tree);
|
|
95452
|
-
const tocHast = headings.length > 0 ? tocToHast(headings) : null;
|
|
95453
|
-
return buildRMDXModule(content, headings, tocHast, contextOpts);
|
|
95569
|
+
const tocHast = headings.length > 0 ? tocToHast(headings, variables) : null;
|
|
95570
|
+
return buildRMDXModule(content, headings, tocHast, { ...contextOpts, variables });
|
|
95454
95571
|
};
|
|
95455
95572
|
/* harmony default export */ const lib_renderMdxish = (renderMdxish);
|
|
95456
95573
|
|
|
@@ -95560,7 +95677,7 @@ const run_run = (string, _opts = {}) => {
|
|
|
95560
95677
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
95561
95678
|
const { Toc: _Toc, toc, default: Content, stylesheet, ...exports } = exec(string);
|
|
95562
95679
|
let Toc;
|
|
95563
|
-
const tocMdx = tocHastToMdx(toc, tocsByTag);
|
|
95680
|
+
const tocMdx = tocHastToMdx(toc, tocsByTag, variables);
|
|
95564
95681
|
if (tocMdx) {
|
|
95565
95682
|
const compiledToc = lib_compile(tocMdx);
|
|
95566
95683
|
const tocModule = exec(compiledToc, { useMDXComponents: () => ({ p: Fragment }) });
|
|
@@ -95621,13 +95738,26 @@ const mdxishTags_tags = (doc) => {
|
|
|
95621
95738
|
|
|
95622
95739
|
|
|
95623
95740
|
|
|
95741
|
+
|
|
95742
|
+
|
|
95743
|
+
|
|
95624
95744
|
/**
|
|
95625
95745
|
* Removes Markdown and MDX comments.
|
|
95626
95746
|
*/
|
|
95627
|
-
async function stripComments(doc, { mdx } = {}) {
|
|
95747
|
+
async function stripComments(doc, { mdx, mdxish } = {}) {
|
|
95628
95748
|
const { replaced, blocks } = extractMagicBlocks(doc);
|
|
95629
|
-
const processor = unified()
|
|
95749
|
+
const processor = unified();
|
|
95750
|
+
// we still require these two extensions because:
|
|
95751
|
+
// 1. we can rely on remarkMdx to parse MDXish
|
|
95752
|
+
// 2. we need to parse JSX comments into mdxTextExpression nodes so that the transformers can pick them up
|
|
95753
|
+
if (mdxish) {
|
|
95754
|
+
processor
|
|
95755
|
+
.data('micromarkExtensions', [mdxExpression({ allowEmpty: true })])
|
|
95756
|
+
.data('fromMarkdownExtensions', [mdxExpressionFromMarkdown()]);
|
|
95757
|
+
}
|
|
95758
|
+
processor
|
|
95630
95759
|
.use(remarkParse)
|
|
95760
|
+
.use(normalize_malformed_md_syntax)
|
|
95631
95761
|
.use(mdx ? remarkMdx : undefined)
|
|
95632
95762
|
.use(stripCommentsTransformer)
|
|
95633
95763
|
.use(remarkStringify, mdx
|
|
@@ -95647,8 +95777,14 @@ async function stripComments(doc, { mdx } = {}) {
|
|
|
95647
95777
|
// Preserve tight sibling code blocks without adding extra newlines between them.
|
|
95648
95778
|
// Our markdown renderer uses this to group these code blocks into a tabbed interface.
|
|
95649
95779
|
(left, right) => {
|
|
95650
|
-
|
|
95651
|
-
|
|
95780
|
+
if (left.type === 'code' && right.type === 'code') {
|
|
95781
|
+
const isTight = left.position &&
|
|
95782
|
+
right.position &&
|
|
95783
|
+
right.position.start.line - left.position.end.line === 1; // Are the blocks on adjacent lines?
|
|
95784
|
+
// 0 = no newline between blocks
|
|
95785
|
+
return isTight ? 0 : undefined;
|
|
95786
|
+
}
|
|
95787
|
+
return undefined;
|
|
95652
95788
|
},
|
|
95653
95789
|
],
|
|
95654
95790
|
});
|
package/dist/main.node.js
CHANGED
|
@@ -73127,6 +73127,37 @@ const stripCommentsTransformer = () => {
|
|
|
73127
73127
|
|
|
73128
73128
|
|
|
73129
73129
|
const STRIP_TAGS = ['script', 'style'];
|
|
73130
|
+
/**
|
|
73131
|
+
* Extract variable key from MDX expression AST (e.g., {user.name} → 'name')
|
|
73132
|
+
* Uses ESTree AST inspection, matching the approach in processor/transform/variables.ts
|
|
73133
|
+
*
|
|
73134
|
+
* @see https://github.com/syntax-tree/mdast-util-mdx-expression - MdxTextExpressionHast type
|
|
73135
|
+
* @see https://github.com/estree/estree/blob/master/es5.md - ESTree spec for expression types
|
|
73136
|
+
*/
|
|
73137
|
+
function extractMdxVariableKey(node) {
|
|
73138
|
+
const estree = node.data?.estree;
|
|
73139
|
+
if (!estree || estree.type !== 'Program' || estree.body.length === 0)
|
|
73140
|
+
return undefined;
|
|
73141
|
+
const statement = estree.body[0];
|
|
73142
|
+
if (statement.type !== 'ExpressionStatement')
|
|
73143
|
+
return undefined;
|
|
73144
|
+
const expr = statement.expression;
|
|
73145
|
+
if (expr.type !== 'MemberExpression')
|
|
73146
|
+
return undefined;
|
|
73147
|
+
const memberExpr = expr;
|
|
73148
|
+
const obj = memberExpr.object;
|
|
73149
|
+
if (obj.type !== 'Identifier' || obj.name !== 'user')
|
|
73150
|
+
return undefined;
|
|
73151
|
+
const prop = memberExpr.property;
|
|
73152
|
+
if (prop.type === 'Identifier') {
|
|
73153
|
+
return prop.name;
|
|
73154
|
+
}
|
|
73155
|
+
if (prop.type === 'Literal') {
|
|
73156
|
+
const val = prop.value;
|
|
73157
|
+
return typeof val === 'string' ? val : undefined;
|
|
73158
|
+
}
|
|
73159
|
+
return undefined;
|
|
73160
|
+
}
|
|
73130
73161
|
function plain_one(node, opts) {
|
|
73131
73162
|
if (node.type === 'comment')
|
|
73132
73163
|
return '';
|
|
@@ -73150,6 +73181,8 @@ function plain_one(node, opts) {
|
|
|
73150
73181
|
const body = children ? plain_all({ type: 'root', children }, opts) : '';
|
|
73151
73182
|
return [icon, ' ', title, title && body && ': ', body].filter(Boolean).join('');
|
|
73152
73183
|
}
|
|
73184
|
+
// 'variable' (lowercase) comes from mdxish() after rehypeRaw normalizes HTML tag names
|
|
73185
|
+
case 'variable':
|
|
73153
73186
|
case 'Variable': {
|
|
73154
73187
|
const key = node.properties.name.toString();
|
|
73155
73188
|
const val = 'variables' in opts && opts.variables[key];
|
|
@@ -73171,6 +73204,13 @@ function plain_one(node, opts) {
|
|
|
73171
73204
|
break;
|
|
73172
73205
|
}
|
|
73173
73206
|
}
|
|
73207
|
+
// Handle MDX expressions like {user.name}
|
|
73208
|
+
if (node.type === 'mdxTextExpression') {
|
|
73209
|
+
const key = extractMdxVariableKey(node);
|
|
73210
|
+
if (key) {
|
|
73211
|
+
return ('variables' in opts && opts.variables[key]) || key;
|
|
73212
|
+
}
|
|
73213
|
+
}
|
|
73174
73214
|
if ('value' in node) {
|
|
73175
73215
|
return node.value;
|
|
73176
73216
|
}
|
|
@@ -106832,11 +106872,27 @@ const getDepth = (el) => {
|
|
|
106832
106872
|
return Infinity;
|
|
106833
106873
|
return parseInt(el.tagName?.match(/^h(\d)/)[1], 10);
|
|
106834
106874
|
};
|
|
106875
|
+
/**
|
|
106876
|
+
* Flatten Variables into a simple key-value map for static resolution.
|
|
106877
|
+
* Merges user values with defaults (user values take precedence).
|
|
106878
|
+
*/
|
|
106879
|
+
const flattenVariables = (variables) => {
|
|
106880
|
+
if (!variables)
|
|
106881
|
+
return {};
|
|
106882
|
+
return {
|
|
106883
|
+
...variables.user,
|
|
106884
|
+
...Object.fromEntries((variables.defaults || []).filter(d => !(d.name in variables.user)).map(d => [d.name, d.default])),
|
|
106885
|
+
};
|
|
106886
|
+
};
|
|
106835
106887
|
/*
|
|
106836
106888
|
* `tocToHast` consumes the list generated by `rehypeToc` and produces a hast
|
|
106837
106889
|
* of nested lists to be rendered as a table of contents.
|
|
106890
|
+
*
|
|
106891
|
+
* @param headings - The list of heading elements to render
|
|
106892
|
+
* @param variables - Optional user variables for resolving Variable nodes in headings
|
|
106838
106893
|
*/
|
|
106839
|
-
const tocToHast = (headings = []) => {
|
|
106894
|
+
const tocToHast = (headings = [], variables) => {
|
|
106895
|
+
const flatVars = flattenVariables(variables);
|
|
106840
106896
|
const headingDepths = headings.map(getDepth);
|
|
106841
106897
|
const min = Math.min(...headingDepths);
|
|
106842
106898
|
const ast = hastscript_lib_h('ul');
|
|
@@ -106854,7 +106910,7 @@ const tocToHast = (headings = []) => {
|
|
|
106854
106910
|
stack.pop();
|
|
106855
106911
|
}
|
|
106856
106912
|
if (heading.properties) {
|
|
106857
|
-
const content = lib_plain({ type: 'root', children: heading.children });
|
|
106913
|
+
const content = lib_plain({ type: 'root', children: heading.children }, { variables: flatVars });
|
|
106858
106914
|
stack[stack.length - 1].children.push(hastscript_lib_h('li', null, hastscript_lib_h('a', { href: `#${heading.properties.id}` }, content)));
|
|
106859
106915
|
}
|
|
106860
106916
|
});
|
|
@@ -106864,8 +106920,12 @@ const tocToHast = (headings = []) => {
|
|
|
106864
106920
|
* `tocHastToMdx` is a utility for combining `TocList`s of a root document and
|
|
106865
106921
|
* any child components it may have. Once combined it will generate a markdown
|
|
106866
106922
|
* doc representing a table of contents.
|
|
106923
|
+
*
|
|
106924
|
+
* @param toc - The list of TOC elements
|
|
106925
|
+
* @param components - Custom components that may contain headings
|
|
106926
|
+
* @param variables - Optional user variables for resolving Variable nodes in headings
|
|
106867
106927
|
*/
|
|
106868
|
-
const tocHastToMdx = (toc, components) => {
|
|
106928
|
+
const tocHastToMdx = (toc, components, variables) => {
|
|
106869
106929
|
if (typeof toc === 'undefined')
|
|
106870
106930
|
return '';
|
|
106871
106931
|
const injected = toc.flatMap(node => {
|
|
@@ -106874,7 +106934,7 @@ const tocHastToMdx = (toc, components) => {
|
|
|
106874
106934
|
}
|
|
106875
106935
|
return node;
|
|
106876
106936
|
});
|
|
106877
|
-
const tocHast = tocToHast(injected);
|
|
106937
|
+
const tocHast = tocToHast(injected, variables);
|
|
106878
106938
|
return lib_mdx({ type: 'root', children: [tocHast] }, { hast: true });
|
|
106879
106939
|
};
|
|
106880
106940
|
|
|
@@ -113733,6 +113793,61 @@ function restoreCodeBlocks(content, protectedCode) {
|
|
|
113733
113793
|
return protectedCode.codeBlocks[parseInt(idx, 10)];
|
|
113734
113794
|
});
|
|
113735
113795
|
}
|
|
113796
|
+
/**
|
|
113797
|
+
* Escapes unbalanced braces in content to prevent MDX expression parsing errors.
|
|
113798
|
+
* Handles: already-escaped braces, string literals inside expressions, nested balanced braces.
|
|
113799
|
+
*/
|
|
113800
|
+
function escapeUnbalancedBraces(content) {
|
|
113801
|
+
const opens = [];
|
|
113802
|
+
const unbalanced = new Set();
|
|
113803
|
+
let strDelim = null;
|
|
113804
|
+
let strEscaped = false;
|
|
113805
|
+
for (let i = 0; i < content.length; i += 1) {
|
|
113806
|
+
const ch = content[i];
|
|
113807
|
+
// Track strings inside expressions to ignore braces within them
|
|
113808
|
+
if (opens.length > 0) {
|
|
113809
|
+
if (strDelim) {
|
|
113810
|
+
if (strEscaped)
|
|
113811
|
+
strEscaped = false;
|
|
113812
|
+
else if (ch === '\\')
|
|
113813
|
+
strEscaped = true;
|
|
113814
|
+
else if (ch === strDelim)
|
|
113815
|
+
strDelim = null;
|
|
113816
|
+
// eslint-disable-next-line no-continue
|
|
113817
|
+
continue;
|
|
113818
|
+
}
|
|
113819
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
113820
|
+
strDelim = ch;
|
|
113821
|
+
// eslint-disable-next-line no-continue
|
|
113822
|
+
continue;
|
|
113823
|
+
}
|
|
113824
|
+
}
|
|
113825
|
+
// Skip already-escaped braces (count preceding backslashes)
|
|
113826
|
+
if (ch === '{' || ch === '}') {
|
|
113827
|
+
let bs = 0;
|
|
113828
|
+
for (let j = i - 1; j >= 0 && content[j] === '\\'; j -= 1)
|
|
113829
|
+
bs += 1;
|
|
113830
|
+
if (bs % 2 === 1) {
|
|
113831
|
+
// eslint-disable-next-line no-continue
|
|
113832
|
+
continue;
|
|
113833
|
+
}
|
|
113834
|
+
}
|
|
113835
|
+
if (ch === '{')
|
|
113836
|
+
opens.push(i);
|
|
113837
|
+
else if (ch === '}') {
|
|
113838
|
+
if (opens.length > 0)
|
|
113839
|
+
opens.pop();
|
|
113840
|
+
else
|
|
113841
|
+
unbalanced.add(i);
|
|
113842
|
+
}
|
|
113843
|
+
}
|
|
113844
|
+
opens.forEach(pos => unbalanced.add(pos));
|
|
113845
|
+
if (unbalanced.size === 0)
|
|
113846
|
+
return content;
|
|
113847
|
+
return Array.from(content)
|
|
113848
|
+
.map((ch, i) => (unbalanced.has(i) ? `\\${ch}` : ch))
|
|
113849
|
+
.join('');
|
|
113850
|
+
}
|
|
113736
113851
|
/**
|
|
113737
113852
|
* Converts JSX attribute expressions (attribute={expression}) to HTML attributes (attribute="value").
|
|
113738
113853
|
* Handles style objects (camelCase → kebab-case), className → class, and JSON stringifies objects.
|
|
@@ -113849,7 +113964,9 @@ function preprocessJSXExpressions(content, context = {}) {
|
|
|
113849
113964
|
// For inline expressions, we use a library to parse the expression & evaluate it later
|
|
113850
113965
|
// For attribute expressions, it was difficult to use a library to parse them, so do it manually
|
|
113851
113966
|
processed = evaluateAttributeExpressions(processed, context, protectedCode);
|
|
113852
|
-
// Step 4:
|
|
113967
|
+
// Step 4: Escape unbalanced braces to prevent MDX expression parsing errors
|
|
113968
|
+
processed = escapeUnbalancedBraces(processed);
|
|
113969
|
+
// Step 5: Restore protected code blocks
|
|
113853
113970
|
processed = restoreProtectedCodes(processed, protectedCode);
|
|
113854
113971
|
return processed;
|
|
113855
113972
|
}
|
|
@@ -115644,7 +115761,7 @@ function buildRMDXModule(content, headings, tocHast, opts) {
|
|
|
115644
115761
|
* @see {@link https://github.com/readmeio/rmdx/blob/main/docs/mdxish-flow.md}
|
|
115645
115762
|
*/
|
|
115646
115763
|
const renderMdxish = (tree, opts = {}) => {
|
|
115647
|
-
const { components: userComponents = {}, ...contextOpts } = opts;
|
|
115764
|
+
const { components: userComponents = {}, variables, ...contextOpts } = opts;
|
|
115648
115765
|
const components = {
|
|
115649
115766
|
...loadComponents(),
|
|
115650
115767
|
...userComponents,
|
|
@@ -115653,8 +115770,8 @@ const renderMdxish = (tree, opts = {}) => {
|
|
|
115653
115770
|
const componentsForRehype = exportComponentsForRehype(components);
|
|
115654
115771
|
const processor = createRehypeReactProcessor(componentsForRehype);
|
|
115655
115772
|
const content = processor.stringify(tree);
|
|
115656
|
-
const tocHast = headings.length > 0 ? tocToHast(headings) : null;
|
|
115657
|
-
return buildRMDXModule(content, headings, tocHast, contextOpts);
|
|
115773
|
+
const tocHast = headings.length > 0 ? tocToHast(headings, variables) : null;
|
|
115774
|
+
return buildRMDXModule(content, headings, tocHast, { ...contextOpts, variables });
|
|
115658
115775
|
};
|
|
115659
115776
|
/* harmony default export */ const lib_renderMdxish = (renderMdxish);
|
|
115660
115777
|
|
|
@@ -115764,7 +115881,7 @@ const run_run = (string, _opts = {}) => {
|
|
|
115764
115881
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
115765
115882
|
const { Toc: _Toc, toc, default: Content, stylesheet, ...exports } = exec(string);
|
|
115766
115883
|
let Toc;
|
|
115767
|
-
const tocMdx = tocHastToMdx(toc, tocsByTag);
|
|
115884
|
+
const tocMdx = tocHastToMdx(toc, tocsByTag, variables);
|
|
115768
115885
|
if (tocMdx) {
|
|
115769
115886
|
const compiledToc = lib_compile(tocMdx);
|
|
115770
115887
|
const tocModule = exec(compiledToc, { useMDXComponents: () => ({ p: Fragment }) });
|
|
@@ -115825,13 +115942,26 @@ const mdxishTags_tags = (doc) => {
|
|
|
115825
115942
|
|
|
115826
115943
|
|
|
115827
115944
|
|
|
115945
|
+
|
|
115946
|
+
|
|
115947
|
+
|
|
115828
115948
|
/**
|
|
115829
115949
|
* Removes Markdown and MDX comments.
|
|
115830
115950
|
*/
|
|
115831
|
-
async function stripComments(doc, { mdx } = {}) {
|
|
115951
|
+
async function stripComments(doc, { mdx, mdxish } = {}) {
|
|
115832
115952
|
const { replaced, blocks } = extractMagicBlocks(doc);
|
|
115833
|
-
const processor = unified()
|
|
115953
|
+
const processor = unified();
|
|
115954
|
+
// we still require these two extensions because:
|
|
115955
|
+
// 1. we can rely on remarkMdx to parse MDXish
|
|
115956
|
+
// 2. we need to parse JSX comments into mdxTextExpression nodes so that the transformers can pick them up
|
|
115957
|
+
if (mdxish) {
|
|
115958
|
+
processor
|
|
115959
|
+
.data('micromarkExtensions', [mdxExpression({ allowEmpty: true })])
|
|
115960
|
+
.data('fromMarkdownExtensions', [mdxExpressionFromMarkdown()]);
|
|
115961
|
+
}
|
|
115962
|
+
processor
|
|
115834
115963
|
.use(remarkParse)
|
|
115964
|
+
.use(normalize_malformed_md_syntax)
|
|
115835
115965
|
.use(mdx ? remarkMdx : undefined)
|
|
115836
115966
|
.use(stripCommentsTransformer)
|
|
115837
115967
|
.use(remarkStringify, mdx
|
|
@@ -115851,8 +115981,14 @@ async function stripComments(doc, { mdx } = {}) {
|
|
|
115851
115981
|
// Preserve tight sibling code blocks without adding extra newlines between them.
|
|
115852
115982
|
// Our markdown renderer uses this to group these code blocks into a tabbed interface.
|
|
115853
115983
|
(left, right) => {
|
|
115854
|
-
|
|
115855
|
-
|
|
115984
|
+
if (left.type === 'code' && right.type === 'code') {
|
|
115985
|
+
const isTight = left.position &&
|
|
115986
|
+
right.position &&
|
|
115987
|
+
right.position.start.line - left.position.end.line === 1; // Are the blocks on adjacent lines?
|
|
115988
|
+
// 0 = no newline between blocks
|
|
115989
|
+
return isTight ? 0 : undefined;
|
|
115990
|
+
}
|
|
115991
|
+
return undefined;
|
|
115856
115992
|
},
|
|
115857
115993
|
],
|
|
115858
115994
|
});
|