@readme/markdown 15.5.1 → 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.
@@ -2,6 +2,8 @@
2
2
  * Pattern to match component tags (PascalCase or snake_case)
3
3
  */
4
4
  export declare const componentTagPattern: RegExp;
5
+ /** A bare component name, the same shape `componentTagPattern` accepts inside `<…>`. */
6
+ export declare const componentNamePattern: RegExp;
5
7
  /**
6
8
  * MDAST flow (block-level) content types that cannot be represented
7
9
  * inside GFM table cells. Used to decide whether a table should be
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Collect the capitalized names an expression uses as JSX tags. Parsed rather than pattern
2
+ * Collect the component names an expression uses as JSX tags. Parsed rather than pattern
3
3
  * matched: `{count < Max ? <Foo/> : <Bar/>}` puts a capitalized name straight after a `<` without
4
4
  * it being a tag, and only the parser can tell the two apart. Unparseable input yields nothing —
5
5
  * evaluation is about to throw on it anyway.
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
+
76987
77006
  /**
76988
- * Lenient MDX text-expression tokenizer (agnostic / no acorn).
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 };
77055
+ /**
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
 
@@ -104136,6 +104249,111 @@ const parseTag = (value, opts = {}) => {
104136
104249
  };
104137
104250
  };
104138
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
+
104139
104357
  ;// ./processor/transform/mdxish/components/utils.ts
104140
104358
 
104141
104359
 
@@ -104213,19 +104431,15 @@ const NON_PROMOTABLE_PLAIN_TAGS = new Set([...htmlRawNames, ...tableTags, 'table
104213
104431
  const NESTED_TABLE_RE = /<table[\s>]/i;
104214
104432
  const isMarkdownPromotableHtmlTag = (tag) => !NON_PROMOTABLE_PLAIN_TAGS.has(tag);
104215
104433
  // Expression nodes count as plain so `<div>{1+1}</div>` keeps its current
104216
- // literal-brace behavior; variables/glossary already resolve inside raw html.
104217
- const PLAIN_CONTENT_TYPES = new Set([
104218
- 'paragraph',
104219
- 'text',
104220
- 'html',
104221
- 'mdxTextExpression',
104222
- 'mdxFlowExpression',
104223
- NodeTypes.variable,
104224
- NodeTypes.glossary,
104225
- ]);
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']);
104226
104437
  // Promoting plain HTML is only worth bypassing rehype-raw's parse5 pass when
104227
- // 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.
104228
104441
  const containsMarkdownConstruct = (nodes) => nodes.some(node => !PLAIN_CONTENT_TYPES.has(node.type) ||
104442
+ soleUserVariableExpression(node) !== null ||
104229
104443
  ('children' in node && Array.isArray(node.children) && containsMarkdownConstruct(node.children)));
104230
104444
  /**
104231
104445
  * Index of the `</tag>` that balances the already-consumed opening tag (the
@@ -104991,6 +105205,7 @@ const evaluateExports = () => (tree, file) => {
104991
105205
 
104992
105206
 
104993
105207
 
105208
+
104994
105209
  const parseExpression = (expression) => jsxAcornParser.parse(expression, { ecmaVersion: 'latest', sourceType: 'module' });
104995
105210
  /**
104996
105211
  * Recursively report whether an estree value contains any JSX element or fragment node.
@@ -105007,22 +105222,16 @@ const containsJsxNode = (value) => {
105007
105222
  return true;
105008
105223
  return Object.values(value).some(containsJsxNode);
105009
105224
  };
105010
- /** Read the component name off a JSX element name node (`Foo`, `Foo.Bar`, `foo:Bar`). */
105011
105225
  const jsxElementName = (name) => {
105012
105226
  if (name === null || typeof name !== 'object')
105013
105227
  return undefined;
105014
105228
  const node = name;
105015
- if (node.type === 'JSXIdentifier')
105016
- return typeof node.name === 'string' ? node.name : undefined;
105017
- // `<Foo.Bar/>` and `<foo:Bar/>` resolve through their leftmost part.
105018
- if (node.type === 'JSXMemberExpression')
105019
- return jsxElementName(node.object);
105020
- if (node.type === 'JSXNamespacedName')
105021
- return jsxElementName(node.namespace);
105022
- return undefined;
105229
+ if (node.type !== 'JSXIdentifier' || typeof node.name !== 'string')
105230
+ return undefined;
105231
+ return componentNamePattern.test(node.name) ? node.name : undefined;
105023
105232
  };
