@readme/markdown 15.5.1 → 15.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -11423,7 +11423,7 @@ module.exports = function () {
11423
11423
 
11424
11424
  /***/ },
11425
11425
 
11426
- /***/ 912
11426
+ /***/ 7070
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);
105305
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);
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
@@ -105458,6 +105688,106 @@ const generateSlugForHeadings = () => (tree) => {
105458
105688
  };
105459
105689
  /* harmony default export */ const heading_slugs = (generateSlugForHeadings);
105460
105690
 
105691
+ ;// ./processor/transform/mdxish/images-to-jsx.ts
105692
+
105693
+
105694
+
105695
+
105696
+
105697
+ const IMAGE_ATTRS = ['align', 'alt', 'border', 'caption', 'className', 'height', 'lazy', 'src', 'title', 'width'];
105698
+ /** The three attributes markdown's `![alt](src "title")` can carry. Anything else forces JSX. */
105699
+ const PLAIN_IMAGE_ATTRS = ['alt', 'src', 'title'];
105700
+ const EMOJI_COLONS = /^:(.*):$/;
105701
+ const hasExtra = (attributes) => attributes.some(attr => !PLAIN_IMAGE_ATTRS.includes(attr.name));
105702
+ const toImageJsx = (attributes, children = []) => ({
105703
+ type: 'mdxJsxFlowElement',
105704
+ name: 'Image',
105705
+ attributes,
105706
+ children,
105707
+ });
105708
+ /**
105709
+ * An `image` is phrasing content, but the nodes it replaces here (`image-block`, and a plain
105710
+ * `[block:image]`) sit where flow content belongs. `mdast-util-to-markdown` picks a block separator
105711
+ * from the two node types around it and has no rule for phrasing-next-to-flow, so it emits none and
105712
+ * glues the image to whatever follows (`![a](src)# Heading`), which then cascades through the rest
105713
+ * of the document. Wrapping restores a block for it to join against.
105714
+ */
105715
+ const placeImage = (parent, index, image) => {
105716
+ const wrapped = { type: 'paragraph', children: [image] };
105717
+ parent.children[index] = INLINE_ONLY_PARENT_TYPES.has(parent.type) ? image : wrapped;
105718
+ };
105719
+ /**
105720
+ * Serializes the three image shapes a parsed document carries. An image that picked up readme
105721
+ * attributes has no markdown spelling left and goes out as `<Image>`, a plain one stays
105722
+ * `![alt](src)`.
105723
+ *
105724
+ * Order matters: `figure` unwraps into the `image` its caption belongs to, so it runs first.
105725
+ */
105726
+ const imagesToJsx = () => tree => {
105727
+ visit(tree, NodeTypes.figure, (node, index, parent) => {
105728
+ if (!parent || index === undefined)
105729
+ return;
105730
+ const [image, caption] = node.children;
105731
+ const { align, className, width } = image.data.hProperties;
105732
+ parent.children[index] = toImageJsx(toAttributes({ ...image, align, width, ...(className === 'border' && { border: true }), src: image.src || image.url }, IMAGE_ATTRS),
105733
+ // The caption stays a child rather than a pre-serialized `caption` attribute, so readme nodes
105734
+ // inside it (emoji, variables, glossary) reach their own handlers instead of the bare
105735
+ // `toMarkdown` in `compatibility.figureToImageBlock`, which throws on them.
105736
+ caption.children);
105737
+ });
105738
+ visit(tree, 'image', (n, index, parent) => {
105739
+ const node = n;
105740
+ if (!parent || index === undefined)
105741
+ return undefined;
105742
+ const hProperties = node.data?.hProperties;
105743
+ // A `:joy:`-style shortcode round-trips as a gemoji rather than the image it parsed into.
105744
+ if (hProperties?.className === 'emoji' && node.title) {
105745
+ const emoji = {
105746
+ type: NodeTypes.emoji,
105747
+ name: node.title.replace(EMOJI_COLONS, '$1'),
105748
+ value: node.title,
105749
+ };
105750
+ parent.children[index] = emoji;
105751
+ return undefined;
105752
+ }
105753
+ if (hProperties) {
105754
+ const attributes = toAttributes({ ...node, ...hProperties, src: node.url }, IMAGE_ATTRS);
105755
+ if (hasExtra(attributes)) {
105756
+ parent.children[index] = toImageJsx(attributes);
105757
+ return undefined;
105758
+ }
105759
+ }
105760
+ // An attribute-less `[block:image]` parses straight to an `image` in a flow slot, so it needs
105761
+ // the same wrap. Editor images always arrive inside a paragraph and are left alone.
105762
+ if (INLINE_ONLY_PARENT_TYPES.has(parent.type))
105763
+ return undefined;
105764
+ placeImage(parent, index, node);
105765
+ // Step over the paragraph just created, or the visitor walks back into the same image.
105766
+ return [(/* inlined export .SKIP */"skip"), index + 1];
105767
+ });
105768
+ visit(tree, NodeTypes.imageBlock, (node, index, parent) => {
105769
+ if (!parent || index === undefined)
105770
+ return;
105771
+ // An image-block keeps its caption as children, so it has to be re-serialized back into the
105772
+ // `caption` attribute before the node is replaced.
105773
+ const captionChildren = node.children ?? [];
105774
+ const caption = captionChildren.length ? toMarkdown({ type: 'root', children: captionChildren }).trim() : '';
105775
+ const attributes = toAttributes({ ...node, ...node.data.hProperties, ...(caption && { caption }) }, IMAGE_ATTRS);
105776
+ if (hasExtra(attributes)) {
105777
+ parent.children[index] = toImageJsx(attributes);
105778
+ return;
105779
+ }
105780
+ placeImage(parent, index, {
105781
+ type: 'image',
105782
+ url: node.src,
105783
+ ...(node.title && { title: node.title }),
105784
+ ...(node.alt && { alt: node.alt }),
105785
+ });
105786
+ });
105787
+ return tree;
105788
+ };
105789
+ /* harmony default export */ const images_to_jsx = (imagesToJsx);
105790
+
105461
105791
  // EXTERNAL MODULE: external "@readme/variable"
