@readme/markdown 14.11.0 → 14.11.1

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.
@@ -0,0 +1,18 @@
1
+ import type { Root } from 'mdast';
2
+ import type { Plugin } from 'unified';
3
+ /**
4
+ * Evaluate the JSX expression wrapping a `<style>` tag's contents (typically a template
5
+ * literal, e.g. `<style>{\`.foo { color: red; }\`}</style>`) so the tag ends up holding plain
6
+ * CSS text instead of the literal `{`...`}` wrapper — which the browser treats as invalid CSS
7
+ * and drops the entire stylesheet for.
8
+ *
9
+ * `<style>` is one of CommonMark's "raw text" HTML elements (along with script/pre/textarea):
10
+ * its contents are captured verbatim as a single `html` mdast node with no inner tokenization,
11
+ * so this expression never reaches the mdxFlowExpression tokenizer or `evaluateExpressions` —
12
+ * it has to be matched and evaluated directly against the raw node's string value.
13
+ *
14
+ * Must run after `evaluateExports` (needs `file.data.mdxishScope`) and before `remarkRehype`
15
+ * turns `html` mdast nodes into raw hast strings. Skipped in safeMode.
16
+ */
17
+ declare const evaluateStyleBlockExpressions: Plugin<[], Root>;
18
+ export default evaluateStyleBlockExpressions;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * True for a value that should be serialized as a style object rather than passed through
3
+ * as an already-CSS string. React elements are excluded since they're valid objects too.
4
+ */
5
+ export declare const isPlainObject: (value: unknown) => value is Record<string, unknown>;
6
+ /**
7
+ * Serialize a React-style inline style object (`{ color: "red", fontSize: 12 }`) into a
8
+ * CSS declaration string (`color:red;font-size:12px`), the shape hast/HTML expects for a
9
+ * `style` attribute. Empty (`undefined`/`null`/`''`) entries are dropped.
10
+ */
11
+ export declare const styleObjectToCssText: (style: Record<string, unknown>) => string;
@@ -9,6 +9,11 @@ export interface RepairResult {
9
9
  value: string;
10
10
  }
11
11
  export declare const tableTags: Set<string>;
12
+ /**
13
+ * Replaces every paragraph node with its inline children. Used where paragraphs
14
+ * are parser artifacts (remarkMdx wrapping inline JSX), not real content.
15
+ */
16
+ export declare const unwrapParagraphNodes: (children: Node[]) => Node[];
12
17
  /**
13
18
  * If the cell has exactly one paragraph child, unwrap it so its inline children sit
14
19
  * directly under the cell (matches GFM table cell shape and avoids stray `<p>` wrappers).
@@ -95894,6 +95894,18 @@ const tableTags = new Set([
95894
95894
  'th',
95895
95895
  'td',
95896
95896
  ]);
95897
+ /**
95898
+ * Replaces every paragraph node with its inline children. Used where paragraphs
95899
+ * are parser artifacts (remarkMdx wrapping inline JSX), not real content.
95900
+ */
95901
+ const unwrapParagraphNodes = (children) => {
95902
+ return children.flatMap(child => {
95903
+ if (child.type === 'paragraph' && 'children' in child && Array.isArray(child.children)) {
95904
+ return child.children;
95905
+ }
95906
+ return [child];
95907
+ });
95908
+ };
95897
95909
  /**
95898
95910
  * If the cell has exactly one paragraph child, unwrap it so its inline children sit
95899
95911
  * directly under the cell (matches GFM table cell shape and avoids stray `<p>` wrappers).
@@ -95905,12 +95917,7 @@ const unwrapSoleParagraph = (children) => {
95905
95917
  const paragraphCount = children.filter(c => c.type === 'paragraph').length;
95906
95918
  if (paragraphCount !== 1)
95907
95919
  return children;
95908
- return children.flatMap(child => {
95909
- if (child.type === 'paragraph' && 'children' in child && Array.isArray(child.children)) {
95910
- return child.children;
95911
- }
95912
- return [child];
95913
- });
95920
+ return unwrapParagraphNodes(children);
95914
95921
  };
95915
95922
  /**
95916
95923
  * Splice each text into `html` at its offset. Inserts at the same offset
@@ -96260,6 +96267,23 @@ const RUNTIME_COMPONENT_TAGS = new Set(['Variable', 'variable', 'html-block', 'r
96260
96267
  * Uses the html-tags package, converted to a Set<string> for efficient lookups.
96261
96268
  */
