@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.node.js CHANGED
@@ -19045,7 +19045,7 @@ module.exports = function () {
19045
19045
 
19046
19046
  /***/ },
19047
19047
 
19048
- /***/ 8719
19048
+ /***/ 6817
19049
19049
  (module, __webpack_exports__, __webpack_require__) {
19050
19050
 
19051
19051
  "use strict";
@@ -25638,6 +25638,8 @@ const parseOptions = (userOpts = {}) => {
25638
25638
  * Pattern to match component tags (PascalCase or snake_case)
25639
25639
  */
25640
25640
  const componentTagPattern = /<(\/?[A-Z][A-Za-z0-9_]*)([^>]*?)(\/?)>/g;
25641
+ /** A bare component name, the same shape `componentTagPattern` accepts inside `<…>`. */
25642
+ const componentNamePattern = /^[A-Z][A-Za-z0-9_]*$/;
25641
25643
  /**
25642
25644
  * MDAST flow (block-level) content types that cannot be represented
25643
25645
  * inside GFM table cells. Used to decide whether a table should be
@@ -96923,6 +96925,14 @@ function syntax_createTokenize(mode) {
96923
96925
  }
96924
96926
  // ── Tag detection inside body ──────────────────────────────────────────
96925
96927
  function bodyLessThan(code) {
96928
+ // A second `<` means legacy variable syntax (`<<var>>`), not a tag: the name that
96929
+ // follows belongs to the variable. Counting it as a nested opener would leave the
96930
+ // body unbalanced and lose the claim on this block.
96931
+ if (code === codes.lessThan) {
96932
+ effects.consume(code);
96933
+ atLineStart = false;
96934
+ return bodyAngleRun;
96935
+ }
96926
96936
  if (code === codes.slash) {
96927
96937
  if (onOpenerLine)
96928
96938
  openerLineCloses += 1;
@@ -96947,6 +96957,14 @@ function syntax_createTokenize(mode) {
96947
96957
  atLineStart = false;
96948
96958
  return body(code);
96949
96959
  }
96960
+ function bodyAngleRun(code) {
96961
+ if (code === codes.lessThan) {
96962
+ effects.consume(code);
96963
+ return bodyAngleRun;
96964
+ }
96965
+ atLineStart = false;
96966
+ return body(code);
96967
+ }
96950
96968
  // ── Nested opening tag ─────────────────────────────────────────────────
96951
96969
  function nestedOpenTagName(code) {
96952
96970
  if (code !== null &&
@@ -97167,11 +97185,61 @@ function mdxComponent() {
97167
97185
  ;// ./lib/micromark/mdx-expression-lenient/syntax.ts
97168
97186
 
97169
97187
 
97188
+
97170
97189
  /**
97171
- * Lenient MDX text-expression tokenizer (agnostic / no acorn).
97190
+ * The flow constructs allowed to end a multiline run, per parser. Everything but `<`: a
97191
+ * `<Component>` on its own line is the very shape the flow construct exists to keep together.
97192
+ */
97193
+ const interrupters = new WeakMap();
97194
+ const flowInterrupters = (flow) => {
97195
+ let record = interrupters.get(flow);
97196
+ if (!record) {
97197
+ record = Object.fromEntries(Object.entries(flow).filter(([code]) => Number(code) !== codes.lessThan));
97198
+ interrupters.set(flow, record);
97199
+ }
97200
+ return record;
97201
+ };
97202
+ /**
97203
+ * Lookahead run at every line ending of a flow run, mirroring the paragraph continuation check
97204
+ * in `micromark-core-commonmark/lib/content.js`: `ok` when the next line continues the run,
97205
+ * `nok` when it would start a fenced code block, heading, or thematic break — which the run
97206
+ * must then leave alone rather than swallow. A blank line (including whitespace-only) ends the
97207
+ * run too, unless `blankContinues`: see `nestedLineContinues`.
97208
+ */
97209
+ function tokenizeLineContinues(blankContinues) {
97210
+ return function tokenize(effects, ok, nok) {
97211
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
97212
+ const self = this;
97213
+ return start;
97214
+ function start(code) {
97215
+ effects.enter(types_types.lineEnding);
97216
+ effects.consume(code);
97217
+ effects.exit(types_types.lineEnding);
97218
+ return factorySpace(effects, prefixed, types_types.linePrefix);
97219
+ }
97220
+ // The paragraph rule also exempts lines indented 4+ columns (indented code can't interrupt);
97221
+ // mdxish disables indented code, so an indented line may still start a block here.
97222
+ function prefixed(code) {
97223
+ if (code === codes.eof)
97224
+ return nok(code);
97225
+ if (markdownLineEnding(code))
97226
+ return blankContinues ? ok(code) : nok(code);
97227
+ return effects.interrupt(flowInterrupters(self.parser.constructs.flow), nok, ok)(code);
97228
+ }
97229
+ };
97230
+ }
97231
+ const lineContinues = { tokenize: tokenizeLineContinues(false), partial: true };
97232
+ /**
97233
+ * Inside an open `{`, `(` or `[` a blank line is JavaScript formatting (between statements in a
97234
+ * callback, inside a JSX branch), so it may not end the run. At depth zero it's far more likely
97235
+ * a prose paragraph break after a stray `{`, and ending the run there keeps the paragraphs apart.
97236
+ */
97237
+ const nestedLineContinues = { tokenize: tokenizeLineContinues(true), partial: true };
97238
+ /**
97239
+ * Lenient MDX expression tokenizer (agnostic / no acorn).
97172
97240
  *
97173
97241
  * Matches a balanced `{ ... }` run — tracking nested braces and spanning soft
97174
- * line breaks — and emits the standard `mdxTextExpression*` tokens so the
97242
+ * line breaks — and emits the standard `mdx*Expression*` tokens so the
97175
97243
  * upstream `mdxExpressionFromMarkdown()` builds the node.
97176
97244
  *
97177
97245
  * Reimplements the official micromark mdxExpression, but an unbalanced brace that
@@ -97179,70 +97247,115 @@ function mdxComponent() {
97179
97247
  * and the `{` renders as literal text, making the pipeline forgiving of stray
97180
97248
  * braces that upstream would hard-error on.
97181
97249
  */
97182
- function tokenizeTextExpression(effects, ok, nok) {
97183
- let depth = 0;
97184
- return start;
97185
- function start(code) {
97186
- if (code !== codes.leftCurlyBrace)
97187
- return nok(code);
97188
- effects.enter('mdxTextExpression');
97189
- effects.enter('mdxTextExpressionMarker');
97190
- effects.consume(code);
97191
- effects.exit('mdxTextExpressionMarker');
97192
- return before;
97193
- }
97194
- function before(code) {
97195
- if (code === codes.eof) {
97196
- effects.exit('mdxTextExpression');
97197
- return nok(code);
97250
+ function tokenizeExpression(variant) {
97251
+ const isFlow = variant === 'mdxFlowExpression';
97252
+ return function tokenize(effects, ok, nok) {
97253
+ // Read eagerly: micromark calls `tokenize` with a fresh context per attempt, so this is the
97254
+ // value for this attempt, and the state functions below don't receive `this` at all.
97255
+ const { interrupt } = this;
97256
+ let depth = 0;
97257
+ let nesting = 0;
97258
+ let multiline = false;
97259
+ return start;
97260
+ function start(code) {
97261
+ if (code !== codes.leftCurlyBrace)
97262
+ return nok(code);
97263
+ // Never end a paragraph. A `{` continuing the line above is prose or a magic-block body
97264
+ // (`[block:callout]` puts one on the next line), not a block of its own.
97265
+ if (isFlow && interrupt)
97266
+ return nok(code);
97267
+ effects.enter(variant);
97268
+ effects.enter(`${variant}Marker`);
97269
+ effects.consume(code);
97270
+ effects.exit(`${variant}Marker`);
97271
+ return before;
97198
97272
  }
97199
- if (markdownLineEnding(code)) {
97200
- effects.enter('lineEnding');
97273
+ function before(code) {
97274
+ if (code === codes.eof)
97275
+ return end(code);
97276
+ if (markdownLineEnding(code)) {
97277
+ // A text run lives in a paragraph micromark has already bounded. A flow run bounds
97278
+ // itself: a blank line or an interrupting block ends it, so a stray `{` can't claim
97279
+ // the rest of the document, and `{\n\n}` stays two literal paragraphs.
97280
+ if (!isFlow)
97281
+ return lineEnding(code);
97282
+ return effects.check(depth > 0 || nesting > 0 ? nestedLineContinues : lineContinues, lineEnding, end)(code);
97283
+ }
97284
+ if (code === codes.rightCurlyBrace && depth === 0)
97285
+ return close(code);
97286
+ effects.enter(`${variant}Chunk`);
97287
+ return inside(code);
97288
+ }
97289
+ function lineEnding(code) {
97290
+ multiline = true;
97291
+ effects.enter(types_types.lineEnding);
97201
97292
  effects.consume(code);
97202
- effects.exit('lineEnding');
97293
+ effects.exit(types_types.lineEnding);
97203
97294
  return before;
97204
97295
  }
97205
- if (code === codes.rightCurlyBrace && depth === 0) {
97206
- return close(code);
97296
+ function end(code) {
97297
+ effects.exit(variant);
97298
+ return nok(code);
97207
97299
  }
97208
- effects.enter('mdxTextExpressionChunk');
97209
- return inside(code);
97210
- }
97211
- function inside(code) {
97212
- if (code === codes.eof || markdownLineEnding(code)) {
97213
- effects.exit('mdxTextExpressionChunk');
97214
- return before(code);
97300
+ function inside(code) {
97301
+ if (code === codes.eof || markdownLineEnding(code)) {
97302
+ effects.exit(`${variant}Chunk`);
97303
+ return before(code);
97304
+ }
97305
+ if (code === codes.rightCurlyBrace && depth === 0) {
97306
+ effects.exit(`${variant}Chunk`);
97307
+ return close(code);
97308
+ }
97309
+ if (code === codes.leftCurlyBrace)
97310
+ depth += 1;
97311
+ else if (code === codes.rightCurlyBrace)
97312
+ depth -= 1;
97313
+ else if (code === codes.leftParenthesis || code === codes.leftSquareBracket)
97314
+ nesting += 1;
97315
+ else if (code === codes.rightParenthesis || code === codes.rightSquareBracket)
97316
+ nesting -= 1;
97317
+ effects.consume(code);
97318
+ return inside;
97215
97319
  }
97216
- if (code === codes.rightCurlyBrace && depth === 0) {
97217
- effects.exit('mdxTextExpressionChunk');
97218
- return close(code);
97320
+ function close(code) {
97321
+ effects.enter(`${variant}Marker`);
97322
+ effects.consume(code);
97323
+ effects.exit(`${variant}Marker`);
97324
+ effects.exit(variant);
97325
+ if (!isFlow)
97326
+ return ok;
97327
+ // A run that closes on the line it opened stays a text expression inside its paragraph:
97328
+ // claiming it as flow would strip the `<p>` every standalone `{expr}` line renders with.
97329
+ return multiline ? after : nok;
97219
97330
  }
97220
- if (code === codes.leftCurlyBrace)
97221
- depth += 1;
97222
- else if (code === codes.rightCurlyBrace)
97223
- depth -= 1;
97224
- effects.consume(code);
97225
- return inside;
97226
- }
97227
- function close(code) {
97228
- effects.enter('mdxTextExpressionMarker');
97229
- effects.consume(code);
97230
- effects.exit('mdxTextExpressionMarker');
97231
- effects.exit('mdxTextExpression');
97232
- return ok;
97233
- }
97331
+ function after(code) {
97332
+ return markdownSpace(code) ? factorySpace(effects, lineEnd, types_types.whitespace)(code) : lineEnd(code);
97333
+ }
97334
+ function lineEnd(code) {
97335
+ return code === codes.eof || markdownLineEnding(code) ? ok(code) : nok(code);
97336
+ }
97337
+ };
97234
97338
  }
97235
97339
  function mdxExpressionLenient() {
97236
97340
  return {
97341
+ flow: {
97342
+ [codes.leftCurlyBrace]: {
97343
+ name: 'mdxFlowExpression',
97344
+ tokenize: tokenizeExpression('mdxFlowExpression'),
97345
+ },
97346
+ },
97237
97347
  text: {
97238
97348
  [codes.leftCurlyBrace]: {
97239
97349
  name: 'mdxTextExpression',
97240
- tokenize: tokenizeTextExpression,
97350
+ tokenize: tokenizeExpression('mdxTextExpression'),
97241
97351
  },
97242
97352
  },
97243
97353
  };
97244
97354
  }
97245
97355
 
97356
+ ;// ./lib/micromark/mdx-expression-lenient/index.ts
97357
+
97358
+
97246
97359
  ;// ./lib/micromark/mdxish-extensions.ts
97247
97360
 
97248
97361
 
@@ -124319,6 +124432,111 @@ const parseTag = (value, opts = {}) => {
124319
124432
  };
124320
124433
  };
124321
124434
 
124435
+ ;// ./processor/transform/mdxish/variables-text.ts
124436
+
124437
+
124438
+ /**
124439
+ * Matches {user.<field>} patterns:
124440
+ * - {user.name}
124441
+ * - {user.email}
124442
+ * - {user['field']}
124443
+ * - {user["field"]}
124444
+ *
124445
+ * Captures the field name in group 1 (dot notation) or group 2 (bracket notation)
124446
+ */
124447
+ const USER_VAR_REGEX = /\{user\.(\w+)\}|\{user\[['"](\w+)['"]\]\}/g;
124448
+ /**
124449
+ * The lone `{user.<field>}` reference an expression node holds, or `null` when the node isn't an
124450
+ * expression or holds anything else (`{1 + 1}`, a CSS-looking body, two references in one brace).
124451
+ * Exported so promotion gates can tell a variable reference apart from an expression that must
124452
+ * stay literal.
124453
+ */
124454
+ function soleUserVariableExpression(node) {
124455
+ if (node.type !== 'mdxFlowExpression' && node.type !== 'mdxTextExpression')
124456
+ return null;
124457
+ const { value } = node;
124458
+ const wrapped = `{${(value ?? '').trim()}}`;
124459
+ const matches = [...wrapped.matchAll(USER_VAR_REGEX)];
124460
+ if (matches.length !== 1 || matches[0][0] !== wrapped)
124461
+ return null;
124462
+ return { name: matches[0][1] || matches[0][2], wrapped };
124463
+ }
124464
+ function makeVariableNode(varName, rawValue) {
124465
+ return {
124466
+ type: NodeTypes.variable,
124467
+ data: {
124468
+ hName: 'Variable',
124469
+ hProperties: { name: varName },
124470
+ },
124471
+ value: rawValue,
124472
+ };
124473
+ }
124474
+ /**
124475
+ * A remark plugin that parses {user.<field>} patterns from text nodes and
124476
+ * mdx expression nodes, creating Variable nodes for runtime resolution.
124477
+ *
124478
+ * Handles:
124479
+ * - `text` nodes: when safeMode is true or after expression evaluation
124480
+ * - `mdxTextExpression` nodes: when mdxExpression has parsed {user.*} before evaluation
124481
+ * - `mdxFlowExpression` nodes: when {user.*} appears on its own line (e.g. inside JSX table cells)
124482
+ *
124483
+ * Supports any user field: name, email, email_verified, exp, iat, etc.
124484
+ */
124485
+ function visitExpressionNode(node, index, parent) {
124486
+ if (index === undefined || !parent)
124487
+ return;
124488
+ const match = soleUserVariableExpression(node);
124489
+ if (!match)
124490
+ return;
124491
+ parent.children.splice(index, 1, makeVariableNode(match.name, match.wrapped));
124492
+ }
124493
+ const variablesTextTransformer = () => tree => {
124494
+ visit(tree, 'mdxTextExpression', (node, index, parent) => {
124495
+ visitExpressionNode(node, index, parent);
124496
+ });
124497
+ visit(tree, 'mdxFlowExpression', (node, index, parent) => {
124498
+ visitExpressionNode(node, index, parent);
124499
+ });
124500
+ visit(tree, 'text', (node, index, parent) => {
124501
+ if (index === undefined || !parent)
124502
+ return;
124503
+ // Skip if inside inline code
124504
+ if (parent.type === 'inlineCode')
124505
+ return;
124506
+ const text = node.value;
124507
+ if (typeof text !== 'string' || !text.trim())
124508
+ return;
124509
+ if (!text.includes('{user.') && !text.includes('{user['))
124510
+ return;
124511
+ const matches = [...text.matchAll(USER_VAR_REGEX)];
124512
+ if (matches.length === 0)
124513
+ return;
124514
+ const parts = [];
124515
+ let lastIndex = 0;
124516
+ matches.forEach(match => {
124517
+ const matchIndex = match.index ?? 0;
124518
+ // Add text before the match
124519
+ if (matchIndex > lastIndex) {
124520
+ parts.push({ type: 'text', value: text.slice(lastIndex, matchIndex) });
124521
+ }
124522
+ // Extract variable name from either capture group (dot or bracket notation)
124523
+ const varName = match[1] || match[2];
124524
+ parts.push(makeVariableNode(varName, match[0]));
124525
+ lastIndex = matchIndex + match[0].length;
124526
+ });
124527
+ // Add remaining text after last match
124528
+ if (lastIndex < text.length) {
124529
+ parts.push({ type: 'text', value: text.slice(lastIndex) });
124530
+ }
124531
+ // Replace node with parts
124532
+ if (parts.length > 1 || (parts.length === 1 && parts[0].type !== 'text')) {
124533
+ parent.children.splice(index, 1, ...parts);
124534
+ }
124535
+ });
124536
+ return tree;
124537
+ };
124538
+ /* harmony default export */ const variables_text = (variablesTextTransformer);
124539
+
124322
124540
  ;// ./processor/transform/mdxish/components/utils.ts
124323
124541
 
124324
124542
 
@@ -124396,19 +124614,15 @@ const NON_PROMOTABLE_PLAIN_TAGS = new Set([...htmlRawNames, ...tableTags, 'table
124396
124614
  const NESTED_TABLE_RE = /<table[\s>]/i;
124397
124615
  const isMarkdownPromotableHtmlTag = (tag) => !NON_PROMOTABLE_PLAIN_TAGS.has(tag);
124398
124616
  // Expression nodes count as plain so `<div>{1+1}</div>` keeps its current
124399
- // literal-brace behavior; variables/glossary already resolve inside raw html.
124400
- const PLAIN_CONTENT_TYPES = new Set([
124401
- 'paragraph',
124402
- 'text',
124403
- 'html',
124404
- 'mdxTextExpression',
124405
- 'mdxFlowExpression',
124406
- NodeTypes.variable,
124407
- NodeTypes.glossary,
124408
- ]);
124617
+ // literal-brace behavior — authors put CSS-shaped braces in these bodies, and only
124618
+ // a reference that names a real variable may resolve.
124619
+ const PLAIN_CONTENT_TYPES = new Set(['paragraph', 'text', 'html', 'mdxTextExpression', 'mdxFlowExpression']);
124409
124620
  // Promoting plain HTML is only worth bypassing rehype-raw's parse5 pass when
124410
- // the body parses into an actual markdown construct.
124621
+ // the body parses into an actual markdown construct. Variables and glossary terms
124622
+ // count: parse5 renders `<<var>>` as a stray `<` plus a `<var>` tag, so a body that
124623
+ // holds nothing else still has to be promoted to resolve.
124411
124624
  const containsMarkdownConstruct = (nodes) => nodes.some(node => !PLAIN_CONTENT_TYPES.has(node.type) ||
124625
+ soleUserVariableExpression(node) !== null ||
124412
124626
  ('children' in node && Array.isArray(node.children) && containsMarkdownConstruct(node.children)));
124413
124627
  /**
124414
124628
  * Index of the `</tag>` that balances the already-consumed opening tag (the
@@ -125174,6 +125388,7 @@ const evaluateExports = () => (tree, file) => {
125174
125388
 
125175
125389
 
125176
125390
 
125391
+
125177
125392
  const parseExpression = (expression) => jsxAcornParser.parse(expression, { ecmaVersion: 'latest', sourceType: 'module' });
125178
125393
  /**
125179
125394
  * Recursively report whether an estree value contains any JSX element or fragment node.
@@ -125190,22 +125405,16 @@ const containsJsxNode = (value) => {
125190
125405
  return true;
125191
125406
  return Object.values(value).some(containsJsxNode);
125192
125407
  };
125193
- /** Read the component name off a JSX element name node (`Foo`, `Foo.Bar`, `foo:Bar`). */
125194
125408
  const jsxElementName = (name) => {
125195
125409
  if (name === null || typeof name !== 'object')
125196
125410
  return undefined;
125197
125411
  const node = name;
125198
- if (node.type === 'JSXIdentifier')
125199
- return typeof node.name === 'string' ? node.name : undefined;
125200
- // `<Foo.Bar/>` and `<foo:Bar/>` resolve through their leftmost part.
125201
- if (node.type === 'JSXMemberExpression')
125202
- return jsxElementName(node.object);
125203
- if (node.type === 'JSXNamespacedName')
125204
- return jsxElementName(node.namespace);
125205
- return undefined;
125412
+ if (node.type !== 'JSXIdentifier' || typeof node.name !== 'string')
125413
+ return undefined;
125414
+ return componentNamePattern.test(node.name) ? node.name : undefined;
125206
125415
  };
125207
125416
  /**
125208
- * Collect the capitalized names an expression uses as JSX tags. Parsed rather than pattern
125417
+ * Collect the component names an expression uses as JSX tags. Parsed rather than pattern
125209
125418
  * matched: `{count < Max ? <Foo/> : <Bar/>}` puts a capitalized name straight after a `<` without
125210
125419
  * it being a tag, and only the parser can tell the two apart. Unparseable input yields nothing —
125211
125420
  * evaluation is about to throw on it anyway.
@@ -125229,8 +125438,7 @@ const jsxComponentNames = (expression) => {
125229
125438
  const node = value;
125230
125439
  if (node.type === 'JSXOpeningElement') {
125231
125440
  const name = jsxElementName(node.name);
125232
- // Lowercase tags compile to a string type, never a variable reference.
125233
- if (name && /^[A-Z]/.test(name))
125441
+ if (name)
125234
125442
  names.add(name);
125235
125443
  }
125236
125444
  Object.values(node).forEach(walk);
@@ -125389,7 +125597,8 @@ function renderFallbackHtml(element) {
125389
125597
  const rawNode = { type: 'raw', value: __WEBPACK_CJS_EXPORT_renderToStaticMarkup__(element) };
125390
125598
  return [rawNode];
125391
125599
  }
125392
- catch {
125600
+ catch (error) {
125601
+ console.warn('[mdxish] Could not render expression result:', error);
125393
125602
  return [];
125394
125603
  }
125395
125604
  }
@@ -125447,6 +125656,7 @@ function reactElementToHast(node) {
125447
125656
 
125448
125657
 
125449
125658
 
125659
+
125450
125660
  /**
125451
125661
  * Bind the components a given expression actually uses as tags. Scoped per expression on purpose:
125452
125662
  * binding the whole hash would shadow same-named globals (a `math` component would break
@@ -125475,17 +125685,28 @@ const isRenderable = (value) => {
125475
125685
  return true;
125476
125686
  return Array.isArray(value) && value.some(isRenderable);
125477
125687
  };
125688
+ const isPhrasingTag = (tagName) => lib_phrasing({ type: 'element', tagName, properties: {}, children: [] });
125478
125689
  /**
125479
- * Whether an expression evaluated to block-level content. Components count as block-level
125480
- * unless they're on the inline list, matching the assumption the rest of the component
125481
- * pipeline makes.
125690
+ * Whether an expression evaluated to block-level content. A capitalized tag is a component,
125691
+ * block-level unless it's on the inline list (so a `Table` component isn't mistaken for an inline
125692
+ * `<table>`); a lowercase tag is HTML, block-level unless it's phrasing content. A `raw` node is
125693
+ * HTML from `renderFallbackHtml`, so it's parsed and judged by the same rule.
125482
125694
  */
125483
125695
  const isBlockResult = (children) => children.some(child => {
125696
+ if (child.type === 'raw')
125697
+ return isBlockResult(fromHtml(child.value, { fragment: true }).children);
125484
125698
  if (child.type !== 'element' && child.type !== 'mdx-jsx')
125485
125699
  return false;
125486
125700
  const { tagName } = child;
125487
- return !STANDARD_HTML_TAGS.has(tagName.toLowerCase()) && !INLINE_COMPONENT_TAGS.has(tagName);
125701
+ return /^[A-Z]/.test(tagName) ? !INLINE_COMPONENT_TAGS.has(tagName) : !isPhrasingTag(tagName);
125488
125702
  });
125703
+ const JSX_ELEMENT_TYPES = new Set(['mdxJsxFlowElement', 'mdxJsxTextElement']);
125704
+ const wrapInParagraph = (child) => ({
125705
+ type: 'paragraph',
125706
+ children: [child],
125707
+ position: child.position,
125708
+ });
125709
+ const placeText = (text, needsBlock) => (needsBlock ? wrapInParagraph(text) : text);
125489
125710
  /** Turn a non-renderable evaluation result into a text node. */
125490
125711
  const createTextNode = (result, position) => {
125491
125712
  if (result === null || result === undefined)
@@ -125521,6 +125742,7 @@ const evaluateExpressions = ({ components, variables } = {}) => (tree, file) =>
125521
125742
  const expression = value?.trim();
125522
125743
  if (!expression)
125523
125744
  return;
125745
+ const needsBlock = expressionNode.type === 'mdxFlowExpression' && !JSX_ELEMENT_TYPES.has(parent.type);
125524
125746
  try {
125525
125747
  const scope = { ...componentScope(expression, components ?? {}), ...baseScope };
125526
125748
  const result = evalExpression(expression, scope);
@@ -125529,10 +125751,17 @@ const evaluateExpressions = ({ components, variables } = {}) => (tree, file) =>
125529
125751
  // passes through rehypeRaw/parse5 step later in the pipeline. This ensures that the
125530
125752
  // expression result is not parsed by parse5 and fragmenting the nesting that is valid JSX
125531
125753
  // but invalid HTML — e.g. an `<a>` wrapping `<a>`.
125532
- expressionNode.data = { ...expressionNode.data, hChildren: reactElementToHast(result) };
125754
+ const hChildren = reactElementToHast(result);
125755
+ expressionNode.data = { ...expressionNode.data, hChildren };
125756
+ // An inline result in a block slot renders inside a `<p>`, as its one-line form does.
125757
+ // Retyping it as a text expression makes it legal paragraph content.
125758
+ if (needsBlock && !isBlockResult(hChildren)) {
125759
+ const inline = { ...expressionNode, type: 'mdxTextExpression' };
125760
+ parent.children.splice(index, 1, wrapInParagraph(inline));
125761
+ }
125533
125762
  }
125534
125763
  else {
125535
- parent.children.splice(index, 1, createTextNode(result, position));
125764
+ parent.children.splice(index, 1, placeText(createTextNode(result, position), needsBlock));
125536
125765
  }
125537
125766
  }
125538
125767
  catch (_error) {
@@ -125540,7 +125769,8 @@ const evaluateExpressions = ({ components, variables } = {}) => (tree, file) =>
125540
125769
  // parser treats contents as code, so backslash escapes aren't applied;
125541
125770
  // restore them here so e.g. `{\!}` round-trips to `{!}`.
125542
125771
  const processed = expression.replace(/\\([!-/:-@[-`{-~])/g, '$1');
125543
- parent.children.splice(index, 1, { type: 'text', value: `{${processed}}`, position });
125772
+ const literal = { type: 'text', value: `{${processed}}`, position };
125773
+ parent.children.splice(index, 1, placeText(literal, needsBlock));
125544
125774
  }
125545
125775
  });
125546
125776
  // A text expression is parsed inside a paragraph, but its result can be block content: a
@@ -125641,6 +125871,106 @@ const generateSlugForHeadings = () => (tree) => {
125641
125871
  };
125642
125872
  /* harmony default export */ const heading_slugs = (generateSlugForHeadings);
125643
125873
 
125874
+ ;// ./processor/transform/mdxish/images-to-jsx.ts
125875
+
125876
+
125877
+
125878
+
125879
+
125880
+ const IMAGE_ATTRS = ['align', 'alt', 'border', 'caption', 'className', 'height', 'lazy', 'src', 'title', 'width'];
125881
+ /** The three attributes markdown's `![alt](src "title")` can carry. Anything else forces JSX. */
125882
+ const PLAIN_IMAGE_ATTRS = ['alt', 'src', 'title'];
125883
+ const EMOJI_COLONS = /^:(.*):$/;
125884
+ const hasExtra = (attributes) => attributes.some(attr => !PLAIN_IMAGE_ATTRS.includes(attr.name));
125885
+ const toImageJsx = (attributes, children = []) => ({
125886
+ type: 'mdxJsxFlowElement',
125887
+ name: 'Image',
125888
+ attributes,
125889
+ children,
125890
+ });
125891
+ /**
125892
+ * An `image` is phrasing content, but the nodes it replaces here (`image-block`, and a plain
125893
+ * `[block:image]`) sit where flow content belongs. `mdast-util-to-markdown` picks a block separator
125894
+ * from the two node types around it and has no rule for phrasing-next-to-flow, so it emits none and
125895
+ * glues the image to whatever follows (`![a](src)# Heading`), which then cascades through the rest
125896
+ * of the document. Wrapping restores a block for it to join against.
125897
+ */
125898
+ const placeImage = (parent, index, image) => {
125899
+ const wrapped = { type: 'paragraph', children: [image] };
125900
+ parent.children[index] = INLINE_ONLY_PARENT_TYPES.has(parent.type) ? image : wrapped;
125901
+ };
125902
+ /**
125903
+ * Serializes the three image shapes a parsed document carries. An image that picked up readme
125904
+ * attributes has no markdown spelling left and goes out as `<Image>`, a plain one stays
125905
+ * `![alt](src)`.
125906
+ *
125907
+ * Order matters: `figure` unwraps into the `image` its caption belongs to, so it runs first.
125908
+ */
125909
+ const imagesToJsx = () => tree => {
125910
+ visit(tree, NodeTypes.figure, (node, index, parent) => {
125911
+ if (!parent || index === undefined)
125912
+ return;
125913
+ const [image, caption] = node.children;
125914
+ const { align, className, width } = image.data.hProperties;
125915
+ parent.children[index] = toImageJsx(toAttributes({ ...image, align, width, ...(className === 'border' && { border: true }), src: image.src || image.url }, IMAGE_ATTRS),
125916
+ // The caption stays a child rather than a pre-serialized `caption` attribute, so readme nodes
125917
+ // inside it (emoji, variables, glossary) reach their own handlers instead of the bare
125918
+ // `toMarkdown` in `compatibility.figureToImageBlock`, which throws on them.
125919
+ caption.children);
125920
+ });
125921
+ visit(tree, 'image', (n, index, parent) => {
125922
+ const node = n;
125923
+ if (!parent || index === undefined)
125924
+ return undefined;
125925
+ const hProperties = node.data?.hProperties;
125926
+ // A `:joy:`-style shortcode round-trips as a gemoji rather than the image it parsed into.
125927
+ if (hProperties?.className === 'emoji' && node.title) {
125928
+ const emoji = {
125929
+ type: NodeTypes.emoji,
125930
+ name: node.title.replace(EMOJI_COLONS, '$1'),
125931
+ value: node.title,
125932
+ };
125933
+ parent.children[index] = emoji;
125934
+ return undefined;
125935
+ }
125936
+ if (hProperties) {
125937
+ const attributes = toAttributes({ ...node, ...hProperties, src: node.url }, IMAGE_ATTRS);
125938
+ if (hasExtra(attributes)) {
125939
+ parent.children[index] = toImageJsx(attributes);
125940
+ return undefined;
125941
+ }
125942
+ }
125943
+ // An attribute-less `[block:image]` parses straight to an `image` in a flow slot, so it needs
125944
+ // the same wrap. Editor images always arrive inside a paragraph and are left alone.
125945
+ if (INLINE_ONLY_PARENT_TYPES.has(parent.type))
125946
+ return undefined;
125947
+ placeImage(parent, index, node);
125948
+ // Step over the paragraph just created, or the visitor walks back into the same image.
125949
+ return [(/* inlined export .SKIP */"skip"), index + 1];
125950
+ });
125951
+ visit(tree, NodeTypes.imageBlock, (node, index, parent) => {
125952
+ if (!parent || index === undefined)
125953
+ return;
125954
+ // An image-block keeps its caption as children, so it has to be re-serialized back into the
125955
+ // `caption` attribute before the node is replaced.
125956
+ const captionChildren = node.children ?? [];
125957
+ const caption = captionChildren.length ? toMarkdown({ type: 'root', children: captionChildren }).trim() : '';
125958
+ const attributes = toAttributes({ ...node, ...node.data.hProperties, ...(caption && { caption }) }, IMAGE_ATTRS);
125959
+ if (hasExtra(attributes)) {
125960
+ parent.children[index] = toImageJsx(attributes);
125961
+ return;
125962
+ }
125963
+ placeImage(parent, index, {
125964
+ type: 'image',
125965
+ url: node.src,
125966
+ ...(node.title && { title: node.title }),
125967
+ ...(node.alt && { alt: node.alt }),
125968
+ });
125969
+ });
125970
+ return tree;
125971
+ };
125972
+ /* harmony default export */ const images_to_jsx = (imagesToJsx);
125973
+
125644
125974
  // EXTERNAL MODULE: ./node_modules/@readme/variable/dist/index.js
125645
125975
  var variable_dist = __webpack_require__(4355);
125646
125976
  var variable_dist_default = /*#__PURE__*/__webpack_require__.n(variable_dist);
@@ -127677,6 +128007,24 @@ function normalizeTableSeparator(content) {
127677
128007
  }
127678
128008
  /* harmony default export */ const normalize_table_separator = ((/* unused pure expression or super */ null && (normalizeTableSeparator)));
127679
128009
 
128010
+ ;// ./processor/transform/mdxish/unwrap-pins.ts
128011
+
128012
+ /**
128013
+ * A `sidebar: true` magic block (e.g. `[block:code]`) parses into an `rdme-pin` wrapper via
128014
+ * `wrapPinnedBlocks`, and remark-stringify throws on any node type without a handler. The mdxish
128015
+ * dialect has no spelling for a pin, so the wrapper serializes as its content instead — the same
128016
+ * (lossy) unwrap `readmeToMdx` has always done, trading the `sidebar` flag for not crashing.
128017
+ */
128018
+ const unwrapPins = () => tree => {
128019
+ visit(tree, 'rdme-pin', (node, index, parent) => {
128020
+ if (!parent || index === undefined)
128021
+ return;
128022
+ parent.children.splice(index, 1, ...node.children);
128023
+ });
128024
+ return tree;
128025
+ };
128026
+ /* harmony default export */ const unwrap_pins = (unwrapPins);
128027
+
127680
128028
  ;// ./processor/transform/mdxish/variables-code.ts
127681
128029
 
127682
128030
 
@@ -127743,97 +128091,6 @@ const variablesCodeResolver = ({ variables } = {}) => tree => {
127743
128091
  };
127744
128092
  /* harmony default export */ const variables_code = (variablesCodeResolver);
127745
128093
 
127746
- ;// ./processor/transform/mdxish/variables-text.ts
127747
-
127748
-
127749
- /**
127750
- * Matches {user.<field>} patterns:
127751
- * - {user.name}
127752
- * - {user.email}
127753
- * - {user['field']}
127754
- * - {user["field"]}
127755
- *
127756
- * Captures the field name in group 1 (dot notation) or group 2 (bracket notation)
127757
- */
127758
- const USER_VAR_REGEX = /\{user\.(\w+)\}|\{user\[['"](\w+)['"]\]\}/g;
127759
- function makeVariableNode(varName, rawValue) {
127760
- return {
127761
- type: NodeTypes.variable,
127762
- data: {
127763
- hName: 'Variable',
127764
- hProperties: { name: varName },
127765
- },
127766
- value: rawValue,
127767
- };
127768
- }
127769
- /**
127770
- * A remark plugin that parses {user.<field>} patterns from text nodes and
127771
- * mdx expression nodes, creating Variable nodes for runtime resolution.
127772
- *
127773
- * Handles:
127774
- * - `text` nodes: when safeMode is true or after expression evaluation
127775
- * - `mdxTextExpression` nodes: when mdxExpression has parsed {user.*} before evaluation
127776
- * - `mdxFlowExpression` nodes: when {user.*} appears on its own line (e.g. inside JSX table cells)
127777
- *
127778
- * Supports any user field: name, email, email_verified, exp, iat, etc.
127779
- */
127780
- function visitExpressionNode(node, index, parent) {
127781
- if (index === undefined || !parent)
127782
- return;
127783
- const wrapped = `{${(node.value ?? '').trim()}}`;
127784
- const matches = [...wrapped.matchAll(USER_VAR_REGEX)];
127785
- if (matches.length !== 1)
127786
- return;
127787
- const varName = matches[0][1] || matches[0][2];
127788
- parent.children.splice(index, 1, makeVariableNode(varName, wrapped));
127789
- }
127790
- const variablesTextTransformer = () => tree => {
127791
- visit(tree, 'mdxTextExpression', (node, index, parent) => {
127792
- visitExpressionNode(node, index, parent);
127793
- });
127794
- visit(tree, 'mdxFlowExpression', (node, index, parent) => {
127795
- visitExpressionNode(node, index, parent);
127796
- });
127797
- visit(tree, 'text', (node, index, parent) => {
127798
- if (index === undefined || !parent)
127799
- return;
127800
- // Skip if inside inline code
127801
- if (parent.type === 'inlineCode')
127802
- return;
127803
- const text = node.value;
127804
- if (typeof text !== 'string' || !text.trim())
127805
- return;
127806
- if (!text.includes('{user.') && !text.includes('{user['))
127807
- return;
127808
- const matches = [...text.matchAll(USER_VAR_REGEX)];
127809
- if (matches.length === 0)
127810
- return;
127811
- const parts = [];
127812
- let lastIndex = 0;
127813
- matches.forEach(match => {
127814
- const matchIndex = match.index ?? 0;
127815
- // Add text before the match
127816
- if (matchIndex > lastIndex) {
127817
- parts.push({ type: 'text', value: text.slice(lastIndex, matchIndex) });
127818
- }
127819
- // Extract variable name from either capture group (dot or bracket notation)
127820
- const varName = match[1] || match[2];
127821
- parts.push(makeVariableNode(varName, match[0]));
127822
- lastIndex = matchIndex + match[0].length;
127823
- });
127824
- // Add remaining text after last match
127825
- if (lastIndex < text.length) {
127826
- parts.push({ type: 'text', value: text.slice(lastIndex) });
127827
- }
127828
- // Replace node with parts
127829
- if (parts.length > 1 || (parts.length === 1 && parts[0].type !== 'text')) {
127830
- parent.children.splice(index, 1, ...parts);
127831
- }
127832
- });
127833
- return tree;
127834
- };
127835
- /* harmony default export */ const variables_text = (variablesTextTransformer);
127836
-
127837
128094
  ;// ./lib/utils/mdxish/mdxish-load-components.ts
127838
128095
 
127839
128096
 
@@ -127914,6 +128171,10 @@ function loadComponents() {
127914
128171
 
127915
128172
 
127916
128173
 
128174
+
128175
+
128176
+
128177
+
127917
128178
 
127918
128179
 
127919
128180
 
@@ -127997,35 +128258,43 @@ function mdxishAstProcessor(mdContent, opts = {}) {
127997
128258
  };
127998
128259
  }
127999
128260
  /**
128000
- * Registers the mdx-jsx serialization extension so remark-stringify
128001
- * can convert JSX nodes (e.g. `<Table>`) to markdown.
128261
+ * Registers serialization for the node types remark-stringify can't write on its own:
128262
+ * JSX elements (e.g. `<Table>`), MDX expressions, and ESM exports.
128002
128263
  */
128003
- function mdxJsxStringify() {
128264
+ function mdxStringifyExtensions() {
128004
128265
  const data = this.data();
128005
128266
  const extensions = data.toMarkdownExtensions || (data.toMarkdownExtensions = []);
128006
- extensions.push({ extensions: [mdxJsxToMarkdown()] });
128267
+ extensions.push({ extensions: [mdxJsxToMarkdown(), mdxExpressionToMarkdown(), mdxjsEsmToMarkdown()] });
128007
128268
  }
128269
+ const stringifyOptions = {
128270
+ bullet: (/* inlined export .DEFAULT_BULLET */"-"),
128271
+ emphasis: '_',
128272
+ // Escape literal braces in text so they don't parse as (often
128273
+ // unterminated) MDX expressions on the next round trip.
128274
+ unsafe: [
128275
+ { character: '{', inConstruct: 'phrasing' },
128276
+ { character: '}', inConstruct: 'phrasing' },
128277
+ ],
128278
+ };
128008
128279
  /**
128009
128280
  * Serializes an Mdast back into a markdown string.
128010
128281
  */
128011
128282
  function mdxishMdastToMd(mdast) {
128012
128283
  const processor = unified()
128013
128284
  .use(remarkGfm)
128285
+ // Readme nodes with no markdown spelling go out as JSX, the same tags mdxish re-parses.
128014
128286
  .use(callout_to_jsx)
128015
128287
  .use(mdxish_tables_to_jsx)
128016
128288
  .use(anchor_to_jsx)
128289
+ // The rest only a PARSER tree carries.
128290
+ // A `sidebar: true` block parses into an `rdme-pin` wrapper the dialect can't spell.
128291
+ .use(unwrap_pins)
128292
+ // Figures, image blocks, and images that picked up readme attributes while parsing.
128293
+ .use(images_to_jsx)
128294
+ // Handlers for the readme nodes that stay themselves (variables, emoji, html-blocks, lists).
128017
128295
  .use(mdxishCompilers)
128018
- .use(mdxJsxStringify)
128019
- .use(remarkStringify, {
128020
- bullet: (/* inlined export .DEFAULT_BULLET */"-"),
128021
- emphasis: '_',
128022
- // Escape literal braces in text so they don't parse as (often
128023
- // unterminated) MDX expressions on the next round trip.
128024
- unsafe: [
128025
- { character: '{', inConstruct: 'phrasing' },
128026
- { character: '}', inConstruct: 'phrasing' },
128027
- ],
128028
- });
128296
+ .use(mdxStringifyExtensions)
128297
+ .use(remarkStringify, stringifyOptions);
128029
128298
  return processor.stringify(processor.runSync(mdast));
128030
128299
  }
128031
128300
  /**
@@ -129094,7 +129363,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"*":["about","acceptCharset","accessK
129094
129363
  /******/ // startup
129095
129364
  /******/ // Load entry module and return exports
129096
129365
  /******/ // This entry module used 'module' so it can't be inlined
129097
- /******/ let __webpack_exports__ = __webpack_require__(8719);
129366
+ /******/ let __webpack_exports__ = __webpack_require__(6817);
129098
129367
  /******/ module.exports = __webpack_exports__;
129099
129368
  /******/
129100
129369
  /******/ })()