105462
105792
  var variable_ = __webpack_require__(8167);
105463
105793
  var variable_default = /*#__PURE__*/__webpack_require__.n(variable_);
@@ -107494,6 +107824,24 @@ function normalizeTableSeparator(content) {
107494
107824
  }
107495
107825
  /* harmony default export */ const normalize_table_separator = ((/* unused pure expression or super */ null && (normalizeTableSeparator)));
107496
107826
 
107827
+ ;// ./processor/transform/mdxish/unwrap-pins.ts
107828
+
107829
+ /**
107830
+ * A `sidebar: true` magic block (e.g. `[block:code]`) parses into an `rdme-pin` wrapper via
107831
+ * `wrapPinnedBlocks`, and remark-stringify throws on any node type without a handler. The mdxish
107832
+ * dialect has no spelling for a pin, so the wrapper serializes as its content instead — the same
107833
+ * (lossy) unwrap `readmeToMdx` has always done, trading the `sidebar` flag for not crashing.
107834
+ */
107835
+ const unwrapPins = () => tree => {
107836
+ visit(tree, 'rdme-pin', (node, index, parent) => {
107837
+ if (!parent || index === undefined)
107838
+ return;
107839
+ parent.children.splice(index, 1, ...node.children);
107840
+ });
107841
+ return tree;
107842
+ };
107843
+ /* harmony default export */ const unwrap_pins = (unwrapPins);
107844
+
107497
107845
  ;// ./processor/transform/mdxish/variables-code.ts
107498
107846
 
107499
107847
 
@@ -107560,97 +107908,6 @@ const variablesCodeResolver = ({ variables } = {}) => tree => {
107560
107908
  };
107561
107909
  /* harmony default export */ const variables_code = (variablesCodeResolver);
107562
107910
 
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
107911
  ;// ./lib/utils/mdxish/mdxish-load-components.ts