105024
105233
  /**
105025
- * 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
105026
105235
  * matched: `{count < Max ? <Foo/> : <Bar/>}` puts a capitalized name straight after a `<` without
105027
105236
  * it being a tag, and only the parser can tell the two apart. Unparseable input yields nothing —
105028
105237
  * evaluation is about to throw on it anyway.
@@ -105046,8 +105255,7 @@ const jsxComponentNames = (expression) => {
105046
105255
  const node = value;
105047
105256
  if (node.type === 'JSXOpeningElement') {
105048
105257
  const name = jsxElementName(node.name);
105049
- // Lowercase tags compile to a string type, never a variable reference.
105050
- if (name && /^[A-Z]/.test(name))
105258
+ if (name)
105051
105259
  names.add(name);
105052
105260
  }
105053
105261
  Object.values(node).forEach(walk);
@@ -105206,7 +105414,8 @@ function renderFallbackHtml(element) {
105206
105414
  const rawNode = { type: 'raw', value: __WEBPACK_CJS_EXPORT_renderToStaticMarkup__(element) };
105207
105415
  return [rawNode];
105208
105416
  }
105209
- catch {
105417
+ catch (error) {
105418
+ console.warn('[mdxish] Could not render expression result:', error);
105210
105419
  return [];
105211
105420
  }
105212
105421
  }
@@ -105264,6 +105473,7 @@ function reactElementToHast(node) {
105264
105473
 
105265
105474
 
105266
105475
 
105476
+
105267
105477
  /**
105268
105478
  * Bind the components a given expression actually uses as tags. Scoped per expression on purpose:
105269
105479
  * binding the whole hash would shadow same-named globals (a `math` component would break
@@ -105292,17 +105502,28 @@ const isRenderable = (value) => {
105292
105502
  return true;
105293
105503
  return Array.isArray(value) && value.some(isRenderable);
105294
105504
  };
105505
+ const isPhrasingTag = (tagName) => lib_phrasing({ type: 'element', tagName, properties: {}, children: [] });
105295
105506
  /**
105296
- * Whether an expression evaluated to block-level content. Components count as block-level
105297
- * unless they're on the inline list, matching the assumption the rest of the component
105298
- * 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.
105299
105511
  */
105300
105512
  const isBlockResult = (children) => children.some(child => {
105513
+ if (child.type === 'raw')
105514
+ return isBlockResult(fromHtml(child.value, { fragment: true }).children);
105301
105515
  if (child.type !== 'element' && child.type !== 'mdx-jsx')
105302
105516
  return false;
105303
105517
  const { tagName } = child;
105304
- 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);
105519
+ });
105520
+ const JSX_ELEMENT_TYPES = new Set(['mdxJsxFlowElement', 'mdxJsxTextElement']);
105521
+ const wrapInParagraph = (child) => ({
105522
+ type: 'paragraph',
105523
+ children: [child],
105524
+ position: child.position,
105305
105525
  });
105526
+ const placeText = (text, needsBlock) => (needsBlock ? wrapInParagraph(text) : text);
105306
105527
  /** Turn a non-renderable evaluation result into a text node. */
105307
105528
  const createTextNode = (result, position) => {
105308
105529
  if (result === null || result === undefined)
@@ -105338,6 +105559,7 @@ const evaluateExpressions = ({ components, variables } = {}) => (tree, file) =>
105338
105559
  const expression = value?.trim();
105339
105560
  if (!expression)
105340
105561
  return;
105562
+ const needsBlock = expressionNode.type === 'mdxFlowExpression' && !JSX_ELEMENT_TYPES.has(parent.type);
105341
105563
  try {
105342
105564
  const scope = { ...componentScope(expression, components ?? {}), ...baseScope };
105343
105565
  const result = evalExpression(expression, scope);
@@ -105346,10 +105568,17 @@ const evaluateExpressions = ({ components, variables } = {}) => (tree, file) =>
105346
105568
  // passes through rehypeRaw/parse5 step later in the pipeline. This ensures that the
105347
105569
  // expression result is not parsed by parse5 and fragmenting the nesting that is valid JSX
105348
105570
  // but invalid HTML — e.g. an `<a>` wrapping `<a>`.
105349
- 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
+ }
105350
105579
  }
105351
105580
  else {
105352
- parent.children.splice(index, 1, createTextNode(result, position));
105581
+ parent.children.splice(index, 1, placeText(createTextNode(result, position), needsBlock));
105353
105582
  }
105354
105583
  }
