@readme/markdown 14.11.0 → 14.11.2
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/components/TableOfContents/index.tsx +29 -1
- package/dist/main.js +322 -37
- package/dist/main.node.js +322 -37
- package/dist/main.node.js.map +1 -1
- package/dist/processor/transform/mdxish/evaluate-style-block-expressions.d.ts +18 -0
- package/dist/processor/transform/mdxish/style-object-to-css.d.ts +11 -0
- package/dist/processor/transform/mdxish/tables/utils.d.ts +5 -0
- package/dist/render-fixture.node.js +322 -37
- package/dist/render-fixture.node.js.map +1 -1
- package/dist/utils/common-html-words.d.ts +7 -0
- package/package.json +1 -1
|
@@ -83,6 +83,34 @@ function useScrollHighlight(navRef: React.RefObject<HTMLElement | null>) {
|
|
|
83
83
|
&& scrollParent.scrollTop + scrollParent.clientHeight >= scrollParent.scrollHeight - SCROLL_BOTTOM_TOLERANCE;
|
|
84
84
|
};
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Keeps the active link visible within the TOC's own scroll container —
|
|
88
|
+
* the same result as `scrollIntoView({ block: 'nearest' })`, but scoped to
|
|
89
|
+
* a single scroller. `scrollIntoView` adjusts *every* scrollable ancestor,
|
|
90
|
+
* and starting a scroll on the page's content scroller cancels any
|
|
91
|
+
* in-flight smooth scroll there — e.g. the hub's scroll-to-top reset when
|
|
92
|
+
* navigating between pages (CX-3667).
|
|
93
|
+
*/
|
|
94
|
+
const scrollTOCLinkIntoView = (link: HTMLAnchorElement) => {
|
|
95
|
+
const tocScroller = getScrollParent(link);
|
|
96
|
+
// Without a TOC-local scroll area, the link's nearest scrollable
|
|
97
|
+
// ancestor is the window (`getScrollParent`'s fallback) or the scroller
|
|
98
|
+
// holding the page content — never auto-scroll those just to reveal a
|
|
99
|
+
// TOC link.
|
|
100
|
+
if (!(tocScroller instanceof HTMLElement) || tocScroller.contains(headings[0])) return;
|
|
101
|
+
|
|
102
|
+
const scrollerRect = tocScroller.getBoundingClientRect();
|
|
103
|
+
const linkRect = link.getBoundingClientRect();
|
|
104
|
+
if (linkRect.top < scrollerRect.top) {
|
|
105
|
+
tocScroller.scrollTo?.({ top: tocScroller.scrollTop + (linkRect.top - scrollerRect.top), behavior: 'smooth' });
|
|
106
|
+
} else if (linkRect.bottom > scrollerRect.bottom) {
|
|
107
|
+
tocScroller.scrollTo?.({
|
|
108
|
+
top: tocScroller.scrollTop + (linkRect.bottom - scrollerRect.bottom),
|
|
109
|
+
behavior: 'smooth',
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
86
114
|
const activate = (id: string | null) => {
|
|
87
115
|
if (id === activeId) return;
|
|
88
116
|
if (activeId) linkMap.get(activeId)?.forEach(a => a.classList.remove('active'));
|
|
@@ -100,7 +128,7 @@ function useScrollHighlight(navRef: React.RefObject<HTMLElement | null>) {
|
|
|
100
128
|
nav.style.setProperty('--ToC-border-active-height', `${linkRect.height}px`);
|
|
101
129
|
nav.style.setProperty('--ToC-border-active-top', `${linkRect.top - navRect.top}px`);
|
|
102
130
|
|
|
103
|
-
link
|
|
131
|
+
scrollTOCLinkIntoView(link);
|
|
104
132
|
}
|
|
105
133
|
}
|
|
106
134
|
};
|
package/dist/main.js
CHANGED
|
@@ -12484,6 +12484,34 @@ function useScrollHighlight(navRef) {
|
|
|
12484
12484
|
return scrollParent.scrollHeight > scrollParent.clientHeight
|
|
12485
12485
|
&& scrollParent.scrollTop + scrollParent.clientHeight >= scrollParent.scrollHeight - SCROLL_BOTTOM_TOLERANCE;
|
|
12486
12486
|
};
|
|
12487
|
+
/**
|
|
12488
|
+
* Keeps the active link visible within the TOC's own scroll container —
|
|
12489
|
+
* the same result as `scrollIntoView({ block: 'nearest' })`, but scoped to
|
|
12490
|
+
* a single scroller. `scrollIntoView` adjusts *every* scrollable ancestor,
|
|
12491
|
+
* and starting a scroll on the page's content scroller cancels any
|
|
12492
|
+
* in-flight smooth scroll there — e.g. the hub's scroll-to-top reset when
|
|
12493
|
+
* navigating between pages (CX-3667).
|
|
12494
|
+
*/
|
|
12495
|
+
const scrollTOCLinkIntoView = (link) => {
|
|
12496
|
+
const tocScroller = getScrollParent(link);
|
|
12497
|
+
// Without a TOC-local scroll area, the link's nearest scrollable
|
|
12498
|
+
// ancestor is the window (`getScrollParent`'s fallback) or the scroller
|
|
12499
|
+
// holding the page content — never auto-scroll those just to reveal a
|
|
12500
|
+
// TOC link.
|
|
12501
|
+
if (!(tocScroller instanceof HTMLElement) || tocScroller.contains(headings[0]))
|
|
12502
|
+
return;
|
|
12503
|
+
const scrollerRect = tocScroller.getBoundingClientRect();
|
|
12504
|
+
const linkRect = link.getBoundingClientRect();
|
|
12505
|
+
if (linkRect.top < scrollerRect.top) {
|
|
12506
|
+
tocScroller.scrollTo?.({ top: tocScroller.scrollTop + (linkRect.top - scrollerRect.top), behavior: 'smooth' });
|
|
12507
|
+
}
|
|
12508
|
+
else if (linkRect.bottom > scrollerRect.bottom) {
|
|
12509
|
+
tocScroller.scrollTo?.({
|
|
12510
|
+
top: tocScroller.scrollTop + (linkRect.bottom - scrollerRect.bottom),
|
|
12511
|
+
behavior: 'smooth',
|
|
12512
|
+
});
|
|
12513
|
+
}
|
|
12514
|
+
};
|
|
12487
12515
|
const activate = (id) => {
|
|
12488
12516
|
if (id === activeId)
|
|
12489
12517
|
return;
|
|
@@ -12500,7 +12528,7 @@ function useScrollHighlight(navRef) {
|
|
|
12500
12528
|
const linkRect = link.getBoundingClientRect();
|
|
12501
12529
|
nav.style.setProperty('--ToC-border-active-height', `${linkRect.height}px`);
|
|
12502
12530
|
nav.style.setProperty('--ToC-border-active-top', `${linkRect.top - navRect.top}px`);
|
|
12503
|
-
link
|
|
12531
|
+
scrollTOCLinkIntoView(link);
|
|
12504
12532
|
}
|
|
12505
12533
|
}
|
|
12506
12534
|
};
|
|
@@ -75719,6 +75747,18 @@ const tableTags = new Set([
|
|
|
75719
75747
|
'th',
|
|
75720
75748
|
'td',
|
|
75721
75749
|
]);
|
|
75750
|
+
/**
|
|
75751
|
+
* Replaces every paragraph node with its inline children. Used where paragraphs
|
|
75752
|
+
* are parser artifacts (remarkMdx wrapping inline JSX), not real content.
|
|
75753
|
+
*/
|
|
75754
|
+
const unwrapParagraphNodes = (children) => {
|
|
75755
|
+
return children.flatMap(child => {
|
|
75756
|
+
if (child.type === 'paragraph' && 'children' in child && Array.isArray(child.children)) {
|
|
75757
|
+
return child.children;
|
|
75758
|
+
}
|
|
75759
|
+
return [child];
|
|
75760
|
+
});
|
|
75761
|
+
};
|
|
75722
75762
|
/**
|
|
75723
75763
|
* If the cell has exactly one paragraph child, unwrap it so its inline children sit
|
|
75724
75764
|
* directly under the cell (matches GFM table cell shape and avoids stray `<p>` wrappers).
|
|
@@ -75730,12 +75770,7 @@ const unwrapSoleParagraph = (children) => {
|
|
|
75730
75770
|
const paragraphCount = children.filter(c => c.type === 'paragraph').length;
|
|
75731
75771
|
if (paragraphCount !== 1)
|
|
75732
75772
|
return children;
|
|
75733
|
-
return children
|
|
75734
|
-
if (child.type === 'paragraph' && 'children' in child && Array.isArray(child.children)) {
|
|
75735
|
-
return child.children;
|
|
75736
|
-
}
|
|
75737
|
-
return [child];
|
|
75738
|
-
});
|
|
75773
|
+
return unwrapParagraphNodes(children);
|
|
75739
75774
|
};
|
|
75740
75775
|
/**
|
|
75741
75776
|
* Splice each text into `html` at its offset. Inserts at the same offset
|
|
@@ -76085,6 +76120,23 @@ const RUNTIME_COMPONENT_TAGS = new Set(['Variable', 'variable', 'html-block', 'r
|
|
|
76085
76120
|
* Uses the html-tags package, converted to a Set<string> for efficient lookups.
|
|
76086
76121
|
*/
|
|
76087
76122
|
const STANDARD_HTML_TAGS = new Set(html_tags_namespaceObject);
|
|
76123
|
+
/**
|
|
76124
|
+
* Table structural tags. Blank lines inside these carry deliberate meaning for
|
|
76125
|
+
* `mdxishTables` (e.g. splitting cell content into paragraphs, or deciding
|
|
76126
|
+
* whether a table stays plain HTML vs a JSX `<Table>`), so transforms that
|
|
76127
|
+
* neutralize or claim across blank lines must leave them alone.
|
|
76128
|
+
*/
|
|
76129
|
+
const HTML_TABLE_STRUCTURE_TAGS = new Set([
|
|
76130
|
+
'table',
|
|
76131
|
+
'thead',
|
|
76132
|
+
'tbody',
|
|
76133
|
+
'tfoot',
|
|
76134
|
+
'tr',
|
|
76135
|
+
'td',
|
|
76136
|
+
'th',
|
|
76137
|
+
'caption',
|
|
76138
|
+
'colgroup',
|
|
76139
|
+
]);
|
|
76088
76140
|
/**
|
|
76089
76141
|
* HTML void elements — elements that have no closing tag and no children.
|
|
76090
76142
|
*
|
|
@@ -76396,18 +76448,17 @@ const processTableNode = (node, index, parent, documentPosition) => {
|
|
|
76396
76448
|
// same line as content becomes mdxJsxTextElement inside a paragraph).
|
|
76397
76449
|
// Unwrap these so <td>/<th> sit directly under <tr>, and strip
|
|
76398
76450
|
// whitespace-only text nodes to avoid rendering empty <p>/<br>.
|
|
76399
|
-
const
|
|
76400
|
-
.flatMap(child => {
|
|
76401
|
-
if (child.type === 'paragraph' && 'children' in child && Array.isArray(child.children)) {
|
|
76402
|
-
return child.children;
|
|
76403
|
-
}
|
|
76404
|
-
return [child];
|
|
76405
|
-
})
|
|
76406
|
-
.filter(child => !(child.type === 'text' && 'value' in child && typeof child.value === 'string' && !child.value.trim()));
|
|
76451
|
+
const removeWhitespaceOnlyTextNodes = (children) => children.filter(child => !(child.type === 'text' && 'value' in child && typeof child.value === 'string' && !child.value.trim()));
|
|
76407
76452
|
visit(node, isMDXElement, (el) => {
|
|
76408
|
-
if ('children' in el
|
|
76409
|
-
|
|
76410
|
-
|
|
76453
|
+
if (!('children' in el) || !Array.isArray(el.children))
|
|
76454
|
+
return;
|
|
76455
|
+
// Filtering transformers
|
|
76456
|
+
// A cell only unwraps a sole paragraph: multiple paragraphs are real
|
|
76457
|
+
// blank-line-separated content that must stay separated
|
|
76458
|
+
const unwrapped = isTableCell(el)
|
|
76459
|
+
? unwrapSoleParagraph(el.children)
|
|
76460
|
+
: unwrapParagraphNodes(el.children);
|
|
76461
|
+
el.children = removeWhitespaceOnlyTextNodes(unwrapped);
|
|
76411
76462
|
});
|
|
76412
76463
|
parent.children[index] = {
|
|
76413
76464
|
...node,
|
|
@@ -100797,15 +100848,26 @@ const types_types = /** @type {const} */ ({
|
|
|
100797
100848
|
|
|
100798
100849
|
|
|
100799
100850
|
|
|
100851
|
+
|
|
100800
100852
|
// Raw tags (type-1: pre/script/style/textarea) and block tags (type-6: div,
|
|
100801
100853
|
// section, …) always start a block, so they stay flow even with trailing
|
|
100802
100854
|
// content. Other lowercase tags (i, span, …) follow the type-7 rule and only
|
|
100803
100855
|
// stay flow when nothing trails the close tag.
|
|
100804
100856
|
const htmlFlowTagNames = new Set([...htmlRawNames, ...htmlBlockNames]);
|
|
100857
|
+
// Lowercase type-6 block tags claimable in flow even without a `{…}` attribute, so
|
|
100858
|
+
// blank lines between nested JSX siblings don't fragment the block. Excludes table
|
|
100859
|
+
// tags (mdxishTables owns their blank lines) and voids (never close).
|
|
100860
|
+
const plainBlockClaimTagNames = new Set([...htmlBlockNames].filter(tag => !HTML_TABLE_STRUCTURE_TAGS.has(tag) && !HTML_VOID_ELEMENTS.has(tag)));
|
|
100805
100861
|
const syntax_nonLazyContinuationStart = {
|
|
100806
100862
|
tokenize: syntax_tokenizeNonLazyContinuationStart,
|
|
100807
100863
|
partial: true,
|
|
100808
100864
|
};
|
|
100865
|
+
// Lookahead for `plainClaimLineStart`: is this line markup-only, or a paragraph
|
|
100866
|
+
// that merely starts with a tag? Run via `effects.check` so it never consumes.
|
|
100867
|
+
const markupOnlyContinuation = {
|
|
100868
|
+
tokenize: tokenizeMarkupOnlyContinuation,
|
|
100869
|
+
partial: true,
|
|
100870
|
+
};
|
|
100809
100871
|
function resolveToMdxComponent(events) {
|
|
100810
100872
|
let index = events.length;
|
|
100811
100873
|
while (index > 0) {
|
|
@@ -100860,6 +100922,10 @@ function createTokenize(mode) {
|
|
|
100860
100922
|
// (INLINE_COMPONENT_TAGS — Anchor, Glossary), also brace-gated.
|
|
100861
100923
|
let isLowercaseTag = false;
|
|
100862
100924
|
let sawBraceAttr = false;
|
|
100925
|
+
// A plain lowercase block tag claimed without a `{…}` attribute, gated by
|
|
100926
|
+
// `plainClaimLineStart`: after a blank line it may only continue on a tag line.
|
|
100927
|
+
let isPlainBlockClaim = false;
|
|
100928
|
+
let pendingBlankLine = false;
|
|
100863
100929
|
// Code span tracking
|
|
100864
100930
|
let codeSpanOpenSize = 0;
|
|
100865
100931
|
let codeSpanCloseSize = 0;
|
|
@@ -101093,8 +101159,13 @@ function createTokenize(mode) {
|
|
|
101093
101159
|
}
|
|
101094
101160
|
// End of opening tag
|
|
101095
101161
|
if (code === codes.greaterThan) {
|
|
101096
|
-
if (requiresBraceAttr && !sawBraceAttr)
|
|
101097
|
-
|
|
101162
|
+
if (requiresBraceAttr && !sawBraceAttr) {
|
|
101163
|
+
// Plain lowercase block tags stay claimable in flow, gated per line by
|
|
101164
|
+
// `plainClaimLineStart`; everything else falls through to CommonMark.
|
|
101165
|
+
if (!isFlow || !plainBlockClaimTagNames.has(tagName))
|
|
101166
|
+
return nok(code);
|
|
101167
|
+
isPlainBlockClaim = true;
|
|
101168
|
+
}
|
|
101098
101169
|
effects.consume(code);
|
|
101099
101170
|
onOpenerLine = isFlow;
|
|
101100
101171
|
return body;
|
|
@@ -101244,6 +101315,7 @@ function createTokenize(mode) {
|
|
|
101244
101315
|
if (atLineStart && codeSpanOpenSize >= 3) {
|
|
101245
101316
|
fenceChar = codes.graveAccent;
|
|
101246
101317
|
fenceLength = codeSpanOpenSize;
|
|
101318
|
+
atLineStart = false;
|
|
101247
101319
|
return inFencedCode(code);
|
|
101248
101320
|
}
|
|
101249
101321
|
return inCodeSpan(code);
|
|
@@ -101391,8 +101463,14 @@ function createTokenize(mode) {
|
|
|
101391
101463
|
effects.consume(code);
|
|
101392
101464
|
return nestedOpenTagName;
|
|
101393
101465
|
}
|
|
101394
|
-
//
|
|
101395
|
-
|
|
101466
|
+
// Same-name opener followed by a tag-end char bumps depth. A line ending
|
|
101467
|
+
// counts too: Prettier puts a newline right after the name (`<div\n …\n>`).
|
|
101468
|
+
if (closingTagName === tagName &&
|
|
101469
|
+
(code === codes.greaterThan ||
|
|
101470
|
+
code === codes.slash ||
|
|
101471
|
+
code === codes.space ||
|
|
101472
|
+
code === codes.horizontalTab ||
|
|
101473
|
+
markdownLineEnding(code))) {
|
|
101396
101474
|
depth += 1;
|
|
101397
101475
|
}
|
|
101398
101476
|
atLineStart = false;
|
|
@@ -101481,6 +101559,10 @@ function createTokenize(mode) {
|
|
|
101481
101559
|
}
|
|
101482
101560
|
function bodyContinuationBefore(code) {
|
|
101483
101561
|
if (code === null || markdownLineEnding(code)) {
|
|
101562
|
+
// Empty line: outside any `{…}` expression this is CommonMark's html-block
|
|
101563
|
+
// terminator, so a plain block claim must pass the guard below to continue.
|
|
101564
|
+
if (isPlainBlockClaim && braceDepth === 0)
|
|
101565
|
+
pendingBlankLine = true;
|
|
101484
101566
|
return bodyContinuationStart(code);
|
|
101485
101567
|
}
|
|
101486
101568
|
effects.enter('mdxComponentData');
|
|
@@ -101492,19 +101574,52 @@ function createTokenize(mode) {
|
|
|
101492
101574
|
return inBodyBraceString(code);
|
|
101493
101575
|
return inBodyBraceExpr(code);
|
|
101494
101576
|
}
|
|
101495
|
-
|
|
101496
|
-
|
|
101577
|
+
if (isPlainBlockClaim)
|
|
101578
|
+
return plainClaimLineStart(code);
|
|
101579
|
+
return bodyLineStart(code);
|
|
101580
|
+
}
|
|
101581
|
+
// Dispatch a non-blank continuation line: fenced code at line start, else body.
|
|
101582
|
+
function bodyLineStart(code) {
|
|
101583
|
+
if (atLineStart && code === codes.tilde)
|
|
101497
101584
|
return bodyAfterLineStart(code);
|
|
101498
|
-
}
|
|
101499
|
-
// Detect backtick fences at line start
|
|
101500
101585
|
if (atLineStart && code === codes.graveAccent) {
|
|
101501
101586
|
codeSpanOpenSize = 0;
|
|
101502
|
-
atLineStart
|
|
101587
|
+
// Leave `atLineStart` set — `countOpenTicks` needs it to tell a fence from an
|
|
101588
|
+
// inline code span once the run of backticks is fully counted; it's cleared once
|
|
101589
|
+
// that fence-vs-span decision has actually been made.
|
|
101503
101590
|
return countOpenTicks(code);
|
|
101504
101591
|
}
|
|
101505
101592
|
atLineStart = false;
|
|
101506
101593
|
return body(code);
|
|
101507
101594
|
}
|
|
101595
|
+
// Line-start gate for plain block claims. After a blank line the block may only
|
|
101596
|
+
// continue on a tag line (`<…`); any markdown island (`**bold**`, `[block:…]`, a
|
|
101597
|
+
// fence) refuses so CommonMark html-flow reparses it exactly as it does today.
|
|
101598
|
+
function plainClaimLineStart(code) {
|
|
101599
|
+
// Leading whitespace only → treat as a blank line, matching CommonMark.
|
|
101600
|
+
if (code === codes.space || code === codes.horizontalTab) {
|
|
101601
|
+
effects.consume(code);
|
|
101602
|
+
return plainClaimLineStart;
|
|
101603
|
+
}
|
|
101604
|
+
if (code === null || markdownLineEnding(code)) {
|
|
101605
|
+
pendingBlankLine = true;
|
|
101606
|
+
effects.exit('mdxComponentData');
|
|
101607
|
+
return bodyContinuationStart(code);
|
|
101608
|
+
}
|
|
101609
|
+
// Across a blank line the block only continues onto a markup-only line; a
|
|
101610
|
+
// paragraph that merely starts with a tag must fall back so its markdown
|
|
101611
|
+
// parses and rehype-raw re-nests it into the wrapper.
|
|
101612
|
+
if (pendingBlankLine) {
|
|
101613
|
+
if (code !== codes.lessThan)
|
|
101614
|
+
return nok(code);
|
|
101615
|
+
return effects.check(markupOnlyContinuation, plainClaimContinue, nok)(code);
|
|
101616
|
+
}
|
|
101617
|
+
return bodyLineStart(code);
|
|
101618
|
+
}
|
|
101619
|
+
function plainClaimContinue(code) {
|
|
101620
|
+
pendingBlankLine = false;
|
|
101621
|
+
return bodyLineStart(code);
|
|
101622
|
+
}
|
|
101508
101623
|
// ── Shared lazy continuation failure ───────────────────────────────────
|
|
101509
101624
|
function continuationAfter(code) {
|
|
101510
101625
|
if (code === null) {
|
|
@@ -101535,6 +101650,46 @@ function syntax_tokenizeNonLazyContinuationStart(effects, ok, nok) {
|
|
|
101535
101650
|
return ok(code);
|
|
101536
101651
|
}
|
|
101537
101652
|
}
|
|
101653
|
+
// A markup-only line opens with a tag (`<x`/`</x`) and ends (ignoring trailing
|
|
101654
|
+
// spaces) at a `>`. That distinguishes a structural continuation like
|
|
101655
|
+
// `<span>b</span></div>` from a paragraph like `<b>Note:</b> read *this*`.
|
|
101656
|
+
function tokenizeMarkupOnlyContinuation(effects, ok, nok) {
|
|
101657
|
+
let lastNonSpace = null;
|
|
101658
|
+
return start;
|
|
101659
|
+
function start(code) {
|
|
101660
|
+
// Caller guarantees we are at `<` at the (already de-indented) line start.
|
|
101661
|
+
effects.enter(types_types.data);
|
|
101662
|
+
effects.consume(code);
|
|
101663
|
+
return afterLessThan;
|
|
101664
|
+
}
|
|
101665
|
+
function afterLessThan(code) {
|
|
101666
|
+
if (code === codes.slash) {
|
|
101667
|
+
effects.consume(code);
|
|
101668
|
+
return afterSlash;
|
|
101669
|
+
}
|
|
101670
|
+
return afterSlash(code);
|
|
101671
|
+
}
|
|
101672
|
+
// The `<` (or `</`) must introduce a real tag, not a stray `<` in prose.
|
|
101673
|
+
function afterSlash(code) {
|
|
101674
|
+
if (asciiAlpha(code)) {
|
|
101675
|
+
lastNonSpace = code;
|
|
101676
|
+
effects.consume(code);
|
|
101677
|
+
return scanToLineEnd;
|
|
101678
|
+
}
|
|
101679
|
+
effects.exit(types_types.data);
|
|
101680
|
+
return nok(code);
|
|
101681
|
+
}
|
|
101682
|
+
function scanToLineEnd(code) {
|
|
101683
|
+
if (code === null || markdownLineEnding(code)) {
|
|
101684
|
+
effects.exit(types_types.data);
|
|
101685
|
+
return lastNonSpace === codes.greaterThan ? ok(code) : nok(code);
|
|
101686
|
+
}
|
|
101687
|
+
if (!markdownSpace(code))
|
|
101688
|
+
lastNonSpace = code;
|
|
101689
|
+
effects.consume(code);
|
|
101690
|
+
return scanToLineEnd;
|
|
101691
|
+
}
|
|
101692
|
+
}
|
|
101538
101693
|
/**
|
|
101539
101694
|
* Micromark extension that tokenizes MDX-like components.
|
|
101540
101695
|
*
|
|
@@ -101781,6 +101936,8 @@ const mdxishInlineMdxComponents = () => tree => {
|
|
|
101781
101936
|
|
|
101782
101937
|
|
|
101783
101938
|
|
|
101939
|
+
/** Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. */
|
|
101940
|
+
const NESTED_ATTR_EXPRESSION_RE = /[\w-]+\s*=\s*\{/;
|
|
101784
101941
|
/**
|
|
101785
101942
|
* Reduce leading whitespace on all lines just enough to prevent
|
|
101786
101943
|
* remark from treating indented content as code blocks (4+ spaces).
|
|
@@ -101940,10 +102097,16 @@ const mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
|
|
|
101940
102097
|
// inline, which is how ReadMe's custom components are modeled).
|
|
101941
102098
|
if (!isPascal && parent.type === 'paragraph')
|
|
101942
102099
|
return;
|
|
101943
|
-
// Lowercase HTML tags are
|
|
101944
|
-
//
|
|
102100
|
+
// Lowercase HTML tags are eligible when they (or a descendant tag in their
|
|
102101
|
+
// content, e.g. a `.map()` body returning JSX with `key={i}`) carry a
|
|
102102
|
+
// JSX-expression attribute. Without this, a wrapper `<div>` with only plain
|
|
102103
|
+
// attributes (or none) swallows its whole nested block — including any
|
|
102104
|
+
// `style={{...}}`/`.map()` JSX inside it — as literal HTML text, which
|
|
102105
|
+
// rehype-raw's parse5 pass then garbles (it has no notion of JS expressions).
|
|
102106
|
+
// Plain HTML with no expressions anywhere stays as an html node so
|
|
101945
102107
|
// rehype-raw handles it as normal.
|
|
101946
|
-
|
|
102108
|
+
const hasNestedExpressionAttr = !selfClosing && NESTED_ATTR_EXPRESSION_RE.test(contentAfterTag);
|
|
102109
|
+
if (!isPascal && !hasExpressionAttr(attributes) && !hasNestedExpressionAttr)
|
|
101947
102110
|
return;
|
|
101948
102111
|
const closingTagStr = `</${tag}>`;
|
|
101949
102112
|
// Case 1: Self-closing tag
|
|
@@ -102382,15 +102545,17 @@ const evalExpression = (expression, scope) => {
|
|
|
102382
102545
|
|
|
102383
102546
|
|
|
102384
102547
|
|
|
102548
|
+
/** True for an array containing at least one React element, e.g. the result of `.map()` returning JSX. */
|
|
102549
|
+
const isRenderableElementArray = (value) => Array.isArray(value) && value.some((external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default()).isValidElement);
|
|
102385
102550
|
/** Given the type of the expression result, create the corresponding mdast node. */
|
|
102386
102551
|
const createEvaluatedNode = (result, position) => {
|
|
102387
102552
|
if (result === null || result === undefined) {
|
|
102388
102553
|
return { type: 'text', value: '', position };
|
|
102389
102554
|
}
|
|
102390
|
-
else if (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().isValidElement(result)) {
|
|
102391
|
-
// Convert react elements to
|
|
102392
|
-
// This must come before the object check as
|
|
102393
|
-
return { type: 'html', value: (0,server_browser/* renderToStaticMarkup */.qV)(result), position };
|
|
102555
|
+
else if (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().isValidElement(result) || isRenderableElementArray(result)) {
|
|
102556
|
+
// Convert react elements (or arrays of them, e.g. `.map()` returning JSX) to their HTML
|
|
102557
|
+
// representation. This must come before the object check as both are a subset of it.
|
|
102558
|
+
return { type: 'html', value: (0,server_browser/* renderToStaticMarkup */.qV)(external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().createElement((external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default()).Fragment, null, result)), position };
|
|
102394
102559
|
}
|
|
102395
102560
|
else if (typeof result === 'object') {
|
|
102396
102561
|
return { type: 'text', value: JSON.stringify(result), position };
|
|
@@ -102430,6 +102595,47 @@ const evaluateExpressions = () => (tree, file) => {
|
|
|
102430
102595
|
};
|
|
102431
102596
|
/* harmony default export */ const evaluate_expressions = (evaluateExpressions);
|
|
102432
102597
|
|
|
102598
|
+
;// ./processor/transform/mdxish/evaluate-style-block-expressions.ts
|
|
102599
|
+
|
|
102600
|
+
|
|
102601
|
+
|
|
102602
|
+
// Matches a standalone `<style ...>{ <expr> }</style>` block — the JSX shape MDX evaluates
|
|
102603
|
+
// (typically a template-literal-wrapped CSS string) but which mdxish otherwise leaves as
|
|
102604
|
+
// literal, invalid-CSS text (see CX-3646).
|
|
102605
|
+
const STYLE_EXPRESSION_RE = /^(<style\b[^>]*>)\s*\{([\s\S]*)\}\s*(<\/style>)$/i;
|
|
102606
|
+
/**
|
|
102607
|
+
* Evaluate the JSX expression wrapping a `<style>` tag's contents (typically a template
|
|
102608
|
+
* literal, e.g. `<style>{\`.foo { color: red; }\`}</style>`) so the tag ends up holding plain
|
|
102609
|
+
* CSS text instead of the literal `{`...`}` wrapper — which the browser treats as invalid CSS
|
|
102610
|
+
* and drops the entire stylesheet for.
|
|
102611
|
+
*
|
|
102612
|
+
* `<style>` is one of CommonMark's "raw text" HTML elements (along with script/pre/textarea):
|
|
102613
|
+
* its contents are captured verbatim as a single `html` mdast node with no inner tokenization,
|
|
102614
|
+
* so this expression never reaches the mdxFlowExpression tokenizer or `evaluateExpressions` —
|
|
102615
|
+
* it has to be matched and evaluated directly against the raw node's string value.
|
|
102616
|
+
*
|
|
102617
|
+
* Must run after `evaluateExports` (needs `file.data.mdxishScope`) and before `remarkRehype`
|
|
102618
|
+
* turns `html` mdast nodes into raw hast strings. Skipped in safeMode.
|
|
102619
|
+
*/
|
|
102620
|
+
const evaluateStyleBlockExpressions = () => (tree, file) => {
|
|
102621
|
+
const scope = { ...file.data.mdxishScope, React: (external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default()) };
|
|
102622
|
+
visit(tree, 'html', (node) => {
|
|
102623
|
+
const match = node.value.trim().match(STYLE_EXPRESSION_RE);
|
|
102624
|
+
if (!match)
|
|
102625
|
+
return;
|
|
102626
|
+
const [, openTag, expression, closeTag] = match;
|
|
102627
|
+
try {
|
|
102628
|
+
const css = evalExpression(expression, scope);
|
|
102629
|
+
node.value = `${openTag}${String(css)}${closeTag}`;
|
|
102630
|
+
}
|
|
102631
|
+
catch {
|
|
102632
|
+
// Evaluation failed — leave the node untouched so it round-trips as literal text.
|
|
102633
|
+
}
|
|
102634
|
+
});
|
|
102635
|
+
return tree;
|
|
102636
|
+
};
|
|
102637
|
+
/* harmony default export */ const evaluate_style_block_expressions = (evaluateStyleBlockExpressions);
|
|
102638
|
+
|
|
102433
102639
|
;// ./processor/transform/mdxish/heading-slugs.ts
|
|
102434
102640
|
|
|
102435
102641
|
|
|
@@ -104286,10 +104492,84 @@ function removeJSXComments(content) {
|
|
|
104286
104492
|
return content.replace(JSX_COMMENT_REGEX, '');
|
|
104287
104493
|
}
|
|
104288
104494
|
|
|
104495
|
+
;// ./processor/transform/mdxish/style-object-to-css.ts
|
|
104496
|
+
|
|
104497
|
+
/**
|
|
104498
|
+
* CSS properties React treats as unitless — a bare number stays as-is instead of
|
|
104499
|
+
* getting `px` appended. Mirrors React DOM's `isUnitlessNumber` whitelist.
|
|
104500
|
+
* @see https://github.com/facebook/react/blob/main/packages/react-dom-bindings/src/shared/CSSProperty.js
|
|
104501
|
+
*/
|
|
104502
|
+
const UNITLESS_CSS_PROPERTIES = new Set([
|
|
104503
|
+
'animationIterationCount',
|
|
104504
|
+
'aspectRatio',
|
|
104505
|
+
'borderImageOutset',
|
|
104506
|
+
'borderImageSlice',
|
|
104507
|
+
'borderImageWidth',
|
|
104508
|
+
'boxFlex',
|
|
104509
|
+
'boxFlexGroup',
|
|
104510
|
+
'boxOrdinalGroup',
|
|
104511
|
+
'columnCount',
|
|
104512
|
+
'columns',
|
|
104513
|
+
'flex',
|
|
104514
|
+
'flexGrow',
|
|
104515
|
+
'flexPositive',
|
|
104516
|
+
'flexShrink',
|
|
104517
|
+
'flexNegative',
|
|
104518
|
+
'flexOrder',
|
|
104519
|
+
'gridArea',
|
|
104520
|
+
'gridRow',
|
|
104521
|
+
'gridRowEnd',
|
|
104522
|
+
'gridRowSpan',
|
|
104523
|
+
'gridRowStart',
|
|
104524
|
+
'gridColumn',
|
|
104525
|
+
'gridColumnEnd',
|
|
104526
|
+
'gridColumnSpan',
|
|
104527
|
+
'gridColumnStart',
|
|
104528
|
+
'fontWeight',
|
|
104529
|
+
'lineClamp',
|
|
104530
|
+
'lineHeight',
|
|
104531
|
+
'opacity',
|
|
104532
|
+
'order',
|
|
104533
|
+
'orphans',
|
|
104534
|
+
'tabSize',
|
|
104535
|
+
'widows',
|
|
104536
|
+
'zIndex',
|
|
104537
|
+
'zoom',
|
|
104538
|
+
'fillOpacity',
|
|
104539
|
+
'floodOpacity',
|
|
104540
|
+
'stopOpacity',
|
|
104541
|
+
'strokeDasharray',
|
|
104542
|
+
'strokeDashoffset',
|
|
104543
|
+
'strokeMiterlimit',
|
|
104544
|
+
'strokeOpacity',
|
|
104545
|
+
'strokeWidth',
|
|
104546
|
+
]);
|
|
104547
|
+
/** Convert a camelCase (or CSS custom property) key into its kebab-case CSS name. */
|
|
104548
|
+
const cssPropertyName = (key) => (key.startsWith('--') ? key : key.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`));
|
|
104549
|
+
/** React appends `px` to non-zero numeric values, except unitless and custom properties. */
|
|
104550
|
+
const cssPropertyValue = (key, value) => typeof value === 'number' && value !== 0 && !key.startsWith('--') && !UNITLESS_CSS_PROPERTIES.has(key)
|
|
104551
|
+
? `${value}px`
|
|
104552
|
+
: `${value}`;
|
|
104553
|
+
/**
|
|
104554
|
+
* True for a value that should be serialized as a style object rather than passed through
|
|
104555
|
+
* as an already-CSS string. React elements are excluded since they're valid objects too.
|
|
104556
|
+
*/
|
|
104557
|
+
const style_object_to_css_isPlainObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value) && !external_amd_react_commonjs_react_commonjs2_react_root_React_umd_react_default().isValidElement(value);
|
|
104558
|
+
/**
|
|
104559
|
+
* Serialize a React-style inline style object (`{ color: "red", fontSize: 12 }`) into a
|
|
104560
|
+
* CSS declaration string (`color:red;font-size:12px`), the shape hast/HTML expects for a
|
|
104561
|
+
* `style` attribute. Empty (`undefined`/`null`/`''`) entries are dropped.
|
|
104562
|
+
*/
|
|
104563
|
+
const styleObjectToCssText = (style) => Object.entries(style)
|
|
104564
|
+
.filter(([, value]) => value !== undefined && value !== null && value !== '')
|
|
104565
|
+
.map(([key, value]) => `${cssPropertyName(key)}:${cssPropertyValue(key, value)}`)
|
|
104566
|
+
.join(';');
|
|
104567
|
+
|
|
104289
104568
|
;// ./processor/transform/mdxish/resolve-deferred-attribute-expression-props.ts
|
|
104290
104569
|
|
|
104291
104570
|
|
|
104292
104571
|
|
|
104572
|
+
|
|
104293
104573
|
/**
|
|
104294
104574
|
* Resolve attribute expressions that `mdxJsxElementHandler` deferred.
|
|
104295
104575
|
*
|
|
@@ -104311,7 +104591,10 @@ const resolveDeferredAttributeExpressionProps = () => (tree, file) => {
|
|
|
104311
104591
|
const properties = node.properties;
|
|
104312
104592
|
Object.entries(deferredExpressions).forEach(([name, source]) => {
|
|
104313
104593
|
try {
|
|
104314
|
-
|
|
104594
|
+
const result = evalExpression(source, scope);
|
|
104595
|
+
// hast/HTML `style` is a plain CSS string; a `style={{...}}` expression evaluates to
|
|
104596
|
+
// a JS object, which must be serialized or it renders as the literal "[object Object]".
|
|
104597
|
+
properties[name] = name === 'style' && style_object_to_css_isPlainObject(result) ? styleObjectToCssText(result) : result;
|
|
104315
104598
|
}
|
|
104316
104599
|
catch {
|
|
104317
104600
|
// Evaluation failed — fall back to the raw expression source so the attribute
|
|
@@ -104631,13 +104914,13 @@ function normalizeTableSeparator(content) {
|
|
|
104631
104914
|
;// ./processor/transform/mdxish/terminate-html-flow-blocks.ts
|
|
104632
104915
|
|
|
104633
104916
|
|
|
104917
|
+
|
|
104634
104918
|
const STANDALONE_HTML_LINE_REGEX = /^(<[a-z][^<>]*>|<\/[a-z][^<>]*>)+\s*$/;
|
|
104635
104919
|
const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/;
|
|
104636
|
-
const terminate_html_flow_blocks_TABLE_STRUCTURE_TAGS = ['table', 'thead', 'tbody', 'tfoot', 'tr', 'td', 'th', 'caption', 'colgroup'];
|
|
104637
104920
|
// Tags whose contents must be preserved as is, inserting a blank line after the
|
|
104638
104921
|
// opener corrupts the payload.
|
|
104639
104922
|
// htmlRawNames here refer to <pre>, <textarea>, <script>, <style>
|
|
104640
|
-
const RAW_CONTENT_TAGS = [...htmlRawNames, ...
|
|
104923
|
+
const RAW_CONTENT_TAGS = [...htmlRawNames, ...HTML_TABLE_STRUCTURE_TAGS];
|
|
104641
104924
|
// The `(?=[\s/>])` lookahead avoids false matches on lookalike names like `<script-foo>`.
|
|
104642
104925
|
const RAW_CONTENT_TAG_MATCHERS = RAW_CONTENT_TAGS.map(tag => ({
|
|
104643
104926
|
open: new RegExp(`<${tag}(?=[\\s/>])[^>]*?(?<!/)>`, 'gi'),
|
|
@@ -105435,6 +105718,7 @@ function loadComponents() {
|
|
|
105435
105718
|
|
|
105436
105719
|
|
|
105437
105720
|
|
|
105721
|
+
|
|
105438
105722
|
|
|
105439
105723
|
|
|
105440
105724
|
const defaultTransformers = [
|
|
@@ -105585,6 +105869,7 @@ function mdxish(mdContent, opts = {}) {
|
|
|
105585
105869
|
.use(safeMode ? undefined : evaluate_exports) // Evaluate `export const/function` and stash scope on file.data.mdxishScope
|
|
105586
105870
|
.use(remarkBreaks) // Must precede evaluateExpressions to avoid splitting the \n in an evaluated template literal into a <br> node
|
|
105587
105871
|
.use(safeMode ? undefined : evaluate_expressions) // Evaluate self-contained MDX expressions (e.g. `{1+1}`)
|
|
105872
|
+
.use(safeMode ? undefined : evaluate_style_block_expressions) // Evaluate `<style>{`...`}</style>` template literals into plain CSS
|
|
105588
105873
|
.use(variables_code, { variables }) // Resolve <<...>> and {user.*} inside code and inline code nodes
|
|
105589
105874
|
.use(remarkRehype, { allowDangerousHtml: true, handlers: mdxComponentHandlers })
|
|
105590
105875
|
.use(preserveBooleanProperties) // RehypeRaw converts boolean properties to empty strings
|