107655
107912
 
107656
107913
 
@@ -107731,6 +107988,10 @@ function loadComponents() {
107731
107988
 
107732
107989
 
107733
107990
 
107991
+
107992
+
107993
+
107994
+
107734
107995
 
107735
107996
 
107736
107997
 
@@ -107814,35 +108075,43 @@ function mdxishAstProcessor(mdContent, opts = {}) {
107814
108075
  };
107815
108076
  }
107816
108077
  /**
107817
- * Registers the mdx-jsx serialization extension so remark-stringify
107818
- * can convert JSX nodes (e.g. `<Table>`) to markdown.
108078
+ * Registers serialization for the node types remark-stringify can't write on its own:
108079
+ * JSX elements (e.g. `<Table>`), MDX expressions, and ESM exports.
107819
108080
  */
107820
- function mdxJsxStringify() {
108081
+ function mdxStringifyExtensions() {
107821
108082
  const data = this.data();
107822
108083
  const extensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = []);
107823
- extensions.push({ extensions: [mdxJsxToMarkdown()] });
108084
+ extensions.push({ extensions: [mdxJsxToMarkdown(), mdxExpressionToMarkdown(), mdxjsEsmToMarkdown()] });
107824
108085
  }
108086
+ const stringifyOptions = {
108087
+ bullet: (/* inlined export .DEFAULT_BULLET */"-"),
108088
+ emphasis: '_',
108089
+ // Escape literal braces in text so they don't parse as (often
108090
+ // unterminated) MDX expressions on the next round trip.
108091
+ unsafe: [
108092
+ { character: '{', inConstruct: 'phrasing' },
108093
+ { character: '}', inConstruct: 'phrasing' },
108094
+ ],
108095
+ };
107825
108096
  /**
107826
108097
  * Serializes an Mdast back into a markdown string.
107827
108098
  */
107828
108099
  function mdxishMdastToMd(mdast) {
107829
108100
  const processor = unified()
107830
108101
  .use(remarkGfm)
108102
+ // Readme nodes with no markdown spelling go out as JSX, the same tags mdxish re-parses.
107831
108103
  .use(callout_to_jsx)
107832
108104
  .use(mdxish_tables_to_jsx)
107833
108105
  .use(anchor_to_jsx)
108106
+ // The rest only a PARSER tree carries.
108107
+ // A `sidebar: true` block parses into an `rdme-pin` wrapper the dialect can't spell.
108108
+ .use(unwrap_pins)
108109
+ // Figures, image blocks, and images that picked up readme attributes while parsing.
108110
+ .use(images_to_jsx)
108111
+ // Handlers for the readme nodes that stay themselves (variables, emoji, html-blocks, lists).
107834
108112
  .use(mdxishCompilers)
107835
- .use(mdxJsxStringify)
107836
- .use(remarkStringify, {
107837
- bullet: (/* inlined export .DEFAULT_BULLET */"-"),
107838
- emphasis: '_',
107839
- // Escape literal braces in text so they don't parse as (often
107840
- // unterminated) MDX expressions on the next round trip.
107841
- unsafe: [
107842
- { character: '{', inConstruct: 'phrasing' },
107843
- { character: '}', inConstruct: 'phrasing' },
107844
- ],
107845
- });
108113
+ .use(mdxStringifyExtensions)
108114
+ .use(remarkStringify, stringifyOptions);
107846
108115
  return processor.stringify(processor.runSync(mdast));
107847
108116
  }
107848
108117
  /**
@@ -108852,7 +109121,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
108852
109121
  /******/ // startup
108853
109122
  /******/ // Load entry module and return exports
108854
109123
  /******/ // This entry module used 'module' so it can't be inlined
108855
- /******/ let __webpack_exports__ = __webpack_require__(912);
109124
+ /******/ let __webpack_exports__ = __webpack_require__(7070);
108856
109125
  /******/
108857
109126
  /******/ return __webpack_exports__;
108858
109127
  /******/ })()