96262
96269
  const STANDARD_HTML_TAGS = new Set(html_tags_namespaceObject);
96270
+ /**
96271
+ * Table structural tags. Blank lines inside these carry deliberate meaning for
96272
+ * `mdxishTables` (e.g. splitting cell content into paragraphs, or deciding
96273
+ * whether a table stays plain HTML vs a JSX `<Table>`), so transforms that
96274
+ * neutralize or claim across blank lines must leave them alone.
96275
+ */
96276
+ const HTML_TABLE_STRUCTURE_TAGS = new Set([
96277
+ 'table',
96278
+ 'thead',
96279
+ 'tbody',
96280
+ 'tfoot',
96281
+ 'tr',
96282
+ 'td',
96283
+ 'th',
96284
+ 'caption',
96285
+ 'colgroup',
96286
+ ]);
96263
96287
  /**
96264
96288
  * HTML void elements — elements that have no closing tag and no children.
96265
96289
  *
@@ -96571,18 +96595,17 @@ const processTableNode = (node, index, parent, documentPosition) => {
96571
96595
  // same line as content becomes mdxJsxTextElement inside a paragraph).
96572
96596
  // Unwrap these so <td>/<th> sit directly under <tr>, and strip
96573
96597
  // whitespace-only text nodes to avoid rendering empty <p>/<br>.
96574
- const cleanChildren = (children) => children
96575
- .flatMap(child => {
96576
- if (child.type === 'paragraph' && 'children' in child && Array.isArray(child.children)) {
96577
- return child.children;
96578
- }
96579
- return [child];
96580
- })
96581
- .filter(child => !(child.type === 'text' && 'value' in child && typeof child.value === 'string' && !child.value.trim()));
96598
+ const removeWhitespaceOnlyTextNodes = (children) => children.filter(child => !(child.type === 'text' && 'value' in child && typeof child.value === 'string' && !child.value.trim()));
96582
96599
  lib_visit(node, utils_isMDXElement, (el) => {
96583
- if ('children' in el && Array.isArray(el.children)) {
96584
- el.children = cleanChildren(el.children);
96585
- }
96600
+ if (!('children' in el) || !Array.isArray(el.children))
96601
+ return;
96602
+ // Filtering transformers
96603
+ // A cell only unwraps a sole paragraph: multiple paragraphs are real
96604
+ // blank-line-separated content that must stay separated
96605
+ const unwrapped = isTableCell(el)
96606
+ ? unwrapSoleParagraph(el.children)
96607
+ : unwrapParagraphNodes(el.children);
96608
+ el.children = removeWhitespaceOnlyTextNodes(unwrapped);
96586
96609
  });
96587
96610
  parent.children[index] = {
96588
96611
  ...node,
@@ -119552,15 +119575,26 @@ const types_types = /** @type {const} */ ({
119552
119575
 
119553
119576
 
119554
119577
 
119578
+
119555
119579
  // Raw tags (type-1: pre/script/style/textarea) and block tags (type-6: div,
119556
119580
  // section, …) always start a block, so they stay flow even with trailing
119557
119581
  // content. Other lowercase tags (i, span, …) follow the type-7 rule and only
119558
119582
  // stay flow when nothing trails the close tag.
119559
119583
  const htmlFlowTagNames = new Set([...htmlRawNames, ...htmlBlockNames]);
119584
+ // Lowercase type-6 block tags claimable in flow even without a `{…}` attribute, so
119585
+ // blank lines between nested JSX siblings don't fragment the block. Excludes table
119586
+ // tags (mdxishTables owns their blank lines) and voids (never close).
119587
+ const plainBlockClaimTagNames = new Set([...htmlBlockNames].filter(tag => !HTML_TABLE_STRUCTURE_TAGS.has(tag) && !HTML_VOID_ELEMENTS.has(tag)));
119560
119588
  const syntax_nonLazyContinuationStart = {
119561
119589
  tokenize: syntax_tokenizeNonLazyContinuationStart,
119562
119590
  partial: true,
119563
119591
  };
119592
+ // Lookahead for `plainClaimLineStart`: is this line markup-only, or a paragraph
119593
+ // that merely starts with a tag? Run via `effects.check` so it never consumes.
119594
+ const markupOnlyContinuation = {
119595
+ tokenize: tokenizeMarkupOnlyContinuation,
119596
+ partial: true,
119597
+ };
119564
119598
  function resolveToMdxComponent(events) {
119565
119599
  let index = events.length;
119566
119600
  while (index > 0) {
@@ -119615,6 +119649,10 @@ function createTokenize(mode) {
119615
119649
  // (INLINE_COMPONENT_TAGS — Anchor, Glossary), also brace-gated.
119616
119650
  let isLowercaseTag = false;
119617
119651
  let sawBraceAttr = false;
119652
+ // A plain lowercase block tag claimed without a `{…}` attribute, gated by
119653
+ // `plainClaimLineStart`: after a blank line it may only continue on a tag line.
119654
+ let isPlainBlockClaim = false;
119655
+ let pendingBlankLine = false;
119618
119656
  // Code span tracking
119619
119657
  let codeSpanOpenSize = 0;
119620
119658
  let codeSpanCloseSize = 0;
@@ -119848,8 +119886,13 @@ function createTokenize(mode) {
119848
119886
  }
119849
119887
  // End of opening tag
119850
119888
  if (code === codes.greaterThan) {
119851
- if (requiresBraceAttr && !sawBraceAttr)
119852
- return nok(code);
119889
+ if (requiresBraceAttr && !sawBraceAttr) {
119890
+ // Plain lowercase block tags stay claimable in flow, gated per line by
119891
+ // `plainClaimLineStart`; everything else falls through to CommonMark.
119892
+ if (!isFlow || !plainBlockClaimTagNames.has(tagName))
119893
+ return nok(code);
119894
+ isPlainBlockClaim = true;
119895
+ }
119853
119896
  effects.consume(code);
119854
119897
  onOpenerLine = isFlow;
119855
119898
  return body;
@@ -119999,6 +120042,7 @@ function createTokenize(mode) {
119999
120042
  if (atLineStart && codeSpanOpenSize >= 3) {
120000
120043
  fenceChar = codes.graveAccent;
120001
120044
  fenceLength = codeSpanOpenSize;
120045
+ atLineStart = false;
120002
120046
  return inFencedCode(code);
120003
120047
  }
120004
120048
  return inCodeSpan(code);
@@ -120146,8 +120190,14 @@ function createTokenize(mode) {
120146
120190
  effects.consume(code);
120147
120191
  return nestedOpenTagName;
120148
120192
  }
120149
- // Only increment depth for same-name tags that are followed by valid tag-end chars
120150
- if (closingTagName === tagName && (code === codes.greaterThan || code === codes.slash || code === codes.space || code === codes.horizontalTab)) {
120193
+ // Same-name opener followed by a tag-end char bumps depth. A line ending
120194
+ // counts too: Prettier puts a newline right after the name (`<div\n …\n>`).
120195
+ if (closingTagName === tagName &&
120196
+ (code === codes.greaterThan ||
120197
+ code === codes.slash ||
120198
+ code === codes.space ||
120199
+ code === codes.horizontalTab ||
120200
+ markdownLineEnding(code))) {
120151
120201
  depth += 1;
120152
120202
  }
120153
120203
  atLineStart = false;
@@ -120236,6 +120286,10 @@ function createTokenize(mode) {
120236
120286
  }
120237
120287
  function bodyContinuationBefore(code) {
120238
120288
  if (code === null || markdownLineEnding(code)) {
120289
+ // Empty line: outside any `{…}` expression this is CommonMark's html-block
120290
+ // terminator, so a plain block claim must pass the guard below to continue.
120291
+ if (isPlainBlockClaim && braceDepth === 0)
120292
+ pendingBlankLine = true;
120239
120293
  return bodyContinuationStart(code);
120240
120294
  }
120241
120295
  effects.enter('mdxComponentData');
@@ -120247,19 +120301,52 @@ function createTokenize(mode) {
120247
120301
  return inBodyBraceString(code);
120248
120302
  return inBodyBraceExpr(code);
120249
120303
  }
120250
- // Detect tilde fences at line start
120251
- if (atLineStart && code === codes.tilde) {
120304
+ if (isPlainBlockClaim)
120305
+ return plainClaimLineStart(code);
120306
+ return bodyLineStart(code);
120307
+ }
120308
+ // Dispatch a non-blank continuation line: fenced code at line start, else body.
120309
+ function bodyLineStart(code) {
120310
+ if (atLineStart && code === codes.tilde)
120252
120311
  return bodyAfterLineStart(code);
120253
- }
120254
- // Detect backtick fences at line start
120255
120312
  if (atLineStart && code === codes.graveAccent) {
120256
120313
  codeSpanOpenSize = 0;
120257
- atLineStart = false;
120314
+ // Leave `atLineStart` set — `countOpenTicks` needs it to tell a fence from an
120315
+ // inline code span once the run of backticks is fully counted; it's cleared once
120316
+ // that fence-vs-span decision has actually been made.
120258
120317
  return countOpenTicks(code);
120259
120318
  }
120260
120319
  atLineStart = false;
120261
120320
  return body(code);
120262
120321
  }
120322
+ // Line-start gate for plain block claims. After a blank line the block may only
120323
+ // continue on a tag line (`<…`); any markdown island (`**bold**`, `[block:…]`, a
120324
+ // fence) refuses so CommonMark html-flow reparses it exactly as it does today.
120325
+ function plainClaimLineStart(code) {
120326
+ // Leading whitespace only → treat as a blank line, matching CommonMark.
120327
+ if (code === codes.space || code === codes.horizontalTab) {
120328
+ effects.consume(code);
120329
+ return plainClaimLineStart;
120330
+ }
120331
+ if (code === null || markdownLineEnding(code)) {
120332
+ pendingBlankLine = true;
120333
+ effects.exit('mdxComponentData');
120334
+ return bodyContinuationStart(code);
120335
+ }
120336
+ // Across a blank line the block only continues onto a markup-only line; a
120337
+ // paragraph that merely starts with a tag must fall back so its markdown
120338
+ // parses and rehype-raw re-nests it into the wrapper.
120339
+ if (pendingBlankLine) {
120340
+ if (code !== codes.lessThan)
120341
+ return nok(code);
120342
+ return effects.check(markupOnlyContinuation, plainClaimContinue, nok)(code);
120343
+ }
120344
+ return bodyLineStart(code);
120345
+ }
120346
+ function plainClaimContinue(code) {
120347
+ pendingBlankLine = false;
120348
+ return bodyLineStart(code);
120349
+ }
120263
120350
  // ── Shared lazy continuation failure ───────────────────────────────────
120264
120351
  function continuationAfter(code) {
120265
120352
  if (code === null) {
@@ -120290,6 +120377,46 @@ function syntax_tokenizeNonLazyContinuationStart(effects, ok, nok) {
120290
120377
  return ok(code);
120291
120378
  }
120292
120379
  }
120380
+ // A markup-only line opens with a tag (`<x`/`</x`) and ends (ignoring trailing
120381
+ // spaces) at a `>`. That distinguishes a structural continuation like
120382
+ // `<span>b</span></div>` from a paragraph like `<b>Note:</b> read *this*`.
120383
+ function tokenizeMarkupOnlyContinuation(effects, ok, nok) {
120384
+ let lastNonSpace = null;
120385
+ return start;
120386
+ function start(code) {
120387
+ // Caller guarantees we are at `<` at the (already de-indented) line start.
120388
+ effects.enter(types_types.data);
120389
+ effects.consume(code);
120390
+ return afterLessThan;
120391
+ }
120392
+ function afterLessThan(code) {
120393
+ if (code === codes.slash) {
120394
+ effects.consume(code);
120395
+ return afterSlash;
120396
+ }
120397
+ return afterSlash(code);
120398
+ }
120399
+ // The `<` (or `</`) must introduce a real tag, not a stray `<` in prose.
120400
+ function afterSlash(code) {
120401
+ if (asciiAlpha(code)) {
120402
+ lastNonSpace = code;
120403
+ effects.consume(code);
120404
+ return scanToLineEnd;
120405
+ }
120406
+ effects.exit(types_types.data);
120407
+ return nok(code);
120408
+ }
120409
+ function scanToLineEnd(code) {
120410
+ if (code === null || markdownLineEnding(code)) {
120411
+ effects.exit(types_types.data);
120412
+ return lastNonSpace === codes.greaterThan ? ok(code) : nok(code);
120413
+ }
120414
+ if (!markdownSpace(code))
120415
+ lastNonSpace = code;
120416
+ effects.consume(code);
120417
+ return scanToLineEnd;
120418
+ }
120419
+ }
120293
120420
  /**
120294
120421
  * Micromark extension that tokenizes MDX-like components.
120295
120422
  *
@@ -120536,6 +120663,8 @@ const mdxishInlineMdxComponents = () => tree => {
120536
120663
 
120537
120664
 
120538
120665
 
120666
+ /** Matches a JSX attribute expression (e.g. `key={i}`) anywhere in a string. */
120667
+ const NESTED_ATTR_EXPRESSION_RE = /[\w-]+\s*=\s*\{/;
120539
120668
  /**
120540
120669
  * Reduce leading whitespace on all lines just enough to prevent
120541
120670
  * remark from treating indented content as code blocks (4+ spaces).
@@ -120695,10 +120824,16 @@ const mdx_blocks_mdxishMdxComponentBlocks = (opts = {}) => (tree, file) => {
120695
120824
  // inline, which is how ReadMe's custom components are modeled).
120696
120825
  if (!isPascal && parent.type === 'paragraph')
120697
120826
  return;
120698
- // Lowercase HTML tags are only eligible when the tokenizer claimed them
120699
- // for JSX-expression attributes. Plain HTML should stay as html nodes so
120827
+ // Lowercase HTML tags are eligible when they (or a descendant tag in their
120828
+ // content, e.g. a `.map()` body returning JSX with `key={i}`) carry a
120829
+ // JSX-expression attribute. Without this, a wrapper `<div>` with only plain
120830
+ // attributes (or none) swallows its whole nested block — including any
120831
+ // `style={{...}}`/`.map()` JSX inside it — as literal HTML text, which
120832
+ // rehype-raw's parse5 pass then garbles (it has no notion of JS expressions).
120833
+ // Plain HTML with no expressions anywhere stays as an html node so
120700
120834
  // rehype-raw handles it as normal.
120701
- if (!isPascal && !hasExpressionAttr(attributes))
120835
+ const hasNestedExpressionAttr = !selfClosing && NESTED_ATTR_EXPRESSION_RE.test(contentAfterTag);
120836
+ if (!isPascal && !hasExpressionAttr(attributes) && !hasNestedExpressionAttr)
120702
120837
  return;
120703
120838
  const closingTagStr = `</${tag}>`;
120704
120839
  // Case 1: Self-closing tag
@@ -121135,15 +121270,17 @@ const evalExpression = (expression, scope) => {
121135
121270
 
121136
121271
 
121137
121272
 
121273
+ /** True for an array containing at least one React element, e.g. the result of `.map()` returning JSX. */
121274
+ const isRenderableElementArray = (value) => Array.isArray(value) && value.some((external_react_default()).isValidElement);
121138
121275
  /** Given the type of the expression result, create the corresponding mdast node. */
121139
121276
  const createEvaluatedNode = (result, position) => {
121140
121277
  if (result === null || result === undefined) {
121141
121278
  return { type: 'text', value: '', position };
121142
121279
  }
121143
- else if (external_react_default().isValidElement(result)) {
121144
- // Convert react elements to its HTML representation
121145
- // This must come before the object check as this is a subset of it
121146
- return { type: 'html', value: (0,server_node/* renderToStaticMarkup */.qV)(result), position };
121280
+ else if (external_react_default().isValidElement(result) || isRenderableElementArray(result)) {
121281
+ // Convert react elements (or arrays of them, e.g. `.map()` returning JSX) to their HTML
121282
+ // representation. This must come before the object check as both are a subset of it.
121283
+ return { type: 'html', value: (0,server_node/* renderToStaticMarkup */.qV)(external_react_default().createElement((external_react_default()).Fragment, null, result)), position };
121147
121284
  }
121148
121285
  else if (typeof result === 'object') {
121149
121286
  return { type: 'text', value: JSON.stringify(result), position };
@@ -121183,6 +121320,47 @@ const evaluateExpressions = () => (tree, file) => {
121183
121320
  };
121184
121321
  /* harmony default export */ const evaluate_expressions = (evaluateExpressions);
121185
121322
 
121323
+ ;// ./processor/transform/mdxish/evaluate-style-block-expressions.ts
121324
+
121325
+
121326
+
121327
+ // Matches a standalone `<style ...>{ <expr> }</style>` block — the JSX shape MDX evaluates
121328
+ // (typically a template-literal-wrapped CSS string) but which mdxish otherwise leaves as
121329
+ // literal, invalid-CSS text (see CX-3646).
121330
+ const STYLE_EXPRESSION_RE = /^(<style\b[^>]*>)\s*\{([\s\S]*)\}\s*(<\/style>)$/i;
121331
+ /**
121332
+ * Evaluate the JSX expression wrapping a `<style>` tag's contents (typically a template
121333
+ * literal, e.g. `<style>{\`.foo { color: red; }\`}</style>`) so the tag ends up holding plain
121334
+ * CSS text instead of the literal `{`...`}` wrapper — which the browser treats as invalid CSS
121335
+ * and drops the entire stylesheet for.
121336
+ *
121337
+ * `<style>` is one of CommonMark's "raw text" HTML elements (along with script/pre/textarea):
121338
+ * its contents are captured verbatim as a single `html` mdast node with no inner tokenization,
121339
+ * so this expression never reaches the mdxFlowExpression tokenizer or `evaluateExpressions` —
121340
+ * it has to be matched and evaluated directly against the raw node's string value.
121341
+ *
121342
+ * Must run after `evaluateExports` (needs `file.data.mdxishScope`) and before `remarkRehype`
121343
+ * turns `html` mdast nodes into raw hast strings. Skipped in safeMode.
121344
+ */
121345
+ const evaluateStyleBlockExpressions = () => (tree, file) => {
121346
+ const scope = { ...file.data.mdxishScope, React: (external_react_default()) };
121347
+ lib_visit(tree, 'html', (node) => {
121348
+ const match = node.value.trim().match(STYLE_EXPRESSION_RE);
121349
+ if (!match)
121350
+ return;
121351
+ const [, openTag, expression, closeTag] = match;
121352
+ try {
121353
+ const css = evalExpression(expression, scope);
121354
+ node.value = `${openTag}${String(css)}${closeTag}`;
121355
+ }
121356
+ catch {
121357
+ // Evaluation failed — leave the node untouched so it round-trips as literal text.
121358
+ }
121359
+ });
121360
+ return tree;
121361
+ };
121362
+ /* harmony default export */ const evaluate_style_block_expressions = (evaluateStyleBlockExpressions);
121363
+
121186
121364
  ;// ./processor/transform/mdxish/heading-slugs.ts
121187
121365
 
121188
121366
 
@@ -124459,10 +124637,84 @@ function removeJSXComments(content) {
124459
124637
  return content.replace(JSX_COMMENT_REGEX, '');
124460
124638
  }
124461
124639
 
124640
+ ;// ./processor/transform/mdxish/style-object-to-css.ts
124641
+
124642
+ /**
124643
+ * CSS properties React treats as unitless — a bare number stays as-is instead of
124644
+ * getting `px` appended. Mirrors React DOM's `isUnitlessNumber` whitelist.
124645
+ * @see https://github.com/facebook/react/blob/main/packages/react-dom-bindings/src/shared/CSSProperty.js
124646
+ */
124647
+ const UNITLESS_CSS_PROPERTIES = new Set([
124648
+ 'animationIterationCount',
124649
+ 'aspectRatio',
124650
+ 'borderImageOutset',
124651
+ 'borderImageSlice',
124652
+ 'borderImageWidth',
124653
+ 'boxFlex',
124654
+ 'boxFlexGroup',
124655
+ 'boxOrdinalGroup',
124656
+ 'columnCount',
124657
+ 'columns',
124658
+ 'flex',
124659
+ 'flexGrow',
124660
+ 'flexPositive',
124661
+ 'flexShrink',
124662
+ 'flexNegative',
124663
+ 'flexOrder',
124664
+ 'gridArea',
124665
+ 'gridRow',
124666
+ 'gridRowEnd',
124667
+ 'gridRowSpan',
124668
+ 'gridRowStart',
124669
+ 'gridColumn',
124670
+ 'gridColumnEnd',
124671
+ 'gridColumnSpan',
124672
+ 'gridColumnStart',
124673
+ 'fontWeight',
124674
+ 'lineClamp',
124675
+ 'lineHeight',
124676
+ 'opacity',
124677
+ 'order',
124678
+ 'orphans',
124679
+ 'tabSize',
124680
+ 'widows',
124681
+ 'zIndex',
124682
+ 'zoom',
124683
+ 'fillOpacity',
124684
+ 'floodOpacity',
124685
+ 'stopOpacity',
124686
+ 'strokeDasharray',
124687
+ 'strokeDashoffset',
124688
+ 'strokeMiterlimit',
124689
+ 'strokeOpacity',
124690
+ 'strokeWidth',
124691
+ ]);
124692
+ /** Convert a camelCase (or CSS custom property) key into its kebab-case CSS name. */
124693
+ const cssPropertyName = (key) => (key.startsWith('--') ? key : key.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`));
124694
+ /** React appends `px` to non-zero numeric values, except unitless and custom properties. */
124695
+ const cssPropertyValue = (key, value) => typeof value === 'number' && value !== 0 && !key.startsWith('--') && !UNITLESS_CSS_PROPERTIES.has(key)
124696
+ ? `${value}px`
124697
+ : `${value}`;
124698
+ /**
124699
+ * True for a value that should be serialized as a style object rather than passed through
124700
+ * as an already-CSS string. React elements are excluded since they're valid objects too.
124701
+ */
124702
+ const style_object_to_css_isPlainObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value) && !external_react_default().isValidElement(value);
124703
+ /**
124704
+ * Serialize a React-style inline style object (`{ color: "red", fontSize: 12 }`) into a
124705
+ * CSS declaration string (`color:red;font-size:12px`), the shape hast/HTML expects for a
124706
+ * `style` attribute. Empty (`undefined`/`null`/`''`) entries are dropped.
124707
+ */
124708
+ const styleObjectToCssText = (style) => Object.entries(style)
124709
+ .filter(([, value]) => value !== undefined && value !== null && value !== '')
124710
+ .map(([key, value]) => `${cssPropertyName(key)}:${cssPropertyValue(key, value)}`)
124711
+ .join(';');
124712
+
124462
124713
  ;// ./processor/transform/mdxish/resolve-deferred-attribute-expression-props.ts
124463
124714
 
124464
124715
 
124465
124716
 
124717
+
124466
124718
  /**
124467
124719
  * Resolve attribute expressions that `mdxJsxElementHandler` deferred.
124468
124720
  *
@@ -124484,7 +124736,10 @@ const resolveDeferredAttributeExpressionProps = () => (tree, file) => {
124484
124736
  const properties = node.properties;
124485
124737
  Object.entries(deferredExpressions).forEach(([name, source]) => {
124486
124738
  try {
124487
- properties[name] = evalExpression(source, scope);
124739
+ const result = evalExpression(source, scope);
124740
+ // hast/HTML `style` is a plain CSS string; a `style={{...}}` expression evaluates to
124741
+ // a JS object, which must be serialized or it renders as the literal "[object Object]".
124742
+ properties[name] = name === 'style' && style_object_to_css_isPlainObject(result) ? styleObjectToCssText(result) : result;
124488
124743
  }
124489
124744
  catch {
124490
124745
  // Evaluation failed — fall back to the raw expression source so the attribute
@@ -124804,13 +125059,13 @@ function normalizeTableSeparator(content) {
124804
125059
  ;// ./processor/transform/mdxish/terminate-html-flow-blocks.ts
124805
125060
 
124806
125061
 
125062
+
124807
125063
  const STANDALONE_HTML_LINE_REGEX = /^(<[a-z][^<>]*>|<\/[a-z][^<>]*>)+\s*$/;
124808
125064
  const HTML_LINE_WITH_CONTENT_REGEX = /^<[a-z][^<>]*>.*<\/[a-z][^<>]*>(?:[^<]*)$/;
124809
- const terminate_html_flow_blocks_TABLE_STRUCTURE_TAGS = ['table', 'thead', 'tbody', 'tfoot', 'tr', 'td', 'th', 'caption', 'colgroup'];
124810
125065
  // Tags whose contents must be preserved as is, inserting a blank line after the
124811
125066
  // opener corrupts the payload.
124812
125067
  // htmlRawNames here refer to <pre>, <textarea>, <script>, <style>
124813
- const RAW_CONTENT_TAGS = [...htmlRawNames, ...terminate_html_flow_blocks_TABLE_STRUCTURE_TAGS];
125068
+ const RAW_CONTENT_TAGS = [...htmlRawNames, ...HTML_TABLE_STRUCTURE_TAGS];
124814
125069
  // The `(?=[\s/>])` lookahead avoids false matches on lookalike names like `<script-foo>`.
124815
125070
  const RAW_CONTENT_TAG_MATCHERS = RAW_CONTENT_TAGS.map(tag => ({
124816
125071
  open: new RegExp(`<${tag}(?=[\\s/>])[^>]*?(?<!/)>`, 'gi'),
@@ -125608,6 +125863,7 @@ function loadComponents() {
125608
125863
 
125609
125864
 
125610
125865
 
125866
+
125611
125867
 
125612
125868
 
125613
125869
  const defaultTransformers = [
@@ -125758,6 +126014,7 @@ function mdxish_mdxish(mdContent, opts = {}) {
125758
126014
  .use(safeMode ? undefined : evaluate_exports) // Evaluate `export const/function` and stash scope on file.data.mdxishScope
125759
126015
  .use(remarkBreaks) // Must precede evaluateExpressions to avoid splitting the \n in an evaluated template literal into a <br> node
125760
126016
  .use(safeMode ? undefined : evaluate_expressions) // Evaluate self-contained MDX expressions (e.g. `{1+1}`)
126017
+ .use(safeMode ? undefined : evaluate_style_block_expressions) // Evaluate `<style>{`...`}</style>` template literals into plain CSS
125761
126018
  .use(variables_code, { variables }) // Resolve <<...>> and {user.*} inside code and inline code nodes
125762
126019
  .use(lib_remarkRehype, { allowDangerousHtml: true, handlers: mdxComponentHandlers })
125763
126020
  .use(preserveBooleanProperties) // RehypeRaw converts boolean properties to empty strings