@readme/markdown 15.5.0 → 15.5.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/dist/main.js CHANGED
@@ -11423,7 +11423,7 @@ module.exports = function () {
11423
11423
 
11424
11424
  /***/ },
11425
11425
 
11426
- /***/ 912
11426
+ /***/ 1595
11427
11427
  (module, __webpack_exports__, __webpack_require__) {
11428
11428
 
11429
11429
  "use strict";
@@ -13106,6 +13106,8 @@ var parseOptions = function parseOptions() {
13106
13106
  * Pattern to match component tags (PascalCase or snake_case)
13107
13107
  */
13108
13108
  const componentTagPattern = /<(\/?[A-Z][A-Za-z0-9_]*)([^>]*?)(\/?)>/g;
13109
+ /** A bare component name, the same shape `componentTagPattern` accepts inside `<…>`. */
13110
+ const componentNamePattern = /^[A-Z][A-Za-z0-9_]*$/;
13109
13111
  /**
13110
13112
  * MDAST flow (block-level) content types that cannot be represented
13111
13113
  * inside GFM table cells. Used to decide whether a table should be
@@ -76740,6 +76742,14 @@ function syntax_createTokenize(mode) {
76740
76742
  }
76741
76743
  // ── Tag detection inside body ──────────────────────────────────────────
76742
76744
  function bodyLessThan(code) {
76745
+ // A second `<` means legacy variable syntax (`<<var>>`), not a tag: the name that
76746
+ // follows belongs to the variable. Counting it as a nested opener would leave the
76747
+ // body unbalanced and lose the claim on this block.
76748
+ if (code === codes.lessThan) {
76749
+ effects.consume(code);
76750
+ atLineStart = false;
76751
+ return bodyAngleRun;
76752
+ }
76743
76753
  if (code === codes.slash) {
76744
76754
  if (onOpenerLine)
76745
76755
  openerLineCloses += 1;
@@ -76764,6 +76774,14 @@ function syntax_createTokenize(mode) {
76764
76774
  atLineStart = false;
76765
76775
  return body(code);
76766
76776
  }
76777
+ function bodyAngleRun(code) {
76778
+ if (code === codes.lessThan) {
76779
+ effects.consume(code);
76780
+ return bodyAngleRun;
76781
+ }
76782
+ atLineStart = false;
76783
+ return body(code);
76784
+ }
76767
76785
  // ── Nested opening tag ─────────────────────────────────────────────────
76768
76786
  function nestedOpenTagName(code) {
76769
76787
  if (code !== null &&
@@ -76984,11 +77002,61 @@ function mdxComponent() {
76984
77002
  ;// ./lib/micromark/mdx-expression-lenient/syntax.ts
76985
77003
 
76986
77004
 
77005
+
77006
+ /**
77007
+ * The flow constructs allowed to end a multiline run, per parser. Everything but `<`: a
77008
+ * `<Component>` on its own line is the very shape the flow construct exists to keep together.
77009
+ */
77010
+ const interrupters = new WeakMap();
77011
+ const flowInterrupters = (flow) => {
77012
+ let record = interrupters.get(flow);
77013
+ if (!record) {
77014
+ record = Object.fromEntries(Object.entries(flow).filter(([code]) => Number(code) !== codes.lessThan));
77015
+ interrupters.set(flow, record);
77016
+ }
77017
+ return record;
77018
+ };
77019
+ /**
77020
+ * Lookahead run at every line ending of a flow run, mirroring the paragraph continuation check
77021
+ * in `micromark-core-commonmark/lib/content.js`: `ok` when the next line continues the run,
77022
+ * `nok` when it would start a fenced code block, heading, or thematic break — which the run
77023
+ * must then leave alone rather than swallow. A blank line (including whitespace-only) ends the
77024
+ * run too, unless `blankContinues`: see `nestedLineContinues`.
77025
+ */
77026
+ function tokenizeLineContinues(blankContinues) {
77027
+ return function tokenize(effects, ok, nok) {
77028
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
77029
+ const self = this;
77030
+ return start;
77031
+ function start(code) {
77032
+ effects.enter(types.lineEnding);
77033
+ effects.consume(code);
77034
+ effects.exit(types.lineEnding);
77035
+ return factorySpace(effects, prefixed, types.linePrefix);
77036
+ }
77037
+ // The paragraph rule also exempts lines indented 4+ columns (indented code can't interrupt);
77038
+ // mdxish disables indented code, so an indented line may still start a block here.
77039
+ function prefixed(code) {
77040
+ if (code === codes.eof)
77041
+ return nok(code);
77042
+ if (markdownLineEnding(code))
77043
+ return blankContinues ? ok(code) : nok(code);
77044
+ return effects.interrupt(flowInterrupters(self.parser.constructs.flow), nok, ok)(code);
77045
+ }
77046
+ };
77047
+ }
77048
+ const lineContinues = { tokenize: tokenizeLineContinues(false), partial: true };
77049
+ /**
77050
+ * Inside an open `{`, `(` or `[` a blank line is JavaScript formatting (between statements in a
77051
+ * callback, inside a JSX branch), so it may not end the run. At depth zero it's far more likely
77052
+ * a prose paragraph break after a stray `{`, and ending the run there keeps the paragraphs apart.
77053
+ */
77054
+ const nestedLineContinues = { tokenize: tokenizeLineContinues(true), partial: true };
76987
77055
  /**
76988
- * Lenient MDX text-expression tokenizer (agnostic / no acorn).
77056
+ * Lenient MDX expression tokenizer (agnostic / no acorn).
76989
77057
  *
76990
77058
  * Matches a balanced `{ ... }` run — tracking nested braces and spanning soft
76991
- * line breaks — and emits the standard `mdxTextExpression*` tokens so the
77059
+ * line breaks — and emits the standard `mdx*Expression*` tokens so the
76992
77060
  * upstream `mdxExpressionFromMarkdown()` builds the node.
76993
77061
  *
76994
77062
  * Reimplements the official micromark mdxExpression, but an unbalanced brace that
@@ -76996,70 +77064,115 @@ function mdxComponent() {
76996
77064
  * and the `{` renders as literal text, making the pipeline forgiving of stray
76997
77065
  * braces that upstream would hard-error on.
76998
77066
  */
76999
- function tokenizeTextExpression(effects, ok, nok) {
77000
- let depth = 0;
77001
- return start;
77002
- function start(code) {
77003
- if (code !== codes.leftCurlyBrace)
77004
- return nok(code);
77005
- effects.enter('mdxTextExpression');
77006
- effects.enter('mdxTextExpressionMarker');
77007
- effects.consume(code);
77008
- effects.exit('mdxTextExpressionMarker');
77009
- return before;
77010
- }
77011
- function before(code) {
77012
- if (code === codes.eof) {
77013
- effects.exit('mdxTextExpression');
77014
- return nok(code);
77067
+ function tokenizeExpression(variant) {
77068
+ const isFlow = variant === 'mdxFlowExpression';
77069
+ return function tokenize(effects, ok, nok) {
77070
+ // Read eagerly: micromark calls `tokenize` with a fresh context per attempt, so this is the
77071
+ // value for this attempt, and the state functions below don't receive `this` at all.
77072
+ const { interrupt } = this;
77073
+ let depth = 0;
77074
+ let nesting = 0;
77075
+ let multiline = false;
77076
+ return start;
77077
+ function start(code) {
77078
+ if (code !== codes.leftCurlyBrace)
77079
+ return nok(code);
77080
+ // Never end a paragraph. A `{` continuing the line above is prose or a magic-block body
77081
+ // (`[block:callout]` puts one on the next line), not a block of its own.
77082
+ if (isFlow && interrupt)
77083
+ return nok(code);
77084
+ effects.enter(variant);
77085
+ effects.enter(`${variant}Marker`);
77086
+ effects.consume(code);
77087
+ effects.exit(`${variant}Marker`);
77088
+ return before;
77015
77089
  }
77016
- if (markdownLineEnding(code)) {
77017
- effects.enter('lineEnding');
77090
+ function before(code) {
77091
+ if (code === codes.eof)
77092
+ return end(code);
77093
+ if (markdownLineEnding(code)) {
77094
+ // A text run lives in a paragraph micromark has already bounded. A flow run bounds
77095
+ // itself: a blank line or an interrupting block ends it, so a stray `{` can't claim
77096
+ // the rest of the document, and `{\n\n}` stays two literal paragraphs.
77097
+ if (!isFlow)
77098
+ return lineEnding(code);
77099
+ return effects.check(depth > 0 || nesting > 0 ? nestedLineContinues : lineContinues, lineEnding, end)(code);
77100
+ }
77101
+ if (code === codes.rightCurlyBrace && depth === 0)
77102
+ return close(code);
77103
+ effects.enter(`${variant}Chunk`);
77104
+ return inside(code);
77105
+ }
77106
+ function lineEnding(code) {
77107
+ multiline = true;
77108
+ effects.enter(types.lineEnding);
77018
77109
  effects.consume(code);
77019
- effects.exit('lineEnding');
77110
+ effects.exit(types.lineEnding);
77020
77111
  return before;
77021
77112
  }
77022
- if (code === codes.rightCurlyBrace && depth === 0) {
77023
- return close(code);
77113
+ function end(code) {
77114
+ effects.exit(variant);
77115
+ return nok(code);
77024
77116
  }
77025
- effects.enter('mdxTextExpressionChunk');
77026
- return inside(code);
77027
- }
77028
- function inside(code) {
77029
- if (code === codes.eof || markdownLineEnding(code)) {
77030
- effects.exit('mdxTextExpressionChunk');
77031
- return before(code);
77117
+ function inside(code) {
77118
+ if (code === codes.eof || markdownLineEnding(code)) {
77119
+ effects.exit(`${variant}Chunk`);
77120
+ return before(code);
77121
+ }
77122
+ if (code === codes.rightCurlyBrace && depth === 0) {
77123
+ effects.exit(`${variant}Chunk`);
77124
+ return close(code);
77125
+ }
77126
+ if (code === codes.leftCurlyBrace)
77127
+ depth += 1;
77128
+ else if (code === codes.rightCurlyBrace)
77129
+ depth -= 1;
77130
+ else if (code === codes.leftParenthesis || code === codes.leftSquareBracket)
77131
+ nesting += 1;
77132
+ else if (code === codes.rightParenthesis || code === codes.rightSquareBracket)
77133
+ nesting -= 1;
77134
+ effects.consume(code);
77135
+ return inside;
77032
77136
  }
77033
- if (code === codes.rightCurlyBrace && depth === 0) {
77034
- effects.exit('mdxTextExpressionChunk');
77035
- return close(code);
77137
+ function close(code) {
77138
+ effects.enter(`${variant}Marker`);
77139
+ effects.consume(code);
77140
+ effects.exit(`${variant}Marker`);
77141
+ effects.exit(variant);
77142
+ if (!isFlow)
77143
+ return ok;
77144
+ // A run that closes on the line it opened stays a text expression inside its paragraph:
77145
+ // claiming it as flow would strip the `<p>` every standalone `{expr}` line renders with.
77146
+ return multiline ? after : nok;
77036
77147
  }
77037
- if (code === codes.leftCurlyBrace)
77038
- depth += 1;
77039
- else if (code === codes.rightCurlyBrace)
77040
- depth -= 1;
77041
- effects.consume(code);
77042
- return inside;
77043
- }
77044
- function close(code) {
77045
- effects.enter('mdxTextExpressionMarker');
77046
- effects.consume(code);
77047
- effects.exit('mdxTextExpressionMarker');
77048
- effects.exit('mdxTextExpression');
77049
- return ok;
77050
- }
77148
+ function after(code) {
77149
+ return markdownSpace(code) ? factorySpace(effects, lineEnd, types.whitespace)(code) : lineEnd(code);
77150
+ }
77151
+ function lineEnd(code) {
77152
+ return code === codes.eof || markdownLineEnding(code) ? ok(code) : nok(code);
77153
+ }
77154
+ };
77051
77155
  }
77052
77156
  function mdxExpressionLenient() {
77053
77157
  return {
77158
+ flow: {
77159
+ [codes.leftCurlyBrace]: {
77160
+ name: 'mdxFlowExpression',
77161
+ tokenize: tokenizeExpression('mdxFlowExpression'),
77162
+ },
77163
+ },
77054
77164
  text: {
77055
77165
  [codes.leftCurlyBrace]: {
77056
77166
  name: 'mdxTextExpression',
77057
- tokenize: tokenizeTextExpression,
77167
+ tokenize: tokenizeExpression('mdxTextExpression'),
77058
77168
  },
77059
77169
  },
77060
77170
  };
77061
77171
  }
77062
77172
 
77173
+ ;// ./lib/micromark/mdx-expression-lenient/index.ts
77174
+
77175
+
77063
77176
  ;// ./lib/micromark/mdxish-extensions.ts
77064
77177
 
77065
77178
 
@@ -81629,6 +81742,17 @@ const tablesToJsx = () => tree => {
81629
81742
  ;// ./processor/transform/tailwind.tsx
81630
81743
 
81631
81744
 
81745
+
81746
+
81747
+ /** Whitespace-only text is layout between blocks, not inline flow. */
81748
+ const isInlineContent = (node) => phrasing(node) && !(node.type === 'text' && node.value.trim() === '');
81749
+ /**
81750
+ * Whether the child at `index` sits in inline flow: its parent only allows
81751
+ * phrasing content (e.g. a paragraph), or it is flanked by phrasing siblings
81752
+ * (e.g. `<div>text <Image /> text</div>`, whose children skip the paragraph).
81753
+ */
81754
+ const isInlineInContext = (index, parent) => INLINE_ONLY_PARENT_TYPES.has(parent.type) ||
81755
+ parent.children.some((sibling, siblingIndex) => siblingIndex !== index && isInlineContent(sibling));
81632
81756
  const injectTailwindRoot = ({ components = {} }) => (node, index, parent) => {
81633
81757
  if (!('name' in node))
81634
81758
  return;
@@ -81637,7 +81761,7 @@ const injectTailwindRoot = ({ components = {} }) => (node, index, parent) => {
81637
81761
  if (!('children' in parent))
81638
81762
  return;
81639
81763
  const attrs = {
81640
- flow: node.type === 'mdxJsxFlowElement',
81764
+ flow: node.type === 'mdxJsxFlowElement' && !isInlineInContext(index, parent),
81641
81765
  };
81642
81766
  const wrapper = {
81643
81767
  type: node.type,
@@ -104125,6 +104249,111 @@ const parseTag = (value, opts = {}) => {
104125
104249
  };
104126
104250
  };
104127
104251
 
104252
+ ;// ./processor/transform/mdxish/variables-text.ts
104253
+
104254
+
104255
+ /**
104256
+ * Matches {user.<field>} patterns:
104257
+ * - {user.name}
104258
+ * - {user.email}
104259
+ * - {user['field']}
104260
+ * - {user["field"]}
104261
+ *
104262
+ * Captures the field name in group 1 (dot notation) or group 2 (bracket notation)
104263
+ */
104264
+ const USER_VAR_REGEX = /\{user\.(\w+)\}|\{user\[['"](\w+)['"]\]\}/g;
104265
+ /**
104266
+ * The lone `{user.<field>}` reference an expression node holds, or `null` when the node isn't an
104267
+ * expression or holds anything else (`{1 + 1}`, a CSS-looking body, two references in one brace).
104268
+ * Exported so promotion gates can tell a variable reference apart from an expression that must
104269
+ * stay literal.
104270
+ */
104271
+ function soleUserVariableExpression(node) {
104272
+ if (node.type !== 'mdxFlowExpression' && node.type !== 'mdxTextExpression')
104273
+ return null;
104274
+ const { value } = node;
104275
+ const wrapped = `{${(value ?? '').trim()}}`;
104276
+ const matches = [...wrapped.matchAll(USER_VAR_REGEX)];
104277
+ if (matches.length !== 1 || matches[0][0] !== wrapped)
104278
+ return null;
104279
+ return { name: matches[0][1] || matches[0][2], wrapped };
104280
+ }
104281
+ function makeVariableNode(varName, rawValue) {
104282
+ return {
104283
+ type: NodeTypes.variable,
104284
+ data: {
104285
+ hName: 'Variable',
104286
+ hProperties: { name: varName },
104287
+ },
104288
+ value: rawValue,
104289
+ };
104290
+ }
104291
+ /**
104292
+ * A remark plugin that parses {user.<field>} patterns from text nodes and
104293
+ * mdx expression nodes, creating Variable nodes for runtime resolution.
104294
+ *
104295
+ * Handles:
104296
+ * - `text` nodes: when safeMode is true or after expression evaluation
104297
+ * - `mdxTextExpression` nodes: when mdxExpression has parsed {user.*} before evaluation
104298
+ * - `mdxFlowExpression` nodes: when {user.*} appears on its own line (e.g. inside JSX table cells)
104299
+ *
104300
+ * Supports any user field: name, email, email_verified, exp, iat, etc.
104301
+ */
104302
+ function visitExpressionNode(node, index, parent) {
104303
+ if (index === undefined || !parent)
104304
+ return;
104305
+ const match = soleUserVariableExpression(node);
104306
+ if (!match)
104307
+ return;
104308
+ parent.children.splice(index, 1, makeVariableNode(match.name, match.wrapped));
104309
+ }
104310
+ const variablesTextTransformer = () => tree => {
104311
+ visit(tree, 'mdxTextExpression', (node, index, parent) => {
104312
+ visitExpressionNode(node, index, parent);
104313
+ });
104314
+ visit(tree, 'mdxFlowExpression', (node, index, parent) => {
104315
+ visitExpressionNode(node, index, parent);
104316
+ });
104317
+ visit(tree, 'text', (node, index, parent) => {
104318
+ if (index === undefined || !parent)
104319
+ return;
104320
+ // Skip if inside inline code
104321
+ if (parent.type === 'inlineCode')
104322
+ return;
104323
+ const text = node.value;
104324
+ if (typeof text !== 'string' || !text.trim())
104325
+ return;
104326
+ if (!text.includes('{user.') && !text.includes('{user['))
104327
+ return;
104328
+ const matches = [...text.matchAll(USER_VAR_REGEX)];
104329
+ if (matches.length === 0)
104330
+ return;
104331
+ const parts = [];
104332
+ let lastIndex = 0;
104333
+ matches.forEach(match => {
104334
+ const matchIndex = match.index ?? 0;
104335
+ // Add text before the match
104336
+ if (matchIndex > lastIndex) {
104337
+ parts.push({ type: 'text', value: text.slice(lastIndex, matchIndex) });
104338
+ }
104339
+ // Extract variable name from either capture group (dot or bracket notation)
104340
+ const varName = match[1] || match[2];
104341
+ parts.push(makeVariableNode(varName, match[0]));
104342
+ lastIndex = matchIndex + match[0].length;
104343
+ });
104344
+ // Add remaining text after last match
104345
+ if (lastIndex < text.length) {
104346
+ parts.push({ type: 'text', value: text.slice(lastIndex) });
104347
+ }
104348
+ // Replace node with parts
104349
+ if (parts.length > 1 || (parts.length === 1 && parts[0].type !== 'text')) {
104350
+ parent.children.splice(index, 1, ...parts);
104351
+ }
104352
+ });
104353
+ return tree;
104354
+ };
104355
+ /* harmony default export */ const variables_text = (variablesTextTransformer);
104356
+
104128
104357
  ;// ./processor/transform/mdxish/components/utils.ts
104129
104358
 
104130
104359
 
@@ -104202,19 +104431,15 @@ const NON_PROMOTABLE_PLAIN_TAGS = new Set([...htmlRawNames, ...tableTags, 'table
104202
104431
  const NESTED_TABLE_RE = /<table[\s>]/i;
104203
104432
  const isMarkdownPromotableHtmlTag = (tag) => !NON_PROMOTABLE_PLAIN_TAGS.has(tag);
104204
104433
  // Expression nodes count as plain so `<div>{1+1}</div>` keeps its current
104205
- // literal-brace behavior; variables/glossary already resolve inside raw html.
104206
- const PLAIN_CONTENT_TYPES = new Set([
104207
- 'paragraph',
104208
- 'text',
104209
- 'html',
104210
- 'mdxTextExpression',
104211
- 'mdxFlowExpression',
104212
- NodeTypes.variable,
104213
- NodeTypes.glossary,
104214
- ]);
104434
+ // literal-brace behavior — authors put CSS-shaped braces in these bodies, and only
104435
+ // a reference that names a real variable may resolve.
104436
+ const PLAIN_CONTENT_TYPES = new Set(['paragraph', 'text', 'html', 'mdxTextExpression', 'mdxFlowExpression']);
104215
104437
  // Promoting plain HTML is only worth bypassing rehype-raw's parse5 pass when
104216
- // the body parses into an actual markdown construct.
104438
+ // the body parses into an actual markdown construct. Variables and glossary terms
104439
+ // count: parse5 renders `<<var>>` as a stray `<` plus a `<var>` tag, so a body that
104440
+ // holds nothing else still has to be promoted to resolve.
104217
104441
  const containsMarkdownConstruct = (nodes) => nodes.some(node => !PLAIN_CONTENT_TYPES.has(node.type) ||
104442
+ soleUserVariableExpression(node) !== null ||
104218
104443
  ('children' in node && Array.isArray(node.children) && containsMarkdownConstruct(node.children)));
104219
104444
  /**
104220
104445
  * Index of the `</tag>` that balances the already-consumed opening tag (the
@@ -104980,6 +105205,7 @@ const evaluateExports = () => (tree, file) => {
104980
105205
 
104981
105206
 
104982
105207
 
105208
+
104983
105209
  const parseExpression = (expression) => jsxAcornParser.parse(expression, { ecmaVersion: 'latest', sourceType: 'module' });
104984
105210
  /**
104985
105211
  * Recursively report whether an estree value contains any JSX element or fragment node.
@@ -104996,22 +105222,16 @@ const containsJsxNode = (value) => {
104996
105222
  return true;
104997
105223
  return Object.values(value).some(containsJsxNode);
104998
105224
  };
104999
- /** Read the component name off a JSX element name node (`Foo`, `Foo.Bar`, `foo:Bar`). */
105000
105225
  const jsxElementName = (name) => {
105001
105226
  if (name === null || typeof name !== 'object')
105002
105227
  return undefined;
105003
105228
  const node = name;
105004
- if (node.type === 'JSXIdentifier')
105005
- return typeof node.name === 'string' ? node.name : undefined;
105006
- // `<Foo.Bar/>` and `<foo:Bar/>` resolve through their leftmost part.
105007
- if (node.type === 'JSXMemberExpression')
105008
- return jsxElementName(node.object);
105009
- if (node.type === 'JSXNamespacedName')
105010
- return jsxElementName(node.namespace);
105011
- return undefined;
105229
+ if (node.type !== 'JSXIdentifier' || typeof node.name !== 'string')
105230
+ return undefined;
105231
+ return componentNamePattern.test(node.name) ? node.name : undefined;
105012
105232
  };
105013
105233
  /**
105014
- * Collect the capitalized names an expression uses as JSX tags. Parsed rather than pattern
105234
+ * Collect the component names an expression uses as JSX tags. Parsed rather than pattern
105015
105235
  * matched: `{count < Max ? <Foo/> : <Bar/>}` puts a capitalized name straight after a `<` without
105016
105236
  * it being a tag, and only the parser can tell the two apart. Unparseable input yields nothing —
105017
105237
  * evaluation is about to throw on it anyway.
@@ -105035,8 +105255,7 @@ const jsxComponentNames = (expression) => {
105035
105255
  const node = value;
105036
105256
  if (node.type === 'JSXOpeningElement') {
105037
105257
  const name = jsxElementName(node.name);
105038
- // Lowercase tags compile to a string type, never a variable reference.
105039
- if (name && /^[A-Z]/.test(name))
105258
+ if (name)
105040
105259
  names.add(name);
105041
105260
  }
105042
105261
  Object.values(node).forEach(walk);
@@ -105195,7 +105414,8 @@ function renderFallbackHtml(element) {
105195
105414
  const rawNode = { type: 'raw', value: __WEBPACK_CJS_EXPORT_renderToStaticMarkup__(element) };
105196
105415
  return [rawNode];
105197
105416
  }
105198
- catch {
105417
+ catch (error) {
105418
+ console.warn('[mdxish] Could not render expression result:', error);
105199
105419
  return [];
105200
105420
  }
105201
105421
  }
@@ -105253,6 +105473,7 @@ function reactElementToHast(node) {
105253
105473
 
105254
105474
 
105255
105475
 
105476
+
105256
105477
  /**
105257
105478
  * Bind the components a given expression actually uses as tags. Scoped per expression on purpose:
105258
105479
  * binding the whole hash would shadow same-named globals (a `math` component would break
@@ -105281,17 +105502,28 @@ const isRenderable = (value) => {
105281
105502
  return true;
105282
105503
  return Array.isArray(value) && value.some(isRenderable);
105283
105504
  };
105505
+ const isPhrasingTag = (tagName) => lib_phrasing({ type: 'element', tagName, properties: {}, children: [] });
105284
105506
  /**
105285
- * Whether an expression evaluated to block-level content. Components count as block-level
105286
- * unless they're on the inline list, matching the assumption the rest of the component
105287
- * pipeline makes.
105507
+ * Whether an expression evaluated to block-level content. A capitalized tag is a component,
105508
+ * block-level unless it's on the inline list (so a `Table` component isn't mistaken for an inline
105509
+ * `<table>`); a lowercase tag is HTML, block-level unless it's phrasing content. A `raw` node is
105510
+ * HTML from `renderFallbackHtml`, so it's parsed and judged by the same rule.
105288
105511
  */
105289
105512
  const isBlockResult = (children) => children.some(child => {
105513
+ if (child.type === 'raw')
105514
+ return isBlockResult(fromHtml(child.value, { fragment: true }).children);
105290
105515
  if (child.type !== 'element' && child.type !== 'mdx-jsx')
105291
105516
  return false;
105292
105517
  const { tagName } = child;
105293
- return !STANDARD_HTML_TAGS.has(tagName.toLowerCase()) && !INLINE_COMPONENT_TAGS.has(tagName);
105518
+ return /^[A-Z]/.test(tagName) ? !INLINE_COMPONENT_TAGS.has(tagName) : !isPhrasingTag(tagName);
105294
105519
  });
105520
+ const JSX_ELEMENT_TYPES = new Set(['mdxJsxFlowElement', 'mdxJsxTextElement']);
105521
+ const wrapInParagraph = (child) => ({
105522
+ type: 'paragraph',
105523
+ children: [child],
105524
+ position: child.position,
105525
+ });
105526
+ const placeText = (text, needsBlock) => (needsBlock ? wrapInParagraph(text) : text);
105295
105527
  /** Turn a non-renderable evaluation result into a text node. */
105296
105528
  const createTextNode = (result, position) => {
105297
105529
  if (result === null || result === undefined)
@@ -105327,6 +105559,7 @@ const evaluateExpressions = ({ components, variables } = {}) => (tree, file) =>
105327
105559
  const expression = value?.trim();
105328
105560
  if (!expression)
105329
105561
  return;
105562
+ const needsBlock = expressionNode.type === 'mdxFlowExpression' && !JSX_ELEMENT_TYPES.has(parent.type);
105330
105563
  try {
105331
105564
  const scope = { ...componentScope(expression, components ?? {}), ...baseScope };
105332
105565
  const result = evalExpression(expression, scope);
@@ -105335,10 +105568,17 @@ const evaluateExpressions = ({ components, variables } = {}) => (tree, file) =>
105335
105568
  // passes through rehypeRaw/parse5 step later in the pipeline. This ensures that the
105336
105569
  // expression result is not parsed by parse5 and fragmenting the nesting that is valid JSX
105337
105570
  // but invalid HTML — e.g. an `<a>` wrapping `<a>`.
105338
- expressionNode.data = { ...expressionNode.data, hChildren: reactElementToHast(result) };
105571
+ const hChildren = reactElementToHast(result);
105572
+ expressionNode.data = { ...expressionNode.data, hChildren };
105573
+ // An inline result in a block slot renders inside a `<p>`, as its one-line form does.
105574
+ // Retyping it as a text expression makes it legal paragraph content.
105575
+ if (needsBlock && !isBlockResult(hChildren)) {
105576
+ const inline = { ...expressionNode, type: 'mdxTextExpression' };
105577
+ parent.children.splice(index, 1, wrapInParagraph(inline));
105578
+ }
105339
105579
  }
105340
105580
  else {
105341
- parent.children.splice(index, 1, createTextNode(result, position));
105581
+ parent.children.splice(index, 1, placeText(createTextNode(result, position), needsBlock));
105342
105582
  }
105343
105583
  }
105344
105584
  catch (_error) {
@@ -105346,7 +105586,8 @@ const evaluateExpressions = ({ components, variables } = {}) => (tree, file) =>
105346
105586
  // parser treats contents as code, so backslash escapes aren't applied;
105347
105587
  // restore them here so e.g. `{\!}` round-trips to `{!}`.
105348
105588
  const processed = expression.replace(/\\([!-/:-@[-`{-~])/g, '$1');
105349
- parent.children.splice(index, 1, { type: 'text', value: `{${processed}}`, position });
105589
+ const literal = { type: 'text', value: `{${processed}}`, position };
105590
+ parent.children.splice(index, 1, placeText(literal, needsBlock));
105350
105591
  }
105351
105592
  });
105352
105593
  // A text expression is parsed inside a paragraph, but its result can be block content: a
@@ -107059,12 +107300,20 @@ function removeJSXComments(content) {
107059
107300
  const OPAQUE_CONTAINERS = new Set(['pre', 'textarea', 'htmlblock']);
107060
107301
  /** The missing-slash typo shape: an attribute-less, non-self-closing opener. */
107061
107302
  const BARE_OPENER_SOURCE_RE = /^<[A-Za-z]+\s*>$/;
107062
- /** The opener shares its line with nothing but whitespace. */
107063
- const isAloneOnLine = (content, start, end) => {
107064
- const lineStart = content.lastIndexOf('\n', start - 1) + 1;
107303
+ /** The opener is immediately preceded by a complete tag, so the prefix is markup and not prose. */
107304
+ const PREFIX_ENDS_WITH_TAG_RE = /<[^<>]*>\s*$/;
107305
+ /**
107306
+ * The opener finishes its line, preceded by nothing but whitespace or by markup.
107307
+ * Requiring markup (not merely "something") keeps a markdown block prefix such as
107308
+ * a `>` quote marker from qualifying, while allowing the closer to trail a cell or
107309
+ * `</thead>` on a single-line table.
107310
+ */
107311
+ const endsLineAfterMarkup = (content, start, end) => {
107065
107312
  const lineEnd = content.indexOf('\n', end);
107066
- return (content.slice(lineStart, start).trim() === '' &&
107067
- content.slice(end, lineEnd === -1 ? content.length : lineEnd).trim() === '');
107313
+ if (content.slice(end, lineEnd === -1 ? content.length : lineEnd).trim() !== '')
107314
+ return false;
107315
+ const prefix = content.slice(content.lastIndexOf('\n', start - 1) + 1, start);
107316
+ return prefix.trim() === '' || PREFIX_ENDS_WITH_TAG_RE.test(prefix);
107068
107317
  };
107069
107318
  /**
107070
107319
  * Rewrites a bare `<table>` that is almost certainly a missing-slash closer
@@ -107072,7 +107321,7 @@ const isAloneOnLine = (content, start, end) => {
107072
107321
  * Customer docs (CX-3850) close tables with a second bare opener; without this,
107073
107322
  * the never-terminated HTML flow block swallows the markdown that follows.
107074
107323
  *
107075
- * A candidate is a bare opener alone on its line while a table is already open.
107324
+ * A candidate is a bare opener that finishes its line while a table is already open.
107076
107325
  * It is judged a typo only when its element ends implicitly (no explicit
107077
107326
  * `</table>`) without ever acquiring table-structure children — so genuine
107078
107327
  * nested tables, whatever precedes their rows, are left alone.
@@ -107101,7 +107350,7 @@ function repairMistakenTableClosers(content) {
107101
107350
  start,
107102
107351
  end,
107103
107352
  hasStructureChild: false,
107104
- isCandidate: stack.length > 0 && BARE_OPENER_SOURCE_RE.test(source) && isAloneOnLine(content, start, end),
107353
+ isCandidate: stack.length > 0 && BARE_OPENER_SOURCE_RE.test(source) && endsLineAfterMarkup(content, start, end),
107105
107354
  });
107106
107355
  }
107107
107356
  else if (opaqueDepth === 0 && tableTags.has(lower) && stack.length > 0) {
@@ -107541,97 +107790,6 @@ const variablesCodeResolver = ({ variables } = {}) => tree => {
107541
107790
  };
107542
107791
  /* harmony default export */ const variables_code = (variablesCodeResolver);
107543
107792
 
107544
- ;// ./processor/transform/mdxish/variables-text.ts
107545
-
107546
-
107547
- /**
107548
- * Matches {user.<field>} patterns:
107549
- * - {user.name}
107550
- * - {user.email}
107551
- * - {user['field']}
107552
- * - {user["field"]}
107553
- *
107554
- * Captures the field name in group 1 (dot notation) or group 2 (bracket notation)
107555
- */
107556
- const USER_VAR_REGEX = /\{user\.(\w+)\}|\{user\[['"](\w+)['"]\]\}/g;
107557
- function makeVariableNode(varName, rawValue) {
107558
- return {
107559
- type: NodeTypes.variable,
107560
- data: {
107561
- hName: 'Variable',
107562
- hProperties: { name: varName },
107563
- },
107564
- value: rawValue,
107565
- };
107566
- }
107567
- /**
107568
- * A remark plugin that parses {user.<field>} patterns from text nodes and
107569
- * mdx expression nodes, creating Variable nodes for runtime resolution.
107570
- *
107571
- * Handles:
107572
- * - `text` nodes: when safeMode is true or after expression evaluation
107573
- * - `mdxTextExpression` nodes: when mdxExpression has parsed {user.*} before evaluation
107574
- * - `mdxFlowExpression` nodes: when {user.*} appears on its own line (e.g. inside JSX table cells)
107575
- *
107576
- * Supports any user field: name, email, email_verified, exp, iat, etc.
107577
- */
107578
- function visitExpressionNode(node, index, parent) {
107579
- if (index === undefined || !parent)
107580
- return;
107581
- const wrapped = `{${(node.value ?? '').trim()}}`;
107582
- const matches = [...wrapped.matchAll(USER_VAR_REGEX)];
107583
- if (matches.length !== 1)
107584
- return;
107585
- const varName = matches[0][1] || matches[0][2];
107586
- parent.children.splice(index, 1, makeVariableNode(varName, wrapped));
107587
- }
107588
- const variablesTextTransformer = () => tree => {
107589
- visit(tree, 'mdxTextExpression', (node, index, parent) => {
107590
- visitExpressionNode(node, index, parent);
107591
- });
107592
- visit(tree, 'mdxFlowExpression', (node, index, parent) => {
107593
- visitExpressionNode(node, index, parent);
107594
- });
107595
- visit(tree, 'text', (node, index, parent) => {
107596
- if (index === undefined || !parent)
107597
- return;
107598
- // Skip if inside inline code
107599
- if (parent.type === 'inlineCode')
107600
- return;
107601
- const text = node.value;
107602
- if (typeof text !== 'string' || !text.trim())
107603
- return;
107604
- if (!text.includes('{user.') && !text.includes('{user['))
107605
- return;
107606
- const matches = [...text.matchAll(USER_VAR_REGEX)];
107607
- if (matches.length === 0)
107608
- return;
107609
- const parts = [];
107610
- let lastIndex = 0;
107611
- matches.forEach(match => {
107612
- const matchIndex = match.index ?? 0;
107613
- // Add text before the match
107614
- if (matchIndex > lastIndex) {
107615
- parts.push({ type: 'text', value: text.slice(lastIndex, matchIndex) });
107616
- }
107617
- // Extract variable name from either capture group (dot or bracket notation)
107618
- const varName = match[1] || match[2];
107619
- parts.push(makeVariableNode(varName, match[0]));
107620
- lastIndex = matchIndex + match[0].length;
107621
- });
107622
- // Add remaining text after last match
107623
- if (lastIndex < text.length) {
107624
- parts.push({ type: 'text', value: text.slice(lastIndex) });
107625
- }
107626
- // Replace node with parts
107627
- if (parts.length > 1 || (parts.length === 1 && parts[0].type !== 'text')) {
107628
- parent.children.splice(index, 1, ...parts);
107629
- }
107630
- });
107631
- return tree;
107632
- };
107633
- /* harmony default export */ const variables_text = (variablesTextTransformer);
107634
-
107635
107793
  ;// ./lib/utils/mdxish/mdxish-load-components.ts
107636
107794
 
107637
107795
 
@@ -108833,7 +108991,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
108833
108991
  /******/ // startup
108834
108992
  /******/ // Load entry module and return exports
108835
108993
  /******/ // This entry module used 'module' so it can't be inlined
108836
- /******/ let __webpack_exports__ = __webpack_require__(912);
108994
+ /******/ let __webpack_exports__ = __webpack_require__(1595);
108837
108995
  /******/
108838
108996
  /******/ return __webpack_exports__;
108839
108997
  /******/ })()