105355
105584
  catch (_error) {
@@ -105357,7 +105586,8 @@ const evaluateExpressions = ({ components, variables } = {}) => (tree, file) =>
105357
105586
  // parser treats contents as code, so backslash escapes aren't applied;
105358
105587
  // restore them here so e.g. `{\!}` round-trips to `{!}`.
105359
105588
  const processed = expression.replace(/\\([!-/:-@[-`{-~])/g, '$1');
105360
- 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));
105361
105591
  }
105362
105592
  });
105363
105593
  // A text expression is parsed inside a paragraph, but its result can be block content: a
@@ -107560,97 +107790,6 @@ const variablesCodeResolver = ({ variables } = {}) => tree => {
107560
107790
  };
107561
107791
  /* harmony default export */ const variables_code = (variablesCodeResolver);
107562
107792
 
107563
- ;// ./processor/transform/mdxish/variables-text.ts
107564
-
107565
-
107566
- /**
107567
- * Matches {user.<field>} patterns:
107568
- * - {user.name}
107569
- * - {user.email}
107570
- * - {user['field']}
107571
- * - {user["field"]}
107572
- *
107573
- * Captures the field name in group 1 (dot notation) or group 2 (bracket notation)
107574
- */
107575
- const USER_VAR_REGEX = /\{user\.(\w+)\}|\{user\[['"](\w+)['"]\]\}/g;
107576
- function makeVariableNode(varName, rawValue) {
107577
- return {
107578
- type: NodeTypes.variable,
107579
- data: {
107580
- hName: 'Variable',
107581
- hProperties: { name: varName },
107582
- },
107583
- value: rawValue,
107584
- };
107585
- }
107586
- /**
107587
- * A remark plugin that parses {user.<field>} patterns from text nodes and
107588
- * mdx expression nodes, creating Variable nodes for runtime resolution.
107589
- *
107590
- * Handles:
107591
- * - `text` nodes: when safeMode is true or after expression evaluation
107592
- * - `mdxTextExpression` nodes: when mdxExpression has parsed {user.*} before evaluation
107593
- * - `mdxFlowExpression` nodes: when {user.*} appears on its own line (e.g. inside JSX table cells)
107594
- *
107595
- * Supports any user field: name, email, email_verified, exp, iat, etc.
107596
- */
107597
- function visitExpressionNode(node, index, parent) {
107598
- if (index === undefined || !parent)
107599
- return;
107600
- const wrapped = `{${(node.value ?? '').trim()}}`;
107601
- const matches = [...wrapped.matchAll(USER_VAR_REGEX)];
107602
- if (matches.length !== 1)
107603
- return;
107604
- const varName = matches[0][1] || matches[0][2];
107605
- parent.children.splice(index, 1, makeVariableNode(varName, wrapped));
107606
- }
107607
- const variablesTextTransformer = () => tree => {
107608
- visit(tree, 'mdxTextExpression', (node, index, parent) => {
107609
- visitExpressionNode(node, index, parent);
107610
- });
107611
- visit(tree, 'mdxFlowExpression', (node, index, parent) => {
107612
- visitExpressionNode(node, index, parent);
107613
- });
107614
- visit(tree, 'text', (node, index, parent) => {
107615
- if (index === undefined || !parent)
107616
- return;
107617
- // Skip if inside inline code
107618
- if (parent.type === 'inlineCode')
107619
- return;
107620
- const text = node.value;
107621
- if (typeof text !== 'string' || !text.trim())
107622
- return;
107623
- if (!text.includes('{user.') && !text.includes('{user['))
107624
- return;
107625
- const matches = [...text.matchAll(USER_VAR_REGEX)];
107626
- if (matches.length === 0)
107627
- return;
107628
- const parts = [];
107629
- let lastIndex = 0;
107630
- matches.forEach(match => {
107631
- const matchIndex = match.index ?? 0;
107632
- // Add text before the match
107633
- if (matchIndex > lastIndex) {
107634
- parts.push({ type: 'text', value: text.slice(lastIndex, matchIndex) });
107635
- }
107636
- // Extract variable name from either capture group (dot or bracket notation)
107637
- const varName = match[1] || match[2];
107638
- parts.push(makeVariableNode(varName, match[0]));
107639
- lastIndex = matchIndex + match[0].length;
107640
- });
107641
- // Add remaining text after last match
107642
- if (lastIndex < text.length) {
107643
- parts.push({ type: 'text', value: text.slice(lastIndex) });
107644
- }
107645
- // Replace node with parts
107646
- if (parts.length > 1 || (parts.length === 1 && parts[0].type !== 'text')) {
107647
- parent.children.splice(index, 1, ...parts);
107648
- }
107649
- });
107650
- return tree;
107651
- };
107652
- /* harmony default export */ const variables_text = (variablesTextTransformer);
107653
-
107654
107793
  ;// ./lib/utils/mdxish/mdxish-load-components.ts
107655
107794
 
107656
107795
 
@@ -108852,7 +108991,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
108852
108991
  /******/ // startup
108853
108992
  /******/ // Load entry module and return exports
108854
108993
  /******/ // This entry module used 'module' so it can't be inlined
108855
- /******/ let __webpack_exports__ = __webpack_require__(912);
108994
+ /******/ let __webpack_exports__ = __webpack_require__(1595);
108856
108995
  /******/
108857
108996
  /******/ return __webpack_exports__;
108858
108997
  /******/ })()